med-cms 1.0.0-beta.2 → 1.0.0

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.
Files changed (49) hide show
  1. package/LICENSE.txt +13 -0
  2. package/README.md +117 -0
  3. package/database/config/db-init.js +67 -53
  4. package/database/models/appointments.js +35 -20
  5. package/database/models/clinics.js +25 -13
  6. package/database/models/diseases.js +21 -13
  7. package/database/models/districts.js +55 -0
  8. package/database/models/news.js +25 -0
  9. package/database/models/user-addresses.js +21 -14
  10. package/database/models/users.js +69 -26
  11. package/database/services/admin/create-clinic.js +18 -6
  12. package/database/services/admin/db-analytics.js +42 -16
  13. package/database/services/admin/index.js +6 -6
  14. package/database/services/admin/manage-staff.js +47 -17
  15. package/database/services/doctor/conduct-exam.js +26 -20
  16. package/database/services/doctor/get-schedule.js +26 -18
  17. package/database/services/doctor/index.js +6 -6
  18. package/database/services/doctor/patient-access.js +34 -25
  19. package/database/services/patient/book-appointment.js +37 -17
  20. package/database/services/patient/calc-district.js +70 -21
  21. package/database/services/patient/get-profile.js +49 -21
  22. package/database/services/patient/index.js +5 -5
  23. package/env_init.js +55 -0
  24. package/index.d.ts +38 -24
  25. package/index.js +48 -25
  26. package/migrate-diseases.js +192 -120
  27. package/package.json +3 -3
  28. package/public/css/admin.css +80 -80
  29. package/public/css/auth.css +52 -44
  30. package/public/css/doctor.css +131 -125
  31. package/public/css/home.css +155 -0
  32. package/public/html/dashboard-admin.html +423 -101
  33. package/public/html/dashboard-doctor.html +155 -66
  34. package/public/html/dashboard-patient.html +199 -94
  35. package/public/html/home/about.html +0 -0
  36. package/public/html/home/contact.html +0 -0
  37. package/public/html/home/info.html +0 -0
  38. package/public/html/home.html +159 -0
  39. package/public/html/login.html +73 -43
  40. package/public/js/admin.js +329 -165
  41. package/public/js/auth.js +45 -41
  42. package/public/js/doctor.js +117 -103
  43. package/public/js/home.js +113 -0
  44. package/public/js/patient.js +130 -108
  45. package/server/midleware.js +33 -13
  46. package/server/permissions.js +105 -43
  47. package/server/server.js +517 -176
  48. package/README +0 -161
  49. package/test.js +0 -27
@@ -1,49 +1,98 @@
1
1
  const path = require("path");
