Skip to main content

Self-hosting Guide

LOSPOR can be self-hosted by any institution on their own infrastructure. The application is a standard Next.js app backed by a PostgreSQL database.

Requirements

  • Node.js 18 or later
  • PostgreSQL database (Supabase recommended — free tier is sufficient)
  • A Vercel account (free Hobby tier is sufficient) or any server running Node.js

1 — Get the code

git clone https://github.com/kaloyandjunow-prog/lospor-app.git
cd lospor-app
npm install

2 — Set up the database

Create a free project at supabase.com. After creating the project:

  1. Go to Project SettingsDatabaseConnection String
  2. Copy the Transaction pooler URI (port 6543) — this is your DATABASE_URL

3 — Configure environment variables

Copy .env.example to .env and fill in your values:

cp .env.example .env
# Transaction pooler — used by the app at runtime (port 6543)
DATABASE_URL="postgresql://postgres.<ref>:<pass>@aws-<region>.pooler.supabase.com:6543/postgres"
# Session mode pooler — used by Prisma migrations (port 5432, same hostname as DATABASE_URL)
DIRECT_URL="postgresql://postgres.<ref>:<pass>@aws-<region>.pooler.supabase.com:5432/postgres"
NEXTAUTH_SECRET="your-random-secret" # openssl rand -base64 32
NEXTAUTH_URL="https://your-domain.com"

# Optional — AI pre-operative advisor and lab scan (Mistral La Plateforme; free tier available)
MISTRAL_API_KEY="your-mistral-api-key"
MISTRAL_API_BASE="https://api.mistral.ai/v1" # optional — regional overrides fall back to the global base if inference is not allowed
MISTRAL_MODEL="open-mistral-7b" # optional — override model

Generate NEXTAUTH_SECRET

openssl rand -base64 32

4 — Apply the database schema

npx prisma migrate deploy

This creates all the required tables in your database.

5 — Seed institutions (optional)

The Bulgarian NHIF institution list is already included. To seed it:

npx tsx prisma/seed.ts

Diagnosis and comorbidity search requires the ICD-10 vocabulary to be seeded. Download the vocabulary bundle from athena.ohdsi.org (select at minimum: ICD10, ICD10CM, ATC) and place the CSV files in a local folder. Then run:

npx tsx scripts/seed-vocabularies.ts --vocab-dir /path/to/athena-csvs
npx tsx scripts/seed-athena-vocabularies.ts --vocab-dir /path/to/athena-csvs --filtered-lospor
npx tsx scripts/seed-lab-loinc.ts

These scripts are idempotent and safe to re-run. Bulgarian ICD-10 labels are loaded from an Excel file matching ICD10_*.xlsx in the same folder (official MZ export).

Run Athena import locally or from a trusted maintenance machine. Do not run it from Vercel build hooks, serverless API routes, app startup, or deployed runtime. The deployed app only reads already-seeded Supabase tables. Filtered import is recommended for Supabase because full Athena can consume many GB of database storage once indexes are included.

7 — Seed the option library (required)

Every intraop/preop/postop picker and clinical number-control catalogue is served from a database table that starts out empty after step 4: position, technique, vascular access, airway management, monitoring, premedication drugs, intraop drugs, infusions, inhalational agents, fluids, clinical events, sex, blood group, airway grades, disposition, handover items, and numeric range specifications. Without this step the app loads, but clinical pickers will fall back to cached/bundled data or appear blank depending on the client state.

npx tsx scripts/seed-option-library.ts

Idempotent and safe to re-run.

Concept maps, backfill, and quality report

LOSPOR also needs the local concept map and relational research rows to be prepared after the option library is seeded:

npx tsx scripts/seed-concept-maps.ts
npx tsx scripts/backfill-relational.ts
npx tsx scripts/data-quality-report.ts

seed-concept-maps preserves ICD-10 English/Bulgarian labels, LOINC, ATC, INN, app-local option values, mapping method/confidence, review state, and known OMOP concept IDs where the filtered local Athena import provides confident standard mappings. backfill-relational rebuilds normalized research rows from existing cases. data-quality-report is read-only and reports Athena import coverage, relational drift, unmapped/source-only concepts, invalid ranges, future timestamps, export linkage warnings, and missing key research fields.

If seeding fails with a LibraryCategory enum error, do not edit an already applied migration file. Apply the tracked additive enum migration with npx prisma migrate deploy, then re-run the seed. Prisma validates migration checksums, so historical migration files must stay unchanged once applied.

8 — Run in development

npm run dev

The app will be available at http://localhost:3000.

9 — Deploy to Vercel

  1. Push your code to a GitHub repository (private is fine)
  2. Import the project at vercel.com
  3. Set the following environment variables in the Vercel dashboard. You need both DATABASE_URL and DIRECT_URL:
    • DATABASE_URL — transaction pooler, port 6543 (used by the app at runtime)
    • DIRECT_URL — session mode pooler, port 5432 at the same pooler hostname (used by Prisma's migration engine, which requires persistent prepared statements). Use aws-<region>.pooler.supabase.com:5432not db.<ref>.supabase.co:5432, which is the direct connection and is unreachable from Vercel.
    • NEXTAUTH_SECRET, NEXTAUTH_URL, and optionally the Mistral keys.
  4. Deploy

Vercel automatically handles SSL, CDN, and zero-downtime deployments.

10 — Custom domain

In Vercel, go to SettingsDomains and add your domain. Configure the DNS records as directed by Vercel.

Update NEXTAUTH_URL to your production domain and redeploy.

Updating

To update to a newer version of LOSPOR:

git pull origin main
npm install
npx prisma migrate deploy # apply tracked migrations (production-safe)
npx tsx scripts/seed-option-library.ts
npx tsx scripts/seed-concept-maps.ts
npx tsx scripts/backfill-relational.ts
npx tsx scripts/data-quality-report.ts

Commit and push to trigger a Vercel redeployment.

Creating the first admin

After registering your first account, promote it to admin directly in the Supabase table editor:

  1. Supabase → Table EditorUser table
  2. Find your user row → set role to ADMIN
  3. Refresh the app — the Admin panel appears in the navigation bar

Licence

LOSPOR is licensed under AGPL-3.0. Self-hosted installations must make their source code available to users if modified. The unmodified code is already open source at GitHub.