Собствена инсталация
Има два начина да пуснете LOSPOR на своя инфраструктура и те не са равностойни.
Ако сте болница, използвайте болничната система. Тя е пакетирана инсталация с вграден PostgreSQL, TLS, архивиране, проверки на изправността и поддържан път за обновяване. Останалата част от тази страница — три услуги, пуснати на ръка — е за оценка на софтуера, а не за работеща клинична услуга.
Болничната система
lospor-hospital е самостоятелен пакет: API, уеб приложението, PWA и браузърът
за научна работа, закрепени към прегледани версии, записани в
UPSTREAM_VERSIONS.json, с PostgreSQL до тях.
sudo ./scripts/install.sh
Това създава базата данни, прилага миграциите, генерира тайните, създава първия администратор и вдига услугите зад TLS.
Работи като root, защото завършва със записването и стартирането на systemd услугите на системата, а скриптовете, които ги инсталират, отказват да се изпълнят от друг. Пуснат от обикновен потребител, спира по средата, на първия път, собственост на root, до който стигне, и съобщава този път, а не липсващото право.
| Задача | Команда |
|---|---|
| Обновяване към по-нов пакет | sudo ./scripts/update.sh |
| Архивиране | ./scripts/backup-now.sh |
| Възстановяване | ./scripts/restore-backup.sh |
| Проверка на работеща инсталация | ./scripts/doctor.sh |
| Присъединяване към централен регистър | ./scripts/enrol-central.sh |
Нейната собствена документация разглежда инсталирането, архивирането и възстановяването, експлоатацията, сигурността и съвместимостта при обновяване по-подробно от тази страница. Започнете оттам, а не оттук.
Работа от изходния код
За оценка, разработка или заведение, което е решило да поддържа услугите само. Всичко по-долу предполага, че сте свикнали да поддържате производствена услуга на Node.js: тази страница описва приложението, а не администрирането на сървър.
LOSPOR е три услуги на Node.js и една база данни PostgreSQL. Те могат да работят на една машина:
lospor-api, порт по подразбиране3002lospor-app, порт по подразбиране3000lospor-browser, порт по подразбиране3003- PostgreSQL
Мобилното приложение се инсталира на телефони; то не е четвърти сървър.
Изисквания
- Node.js 20 или по-нов
- PostgreSQL
- хранилищата
lospor-api,lospor-app,lospor-browserиlospor-mobile - една маркирана версия на
@lospor/core, използвана от всички - обратен посредник, който терминира TLS, и управител на процесите — вижте Как да го пуснете правилно
1. Настройка на API
В lospor-api създайте .env.local от .env.example. Като минимум задайте:
DATABASE_URL="postgresql://..."
DIRECT_URL="postgresql://..."
LOSPOR_AUTH_SECRET="a-long-random-secret"
NEXTAUTH_SECRET="the-same-secret"
LOSPOR_WEB_URL="http://localhost:3000"
CORS_ALLOW_ORIGINS="http://localhost:3000,http://localhost:3001,http://localhost:3003"
RESEARCH_EXPORT_STORAGE_DRIVER="filesystem"
RESEARCH_EXPORT_STORAGE_DIR=".data/research-exports"
RESEARCH_EXPORT_WORKER_SECRET="another-long-random-secret"
RESEARCH_EXPORT_RETENTION_DAYS="30"
DIRECT_URL трябва да е пряка/сесийна връзка, която поддържа заключвания на
редове и транзакции в PostgreSQL; не я насочвайте към посредник само за
транзакции. Пазете директорията с изследователските износи частна и я включвайте
в шифрованите архиви на сървъра само ако политиката ви за съхранение го изисква.
Дръжте NEXTAUTH_SECRET равна на LOSPOR_AUTH_SECRET през периода на
съвместимост с V6, за да могат съществуващите мобилни bearer токени да бъдат
проверявани.
Незадължителните настройки на API обхващат имейла през Brevo, Mistral AI, псевдонимизацията по OMOP, местоположението на браузъра за PDF, моментните снимки на каталозите и планираната задача за съхранението.
2. Подготовка на базата данни
Командите за базата се изпълняват само от lospor-api:
cd lospor-api
npm ci
npx prisma migrate deploy
npm run db:seed
npm run seed:icd10-bg
npm run seed:athena
npm run seed:concept-maps
Големите вносове от Athena трябва да се изпълняват от доверена машина за поддръжка, а не от безсървърна кука при компилация. Скриптовете за зареждане и попълване назад са идемпотентни, освен ако помощният им текст не казва друго.
Педиатричен режим
Педиатричното дозиране е изключено, освен ако не го включите, и изисква два превключвателя, не един:
PEDIATRIC_MODE_ENABLED="true"
Само това не е достатъчно. API изисква и версия на @lospor/core, в която
PEDIATRIC_PRODUCTION_READY е true — клинично потвърждение, че педиатричните
профили на медикаментите са прегледани като годни да изчислят доза за истинско
дете. И двете трябва да са изпълнени:
pediatricModeEnabled = PEDIATRIC_PRODUCTION_READY && PEDIATRIC_MODE_ENABLED === "true"
Проверете какво действително е определила работещата инсталация:
curl http://localhost:3002/v1/capabilities
features.pediatricMode съобщава enabled, productionReady и действащата
версия на набора от правила. Ако enabled е false, педиатричните случаи се
отказват с 503 PEDIATRIC_MODE_DISABLED и не се предлагат педиатрични дози.
Трябва също да заредите клиничните набори на платформата и да изберете
педиатричния. Без тях педиатричният режим не определя никакъв набор и не
предлага никакви дози, като въпреки това се отчита като изправен — проверете с
npm run clinical-rules:verify-pediatric-v2, вместо да предполагате.
3. Създаване на първия администратор
Празната база има лечебни заведения и няма потребители. Регистрацията създава неодобрен член, а само администратор може да одобрява или повишава, така че без тази стъпка никой не може да влезе.
cd lospor-api
LOSPOR_BOOTSTRAP_ADMIN_EMAIL="you@hospital.example" \
LOSPOR_BOOTSTRAP_ADMIN_PASSWORD="..." \
LOSPOR_BOOTSTRAP_ADMIN_FIRST_NAME="..." \
LOSPOR_BOOTSTRAP_ADMIN_LAST_NAME="..." \
npm run bootstrap:admin
Той задава ролята, одобрението и потвърдения адрес заедно и отказва да се
изпълни, след като администратор вече съществува. Не се опитвайте да го правите
чрез редактиране на базата: задаването само на role оставя профила
непотвърден и без възможност за вход.
Всички останали се регистрират сами, потвърждават имейла си и избират лечебното си заведение. Последваща смяна на заведението изисква одобрение от администратор или от началника на отделението, към което се присъединяват.
4. Компилиране и стартиране на услугите
Компилирайте веднъж и след това пуснете компилирания резултат. npm run dev е
сървър за разработка: по-бавен е, прекомпилира при всяка заявка и не е
предназначен да гледа към мрежа.
cd lospor-api && npm ci && npm run build && npm start # :3002
cd lospor-app && npm ci && npm run build && npm start # :3000
cd lospor-browser && npm ci && npm run build && npm start # :3003
Проверете API, преди да стартирате каквото и да е, което зависи от него:
curl http://localhost:3002/health/live # процесът работи, съобщава версията
curl http://localhost:3002/health/ready # базата също е достъпна
Уеб приложението и браузърът за научна работа нямат данни за достъп до базата.
Само API получава DATABASE_URL, DIRECT_URL и тайната за подписване.
Как да го пуснете правилно
Нищо от горното не преживява рестарт и нищо от него не е шифровано при пренос. Като минимум:
- Управител на процесите — systemd услуги или контейнерна среда по ваш избор — за да се рестартират услугите при повреда и да се върнат след рестарт.
- Обратен посредник, който терминира TLS пред трите: nginx, Caddy или каквото вашето заведение вече използва. Клиничните данни не бива да преминават през болнична мрежа в открит вид, а браузърите няма да дадат на незащитен адрес достъп до service worker или камера, така че PWA и сканирането на лабораторни резултати го изискват независимо от това.
- Вържете услугите към localhost и оставете посредника да е единственото, което слуша публично.
- Планирани задачи — вижте Планирани задачи. Три са и нищо не ги изпълнява вместо вас извън Vercel.
Болничната система прави всичко това вече, което е доводът да я използвате.
5. Свързване на Expo локално
Задайте адреса в локалната мрежа, достъпен за телефона, в
lospor-mobile/.env.local:
EXPO_PUBLIC_API_BASE="http://192.168.x.x:3002"
Телефонът и сървърът трябва да са в една мрежа. Рестартирайте Expo с изчистен кеш след промяна на тази стойност.
Внедряване във Vercel
Създайте три проекта във Vercel:
lospor-apiнаapi.lospor.org, с тайните за база данни, удостоверяване, имейл, изкуствен интелект, CORS, съхранение и OMOP.lospor-browserнаdatabase.lospor.org, сLOSPOR_API_INTERNAL_URL=https://api.lospor.orgиLOSPOR_DATABASE_ORIGIN=https://database.lospor.org. API също задаваLOSPOR_DATABASE_URL=https://database.lospor.orgи го включва вCORS_ALLOW_ORIGINS.lospor-appнаapp.lospor.org, сLOSPOR_API_INTERNAL_URL=https://api.lospor.org.
Само проектът на API изпълнява prisma migrate deploy, задачите за съхранение
на профилите и работния процес и графика за почистване на изследователските
износи. Уеб приложението никога не бива да получава DATABASE_URL,
DIRECT_URL или тайната за подписване на API.
Внедрете първо API, проверете изправността и входа, след това внедрете уеб приложението. Настройте мобилното приложение и PWA с:
EXPO_PUBLIC_API_BASE="https://api.lospor.org"
Архивиране
Архив, който никога не сте възстановявали, е хипотеза, а не архив. И трите стъпки имат значение.
Направете архив
pg_dump --format=custom --file="lospor-$(date +%F).dump" "$DATABASE_URL"
Използвайте custom формата: той позволява избирателно възстановяване и
компресира. Отбележете версията на PostgreSQL, с която сте направили дъмпа —
pg_restore няма да прочете дъмп от по-нова главна версия.
Архивирайте и директорията с изследователските износи
(RESEARCH_EXPORT_STORAGE_DIR) заедно с него, ако политиката ви за съхранение
изисква самите файлове. И двете съдържат клинични данни и мястото им е в
шифровано хранилище.
Възстановете архив
Никога не възстановявайте върху жива база. Възстановете в отделна и първо я разгледайте:
createdb lospor_restore_check
pg_restore --dbname=lospor_restore_check --no-owner "lospor-2026-08-05.dump"
Проверете го
Възстановяването без грешка доказва само че файлът е бил разчетен. Проверете, че данните ги има:
SELECT count(*) FROM "Case";
SELECT count(*) FROM "CaseEvent";
SELECT count(*) FROM "User";
SELECT count(*) FROM _prisma_migrations WHERE finished_at IS NOT NULL;
Сравнете броевете с производствената база. Броят на миграциите има същото значение като броя на редовете: възстановяване, което е с една миграция назад спрямо кода, ще се проваля по начини, които изглеждат като програмни грешки.
Правете това по график, а не само когато ви потрябва.
Обновявания и връщане назад
Направете и проверете архив преди всяка миграция. След това внедрявайте в този ред: Core, API, уеб приложението, браузъра на базата данни, след това мобилното приложение и PWA. API трябва да е жив и изправен преди уеб приложението, защото уеб приложението го вика, а не обратното.
Репетирайте миграциите срещу възстановено копие на производствената база, вместо да им се доверявате. Това не е формалност: репетирането на партидата v8.0.0 разкри, че една от миграциите ѝ зарежда публикуван, но празен клиничен набор, който после взема предимство пред истинския, оставяйки педиатричното дозиране мълчаливо изключено, докато всички проверки на изправността съобщават успех.
Пазете предишните файлове на API и уеб приложението достъпни, докато новите клиенти не преминат тест на случай през няколко устройства.
Посредникът /api/* в уеб приложението поддържа клиенти от V6 за документирания
период на съвместимост. Премахването му по-рано би счупило старите инсталирани
приложения.
Планирани задачи
Три задачи трябва да се изпълняват по график. Нищо не ги изпълнява вместо вас, а две от трите се провалят мълчаливо — регистърът продължава да приема случаи и просто спира да прави нещо, което е обещал.
| Задача | Крайна точка | Честота | Удостоверяване |
|---|---|---|---|
| Приключване на изтеклите случаи | /v1/internal/close-expired-cases | на всеки 5 минути | Bearer <CRON_SECRET> |
| Изчистване по политиката за съхранение | /v1/internal/purge-deleted | нощем | Bearer <CRON_SECRET> |
| Работен процес за изследователските износи | /v1/internal/research-exports/process | нощем | Bearer <RESEARCH_EXPORT_WORKER_SECRET> |
И трите отказват да се изпълнят, когато тайната им липсва или е грешна.
Приключване на изтеклите случаи
Случай, подаден за преглед, се приключва автоматично, когато 30-минутният му срок изтече. Тази задача е това, което го прави. Без нея случаят се приключва само когато клиницист го е отворил в момента на изтичане на брояча или го отвори отново по-късно — така случай, към който никой не се връща, остава Изчаква преглед безкрайно.
Пет минути е честотата, от която възможността се нуждае: тридесетминутен срок изисква преглед, измерван в минути, а изпълнението веднъж нощем не е по-бавна версия на възможността, а нейната липса.
Задачата съзнателно не е във vercel.json на API. Vercel таксува графици
по-чести от дневни и отхвърля цялото внедряване без тях, така че записът там не
се изпълняваше бавно — той изобщо спираше API да бъде публикуван. Затова
хостваната услуга не изпълнява този преглед; болничната система го изпълнява на
всеки пет минути от своя работен процес.
Изчистване по политиката за съхранение
vercel.json на API вика /v1/internal/purge-deleted нощем. То анонимизира
профилите, изтрити преди повече от 30 дни, и премахва свързаните с тях редове за
ограничаване на честотата. Забавянето съществува, за да може случайно изтриване
да бъде отменено; след него заличаването е окончателно и съзнателно
невъзстановимо.
Работен процес за изследователските износи
/v1/internal/research-exports/process обработва чакащите износи, премахва
изоставените частни работни обекти и изтрива файловете за изтегляне след
RESEARCH_EXPORT_RETENTION_DAYS. Метаданните и контролните суми на износите
остават за одит и възпроизводимост.
Лиценз
LOSPOR е под AGPL-3.0 или по-нов. Организациите, които изменят и предоставят софтуера по мрежа, трябва да изпълнят изискванията на лиценза за достъпност на изходния код.