2
- const UserAddress = require(path.join(__dirname, '..', '..', 'models', 'user-addresses'));
2
+ // Импортируем строго синглтон базы данных для исключения круговой зависимости
3
+ const dbConfig = require(
4
+ path.join(__dirname, "..", "..", "config", "db-init.js"),
5
+ );
6
+
3
7
  const calcDistrict = {
4
- determineLocation(street, houseNumber) {
8
+ /**
9
+ * Динамический поиск участка по названию улицы в таблице настроек админа
10
+ */
11
+ async determineLocation(clinicId, street) {
5
12
  const cleanedStreet = street.trim().toLowerCase();
6
- if (cleanedStreet.includes('ленина') || cleanedStreet.includes('центральная')) {
13
+
14
+ // Достаем проинициализированную модель Участков онлайн
15
+ const District = dbConfig.models.District || dbConfig.District;
16
+
17
+ if (!District) {
18
+ console.log(
19
+ "⚠️ [Med Module] Модель District еще не инициализирована в БД",
20
+ );
7
21
  return {
8
- assignedDistrict: 'Участок №1 (Центральный)',
9
- assignedCabinet: 'Кабинет №102 (Терапевт / Педиатр)'
22
+ assignedDistrict: "Общий распределительный участок",
23
+ assignedCabinet: "Кабинет №100 (Регистратура)",
10
24
  };
11
25
  }
12
- if (cleanedStreet.includes('пушкина') || cleanedStreet.includes('мичурина')) {
13
- return {
14
- assignedDistrict: 'Участок №2 (Северный)',
15
- assignedCabinet: 'Кабинет №205 (Терапевт / Педиатр)'
16
- };
26
+
27
+ // Ищем все участки, закрепленные за данной клиникой в SQLite
28
+ const districts = await District.findAll({ where: { ClinicId: clinicId } });
29
+
30
+ // Проверяем, в какой из участков админ записал эту улицу
31
+ for (const district of districts) {
32
+ const streetsArray = district.coveredStreets
33
+ .split(",")
34
+ .map((s) => s.trim());
35
+
36
+ if (streetsArray.includes(cleanedStreet)) {
37
+ return {
38
+ assignedDistrict: district.name,
39
+ assignedCabinet: `${district.cabinet} (Врач: ${district.doctorName || "Не назначен"})`,
40
+ };
41
+ }
17
42
  }
43
+
44
+ // Дефолтный вариант, если админ еще не добавил эту улицу в карту покрытия
18
45
  return {
19
- assignedDistrict: 'Участок №3 (Общий)',
20
- assignedCabinet: 'Кабинет №301 (Врач общей практики)'
46
+ assignedDistrict: "Общий распределительный участок",
47
+ assignedCabinet: "Кабинет №100 (Регистратура)",
21
48
  };
22
49
  },
50
+
51
+ /**
52
+ * Привязка адреса к пациенту с динамическим расчетом участка
53
+ */
23
54
  async saveAddress(userId, addressData) {
24
- const { city, street, houseNumber } = addressData;
25
- if (!city || !street || !houseNumber) {
26
- throw new Error('Для привязки адреса необходимы город, улица и номер дома');
55
+ const { city, street, houseNumber, clinicId } = addressData;
56
+
57
+ // Достаем модель адресов из базы
58
+ const UserAddress = dbConfig.models.UserAddress || dbConfig.UserAddress;
59
+
60
+ if (!city || !street || !houseNumber || !clinicId) {
61
+ throw new Error(
62
+ "Заполните обязательные поля: город, улица, дом, ID клиники прикрепления",
63
+ );
27
64
  }
28
- const { assignedDistrict, assignedCabinet } = this.determineLocation(street, houseNumber);
29
- const existingAddress = await UserAddress.findOne({ where: { UserId: userId } });
65
+
66
+ // Вычисляем лечебный участок по динамической карте улиц из базы данных
67
+ const { assignedDistrict, assignedCabinet } = await this.determineLocation(
68
+ clinicId,
69
+ street,
70
+ );
71
+
72
+ // Проверяем, обновляет ли пациент старый адрес или создает новый
73
+ const existingAddress = await UserAddress.findOne({
74
+ where: { UserId: userId },
75
+ });
76
+
30
77
  if (existingAddress) {
31
78
  return await existingAddress.update({
32
79
  city,
33
80
  street,
34
81
  houseNumber,
35
82
  assignedDistrict,
36
- assignedCabinet
83
+ assignedCabinet,
37
84
  });
38
85
  }
86
+
39
87
  return await UserAddress.create({
40
88
  UserId: userId,
41
89
  city,
42
90
  street,
43
91
  houseNumber,
44
92
  assignedDistrict,
45
- assignedCabinet
93
+ assignedCabinet,
46
94
  });
47
- }
95
+ },
48
96
  };
49
- module.exports = calcDistrict;
97
+
98
+ module.exports = calcDistrict;
@@ -1,47 +1,75 @@
1
- const path = require("path")
2
- const User = require(path.join(__dirname, '..', '..', 'models', 'users'));
3
- const UserAddress = require(path.join(__dirname, '..', '..', 'models', 'user-addresses'));
4
- const Appointment = require(path.join(__dirname, '..', '..', 'models', 'appointments'));
5
- const Disease = require(path.join(__dirname, '..', '..', 'models', 'diseases'));
1
+ const path = require("path");
2
+ // Импортируем синглтон конфигурации базы данных
3
+ const dbConfig = require(
4
+ path.join(__dirname, "..", "..", "config", "db-init.js"),
5
+ );
6
+
6
7
  const getProfile = {
8
+ /**
9
+ * Собрать полное медицинское досье пациента для личного кабинета
10
+ */
7
11
  async getFullData(patientId) {
8
12
  if (!patientId) {
9
- throw new Error('ID пациента не указан');
13
+ throw new Error("ID пациента не указан");
10
14
  }
15
+
16
+ // Безопасно достаем проинициализированные модели
17
+ const User = dbConfig.models.User || dbConfig.User;
18
+ const UserAddress = dbConfig.models.UserAddress || dbConfig.UserAddress;
19
+ const Appointment = dbConfig.models.Appointment || dbConfig.Appointment;
20
+ const Disease = dbConfig.models.Disease || dbConfig.Disease;
21
+
22
+ // 1. Ищем профиль пользователя с адресом
11
23
  const profile = await User.findByPk(patientId, {
12
- attributes: { exclude: ['password'] },
24
+ attributes: { exclude: ["password"] },
13
25
  include: [
14
26
  {
15
27
  model: UserAddress,
16
- attributes: ['city', 'street', 'houseNumber', 'assignedDistrict', 'assignedCabinet']
17
- }
18
- ]
28
+ attributes: [
29
+ "city",
30
+ "street",
31
+ "houseNumber",
32
+ "assignedDistrict",
33
+ "assignedCabinet",
34
+ ],
35
+ },
36
+ ],
19
37
  });
38
+
20
39
  if (!profile) {
21
- throw new Error('Профиль пациента не найден в базе данных');
40
+ throw new Error("Профиль пациента не найден в базе данных");
22
41
  }
42
+
43
+ // 2. Находим всю историю приёмов с врачами и диагнозами (используем правильный alias PatientAppointments)
23
44
  const appointments = await Appointment.findAll({
24
45
  where: { PatientId: patientId },
25
46
  include: [
26
47
  {
27
48
  model: User,
28
- as: 'Doctor',
29
- attributes: ['fullName', 'specialty']
49
+ as: "Doctor",
50
+ attributes: ["fullName", "specialty", "position"],
30
51
  },
31
52
  {
32
53
  model: Disease,
33
- attributes: ['name', 'code', 'treatmentRecommendations', 'recommendedMedications']
34
- }
54
+ attributes: [
55
+ "name",
56
+ "code",
57
+ "treatmentRecommendations",
58
+ "recommendedMedications",
59
+ ],
60
+ },
35
61
  ],
36
62
  order: [
37
- ['date', 'DESC'],
38
- ['time', 'DESC']
39
- ]
63
+ ["date", "DESC"],
64
+ ["time", "DESC"],
65
+ ],
40
66
  });
67
+
41
68
  return {
42
69
  personalInfo: profile,
43
- medicalHistory: appointments
70
+ medicalHistory: appointments,
44
71
  };
45
- }
72
+ },
46
73
  };
