med-cms 1.0.0-beta.1 → 1.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,12 +1,9 @@
1
- const { Sequelize } = require('sequelize');
1
+ const { Sequelize, DataTypes } = require('sequelize');
2
2
  const path = require('path');
3
- const Clinic = require(path.join(__dirname, '..', 'models', 'clinics'));
4
- const User = require(path.join(__dirname, '..', 'models', 'users'));
5
- const UserAddress = require(path.join(__dirname, '..', 'models', 'user-addresses'));
6
- const Disease = require(path.join(__dirname, '..', 'models', 'diseases'));
7
- const Appointment = require(path.join(__dirname, '..', 'models', 'appointments'));
8
3
  const logger = require("custom-color-logs").print;
4
+
9
5
  let sequelize;
6
+
10
7
  function getSequelizeInstance(dbPath) {
11
8
  if (!sequelize) {
12
9
  const storagePath = dbPath || path.join(process.cwd(), 'med-cms.sqlite');
@@ -18,27 +15,76 @@ function getSequelizeInstance(dbPath) {
18
15
  }
19
16
  return sequelize;
20
17
  }
18
+
21
19
  async function initDatabase(dbPath) {
22
20
  const instance = getSequelizeInstance(dbPath);
21
+
23
22
  try {
24
23
  await instance.authenticate();
25
- console.log(logger.DatabaseMedInfo("Успешное подключение к SQLite через Sequelize."))
26
- User.belongsTo(Clinic, { foreignKey: 'ClinicId', onDelete: 'SET NULL' });
27
- Clinic.hasMany(User, { foreignKey: 'ClinicId' });
28
- UserAddress.belongsTo(User, { foreignKey: 'UserId', onDelete: 'CASCADE' });
29
- User.hasOne(UserAddress, { foreignKey: 'UserId' });
30
- Appointment.belongsTo(User, { as: 'Patient', foreignKey: 'PatientId', onDelete: 'CASCADE' });
31
- Appointment.belongsTo(User, { as: 'Doctor', foreignKey: 'DoctorId', onDelete: 'CASCADE' });
32
- Appointment.belongsTo(Disease, { foreignKey: 'DiseaseId', onDelete: 'SET NULL' });
24
+ console.log(logger.DatabaseMedInfo("Успешное подключение к SQLite через Sequelize."));
25
+
26
+ // 1. Импортируем функции моделей и сразу инициализируем их инстансом sequelize
27
+ const models = {
28
+ Clinic: require(path.join(__dirname, '..', 'models', 'clinics'))(instance, DataTypes),
29
+ User: require(path.join(__dirname, '..', 'models', 'users'))(instance, DataTypes),
30
+ UserAddress: require(path.join(__dirname, '..', 'models', 'user-addresses'))(instance, DataTypes),
31
+ Disease: require(path.join(__dirname, '..', 'models', 'diseases'))(instance, DataTypes),
32
+ Appointment: require(path.join(__dirname, '..', 'models', 'appointments'))(instance, DataTypes)
33
+ };
34
+
35
+ // 2. Автоматически запускаем связи, которые мы прописали внутри файлов таблиц
36
+ Object.keys(models).forEach((modelName) => {
37
+ if (models[modelName].associate) {
38
+ models[modelName].associate(models);
39
+ }
40
+ });
41
+
33
42
  await instance.sync({ alter: true });
34
- console.log(logger.DatabaseMedInfo("Все таблицы базы данных успешно синхронизированы."))
43
+ console.log(logger.DatabaseMedInfo("Все таблицы базы данных успешно синхронизированы."));
35
44
  } catch (error) {
36
- console.log(logger.DatabaseMedError('Ошибка инициализации базы данных:', error))
45
+ console.log(logger.DatabaseMedError('Ошибка инициализации базы данных:', error));
37
46
  throw error;
38
47
  }
39
48
  }
40
- module.exports = {
41
- get sequelize() { return sequelize; },
49
+
50
+ // В самом конце вашего database/config/db-init.js вместо старого exports:
51
+
52
+ const db = {
42
53
  getSequelizeInstance,
43
- initDatabase
44
- };
54
+ initDatabase,
55
+ get sequelize() { return sequelize; },
56
+ // Динамически прокидываем модели наружу, чтобы они были доступны как db.User, db.Clinic и т.д.
57
+ models: {}
58
+ };
59
+
60
+ // Перепишем функцию initDatabase, чтобы она сохраняла модели в db.models и самом объекте db
61
+ async function initDatabase(dbPath) {
62
+ const instance = getSequelizeInstance(dbPath);
63
+
64
+ try {
65
+ await instance.authenticate();
66
+ console.log(logger.DatabaseMedInfo("Успешное подключение к SQLite через Sequelize."));
67
+
68
+ // Инициализируем и сохраняем в объект
69
+ db.models.Clinic = db.Clinic = require(path.join(__dirname, '..', 'models', 'clinics'))(instance, DataTypes);
70
+ db.models.User = db.User = require(path.join(__dirname, '..', 'models', 'users'))(instance, DataTypes);
71
+ db.models.UserAddress = db.UserAddress = require(path.join(__dirname, '..', 'models', 'user-addresses'))(instance, DataTypes);
72
+ db.models.Disease = db.Disease = require(path.join(__dirname, '..', 'models', 'diseases'))(instance, DataTypes);
73
+ db.models.Appointment = db.Appointment = require(path.join(__dirname, '..', 'models', 'appointments'))(instance, DataTypes);
74
+
75
+ // Запускаем связи
76
+ Object.keys(db.models).forEach((modelName) => {
77
+ if (db.models[modelName].associate) {
78
+ db.models[modelName].associate(db.models);
79
+ }
80
+ });
81
+
82
+ await instance.sync({ alter: true });
83
+ console.log(logger.DatabaseMedInfo("Все таблицы базы данных успешно синхронизированы."));
84
+ } catch (error) {
85
+ console.log(logger.DatabaseMedError('Ошибка инициализации базы данных:', error));
86
+ throw error;
87
+ }
88
+ }
89
+
90
+ module.exports = db;
@@ -1,39 +1,29 @@
1
- const { DataTypes } = require('sequelize');
2
- const path = require("path");
3
- const { getSequelizeInstance } = require(path.join(__dirname, '..', 'config', 'db-init.js'));
4
- const sequelize = getSequelizeInstance();
5
- const Appointment = sequelize.define('Appointment', {
6
- id: {
7
- type: DataTypes.UUID,
8
- defaultValue: DataTypes.UUIDV4,
9
- primaryKey: true
10
- },
11
- date: {
12
- type: DataTypes.DATEONLY,
13
- allowNull: false
14
- },
15
- time: {
16
- type: DataTypes.STRING,
17
- allowNull: false
18
- },
19
- status: {
20
- type: DataTypes.ENUM('ожидается', 'завершен', 'отменен'),
21
- defaultValue: 'ожидается',
22
- allowNull: false
23
- },
24
- complaints: {
25
- type: DataTypes.TEXT,
26
- allowNull: true
27
- },
28
- objectively: {
29
- type: DataTypes.TEXT,
30
- allowNull: true
31
- },
32
- doctorNotes: {
33
- type: DataTypes.TEXT,
34
- allowNull: true
35
- }
36
- }, {
37
- timestamps: true
38
- });
39
- module.exports = Appointment;
1
+ module.exports = (sequelize, DataTypes) => {
2
+ const Appointment = sequelize.define('Appointment', {
3
+ id: {
4
+ type: DataTypes.UUID,
5
+ defaultValue: DataTypes.UUIDV4,
6
+ primaryKey: true
7
+ },
8
+ date: { type: DataTypes.DATEONLY, allowNull: false },
9
+ time: { type: DataTypes.STRING, allowNull: false },
10
+ status: {
11
+ type: DataTypes.ENUM('ожидается', 'завершен', 'отменен'),
12
+ defaultValue: 'ожидается',
13
+ allowNull: false
14
+ },
15
+ complaints: { type: DataTypes.TEXT, allowNull: true },
16
+ objectively: { type: DataTypes.TEXT, allowNull: true },
17
+ doctorNotes: { type: DataTypes.TEXT, allowNull: true }
18
+ }, {
19
+ timestamps: true
20
+ });
21
+
22
+ Appointment.associate = (models) => {
23
+ Appointment.belongsTo(models.User, { as: 'Patient', foreignKey: 'PatientId', onDelete: 'CASCADE' });
24
+ Appointment.belongsTo(models.User, { as: 'Doctor', foreignKey: 'DoctorId', onDelete: 'CASCADE' });
25
+ Appointment.belongsTo(models.Disease, { foreignKey: 'DiseaseId', onDelete: 'SET NULL' });
26
+ };
27
+
28
+ return Appointment;
29
+ };
@@ -1,18 +1,22 @@
1
- const { DataTypes } = require('sequelize');
2
- const path = require("path")
3
- const { getSequelizeInstance } = require(path.join(__dirname, '..', 'config', 'db-init.js'));
4
- const sequelize = getSequelizeInstance();
5
- const Clinic = sequelize.define('Clinic', {
6
- id: {
7
- type: DataTypes.INTEGER,
8
- autoIncrement: true,
9
- primaryKey: true
10
- },
11
- name: { type: DataTypes.STRING, allowNull: false, unique: true },
12
- type: { type: DataTypes.ENUM('больница', 'поликлиника', 'станция скорой помощи', 'частный медицинский центр'), allowNull: false },
13
- address: { type: DataTypes.STRING, allowNull: false },
14
- phone: { type: DataTypes.STRING, allowNull: true }
15
- }, {
16
- timestamps: true
17
- });
18
- module.exports = Clinic;
1
+ module.exports = (sequelize, DataTypes) => {
2
+ const Clinic = sequelize.define('Clinic', {
3
+ id: {
4
+ type: DataTypes.INTEGER,
5
+ autoIncrement: true,
6
+ primaryKey: true
7
+ },
8
+ name: { type: DataTypes.STRING, allowNull: false, unique: true },
9
+ type: { type: DataTypes.ENUM('больница', 'поликлиника', 'станция скорой помощи', 'частный медицинский центр'), allowNull: false },
10
+ address: { type: DataTypes.STRING, allowNull: false },
11
+ phone: { type: DataTypes.STRING, allowNull: true }
12
+ }, {
13
+ timestamps: true
14
+ });
15
+
16
+ // Настройка связи прямо в таблице клиник
17
+ Clinic.associate = (models) => {
18
+ Clinic.hasMany(models.User, { foreignKey: 'ClinicId' });
19
+ };
20
+
21
+ return Clinic;
22
+ };
@@ -1,37 +1,19 @@
1
- const { DataTypes } = require('sequelize');
2
- const path = require("path");
3
- const { getSequelizeInstance } = require(path.join(__dirname, '..', 'config', 'db-init.js'));
4
- const sequelize = getSequelizeInstance();
5
- const Disease = sequelize.define('Disease', {
6
- id: {
7
- type: DataTypes.INTEGER,
8
- autoIncrement: true,
9
- primaryKey: true
10
- },
11
- name: {
12
- type: DataTypes.STRING,
13
- allowNull: false,
14
- unique: true
15
- },
16
- code: {
17
- type: DataTypes.STRING,
18
- allowNull: false,
19
- unique: true
20
- },
21
- treatmentRecommendations: {
22
- type: DataTypes.TEXT,
23
- allowNull: true
24
- },
25
- recommendedMedications: {
26
- type: DataTypes.TEXT,
27
- allowNull: true
28
- },
29
- casesCount: {
30
- type: DataTypes.INTEGER,
31
- defaultValue: 0,
32
- allowNull: false
33
- }
34
- }, {
35
- timestamps: true
36
- });
37
- module.exports = Disease;
1
+ module.exports = (sequelize, DataTypes) => {
2
+ const Disease = sequelize.define('Disease', {
3
+ id: {
4
+ type: DataTypes.INTEGER,
5
+ autoIncrement: true,
6
+ primaryKey: true
7
+ },
8
+ name: { type: DataTypes.STRING, allowNull: false, unique: true },
9
+ code: { type: DataTypes.STRING, allowNull: false, unique: true },
10
+ treatmentRecommendations: { type: DataTypes.TEXT, allowNull: true },
11
+ recommendedMedications: { type: DataTypes.TEXT, allowNull: true },
12
+ casesCount: { type: DataTypes.INTEGER, defaultValue: 0, allowNull: false }
13
+ }, {
14
+ timestamps: true
15
+ });
16
+
17
+ // У этой модели нет прямых исходящих связей (зависит от Appointment, но связь прописана там)
18
+ return Disease;
19
+ };
@@ -1,34 +1,22 @@
1
- const { DataTypes } = require('sequelize');
2
- const path = require("path")
3
- const { getSequelizeInstance } = require(path.join('..', 'config', 'db-init.js'));
4
- const sequelize = getSequelizeInstance();
5
- const UserAddress = sequelize.define('UserAddress', {
6
- id: {
7
- type: DataTypes.UUID,
8
- defaultValue: DataTypes.UUIDV4,
9
- primaryKey: true
10
- },
11
- city: {
12
- type: DataTypes.STRING,
13
- allowNull: false
14
- },
15
- street: {
16
- type: DataTypes.STRING,
17
- allowNull: false
18
- },
19
- houseNumber: {
20
- type: DataTypes.STRING,
21
- allowNull: false
22
- },
23
- assignedDistrict: {
24
- type: DataTypes.STRING,
25
- allowNull: true
26
- },
27
- assignedCabinet: {
28
- type: DataTypes.STRING,
29
- allowNull: true
30
- }
31
- }, {
32
- timestamps: true
33
- });
34
- module.exports = UserAddress;
1
+ module.exports = (sequelize, DataTypes) => {
2
+ const UserAddress = sequelize.define('UserAddress', {
3
+ id: {
4
+ type: DataTypes.UUID,
5
+ defaultValue: DataTypes.UUIDV4,
6
+ primaryKey: true
7
+ },
8
+ city: { type: DataTypes.STRING, allowNull: false },
9
+ street: { type: DataTypes.STRING, allowNull: false },
10
+ houseNumber: { type: DataTypes.STRING, allowNull: false },
11
+ assignedDistrict: { type: DataTypes.STRING, allowNull: true },
12
+ assignedCabinet: { type: DataTypes.STRING, allowNull: true }
13
+ }, {
14
+ timestamps: true
15
+ });
16
+
17
+ UserAddress.associate = (models) => {
18
+ UserAddress.belongsTo(models.User, { foreignKey: 'UserId', onDelete: 'CASCADE' });
19
+ };
20
+
21
+ return UserAddress;
22
+ };
@@ -1,31 +1,36 @@
1
- const { DataTypes } = require('sequelize');
2
- const path = require("path");
3
- const { getSequelizeInstance } = require(path.join(__dirname, '..', 'config', 'db-init.js'));
4
- const sequelize = getSequelizeInstance();
5
- const User = sequelize.define('User', {
6
- id: {
7
- type: DataTypes.INTEGER,
8
- autoIncrement: true,
9
- primaryKey: true
10
- },
11
- email: { type: DataTypes.STRING, allowNull: false, unique: true, validate: { isEmail: true } },
12
- password: { type: DataTypes.STRING, allowNull: false },
13
- fullName: { type: DataTypes.STRING, allowNull: false },
14
- role: { type: DataTypes.ENUM('patient', 'doctor', 'admin'), defaultValue: 'patient', allowNull: false },
15
- specialty: {
16
- type: DataTypes.ENUM(
17
- 'Терапевт', 'Педиатр', 'Врач общей практики', 'Кардиолог', 'Невролог',
18
- 'Эндокринолог', 'Гастроэнтеролог', 'Пульмонолог', 'Нефролог', 'Ревматолог',
19
- 'Гематолог', 'Хирург', 'Травматолог-ортопед', 'Сосудистый хирург', 'Нейрохирург',
20
- 'Кардиохирург', 'Пластический хирург', 'Офтальмолог', 'Отоларинголог', 'Дерматолог',
21
- 'Стоматолог', 'Гинеколог', 'Уролог', 'Онколог', 'Инфекционист', 'Аллерголог-иммунолог',
22
- 'Психиатр', 'Психотерапевт', 'Нарколог', 'Анестезиолог-реаниматолог', 'Рентгенолог',
23
- 'Врач УЗД', 'Эндоскопист', 'Патологоанатом'
24
- ),
25
- allowNull: true
26
- },
27
- isTemporaryPassword: { type: DataTypes.BOOLEAN, defaultValue: false }
28
- }, {
29
- timestamps: true
30
- });
31
- module.exports = User;
1
+ module.exports = (sequelize, DataTypes) => {
2
+ const User = sequelize.define('User', {
3
+ id: {
4
+ type: DataTypes.INTEGER,
5
+ autoIncrement: true,
6
+ primaryKey: true
7
+ },
8
+ email: { type: DataTypes.STRING, allowNull: false, unique: true, validate: { isEmail: true } },
9
+ password: { type: DataTypes.STRING, allowNull: false },
10
+ fullName: { type: DataTypes.STRING, allowNull: false },
11
+ role: { type: DataTypes.ENUM('patient', 'doctor', 'admin'), defaultValue: 'patient', allowNull: false },
12
+ specialty: {
13
+ type: DataTypes.ENUM(
14
+ 'Терапевт', 'Педиатр', 'Врач общей практики', 'Кардиолог', 'Невролог',
15
+ 'Эндокринолог', 'Гастроэнтеролог', 'Пульмонолог', 'Нефролог', 'Ревматолог',
16
+ 'Гематолог', 'Хирург', 'Травматолог-ортопед', 'Сосудистый хирург', 'Нейрохирург',
17
+ 'Кардиохирург', 'Пластический хирург', 'Офтальмолог', 'Отоларинголог', 'Дерматолог',
18
+ 'Стоматолог', 'Гинеколог', 'Уролог', 'Онколог', 'Инфекционист', 'Аллерголог-иммунолог',
19
+ 'Психиатр', 'Психотерапевт', 'Нарколог', 'Анестезиолог-реаниматолог', 'Рентгенолог',
20
+ 'Врач УЗД', 'Эндоскопист', 'Патологоанатом'
21
+ ),
22
+ allowNull: true
23
+ },
24
+ isTemporaryPassword: { type: DataTypes.BOOLEAN, defaultValue: false }
25
+ }, {
26
+ timestamps: true
27
+ });
28
+
29
+ // Настройка связей пользователя
30
+ User.associate = (models) => {
31
+ User.belongsTo(models.Clinic, { foreignKey: 'ClinicId', onDelete: 'SET NULL' });
32
+ User.hasOne(models.UserAddress, { foreignKey: 'UserId' });
33
+ };
34
+
35
+ return User;
36
+ };
package/index.js CHANGED
@@ -5,33 +5,43 @@ const chalk = require("chalk-palette");
5
5
  const logger = require("custom-color-logs").print;
6
6
  const path = require('path');
7
7
  const fs = require('fs');
8
- const { initDatabase } = require(path.join(__dirname, "database", "config", "db-init.js"));
8
+
9
+ // Оставляем только импорт самого модуля базы данных
10
+ const dbConfig = require(path.join(__dirname, "database", "config", "db-init.js"));
9
11
  const server = require('./server/server');
10
- const User = require(path.join(__dirname, "database", "models", "users.js"));
11
12
  const bcrypt = require('bcrypt');
13
+
12
14
  async function init(app) {
13
15
  await chalk.animation.textShimmer("MED-CMS");
14
16
  console.log(logger.ServerMedInfo("Запуск сервера..."));
15
17
  await chalk.animation.retroLines({duration:4000, linesCount:4, offset:3});
18
+
16
19
  if (app && typeof app === 'function' && typeof app.handle === 'function') {
17
20
  console.log(logger.ServerMedInfo("Проверка на app пройдена успешно!"));
18
21
  } else {
19
22
  console.log(logger.ServerMedError("app не передан или повреждён!"));
20
23
  return;
21
24
  }
25
+
22
26
  try {
23
27
  const dbPath = path.join(__dirname, "db", "med-cms.sqlite");
24
28
  const dbDir = path.dirname(dbPath);
25
29
  if (!fs.existsSync(dbDir)){
26
30
  fs.mkdirSync(dbDir, { extended: true });
27
31
  }
28
- await initDatabase(dbPath);
32
+
33
+ // Инициализируем БД
34
+ await dbConfig.initDatabase(dbPath);
29
35
  console.log(logger.ServerMedInfo("База данных успешно инициализирована"));
30
36
  } catch (err) {
31
37
  console.log(logger.DatabaseError("Критическая ошибка запуска базы данных med-cms:", err));
32
38
  return;
33
39
  }
40
+
34
41
  try {
42
+ // ИСПРАВЛЕНИЕ: Достаем User из проинициализированного объекта базы данных ОНЛАЙН
43
+ const User = dbConfig.User;
44
+
35
45
  const adminExists = await User.findOne({ where: { role: 'admin' } });
36
46
  if (!adminExists) {
37
47
  console.log(logger.ServerMedInfo("Администратор не найден. Создаю аккаунт по умолчанию..."));
@@ -44,19 +54,20 @@ async function init(app) {
44
54
  role: "admin",
45
55
  specialty: null
46
56
  });
47
- console.log(logger.ServerMedInfo(`Аккаунт суперадмина успешно создан в SQLite!`));
48
- }
49
- else {
57
+ console.log(logger.ServerMedInfo(`Аккаунт суперадмина успешно создан in SQLite!`));
58
+ } else {
50
59
  console.log(logger.ServerMedInfo("Администратор уже существует в базе данных."));
51
60
  }
52
61
  } catch (error) {
53
62
  console.log(logger.ServerMedError("Ошибка при автоматическом создании админа: " + error.message));
54
63
  }
64
+
55
65
  console.log(logger.ServerMedInfo("Подключение роутов..."));
56
66
  const rout = express.Router();
57
67
  const med = server(process.env.JWT_SECRET);
58
68
  const r_api = med.api;
59
69
  const r_web = med.web;
70
+
60
71
  rout.use(cookieParser());
61
72
  rout.use(express.json());
62
73
  rout.use(express.urlencoded({ extended: true }));
@@ -64,4 +75,5 @@ async function init(app) {
64
75
  rout.use("/med/api", r_api);
65
76
  app.use("/", rout);
66
77
  }
67
- module.exports = init;
78
+
79
+ module.exports = init;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "med-cms",
3
- "version": "1.0.0-beta.1",
3
+ "version": "1.0.0-beta.2",
4
4
  "description": "Медицинская мультитендантная CMS на Express и Sequelize (SQLite3) с системой ролей и автоматическим расчетом участков",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
package/server/server.js CHANGED
@@ -2,18 +2,26 @@ const express = require('express');
2
2
  const path = require('path');
3
3
  const bcrypt = require('bcrypt');
4
4
  const jwt = require('jsonwebtoken');
5
- const User = require(path.join(__dirname, '..', 'database', 'models', 'users'));
6
- const Disease = require(path.join(__dirname, '..', 'database', 'models', 'diseases'));
5
+
6
+ // Импортируем проинициализированный объект базы со всеми готовыми моделями
7
+ const db = require(path.join(__dirname, '..', 'database', 'config', 'db-init.js'));
8
+
7
9
  const createMiddleware = require(path.join(__dirname, 'midleware'));
8
10
  const { MED_PERMISSIONS } = require(path.join(__dirname, 'permissions'));
9
11
  const AdminService = require(path.join(__dirname, '..', 'database', 'services', 'admin'));
10
12
  const DoctorService = require(path.join(__dirname, '..', 'database', 'services', 'doctor'));
11
13
  const PatientService = require(path.join(__dirname, '..', 'database', 'services', 'patient'));
14
+
12
15
  function createMedRouter(jwtSecret) {
13
16
  const api = express.Router();
14
17
  const web = express.Router();
18
+
19
+ // Достаем готовые к работе модели из объекта db
20
+ const { User, Disease, Clinic } = db;
21
+
15
22
  web.use("/", express.static(path.join(__dirname, "..", "public")));
16
23
  const { verifyCookie, requireRole, checkPermission } = createMiddleware(jwtSecret);
24
+
17
25
  api.post('/auth/register', async (req, res) => {
18
26
  try {
19
27
  const { email, password, fullName } = req.body;
@@ -27,6 +35,7 @@ function createMedRouter(jwtSecret) {
27
35
  res.status(400).json({ success: false, error: err.message });
28
36
  }
29
37
  });
38
+
30
39
  api.post('/auth/login', async (req, res) => {
31
40
  try {
32
41
  const { email, password } = req.body;
@@ -50,13 +59,16 @@ function createMedRouter(jwtSecret) {
50
59
  res.status(500).json({ success: false, error: err.message });
51
60
  }
52
61
  });
62
+
53
63
  api.post('/auth/logout', (req, res) => {
54
64
  res.clearCookie('med_auth_token');
55
65
  res.json({ success: true, message: 'Успешный выход' });
56
66
  });
67
+
57
68
  web.get('/med-patient', verifyCookie, requireRole(['patient']), (req, res) => {
58
69
  res.sendFile(path.join(__dirname, '..', 'public', 'html', 'dashboard-patient.html'));
59
70
  });
71
+
60
72
  api.get('/patient/me', verifyCookie, async (req, res) => {
61
73
  try {
62
74
  const fullData = await PatientService.getFullMyData(req.user.id);
@@ -65,6 +77,7 @@ function createMedRouter(jwtSecret) {
65
77
  res.status(500).json({ success: false, error: err.message });
66
78
  }
67
79
  });
80
+
68
81
  api.post('/patient/address', verifyCookie, async (req, res) => {
69
82
  try {
70
83
  const updatedAddress = await PatientService.savePatientAddress(req.user.id, req.body);
@@ -73,9 +86,11 @@ function createMedRouter(jwtSecret) {
73
86
  res.status(400).json({ success: false, error: err.message });
74
87
  }
75
88
  });
89
+
76
90
  web.get('/med-doctor', verifyCookie, requireRole(['doctor']), (req, res) => {
77
91
  res.sendFile(path.join(__dirname, '..', 'public', 'html', 'dashboard-doctor.html'));
78
92
  });
93
+
79
94
  api.get('/doctor/schedule', verifyCookie, requireRole(['doctor']), async (req, res) => {
80
95
  try {
81
96
  const schedule = await DoctorService.getMySchedule(req.user.id);
@@ -84,6 +99,7 @@ function createMedRouter(jwtSecret) {
84
99
  res.status(500).json({ success: false, error: err.message });
85
100
  }
86
101
  });
102
+
87
103
  api.get('/doctor/patient-history/:id', verifyCookie, requireRole(['doctor']), async (req, res) => {
88
104
  try {
89
105
  const history = await DoctorService.getPatientHistory(req.params.id);
@@ -92,6 +108,7 @@ function createMedRouter(jwtSecret) {
92
108
  res.status(404).json({ success: false, error: err.message });
93
109
  }
94
110
  });
111
+
95
112
  api.post('/doctor/complete-examination', verifyCookie, requireRole(['doctor']), async (req, res) => {
96
113
  try {
97
114
  const result = await DoctorService.saveExamination(req.body);
@@ -100,12 +117,15 @@ function createMedRouter(jwtSecret) {
100
117
  res.status(400).json({ success: false, error: err.message });
101
118
  }
102
119
  });
120
+
103
121
  api.post('/doctor/schedule-surgery', verifyCookie, requireRole(['doctor']), checkPermission(MED_PERMISSIONS.SCHEDULE_SURGERY), (req, res) => {
104
122
  res.json({ success: true, message: 'Операция успешно добавлена в график хирургического отделения.' });
105
123
  });
124
+
106
125
  web.get('/med-admin', verifyCookie, requireRole(['admin']), (req, res) => {
107
126
  res.sendFile(path.join(__dirname, '..', 'public', 'html', 'dashboard-admin.html'));
108
127
  });
128
+
109
129
  api.post('/admin/clinics', verifyCookie, requireRole(['admin']), async (req, res) => {
110
130
  try {
111
131
  const newClinic = await AdminService.createClinic(req.body);
@@ -114,18 +134,19 @@ function createMedRouter(jwtSecret) {
114
134
  res.status(400).json({ success: false, error: err.message });
115
135
  }
116
136
  });
137
+
117
138
  api.post('/admin/staff', verifyCookie, requireRole(['admin']), async (req, res) => {
118
139
  try {
119
140
  if (req.body.clinicId && !req.body.ClinicId) {
120
141
  req.body.ClinicId = req.body.clinicId;
121
142
  }
122
-
123
143
  const staffUser = await AdminService.createStaffAccount(req.body);
124
144
  res.status(201).json({ success: true, data: staffUser });
125
145
  } catch (err) {
126
146
  res.status(400).json({ success: false, error: err.message });
127
147
  }
128
148
  });
149
+
129
150
  api.delete('/admin/users/:id', verifyCookie, requireRole(['admin']), async (req, res) => {
130
151
  try {
131
152
  const result = await AdminService.deleteAccount(req.params.id);
@@ -134,6 +155,7 @@ function createMedRouter(jwtSecret) {
134
155
  res.status(400).json({ success: false, error: err.message });
135
156
  }
136
157
  });
158
+
137
159
  api.get('/admin/stats', verifyCookie, requireRole(['admin']), async (req, res) => {
138
160
  try {
139
161
  const stats = await AdminService.getSystemStats();
@@ -142,35 +164,33 @@ function createMedRouter(jwtSecret) {
142
164
  res.status(500).json({ success: false, error: err.message });
143
165
  }
144
166
  });
167
+
145
168
  api.get('/admin/list-clinics', verifyCookie, requireRole(['admin']), async (req, res) => {
146
- try {
147
- const Clinic = require('../database/models/clinics');
148
- const clinics = await Clinic.findAll({ order: [['name', 'ASC']] });
149
- res.json({ success: true, data: clinics });
150
- } catch (err) {
151
- res.status(500).json({ success: false, error: err.message });
152
- }
153
- });
154
- api.get('/admin/list-staff', verifyCookie, requireRole(['admin']), async (req, res) => {
155
- try {
156
- const User = require('../database/models/users');
157
- const Clinic = require('../database/models/clinics');
158
- const staff = await User.findAll({
159
- where: { role: 'doctor' },
160
- include: [{ model: Clinic, attributes: ['name'] }],
161
- order: [['fullName', 'ASC']]
162
- });
163
- res.json({ success: true, data: staff });
164
- } catch (err) {
165
- res.status(500).json({ success: false, error: err.message });
166
- }
167
- });
168
- // 1. Получить список всех доступных врачей для записи (Публично для пациентов)
169
+ try {
170
+ // ИСПРАВЛЕНИЕ: берем Clinic из инстанса базы данных, а не сырой файл функции
171
+ const clinics = await Clinic.findAll({ order: [['name', 'ASC']] });
172
+ res.json({ success: true, data: clinics });
173
+ } catch (err) {
174
+ res.status(500).json({ success: false, error: err.message });
175
+ }
176
+ });
177
+
178
+ api.get('/admin/list-staff', verifyCookie, requireRole(['admin']), async (req, res) => {
179
+ try {
180
+ // ИСПРАВЛЕНИЕ: Используем проинициализированные модули для include отношений
181
+ const staff = await User.findAll({
182
+ where: { role: 'doctor' },
183
+ include: [{ model: Clinic, attributes: ['name'] }],
184
+ order: [['fullName', 'ASC']]
185
+ });
186
+ res.json({ success: true, data: staff });
187
+ } catch (err) {
188
+ res.status(500).json({ success: false, error: err.message });
189
+ }
190
+ });
191
+
169
192
  api.get('/patient/doctors-list', verifyCookie, requireRole(['patient']), async (req, res) => {
170
193
  try {
171
- const User = require('../database/models/users');
172
- const Clinic = require('../database/models/clinics');
173
-
174
194
  const doctors = await User.findAll({
175
195
  where: { role: 'doctor' },
176
196
  attributes: ['id', 'fullName', 'specialty'],
@@ -182,52 +202,45 @@ api.get('/admin/list-staff', verifyCookie, requireRole(['admin']), async (req, r
182
202
  }
183
203
  });
184
204
 
185
- // 2. Создать новую запись на прием (Бронирование талона)
186
205
  api.post('/patient/book', verifyCookie, requireRole(['patient']), async (req, res) => {
187
206
  try {
188
- // ID пациента безопасно берем из расшифрованной HttpOnly-куки (req.user.id)
189
207
  const patientId = req.user.id;
190
-
191
208
  const appointment = await PatientService.bookAppointment(patientId, req.body);
192
209
  res.status(212).json({ success: true, data: appointment });
193
210
  } catch (err) {
194
211
  res.status(400).json({ success: false, error: err.message });
195
212
  }
196
213
  });
197
- // ИСПРАВЛЕНИЕ: Добавляем 'admin' в requireRole, чтобы при тесте под админом справочник подгружался!
214
+
198
215
  api.get('/doctor/diseases-list', verifyCookie, requireRole(['doctor', 'admin']), async (req, res) => {
199
216
  try {
200
- const Disease = require('../database/models/diseases');
217
+ // ИСПРАВЛЕНИЕ: Используем готовую модель Disease
201
218
  const diseases = await Disease.findAll({ order: [['name', 'ASC']] });
202
219
  res.json({ success: true, data: diseases });
203
220
  } catch (err) {
204
221
  res.status(500).json({ success: false, error: err.message });
205
222
  }
206
223
  });
207
- // API Смены пароля для любого авторизованного пользователя (Врач, Пациент, Админ)
224
+
208
225
  api.post('/auth/change-password', verifyCookie, async (req, res) => {
209
226
  try {
210
227
  const { oldPassword, newPassword } = req.body;
211
- const userId = req.user.id; // Берем ID строго из HttpOnly куки
228
+ const userId = req.user.id;
212
229
 
213
230
  if (!oldPassword || !newPassword) {
214
231
  throw new Error('Необходимо указать старый и новый пароли');
215
232
  }
216
233
 
217
- // Ищем пользователя в базе
218
234
  const user = await User.findByPk(userId);
219
235
  if (!user) throw new Error('Пользователь не найден');
220
236
 
221
- // Сверяем старый пароль с хешем в БД
222
237
  const isMatch = await bcrypt.compare(oldPassword, user.password);
223
238
  if (!isMatch) {
224
239
  return res.status(400).json({ success: false, message: 'Старый пароль введен неверно' });
225
240
  }
226
241
 
227
- // Хешируем новый пароль перед записью в SQLite
228
242
  const hashedNewPassword = await bcrypt.hash(newPassword, 10);
229
243
 
230
- // Обновляем пароль и снимаем флаг временного пароля
231
244
  await user.update({
232
245
  password: hashedNewPassword,
233
246
  isTemporaryPassword: false
@@ -238,18 +251,14 @@ api.get('/admin/list-staff', verifyCookie, requireRole(['admin']), async (req, r
238
251
  }
239
252
  });
240
253
 
241
- // Обновим также роут выгрузки расписания, чтобы он отдавал данные о вошедшем враче для шапки кабинета
242
254
  api.get('/doctor/me', verifyCookie, requireRole(['doctor']), async (req, res) => {
243
255
  try {
244
256
  const doctorData = await User.findByPk(req.user.id, { attributes: ['fullName', 'position', 'specialty'] });
245
257
  res.json({ success: true, doctor: doctorData });
246
- } catch (err) {
247
- res.status(500).json({ success: false, error: err.message });
248
- }
249
- });
250
-
251
-
252
- return {web:web, api:api};
258
+ } catch (err) {
259
+ res.status(500).json({ success: false, error: err.message });
253
260
  }
254
-
255
- module.exports = createMedRouter;
261
+ });
262
+ return {web:web, api:api};
263
+ }
264
+ module.exports = createMedRouter;