diff --git a/src/interfaces/call-api.ts b/src/interfaces/call-api.ts index 225062e3..3639c37c 100644 --- a/src/interfaces/call-api.ts +++ b/src/interfaces/call-api.ts @@ -4,7 +4,7 @@ import { addLogSequence } from "./utils"; class CallAPI { //Get - public async GetData(request: any, @Path() path: any) { + public async GetData(request: any, @Path() path: any, log = true) { const token = "Bearer " + request.headers.authorization.replace("Bearer ", ""); const url = process.env.API_URL + path; try { diff --git a/src/interfaces/permission.ts b/src/interfaces/permission.ts index 870d2fed..b55d8e72 100644 --- a/src/interfaces/permission.ts +++ b/src/interfaces/permission.ts @@ -3,8 +3,11 @@ import { RequestWithUser } from "../middlewares/user"; import CallAPI from "./call-api"; import HttpError from "./http-error"; import HttpStatus from "./http-status"; +import { promisify } from "util"; class CheckAuth { + private redis = require("redis"); + public async Permission(req: RequestWithUser, system: string, action: string) { if ( req.headers.hasOwnProperty("api_key") && @@ -180,6 +183,41 @@ class CheckAuth { return false; }); } + public async checkOrg(token: any, keycloakId: string) { + const redisClient = await this.redis.createClient({ + host: process.env.REDIS_HOST, + port: process.env.REDIS_PORT, + }); + const getAsync = promisify(redisClient.get).bind(redisClient); + try { + let reply = await getAsync("org_" + keycloakId); + if (reply != null) { + reply = JSON.parse(reply); + } else { + if (!keycloakId) throw new Error("No KeycloakId provided"); + const x = await new CallAPI().GetData( + { + headers: { authorization: token }, + }, + `/org/permission/checkOrg/${keycloakId}`, + false, + ); + + const data = { + orgRootId: x.orgRootId, + orgChild1Id: x.orgChild1Id, + orgChild2Id: x.orgChild2Id, + orgChild3Id: x.orgChild3Id, + orgChild4Id: x.orgChild4Id, + }; + + return data; + } + } catch (error) { + console.error("Error calling API:", error); + throw error; + } + } public async PermissionCreate(req: RequestWithUser, system: string) { return await this.Permission(req, system, "CREATE"); } diff --git a/src/middlewares/logs.ts b/src/middlewares/logs.ts index 2d0d84ab..b1d83ba4 100644 --- a/src/middlewares/logs.ts +++ b/src/middlewares/logs.ts @@ -1,9 +1,6 @@ import { NextFunction, Request, Response } from "express"; import { Client } from "@elastic/elasticsearch"; -import { AppDataSource } from "../database/data-source"; -import { PosMaster } from "../entities/PosMaster"; -import { OrgRevision } from "../entities/OrgRevision"; -import { Profile } from "../entities/Profile"; +import permission from "../interfaces/permission"; if (!process.env.ELASTICSEARCH_INDEX) { throw new Error("Require ELASTICSEARCH_INDEX to store log."); @@ -22,89 +19,69 @@ const LOG_LEVEL_MAP: Record = { const elasticsearch = new Client({ node: `${process.env.ELASTICSEARCH_PROTOCOL}://${process.env.ELASTICSEARCH_HOST}:${process.env.ELASTICSEARCH_PORT}`, }); - async function logMiddleware(req: Request, res: Response, next: NextFunction) { if (!req.url.startsWith("/api/")) return next(); let data: any; - const repoPosmaster = AppDataSource.getRepository(PosMaster); - const repoProfile = AppDataSource.getRepository(Profile); - const repoRevision = AppDataSource.getRepository(OrgRevision); - const originalJson = res.json; res.json = function (v: any) { data = v; return originalJson.call(this, v); }; + const timestamp = new Date().toISOString(); const start = performance.now(); req.app.locals.logData = {}; - const revision = await repoRevision.findOne({ - where: { - orgRevisionIsCurrent: true , - orgRevisionIsDraft: false - }, - }) - - res.on("finish", async() => { - if (!req.url.startsWith("/api/")) return; - let system = "organization"; - if (req.url.startsWith("/api/v1/org/keycloak/log/sso")) system = "inout"; - if (req.url.startsWith("/api/v1/org/metadata/")) system = "master"; - if (req.url.startsWith("/api/v1/org/pos/position/")) system = "master"; - if (req.url.startsWith("/api/v1/org/pos/type/")) system = "master"; - if (req.url.startsWith("/api/v1/org/employee/pos/position/")) system = "master"; - if (req.url.startsWith("/api/v1/org/employee/pos/type/")) system = "master"; - if (req.url.startsWith("/api/v1/org/auth/authRoleAttr/")) system = "admin"; - if (req.url.startsWith("/api/v1/org/auth/authRole/")) system = "admin"; - // if (req.url.startsWith("/api/v1/org/keycloak")) system = "admin"; - if (req.url.startsWith("/api/v1/org/pos/admin/master/list")) system = "admin"; - if (req.url.startsWith("/api/v1/org/super-admin/{id}")) system = "admin"; - if (req.url.startsWith("/api/v1/org/permission-org/")) system = "admin"; - if (req.url.startsWith("/api/v1/org/pos/assign/")) system = "admin"; - if (req.url.startsWith("/api/v1/org/profile/")) system = "registry"; - if (req.url.startsWith("/api/v1/org/profile-employee/")) system = "registry"; - if (req.url.startsWith("/api/v1/org/profile-temp/")) system = "registry"; + res.on("finish", async () => { + try { + if (!req.url.startsWith("/api/")) return; - const level = LOG_LEVEL_MAP[process.env.LOG_LEVEL ?? "debug"] || 4; - const profileByKeycloak = await repoProfile.findOne({ - where: { keycloak: req.app.locals.logData.userId }, - }) - const rootId = await repoPosmaster.findOne({ - where: { - current_holderId: profileByKeycloak?.id, - orgRevisionId: revision?.id - }, - select: ["orgRootId"], - }); - - if (level === 1 && res.statusCode < 500) return; - if (level === 2 && res.statusCode < 400) return; - if (level === 3 && res.statusCode < 200) return; - const obj = { - logType: res.statusCode >= 500 ? "error" : res.statusCode >= 400 ? "warning" : "info", - ip: req.ip, - rootId: rootId?rootId.orgRootId:null, - systemName: system, - startTimeStamp: timestamp, - endTimeStamp: new Date().toISOString(), - processTime: performance.now() - start, - host: req.hostname, - method: req.method, - endpoint: req.url, - responseCode: String(res.statusCode === 304 ? 200 : res.statusCode), - responseDescription: data?.message, - input: (level === 4 && JSON.stringify(req.body, null, 2)) || undefined, - output: (level === 4 && JSON.stringify(data, null, 2)) || undefined, - ...req.app.locals.logData, - }; - elasticsearch.index({ - index: ELASTICSEARCH_INDEX, - document: obj, - }); + const level = LOG_LEVEL_MAP[process.env.LOG_LEVEL ?? "debug"] || 4; + + if (level === 1 && res.statusCode < 500) return; + if (level === 2 && res.statusCode < 400) return; + if (level === 3 && res.statusCode < 200) return; + + const token = req.headers["authorization"]; + let rootId = null; + + try { + rootId = token + ? await new permission().checkOrg(token, req.app.locals.logData.userId) + : null; + } catch (err) { + console.warn("Error fetching rootId:", err); + } + + const obj = { + logType: res.statusCode >= 500 ? "error" : res.statusCode >= 400 ? "warning" : "info", + ip: req.ip, + rootId: rootId?.orgRootId ?? null, + systemName: "evaluation", + startTimeStamp: timestamp, + endTimeStamp: new Date().toISOString(), + processTime: performance.now() - start, + host: req.hostname, + method: req.method, + endpoint: req.url, + responseCode: String(res.statusCode === 304 ? 200 : res.statusCode), + responseDescription: data?.message, + input: level === 4 ? JSON.stringify(req.body, null, 2) : undefined, + output: level === 4 ? JSON.stringify(data, null, 2) : undefined, + ...req.app.locals.logData, + }; + + // Send log to Elasticsearch + await elasticsearch.index({ + index: ELASTICSEARCH_INDEX, + document: obj, + }); + } catch (err) { + console.error("Error in logMiddleware:", err); + } }); return next();