Add comprehensive local logging to all microservices; log all errors locally and forward to error_log_api
This commit is contained in:
+26
-14
@@ -55,6 +55,7 @@ app.use((req, res, next) => {
|
||||
if (res.statusCode >= 400 && body && typeof body === "object") {
|
||||
const rawError =
|
||||
body.error || body.message || body.details || `HTTP ${res.statusCode}`;
|
||||
log("warn", `HTTP ${res.statusCode}`, { path: req.originalUrl, method: req.method, error: rawError });
|
||||
void reportError({
|
||||
partofservice: `${req.method} ${req.originalUrl}`,
|
||||
error: rawError,
|
||||
@@ -86,10 +87,16 @@ app.post("/login", async (req, res) => {
|
||||
[userCode],
|
||||
);
|
||||
const user = result.rows[0];
|
||||
if (!user) return res.status(401).json({ error: "invalid_credentials" });
|
||||
if (!user) {
|
||||
log("warn", "Login attempt for non-existent user", { userCode });
|
||||
return res.status(401).json({ error: "invalid_credentials" });
|
||||
}
|
||||
|
||||
const ok = await bcrypt.compare(password, user.password);
|
||||
if (!ok) return res.status(401).json({ error: "invalid_credentials" });
|
||||
if (!ok) {
|
||||
log("warn", "Invalid password for user", { userCode });
|
||||
return res.status(401).json({ error: "invalid_credentials" });
|
||||
}
|
||||
|
||||
const isAdmin = ADMIN_USER_CODES.has(Number(user.usercode));
|
||||
const payload = {
|
||||
@@ -99,6 +106,7 @@ app.post("/login", async (req, res) => {
|
||||
};
|
||||
const token = jwt.sign(payload, JWT_SECRET, { expiresIn: "1h" });
|
||||
|
||||
log("info", "User logged in", { userCode: user.usercode, isAdmin });
|
||||
res.json({
|
||||
token,
|
||||
user: {
|
||||
@@ -109,28 +117,25 @@ app.post("/login", async (req, res) => {
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
await reportError({
|
||||
partofservice: `${req.method} ${req.originalUrl}`,
|
||||
error: err?.stack || err?.message || String(err),
|
||||
});
|
||||
console.error(err);
|
||||
log("error", "Login error", { error: err.message, stack: err.stack });
|
||||
reportError({ partofservice: `${req.method} ${req.originalUrl}`, error: err.stack || err.message });
|
||||
res.status(500).json({ error: friendlyErrorMessage(500) });
|
||||
}
|
||||
});
|
||||
|
||||
app.get("/me", async (req, res) => {
|
||||
const auth = req.headers.authorization;
|
||||
if (!auth || !auth.startsWith("Bearer "))
|
||||
if (!auth || !auth.startsWith("Bearer ")) {
|
||||
log("warn", "Missing or invalid Authorization header", { path: req.originalUrl });
|
||||
return res.status(401).json({ error: "missing_token" });
|
||||
}
|
||||
const token = auth.slice(7);
|
||||
try {
|
||||
const payload = jwt.verify(token, JWT_SECRET);
|
||||
res.json({ user: payload });
|
||||
} catch (err) {
|
||||
void reportError({
|
||||
partofservice: `${req.method} ${req.originalUrl}`,
|
||||
error: err?.stack || err?.message || String(err),
|
||||
});
|
||||
log("error", "JWT verification failed", { error: err.message, stack: err.stack });
|
||||
reportError({ partofservice: `${req.method} ${req.originalUrl}`, error: err.stack || err.message });
|
||||
res.status(401).json({ error: friendlyErrorMessage(401) });
|
||||
}
|
||||
});
|
||||
@@ -143,8 +148,15 @@ app.get("/me", async (req, res) => {
|
||||
const PORT = process.env.PORT || 5000;
|
||||
app.listen(PORT, () => console.log(`Auth API listening on ${PORT}`));
|
||||
|
||||
app.get("/health", (_req, res) => {
|
||||
res.json({ status: "ok", service: "auth_api" });
|
||||
app.get("/health", async (_req, res) => {
|
||||
try {
|
||||
await db.query("SELECT 1");
|
||||
res.json({ status: "ok", service: "auth_api", database: "ok" });
|
||||
} catch (err) {
|
||||
log("error", "Health check DB query failed", { error: err.message, stack: err.stack });
|
||||
reportError({ partofservice: "GET /health", error: err.stack || err.message });
|
||||
res.status(503).json({ status: "unavailable", service: "auth_api" });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = app;
|
||||
|
||||
+11
-9
@@ -51,6 +51,7 @@ app.use((req, res, next) => {
|
||||
if (res.statusCode >= 400 && body && typeof body === "object") {
|
||||
const rawError =
|
||||
body.error || body.message || body.details || `HTTP ${res.statusCode}`;
|
||||
log("warn", `HTTP ${res.statusCode}`, { path: req.originalUrl, method: req.method, error: rawError });
|
||||
void reportError({
|
||||
partofservice: `${req.method} ${req.originalUrl}`,
|
||||
error: rawError,
|
||||
@@ -67,10 +68,12 @@ app.use((req, res, next) => {
|
||||
app.post("/createemployee", async (req, res) => {
|
||||
try {
|
||||
const { firstname, lastname, job, password } = req.body;
|
||||
if (!firstname || !lastname || !job || !password)
|
||||
if (!firstname || !lastname || !job || !password) {
|
||||
log("warn", "Missing required fields in createemployee", { body: req.body });
|
||||
return res.status(400).json({
|
||||
error: "Firstname, Lastname, Job title and password are required",
|
||||
});
|
||||
}
|
||||
|
||||
const resultCreateEmployee = await db.query(
|
||||
"INSERT INTO workers.employees (firstname, lastname, job) VALUES ($1, $2, $3) RETURNING id",
|
||||
@@ -83,15 +86,13 @@ app.post("/createemployee", async (req, res) => {
|
||||
[newEmployee.id, encryptedpassword],
|
||||
);
|
||||
const newUser = resultCreateUser.rows[0];
|
||||
log("info", "Employee created", { employeeId: newEmployee.id, userCode: newUser.usercode });
|
||||
return res
|
||||
.status(201)
|
||||
.json({ employee: firstname, code: newUser.usercoden });
|
||||
.json({ employee: firstname, code: newUser.usercode });
|
||||
} catch (err) {
|
||||
void reportError({
|
||||
partofservice: `${req.method} ${req.originalUrl}`,
|
||||
error: err?.stack || err?.message || String(err),
|
||||
});
|
||||
console.error(err);
|
||||
log("error", "Create employee error", { error: err.message, stack: err.stack });
|
||||
reportError({ partofservice: `${req.method} ${req.originalUrl}`, error: err.stack || err.message });
|
||||
res.status(500).json({ error: friendlyErrorMessage(500) });
|
||||
}
|
||||
});
|
||||
@@ -101,12 +102,13 @@ app.get("/health", async (_req, res) => {
|
||||
await db.query("SELECT 1");
|
||||
return res.status(200).json({ endpoint: "reachable", database: "ok" });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
log("error", "Health check DB query failed", { error: err.message, stack: err.stack });
|
||||
reportError({ partofservice: "GET /health", error: err.stack || err.message });
|
||||
res.status(503).json({ endpoint: "unreachable", database: "down" });
|
||||
}
|
||||
});
|
||||
|
||||
const PORT = process.env.PORT || 4000;
|
||||
app.listen(PORT, () => console.log(`Auth API listening on ${PORT}`));
|
||||
app.listen(PORT, () => console.log(`Employee API listening on ${PORT}`));
|
||||
|
||||
module.exports = app;
|
||||
|
||||
+14
-4
@@ -64,6 +64,7 @@ app.use((req, res, next) => {
|
||||
if (res.statusCode >= 400 && body && typeof body === "object") {
|
||||
const rawError =
|
||||
body.error || body.message || body.details || `HTTP ${res.statusCode}`;
|
||||
log("warn", `HTTP ${res.statusCode}`, { path: req.originalUrl, method: req.method, error: rawError });
|
||||
void reportError({
|
||||
partofservice: `${req.method} ${req.originalUrl}`,
|
||||
error: rawError,
|
||||
@@ -110,18 +111,22 @@ async function insertErrorLog({
|
||||
function verifyAdmin(req, res, next) {
|
||||
const auth = req.headers.authorization;
|
||||
if (!auth || !auth.startsWith("Bearer ")) {
|
||||
log("warn", "Missing or invalid Authorization header", { path: req.originalUrl });
|
||||
return res.status(401).json({ error: toFriendlyError(401) });
|
||||
}
|
||||
|
||||
try {
|
||||
const payload = jwt.verify(auth.slice(7), JWT_SECRET);
|
||||
if (!payload?.isAdmin) {
|
||||
log("warn", "Non-admin user attempted admin access", { userCode: payload?.userCode, path: req.originalUrl });
|
||||
return res.status(403).json({ error: toFriendlyError(403) });
|
||||
}
|
||||
|
||||
req.user = payload;
|
||||
return next();
|
||||
} catch (err) {
|
||||
log("error", "JWT verification failed", { error: err.message, path: req.originalUrl });
|
||||
reportError({ partofservice: `verifyAdmin ${req.method} ${req.originalUrl}`, error: err.stack || err.message });
|
||||
return res.status(401).json({ error: toFriendlyError(401) });
|
||||
}
|
||||
}
|
||||
@@ -131,7 +136,8 @@ app.get("/health", async (_req, res) => {
|
||||
await db.query("SELECT 1");
|
||||
res.json({ status: "ok", service: "error_log_api", database: "ok" });
|
||||
} catch (err) {
|
||||
console.error("GET /health error:", err);
|
||||
log("error", "Health check DB query failed", { error: err.message, stack: err.stack });
|
||||
reportError({ partofservice: "GET /health", error: err.stack || err.message });
|
||||
res.status(503).json({ status: "unavailable", service: "error_log_api" });
|
||||
}
|
||||
});
|
||||
@@ -144,13 +150,15 @@ app.post("/error_log", async (req, res) => {
|
||||
const error = req.body?.error;
|
||||
|
||||
if (!service || !error) {
|
||||
log("warn", "Missing required fields in error_log", { body: req.body });
|
||||
return res.status(400).json({ error: toFriendlyError(400) });
|
||||
}
|
||||
|
||||
await insertErrorLog({ service, partofservice, error });
|
||||
return res.status(201).json({ ok: true });
|
||||
} catch (err) {
|
||||
console.error("POST /error_log failed:", err);
|
||||
log("error", "POST /error_log failed", { error: err.message, stack: err.stack });
|
||||
reportError({ partofservice: "POST /error_log", error: err.stack || err.message });
|
||||
return res.status(500).json({ error: toFriendlyError(500) });
|
||||
}
|
||||
});
|
||||
@@ -189,12 +197,14 @@ app.get("/error_log", verifyAdmin, async (req, res) => {
|
||||
|
||||
return res.json({ errors: rows.rows });
|
||||
} catch (err) {
|
||||
console.error("GET /error_log failed:", err);
|
||||
log("error", "GET /error_log failed", { error: err.message, stack: err.stack });
|
||||
reportError({ partofservice: "GET /error_log", error: err.stack || err.message });
|
||||
return res.status(500).json({ error: toFriendlyError(500) });
|
||||
}
|
||||
});
|
||||
|
||||
app.use(async (err, req, res, _next) => {
|
||||
log("error", "Unhandled error", { error: err?.message, stack: err?.stack, path: req.originalUrl });
|
||||
try {
|
||||
await insertErrorLog({
|
||||
service: "error_log_api",
|
||||
@@ -202,7 +212,7 @@ app.use(async (err, req, res, _next) => {
|
||||
error: err?.stack || err?.message || String(err),
|
||||
});
|
||||
} catch (logErr) {
|
||||
console.error("Failed to log internal error:", logErr);
|
||||
log("error", "Failed to log internal error", { error: logErr.message });
|
||||
}
|
||||
|
||||
if (res.headersSent) return;
|
||||
|
||||
@@ -54,6 +54,7 @@ app.use((req, res, next) => {
|
||||
if (res.statusCode >= 400 && body && typeof body === "object") {
|
||||
const rawError =
|
||||
body.error || body.message || body.details || `HTTP ${res.statusCode}`;
|
||||
log("warn", `HTTP ${res.statusCode}`, { path: req.originalUrl, method: req.method, error: rawError });
|
||||
void reportError({
|
||||
partofservice: `${req.method} ${req.originalUrl}`,
|
||||
error: rawError,
|
||||
@@ -71,7 +72,8 @@ app.get("/health", async (_req, res) => {
|
||||
await db.query("SELECT 1");
|
||||
res.json({ status: "ok", service: "transit_api", database: "ok" });
|
||||
} catch (err) {
|
||||
console.error("GET /health error:", err);
|
||||
log("error", "GET /health error", { error: err.message, stack: err.stack });
|
||||
reportError({ partofservice: "GET /health", error: err.stack || err.message });
|
||||
res.status(503).json({ status: "unavailable", service: "transit_api" });
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user