6 Commits

Author SHA1 Message Date
Sven laptop 89e4f90fc3 Fix monitoring UI 2026-09-23 17:23:04 +02:00
Sven laptop 39b0b07a77 updated docker build to always deplyo 2026-09-23 17:22:14 +02:00
Sven laptop 48c710bfbf Fix admin check on monitoring UI
Build and Push Docker Images / detect-changes (push) Successful in 12s
Build and Push Docker Images / build-auth-api (push) Has been skipped
Build and Push Docker Images / build-employee-api (push) Has been skipped
Build and Push Docker Images / build-transit-api (push) Has been skipped
Build and Push Docker Images / build-error-log-api (push) Has been skipped
Build and Push Docker Images / build-admin-ui (push) Has been skipped
Build and Push Docker Images / build-monitoring-ui (push) Successful in 1m15s
Build and Push Docker Images / deploy-test-node (push) Successful in 3s
2026-09-23 17:12:41 +02:00
Sven laptop 1c055d84e8 Merge branch 'driver_ui' into dev
Build and Push Docker Images / build-auth-api (push) Has been skipped
Build and Push Docker Images / build-employee-api (push) Has been skipped
Build and Push Docker Images / detect-changes (push) Successful in 10s
Build and Push Docker Images / build-transit-api (push) Has been skipped
Build and Push Docker Images / build-error-log-api (push) Has been skipped
Build and Push Docker Images / build-admin-ui (push) Has been skipped
Build and Push Docker Images / build-monitoring-ui (push) Successful in 1m30s
Build and Push Docker Images / deploy-test-node (push) Successful in 3s
2026-09-23 10:00:20 +02:00
Sven laptop ebcc485f51 updated ui to use multiple screens 2026-09-18 17:15:00 +02:00
Sven laptop c09b3f3f66 created driver_ui with login and viewing assigned routes 2026-09-18 00:03:58 +02:00
5 changed files with 162 additions and 86 deletions
+20 -11
View File
@@ -297,17 +297,26 @@ jobs:
tags: ${{ steps.tags.outputs.tags }}
deploy-test-node:
needs:
[
detect-changes,
build-auth-api,
build-employee-api,
build-transit-api,
build-error-log-api,
build-admin-ui,
build-monitoring-ui,
]
if: ${{ always() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') && gitea.event_name == 'push' && (gitea.ref_name == 'dev' || gitea.ref_name == 'main' || gitea.ref_name == 'develop' || gitea.ref_name == 'auth-api' || gitea.ref_name == 'employee-api' || gitea.ref_name == 'transit-api' || gitea.ref_name == 'error-log-api' || gitea.ref_name == 'admin-ui' || gitea.ref_name == 'monitoring-ui') }}
needs: [detect-changes, build-auth-api, build-employee-api, build-transit-api, build-error-log-api, build-admin-ui, build-monitoring-ui]
if: ${{ always() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') && gitea.event_name == 'push' }}
strategy:
fail-fast: false
matrix:
include:
- name: auth-api
need_job: build-auth-api
- name: employee-api
need_job: build-employee-api
- name: transit-api
need_job: build-transit-api
- name: error-log-api
need_job: build-error-log-api
- name: admin-ui
need_job: build-admin-ui
- name: monitoring-ui
need_job: build-monitoring-ui
if: ${{ needs[matrix.value.need_job].result == 'success' }}
runs-on: ubuntu-latest
steps:
+58 -36
View File
@@ -57,7 +57,12 @@ const healthPaths = {
errors: "/health",
};
const testerRoutes = [
{ service: "auth", method: "POST", path: "/login", label: "Login (userCode + password)" },
{
service: "auth",
method: "POST",
path: "/login",
label: "Login (userCode + password)",
},
{ service: "auth", method: "GET", path: "/me", label: "Current session" },
{
service: "employee",
@@ -108,8 +113,8 @@ function App() {
const [token, setToken] = useState(
() => localStorage.getItem("transit-monitor-token") || "",
);
const [user, setUser] = useState(
() => JSON.parse(localStorage.getItem("transit-monitor-user") || "null"),
const [user, setUser] = useState(() =>
JSON.parse(localStorage.getItem("transit-monitor-user") || "null"),
);
const [health, setHealth] = useState({});
const [history, setHistory] = useState({});
@@ -129,35 +134,38 @@ function App() {
const [loginLoading, setLoginLoading] = useState(false);
const [loginError, setLoginError] = useState("");
const login = useCallback(async (userCode, password) => {
const url = urls.auth;
if (!url) {
setLoginError("Auth API URL not configured");
return;
}
setLoginLoading(true);
setLoginError("");
try {
const response = await fetch(`${url}/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ userCode, password }),
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || `HTTP ${response.status}`);
const login = useCallback(
async (userCode, password) => {
const url = urls.auth;
if (!url) {
setLoginError("Auth API URL not configured");
return;
}
setToken(data.token);
setUser(data.user);
localStorage.setItem("transit-monitor-token", data.token);
localStorage.setItem("transit-monitor-user", JSON.stringify(data.user));
setLoginLoading(true);
setLoginError("");
} catch (error) {
setLoginError(error.message);
} finally {
setLoginLoading(false);
}
}, [urls.auth]);
try {
const response = await fetch(`${url}/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ userCode, password }),
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || `HTTP ${response.status}`);
}
setToken(data.token);
setUser(data.user);
localStorage.setItem("transit-monitor-token", data.token);
localStorage.setItem("transit-monitor-user", JSON.stringify(data.user));
setLoginError("");
} catch (error) {
setLoginError(error.message);
} finally {
setLoginLoading(false);
}
},
[urls.auth],
);
const logout = useCallback(() => {
setToken("");
@@ -590,7 +598,9 @@ function Docs() {
<summary>API Routes</summary>
<ul>
{meta.routes.map((route) => (
<li key={route}><code>{route}</code></li>
<li key={route}>
<code>{route}</code>
</li>
))}
</ul>
</details>
@@ -855,7 +865,11 @@ function Connections({ urls, token, user, login, logout, save, loginLoading }) {
/>
</label>
{loginError && <div className="login-error">{loginError}</div>}
<button type="submit" className="run-button save-button" disabled={loginLoading}>
<button
type="submit"
className="run-button save-button"
disabled={loginLoading}
>
{loginLoading ? "Logging in..." : "Login"}
</button>
</form>
@@ -898,10 +912,18 @@ function Connections({ urls, token, user, login, logout, save, loginLoading }) {
</div>
<div className="user-info">
<h3>Logged in as</h3>
<p><strong>User Code:</strong> {user?.userCode}</p>
<p><strong>Role:</strong> {user?.job}</p>
<p><strong>Admin:</strong> {user?.isAdmin ? "Yes" : "No"}</p>
<button className="ghost-button" onClick={logout}>Logout</button>
<p>
<strong>User Code:</strong> {user?.userCode}
</p>
<p>
<strong>Role:</strong> {user?.job}
</p>
<p>
<strong>Admin:</strong> {user?.isAdmin ? "Yes" : "No"}
</p>
<button className="ghost-button" onClick={logout}>
Logout
</button>
</div>
<button
className="run-button save-button"
+52
View File
@@ -0,0 +1,52 @@
# Windows XP Driver UI
This project is a native Win32 application built for Windows XP compatibility.
It avoids .NET and WinForms completely and targets a classic Windows API implementation instead.
## Features
- Login form for driver authentication
- Calls the existing auth API on 192.168.68.66:5000
- Fetches the daily schedule from 192.168.68.66:5001
- Works with Windows XP and older native Windows systems
- Uses the WinHTTP API, which is available on XP
## Build
```bash
make
```
This uses the MinGW cross-compiler targeting 32-bit Windows.
## Runtime requirements
The app expects the following services to be available:
- auth_api on http://192.168.68.66:5000
- transit_api on http://192.168.68.66:5001
The app intentionally points at the live network host instead of localhost so it works on a Windows XP machine connected to the same LAN.
## Installer
A custom Windows installer is the distribution package for XP-era machines. The executable is bundled inside the installer; distribute `TransitDriverXPSetup.exe` rather than the internal `driver_xp.exe`.
```bash
makensis installer.nsi
```
This creates `TransitDriverXPSetup.exe`, which installs:
- `driver_xp.exe`
- `README.md`
- Start menu shortcut
- Desktop shortcut
- Uninstaller entry
When run on a machine with an existing Transit Driver XP installation, the installer
reuses the saved installation directory, stops the previous app if it is running,
and replaces the installed files with the new version. Existing shortcuts remain in
place and point to the updated app.
The installer is designed for test deployment on a real Windows XP or older Windows machine compatible with native Win32 executables.
+6 -32
View File
@@ -52,30 +52,6 @@ static void SetOutputText(HWND hwnd, const char *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)
{
ShowWindow(GetDlgItem(hwnd, id), visible ? SW_SHOW : SW_HIDE);
@@ -354,7 +330,7 @@ static void ExtractScheduleSummary(const char *json, APPSTATE *state, char *out,
ExtractJsonFieldString(routeText, "lineNumber", routeLine, sizeof(routeLine));
ExtractJsonFieldString(routeText, "startTime", routeStartTime, sizeof(routeStartTime));
ExtractJsonFieldString(routeText, "id", routeLineId, sizeof(routeLineId));
{
if (routeLine[0] == '\0' || routeLineId[0] == '\0') {
char busLine[1024];
const char *busKey = strstr(routeText, "\"busLine\"");
if (busKey) {
@@ -540,7 +516,8 @@ static void ExtractTokenFromJson(const char *json, char *outToken, size_t outSiz
static void HttpErrorMessage(const char *context, DWORD error)
{
LogDiagnostic(context, error);
(void)context;
(void)error;
}
static int HttpRequestText(const char *method,
@@ -561,7 +538,6 @@ static int HttpRequestText(const char *method,
const char *src;
const char *slash;
const char *colon;
int secure = 0;
int port = 80;
DWORD flags = 0;
DWORD status = 0;
@@ -578,7 +554,6 @@ static int HttpRequestText(const char *method,
src += 7;
} else if (strncmp(src, "https://", 8) == 0) {
src += 8;
secure = 1;
}
host[0] = '\0';
@@ -621,7 +596,7 @@ static int HttpRequestText(const char *method,
}
if (host[0] == '\0') {
snprintf(host, sizeof(host), "%s", API_HOST);
snprintf(host, sizeof(host), "192.168.68.66");
}
if (port == 0) {
@@ -658,7 +633,7 @@ static int HttpRequestText(const char *method,
NULL,
WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
secure ? WINHTTP_FLAG_SECURE : 0);
0);
if (!hRequest) {
HttpErrorMessage("Unable to open request", GetLastError());
goto cleanup;
@@ -713,7 +688,6 @@ static int HttpRequestText(const char *method,
}
if (status >= 400) {
LogDiagnostic("HTTP response status", status);
snprintf(response, responseSize, "HTTP error %lu", status);
ok = 0;
goto cleanup;
@@ -810,7 +784,7 @@ static void PerformRefresh(HWND hwnd, APPSTATE *state)
if (date[0] == '\0') {
snprintf(date, sizeof(date), "2026-09-17");
}
snprintf(scheduleUrl, sizeof(scheduleUrl), "http://192.168.68.66:5001/driver/schedules/daily?date=%s", date);
snprintf(scheduleUrl, sizeof(scheduleUrl), "http://192.168.68.66:5001/schedule/daily?date=%s", date);
SetStatusText(hwnd, "Loading schedule...");
if (!HttpRequestText("GET", scheduleUrl, NULL, authHeader, response, sizeof(response))) {
+26 -7
View File
@@ -1,29 +1,39 @@
; Transit Driver XP custom installer
!include "MUI2.nsh"
!include "WinVer.nsh"
!define APPNAME "Transit Driver XP"
!define COMPANYNAME "Transit Project"
!define VERSION "2.0.0"
!define DESCRIPTION "Native Windows XP-compatible driver app"
!define VERSIONMAJOR 1
!define VERSIONMINOR 1
!define VERSIONBUILD 0
Name "${APPNAME}"
OutFile "TransitDriverXPSetup.exe"
InstallDir "$PROGRAMFILES\TransitDriverXP"
InstallDirRegKey HKCU "Software\TransitDriverXP" "InstallDir"
RequestExecutionLevel admin
VIProductVersion "2.0.0.0"
VIProductVersion "${VERSIONMAJOR}.${VERSIONMINOR}.${VERSIONBUILD}.0"
VIAddVersionKey "ProductName" "${APPNAME}"
VIAddVersionKey "CompanyName" "${COMPANYNAME}"
VIAddVersionKey "FileDescription" "Native Windows XP driver client"
VIAddVersionKey "FileVersion" "${VERSION}"
VIAddVersionKey "ProductVersion" "${VERSION}"
VIAddVersionKey "LegalCopyright" "Transit Project"
VIAddVersionKey "FileDescription" "${DESCRIPTION}"
VIAddVersionKey "FileVersion" "${VERSIONMAJOR}.${VERSIONMINOR}.${VERSIONBUILD}"
VIAddVersionKey "ProductVersion" "${VERSIONMAJOR}.${VERSIONMINOR}.${VERSIONBUILD}"
Function .onInit
ReadRegStr $0 HKCU "Software\TransitDriverXP" "InstallDir"
StrCmp $0 "" done
IfFileExists "$0\driver_xp.exe" 0 done
StrCpy $INSTDIR $0
MessageBox MB_OK|MB_ICONINFORMATION "An existing Transit Driver XP installation was found. It will be updated."
MessageBox MB_OK|MB_ICONINFORMATION "An existing Transit Driver XP installation was found. It will be updated in $INSTDIR."
; Release the old executable before NSIS replaces it.
ExecWait '"$SYSDIR\taskkill.exe" /F /IM driver_xp.exe'
done:
FunctionEnd
@@ -35,14 +45,20 @@ Section "Install"
SetOutPath "$INSTDIR"
SetOverwrite on
File "driver_xp.exe"
File "README.md"
WriteRegStr HKCU "Software\TransitDriverXP" "InstallDir" "$INSTDIR"
WriteRegStr HKCU "Software\TransitDriverXP" "DisplayName" "${APPNAME}"
WriteRegStr HKCU "Software\TransitDriverXP" "Version" "${VERSION}"
CreateDirectory "$SMPROGRAMS\TransitDriverXP"
CreateShortCut "$SMPROGRAMS\TransitDriverXP\Transit Driver XP.lnk" "$INSTDIR\driver_xp.exe"
CreateShortCut "$DESKTOP\Transit Driver XP.lnk" "$INSTDIR\driver_xp.exe"
WriteUninstaller "$INSTDIR\Uninstall.exe"
CreateShortCut "$SMPROGRAMS\TransitDriverXP\Uninstall Transit Driver XP.lnk" "$INSTDIR\Uninstall.exe"
; launch after install
ExecShell "open" "$INSTDIR\driver_xp.exe"
SectionEnd
Section "Uninstall"
@@ -50,8 +66,11 @@ Section "Uninstall"
Delete "$SMPROGRAMS\TransitDriverXP\Transit Driver XP.lnk"
Delete "$SMPROGRAMS\TransitDriverXP\Uninstall Transit Driver XP.lnk"
RMDir "$SMPROGRAMS\TransitDriverXP"
Delete "$INSTDIR\driver_xp.exe"
Delete "$INSTDIR\README.md"
Delete "$INSTDIR\Uninstall.exe"
RMDir "$INSTDIR"
DeleteRegKey HKCU "Software\TransitDriverXP"
SectionEnd