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
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:
@@ -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
|
||||||
|
```
|
||||||
@@ -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.).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
`,
|
`,
|
||||||
|
|||||||
@@ -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.
@@ -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.
Reference in New Issue
Block a user