med-cms 1.1.0 → 1.2.1
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.
- package/README.md +2 -1
- package/check_env.js +39 -0
- package/env_init.js +5 -5
- package/index.js +3 -0
- package/package.json +15 -3
- package/public/js/admin.js +55 -12
- package/public/js/auth.js +8 -2
- package/public/js/doctor.js +29 -24
- package/public/js/home.js +11 -37
- package/public/js/patient.js +25 -17
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# MED-CMS (v1.
|
|
1
|
+
# MED-CMS (v1.2.0)
|
|
2
2
|
|
|
3
3
|
> ## `med-cms` — Это автономный медецинский движок для управления сетями клиник, персоналом и расписание приймов и т.д. Написан `med-cms` на node.js с использованием **express**, **sequelize**
|
|
4
4
|
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
- **Статистика болезней и МКБ-10:** Выделенный независимый справочник заболеваний в базе данных с возможностью автономной миграции и автоматическим инкрементом счетчиков заболеваемости при закрытии приемов врачами.
|
|
20
20
|
- **Возможность управлять новостями** А админ панеле есть форма позволяющая выкладывать новости.
|
|
21
21
|
- **Автозапись в `.env`** При запуске проекта код проверяет есть ли `.env` и настройки в нём, если то записывает настройки в него.
|
|
22
|
+
- **Автопроверка значений** Если вы не изменили значение в `.env` с по умолчанию то ваша консль будет заспамлена
|
|
22
23
|
|
|
23
24
|
---
|
|
24
25
|
|
package/check_env.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const print = require("custom-color-logs").print;
|
|
2
|
+
|
|
3
|
+
(function enforceEnvSecurity() {
|
|
4
|
+
const isDefaultJWT = process.env.JWT_SECRET === "0000";
|
|
5
|
+
const isDefaultPass = process.env.ADMIN_PASSWORD === "1234";
|
|
6
|
+
|
|
7
|
+
if (isDefaultJWT || isDefaultPass) {
|
|
8
|
+
// Красивое стартовое предупреждение
|
|
9
|
+
console.log(
|
|
10
|
+
print.ServerMedSeqError(
|
|
11
|
+
"======================================================",
|
|
12
|
+
),
|
|
13
|
+
);
|
|
14
|
+
console.log(print.ServerMedSeqWarn("ОБНАРУЖЕНЫ НАСТРОЙКИ ПО УМОЛЧАНИЮ!"));
|
|
15
|
+
console.log(
|
|
16
|
+
print.ServerMedSeqWarn(
|
|
17
|
+
"Безопасность системы под угрозой. Данные пациентов уязвимы.",
|
|
18
|
+
),
|
|
19
|
+
);
|
|
20
|
+
console.log(
|
|
21
|
+
print.ServerMedSeqInfo(
|
|
22
|
+
"Пожалуйста, измените значения JWT_SECRET и ADMIN_PASSWORD в .env",
|
|
23
|
+
),
|
|
24
|
+
);
|
|
25
|
+
console.log(
|
|
26
|
+
print.ServerMedSeqError(
|
|
27
|
+
"======================================================",
|
|
28
|
+
),
|
|
29
|
+
);
|
|
30
|
+
const alertInterval = setInterval(() => {
|
|
31
|
+
console.log(
|
|
32
|
+
print.ServerMedSeqError(
|
|
33
|
+
"СРОЧНО СМЕНИТЕ ДЕФОЛТНЫЕ ДАННЫЕ В .env ДЛЯ БЕЗОПАСНОСТИ!",
|
|
34
|
+
),
|
|
35
|
+
);
|
|
36
|
+
}, 4000);
|
|
37
|
+
alertInterval.unref();
|
|
38
|
+
}
|
|
39
|
+
})();
|
package/env_init.js
CHANGED
|
@@ -7,7 +7,7 @@ const defaultEnvContent = `
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
# ==============================================================================
|
|
10
|
-
# MED-CMS (v1.
|
|
10
|
+
# MED-CMS (v1.2.0)
|
|
11
11
|
# ==============================================================================
|
|
12
12
|
|
|
13
13
|
# Специяльный тег инициализации (значение не меняйте)
|
|
@@ -15,18 +15,18 @@ MED_CMS_INITIALIZED = true
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
# Код который будет хешироваться в куки
|
|
18
|
-
JWT_SECRET = 0000
|
|
18
|
+
JWT_SECRET = "0000"
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
# Данные админа
|
|
22
22
|
# Почта админестратора
|
|
23
|
-
ADMIN_GMAIL = x@gmail.com
|
|
23
|
+
ADMIN_GMAIL = "x@gmail.com"
|
|
24
24
|
|
|
25
25
|
# Телефон админестратора
|
|
26
|
-
ADMIN_PHONE = +375 00 00000000
|
|
26
|
+
ADMIN_PHONE = "+375 00 00000000"
|
|
27
27
|
|
|
28
28
|
# Пароль аккаунта админестратора
|
|
29
|
-
ADMIN_PASSWORD = 1234
|
|
29
|
+
ADMIN_PASSWORD = "1234"
|
|
30
30
|
`;
|
|
31
31
|
|
|
32
32
|
function initializeEnvironment() {
|
package/index.js
CHANGED
|
@@ -6,6 +6,7 @@ const logger = require("custom-color-logs").print;
|
|
|
6
6
|
const path = require("path");
|
|
7
7
|
const fs = require("fs");
|
|
8
8
|
require(path.join(__dirname, "env_init.js"));
|
|
9
|
+
|
|
9
10
|
// Оставляем только импорт самого модуля базы данных
|
|
10
11
|
const dbConfig = require(
|
|
11
12
|
path.join(__dirname, "database", "config", "db-init.js"),
|
|
@@ -97,6 +98,8 @@ async function init(app) {
|
|
|
97
98
|
rout.use("/med", r_web);
|
|
98
99
|
rout.use("/med/api", r_api);
|
|
99
100
|
app.use("/", rout);
|
|
101
|
+
console.log(logger.ServerMedInfo("Роуты подключены"));
|
|
102
|
+
require(path.join(__dirname, "check_env.js"));
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
module.exports = init;
|
package/package.json
CHANGED
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "med-cms",
|
|
3
|
-
"version": "1.1
|
|
4
|
-
"description": "Медицинская мультитендантная CMS на Express и Sequelize (SQLite3) с системой ролей и автоматическим расчетом
|
|
3
|
+
"version": "1.2.1",
|
|
4
|
+
"description": "Медицинская мультитендантная CMS на Express и Sequelize (SQLite3) с системой ролей и автоматическим расчетом участков, а также защитой данных",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"test": "node test.js"
|
|
9
9
|
},
|
|
10
10
|
"keywords": [
|
|
11
|
-
"
|
|
11
|
+
"med-cms",
|
|
12
12
|
"cms",
|
|
13
|
+
"medical-engine",
|
|
14
|
+
"clinic-management",
|
|
15
|
+
"hospital-system",
|
|
16
|
+
"arm-doctor",
|
|
17
|
+
"patient-cabinet",
|
|
18
|
+
"miz-by",
|
|
19
|
+
"mkb-10",
|
|
20
|
+
"express-cms",
|
|
21
|
+
"sequelize-cms",
|
|
22
|
+
"sqlite3-autonomous",
|
|
23
|
+
"autonomous-engine",
|
|
24
|
+
"medical",
|
|
13
25
|
"express",
|
|
14
26
|
"sequelize",
|
|
15
27
|
"sqlite3",
|
package/public/js/admin.js
CHANGED
|
@@ -11,7 +11,11 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
11
11
|
// Базовая статистика верхних карточек
|
|
12
12
|
async function loadDashboardStats() {
|
|
13
13
|
try {
|
|
14
|
-
const response = await fetch("/med/api/admin/stats"
|
|
14
|
+
const response = await fetch("/med/api/admin/stats", {
|
|
15
|
+
headers: {
|
|
16
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
17
|
+
},
|
|
18
|
+
});
|
|
15
19
|
const result = await response.json();
|
|
16
20
|
if (!result.success) {
|
|
17
21
|
alert("Ошибка доступа к админ-панели: " + result.message);
|
|
@@ -32,7 +36,11 @@ async function loadDashboardStats() {
|
|
|
32
36
|
// Загрузка клиник в форму №2 (Создание сотрудника)
|
|
33
37
|
async function loadClinicsDropdown() {
|
|
34
38
|
try {
|
|
35
|
-
const response = await fetch("/med/api/admin/list-clinics"
|
|
39
|
+
const response = await fetch("/med/api/admin/list-clinics", {
|
|
40
|
+
headers: {
|
|
41
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
42
|
+
},
|
|
43
|
+
});
|
|
36
44
|
const result = await response.json();
|
|
37
45
|
const select = document.getElementById("staffClinic");
|
|
38
46
|
if (!select) return;
|
|
@@ -56,7 +64,11 @@ async function loadClinicsForDistrictsSelect() {
|
|
|
56
64
|
const select = document.getElementById("distClinicId");
|
|
57
65
|
if (!select) return;
|
|
58
66
|
try {
|
|
59
|
-
const response = await fetch("/med/api/admin/list-clinics"
|
|
67
|
+
const response = await fetch("/med/api/admin/list-clinics", {
|
|
68
|
+
headers: {
|
|
69
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
70
|
+
},
|
|
71
|
+
});
|
|
60
72
|
const result = await response.json();
|
|
61
73
|
select.innerHTML =
|
|
62
74
|
'<option value="">-- Выберите медицинское учреждение --</option>';
|
|
@@ -81,7 +93,11 @@ async function loadStaffForDistrictsSelect() {
|
|
|
81
93
|
const select = document.getElementById("distDoctor");
|
|
82
94
|
if (!select) return;
|
|
83
95
|
try {
|
|
84
|
-
const response = await fetch("/med/api/admin/list-staff"
|
|
96
|
+
const response = await fetch("/med/api/admin/list-staff", {
|
|
97
|
+
headers: {
|
|
98
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
99
|
+
},
|
|
100
|
+
});
|
|
85
101
|
const result = await response.json();
|
|
86
102
|
select.innerHTML =
|
|
87
103
|
'<option value="">-- Выберите сотрудника из базы данных --</option>';
|
|
@@ -110,7 +126,11 @@ async function renderLiveDistrictsTable() {
|
|
|
110
126
|
const container = document.getElementById("liveDistrictsTableBox");
|
|
111
127
|
if (!container) return;
|
|
112
128
|
try {
|
|
113
|
-
const res = await fetch("/med/api/admin/list-districts"
|
|
129
|
+
const res = await fetch("/med/api/admin/list-districts", {
|
|
130
|
+
headers: {
|
|
131
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
132
|
+
},
|
|
133
|
+
});
|
|
114
134
|
const result = await res.json();
|
|
115
135
|
if (result.success && result.data && result.data.length > 0) {
|
|
116
136
|
let html = `
|
|
@@ -168,6 +188,7 @@ document.getElementById("clinicForm").addEventListener("submit", async (e) => {
|
|
|
168
188
|
method: "POST",
|
|
169
189
|
headers: {
|
|
170
190
|
"Content-Type": "application/json",
|
|
191
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
171
192
|
},
|
|
172
193
|
body: JSON.stringify(payload),
|
|
173
194
|
});
|
|
@@ -209,7 +230,10 @@ document.getElementById("staffForm").addEventListener("submit", async (e) => {
|
|
|
209
230
|
try {
|
|
210
231
|
const response = await fetch("/med/api/admin/staff", {
|
|
211
232
|
method: "POST",
|
|
212
|
-
headers: {
|
|
233
|
+
headers: {
|
|
234
|
+
"Content-Type": "application/json",
|
|
235
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
236
|
+
},
|
|
213
237
|
body: JSON.stringify(payload),
|
|
214
238
|
});
|
|
215
239
|
const result = await response.json();
|
|
@@ -238,7 +262,10 @@ document.getElementById("newsForm").addEventListener("submit", async (e) => {
|
|
|
238
262
|
try {
|
|
239
263
|
const response = await fetch("/med/api/admin/news", {
|
|
240
264
|
method: "POST",
|
|
241
|
-
headers: {
|
|
265
|
+
headers: {
|
|
266
|
+
"Content-Type": "application/json",
|
|
267
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
268
|
+
},
|
|
242
269
|
body: JSON.stringify(payload),
|
|
243
270
|
});
|
|
244
271
|
const result = await response.json();
|
|
@@ -273,7 +300,10 @@ document
|
|
|
273
300
|
try {
|
|
274
301
|
const response = await fetch("/med/api/admin/districts", {
|
|
275
302
|
method: "POST",
|
|
276
|
-
headers: {
|
|
303
|
+
headers: {
|
|
304
|
+
"Content-Type": "application/json",
|
|
305
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
306
|
+
},
|
|
277
307
|
body: JSON.stringify(payload),
|
|
278
308
|
});
|
|
279
309
|
const result = await response.json();
|
|
@@ -297,7 +327,11 @@ async function viewClinicsTable() {
|
|
|
297
327
|
title.textContent = " 🏥 Реестр медицинских учреждений системы";
|
|
298
328
|
content.innerHTML = "Загрузка таблицы...";
|
|
299
329
|
try {
|
|
300
|
-
const res = await fetch("/med/api/admin/list-clinics"
|
|
330
|
+
const res = await fetch("/med/api/admin/list-clinics", {
|
|
331
|
+
headers: {
|
|
332
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
333
|
+
},
|
|
334
|
+
});
|
|
301
335
|
const result = await res.json();
|
|
302
336
|
if (result.success && result.data.length > 0) {
|
|
303
337
|
let html =
|
|
@@ -322,7 +356,11 @@ async function viewStaffTable() {
|
|
|
322
356
|
title.textContent = "👨⚕️ Зарегистрированный медицинский персонал";
|
|
323
357
|
content.innerHTML = "Загрузка таблицы...";
|
|
324
358
|
try {
|
|
325
|
-
const res = await fetch("/med/api/admin/list-staff"
|
|
359
|
+
const res = await fetch("/med/api/admin/list-staff", {
|
|
360
|
+
headers: {
|
|
361
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
362
|
+
},
|
|
363
|
+
});
|
|
326
364
|
const result = await res.json();
|
|
327
365
|
if (result.success && result.data.length > 0) {
|
|
328
366
|
let html = `<table><thead><tr><th>ID Сотрудника</th><th>ФИО</th><th>Email</th><th>Должность</th><th>Специализация</th><th>Место работы</th></tr></thead><tbody>`;
|
|
@@ -358,6 +396,11 @@ async function viewStaffTable() {
|
|
|
358
396
|
}
|
|
359
397
|
}
|
|
360
398
|
async function logout() {
|
|
361
|
-
await fetch("/med/api/auth/logout", {
|
|
362
|
-
|
|
399
|
+
await fetch("/med/api/auth/logout", {
|
|
400
|
+
method: "POST",
|
|
401
|
+
headers: {
|
|
402
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
403
|
+
},
|
|
404
|
+
});
|
|
405
|
+
window.location.href = "/med/html/login.html";
|
|
363
406
|
}
|
package/public/js/auth.js
CHANGED
|
@@ -11,7 +11,10 @@ document.getElementById("loginForm").addEventListener("submit", async (e) => {
|
|
|
11
11
|
try {
|
|
12
12
|
const response = await fetch("/med/api/auth/login", {
|
|
13
13
|
method: "POST",
|
|
14
|
-
headers: {
|
|
14
|
+
headers: {
|
|
15
|
+
"Content-Type": "application/json",
|
|
16
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
17
|
+
},
|
|
15
18
|
body: JSON.stringify({ email, password }),
|
|
16
19
|
});
|
|
17
20
|
|
|
@@ -42,7 +45,10 @@ document
|
|
|
42
45
|
try {
|
|
43
46
|
const response = await fetch("/med/api/auth/register", {
|
|
44
47
|
method: "POST",
|
|
45
|
-
headers: {
|
|
48
|
+
headers: {
|
|
49
|
+
"Content-Type": "application/json",
|
|
50
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
51
|
+
},
|
|
46
52
|
body: JSON.stringify({ email, password, fullName }),
|
|
47
53
|
});
|
|
48
54
|
|
package/public/js/doctor.js
CHANGED
|
@@ -3,17 +3,17 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
3
3
|
loadDiseasesCatalog();
|
|
4
4
|
loadDoctorProfile();
|
|
5
5
|
});
|
|
6
|
-
|
|
7
6
|
async function loadDiseasesCatalog() {
|
|
8
7
|
try {
|
|
9
|
-
const res = await fetch("/med/api/doctor/diseases-list"
|
|
8
|
+
const res = await fetch("/med/api/doctor/diseases-list", {
|
|
9
|
+
headers: {
|
|
10
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
11
|
+
},
|
|
12
|
+
});
|
|
10
13
|
const result = await res.json();
|
|
11
|
-
|
|
12
14
|
const select = document.getElementById("examDisease");
|
|
13
|
-
|
|
14
15
|
select.innerHTML =
|
|
15
16
|
'<option value="">-- Выберите диагноз из справочника МКБ --</option>';
|
|
16
|
-
|
|
17
17
|
if (result.success && result.data.length > 0) {
|
|
18
18
|
result.data.forEach((disease) => {
|
|
19
19
|
const opt = document.createElement("option");
|
|
@@ -32,15 +32,16 @@ async function loadDiseasesCatalog() {
|
|
|
32
32
|
console.error("❌ Ошибка при получении справочника болезней:", err);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
-
|
|
36
35
|
async function loadSchedule() {
|
|
37
36
|
try {
|
|
38
|
-
const res = await fetch("/med/api/doctor/schedule"
|
|
37
|
+
const res = await fetch("/med/api/doctor/schedule", {
|
|
38
|
+
headers: {
|
|
39
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
40
|
+
},
|
|
41
|
+
});
|
|
39
42
|
const data = await res.json();
|
|
40
|
-
|
|
41
43
|
const list = document.getElementById("scheduleList");
|
|
42
44
|
list.innerHTML = "";
|
|
43
|
-
|
|
44
45
|
if (data.success && data.schedule && data.schedule.length > 0) {
|
|
45
46
|
data.schedule.forEach((ap) => {
|
|
46
47
|
const li = document.createElement("li");
|
|
@@ -55,12 +56,9 @@ async function loadSchedule() {
|
|
|
55
56
|
console.error("Ошибка загрузки расписания:", err);
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
|
-
|
|
59
59
|
document.getElementById("examForm").addEventListener("submit", async (e) => {
|
|
60
60
|
e.preventDefault();
|
|
61
|
-
|
|
62
61
|
const selectedDiseaseId = document.getElementById("examDisease").value;
|
|
63
|
-
|
|
64
62
|
const payload = {
|
|
65
63
|
appointmentId: document.getElementById("activeAppointmentId").value,
|
|
66
64
|
complaints: document.getElementById("examComplaints").value,
|
|
@@ -68,14 +66,15 @@ document.getElementById("examForm").addEventListener("submit", async (e) => {
|
|
|
68
66
|
doctorNotes: document.getElementById("examNotes").value,
|
|
69
67
|
diseaseId: selectedDiseaseId ? parseInt(selectedDiseaseId, 10) : null,
|
|
70
68
|
};
|
|
71
|
-
|
|
72
69
|
try {
|
|
73
70
|
const res = await fetch("/med/api/doctor/complete-examination", {
|
|
74
71
|
method: "POST",
|
|
75
|
-
headers: {
|
|
72
|
+
headers: {
|
|
73
|
+
"Content-Type": "application/json",
|
|
74
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
75
|
+
},
|
|
76
76
|
body: JSON.stringify(payload),
|
|
77
77
|
});
|
|
78
|
-
|
|
79
78
|
const result = await res.json();
|
|
80
79
|
if (result.success) {
|
|
81
80
|
alert(
|
|
@@ -91,7 +90,6 @@ document.getElementById("examForm").addEventListener("submit", async (e) => {
|
|
|
91
90
|
alert("Ошибка сети при отправке данных осмотра");
|
|
92
91
|
}
|
|
93
92
|
});
|
|
94
|
-
|
|
95
93
|
function startExamination(appointmentId, patientName) {
|
|
96
94
|
document.getElementById("examBlock").classList.remove("hidden");
|
|
97
95
|
document.getElementById("activeAppointmentId").value = appointmentId;
|
|
@@ -99,12 +97,21 @@ function startExamination(appointmentId, patientName) {
|
|
|
99
97
|
}
|
|
100
98
|
|
|
101
99
|
async function logout() {
|
|
102
|
-
await fetch("/med/api/auth/logout", {
|
|
100
|
+
await fetch("/med/api/auth/logout", {
|
|
101
|
+
method: "POST",
|
|
102
|
+
headers: {
|
|
103
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
104
|
+
},
|
|
105
|
+
});
|
|
103
106
|
window.location.href = "/med/html/login.html";
|
|
104
107
|
}
|
|
105
108
|
async function loadDoctorProfile() {
|
|
106
109
|
try {
|
|
107
|
-
const res = await fetch("/med/api/doctor/me"
|
|
110
|
+
const res = await fetch("/med/api/doctor/me", {
|
|
111
|
+
headers: {
|
|
112
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
113
|
+
},
|
|
114
|
+
});
|
|
108
115
|
const result = await res.json();
|
|
109
116
|
if (result.success && result.doctor) {
|
|
110
117
|
document.getElementById("doctorName").textContent =
|
|
@@ -116,28 +123,26 @@ async function loadDoctorProfile() {
|
|
|
116
123
|
console.error("Ошибка загрузки профиля сотрудника:", err);
|
|
117
124
|
}
|
|
118
125
|
}
|
|
119
|
-
|
|
120
126
|
function togglePasswordModal() {
|
|
121
127
|
const modal = document.getElementById("passwordModal");
|
|
122
128
|
modal.classList.toggle("hidden");
|
|
123
129
|
document.getElementById("changePasswordForm").reset();
|
|
124
130
|
}
|
|
125
|
-
|
|
126
131
|
document
|
|
127
132
|
.getElementById("changePasswordForm")
|
|
128
133
|
.addEventListener("submit", async (e) => {
|
|
129
134
|
e.preventDefault();
|
|
130
|
-
|
|
131
135
|
const oldPassword = document.getElementById("oldPassword").value;
|
|
132
136
|
const newPassword = document.getElementById("newPassword").value;
|
|
133
|
-
|
|
134
137
|
try {
|
|
135
138
|
const response = await fetch("/med/api/auth/change-password", {
|
|
136
139
|
method: "POST",
|
|
137
|
-
headers: {
|
|
140
|
+
headers: {
|
|
141
|
+
"Content-Type": "application/json",
|
|
142
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
143
|
+
},
|
|
138
144
|
body: JSON.stringify({ oldPassword, newPassword }),
|
|
139
145
|
});
|
|
140
|
-
|
|
141
146
|
const result = await response.json();
|
|
142
147
|
if (result.success) {
|
|
143
148
|
alert("Ваш пароль успешно изменен в базе данных SQLite!");
|
package/public/js/home.js
CHANGED
|
@@ -1,34 +1,17 @@
|
|
|
1
|
-
// Запуск подгрузки живой ленты новостей при открытии сайта
|
|
2
1
|
document.addEventListener("DOMContentLoaded", () => {
|
|
3
2
|
loadLiveNews();
|
|
4
3
|
});
|
|
5
|
-
|
|
6
|
-
// Статические вкладки информационного портала
|
|
7
|
-
const staticSections = {
|
|
8
|
-
about: {
|
|
9
|
-
title: "О нашей клинике",
|
|
10
|
-
text: "Мы — современный цифровой медицинский центр РБ, работающий по стандартам автоматизированного распределения пациентов. Наша цель — сделать медицинскую помощь доступной и быстрой.",
|
|
11
|
-
},
|
|
12
|
-
info: {
|
|
13
|
-
title: "Информационный источник",
|
|
14
|
-
text: "Полезные статьи о профилактике сезонных ОРВИ, контроле артериального давления при гипертонии и правилах подготовки к плановым диагностическим осмотрам в кабинетах поликлиники.",
|
|
15
|
-
},
|
|
16
|
-
contacts: {
|
|
17
|
-
title: "Контактная информация",
|
|
18
|
-
text: "Телефон регистратуры: +375 (17) 000-00-00. Режим работы окон справок: Пн-Пт с 8:00 до 20:00. Адрес: г. Минск, центральный лечебный корпус.",
|
|
19
|
-
},
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
// Динамическое скачивание новостей из SQLite без авторизации
|
|
23
4
|
async function loadLiveNews() {
|
|
24
5
|
const box = document.getElementById("news-box");
|
|
25
6
|
box.innerHTML =
|
|
26
7
|
'<p style="color: #64748b;">Загрузка свежих новостей клиники...</p>';
|
|
27
|
-
|
|
28
8
|
try {
|
|
29
|
-
const res = await fetch("/med/api/public/news-list"
|
|
9
|
+
const res = await fetch("/med/api/public/news-list", {
|
|
10
|
+
headers: {
|
|
11
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
12
|
+
},
|
|
13
|
+
});
|
|
30
14
|
const result = await res.json();
|
|
31
|
-
|
|
32
15
|
if (result.success && result.data.length > 0) {
|
|
33
16
|
let html = "";
|
|
34
17
|
result.data.forEach((post) => {
|
|
@@ -52,8 +35,6 @@ async function loadLiveNews() {
|
|
|
52
35
|
'<p style="color:red;">Не удалось загрузить ленту новостей сервера.</p>';
|
|
53
36
|
}
|
|
54
37
|
}
|
|
55
|
-
|
|
56
|
-
// Переключатель вкладок навигации по чертежу
|
|
57
38
|
function switchSection(sectionKey, element) {
|
|
58
39
|
document
|
|
59
40
|
.querySelectorAll(".nav-btn")
|
|
@@ -63,43 +44,36 @@ function switchSection(sectionKey, element) {
|
|
|
63
44
|
news: document.getElementById("news-box"),
|
|
64
45
|
about: document.getElementById("about-box"),
|
|
65
46
|
info: document.getElementById("info-box"),
|
|
66
|
-
contact: document.getElementById("contacts-box"),
|
|
47
|
+
contact: document.getElementById("contacts-box"),
|
|
67
48
|
};
|
|
68
|
-
|
|
69
|
-
// 1. Вызываем функцию для новостей, если выбрана эта секция
|
|
70
49
|
if (sectionKey === "news" && typeof loadLiveNews === "function") {
|
|
71
50
|
loadLiveNews();
|
|
72
51
|
}
|
|
73
|
-
|
|
74
|
-
// 2. Скрываем все элементы и показываем только нужный
|
|
75
52
|
Object.keys(elements).forEach((key) => {
|
|
76
53
|
if (elements[key]) {
|
|
77
|
-
// проверка, что элемент нашелся в DOM
|
|
78
54
|
elements[key].style.display = key === sectionKey ? "block" : "none";
|
|
79
55
|
}
|
|
80
56
|
});
|
|
81
57
|
}
|
|
82
|
-
// Онлайн-проверка лечебного участка по полному адресу кнопкой "ОК"
|
|
83
58
|
async function checkDistrictOnline() {
|
|
84
59
|
const city = document.getElementById("checkCityInput").value;
|
|
85
60
|
const street = document.getElementById("checkStreetInput").value;
|
|
86
61
|
const house = document.getElementById("checkHouseInput").value;
|
|
87
62
|
const resultBox = document.getElementById("checkResultBox");
|
|
88
|
-
|
|
89
63
|
if (!city || !street || !house) {
|
|
90
64
|
resultBox.innerHTML =
|
|
91
65
|
'<span style="color:red;">Заполните все три поля: Город, Улица, Дом!</span>';
|
|
92
66
|
return;
|
|
93
67
|
}
|
|
94
|
-
|
|
95
68
|
resultBox.innerHTML = "Проверка адреса в SQLite...";
|
|
96
|
-
|
|
97
69
|
try {
|
|
98
|
-
// Формируем безопасный GET-запрос со всеми параметрами адреса
|
|
99
70
|
const url = `/med/api/public/check-district?clinicId=1&city=${encodeURIComponent(city)}&street=${encodeURIComponent(street)}&house=${encodeURIComponent(house)}`;
|
|
100
|
-
const res = await fetch(url
|
|
71
|
+
const res = await fetch(url, {
|
|
72
|
+
headers: {
|
|
73
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
74
|
+
},
|
|
75
|
+
});
|
|
101
76
|
const result = await res.json();
|
|
102
|
-
|
|
103
77
|
if (result.success) {
|
|
104
78
|
resultBox.innerHTML = `🩺 ${result.data.assignedDistrict} <br> 🚪 ${result.data.assignedCabinet}`;
|
|
105
79
|
} else {
|
package/public/js/patient.js
CHANGED
|
@@ -2,26 +2,26 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
2
2
|
loadPatientData();
|
|
3
3
|
loadDoctorsForBooking();
|
|
4
4
|
});
|
|
5
|
-
|
|
6
5
|
async function loadPatientData() {
|
|
7
6
|
try {
|
|
8
|
-
const response = await fetch("/med/api/patient/me"
|
|
7
|
+
const response = await fetch("/med/api/patient/me", {
|
|
8
|
+
headers: {
|
|
9
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
10
|
+
},
|
|
11
|
+
});
|
|
9
12
|
const result = await response.json();
|
|
10
13
|
|
|
11
14
|
if (!result.success) {
|
|
12
|
-
// ИСПРАВЛЕНИЕ: Читаем result.error (как отдает бэкенд), либо result.message
|
|
13
15
|
const errorText = result.error || result.message || "Неизвестная ошибка";
|
|
14
16
|
alert("Ошибка загрузки данных: " + errorText);
|
|
15
|
-
window.location.href = "/med/med-login";
|
|
17
|
+
window.location.href = "/med/med-login";
|
|
16
18
|
return;
|
|
17
19
|
}
|
|
18
|
-
|
|
19
20
|
const data = result.data;
|
|
20
21
|
document.getElementById("patientName").textContent =
|
|
21
22
|
data.personalInfo.fullName;
|
|
22
23
|
document.getElementById("patientEmail").textContent =
|
|
23
24
|
data.personalInfo.email;
|
|
24
|
-
|
|
25
25
|
const districtInfo = document.getElementById("districtInfo");
|
|
26
26
|
if (data.personalInfo.UserAddress) {
|
|
27
27
|
const addr = data.personalInfo.UserAddress;
|
|
@@ -34,10 +34,8 @@ async function loadPatientData() {
|
|
|
34
34
|
document.getElementById("addrStreet").value = addr.street;
|
|
35
35
|
document.getElementById("addrHouse").value = addr.houseNumber;
|
|
36
36
|
}
|
|
37
|
-
|
|
38
37
|
const historyContainer = document.getElementById("medicalHistoryList");
|
|
39
38
|
historyContainer.innerHTML = "";
|
|
40
|
-
|
|
41
39
|
if (data.medicalHistory && data.medicalHistory.length > 0) {
|
|
42
40
|
data.medicalHistory.forEach((record) => {
|
|
43
41
|
const card = document.createElement("div");
|
|
@@ -77,7 +75,6 @@ async function loadPatientData() {
|
|
|
77
75
|
alert("Критическая ошибка фронтенда при разборе JSON");
|
|
78
76
|
}
|
|
79
77
|
}
|
|
80
|
-
|
|
81
78
|
document.getElementById("addressForm").addEventListener("submit", async (e) => {
|
|
82
79
|
e.preventDefault();
|
|
83
80
|
const payload = {
|
|
@@ -88,7 +85,10 @@ document.getElementById("addressForm").addEventListener("submit", async (e) => {
|
|
|
88
85
|
try {
|
|
89
86
|
const response = await fetch("/med/api/patient/address", {
|
|
90
87
|
method: "POST",
|
|
91
|
-
headers: {
|
|
88
|
+
headers: {
|
|
89
|
+
"Content-Type": "application/json",
|
|
90
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
91
|
+
},
|
|
92
92
|
body: JSON.stringify(payload),
|
|
93
93
|
});
|
|
94
94
|
const result = await response.json();
|
|
@@ -104,16 +104,22 @@ document.getElementById("addressForm").addEventListener("submit", async (e) => {
|
|
|
104
104
|
alert("Ошибка сети при отправке адреса");
|
|
105
105
|
}
|
|
106
106
|
});
|
|
107
|
-
|
|
108
107
|
async function logout() {
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
await fetch("/med/api/auth/logout", {
|
|
109
|
+
method: "POST",
|
|
110
|
+
headers: {
|
|
111
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
112
|
+
},
|
|
113
|
+
});
|
|
111
114
|
window.location.href = "/med/med-login";
|
|
112
115
|
}
|
|
113
|
-
|
|
114
116
|
async function loadDoctorsForBooking() {
|
|
115
117
|
try {
|
|
116
|
-
const response = await fetch("/med/api/patient/doctors-list"
|
|
118
|
+
const response = await fetch("/med/api/patient/doctors-list", {
|
|
119
|
+
headers: {
|
|
120
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
121
|
+
},
|
|
122
|
+
});
|
|
117
123
|
const result = await response.json();
|
|
118
124
|
const select = document.getElementById("bookingDoctor");
|
|
119
125
|
select.innerHTML = '<option value="">-- Выберите врача --</option>';
|
|
@@ -133,7 +139,6 @@ async function loadDoctorsForBooking() {
|
|
|
133
139
|
console.error("Ошибка загрузки врачей:", err);
|
|
134
140
|
}
|
|
135
141
|
}
|
|
136
|
-
|
|
137
142
|
document.getElementById("bookingForm").addEventListener("submit", async (e) => {
|
|
138
143
|
e.preventDefault();
|
|
139
144
|
const payload = {
|
|
@@ -144,7 +149,10 @@ document.getElementById("bookingForm").addEventListener("submit", async (e) => {
|
|
|
144
149
|
try {
|
|
145
150
|
const response = await fetch("/med/api/patient/book", {
|
|
146
151
|
method: "POST",
|
|
147
|
-
headers: {
|
|
152
|
+
headers: {
|
|
153
|
+
"Content-Type": "application/json",
|
|
154
|
+
"Bypass-Tunnel-Reminder": "true",
|
|
155
|
+
},
|
|
148
156
|
body: JSON.stringify(payload),
|
|
149
157
|
});
|
|
150
158
|
const result = await response.json();
|