Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copy to .env (git-ignored) and load it with your process manager, or export the variables.
NODE_ENV=development
PORT=4000
# pg | mongo
DB_BACKEND=pg
DATABASE_URL=postgres://postgres:postgres@localhost:5432/mgpl
# Sessions, reset tokens, rate limits and the read cache. Leave empty for in-memory (one server only).
REDIS_URL=
# Comma-separated browser origins allowed to call the API ("*" only in development)
CORS_ORIGINS=http://localhost:3000
GRAPHIQL=true
INTROSPECTION=true
# Demo report / invoice samples in `documents`
MGPL_SAMPLES=true
# Password-reset links: <PASSWORD_RESET_URL>?token=…
PASSWORD_RESET_URL=http://localhost:3000/reset-password
# Real signatures (never committed): files outside the repo
# MGPL_SIGNATURE_HEADER_FILE=/secure/signature-header.png
# MGPL_SIGNATURE_NONHEADER_FILE=/secure/signature-nonheader.png
2 changes: 2 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Every change to this repo needs a review from a code owner (see CONTRIBUTING in mgpl-diagnostics/.github).
* @BackStacked
21 changes: 21 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
version: 2
updates:
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 5
groups:
minor-and-patch:
update-types: [minor, patch]
ignore:
# our own packages are released together and bumped by hand
- dependency-name: "@mgpl-diagnostics/*"
- dependency-name: "typescript"
update-types: ["version-update:semver-major"]
- dependency-name: "@types/node"
update-types: ["version-update:semver-major"]
- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly
37 changes: 37 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: CI

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
# The job id "ci" is the required status check on main (see mgpl-diagnostics/.github scripts/protect-repos.mjs).
ci:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 22
# Builds every kit from its public repo at the ref in kits.json, then installs. No package token needed.
# (Switch to the registry once every kit is published: docs/LOCAL-AGENT-BRIEF.md.)
- run: npm run kits
- run: npm run typecheck
- name: End-to-end tests (GraphQL over HTTP, auth-kit sessions) on PostgreSQL and MongoDB
run: npm test
- run: npm run build
- name: schema.graphql is up to date (run `npm run schema` and commit if this fails)
run: npm run schema && git diff --exit-code -- schema.graphql
- name: docs/04-api-reference.md is up to date (run `npm run docs:reference` and commit if this fails)
run: npm run docs:reference && git diff --exit-code -- docs/04-api-reference.md
- name: llms-full.txt is up to date (run `npm run llms` and commit if this fails)
run: npm run llms && git diff --exit-code -- llms-full.txt
67 changes: 67 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# AGENTS.md — @mgpl-diagnostics/api

The MGPL Diagnostics backend: **one GraphQL endpoint** (`POST /graphql`, GraphQL Yoga on Fastify) over
the kits, used as internal libraries. Read [docs/README.md](docs/README.md) for the full picture and
[docs/13-adding-a-feature.md](docs/13-adding-a-feature.md) before adding anything.

## Map

```
src/main.ts start (config → container → server)
src/server.ts Fastify + Yoga + GET /health (no other routes)
src/container.ts createDb (posting: financePosting) + createAuth + services, once per process
src/config/env.ts loadConfig: every environment variable
src/graphql/ schema.ts (MODULES → buildSchema), context, guards, errors, scalars, loaders, limits
src/schema/ SDL per module: base auth organization catalog lab billing books finance documents
src/controllers/ resolvers per module: authorize → map → service
src/services/ business calls on the kits: (actor, input), ownership checks, no GraphQL
scripts/ kits.mjs (build kits from source), seed.ts, print-schema.ts, api-reference.ts, llms-full.mjs
test/run.ts end-to-end over HTTP on PostgreSQL + MongoDB, plus docs/examples validation
docs/ A-to-Z docs (Mermaid), examples/*.graphql, LOCAL-AGENT-BRIEF.md
schema.graphql printed schema (generated)
kits.json kit refs for `npm run kits`
```

## Rules

1. **One endpoint.** Everything is a GraphQL field. No REST routes besides `GET /health`; documents
travel as `Document { base64 | html }`, exports as `CsvFile`.
2. **Layers.** Schema → controller → service → kit. Controllers check permissions and map; services hold
the logic and never import GraphQL; storage changes go to db-kit, finance rules to finance-kit,
posting to tally-kit, permissions to auth-kit, catalog data to catalog-kit, layouts to report-kit /
invoice-kit.
3. **Tenant isolation.** No argument carries an `orgId` or `actorId`. They come from the session
(`actorOf`). Every id is checked against the organisation (`mustOwn`, `owned`, `scopedFind`).
4. **Permissions per branch.** Load a record, then `authorize(ctx, perm, record.branchId)`; lists use
`branchScope`. Every permission must exist in auth-kit's `PERMISSIONS`.
5. **Errors.** Wrap controllers in `withErrorCodes`. Never build a `GraphQLError` with `originalError`
(Yoga masks it). Error codes are a public contract: add, never rename.
6. **Money is integer paise; days are IST `Day` strings; the financial year is April–March.**
7. **No N+1.** Nested fields go through `ctx.loaders`. Lists are capped with `cap()` (≤ 200).
8. **Both databases.** Tests run on PostgreSQL and MongoDB; a change must pass on both.
9. **Generated files stay current** (CI checks): `npm run schema`, `npm run docs:reference`, `npm run llms`.
10. **Client contract.** `@mgpl-diagnostics/client` mirrors the schema; when a field is added or renamed,
update the client (operations, types, method, docs) and bump its ref in `kits.json`.
11. **Compliance values are configuration** verified by the lab's CA (`verifyWithCA`). Ask the owner
before changing one. No doctor referral commissions.
12. Never commit patient data, signatures, secrets or `.env` files. Test fixtures use fictional names.

