Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
d1c4744231
|
|||
|
fe90414dd9
|
|||
|
21ceb9fa26
|
|||
|
5081819281
|
|||
|
240bd9cba1
|
@@ -10,12 +10,9 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Set up Node.js
|
- uses: oven-sh/setup-bun@v2
|
||||||
uses: actions/setup-node@v4
|
- run: bun install --frozen-lockfile
|
||||||
with:
|
- run: bun licenses:export
|
||||||
node-version: 19
|
|
||||||
- run: npm i -g pnpm@10.7 && pnpm i
|
|
||||||
- run: pnpm licenses:export
|
|
||||||
- name: Login to registry
|
- name: Login to registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -126,8 +126,12 @@ dist
|
|||||||
.yarn/build-state.yml
|
.yarn/build-state.yml
|
||||||
.yarn/install-state.gz
|
.yarn/install-state.gz
|
||||||
.pnp.*
|
.pnp.*
|
||||||
|
|
||||||
|
# Old package manager lockfiles (Bun migration - keep bun.lock)
|
||||||
yarn.lock
|
yarn.lock
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
pnpm-lock.yaml
|
||||||
|
|
||||||
build
|
build
|
||||||
|
|
||||||
*.sqlite
|
*.sqlite
|
||||||
|
|||||||
15
CHANGELOG.md
15
CHANGELOG.md
@@ -2,10 +2,25 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file. Dates are displayed in UTC.
|
All notable changes to this project will be documented in this file. Dates are displayed in UTC.
|
||||||
|
|
||||||
|
#### [1.7.1](https://git.odit.services/lfk/backend/compare/1.7.0...1.7.1)
|
||||||
|
|
||||||
|
- fix(ci): Switch to bun in ci [`fe90414`](https://git.odit.services/lfk/backend/commit/fe90414dd910baff8107197408575b6af0cc4cbf)
|
||||||
|
- perf(db): Added indexes [`21ceb9f`](https://git.odit.services/lfk/backend/commit/21ceb9fa265df2f2193a6c4fb58080ead9c72bf8)
|
||||||
|
|
||||||
|
#### [1.7.0](https://git.odit.services/lfk/backend/compare/1.6.0...1.7.0)
|
||||||
|
|
||||||
|
> 20 February 2026
|
||||||
|
|
||||||
|
- refactor: Bun by default [`240bd9c`](https://git.odit.services/lfk/backend/commit/240bd9cba10636bfc100ea2732508d805639f105)
|
||||||
|
- chore(release): 1.7.0 [`5081819`](https://git.odit.services/lfk/backend/commit/5081819281eacd6beb8d4876f0a9df71c901e84e)
|
||||||
|
|
||||||
#### [1.6.0](https://git.odit.services/lfk/backend/compare/1.5.2...1.6.0)
|
#### [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)
|
- 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(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(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)
|
- 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)
|
- 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
|
# Typescript Build
|
||||||
FROM registry.odit.services/hub/library/node:23.10.0-alpine3.21 AS build
|
FROM registry.odit.services/hub/oven/bun:1.3.9-alpine AS build
|
||||||
ARG NPM_REGISTRY_URL=https://registry.npmjs.org
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY package.json ./
|
COPY package.json bun.lockb* ./
|
||||||
COPY pnpm-workspace.yaml ./
|
RUN bun install --frozen-lockfile
|
||||||
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 tsconfig.json ormconfig.js ./
|
COPY tsconfig.json ormconfig.js bunfig.toml ./
|
||||||
COPY src ./src
|
COPY src ./src
|
||||||
RUN pnpm run build \
|
RUN bun run build \
|
||||||
&& rm -rf /app/node_modules \
|
&& rm -rf /app/node_modules \
|
||||||
&& pnpm i --production --prefer-offline
|
&& bun install --production --frozen-lockfile
|
||||||
|
|
||||||
# final image
|
# 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
|
WORKDIR /app
|
||||||
COPY --from=build /app/package.json /app/package.json
|
COPY --from=build /app/package.json /app/package.json
|
||||||
COPY --from=build /app/pnpm-lock.yaml /app/pnpm-lock.yaml
|
COPY --from=build /app/bun.lockb* /app/
|
||||||
COPY --from=build /app/pnpm-workspace.yaml /app/pnpm-workspace.yaml
|
|
||||||
COPY --from=build /app/ormconfig.js /app/ormconfig.js
|
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/dist /app/dist
|
||||||
COPY --from=build /app/node_modules /app/node_modules
|
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
|
Backend Server
|
||||||
|
|
||||||
## Quickstart 🐳
|
## Prerequisites
|
||||||
> Use this to run the backend with a postgresql db in docker
|
|
||||||
|
|
||||||
1. Clone the repo or copy the docker-compose
|
This project uses **Bun** as the runtime and package manager. Install Bun first:
|
||||||
2. Run in toe folder that contains the docker-compose file: `docker-compose up -d`
|
|
||||||
|
```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
|
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`)
|
4. You can now use the default admin user (`demo:demo`)
|
||||||
|
|
||||||
## Dev Setup 🛠
|
## 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)
|
1. Rename the `.env.example` file to `.env` (you can adjust app port and other settings if needed)
|
||||||
2. Install Dependencies
|
2. Start NATS (required for KV cache):
|
||||||
```bash
|
```bash
|
||||||
pnpm i
|
docker-compose up -d nats
|
||||||
```
|
```
|
||||||
3. Start the server
|
3. Install dependencies:
|
||||||
```bash
|
```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
|
### Run Tests
|
||||||
```bash
|
```bash
|
||||||
# Run tests once (server has to run)
|
# Run tests once (server has to be running)
|
||||||
pnpm test
|
bun test
|
||||||
|
|
||||||
# Run test in watch mode (reruns on change)
|
# 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)
|
# Run test in CI mode (automatically starts the dev server)
|
||||||
pnpm test:ci
|
bun run test:ci
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run Benchmarks
|
||||||
|
```bash
|
||||||
|
# Start the server first
|
||||||
|
bun run dev
|
||||||
|
|
||||||
|
# In another terminal:
|
||||||
|
bun run benchmark
|
||||||
```
|
```
|
||||||
|
|
||||||
### Generate Docs
|
### Generate Docs
|
||||||
```bash
|
```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
|
## ENV Vars
|
||||||
> You can provide them via .env file or docker 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 |
|
| Name | Type | Default | Description |
|
||||||
| ---------------------- | ------------------ | -------------------- | -------------------------------------------------------------------------------------------------------------- |
|
| ------------------------- | ------------------ | -------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
||||||
| APP_PORT | Number | 4010 | The port the backend server listens on. Is optional. |
|
| 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_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's ip-address/fqdn or file path for sqlite |
|
| 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_PORT | String | N/A | The db's port |
|
||||||
| DB_USER | String | N/A | The user for accessing the db |
|
| 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_PASSWORD | String | N/A | The user's password for accessing the db |
|
||||||
| DB_NAME | String | N/A | The db's name |
|
| 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. |
|
| 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 countrycode used to validate address's postal codes |
|
| POSTALCODE_COUNTRYCODE | String/CountryCode | N/A | The country code used to validate address postal codes |
|
||||||
| PHONE_COUNTRYCODE | String/CountryCode | null (international) | The countrycode used to validate phone numers |
|
| 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 some example data set this to true |
|
| SEED_TEST_DATA | Boolean | false | If you want the app to seed example data, set this to true |
|
||||||
| MAILER_URL | String(Url) | N/A | The mailer's base url (no trailing slash) |
|
| STATION_TOKEN_SECRET | String | N/A | Secret key for HMAC-SHA256 station token generation (min 32 chars). **Required.** |
|
||||||
| MAILER_KEY | String | N/A | The mailer's api key. |
|
| NATS_URL | String(URL) | nats://localhost:4222 | NATS server connection URL for KV cache |
|
||||||
| SELFSERVICE_URL | String(Url) | N/A | The link to selfservice (no trailing slash) |
|
| NATS_PREWARM | Boolean | false | Preload all runner state into NATS cache at startup (eliminates DB reads on first scan) |
|
||||||
| IMPRINT_URL | String(Url) | /imprint | The link to a imprint page for the system (Defaults to the frontend's imprint) |
|
| MAILER_URL | String(URL) | N/A | The mailer's base URL (no trailing slash) |
|
||||||
| PRIVACY_URL | String(Url) | /privacy | The link to a privacy page for the system (Defaults to the frontend's privacy page) |
|
| 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
|
## 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.
|
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
|
# release-it
|
||||||
**Author**: [object Object]
|
**Author**: [object Object]
|
||||||
**Repo**: [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.
|
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
|
# typedoc
|
||||||
**Author**: undefined
|
**Author**: undefined
|
||||||
**Repo**: [object Object]
|
**Repo**: [object Object]
|
||||||
|
|||||||
10
ormconfig.js
10
ormconfig.js
@@ -1,7 +1,8 @@
|
|||||||
const dotenv = require('dotenv');
|
const dotenv = require('dotenv');
|
||||||
dotenv.config();
|
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 = {
|
module.exports = {
|
||||||
type: process.env.DB_TYPE,
|
type: process.env.DB_TYPE,
|
||||||
host: process.env.DB_HOST,
|
host: process.env.DB_HOST,
|
||||||
@@ -9,8 +10,7 @@ module.exports = {
|
|||||||
username: process.env.DB_USER,
|
username: process.env.DB_USER,
|
||||||
password: process.env.DB_PASSWORD,
|
password: process.env.DB_PASSWORD,
|
||||||
database: process.env.DB_NAME,
|
database: process.env.DB_NAME,
|
||||||
// entities: ["src/**/entities/*.ts"],
|
// Always load compiled .js files from dist/ (TypeORM entities have circular deps)
|
||||||
entities: [ `${SOURCE_PATH}/**/entities/*{.ts,.js}` ],
|
entities: [ `${SOURCE_PATH}/**/entities/*.js` ],
|
||||||
seeds: [ `${SOURCE_PATH}/**/seeds/*{.ts,.js}` ]
|
seeds: [ `${SOURCE_PATH}/**/seeds/*.js` ]
|
||||||
// seeds: ['src/seeds/*.ts'],
|
|
||||||
};
|
};
|
||||||
|
|||||||
25
package.json
25
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@odit/lfk-backend",
|
"name": "@odit/lfk-backend",
|
||||||
"version": "1.6.0",
|
"version": "1.7.1",
|
||||||
"main": "src/app.ts",
|
"main": "src/app.ts",
|
||||||
"repository": "https://git.odit.services/lfk/backend",
|
"repository": "https://git.odit.services/lfk/backend",
|
||||||
"author": {
|
"author": {
|
||||||
@@ -63,27 +63,26 @@
|
|||||||
"auto-changelog": "2.4.0",
|
"auto-changelog": "2.4.0",
|
||||||
"cp-cli": "2.0.0",
|
"cp-cli": "2.0.0",
|
||||||
"jest": "26.6.3",
|
"jest": "26.6.3",
|
||||||
"nodemon": "2.0.7",
|
|
||||||
"release-it": "14.2.2",
|
"release-it": "14.2.2",
|
||||||
"rimraf": "3.0.2",
|
"rimraf": "3.0.2",
|
||||||
"start-server-and-test": "1.11.7",
|
"start-server-and-test": "1.11.7",
|
||||||
"ts-jest": "26.5.0",
|
"ts-jest": "26.5.0",
|
||||||
"ts-node": "10.9.2",
|
|
||||||
"typedoc": "0.20.19",
|
"typedoc": "0.20.19",
|
||||||
"typescript": "5.9.3"
|
"typescript": "5.9.3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"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",
|
"build": "rimraf ./dist && tsc && cp-cli ./src/static ./dist/static",
|
||||||
"docs": "typedoc --out docs src",
|
"docs": "typedoc --out docs src",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:watch": "jest --watchAll",
|
"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: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",
|
"test:ci": "bun run test:ci:generate_env && bun run test:ci:run",
|
||||||
"benchmark": "ts-node scripts/benchmark_scan_intake.ts",
|
"benchmark": "bun 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",
|
"seed": "bun ./node_modules/typeorm/cli.js schema:sync && bun ./node_modules/typeorm-seeding/dist/cli.js seed",
|
||||||
"openapi:export": "ts-node scripts/openapi_export.ts",
|
"openapi:export": "bun scripts/openapi_export.ts",
|
||||||
"licenses:export": "license-exporter --markdown",
|
"licenses:export": "license-exporter --markdown",
|
||||||
"changelog:export": "auto-changelog --commit-limit false -p -u --hide-credit",
|
"changelog:export": "auto-changelog --commit-limit false -p -u --hide-credit",
|
||||||
"release": "release-it --only-version"
|
"release": "release-it --only-version"
|
||||||
@@ -103,13 +102,7 @@
|
|||||||
"publish": false
|
"publish": false
|
||||||
},
|
},
|
||||||
"hooks": {
|
"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
|
* Scan Intake Benchmark Script
|
||||||
*
|
*
|
||||||
* Measures TrackScan creation performance before and after each optimisation phase.
|
* 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:
|
* Usage:
|
||||||
* npx ts-node scripts/benchmark_scan_intake.ts
|
* bun run benchmark
|
||||||
* npx ts-node scripts/benchmark_scan_intake.ts --base http://localhost:4010
|
* bun scripts/benchmark_scan_intake.ts --base http://localhost:4010
|
||||||
*
|
*
|
||||||
* What it measures:
|
* What it measures:
|
||||||
* 1. Single sequential scans — baseline latency per request (p50/p95/p99/max)
|
* 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 { ErrorHandler } from './middlewares/ErrorHandler';
|
||||||
import UserChecker from './middlewares/UserChecker';
|
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({
|
const app = createExpressServer({
|
||||||
authorizationChecker: authchecker,
|
authorizationChecker: authchecker,
|
||||||
currentUserChecker: UserChecker,
|
currentUserChecker: UserChecker,
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import consola from 'consola';
|
import consola from 'consola';
|
||||||
import { config as configDotenv } from 'dotenv';
|
import { config as configDotenv } from 'dotenv';
|
||||||
import { CountryCode } from 'libphonenumber-js';
|
import { CountryCode } from 'libphonenumber-js';
|
||||||
import ValidatorJS from 'validator';
|
import ValidatorJS from 'validator';
|
||||||
|
|
||||||
configDotenv();
|
configDotenv();
|
||||||
export const config = {
|
export const config = {
|
||||||
internal_port: parseInt(process.env.APP_PORT) || 4010,
|
internal_port: parseInt(process.env.APP_PORT) || 4010,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { IsInt, IsNotEmpty, IsPositive } from "class-validator";
|
import { IsInt, IsNotEmpty, IsPositive } from "class-validator";
|
||||||
import { ChildEntity, Column, ManyToOne } from "typeorm";
|
import { ChildEntity, Column, Index, ManyToOne } from "typeorm";
|
||||||
import { ResponseDistanceDonation } from '../responses/ResponseDistanceDonation';
|
import { ResponseDistanceDonation } from '../responses/ResponseDistanceDonation';
|
||||||
import { Donation } from "./Donation";
|
import { Donation } from "./Donation";
|
||||||
import { Runner } from "./Runner";
|
import { Runner } from "./Runner";
|
||||||
@@ -7,8 +7,9 @@ import { Runner } from "./Runner";
|
|||||||
/**
|
/**
|
||||||
* Defines the DistanceDonation entity.
|
* Defines the DistanceDonation entity.
|
||||||
* For distanceDonations a donor pledges to donate a certain amount for each kilometer ran by a runner.
|
* For distanceDonations a donor pledges to donate a certain amount for each kilometer ran by a runner.
|
||||||
*/
|
*/
|
||||||
@ChildEntity()
|
@ChildEntity()
|
||||||
|
@Index(['runner'])
|
||||||
export class DistanceDonation extends Donation {
|
export class DistanceDonation extends Donation {
|
||||||
/**
|
/**
|
||||||
* The donation's associated runner.
|
* The donation's associated runner.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import {
|
|||||||
IsInt,
|
IsInt,
|
||||||
IsPositive
|
IsPositive
|
||||||
} from "class-validator";
|
} from "class-validator";
|
||||||
import { BeforeInsert, BeforeUpdate, Column, Entity, ManyToOne, PrimaryGeneratedColumn, TableInheritance } from "typeorm";
|
import { BeforeInsert, BeforeUpdate, Column, Entity, Index, ManyToOne, PrimaryGeneratedColumn, TableInheritance } from "typeorm";
|
||||||
import { ResponseDonation } from '../responses/ResponseDonation';
|
import { ResponseDonation } from '../responses/ResponseDonation';
|
||||||
import { Donor } from './Donor';
|
import { Donor } from './Donor';
|
||||||
|
|
||||||
@@ -10,9 +10,10 @@ import { Donor } from './Donor';
|
|||||||
* Defines the Donation entity.
|
* Defines the Donation entity.
|
||||||
* A donation just associates a donor with a donation amount.
|
* A donation just associates a donor with a donation amount.
|
||||||
* The specifics of the amoun's determination has to be implemented in child classes.
|
* The specifics of the amoun's determination has to be implemented in child classes.
|
||||||
*/
|
*/
|
||||||
@Entity()
|
@Entity()
|
||||||
@TableInheritance({ column: { name: "type", type: "varchar" } })
|
@TableInheritance({ column: { name: "type", type: "varchar" } })
|
||||||
|
@Index(['donor'])
|
||||||
export abstract class Donation {
|
export abstract class Donation {
|
||||||
/**
|
/**
|
||||||
* Autogenerated unique id (primary key).
|
* Autogenerated unique id (primary key).
|
||||||
|
|||||||
@@ -9,18 +9,19 @@ import {
|
|||||||
|
|
||||||
IsString
|
IsString
|
||||||
} from "class-validator";
|
} from "class-validator";
|
||||||
import { BeforeInsert, BeforeUpdate, Column, Entity, PrimaryGeneratedColumn, TableInheritance } from "typeorm";
|
import { BeforeInsert, BeforeUpdate, Column, Entity, Index, PrimaryGeneratedColumn, TableInheritance } from "typeorm";
|
||||||
import { config } from '../../config';
|
import { config } from '../../config';
|
||||||
import { ResponseParticipant } from '../responses/ResponseParticipant';
|
import { ResponseParticipant } from '../responses/ResponseParticipant';
|
||||||
import { Address } from "./Address";
|
import { Address } from "./Address";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the Participant entity.
|
* Defines the Participant entity.
|
||||||
* Participans can donate and therefor be associated with donation entities.
|
* Participans can donate and therefor be associated with donation entities.
|
||||||
*/
|
*/
|
||||||
@Entity()
|
@Entity()
|
||||||
@TableInheritance({ column: { name: "type", type: "varchar" } })
|
@TableInheritance({ column: { name: "type", type: "varchar" } })
|
||||||
export abstract class Participant {
|
@Index(['email'])
|
||||||
|
export abstract class Participant {
|
||||||
/**
|
/**
|
||||||
* Autogenerated unique id (primary key).
|
* Autogenerated unique id (primary key).
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { IsInt, IsNotEmpty, IsOptional, IsString } from "class-validator";
|
import { IsInt, IsNotEmpty, IsOptional, IsString } from "class-validator";
|
||||||
import { ChildEntity, Column, ManyToOne, OneToMany } from "typeorm";
|
import { ChildEntity, Column, Index, ManyToOne, OneToMany } from "typeorm";
|
||||||
import { ResponseRunner } from '../responses/ResponseRunner';
|
import { ResponseRunner } from '../responses/ResponseRunner';
|
||||||
import { DistanceDonation } from "./DistanceDonation";
|
import { DistanceDonation } from "./DistanceDonation";
|
||||||
import { Participant } from "./Participant";
|
import { Participant } from "./Participant";
|
||||||
@@ -11,8 +11,9 @@ import { Scan } from "./Scan";
|
|||||||
* Defines the runner entity.
|
* Defines the runner entity.
|
||||||
* Runners differ from participants in being able to actually accumulate a ran distance through scans.
|
* Runners differ from participants in being able to actually accumulate a ran distance through scans.
|
||||||
* Runner's get organized in groups.
|
* Runner's get organized in groups.
|
||||||
*/
|
*/
|
||||||
@ChildEntity()
|
@ChildEntity()
|
||||||
|
@Index(['group'])
|
||||||
export class Runner extends Participant {
|
export class Runner extends Participant {
|
||||||
/**
|
/**
|
||||||
* The runner's associated group.
|
* The runner's associated group.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
IsOptional,
|
IsOptional,
|
||||||
IsPositive
|
IsPositive
|
||||||
} from "class-validator";
|
} from "class-validator";
|
||||||
import { BeforeInsert, BeforeUpdate, Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm";
|
import { BeforeInsert, BeforeUpdate, Column, Entity, Index, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm";
|
||||||
import { RunnerCardIdOutOfRangeError } from '../../errors/RunnerCardErrors';
|
import { RunnerCardIdOutOfRangeError } from '../../errors/RunnerCardErrors';
|
||||||
import { ResponseRunnerCard } from '../responses/ResponseRunnerCard';
|
import { ResponseRunnerCard } from '../responses/ResponseRunnerCard';
|
||||||
import { Runner } from "./Runner";
|
import { Runner } from "./Runner";
|
||||||
@@ -16,8 +16,10 @@ import { TrackScan } from "./TrackScan";
|
|||||||
* Defines the RunnerCard entity.
|
* Defines the RunnerCard entity.
|
||||||
* A runnerCard is a physical representation for a runner.
|
* A runnerCard is a physical representation for a runner.
|
||||||
* It can be associated with a runner to create scans via the scan station's.
|
* It can be associated with a runner to create scans via the scan station's.
|
||||||
*/
|
*/
|
||||||
@Entity()
|
@Entity()
|
||||||
|
@Index(['runner'])
|
||||||
|
@Index(['enabled'])
|
||||||
export class RunnerCard {
|
export class RunnerCard {
|
||||||
/**
|
/**
|
||||||
* Autogenerated unique id (primary key).
|
* Autogenerated unique id (primary key).
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { IsNotEmpty } from "class-validator";
|
import { IsNotEmpty } from "class-validator";
|
||||||
import { ChildEntity, ManyToOne } from "typeorm";
|
import { ChildEntity, Index, ManyToOne } from "typeorm";
|
||||||
import { ResponseRunnerTeam } from '../responses/ResponseRunnerTeam';
|
import { ResponseRunnerTeam } from '../responses/ResponseRunnerTeam';
|
||||||
import { RunnerGroup } from "./RunnerGroup";
|
import { RunnerGroup } from "./RunnerGroup";
|
||||||
import { RunnerOrganization } from "./RunnerOrganization";
|
import { RunnerOrganization } from "./RunnerOrganization";
|
||||||
@@ -7,8 +7,9 @@ import { RunnerOrganization } from "./RunnerOrganization";
|
|||||||
/**
|
/**
|
||||||
* Defines the RunnerTeam entity.
|
* Defines the RunnerTeam entity.
|
||||||
* This usually is a school class or department in a company.
|
* This usually is a school class or department in a company.
|
||||||
*/
|
*/
|
||||||
@ChildEntity()
|
@ChildEntity()
|
||||||
|
@Index(['parentGroup'])
|
||||||
export class RunnerTeam extends RunnerGroup {
|
export class RunnerTeam extends RunnerGroup {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -5,16 +5,19 @@ import {
|
|||||||
|
|
||||||
IsPositive
|
IsPositive
|
||||||
} from "class-validator";
|
} from "class-validator";
|
||||||
import { BeforeInsert, BeforeUpdate, Column, Entity, ManyToOne, PrimaryGeneratedColumn, TableInheritance } from "typeorm";
|
import { BeforeInsert, BeforeUpdate, Column, Entity, Index, ManyToOne, PrimaryGeneratedColumn, TableInheritance } from "typeorm";
|
||||||
import { ResponseScan } from '../responses/ResponseScan';
|
import { ResponseScan } from '../responses/ResponseScan';
|
||||||
import { Runner } from "./Runner";
|
import { Runner } from "./Runner";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the Scan entity.
|
* Defines the Scan entity.
|
||||||
* A scan basicly adds a certain distance to a runner's total ran distance.
|
* A scan basicly adds a certain distance to a runner's total ran distance.
|
||||||
*/
|
*/
|
||||||
@Entity()
|
@Entity()
|
||||||
@TableInheritance({ column: { name: "type", type: "varchar" } })
|
@TableInheritance({ column: { name: "type", type: "varchar" } })
|
||||||
|
@Index(['runner'])
|
||||||
|
@Index(['runner', 'created_at'])
|
||||||
|
@Index(['valid'])
|
||||||
export class Scan {
|
export class Scan {
|
||||||
/**
|
/**
|
||||||
* Autogenerated unique id (primary key).
|
* Autogenerated unique id (primary key).
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
IsPositive,
|
IsPositive,
|
||||||
IsString
|
IsString
|
||||||
} from "class-validator";
|
} from "class-validator";
|
||||||
import { BeforeInsert, BeforeUpdate, Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm";
|
import { BeforeInsert, BeforeUpdate, Column, Entity, Index, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm";
|
||||||
import { ResponseScanStation } from '../responses/ResponseScanStation';
|
import { ResponseScanStation } from '../responses/ResponseScanStation';
|
||||||
import { Track } from "./Track";
|
import { Track } from "./Track";
|
||||||
import { TrackScan } from "./TrackScan";
|
import { TrackScan } from "./TrackScan";
|
||||||
@@ -14,8 +14,11 @@ import { TrackScan } from "./TrackScan";
|
|||||||
/**
|
/**
|
||||||
* Defines the ScanStation entity.
|
* Defines the ScanStation entity.
|
||||||
* ScanStations get used to create TrackScans for runners based on a scan of their runnerCard.
|
* ScanStations get used to create TrackScans for runners based on a scan of their runnerCard.
|
||||||
*/
|
*/
|
||||||
@Entity()
|
@Entity()
|
||||||
|
@Index(['track'])
|
||||||
|
@Index(['prefix'])
|
||||||
|
@Index(['enabled'])
|
||||||
export class ScanStation {
|
export class ScanStation {
|
||||||
/**
|
/**
|
||||||
* Autogenerated unique id (primary key).
|
* Autogenerated unique id (primary key).
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
|
|
||||||
IsPositive
|
IsPositive
|
||||||
} from "class-validator";
|
} from "class-validator";
|
||||||
import { ChildEntity, Column, ManyToOne } from "typeorm";
|
import { ChildEntity, Column, Index, ManyToOne } from "typeorm";
|
||||||
import { ResponseTrackScan } from '../responses/ResponseTrackScan';
|
import { ResponseTrackScan } from '../responses/ResponseTrackScan';
|
||||||
import { RunnerCard } from "./RunnerCard";
|
import { RunnerCard } from "./RunnerCard";
|
||||||
import { Scan } from "./Scan";
|
import { Scan } from "./Scan";
|
||||||
@@ -16,8 +16,13 @@ import { Track } from "./Track";
|
|||||||
/**
|
/**
|
||||||
* Defines the TrackScan entity.
|
* Defines the TrackScan entity.
|
||||||
* A track scan usaually get's generated by a scan station.
|
* A track scan usaually get's generated by a scan station.
|
||||||
*/
|
*/
|
||||||
@ChildEntity()
|
@ChildEntity()
|
||||||
|
@Index(['track'])
|
||||||
|
@Index(['card'])
|
||||||
|
@Index(['station'])
|
||||||
|
@Index(['timestamp'])
|
||||||
|
@Index(['station', 'timestamp'])
|
||||||
export class TrackScan extends Scan {
|
export class TrackScan extends Scan {
|
||||||
/**
|
/**
|
||||||
* The scan's associated track.
|
* The scan's associated track.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { IsBoolean, IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString, IsUrl, IsUUID } from "class-validator";
|
import { IsBoolean, IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString, IsUrl, IsUUID } from "class-validator";
|
||||||
import { ChildEntity, Column, JoinTable, ManyToMany, OneToMany } from "typeorm";
|
import { ChildEntity, Column, Index, JoinTable, ManyToMany, OneToMany } from "typeorm";
|
||||||
import { config } from '../../config';
|
import { config } from '../../config';
|
||||||
import { ResponsePrincipal } from '../responses/ResponsePrincipal';
|
import { ResponsePrincipal } from '../responses/ResponsePrincipal';
|
||||||
import { ResponseUser } from '../responses/ResponseUser';
|
import { ResponseUser } from '../responses/ResponseUser';
|
||||||
@@ -8,12 +8,13 @@ import { Principal } from './Principal';
|
|||||||
import { UserAction } from './UserAction';
|
import { UserAction } from './UserAction';
|
||||||
import { UserGroup } from './UserGroup';
|
import { UserGroup } from './UserGroup';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the User entity.
|
* Defines the User entity.
|
||||||
* Users are the ones that can use the "admin" webui and do stuff in the backend.
|
* Users are the ones that can use the "admin" webui and do stuff in the backend.
|
||||||
*/
|
*/
|
||||||
@ChildEntity()
|
@ChildEntity()
|
||||||
export class User extends Principal {
|
@Index(['enabled'])
|
||||||
|
export class User extends Principal {
|
||||||
/**
|
/**
|
||||||
* The user's uuid.
|
* The user's uuid.
|
||||||
* Mainly gets used as a per-user salt for the password hash.
|
* Mainly gets used as a per-user salt for the password hash.
|
||||||
|
|||||||
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