This commit is contained in:
2025-06-30 13:08:35 +00:00
parent 1bf921a56e
commit 841cb61acf
579 changed files with 0 additions and 70142 deletions

View File

@@ -1,23 +0,0 @@
# Resource Provider (Node.js) Dockerfile
FROM node:20-alpine
# Set working directory
WORKDIR /app
# Install pnpm globally
RUN npm install -g pnpm
# Copy package.json and pnpm-lock.yaml
COPY package.json pnpm-lock.yaml* ./
# Install dependencies
RUN pnpm install --frozen-lockfile
# Copy the rest of the application
COPY . .
# Expose port
EXPOSE 3000
# Start the Node.js application
CMD ["node", "server.js"]

View File

@@ -1,150 +0,0 @@
export const STATIC_DIR =
process.env.NODE_ENV === "production" ? "/static-files" : "../static-files";
const _SUBJECTS = [
{
name: "ВЪТРЕШНИ БОЛЕСТИ",
topicTitles: [
"Предмет и задачи на вътрешната медицина. Раздели на вътрешните болести.",
"Болест и здраве. Етиология и патогенеза на болестите. Периоди на болестта.",
"Агония. Клинична смърт. Биологична смърт.",
"Основни класически методи на изследване във вътрешната медицина анамнеза. Физикални методи на изследване на пациентите оглед, палпация, перкусия, аускултация. Специални методи на изследване на пациентите.",
"Сегашно състояние обективен статус на болния. Клинична диагноза и прогноза. Проследяване на болния декурзус.",
"Изследване на дихателната система. Основни симптоми и синдроми при заболявания на дихателната система. Физикални и специални методи на изследване на дихателната система.",
"Остър и хроничен бронхит. Белодробен емфизем. ХОББ.",
"Пневмонии: класификации, клиника, лечение.",
"Бронхиектазии. Белодробен абсцес.",
"Тумори на белия дроб.",
"Белодробен тромбоемболизъм.",
"Плеврити.",
"Белодробна туберкулоза етиология, патогенеза и клинична картина. Първична белодробна туберкулоза. Вторична белодробна туберкулоза. Лечение и профилактика на белодробната туберкулоза.",
"Дихателна недостатъчност остра и хронична. Етиология и патогенеза. Степени, клиника и поведение.",
"Основни симптоми и синдроми при заболявания на сърдечно-съдовата система. Физикални и специални методи на изследване на сърдечно-съдовата система.",
"Ревматизъм.",
"Сърдечна недостатъчност остра и хронична.",
"Лечение на сърдечната недостатъчност. Поведение на медицинската сестра при спешни състояния на остра или обострена хронична сърдечна недостатъчност.",
"Ендокардити, перикардити. Миокардити.",
"Хипертонична болест: рискови фактори, патогенеза, клиника, лечение.",
"Остра периферна сърдечно-съдова недостатъчност. Кардиогенен шок.",
"Ритъмни и проводни нарушения на сърдечната дейност.",
"Исхемична болест на сърцето: етиология и патогенеза, рискови фактори. Стенокардия.",
"Исхемична болест на сърцето: етиология и патогенеза, рискови фактори. Инфаркт на миокарда.",
"Болести на хипофизата: Акромегалия. Безвкусен диабет.",
"Болести на щитовидната жлеза: Тиреотоксикоза. Микседем. Ендемична гуша.",
"Болести на надбъбречните жлези: Хиперкортицизъм. Хипокортицизъм.",
"Захарен диабет етиология, патогенеза, класификация, клиника. Диабетна кетоацидоза и хипогликемична кома. Поведение на медицинската сестра при диабетно болен в кома.",
"Захарен диабет късни усложнения. Захарен диабет диета и медикаментозно лечение.",
"Метаболитен синдром. Затлъстяване. Подагра.",
"Изследване на стомашно-чревния тракт. Анамнеза. Основни симптоми и синдроми при заболявания на стомашно-чревния тракт.",
"Физикални и специални методи на изследване на стомашно-чревния тракт.",
"Гастрити. ГЕРБ.",
"Язвена болест. Рак на стомаха.",
"Ентерити и колити. Рак на дебелото черво.",
"Основни симптоми и синдроми при заболяване на черния дроб и жлъчните пътища. Анамнеза, физикални и специални методи за изследване на черния дроб и жлъчните пътища.",
"Хронични хепатити. Чернодробни цирози.",
"Холелитиаза, холецистити.",
"Основни симптоми и синдроми при заболявания на отделителната система. Функционално изследване на отделителната система.",
"Остър и хроничен гломерулонефрит.",
"Нефролитиаза.",
"Пиелонефрити.",
"Остра бъбречна недостатъчност. Хронична бъбречна недостатъчност.",
"Балканска ендемична нефропатия. Бъбречна поликистозна болест. Бъбречна туберкулоза.",
"Ревматоиден артрит.",
"Лупус еритематодес.",
"Артрозна болест. Остеопороза.",
"Желязодефицитни анемии.",
"Витамин В12-дефицитни анемии.",
"Хемолитични анемии вследствие на вътре- и извънеритроцитни фактори: вродени и придобити.",
"Хеморагични диатези хемофилия, есенциална тромбоцитопения, капиляротоксикоза.",
"Бластна левкоза. Хронична миелолевкоза.",
"Нехочкинови и хочкинови лимфоми.",
"Остри екзогенни интоксикации. Общи принципи и правила в лечението на острите екзогенни отравяния. Поведение на медицинската сестра и грижи за болния с остро отравяне.",
"Алергия. Алергични заболявания. Анафилактичен шок. Поведение на медицинската сестра при спешни алергични състояния.",
],
},
{
name: "ФАРМАКОЛОГИЯ",
topicTitles: [
"Лекарствена форма определение, видове (твърди, течни, меки, газообразни и галенови лекарствени форми). Рецептурни примери.",
"Видове дози терапевтична, токсична. Терапевтичен индекс и терапевтична ширина. Дозиране на лекарствата при различните възрастови групи.",
"Въвеждане на лекарството в организма. Явления при многократно и при комбинирано прилагане.",
"Ефективност и потентност на лекарството. Фактори, модификациращи лекарствените действия от страна на организма и на околната среда.",
"Хистамин и антихистаминови лекарствени средства.",
"Психотропни лекарствени средства анксиолитици, психостимуланти, ноотропни средства.",
"Наркотични (опиоидни) аналгетици.",
"Ненаркотични (неопиоидни) аналгетици пиразолонови, пиразолидиндионови, анилинови, салицилови производни. Нестероидни противовъзпалителни средства.",
"Лекарствени средства за лечение на сърдечна недостатъчност. Антиаритмични лекарствени средства.",
"Антистенокардни (антиангинозни) и антиатероматозни лекарствени средства.",
"Антихипертензивни лекарствени средства централни и периферни симпатиколитици, миотропни вазодилататори, калциеви антагонисти, инхибитори на ренин-ангиотензин II-алдостероновата система, диуретици.",
"Лекарствени средства, влияещи върху хемопоезата и кръвосъсирването (антианемични средства, хемостатици, коагуланти, антикоагуланти, фибринолитици и инхибитори на стимулирания фибринолитичен процес).",
"Беталактамни антибиотици пеницилини, цефалоспорини, карбапенеми, монобактами.",
"Гликопептидни антибиотици (ванкомицин, тейкопланин). Аминогликозидни антибиотици.",
"Макролидни антибиотици. Линкозамиди. Рифамицини.",
"Полипептидни антибиотици. Противотуберкулозни лекарствени средства.",
"Тетрациклини, сулфонамиди, рифамицини.",
"Флуорохинолони.",
"Антимикотични и антивирусни лекарствени средства.",
"Лекарствени средства, действащи върху дихателната система аналептици на дихателния център, противокашлични, отхрачващи и антисептични средства.",
"Лекарствени средства, действащи върху храносмилателната система апетитостимулиращи, апетитопотискащи (анорексигенни), противоповръщащи (антиеметични), противоязвени (антиулкусни).",
],
},
{
name: "Соц. Медицина",
topicTitles: [
"Социалната медицина като наука определение, предмет, задачи, методи. Обществено здраве дефиниране, цикъл на социално-здравните явления. Системен подход при анализ на общественото здраве.",
"Здраве дефиниция, концепции, същност и измерения. Детерминанти на здравето. Нова философия за здраве. Континуум на здравето. Традиционно и съвременно разбиране за здраве.",
"Социално-медицински подход към личността в здраве и болест. Социални фактори на здравето / болестта. Социална профилактика, социална терапия, социална рехабилитация. Социална история на заболяването / болния.",
"Демографски измерители на общественото здраве. Статика на населението –значение за здравеопазването. Демографски показатели за естествено движение на населението.",
"Риск и рискови фактори. Дефиниция, видове, класификация. Рискови групи. Подходи за определяне на рискови групи в здравеопазването. Фази на естественото развитие на патологичния процес.",
"Профилактика на заболяванията. Нива на профилактика - цели, прицелни контингенти, обхват. Стратегии за първична профилактика.",
"Здравна система определение, цели, функции, типове. Континуум на здравните дейности. Здравни заведения определение и класификация.",
"Здравна помощ дефиниция, характеристики, нива на специализация. Здравни потребности и здравни нужди определение. Функции на здравните служби, организационни принципи.",
"Първична здравна помощ. Извънболнична специализирана медицинска помощ. Лечебни заведения за извънболнична медицинска помощ определение и класификация.",
"Болнична медицинска помощ. Видове болнични заведения. Съвременни тенденции за развитие на болничната помощ.",
"Съвременни проблеми и промени в световното здраве. Здравна политика. Приоритети в здравеопазването.",
"Здравна култура дефиниране, видове. Насоки на проява на субективна здравна култура. Планиране на здравно-образователни програми. Формиране на мотиви за здравно поведение.",
"Здравно възпитание - цели, принципи, фази, форми - класически и модерни.",
"Хронични неинфекциозни заболявания - характеристика, обществена значимост, рискови фактори, действия за ограничаването им.",
"Промоция на здравето - определение, цели и компоненти. Съвременни приоритети пред общественото здравеопазване. Актуални области и подходи, залегнали в Отавската харта.",
"Здравословен начин на живот. Аспекти и компоненти на начина на живот. Нагласи и мотивация за водене на здравословен начин на живот.",
"Правен режим на социалното осигуряване. КСО, ЗСП, ЗИХУ.",
"Закон за здравето. Органи в системата на здравеопазването.",
"Закон за здравното осигуряване. Принципи на здравното осигуряване.",
"Закон за лечебните заведения. Видове лечебни заведения.",
"Закон за съсловната организация на медицинските сестри, акушерките и асоциираните медицински специалисти. Функции, устройство, права и задължения.",
"Кодекс на професионалната етика. Здравна информация и документация. Лични данни на пациентите",
"Права на пациента. Правни способи за защита.",
"Кодекс на труда. Характеристика на трудовото правоотношение.",
"Юридическа отговорност. Гражданска отговорност на медицинските специалисти. Дисциплинарна отговорност.",
"Административно-наказателна отговорност на медицинските специалисти. Лишаване от права.",
"Наказателна отговорност на медицинските специалисти.",
],
},
];
export const SUBJECTS = (() => {
const subjects = _SUBJECTS.map((subject, subjectIdx) => {
const subjectSeq = subjectIdx + 1;
const subjectId = `S${subjectSeq}`;
const topics = subject.topicTitles.map((topicTitle, topicIdx) => {
const topicSeq = topicIdx + 1;
const topicId = `${subjectId}_T${topicSeq}`;
return {
id: topicId,
title: topicTitle,
sequence: topicSeq,
resources: [],
};
});
return {
id: subjectId,
name: subject.name,
sequence: subjectSeq,
topics,
};
});
return subjects;
})();