47
- module.exports = getProfile;
74
+
75
+ module.exports = getProfile;
@@ -1,10 +1,10 @@
1
1
  const path = require("path");
2
- const calcDistrict = require(path.join(__dirname, 'calc-district'));
3
- const getProfile = require(path.join(__dirname, 'get-profile'));
4
- const bookAppointment = require(path.join(__dirname, 'book-appointment'));
2
+ const calcDistrict = require(path.join(__dirname, "calc-district"));
3
+ const getProfile = require(path.join(__dirname, "get-profile"));
4
+ const bookAppointment = require(path.join(__dirname, "book-appointment"));
5
5
  const PatientService = {
6
6
  savePatientAddress: calcDistrict.saveAddress.bind(calcDistrict),
7
7
  getFullMyData: getProfile.getFullData,
8
- bookAppointment: bookAppointment.create.bind(bookAppointment)
8
+ bookAppointment: bookAppointment.create.bind(bookAppointment),
9
9
  };
10
- module.exports = PatientService;
10
+ module.exports = PatientService;
package/env_init.js ADDED
@@ -0,0 +1,55 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const process = require("process");
4
+ const envPath = path.resolve(process.cwd(), ".env");
5
+ const logger = require("custom-color-logs").print;
6
+ const defaultEnvContent = `
7
+
8
+
9
+ # ==============================================================================
10
+ # MED-CMS (v1.0.0)
11
+ # ==============================================================================
12
+
13
+ # Специяльный тег инициализации (значение не меняйте)
14
+ MED_CMS_INITIALIZED = true
15
+
16
+
17
+ # Код который будет хешироваться в куки
18
+ JWT_SECRET = 0000
19
+
20
+
21
+ # Данные админа
22
+ # Почта админестратора
23
+ ADMIN_GMAIL = x@gmail.com
24
+
25
+ # Телефон админестратора
26
+ ADMIN_PHONE = +375 00 00000000
27
+
28
+ # Пароль аккаунта админестратора
29
+ ADMIN_PASSWORD = 1234
30
+ `;
31
+
32
+ function initializeEnvironment() {
33
+ try {
34
+ if (!fs.existsSync(envPath)) {
35
+ fs.writeFileSync(envPath, defaultEnvContent, "utf8");
36
+ } else {
37
+ const currentEnv = fs.readFileSync(envPath, "utf8");
38
+ if (currentEnv.includes("MED_CMS_INITIALIZED")) {
39
+ require("dotenv").config({ path: envPath });
40
+ return;
41
+ }
42
+ fs.appendFileSync(envPath, `\n\n${defaultEnvContent}`, "utf8");
43
+ }
44
+ } catch (err) {
45
+ console.log(
46
+ logger.ServerMedError(
47
+ "Не удалось проверить или обновить .env:",
48
+ err.message,
49
+ ),
50
+ );
51
+ }
52
+ require("dotenv").config({ path: envPath });
53
+ }
54
+ initializeEnvironment();
55
+ module.exports = { envPath };
package/index.d.ts CHANGED
@@ -1,25 +1,39 @@
1
- import { Router } from 'express';
2
-
3
- export interface MedCmsConfig {
4
- jwtSecret: string;
5
- dbPath?: string;
6
- }
7
-
1
+ import { Express } from "express";
8
2
  export type DoctorSpecialty =
