first commit: Complete phishing test management panel with Node.js backend and React frontend

This commit is contained in:
salvacybersec
2025-11-10 17:00:40 +03:00
commit 19e551f33b
77 changed files with 6677 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
const { DataTypes } = require('sequelize');
const { sequelize } = require('../config/database');
const AdminUser = sequelize.define('AdminUser', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
validate: {
isOne(value) {
if (value !== 1) {
throw new Error('Only one admin user is allowed (id must be 1)');
}
},
},
},
username: {
type: DataTypes.STRING(100),
allowNull: false,
unique: true,
},
password_hash: {
type: DataTypes.STRING(255),
allowNull: false,
},
last_login: {
type: DataTypes.DATE,
allowNull: true,
},
}, {
tableName: 'admin_user',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
});
module.exports = AdminUser;

View File

@@ -0,0 +1,71 @@
const { DataTypes } = require('sequelize');
const { sequelize } = require('../config/database');
const ClickLog = sequelize.define('ClickLog', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
token_id: {
type: DataTypes.INTEGER,
allowNull: false,
comment: 'FK -> tracking_tokens.id',
},
ip_address: {
type: DataTypes.STRING(45),
allowNull: false,
},
country: {
type: DataTypes.STRING(100),
allowNull: true,
},
city: {
type: DataTypes.STRING(100),
allowNull: true,
},
latitude: {
type: DataTypes.DECIMAL(10, 8),
allowNull: true,
},
longitude: {
type: DataTypes.DECIMAL(11, 8),
allowNull: true,
},
user_agent: {
type: DataTypes.TEXT,
allowNull: true,
},
browser: {
type: DataTypes.STRING(100),
allowNull: true,
},
os: {
type: DataTypes.STRING(100),
allowNull: true,
},
device: {
type: DataTypes.STRING(100),
allowNull: true,
},
referer: {
type: DataTypes.TEXT,
allowNull: true,
},
telegram_sent: {
type: DataTypes.BOOLEAN,
defaultValue: false,
},
}, {
tableName: 'click_logs',
timestamps: true,
createdAt: 'clicked_at',
updatedAt: false,
indexes: [
{ fields: ['token_id'] },
{ fields: ['ip_address'] },
],
});
module.exports = ClickLog;

View File

@@ -0,0 +1,56 @@
const { DataTypes } = require('sequelize');
const { sequelize } = require('../config/database');
const Company = sequelize.define('Company', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: {
type: DataTypes.STRING(255),
allowNull: false,
validate: {
notEmpty: true,
},
},
description: {
type: DataTypes.TEXT,
allowNull: true,
},
logo_url: {
type: DataTypes.TEXT,
allowNull: true,
},
industry: {
type: DataTypes.STRING(100),
allowNull: true,
comment: 'Sektör: Banking, Telecom, Government, etc.',
},
active: {
type: DataTypes.BOOLEAN,
defaultValue: true,
},
// İstatistikler (denormalized)
total_tokens: {
type: DataTypes.INTEGER,
defaultValue: 0,
},
total_clicks: {
type: DataTypes.INTEGER,
defaultValue: 0,
},
click_rate: {
type: DataTypes.DECIMAL(5, 2),
defaultValue: 0,
comment: 'Tıklama oranı %',
},
}, {
tableName: 'companies',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
});
module.exports = Company;

View File

@@ -0,0 +1,48 @@
const { DataTypes } = require('sequelize');
const { sequelize } = require('../config/database');
const MailTemplate = sequelize.define('MailTemplate', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: {
type: DataTypes.STRING(255),
allowNull: false,
},
template_type: {
type: DataTypes.STRING(50),
allowNull: false,
unique: true,
comment: 'bank, edevlet, corporate, etc.',
},
subject_template: {
type: DataTypes.STRING(500),
allowNull: true,
},
body_html: {
type: DataTypes.TEXT,
allowNull: false,
},
description: {
type: DataTypes.TEXT,
allowNull: true,
},
preview_image: {
type: DataTypes.TEXT,
allowNull: true,
},
active: {
type: DataTypes.BOOLEAN,
defaultValue: true,
},
}, {
tableName: 'mail_templates',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
});
module.exports = MailTemplate;

View File

@@ -0,0 +1,36 @@
const { DataTypes } = require('sequelize');
const { sequelize } = require('../config/database');
const Settings = sequelize.define('Settings', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
key: {
type: DataTypes.STRING(100),
allowNull: false,
unique: true,
comment: 'gmail_user, telegram_token, etc.',
},
value: {
type: DataTypes.TEXT,
allowNull: true,
},
is_encrypted: {
type: DataTypes.BOOLEAN,
defaultValue: false,
},
description: {
type: DataTypes.TEXT,
allowNull: true,
},
}, {
tableName: 'settings',
timestamps: true,
createdAt: false,
updatedAt: 'updated_at',
});
module.exports = Settings;

View File

@@ -0,0 +1,78 @@
const { DataTypes } = require('sequelize');
const { sequelize } = require('../config/database');
const TrackingToken = sequelize.define('TrackingToken', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
token: {
type: DataTypes.STRING(64),
allowNull: false,
unique: true,
comment: 'Benzersiz tracking token (32 byte hex)',
},
company_id: {
type: DataTypes.INTEGER,
allowNull: false,
comment: 'FK -> companies.id',
},
target_email: {
type: DataTypes.STRING(255),
allowNull: false,
},
employee_name: {
type: DataTypes.STRING(255),
allowNull: true,
},
template_type: {
type: DataTypes.STRING(50),
defaultValue: 'bank',
},
mail_subject: {
type: DataTypes.STRING(500),
allowNull: true,
},
mail_sent: {
type: DataTypes.BOOLEAN,
defaultValue: false,
},
sent_at: {
type: DataTypes.DATE,
allowNull: true,
},
clicked: {
type: DataTypes.BOOLEAN,
defaultValue: false,
},
click_count: {
type: DataTypes.INTEGER,
defaultValue: 0,
},
first_click_at: {
type: DataTypes.DATE,
allowNull: true,
},
last_click_at: {
type: DataTypes.DATE,
allowNull: true,
},
notes: {
type: DataTypes.TEXT,
allowNull: true,
},
}, {
tableName: 'tracking_tokens',
timestamps: true,
createdAt: 'created_at',
updatedAt: false,
indexes: [
{ fields: ['token'], unique: true },
{ fields: ['company_id'] },
{ fields: ['target_email'] },
],
});
module.exports = TrackingToken;

View File

@@ -0,0 +1,43 @@
const { sequelize } = require('../config/database');
const Company = require('./Company');
const TrackingToken = require('./TrackingToken');
const ClickLog = require('./ClickLog');
const MailTemplate = require('./MailTemplate');
const Settings = require('./Settings');
const AdminUser = require('./AdminUser');
// Define relationships
// Company -> TrackingToken (One-to-Many)
Company.hasMany(TrackingToken, {
foreignKey: 'company_id',
as: 'tokens',
onDelete: 'CASCADE',
});
TrackingToken.belongsTo(Company, {
foreignKey: 'company_id',
as: 'company',
});
// TrackingToken -> ClickLog (One-to-Many)
TrackingToken.hasMany(ClickLog, {
foreignKey: 'token_id',
as: 'clicks',
onDelete: 'CASCADE',
});
ClickLog.belongsTo(TrackingToken, {
foreignKey: 'token_id',
as: 'token',
});
// Export models
module.exports = {
sequelize,
Company,
TrackingToken,
ClickLog,
MailTemplate,
Settings,
AdminUser,
};