View File

@@ -1,54 +0,0 @@
import { promises as fs } from "fs";
import path from "path";
import { STATIC_DIR, SUBJECTS } from "./constants.js";
export const cache = new Map();
async function populateResources() {
const filenamesRaw = await fs.readdir(STATIC_DIR);
const filenames = filenamesRaw
.filter((file) => file.endsWith(".md"))
.map((file) => file.replace(".md", ""));
const topicResources = {};
filenames.forEach((resourceId) => {
const parts = resourceId.split("_");
const subjectPart = parts[1];
const topicPart = parts[2];
const topicId = `${subjectPart}_${topicPart}`;
topicResources[topicId] ||= [];
topicResources[topicId].push({
id: resourceId,
filename: `${resourceId}.md`,
version: parseInt(parts[3].substring(2)),
});
});
Object.values(topicResources).forEach((resources) => {
resources.sort((a, b) => a.version - b.version);
});
Object.values(SUBJECTS).forEach((subject) => {
Object.values(subject.topics).forEach((topic) => {
if (topic.id in topicResources) {
topic.resources = topicResources[topic.id];
}
});
});
}
export async function getStructure({ refresh = false } = {}) {
const structureKey = "structure";
if (refresh) {
cache.delete(structureKey);
}
if (!cache.has(structureKey)) {
await populateResources();
cache.set(structureKey, SUBJECTS);
}
return cache.get(structureKey);
}