9
-
10
- | 'Терапевт' | 'Педиатр' | 'Врач общей практики' | 'Кардиолог' | 'Невролог'
11
- | 'Эндокринолог' | 'Гастроэнтеролог' | 'Пульмонолог' | 'Нефролог' | 'Ревматолог'
12
- | 'Гематолог' | 'Хирург' | 'Травматолог-ортопед' | 'Сосудистый хирург' | 'Нейрохирург'
13
- | 'Кардиохирург' | 'Пластический хирург' | 'Офтальмолог' | 'Отоларинголог' | 'Дерматолог'
14
- | 'Стоматолог' | 'Гинеколог' | 'Уролог' | 'Онколог' | 'Инфекционист' | 'Аллерголог-иммунолог'
15
- | 'Психиатр' | 'Психотерапевт' | 'Нарколог' | 'Анестезиолог-реаниматолог' | 'Рентгенолог'
16
- | 'Врач УЗД' | 'Эндоскопист' | 'Патологоанатом';
17
-
18
- export type UserRole = 'patient' | 'doctor' | 'admin';
19
-
20
- /**
21
- * Инициализирует модуль med-cms и возвращает Express Router
22
- */
23
- declare function initMedCms(config: MedCmsConfig): Router;
24
-
25
- export default initMedCms;
3
+ | "Терапевт"
4
+ | "Педиатр"
5
+ | "Врач общей практики"
6
+ | "Кардиолог"
7
+ | "Невролог"
8
+ | "Эндокринолог"
9
+ | "Гастроэнтеролог"
10
+ | "Пульмонолог"
11
+ | "Нефролог"
12
+ | "Ревматолог"
13
+ | "Гематолог"
14
+ | "Хирург"
15
+ | "Травматолог-ортопед"
16
+ | "Сосудистый хирург"
17
+ | "Нейрохирург"
18
+ | "Кардиохирург"
19
+ | "Пластический хирург"
20
+ | "Офтальмолог"
21
+ | "Отоларинголог"
22
+ | "Дерматолог"
23
+ | "Стоматолог"
24
+ | "Гинеколог"
25
+ | "Уролог"
26
+ | "Онколог"
27
+ | "Инфекционист"
28
+ | "Аллерголог-иммунолог"
29
+ | "Психиатр"
30
+ | "Психотерапевт"
31
+ | "Нарколог"
32
+ | "Анестезиолог-реаниматолог"
33
+ | "Рентгенолог"
34
+ | "Врач УЗД"
35
+ | "Эндоскопист"
36
+ | "Патологоанатом";
37
+ export type UserRole = "patient" | "doctor" | "admin";
38
+ declare function init(app: Express): void;
39
+ export default init;
package/index.js CHANGED
@@ -1,22 +1,28 @@
1
- require("dotenv").config({debug:false});
2
- const express = require('express');
3
- const cookieParser = require('cookie-parser');
1
+ require("dotenv").config({ debug: false });
2
+ const express = require("express");
3
+ const cookieParser = require("cookie-parser");
4
4
  const chalk = require("chalk-palette");
5
5
  const logger = require("custom-color-logs").print;
6
- const path = require('path');
7
- const fs = require('fs');
8
-
6
+ const path = require("path");
7
+ const fs = require("fs");
8
+ require(path.join(__dirname, "env_init.js"));
9
9
  // Оставляем только импорт самого модуля базы данных
