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