3 Commits

Author SHA1 Message Date
Sven laptop 0da6d07283 Add local logging to all microservices; auto-create error_log table on startup
Build and Push Docker Images / detect-changes (push) Successful in 9s
Build and Push Docker Images / build-auth-api (push) Successful in 1m3s
Build and Push Docker Images / build-employee-api (push) Successful in 59s
Build and Push Docker Images / build-transit-api (push) Successful in 56s
Build and Push Docker Images / build-error-log-api (push) Successful in 1m4s
Build and Push Docker Images / build-admin-ui (push) Successful in 1m20s
Build and Push Docker Images / build-monitoring-ui (push) Successful in 1m12s
Build and Push Docker Images / deploy-test-node (push) Successful in 3s
2026-09-23 18:29:46 +02:00
Sven laptop 04bb0e3347 Add error reporting middleware to error_log_api; fix boolean isAdmin in auth_api 2026-09-23 18:21:27 +02:00
Sven laptop a5924086f4 Fixed payload check
Build and Push Docker Images / detect-changes (push) Successful in 10s
Build and Push Docker Images / build-auth-api (push) Successful in 58s
Build and Push Docker Images / build-employee-api (push) Successful in 58s
Build and Push Docker Images / build-transit-api (push) Successful in 58s
Build and Push Docker Images / build-error-log-api (push) Successful in 1m9s
Build and Push Docker Images / build-admin-ui (push) Successful in 1m15s
Build and Push Docker Images / build-monitoring-ui (push) Successful in 1m12s
Build and Push Docker Images / deploy-test-node (push) Successful in 3s
2026-09-23 17:31:23 +02:00
5 changed files with 101 additions and 26 deletions
+11 -20
View File
@@ -297,26 +297,17 @@ jobs:
tags: ${{ steps.tags.outputs.tags }} tags: ${{ steps.tags.outputs.tags }}
deploy-test-node: 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] needs:
if: ${{ always() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') && gitea.event_name == 'push' }} [
strategy: detect-changes,
fail-fast: false build-auth-api,
matrix: build-employee-api,
include: build-transit-api,
- name: auth-api build-error-log-api,
need_job: build-auth-api build-admin-ui,
- name: employee-api build-monitoring-ui,
need_job: build-employee-api ]
- name: transit-api 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') }}
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 runs-on: ubuntu-latest
steps: steps:
+16 -2
View File
@@ -4,6 +4,11 @@ const jwt = require("jsonwebtoken");
const db = require("./db"); const db = require("./db");
require("dotenv").config(); require("dotenv").config();
function log(level, message, meta = {}) {
const timestamp = new Date().toISOString();
console.log(JSON.stringify({ timestamp, level, service: "auth_api", message, ...meta }));
}
const app = express(); const app = express();
app.use(express.json()); app.use(express.json());
@@ -87,12 +92,21 @@ app.post("/login", async (req, res) => {
if (!ok) return res.status(401).json({ error: "invalid_credentials" }); if (!ok) return res.status(401).json({ error: "invalid_credentials" });
const isAdmin = ADMIN_USER_CODES.has(Number(user.usercode)); const isAdmin = ADMIN_USER_CODES.has(Number(user.usercode));
const payload = { userId: user.id, userCode: user.usercode, isAdmin }; const payload = {
userId: user.id,
userCode: user.usercode,
isAdmin: Boolean(isAdmin),
};
const token = jwt.sign(payload, JWT_SECRET, { expiresIn: "1h" }); const token = jwt.sign(payload, JWT_SECRET, { expiresIn: "1h" });
res.json({ res.json({
token, token,
user: { id: user.id, userCode: user.usercode, job: user.job, isAdmin }, user: {
id: user.id,
userCode: user.usercode,
job: user.job,
isAdmin: Boolean(isAdmin),
},
}); });
} catch (err) { } catch (err) {
await reportError({ await reportError({
+5
View File
@@ -3,6 +3,11 @@ const bcrypt = require("bcryptjs");
const db = require("./db"); const db = require("./db");
require("dotenv").config(); require("dotenv").config();
function log(level, message, meta = {}) {
const timestamp = new Date().toISOString();
console.log(JSON.stringify({ timestamp, level, service: "employee_api", message, ...meta }));
}
const app = express(); const app = express();
app.use(express.json()); app.use(express.json());
+64 -4
View File
@@ -5,13 +5,36 @@ require("dotenv").config();
const db = require("./db"); const db = require("./db");
function log(level, message, meta = {}) {
const timestamp = new Date().toISOString();
console.log(JSON.stringify({ timestamp, level, service: "error_log_api", message, ...meta }));
}
async function ensureErrorLogTable() {
try {
await db.query(`CREATE SCHEMA IF NOT EXISTS data`);
await db.query(`
CREATE TABLE IF NOT EXISTS data.errors (
id BIGSERIAL PRIMARY KEY,
"date" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"service" TEXT NOT NULL,
partofservice TEXT,
error TEXT NOT NULL
)
`);
console.log("Error log table ready");
} catch (err) {
console.error("Failed to create error log table:", err);
}
}
const app = express(); const app = express();
app.use(express.json({ limit: "64kb" })); app.use(express.json({ limit: "64kb" }));
app.use(cors()); app.use(cors());
const PORT = Number(process.env.PORT || 5003); const ERROR_LOG_API_URL =
const JWT_SECRET = process.env.JWT_SECRET || ""; process.env.ERROR_LOG_API_URL ||
// Admin verification is done by `auth_api` and carried in the JWT as `isAdmin`. "https://error.xtransit.testinstance.nl/error_log";
function toFriendlyError(statusCode) { function toFriendlyError(statusCode) {
if (statusCode === 400) return "Controleer je invoer en probeer opnieuw."; if (statusCode === 400) return "Controleer je invoer en probeer opnieuw.";
@@ -21,6 +44,40 @@ function toFriendlyError(statusCode) {
return "Er ging iets mis. Probeer het later opnieuw."; return "Er ging iets mis. Probeer het later opnieuw.";
} }
async function reportError({ partofservice, error }) {
try {
await fetch(ERROR_LOG_API_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
service: "error_log_api",
partofservice,
error,
}),
});
} catch (_) {}
}
app.use((req, res, next) => {
const originalJson = res.json.bind(res);
res.json = (body) => {
if (res.statusCode >= 400 && body && typeof body === "object") {
const rawError =
body.error || body.message || body.details || `HTTP ${res.statusCode}`;
void reportError({
partofservice: `${req.method} ${req.originalUrl}`,
error: rawError,
});
return originalJson({ error: toFriendlyError(res.statusCode) });
}
return originalJson(body);
};
next();
});
const PORT = Number(process.env.PORT || 5003);
function normalizeErrorText(value) { function normalizeErrorText(value) {
if (!value) return "Unknown error"; if (!value) return "Unknown error";
if (typeof value === "string") return value; if (typeof value === "string") return value;
@@ -152,6 +209,9 @@ app.use(async (err, req, res, _next) => {
return res.status(500).json({ error: toFriendlyError(500) }); return res.status(500).json({ error: toFriendlyError(500) });
}); });
app.listen(PORT, () => console.log(`Error Log API listening on ${PORT}`)); app.listen(PORT, async () => {
await ensureErrorLogTable();
console.log(`Error Log API listening on ${PORT}`);
});
module.exports = app; module.exports = app;
+5
View File
@@ -5,6 +5,11 @@ require("dotenv").config();
const db = require("./db"); const db = require("./db");
const { authMiddleware } = require("./middleware"); const { authMiddleware } = require("./middleware");
function log(level, message, meta = {}) {
const timestamp = new Date().toISOString();
console.log(JSON.stringify({ timestamp, level, service: "transit_api", message, ...meta }));
}
const app = express(); const app = express();
app.use(express.json()); app.use(express.json());
app.use(cors()); app.use(cors());