View File

@@ -1,24 +0,0 @@
{
"name": "resource-provider",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"dev": "nodemon server.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.5.2",
"dependencies": {
"cors": "^2.8.5",
"express": "^5.1.0",
"helmet": "^8.1.0",
"morgan": "^1.10.0",
"mutter": "^1.0.1",
"nodemon": "^3.1.10",
"prettier": "^3.6.1",
"socket.io": "^4.8.1"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,250 +0,0 @@
import { readdir } from "fs/promises";
import express from "express";
import cors from "cors";
import helmet from "helmet";
import path from "path";
import fs from "fs/promises";
import { createServer } from "http";
import { Server } from "socket.io";
import morgan from "morgan";
import { STATIC_DIR } from "./constants.js";
import { getStructure, cache } from "./helper.js";
if (typeof process.env.API_TOKEN === "undefined") {
throw new Error("Service cannot be started without API_TOKEN");
}
// Load cache on start
getStructure({ refresh: true });
const app = express();
const server = createServer(app);
// Global boolean array
let booleanArray = new Array(10).fill(false);
// Socket.IO setup with CORS
const io = new Server(server, {
cors: {
origin: "http://localhost:5173", // React app URL
methods: ["GET", "POST"],
},
});
const corsOptions = {
methods: ["GET", "POST", "PUT", "DELETE"],
credentials: true,
origin: (origin, callback) => {
if (!origin) {
if (process.env.NODE_ENV === "production") {
return callback(new Error("Origin required in production"));
}
return callback(null, true);
}
if (isOriginAllowed(origin)) {
return callback(null, true);
} else {
return callback(new Error("Not allowed by CORS"));
}
},
};
app.use(cors(corsOptions));
app.use(helmet());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(morgan("tiny"));
// Socket connection handling
//io.on("connection", (socket) => {
// console.log("Client connected:", socket.id);
//
// // Send current array state to newly connected client
// socket.emit("arrayChanged", booleanArray);
//
// // Handle array updates from client
// socket.on("setArrayValue", (data) => {
// const { index, value } = data;
//
// if (index >= 0 && index < booleanArray.length) {
// booleanArray[index] = value;
// console.log(`Updated index ${index} to ${value}`);
//
// // Broadcast updated array to all clients
// io.emit("arrayChanged", booleanArray);
// }
// });
//
// // Handle getting current array state
// socket.on("getArray", () => {
// socket.emit("arrayChanged", booleanArray);
// });
//
// socket.on("disconnect", () => {
// console.log("Client disconnected:", socket.id);
// });
//});
app.get("/_health", (req, res) => {
res.json({ healthy: true });
});
app.get(
"/resources-status",
asyncHandler((req, res) => {
res.json({ array: booleanArray });
}),
);
// Serve static files with automatic ETag handling
app.use(
"/content",
express.static(STATIC_DIR, {
etag: true, // Enable automatic ETag generation
lastModified: true, // Include Last-Modified header
maxAge: 3600000, // Cache for 1 hour, but always revalidate
immutable: false, // Files can change
}),
);
app.get(
"/structure",
asyncHandler(async (req, res) => {
let { refresh } = req.query;
refresh = Boolean(refresh);
const structure = await getStructure({ refresh });
res.json(structure);
}),
);
app.post(
"/resources",
verifyToken,
asyncHandler(async (req, res) => {
try {
const { topicId, content } = req.body;
if (!topicId || !content) {
throw new Error("Missing topic id or content");
}
const [subjectIdx, topicIdx] = topicId
.match(/\d+/g)
?.map((n) => parseInt(n) - 1);
// Get next resource version
const subjects = await getStructure();
const resources = subjects[subjectIdx].topics[topicIdx].resources;
const resourceVersion = resources.length + 1;
const rv = `RV${resourceVersion}`;
const resourceId = `F_${topicId}_${rv}`;
const resource = {
id: resourceId,
filename: `${resourceId}.md`,
version: resourceVersion,
};
const filePath = path.join(STATIC_DIR, resource.filename);
await fs.writeFile(filePath, content, "utf8");
subjects[subjectIdx].topics[topicIdx].resources.push(resource);
res.json({ success: true, filename: resource.filename });
} catch (error) {
console.log(error);
res.status(500).json({ error: "Failed to update topic resource" });
}
}),
);
app.delete(
"/resources/:filename",
verifyToken,
asyncHandler(async (req, res) => {
try {
const { filename } = req.params;
if (!filename) {
return res.status(400).json({ error: "Filename is required" });
}
// Validate filename format (basic security check)
const pattern = /^F_S\d+_T\d+_RV\d+\.md$/;
if (!pattern.test(filename)) {
return res.status(400).json({ error: "Invalid filename format" });
}
const filePath = path.join(STATIC_DIR, filename);
// Check if file exists
try {
await fs.access(filePath);
} catch (error) {
return res.status(404).json({ error: "File not found" });
}
// Delete the file
await fs.unlink(filePath);
// Refresh the structure cache
const structure = await getStructure({ refresh: true });
res.json({
success: true,
message: `File ${filename} deleted successfully`,
structure,
});
} catch (error) {
console.log(error);
res.status(500).json({ error: "Failed to delete file" });
}
}),
);
app.use(errorRequestHandler);
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => console.log("Resource Provider started"));
/**
* HELPERS
*/
function asyncHandler(fn) {
return (req, res, next) => {
return Promise.resolve(fn(req, res, next)).catch(next);
};
}
function errorRequestHandler(error, _req, res, next) {
if (error) {
console.log(error);
res
.status(error.status || 500)
.json({ message: error.message || "Server failed" });
} else {
next();
}
}
function isOriginAllowed(origin) {
const url = new URL(origin);
if (url.hostname === "localhost" || url.hostname === "127.0.0.1") {
return true;
}
return url.hostname.endsWith("tomastm.com");
}
function verifyToken(req, res, next) {
const token = req.headers["token"];
if (!token || token !== process.env.API_TOKEN) {
res.status(401).json({ message: "Token not provided" });
return;
}
next();
}