Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
7910beb5cb
|
|||
|
1be8836df1
|
|||
|
70d6091a6a
|
@@ -2,9 +2,17 @@
|
|||||||
|
|
||||||
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.8.3](https://git.odit.services/lfk/backend/compare/1.8.2...1.8.3)
|
||||||
|
|
||||||
|
- fix(RunnerSelfServiceController): Update getStationMe method to use req.stationId instead of header [`70d6091`](https://git.odit.services/lfk/backend/commit/70d6091a6a8a1a3d532c7648d9af51cdf8735c94)
|
||||||
|
- refactor(ScanController, ResponseScanIntake, RunnerKV): Rename totalDistance to distance for consistency [`1be8836`](https://git.odit.services/lfk/backend/commit/1be8836df1a7dec7261356faac8d23bc9558df56)
|
||||||
|
|
||||||
#### [1.8.2](https://git.odit.services/lfk/backend/compare/1.8.1...1.8.2)
|
#### [1.8.2](https://git.odit.services/lfk/backend/compare/1.8.1...1.8.2)
|
||||||
|
|
||||||
|
> 20 February 2026
|
||||||
|
|
||||||
- chore(PERFORMANCE_IDEAS): Remove outdated performance optimization ideas document [`a1a2c27`](https://git.odit.services/lfk/backend/commit/a1a2c2747cda8ad4c049d0d3b188e993daa67a01)
|
- chore(PERFORMANCE_IDEAS): Remove outdated performance optimization ideas document [`a1a2c27`](https://git.odit.services/lfk/backend/commit/a1a2c2747cda8ad4c049d0d3b188e993daa67a01)
|
||||||
|
- chore(release): 1.8.2 [`1d9c451`](https://git.odit.services/lfk/backend/commit/1d9c451dfbcac105d7440797c32080e30252fd18)
|
||||||
- refactor(Dockerfile): Update build process and entry point for TypeScript application [`3197498`](https://git.odit.services/lfk/backend/commit/3197498ab37221156eef42311e58a4038c3309d1)
|
- refactor(Dockerfile): Update build process and entry point for TypeScript application [`3197498`](https://git.odit.services/lfk/backend/commit/3197498ab37221156eef42311e58a4038c3309d1)
|
||||||
- fix(deps): Add @types/bun dependency to devDependencies [`6caa185`](https://git.odit.services/lfk/backend/commit/6caa1850e3668bbf72912121d2d1923a5e22d6e8)
|
- fix(deps): Add @types/bun dependency to devDependencies [`6caa185`](https://git.odit.services/lfk/backend/commit/6caa1850e3668bbf72912121d2d1923a5e22d6e8)
|
||||||
- fix(CreateUser): Await password hashing in toEntity method [`80e7e79`](https://git.odit.services/lfk/backend/commit/80e7e7939c1ab35da9ece1cd9e6e1002e4e50d3a)
|
- fix(CreateUser): Await password hashing in toEntity method [`80e7e79`](https://git.odit.services/lfk/backend/commit/80e7e7939c1ab35da9ece1cd9e6e1002e4e50d3a)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@odit/lfk-backend",
|
"name": "@odit/lfk-backend",
|
||||||
"version": "1.8.2",
|
"version": "1.8.3",
|
||||||
"main": "src/app.ts",
|
"main": "src/app.ts",
|
||||||
"repository": "https://git.odit.services/lfk/backend",
|
"repository": "https://git.odit.services/lfk/backend",
|
||||||
"author": {
|
"author": {
|
||||||
|
|||||||
@@ -104,17 +104,21 @@ export class RunnerSelfServiceController {
|
|||||||
return responseScans;
|
return responseScans;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('/stations/me')
|
@Get('/stations/me')
|
||||||
@UseBefore(ScanAuth)
|
@UseBefore(ScanAuth)
|
||||||
@ResponseSchema(ResponseScanStation)
|
@ResponseSchema(ResponseScanStation)
|
||||||
@ResponseSchema(ScanStationNotFoundError, { statusCode: 404 })
|
@ResponseSchema(ScanStationNotFoundError, { statusCode: 404 })
|
||||||
@OnUndefined(ScanStationNotFoundError)
|
@OnUndefined(ScanStationNotFoundError)
|
||||||
@OpenAPI({ description: 'Lists basic information about the station whose token got provided. <br> This includes it\'s associated track.', security: [{ "StationApiToken": [] }] })
|
@OpenAPI({ description: 'Lists basic information about the station whose token got provided. <br> This includes it\'s associated track.', security: [{ "StationApiToken": [] }] })
|
||||||
async getStationMe(@Req() req: Request) {
|
async getStationMe(@Req() req: Request) {
|
||||||
let scan = await this.stationRepository.findOne({ id: parseInt(req.headers["station_id"].toString()) }, { relations: ['track'] })
|
// ScanAuth middleware sets req.stationId (not a header)
|
||||||
if (!scan) { throw new ScanStationNotFoundError(); }
|
if (!req.stationId) {
|
||||||
return scan.toResponse();
|
throw new ScanStationNotFoundError();
|
||||||
}
|
}
|
||||||
|
let scan = await this.stationRepository.findOne({ id: req.stationId }, { relations: ['track'] })
|
||||||
|
if (!scan) { throw new ScanStationNotFoundError(); }
|
||||||
|
return scan.toResponse();
|
||||||
|
}
|
||||||
|
|
||||||
@Post('/runners/login')
|
@Post('/runners/login')
|
||||||
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
|
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
import type { Request } from "express";
|
import type { Request } from "express";
|
||||||
import { Authorized, Body, Delete, Get, HttpError, JsonController, OnUndefined, Param, Post, Put, QueryParam, Req, UseBefore } from 'routing-controllers';
|
import { Authorized, Body, Delete, Get, HttpError, JsonController, OnUndefined, Param, Post, Put, QueryParam, Req, UseBefore } from 'routing-controllers';
|
||||||
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
|
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
|
||||||
import { Repository, getConnection, getConnectionManager } from 'typeorm';
|
import { getConnection, getConnectionManager, Repository } from 'typeorm';
|
||||||
import { RunnerNotFoundError } from '../errors/RunnerErrors';
|
import { RunnerNotFoundError } from '../errors/RunnerErrors';
|
||||||
import { ScanIdsNotMatchingError, ScanNotFoundError } from '../errors/ScanErrors';
|
import { ScanIdsNotMatchingError, ScanNotFoundError } from '../errors/ScanErrors';
|
||||||
import { ScanStationNotFoundError } from '../errors/ScanStationErrors';
|
import { ScanStationNotFoundError } from '../errors/ScanStationErrors';
|
||||||
import ScanAuth from '../middlewares/ScanAuth';
|
import ScanAuth from '../middlewares/ScanAuth';
|
||||||
import { deleteCardEntry, getCardEntry, setCardEntry } from '../nats/CardKV';
|
|
||||||
import { deleteRunnerEntry, getRunnerEntry, RunnerKVEntry, setRunnerEntry, warmRunner } from '../nats/RunnerKV';
|
|
||||||
import { getStationEntryById } from '../nats/StationKV';
|
|
||||||
import { CreateScan } from '../models/actions/create/CreateScan';
|
import { CreateScan } from '../models/actions/create/CreateScan';
|
||||||
import { CreateTrackScan } from '../models/actions/create/CreateTrackScan';
|
import { CreateTrackScan } from '../models/actions/create/CreateTrackScan';
|
||||||
import { UpdateScan } from '../models/actions/update/UpdateScan';
|
import { UpdateScan } from '../models/actions/update/UpdateScan';
|
||||||
@@ -20,6 +17,9 @@ import { ResponseEmpty } from '../models/responses/ResponseEmpty';
|
|||||||
import { ResponseScan } from '../models/responses/ResponseScan';
|
import { ResponseScan } from '../models/responses/ResponseScan';
|
||||||
import { ResponseScanIntake, ResponseScanIntakeRunner } from '../models/responses/ResponseScanIntake';
|
import { ResponseScanIntake, ResponseScanIntakeRunner } from '../models/responses/ResponseScanIntake';
|
||||||
import { ResponseTrackScan } from '../models/responses/ResponseTrackScan';
|
import { ResponseTrackScan } from '../models/responses/ResponseTrackScan';
|
||||||
|
import { getCardEntry, setCardEntry } from '../nats/CardKV';
|
||||||
|
import { deleteRunnerEntry, getRunnerEntry, RunnerKVEntry, setRunnerEntry, warmRunner } from '../nats/RunnerKV';
|
||||||
|
import { getStationEntryById } from '../nats/StationKV';
|
||||||
@JsonController('/scans')
|
@JsonController('/scans')
|
||||||
@OpenAPI({ security: [{ "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
|
@OpenAPI({ security: [{ "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
|
||||||
export class ScanController {
|
export class ScanController {
|
||||||
@@ -145,12 +145,12 @@ export class ScanController {
|
|||||||
// Compute
|
// Compute
|
||||||
const lapTime = entry.latestTimestamp === 0 ? 0 : now - entry.latestTimestamp;
|
const lapTime = entry.latestTimestamp === 0 ? 0 : now - entry.latestTimestamp;
|
||||||
const valid = minimumLapTime === 0 || lapTime > minimumLapTime;
|
const valid = minimumLapTime === 0 || lapTime > minimumLapTime;
|
||||||
const newDistance = entry.totalDistance + (valid ? trackDistance : 0);
|
const newDistance = entry.distance + (valid ? trackDistance : 0);
|
||||||
const newTimestamp = valid ? now : entry.latestTimestamp;
|
const newTimestamp = valid ? now : entry.latestTimestamp;
|
||||||
|
|
||||||
const updated: RunnerKVEntry = {
|
const updated: RunnerKVEntry = {
|
||||||
displayName: entry.displayName,
|
displayName: entry.displayName,
|
||||||
totalDistance: newDistance,
|
distance: newDistance,
|
||||||
latestTimestamp: newTimestamp,
|
latestTimestamp: newTimestamp,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -174,7 +174,7 @@ export class ScanController {
|
|||||||
|
|
||||||
const runnerInfo = new ResponseScanIntakeRunner();
|
const runnerInfo = new ResponseScanIntakeRunner();
|
||||||
runnerInfo.displayName = entry.displayName;
|
runnerInfo.displayName = entry.displayName;
|
||||||
runnerInfo.totalDistance = newDistance;
|
runnerInfo.distance = newDistance;
|
||||||
|
|
||||||
response = new ResponseScanIntake();
|
response = new ResponseScanIntake();
|
||||||
response.accepted = true;
|
response.accepted = true;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export class ResponseScanIntakeRunner {
|
|||||||
displayName: string;
|
displayName: string;
|
||||||
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
totalDistance: number;
|
distance: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ResponseScanIntake {
|
export class ResponseScanIntake {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export interface RunnerKVEntry {
|
|||||||
/** "Firstname Lastname" — middlename omitted. */
|
/** "Firstname Lastname" — middlename omitted. */
|
||||||
displayName: string;
|
displayName: string;
|
||||||
/** Sum of all valid scan distances in metres. */
|
/** Sum of all valid scan distances in metres. */
|
||||||
totalDistance: number;
|
distance: number;
|
||||||
/** Unix seconds timestamp of the last valid scan. 0 if none. */
|
/** Unix seconds timestamp of the last valid scan. 0 if none. */
|
||||||
latestTimestamp: number;
|
latestTimestamp: number;
|
||||||
}
|
}
|
||||||
@@ -93,7 +93,7 @@ export async function deleteRunnerEntry(runnerId: number): Promise<void> {
|
|||||||
* scan timestamp from the database, writes the result to KV, and returns it.
|
* scan timestamp from the database, writes the result to KV, and returns it.
|
||||||
*
|
*
|
||||||
* Called on any KV cache miss during the scan intake flow.
|
* Called on any KV cache miss during the scan intake flow.
|
||||||
* Also handles the first-scan-ever case — latestTimestamp=0, totalDistance=0.
|
* Also handles the first-scan-ever case — latestTimestamp=0, distance=0.
|
||||||
*/
|
*/
|
||||||
export async function warmRunner(runnerId: number): Promise<RunnerKVEntry> {
|
export async function warmRunner(runnerId: number): Promise<RunnerKVEntry> {
|
||||||
const connection = getConnection();
|
const connection = getConnection();
|
||||||
@@ -119,7 +119,7 @@ export async function warmRunner(runnerId: number): Promise<RunnerKVEntry> {
|
|||||||
|
|
||||||
const entry: RunnerKVEntry = {
|
const entry: RunnerKVEntry = {
|
||||||
displayName,
|
displayName,
|
||||||
totalDistance: parseInt(distanceResult?.total ?? '0', 10),
|
distance: parseInt(distanceResult?.total ?? '0', 10),
|
||||||
latestTimestamp: latestScan?.timestamp ?? 0,
|
latestTimestamp: latestScan?.timestamp ?? 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -180,7 +180,7 @@ export async function warmAll(): Promise<void> {
|
|||||||
const writePromises = runners.map((runner) => {
|
const writePromises = runners.map((runner) => {
|
||||||
const entry: RunnerKVEntry = {
|
const entry: RunnerKVEntry = {
|
||||||
displayName: `${runner.firstname} ${runner.lastname}`,
|
displayName: `${runner.firstname} ${runner.lastname}`,
|
||||||
totalDistance: distanceMap.get(runner.id) ?? 0,
|
distance: distanceMap.get(runner.id) ?? 0,
|
||||||
latestTimestamp: latestMap.get(runner.id) ?? 0,
|
latestTimestamp: latestMap.get(runner.id) ?? 0,
|
||||||
};
|
};
|
||||||
return setRunnerEntry(runner.id, entry);
|
return setRunnerEntry(runner.id, entry);
|
||||||
|
|||||||
Reference in New Issue
Block a user