Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
5081819281
|
|||
|
240bd9cba1
|
4
.gitignore
vendored
4
.gitignore
vendored
@@ -126,8 +126,12 @@ dist
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
# Old package manager lockfiles (Bun migration - keep bun.lock)
|
||||
yarn.lock
|
||||
package-lock.json
|
||||
pnpm-lock.yaml
|
||||
|
||||
build
|
||||
|
||||
*.sqlite
|
||||
|
||||
@@ -2,10 +2,17 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. Dates are displayed in UTC.
|
||||
|
||||
#### [1.7.0](https://git.odit.services/lfk/backend/compare/1.6.0...1.7.0)
|
||||
|
||||
- refactor: Bun by default [`240bd9c`](https://git.odit.services/lfk/backend/commit/240bd9cba10636bfc100ea2732508d805639f105)
|
||||
|
||||
#### [1.6.0](https://git.odit.services/lfk/backend/compare/1.5.2...1.6.0)
|
||||
|
||||
> 20 February 2026
|
||||
|
||||
- feat(data): Added nats jetstream dependency [`bbf6ea6`](https://git.odit.services/lfk/backend/commit/bbf6ea6c0fdffa11dacdf4b9afb6160ce54e197d)
|
||||
- chore(deps): Bump typescript and get rid of now legacy imports [`2da8247`](https://git.odit.services/lfk/backend/commit/2da8247978c5142eec194651a7520fa53396d762)
|
||||
- chore(release): 1.6.0 [`53fb038`](https://git.odit.services/lfk/backend/commit/53fb0389cd1da2b71b82102e82fc3d30f0be3820)
|
||||
- feat(nats): Implement caching for card, runner, and station entries with improved key management [`b0c6759`](https://git.odit.services/lfk/backend/commit/b0c67598132deffce697f19c83bd4826420abe76)
|
||||
- feat(auth): Implement caching for scanauth [`526738e`](https://git.odit.services/lfk/backend/commit/526738e48722fffe4493102fad69f65b40fc3b49)
|
||||
- refactor(scan): Implement KV-backed scan station submissions and response model [`d3e0206`](https://git.odit.services/lfk/backend/commit/d3e0206a3ccbff0e69024426bb2bf266cde30eeb)
|
||||
|
||||
24
Dockerfile
24
Dockerfile
@@ -1,27 +1,23 @@
|
||||
# Typescript Build
|
||||
FROM registry.odit.services/hub/library/node:23.10.0-alpine3.21 AS build
|
||||
ARG NPM_REGISTRY_URL=https://registry.npmjs.org
|
||||
FROM registry.odit.services/hub/oven/bun:1.3.9-alpine AS build
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json ./
|
||||
COPY pnpm-workspace.yaml ./
|
||||
COPY pnpm-lock.yaml ./
|
||||
RUN npm config set registry $NPM_REGISTRY_URL && npm i -g pnpm@10.7
|
||||
RUN mkdir /pnpm && pnpm config set store-dir /pnpm && pnpm i
|
||||
COPY package.json bun.lockb* ./
|
||||
RUN bun install --frozen-lockfile
|
||||
|
||||
COPY tsconfig.json ormconfig.js ./
|
||||
COPY tsconfig.json ormconfig.js bunfig.toml ./
|
||||
COPY src ./src
|
||||
RUN pnpm run build \
|
||||
RUN bun run build \
|
||||
&& rm -rf /app/node_modules \
|
||||
&& pnpm i --production --prefer-offline
|
||||
&& bun install --production --frozen-lockfile
|
||||
|
||||
# final image
|
||||
FROM registry.odit.services/hub/library/node:23.10.0-alpine3.21 AS final
|
||||
FROM registry.odit.services/hub/oven/bun:1.3.9-alpine AS final
|
||||
WORKDIR /app
|
||||
COPY --from=build /app/package.json /app/package.json
|
||||
COPY --from=build /app/pnpm-lock.yaml /app/pnpm-lock.yaml
|
||||
COPY --from=build /app/pnpm-workspace.yaml /app/pnpm-workspace.yaml
|
||||
COPY --from=build /app/bun.lockb* /app/
|
||||
COPY --from=build /app/ormconfig.js /app/ormconfig.js
|
||||
COPY --from=build /app/bunfig.toml /app/bunfig.toml
|
||||
COPY --from=build /app/dist /app/dist
|
||||
COPY --from=build /app/node_modules /app/node_modules
|
||||
ENTRYPOINT ["node", "/app/dist/app.js"]
|
||||
ENTRYPOINT ["bun", "/app/dist/app.js"]
|
||||
123
README.md
123
README.md
@@ -2,66 +2,119 @@
|
||||
|
||||
Backend Server
|
||||
|
||||
## Quickstart 🐳
|
||||
> Use this to run the backend with a postgresql db in docker
|
||||
## Prerequisites
|
||||
|
||||
1. Clone the repo or copy the docker-compose
|
||||
2. Run in toe folder that contains the docker-compose file: `docker-compose up -d`
|
||||
This project uses **Bun** as the runtime and package manager. Install Bun first:
|
||||
|
||||
```bash
|
||||
# macOS/Linux
|
||||
curl -fsSL https://bun.sh/install | bash
|
||||
|
||||
# Windows
|
||||
powershell -c "irm bun.sh/install.ps1 | iex"
|
||||
```
|
||||
|
||||
Or visit [bun.sh](https://bun.sh) for other installation methods.
|
||||
|
||||
## Quickstart 🐳
|
||||
> Use this to run the backend with a PostgreSQL db in Docker
|
||||
|
||||
1. Clone the repo or copy the docker-compose
|
||||
2. Run in the folder that contains the docker-compose file: `docker-compose up -d`
|
||||
3. Visit http://127.0.0.1:4010/api/docs to check if the server is running
|
||||
4. You can now use the default admin user (`demo:demo`)
|
||||
|
||||
## Dev Setup 🛠
|
||||
> Local dev setup utilizing sqlite3 as the database.
|
||||
> Local dev setup utilizing SQLite3 as the database and NATS for caching.
|
||||
|
||||
1. Rename the .env.example file to .env (you can adjust app port and other settings, if needed)
|
||||
2. Install Dependencies
|
||||
1. Rename the `.env.example` file to `.env` (you can adjust app port and other settings if needed)
|
||||
2. Start NATS (required for KV cache):
|
||||
```bash
|
||||
pnpm i
|
||||
docker-compose up -d nats
|
||||
```
|
||||
3. Start the server
|
||||
3. Install dependencies:
|
||||
```bash
|
||||
pnpm dev
|
||||
bun install
|
||||
```
|
||||
4. Start the server:
|
||||
```bash
|
||||
bun run dev
|
||||
```
|
||||
|
||||
**Note**: Bun cannot run TypeScript source files directly due to circular TypeORM dependencies. The `dev` script automatically builds and runs the compiled output. For hot-reload during development, you may need to rebuild manually after code changes.
|
||||
|
||||
### Run Tests
|
||||
```bash
|
||||
# Run tests once (server has to run)
|
||||
pnpm test
|
||||
# Run tests once (server has to be running)
|
||||
bun test
|
||||
|
||||
# Run test in watch mode (reruns on change)
|
||||
pnpm test:watch
|
||||
bun run test:watch
|
||||
|
||||
# Run test in ci mode (automaticly starts the dev server)
|
||||
pnpm test:ci
|
||||
# Run test in CI mode (automatically starts the dev server)
|
||||
bun run test:ci
|
||||
```
|
||||
|
||||
### Run Benchmarks
|
||||
```bash
|
||||
# Start the server first
|
||||
bun run dev
|
||||
|
||||
# In another terminal:
|
||||
bun run benchmark
|
||||
```
|
||||
|
||||
### Generate Docs
|
||||
```bash
|
||||
pnpm docs
|
||||
bun run docs
|
||||
```
|
||||
|
||||
### Other Commands
|
||||
```bash
|
||||
# Build for production
|
||||
bun run build
|
||||
|
||||
# Start production server
|
||||
bun start
|
||||
|
||||
# Seed database with test data
|
||||
bun run seed
|
||||
|
||||
# Export OpenAPI spec
|
||||
bun run openapi:export
|
||||
|
||||
# Generate license report
|
||||
bun run licenses:export
|
||||
|
||||
# Generate changelog
|
||||
bun run changelog:export
|
||||
```
|
||||
|
||||
## ENV Vars
|
||||
> You can provide them via .env file or docker env vars.
|
||||
> You can use the `test:ci:generate_env` package script to generate a example env (uses bs data as test server and ignores the errors).
|
||||
> You can use the `test:ci:generate_env` package script to generate an example env (uses placeholder data for test server and ignores the errors).
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| ---------------------- | ------------------ | -------------------- | -------------------------------------------------------------------------------------------------------------- |
|
||||
| APP_PORT | Number | 4010 | The port the backend server listens on. Is optional. |
|
||||
| DB_TYPE | String | N/A | The type of the db u want to use. It has to be supported by typeorm. Possible: `sqlite`, `mysql`, `postgresql` |
|
||||
| DB_HOST | String | N/A | The db's host's ip-address/fqdn or file path for sqlite |
|
||||
| DB_PORT | String | N/A | The db's port |
|
||||
| DB_USER | String | N/A | The user for accessing the db |
|
||||
| DB_PASSWORD | String | N/A | The user's password for accessing the db |
|
||||
| DB_NAME | String | N/A | The db's name |
|
||||
| NODE_ENV | String | dev | The apps env - influences debug info. Also when the env is set to "test", mailing errors get ignored. |
|
||||
| POSTALCODE_COUNTRYCODE | String/CountryCode | N/A | The countrycode used to validate address's postal codes |
|
||||
| PHONE_COUNTRYCODE | String/CountryCode | null (international) | The countrycode used to validate phone numers |
|
||||
| SEED_TEST_DATA | Boolean | False | If you want the app to seed some example data set this to true |
|
||||
| MAILER_URL | String(Url) | N/A | The mailer's base url (no trailing slash) |
|
||||
| MAILER_KEY | String | N/A | The mailer's api key. |
|
||||
| SELFSERVICE_URL | String(Url) | N/A | The link to selfservice (no trailing slash) |
|
||||
| IMPRINT_URL | String(Url) | /imprint | The link to a imprint page for the system (Defaults to the frontend's imprint) |
|
||||
| PRIVACY_URL | String(Url) | /privacy | The link to a privacy page for the system (Defaults to the frontend's privacy page) |
|
||||
| Name | Type | Default | Description |
|
||||
| ------------------------- | ------------------ | -------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
||||
| APP_PORT | Number | 4010 | The port the backend server listens on. Is optional. |
|
||||
| DB_TYPE | String | N/A | The type of the db you want to use. Supported by TypeORM. Possible: `sqlite`, `mysql`, `postgresql` |
|
||||
| DB_HOST | String | N/A | The db's host IP address/FQDN or file path for sqlite |
|
||||
| DB_PORT | String | N/A | The db's port |
|
||||
| DB_USER | String | N/A | The user for accessing the db |
|
||||
| DB_PASSWORD | String | N/A | The user's password for accessing the db |
|
||||
| DB_NAME | String | N/A | The db's name |
|
||||
| NODE_ENV | String | dev | The app's env - influences debug info. When set to "test", mailing errors get ignored. |
|
||||
| POSTALCODE_COUNTRYCODE | String/CountryCode | N/A | The country code used to validate address postal codes |
|
||||
| PHONE_COUNTRYCODE | String/CountryCode | null (international) | The country code used to validate phone numbers |
|
||||
| SEED_TEST_DATA | Boolean | false | If you want the app to seed example data, set this to true |
|
||||
| STATION_TOKEN_SECRET | String | N/A | Secret key for HMAC-SHA256 station token generation (min 32 chars). **Required.** |
|
||||
| NATS_URL | String(URL) | nats://localhost:4222 | NATS server connection URL for KV cache |
|
||||
| NATS_PREWARM | Boolean | false | Preload all runner state into NATS cache at startup (eliminates DB reads on first scan) |
|
||||
| MAILER_URL | String(URL) | N/A | The mailer's base URL (no trailing slash) |
|
||||
| MAILER_KEY | String | N/A | The mailer's API key |
|
||||
| SELFSERVICE_URL | String(URL) | N/A | The link to selfservice (no trailing slash) |
|
||||
| IMPRINT_URL | String(URL) | /imprint | The link to an imprint page for the system (defaults to the frontend's imprint) |
|
||||
| PRIVACY_URL | String(URL) | /privacy | The link to a privacy page for the system (defaults to the frontend's privacy page) |
|
||||
|
||||
|
||||
## Recommended Editor
|
||||
|
||||
6
bunfig.toml
Normal file
6
bunfig.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
# Bun configuration
|
||||
# See: https://bun.sh/docs/runtime/bunfig
|
||||
|
||||
[runtime]
|
||||
# Enable Node.js compatibility mode
|
||||
bun = true
|
||||
58
licenses.md
58
licenses.md
@@ -1280,35 +1280,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
# nodemon
|
||||
**Author**: [object Object]
|
||||
**Repo**: [object Object]
|
||||
**License**: MIT
|
||||
**Description**: Simple monitor script for use during development of a node.js app.
|
||||
## License Text
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2010 - present, Remy Sharp, https://remysharp.com <remy@remysharp.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
# release-it
|
||||
**Author**: [object Object]
|
||||
**Repo**: [object Object]
|
||||
@@ -1398,35 +1369,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
# ts-node
|
||||
**Author**: [object Object]
|
||||
**Repo**: [object Object]
|
||||
**License**: MIT
|
||||
**Description**: TypeScript execution environment and REPL for node.js, with source map support
|
||||
## License Text
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Blake Embrey (hello@blakeembrey.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
# typedoc
|
||||
**Author**: undefined
|
||||
**Repo**: [object Object]
|
||||
|
||||
10
ormconfig.js
10
ormconfig.js
@@ -1,7 +1,8 @@
|
||||
const dotenv = require('dotenv');
|
||||
dotenv.config();
|
||||
//
|
||||
const SOURCE_PATH = process.env.NODE_ENV === 'production' ? 'dist' : 'src';
|
||||
// Bun workflow: always compile first, then run from dist/
|
||||
const SOURCE_PATH = 'dist';
|
||||
module.exports = {
|
||||
type: process.env.DB_TYPE,
|
||||
host: process.env.DB_HOST,
|
||||
@@ -9,8 +10,7 @@ module.exports = {
|
||||
username: process.env.DB_USER,
|
||||
password: process.env.DB_PASSWORD,
|
||||
database: process.env.DB_NAME,
|
||||
// entities: ["src/**/entities/*.ts"],
|
||||
entities: [ `${SOURCE_PATH}/**/entities/*{.ts,.js}` ],
|
||||
seeds: [ `${SOURCE_PATH}/**/seeds/*{.ts,.js}` ]
|
||||
// seeds: ['src/seeds/*.ts'],
|
||||
// Always load compiled .js files from dist/ (TypeORM entities have circular deps)
|
||||
entities: [ `${SOURCE_PATH}/**/entities/*.js` ],
|
||||
seeds: [ `${SOURCE_PATH}/**/seeds/*.js` ]
|
||||
};
|
||||
|
||||
25
package.json
25
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@odit/lfk-backend",
|
||||
"version": "1.6.0",
|
||||
"version": "1.7.0",
|
||||
"main": "src/app.ts",
|
||||
"repository": "https://git.odit.services/lfk/backend",
|
||||
"author": {
|
||||
@@ -63,27 +63,26 @@
|
||||
"auto-changelog": "2.4.0",
|
||||
"cp-cli": "2.0.0",
|
||||
"jest": "26.6.3",
|
||||
"nodemon": "2.0.7",
|
||||
"release-it": "14.2.2",
|
||||
"rimraf": "3.0.2",
|
||||
"start-server-and-test": "1.11.7",
|
||||
"ts-jest": "26.5.0",
|
||||
"ts-node": "10.9.2",
|
||||
"typedoc": "0.20.19",
|
||||
"typescript": "5.9.3"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "nodemon src/app.ts",
|
||||
"dev": "bun scripts/dev_watch.ts",
|
||||
"start": "bun dist/app.js",
|
||||
"build": "rimraf ./dist && tsc && cp-cli ./src/static ./dist/static",
|
||||
"docs": "typedoc --out docs src",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watchAll",
|
||||
"test:ci:generate_env": "ts-node scripts/create_testenv.ts",
|
||||
"test:ci:generate_env": "bun scripts/create_testenv.ts",
|
||||
"test:ci:run": "start-server-and-test dev http://localhost:4010/api/docs/openapi.json test",
|
||||
"test:ci": "npm run test:ci:generate_env && npm run test:ci:run",
|
||||
"benchmark": "ts-node scripts/benchmark_scan_intake.ts",
|
||||
"seed": "ts-node ./node_modules/typeorm/cli.js schema:sync && ts-node ./node_modules/typeorm-seeding/dist/cli.js seed",
|
||||
"openapi:export": "ts-node scripts/openapi_export.ts",
|
||||
"test:ci": "bun run test:ci:generate_env && bun run test:ci:run",
|
||||
"benchmark": "bun scripts/benchmark_scan_intake.ts",
|
||||
"seed": "bun ./node_modules/typeorm/cli.js schema:sync && bun ./node_modules/typeorm-seeding/dist/cli.js seed",
|
||||
"openapi:export": "bun scripts/openapi_export.ts",
|
||||
"licenses:export": "license-exporter --markdown",
|
||||
"changelog:export": "auto-changelog --commit-limit false -p -u --hide-credit",
|
||||
"release": "release-it --only-version"
|
||||
@@ -103,13 +102,7 @@
|
||||
"publish": false
|
||||
},
|
||||
"hooks": {
|
||||
"after:bump": "npm run changelog:export && npm run licenses:export && git add CHANGELOG.md && git add licenses.md"
|
||||
"after:bump": "bun run changelog:export && bun run licenses:export && git add CHANGELOG.md && git add licenses.md"
|
||||
}
|
||||
},
|
||||
"nodemonConfig": {
|
||||
"ignore": [
|
||||
"src/tests/*",
|
||||
"docs/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
* Scan Intake Benchmark Script
|
||||
*
|
||||
* Measures TrackScan creation performance before and after each optimisation phase.
|
||||
* Run against a live dev server: npm run dev
|
||||
* Run against a live dev server: bun run dev
|
||||
*
|
||||
* Usage:
|
||||
* npx ts-node scripts/benchmark_scan_intake.ts
|
||||
* npx ts-node scripts/benchmark_scan_intake.ts --base http://localhost:4010
|
||||
* bun run benchmark
|
||||
* bun scripts/benchmark_scan_intake.ts --base http://localhost:4010
|
||||
*
|
||||
* What it measures:
|
||||
* 1. Single sequential scans — baseline latency per request (p50/p95/p99/max)
|
||||
|
||||
138
scripts/dev_watch.ts
Normal file
138
scripts/dev_watch.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env bun
|
||||
/**
|
||||
* Development watch script for Bun
|
||||
*
|
||||
* Watches src/ for changes, rebuilds on change, and restarts the server.
|
||||
* This is necessary because we must compile TypeScript first due to circular
|
||||
* TypeORM entity dependencies that Bun's TS loader cannot handle directly.
|
||||
*/
|
||||
|
||||
import { watch } from "fs";
|
||||
import { spawn } from "child_process";
|
||||
import consola from "consola";
|
||||
|
||||
let serverProcess: ReturnType<typeof spawn> | null = null;
|
||||
let isRebuilding = false;
|
||||
let pendingRestart = false;
|
||||
let debounceTimer: NodeJS.Timeout | null = null;
|
||||
let watcherReady = false;
|
||||
|
||||
function killServer() {
|
||||
return new Promise<void>((resolve) => {
|
||||
if (serverProcess) {
|
||||
consola.info("Stopping server...");
|
||||
serverProcess.kill();
|
||||
serverProcess = null;
|
||||
// Wait for port to be fully released (longer on Windows)
|
||||
setTimeout(resolve, 2000);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function startServer() {
|
||||
consola.info("Starting server...");
|
||||
serverProcess = spawn("bun", ["dist/app.js"], {
|
||||
stdio: "inherit",
|
||||
shell: true,
|
||||
});
|
||||
|
||||
serverProcess.on("error", (err) => {
|
||||
consola.error("Server process error:", err);
|
||||
});
|
||||
|
||||
serverProcess.on("exit", (code) => {
|
||||
if (code !== null && code !== 0 && code !== 143) {
|
||||
consola.error(`Server exited with code ${code}`);
|
||||
}
|
||||
});
|
||||
|
||||
// Enable watcher after initial server start
|
||||
if (!watcherReady) {
|
||||
setTimeout(() => {
|
||||
watcherReady = true;
|
||||
consola.success("👀 Watching for file changes...");
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
async function rebuild() {
|
||||
if (isRebuilding) {
|
||||
pendingRestart = true;
|
||||
return;
|
||||
}
|
||||
|
||||
isRebuilding = true;
|
||||
pendingRestart = false;
|
||||
|
||||
consola.info("Rebuilding...");
|
||||
await killServer();
|
||||
|
||||
const buildProcess = spawn("bun", ["run", "build"], {
|
||||
stdio: "inherit",
|
||||
shell: true,
|
||||
});
|
||||
|
||||
buildProcess.on("exit", (code) => {
|
||||
isRebuilding = false;
|
||||
|
||||
if (code === 0) {
|
||||
consola.success("Build complete!");
|
||||
startServer();
|
||||
|
||||
if (pendingRestart) {
|
||||
consola.info("Change detected during build, rebuilding again...");
|
||||
setTimeout(() => rebuild(), 100);
|
||||
}
|
||||
} else {
|
||||
consola.error(`Build failed with code ${code}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Initial build and start
|
||||
consola.info("🔄 Development mode - watching for changes...");
|
||||
rebuild();
|
||||
|
||||
// Watch src/ for changes (including subdirectories)
|
||||
const watcher = watch(
|
||||
"./src",
|
||||
{ recursive: true },
|
||||
(eventType, filename) => {
|
||||
if (!watcherReady) return; // Ignore changes during initial build
|
||||
|
||||
if (filename && filename.endsWith(".ts")) {
|
||||
// Ignore test files and declaration files
|
||||
if (filename.endsWith(".spec.ts") || filename.endsWith(".d.ts")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Debounce: wait 500ms for multiple rapid changes
|
||||
if (debounceTimer) {
|
||||
clearTimeout(debounceTimer);
|
||||
}
|
||||
|
||||
debounceTimer = setTimeout(() => {
|
||||
consola.info(`File changed: ${filename}`);
|
||||
rebuild();
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Cleanup on exit
|
||||
process.on("SIGINT", () => {
|
||||
consola.info("\nShutting down...");
|
||||
if (debounceTimer) clearTimeout(debounceTimer);
|
||||
killServer();
|
||||
watcher.close();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
process.on("SIGTERM", () => {
|
||||
if (debounceTimer) clearTimeout(debounceTimer);
|
||||
killServer();
|
||||
watcher.close();
|
||||
process.exit(0);
|
||||
});
|
||||
@@ -7,7 +7,8 @@ import authchecker from "./middlewares/authchecker";
|
||||
import { ErrorHandler } from './middlewares/ErrorHandler';
|
||||
import UserChecker from './middlewares/UserChecker';
|
||||
|
||||
const CONTROLLERS_FILE_EXTENSION = process.env.NODE_ENV === 'production' ? 'js' : 'ts';
|
||||
// Always use .js when running from dist/ (Bun workflow: always build first)
|
||||
const CONTROLLERS_FILE_EXTENSION = 'js';
|
||||
const app = createExpressServer({
|
||||
authorizationChecker: authchecker,
|
||||
currentUserChecker: UserChecker,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import consola from 'consola';
|
||||
import { config as configDotenv } from 'dotenv';
|
||||
import { CountryCode } from 'libphonenumber-js';
|
||||
import ValidatorJS from 'validator';
|
||||
|
||||
import consola from 'consola';
|
||||
import { config as configDotenv } from 'dotenv';
|
||||
import { CountryCode } from 'libphonenumber-js';
|
||||
import ValidatorJS from 'validator';
|
||||
|
||||
configDotenv();
|
||||
export const config = {
|
||||
internal_port: parseInt(process.env.APP_PORT) || 4010,
|
||||
|
||||
35
src/models/entities/index.ts
Normal file
35
src/models/entities/index.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Entity barrel file for Bun compatibility.
|
||||
* Imports all entities in the correct order to resolve circular dependencies.
|
||||
*/
|
||||
|
||||
// Base/parent entities first
|
||||
export * from './Participant';
|
||||
export * from './Donation';
|
||||
export * from './Scan';
|
||||
|
||||
// Child entities that depend on the above
|
||||
export * from './Runner';
|
||||
export * from './DistanceDonation';
|
||||
export * from './FixedDonation';
|
||||
export * from './TrackScan';
|
||||
|
||||
// Entities with cross-references
|
||||
export * from './RunnerCard';
|
||||
export * from './RunnerGroup';
|
||||
export * from './RunnerOrganization';
|
||||
export * from './RunnerTeam';
|
||||
export * from './ScanStation';
|
||||
export * from './Track';
|
||||
|
||||
// Independent entities
|
||||
export * from './Address';
|
||||
export * from './ConfigFlags';
|
||||
export * from './Donor';
|
||||
export * from './GroupContact';
|
||||
export * from './Permission';
|
||||
export * from './Principal';
|
||||
export * from './StatsClient';
|
||||
export * from './User';
|
||||
export * from './UserAction';
|
||||
export * from './UserGroup';
|
||||
Reference in New Issue
Block a user