10
- const dbConfig = require(path.join(__dirname, "database", "config", "db-init.js"));
11
- const server = require('./server/server');
12
- const bcrypt = require('bcrypt');
10
+ const dbConfig = require(
11
+ path.join(__dirname, "database", "config", "db-init.js"),
12
+ );
13
+ const server = require("./server/server");
14
+ const bcrypt = require("bcrypt");
13
15
 
14
16
  async function init(app) {
15
17
  await chalk.animation.textShimmer("MED-CMS");
16
18
  console.log(logger.ServerMedInfo("Запуск сервера..."));
17
- await chalk.animation.retroLines({duration:4000, linesCount:4, offset:3});
19
+ await chalk.animation.retroLines({
20
+ duration: 4000,
21
+ linesCount: 4,
22
+ offset: 3,
23
+ });
18
24
 
19
- if (app && typeof app === 'function' && typeof app.handle === 'function') {
25
+ if (app && typeof app === "function" && typeof app.handle === "function") {
20
26
  console.log(logger.ServerMedInfo("Проверка на app пройдена успешно!"));
21
27
  } else {
22
28
  console.log(logger.ServerMedError("app не передан или повреждён!"));
@@ -26,40 +32,57 @@ async function init(app) {
26
32
  try {
27
33
  const dbPath = path.join(__dirname, "db", "med-cms.sqlite");
28
34
  const dbDir = path.dirname(dbPath);
29
- if (!fs.existsSync(dbDir)){
30
- fs.mkdirSync(dbDir, { extended: true });
35
+ if (!fs.existsSync(dbDir)) {
36
+ fs.mkdirSync(dbDir, { extended: true });
31
37
  }
32
-
38
+
33
39
  // Инициализируем БД
34
40
  await dbConfig.initDatabase(dbPath);
35
41
  console.log(logger.ServerMedInfo("База данных успешно инициализирована"));
36
42
  } catch (err) {
37
- console.log(logger.DatabaseError("Критическая ошибка запуска базы данных med-cms:", err));
43
+ console.log(
44
+ logger.DatabaseError(
45
+ "Критическая ошибка запуска базы данных med-cms:",
46
+ err,
47
+ ),
48
+ );
38
49
  return;
39
50
  }
40
51
 
41
52
  try {
42
53
  // ИСПРАВЛЕНИЕ: Достаем User из проинициализированного объекта базы данных ОНЛАЙН
43
- const User = dbConfig.User;
54
+ const User = dbConfig.User;
44
55
 
45
- const adminExists = await User.findOne({ where: { role: 'admin' } });
56
+ const adminExists = await User.findOne({ where: { role: "admin" } });
46
57
  if (!adminExists) {
47
- console.log(logger.ServerMedInfo("Администратор не найден. Создаю аккаунт по умолчанию..."));
48
- const adminPassword = "SuperAdminPassword2026";
58
+ console.log(
59
+ logger.ServerMedInfo(
60
+ "Администратор не найден. Создание аккаунта по умолчанию...",
61
+ ),
62
+ );
63
+ const adminPassword = process.env.ADMIN_PASSWORD;
49
64
  const hashedPassword = await bcrypt.hash(adminPassword, 10);
50
65
  await User.create({
51
- email: "admin@medcms.ru",
66
+ email: process.env.ADMIN_GMAIL,
52
67
  password: hashedPassword,
53
68
  fullName: "Главный Администратор",
54
- role: "admin",
55
- specialty: null
69
+ role: "admin",
70
+ specialty: null,
56
71
  });
57
- console.log(logger.ServerMedInfo(`Аккаунт суперадмина успешно создан in SQLite!`));
72
+ console.log(
73
+ logger.ServerMedInfo(`Аккаунт суперадмина успешно создан in SQLite.`),
74
+ );
58
75
  } else {
59
- console.log(logger.ServerMedInfo("Администратор уже существует в базе данных."));
76
+ console.log(
77
+ logger.ServerMedInfo("Администратор уже существует в базе данных."),
78
+ );
60
79
  }
61
80
  } catch (error) {
62
- console.log(logger.ServerMedError("Ошибка при автоматическом создании админа: " + error.message));
81
+ console.log(
82
+ logger.ServerMedError(
83
+ "Ошибка при автоматическом создании админа: " + error.message,
84
+ ),
85
+ );
63
86
  }
64
87
 
65
88
  console.log(logger.ServerMedInfo("Подключение роутов..."));