## Commands

```bash
npm run kits # once, and after changing kits.json
npm run typecheck && npm test && npm run build
npm run schema && npm run docs:reference && npm run llms
```

`npm test` downloads PostgreSQL and MongoDB binaries on first run. Behind a proxy that blocks MongoDB,
set `MONGOMS_SYSTEM_BINARY` or run `TEST_BACKENDS=pg npm test` (CI still runs both).

## Repository rules

`main` takes pull requests only: squash merge, 1 code-owner review (`@BackStacked`), the `ci` check
required. Rules come from `mgpl-diagnostics/.github` (`scripts/protect-repos.mjs`).

## Licence

All rights reserved (see LICENSE).
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
96 changes: 96 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<div align="center">

<img src="https://raw.githubusercontent.com/mgpl-diagnostics/report-kit/main/assets/report-assets/logo.svg" alt="MGPL Diagnostics" width="300" />

# api

**The MGPL Diagnostics backend: one GraphQL endpoint for the whole lab.**

Sign-in, patients, bookings, samples, results, reports, billing, books, GST, receivables, settlements,
cash and bank, documents. TypeScript, GraphQL Yoga on Fastify, PostgreSQL or MongoDB.

[![Endpoint](https://img.shields.io/badge/endpoint-POST%20%2Fgraphql-14264A)](#)
[![Databases](https://img.shields.io/badge/db-PostgreSQL%20%7C%20MongoDB-14264A)](#)
[![License](https://img.shields.io/badge/license-All%20rights%20reserved-E9A23B)](LICENSE)

[Docs](docs/README.md) · [Architecture](docs/02-architecture.md) · [Getting started](docs/03-getting-started.md) · [API reference](docs/04-api-reference.md) · [Client](docs/14-client.md)

</div>

---

```mermaid
flowchart LR
APP["Apps<br/>@mgpl-diagnostics/client"] -- "POST /graphql" --> API["api<br/>controllers → services"]
API --> K["auth-kit · db-kit · catalog-kit<br/>report-kit · invoice-kit · tally-kit · finance-kit"]
K --> DB[("PostgreSQL / MongoDB")]
K -.-> R[("Redis")]
```

## What you get

- **One endpoint.** 137 queries and mutations; reports and invoices come back as base64 PDFs or HTML
inside the response, CSV exports as text. No other routes except `GET /health`.
- **The kits as internal libraries.** auth-kit sessions and roles, db-kit storage and workflows,
catalog-kit prices and reference ranges, report-kit and invoice-kit rendering, tally-kit books,
finance-kit GST and finance. See [docs/01-overview.md](docs/01-overview.md).
- **Layers.** `schema/` (SDL) → `controllers/` (permissions, mapping) → `services/` (kit calls) → kits.
- **Safe by construction.** The organisation and user come from the session; every record is checked
against them and against its branch; unknown errors are masked; depth and list limits.
- **Money in paise, books always balanced.** Every invoice, payment and credit note posts its voucher in
the same transaction.

## Quick start

```bash
npm run kits # build + install the kits (see docs/03-getting-started.md)
cp .env.example .env # DATABASE_URL, …
SEED_ORG_NAME=… SEED_ORG_CODE=MGPL SEED_BRANCH_CODE=ARG01 SEED_BRANCH_NAME=Arang SEED_BRANCH_STATE=22 \
SEED_OWNER_NAME=… SEED_OWNER_EMAIL=… SEED_OWNER_PASSWORD=… npm run seed
npm run dev # http://localhost:4000/graphql
```

```graphql
mutation { login(input: { email: "owner@example.in", password: "…", orgCode: "MGPL" }) { token } }

query { viewer { user { name } organization { name } permissions } }
```

```ts
import { createMgplGraphQLClient } from "@mgpl-diagnostics/client";
const mgpl = createMgplGraphQLClient({ url: "http://localhost:4000/graphql" });
await mgpl.auth.login({ email, password, orgCode: "MGPL" });
const hits = await mgpl.catalog.search("cbc");
```

## Docs

Everything from A to Z is in [docs/](docs/README.md), with Mermaid diagrams: overview, architecture,
getting started, the generated API reference, errors, auth and permissions, domain flows, documents,
configuration, data and money, testing, deployment, adding a feature, client, glossary, and runnable
[examples](docs/examples/). What has to happen outside this repo (releases, org settings, deployment) is
in [docs/LOCAL-AGENT-BRIEF.md](docs/LOCAL-AGENT-BRIEF.md).

## Development

```bash
npm run typecheck
npm test # end-to-end over HTTP on PostgreSQL + MongoDB (TEST_BACKENDS=pg for one)
npm run build # dist/main.js, dist/index.js
npm run schema # schema.graphql
npm run docs:reference # docs/04-api-reference.md
npm run llms # llms-full.txt
```

## Part of MGPL Diagnostics

| Repo | Role |
|---|---|
| **api** | the backend: one GraphQL endpoint |
| [client](https://github.com/mgpl-diagnostics/client) | frontend SDK (zero dependencies) |
| [auth-kit](https://github.com/mgpl-diagnostics/auth-kit) · [db-kit](https://github.com/mgpl-diagnostics/db-kit) · [catalog-kit](https://github.com/mgpl-diagnostics/catalog-kit) | auth, data, test catalog |
| [report-kit](https://github.com/mgpl-diagnostics/report-kit) · [invoice-kit](https://github.com/mgpl-diagnostics/invoice-kit) | report and invoice rendering |
| [tally-kit](https://github.com/mgpl-diagnostics/tally-kit) · [finance-kit](https://github.com/mgpl-diagnostics/finance-kit) | books, GST and finance |
| [report-studio](https://github.com/mgpl-diagnostics/report-studio) | Next.js reference app |

<sub>© MGPL Diagnostics (Maa Gayatri Path Lab). All rights reserved; see [LICENSE](LICENSE). tally-kit is our own software, not affiliated with Tally Solutions.</sub>
55 changes: 55 additions & 0 deletions docs/01-overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# 01 · Overview

`@mgpl-diagnostics/api` is the backend of MGPL Diagnostics (Maa Gayatri Path Lab). It serves **everything
through one endpoint, `POST /graphql`**: sign-in and users, the test catalog, patients, bookings, samples,
results and reports, billing, the books of account, GST returns, B2B receivables, partner settlements,
cash and bank, lock dates, CSV exports, and the rendered report and invoice documents.

```mermaid
flowchart LR
subgraph Frontends
W["Web app"]
M["Mobile / desk app"]
S["Scripts"]
end
W & M & S -- "POST /graphql<br/>Bearer token" --> API["api<br/>GraphQL Yoga on Fastify"]
API --> PG[("PostgreSQL<br/>or MongoDB")]
API -.-> R[("Redis<br/>optional")]
LB["Load balancer"] -- "GET /health" --> API
```

## What it is made of

The domain logic lives in the kits, which the API uses as **internal libraries**. It has no other HTTP
routes and starts no per-kit servers. The kits never see HTTP: the API calls their functions from its
service layer.

| Kit | Role inside the API |
|---|---|
| [auth-kit](https://github.com/mgpl-diagnostics/auth-kit) | Passwords (scrypt), sessions, login rate limits, roles and permissions |
| [db-kit](https://github.com/mgpl-diagnostics/db-kit) | Repositories on PostgreSQL or MongoDB, transactions, workflows (bookings, reports, invoices) |
| [catalog-kit](https://github.com/mgpl-diagnostics/catalog-kit) | The test catalog: names, codes, prices, units, reference ranges, search |
| [report-kit](https://github.com/mgpl-diagnostics/report-kit) | Report PDFs and HTML (Navy Sidebar and classic designs) |
| [invoice-kit](https://github.com/mgpl-diagnostics/invoice-kit) | Invoice PDFs and HTML, invoice arithmetic |
| [tally-kit](https://github.com/mgpl-diagnostics/tally-kit) | Our own double-entry engine: ledgers, vouchers, books reports |
| [finance-kit](https://github.com/mgpl-diagnostics/finance-kit) | GST, GSTR-1/3B data, receivables, settlements, cash and bank, lock dates, exports |
| [client](https://github.com/mgpl-diagnostics/client) | Not used by the API; the frontend SDK that calls it |

## Design choices

- **One endpoint.** Reports and invoices are GraphQL fields too. A PDF arrives as base64 inside the
response (`Document.base64`), HTML as a string, CSV exports as `CsvFile.content`.
- **Layers.** Schema (SDL) → controllers (resolvers: permissions, mapping) → services (business calls on
the kits) → kits → database. See [02-architecture.md](02-architecture.md).
- **Tenant isolation by construction.** The organisation and the acting user come from the session,
never from arguments. Every id is checked against the session's organisation.
- **Money in integer paise**, days as IST `"YYYY-MM-DD"`, the Indian financial year (April–March).
- **Both databases.** The same code runs on PostgreSQL (Sequelize) or MongoDB (Mongoose, replica set).

## What it is not

- Not a public API for patients. Every operation except `health`, `login`, password reset and the
catalog needs a staff session.
- Not tax advice. GST rates, TDS and thresholds are configuration that the lab's CA must confirm
(`verifyWithCA` lists them).
- Not integrated with Tally software. tally-kit is our own engine with Tally-like concepts.
Loading
Loading