updated transit api
Build and Push Docker Images / detect-changes (push) Successful in 10s
Build and Push Docker Images / build-auth-api (push) Successful in 1m0s
Build and Push Docker Images / build-employee-api (push) Successful in 1m3s
Build and Push Docker Images / build-transit-api (push) Successful in 59s
Build and Push Docker Images / build-error-log-api (push) Successful in 1m9s
Build and Push Docker Images / build-admin-ui (push) Successful in 1m20s
Build and Push Docker Images / deploy-test-node (push) Successful in 4s

This commit is contained in:
Sven laptop
2026-09-18 20:08:10 +02:00
parent 1154d1d658
commit f935bbeb8c
9 changed files with 144 additions and 13 deletions
+53
View File
@@ -0,0 +1,53 @@
# XTransit Domains
This document is the central reference for the public XTransit hostnames.
Use HTTPS for every public request.
## Public Domains
| Domain | Service | Purpose |
| ------------------------------------------ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `https://auth.xtransit.testinstance.nl` | Auth API | Driver and administrator login. Issues and validates JWT authentication tokens. Use this domain only for authentication requests such as `POST /login`. |
| `https://transit.xtransit.testinstance.nl` | Transit API | Transit and schedule data used by the Windows XP driver client, admin UI, and public timetable clients. This includes assigned daily schedules, weekly templates, shifts, bus lines, stops, routes, and timetable views. |
| `https://error.xtransit.testinstance.nl` | Error Log API | Central error collection and administration. Services send errors to `POST /error_log`; administrators can inspect them through `GET /error_log`. |
| `https://admin.xtransit.testinstance.nl` | Admin panel | Browser-based administration interface for managing bus lines, stops, vehicles, weekly schedules, driver shifts, and error logs. |
## Client Rules
### Windows XP driver client
The XP client must use:
- Auth and login: `https://auth.xtransit.testinstance.nl`
- Assigned schedules and route stops: `https://transit.xtransit.testinstance.nl`
The XP client must never use the auth domain for transit data.
### Admin UI and public timetable
The admin panel is available at `https://admin.xtransit.testinstance.nl`.
The admin UI and public timetable use:
- Authentication: `https://auth.xtransit.testinstance.nl`
- Transit API: `https://transit.xtransit.testinstance.nl`
- Error logging: `https://error.xtransit.testinstance.nl`
### Backend services
The backend services report errors to:
```text
https://error.xtransit.testinstance.nl/error_log
```
The database and internal Docker service name are deployment details and should not
be exposed as public client URLs. Public clients must use the HTTPS domains above.
## Quick Reference
```text
auth.xtransit.testinstance.nl Authentication and JWT tokens
transit.xtransit.testinstance.nl All transit and schedule data
error.xtransit.testinstance.nl Central error logging
admin.xtransit.testinstance.nl Browser-based administration panel
```
+5
View File
@@ -10,10 +10,15 @@ Dit repository bevat een suite van Node.js microservices, een admin webapp en ee
De error API is beschikbaar via `https://error.xtransit.testinstance.nl`. De error API is beschikbaar via `https://error.xtransit.testinstance.nl`.
- `admin_ui` — React + Vite beheerinterface - `admin_ui` — React + Vite beheerinterface
Het admin panel is beschikbaar via `https://admin.xtransit.testinstance.nl`.
- `deprecated/` — legacy Flutter bus terminal, retained for reference while moving the client to a Windows XP-era build path - `deprecated/` — legacy Flutter bus terminal, retained for reference while moving the client to a Windows XP-era build path
Er is ook een GitHub Actions workflow aanwezig die Docker images bouwt en pusht naar GitHub Container Registry (GHCR). Er is ook een GitHub Actions workflow aanwezig die Docker images bouwt en pusht naar GitHub Container Registry (GHCR).
Zie [DOMAINS_README.md](DOMAINS_README.md) voor de centrale lijst van alle publieke domeinen en hun functies.
Belangrijk: deze README bevat geen wachtwoorden of andere gevoelige gegevens. Sla secrets altijd buiten versiebeheer op (environment variables, secret managers, Portainer secrets, etc.). Belangrijk: deze README bevat geen wachtwoorden of andere gevoelige gegevens. Sla secrets altijd buiten versiebeheer op (environment variables, secret managers, Portainer secrets, etc.).
--- ---
+2
View File
@@ -15,6 +15,8 @@ The active UI uses these transit API endpoints:
# Transit Admin Control Center # Transit Admin Control Center
Production admin panel: `https://admin.xtransit.testinstance.nl`
Modern React + Vite web application for comprehensive transit system administration. Modern React + Vite web application for comprehensive transit system administration.
## Features ## Features
+6
View File
@@ -59,6 +59,12 @@ API draait op `https://transit.xtransit.testinstance.nl`
Alle endpoints vereisen een `Authorization: Bearer {token}` header. Alle endpoints vereisen een `Authorization: Bearer {token}` header.
**GET** `/health`
- Controleert of de gedeployde transit API draait en de database bereikbaar is.
- Geeft `200` met `{"status":"ok","database":"ok"}` bij succes.
- Geeft `503` als de API wel draait maar de database niet bereikbaar is.
### Schedule ### Schedule
Schedules are weekly templates. Each row in `transit.schedules` represents one Schedules are weekly templates. Each row in `transit.schedules` represents one
+27 -4
View File
@@ -13,6 +13,14 @@ const ERROR_LOG_API_URL =
process.env.ERROR_LOG_API_URL || process.env.ERROR_LOG_API_URL ||
"https://error.xtransit.testinstance.nl/error_log"; "https://error.xtransit.testinstance.nl/error_log";
function isValidDateKey(value) {
if (!/^\d{4}-\d{2}-\d{2}$/.test(String(value || ""))) return false;
const date = new Date(`${value}T00:00:00Z`);
return (
!Number.isNaN(date.getTime()) && date.toISOString().slice(0, 10) === value
);
}
function friendlyErrorMessage(statusCode) { function friendlyErrorMessage(statusCode) {
if (statusCode === 400) return "Controleer je invoer en probeer opnieuw."; if (statusCode === 400) return "Controleer je invoer en probeer opnieuw.";
if (statusCode === 401) return "Je sessie is verlopen. Log opnieuw in."; if (statusCode === 401) return "Je sessie is verlopen. Log opnieuw in.";
@@ -53,6 +61,16 @@ app.use((req, res, next) => {
next(); next();
}); });
app.get("/health", async (_req, res) => {
try {
await db.query("SELECT 1");
res.json({ status: "ok", service: "transit_api", database: "ok" });
} catch (err) {
console.error("GET /health error:", err);
res.status(503).json({ status: "unavailable", service: "transit_api" });
}
});
// Development mock storage when DEV_MOCK=1 // Development mock storage when DEV_MOCK=1
const DEV_MOCK = process.env.DEV_MOCK === "1"; const DEV_MOCK = process.env.DEV_MOCK === "1";
const _mock = { const _mock = {
@@ -1081,8 +1099,9 @@ app.get("/schedule/daily", authMiddleware, async (req, res) => {
app.get("/driver/schedules/daily", authMiddleware, async (req, res) => { app.get("/driver/schedules/daily", authMiddleware, async (req, res) => {
try { try {
const { date } = req.query; const { date } = req.query;
if (!date) if (!isValidDateKey(date)) {
return res.status(400).json({ error: "date query parameter required" }); return res.status(400).json({ error: "date must use YYYY-MM-DD format" });
}
const result = await db.query( const result = await db.query(
` `
@@ -1090,9 +1109,13 @@ app.get("/driver/schedules/daily", authMiddleware, async (req, res) => {
bl.line_number, bl.start_stop, bl.end_stop, bl.estimated_duration_minutes, bl.line_number, bl.start_stop, bl.end_stop, bl.estimated_duration_minutes,
bl.description bl.description
FROM transit.schedules s FROM transit.schedules s
JOIN transit.shifts sh ON sh.schedule_id = s.id AND sh.driver_id = $1
JOIN transit.bus_lines bl ON bl.id = s.bus_line_id JOIN transit.bus_lines bl ON bl.id = s.bus_line_id
WHERE s.day_of_week = EXTRACT(DOW FROM $2::date)::int WHERE EXISTS (
SELECT 1
FROM transit.shifts sh
WHERE sh.schedule_id = s.id AND sh.driver_id = $1
)
AND s.day_of_week = EXTRACT(DOW FROM $2::date)::int
AND s.status <> 'cancelled' AND s.status <> 'cancelled'
ORDER BY s.start_time ASC ORDER BY s.start_time ASC
`, `,
+21 -3
View File
@@ -2,9 +2,13 @@
Native Win32 driver client targeting Windows XP-era systems. Native Win32 driver client targeting Windows XP-era systems.
The client authenticates with `https://auth.xtransit.testinstance.nl`, then requests For Windows XP compatibility, the client temporarily uses the LAN services:
assigned weekly schedule templates from
`https://transit.xtransit.testinstance.nl/driver/schedules/daily`. - Login: `http://192.168.68.66:5000/login`
- Assigned schedules: `http://192.168.68.66:5001/driver/schedules/daily`
- Route stops: `http://192.168.68.66:5001/bus-lines/:busLineId/stops`
This HTTP fallback avoids the TLS 1.2/certificate limitations of Windows XP.
Schedules are assigned through `transit.shifts`; the client does not expect a Schedules are assigned through `transit.shifts`; the client does not expect a
`driver_id`, `bus_id`, or `weekdays` column on `transit.schedules`. `driver_id`, `bus_id`, or `weekdays` column on `transit.schedules`.
@@ -25,3 +29,17 @@ makensis installer.nsi
Distribute `TransitDriverXPSetup.exe`. It detects an existing installation, Distribute `TransitDriverXPSetup.exe`. It detects an existing installation,
stops the previous client, and replaces it in the same directory. stops the previous client, and replaces it in the same directory.
## Login diagnostics
If the app shows `Driver service unreachable`, open `driver_xp.log` in the same
folder as the installed `driver_xp.exe` and inspect the latest entries. The log
records the failing stage and WinHTTP or HTTP status code, but never writes the
password or JWT token.
Common codes:
- `12007`: hostname could not be resolved (DNS)
- `12029`: connection to the server failed
- `12175`: HTTPS certificate or TLS validation failed
- `401` or `403`: the server responded, but authentication was rejected
Binary file not shown.
+30 -6
View File
@@ -25,7 +25,7 @@
#define ID_ROUTES_LABEL 116 #define ID_ROUTES_LABEL 116
#define MAX_BUF 65535 #define MAX_BUF 65535
#define API_HOST "auth.xtransit.testinstance.nl" #define API_HOST "192.168.68.66"
#define AUTH_PORT 5000 #define AUTH_PORT 5000
#define TRANSIT_PORT 5001 #define TRANSIT_PORT 5001
@@ -52,6 +52,30 @@ static void SetOutputText(HWND hwnd, const char *text)
SetWindowTextA(GetDlgItem(hwnd, ID_OUTPUT), text); SetWindowTextA(GetDlgItem(hwnd, ID_OUTPUT), text);
} }
static void LogDiagnostic(const char *stage, DWORD code)
{
char path[MAX_PATH];
char *separator;
FILE *file;
GetModuleFileNameA(NULL, path, sizeof(path));
separator = strrchr(path, '\\');
if (separator) {
*(separator + 1) = '\0';
} else {
snprintf(path, sizeof(path), ".\\");
}
strncat(path, "driver_xp.log", sizeof(path) - strlen(path) - 1);
file = fopen(path, "a");
if (!file) {
return;
}
fprintf(file, "%s: %lu\r\n", stage, (unsigned long)code);
fclose(file);
}
static void ShowControl(HWND hwnd, int id, int visible) static void ShowControl(HWND hwnd, int id, int visible)
{ {
ShowWindow(GetDlgItem(hwnd, id), visible ? SW_SHOW : SW_HIDE); ShowWindow(GetDlgItem(hwnd, id), visible ? SW_SHOW : SW_HIDE);
@@ -516,8 +540,7 @@ static void ExtractTokenFromJson(const char *json, char *outToken, size_t outSiz
static void HttpErrorMessage(const char *context, DWORD error) static void HttpErrorMessage(const char *context, DWORD error)
{ {
(void)context; LogDiagnostic(context, error);
(void)error;
} }
static int HttpRequestText(const char *method, static int HttpRequestText(const char *method,
@@ -690,6 +713,7 @@ static int HttpRequestText(const char *method,
} }
if (status >= 400) { if (status >= 400) {
LogDiagnostic("HTTP response status", status);
snprintf(response, responseSize, "HTTP error %lu", status); snprintf(response, responseSize, "HTTP error %lu", status);
ok = 0; ok = 0;
goto cleanup; goto cleanup;
@@ -749,7 +773,7 @@ static void PerformLogin(HWND hwnd, APPSTATE *state)
userCode, password); userCode, password);
SetStatusText(hwnd, "Logging in..."); SetStatusText(hwnd, "Logging in...");
if (!HttpRequestText("POST", "https://auth.xtransit.testinstance.nl/login", payload, NULL, response, sizeof(response))) { if (!HttpRequestText("POST", "http://192.168.68.66:5000/login", payload, NULL, response, sizeof(response))) {
SetOutputText(hwnd, "Driver service unreachable. Please try again."); SetOutputText(hwnd, "Driver service unreachable. Please try again.");
SetStatusText(hwnd, "Login failed"); SetStatusText(hwnd, "Login failed");
return; return;
@@ -786,7 +810,7 @@ static void PerformRefresh(HWND hwnd, APPSTATE *state)
if (date[0] == '\0') { if (date[0] == '\0') {
snprintf(date, sizeof(date), "2026-09-17"); snprintf(date, sizeof(date), "2026-09-17");
} }
snprintf(scheduleUrl, sizeof(scheduleUrl), "https://transit.xtransit.testinstance.nl/driver/schedules/daily?date=%s", date); snprintf(scheduleUrl, sizeof(scheduleUrl), "http://192.168.68.66:5001/driver/schedules/daily?date=%s", date);
SetStatusText(hwnd, "Loading schedule..."); SetStatusText(hwnd, "Loading schedule...");
if (!HttpRequestText("GET", scheduleUrl, NULL, authHeader, response, sizeof(response))) { if (!HttpRequestText("GET", scheduleUrl, NULL, authHeader, response, sizeof(response))) {
@@ -827,7 +851,7 @@ static void PerformLoadStops(HWND hwnd, APPSTATE *state)
} }
snprintf(authHeader, sizeof(authHeader), "Authorization: Bearer %s\r\n", state->token); snprintf(authHeader, sizeof(authHeader), "Authorization: Bearer %s\r\n", state->token);
snprintf(stopsUrl, sizeof(stopsUrl), "https://transit.xtransit.testinstance.nl/bus-lines/%d/stops", lineId); snprintf(stopsUrl, sizeof(stopsUrl), "http://192.168.68.66:5001/bus-lines/%d/stops", lineId);
SetStatusText(hwnd, "Loading stop details..."); SetStatusText(hwnd, "Loading stop details...");
if (!HttpRequestText("GET", stopsUrl, NULL, authHeader, response, sizeof(response))) { if (!HttpRequestText("GET", stopsUrl, NULL, authHeader, response, sizeof(response))) {
Binary file not shown.