database
This commit is contained in:
commit
a93f949020
1054
mail-service/package-lock.json
generated
Normal file
1054
mail-service/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
12
mail-service/package.json
Normal file
12
mail-service/package.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"scripts": {
|
||||
"start": "node src/index.js"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"dotenv": "^17.2.0",
|
||||
"express": "^5.1.0",
|
||||
"mysql2": "^3.14.2",
|
||||
"sequelize": "^6.37.7"
|
||||
}
|
||||
}
|
||||
27
mail-service/src/index.js
Normal file
27
mail-service/src/index.js
Normal file
@ -0,0 +1,27 @@
|
||||
import dotenv from 'dotenv';
|
||||
dotenv.config();
|
||||
import express from 'express';
|
||||
import { sequelize } from './models/index.js';
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
res.send('Mail Service is running');
|
||||
});
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
await sequelize.authenticate();
|
||||
await sequelize.sync({ alter: true });
|
||||
console.log('Database connected and models synced');
|
||||
} catch (err) {
|
||||
console.error('Unable to connect to the database:', err);
|
||||
process.exit(1);
|
||||
}
|
||||
})();
|
||||
|
||||
const PORT = process.env.PORT || 3000;
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Mail Service listening on port ${PORT}`);
|
||||
});
|
||||
15
mail-service/src/models/campaign.js
Normal file
15
mail-service/src/models/campaign.js
Normal file
@ -0,0 +1,15 @@
|
||||
import { DataTypes, Sequelize } from 'sequelize';
|
||||
|
||||
export default (sequelize) => {
|
||||
const Campaign = sequelize.define('Campaign', {
|
||||
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
|
||||
user_id: { type: DataTypes.INTEGER, allowNull: false },
|
||||
template_version_id: { type: DataTypes.INTEGER, allowNull: false },
|
||||
group_id: { type: DataTypes.INTEGER, allowNull: false },
|
||||
subject_override: { type: DataTypes.STRING },
|
||||
scheduled_at: { type: DataTypes.DATE },
|
||||
status: { type: DataTypes.ENUM('draft', 'scheduled', 'sent', 'failed'), defaultValue: 'draft' },
|
||||
created_at: { type: DataTypes.DATE, defaultValue: Sequelize.NOW },
|
||||
}, { tableName: 'campaigns', timestamps: false });
|
||||
return Campaign;
|
||||
};
|
||||
15
mail-service/src/models/deliveryLog.js
Normal file
15
mail-service/src/models/deliveryLog.js
Normal file
@ -0,0 +1,15 @@
|
||||
import { DataTypes, Sequelize } from 'sequelize';
|
||||
|
||||
export default (sequelize) => {
|
||||
const DeliveryLog = sequelize.define('DeliveryLog', {
|
||||
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
|
||||
campaign_id: { type: DataTypes.INTEGER, allowNull: false },
|
||||
subscriber_id: { type: DataTypes.INTEGER, allowNull: false },
|
||||
sent_at: { type: DataTypes.DATE },
|
||||
status: { type: DataTypes.ENUM('sent', 'bounced', 'failed'), allowNull: false },
|
||||
error_message: { type: DataTypes.STRING },
|
||||
opened_at: { type: DataTypes.DATE },
|
||||
clicked_at: { type: DataTypes.DATE },
|
||||
}, { tableName: 'delivery_logs', timestamps: false });
|
||||
return DeliveryLog;
|
||||
};
|
||||
12
mail-service/src/models/emailTemplate.js
Normal file
12
mail-service/src/models/emailTemplate.js
Normal file
@ -0,0 +1,12 @@
|
||||
import { DataTypes, Sequelize } from 'sequelize';
|
||||
|
||||
export default (sequelize) => {
|
||||
const EmailTemplate = sequelize.define('EmailTemplate', {
|
||||
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
|
||||
user_id: { type: DataTypes.INTEGER, allowNull: false },
|
||||
name: { type: DataTypes.STRING, allowNull: false },
|
||||
created_at: { type: DataTypes.DATE, defaultValue: Sequelize.NOW },
|
||||
updated_at: { type: DataTypes.DATE, defaultValue: Sequelize.NOW },
|
||||
}, { tableName: 'email_templates', timestamps: false });
|
||||
return EmailTemplate;
|
||||
};
|
||||
15
mail-service/src/models/emailTemplateVersion.js
Normal file
15
mail-service/src/models/emailTemplateVersion.js
Normal file
@ -0,0 +1,15 @@
|
||||
import { DataTypes, Sequelize } from 'sequelize';
|
||||
|
||||
export default (sequelize) => {
|
||||
const EmailTemplateVersion = sequelize.define('EmailTemplateVersion', {
|
||||
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
|
||||
template_id: { type: DataTypes.INTEGER, allowNull: false },
|
||||
version_number: { type: DataTypes.INTEGER, allowNull: false },
|
||||
subject: { type: DataTypes.STRING, allowNull: false },
|
||||
body_html: { type: DataTypes.TEXT },
|
||||
body_text: { type: DataTypes.TEXT },
|
||||
is_active: { type: DataTypes.BOOLEAN, defaultValue: true },
|
||||
created_at: { type: DataTypes.DATE, defaultValue: Sequelize.NOW },
|
||||
}, { tableName: 'email_template_versions', timestamps: false });
|
||||
return EmailTemplateVersion;
|
||||
};
|
||||
11
mail-service/src/models/groupSubscriber.js
Normal file
11
mail-service/src/models/groupSubscriber.js
Normal file
@ -0,0 +1,11 @@
|
||||
import { DataTypes, Sequelize } from 'sequelize';
|
||||
|
||||
export default (sequelize) => {
|
||||
const GroupSubscriber = sequelize.define('GroupSubscriber', {
|
||||
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
|
||||
group_id: { type: DataTypes.INTEGER, allowNull: false },
|
||||
subscriber_id: { type: DataTypes.INTEGER, allowNull: false },
|
||||
added_at: { type: DataTypes.DATE, defaultValue: Sequelize.NOW },
|
||||
}, { tableName: 'group_subscribers', timestamps: false });
|
||||
return GroupSubscriber;
|
||||
};
|
||||
62
mail-service/src/models/index.js
Normal file
62
mail-service/src/models/index.js
Normal file
@ -0,0 +1,62 @@
|
||||
import { Sequelize } from 'sequelize';
|
||||
import SubscriberModel from './subscriber.js';
|
||||
import MailingGroupModel from './mailingGroup.js';
|
||||
import GroupSubscriberModel from './groupSubscriber.js';
|
||||
import EmailTemplateModel from './emailTemplate.js';
|
||||
import EmailTemplateVersionModel from './emailTemplateVersion.js';
|
||||
import CampaignModel from './campaign.js';
|
||||
import DeliveryLogModel from './deliveryLog.js';
|
||||
import SmtpServerModel from './smtpServer.js';
|
||||
|
||||
const sequelize = new Sequelize(
|
||||
process.env.DB_NAME,
|
||||
process.env.DB_USER,
|
||||
process.env.DB_PASSWORD,
|
||||
{
|
||||
host: process.env.DB_HOST,
|
||||
port: process.env.DB_PORT,
|
||||
dialect: 'mysql',
|
||||
logging: false,
|
||||
}
|
||||
);
|
||||
|
||||
const Subscriber = SubscriberModel(sequelize);
|
||||
const MailingGroup = MailingGroupModel(sequelize);
|
||||
const GroupSubscriber = GroupSubscriberModel(sequelize);
|
||||
const EmailTemplate = EmailTemplateModel(sequelize);
|
||||
const EmailTemplateVersion = EmailTemplateVersionModel(sequelize);
|
||||
const Campaign = CampaignModel(sequelize);
|
||||
const DeliveryLog = DeliveryLogModel(sequelize);
|
||||
const SmtpServer = SmtpServerModel(sequelize);
|
||||
|
||||
// Связи
|
||||
MailingGroup.belongsToMany(Subscriber, { through: GroupSubscriber, foreignKey: 'group_id', otherKey: 'subscriber_id' });
|
||||
Subscriber.belongsToMany(MailingGroup, { through: GroupSubscriber, foreignKey: 'subscriber_id', otherKey: 'group_id' });
|
||||
GroupSubscriber.belongsTo(MailingGroup, { foreignKey: 'group_id' });
|
||||
GroupSubscriber.belongsTo(Subscriber, { foreignKey: 'subscriber_id' });
|
||||
|
||||
EmailTemplate.hasMany(EmailTemplateVersion, { foreignKey: 'template_id' });
|
||||
EmailTemplateVersion.belongsTo(EmailTemplate, { foreignKey: 'template_id' });
|
||||
|
||||
Campaign.belongsTo(EmailTemplateVersion, { foreignKey: 'template_version_id' });
|
||||
Campaign.belongsTo(MailingGroup, { foreignKey: 'group_id' });
|
||||
|
||||
DeliveryLog.belongsTo(Campaign, { foreignKey: 'campaign_id' });
|
||||
DeliveryLog.belongsTo(Subscriber, { foreignKey: 'subscriber_id' });
|
||||
|
||||
SmtpServer.belongsTo(MailingGroup, { foreignKey: 'group_id' });
|
||||
MailingGroup.hasMany(SmtpServer, { foreignKey: 'group_id' });
|
||||
|
||||
// (связи с user_id только по полю, без внешнего ключа на User)
|
||||
|
||||
export {
|
||||
sequelize,
|
||||
Subscriber,
|
||||
MailingGroup,
|
||||
GroupSubscriber,
|
||||
EmailTemplate,
|
||||
EmailTemplateVersion,
|
||||
Campaign,
|
||||
DeliveryLog,
|
||||
SmtpServer,
|
||||
};
|
||||
12
mail-service/src/models/mailingGroup.js
Normal file
12
mail-service/src/models/mailingGroup.js
Normal file
@ -0,0 +1,12 @@
|
||||
import { DataTypes, Sequelize } from 'sequelize';
|
||||
|
||||
export default (sequelize) => {
|
||||
const MailingGroup = sequelize.define('MailingGroup', {
|
||||
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
|
||||
user_id: { type: DataTypes.INTEGER, allowNull: false },
|
||||
name: { type: DataTypes.STRING, allowNull: false },
|
||||
description: { type: DataTypes.STRING },
|
||||
created_at: { type: DataTypes.DATE, defaultValue: Sequelize.NOW },
|
||||
}, { tableName: 'mailing_groups', timestamps: false });
|
||||
return MailingGroup;
|
||||
};
|
||||
19
mail-service/src/models/smtpServer.js
Normal file
19
mail-service/src/models/smtpServer.js
Normal file
@ -0,0 +1,19 @@
|
||||
import { DataTypes, Sequelize } from 'sequelize';
|
||||
|
||||
export default (sequelize) => {
|
||||
const SmtpServer = sequelize.define('SmtpServer', {
|
||||
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
|
||||
user_id: { type: DataTypes.INTEGER, allowNull: false },
|
||||
group_id: { type: DataTypes.INTEGER, allowNull: true }, // связь с группой подписчиков
|
||||
name: { type: DataTypes.STRING, allowNull: false },
|
||||
host: { type: DataTypes.STRING, allowNull: false },
|
||||
port: { type: DataTypes.INTEGER, allowNull: false },
|
||||
secure: { type: DataTypes.BOOLEAN, defaultValue: false },
|
||||
username: { type: DataTypes.STRING, allowNull: false },
|
||||
password: { type: DataTypes.STRING, allowNull: false },
|
||||
from_email: { type: DataTypes.STRING, allowNull: false },
|
||||
created_at: { type: DataTypes.DATE, defaultValue: Sequelize.NOW },
|
||||
updated_at: { type: DataTypes.DATE, defaultValue: Sequelize.NOW },
|
||||
}, { tableName: 'smtp_servers', timestamps: false });
|
||||
return SmtpServer;
|
||||
};
|
||||
13
mail-service/src/models/subscriber.js
Normal file
13
mail-service/src/models/subscriber.js
Normal file
@ -0,0 +1,13 @@
|
||||
import { DataTypes, Sequelize } from 'sequelize';
|
||||
|
||||
export default (sequelize) => {
|
||||
const Subscriber = sequelize.define('Subscriber', {
|
||||
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
|
||||
email: { type: DataTypes.STRING, allowNull: false, unique: true },
|
||||
name: { type: DataTypes.STRING },
|
||||
status: { type: DataTypes.ENUM('active', 'unsubscribed', 'bounced'), defaultValue: 'active' },
|
||||
created_at: { type: DataTypes.DATE, defaultValue: Sequelize.NOW },
|
||||
unsubscribed_at: { type: DataTypes.DATE },
|
||||
}, { tableName: 'subscribers', timestamps: false });
|
||||
return Subscriber;
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user