All checks were successful
Build and Push LST Docker Image / docker (push) Successful in 4m26s
169 lines
4.3 KiB
TypeScript
169 lines
4.3 KiB
TypeScript
import { eq } from "drizzle-orm";
|
|
import { db } from "../db/db.controller.js";
|
|
import { prodAuditLogState } from "../db/schema/prodAuditlog.lastProcessed.schema.js";
|
|
import {
|
|
type NewProdAuditLog,
|
|
prodAuditLog,
|
|
} from "../db/schema/prodAuditlog.schema.js";
|
|
import { createLogger } from "../logger/logger.controller.js";
|
|
import { prodQuery } from "../prodSql/prodSqlQuery.controller.js";
|
|
import {
|
|
type SqlQuery,
|
|
sqlQuerySelector,
|
|
} from "../prodSql/prodSqlQuerySelector.utils.js";
|
|
import { delay } from "../utils/delay.utils.js";
|
|
import { returnFunc } from "../utils/returnHelper.utils.js";
|
|
import { tryCatch } from "../utils/trycatch.utils.js";
|
|
|
|
const log = createLogger({ module: "system", subModule: "prodAuditLog" });
|
|
let bufferProcessInProgress = false;
|
|
|
|
export const monitorProdAuditLog = async () => {
|
|
const auditLogQuery = sqlQuerySelector(`prod.auditlog`) as SqlQuery;
|
|
|
|
/*
|
|
get the last processed audit log so we can only pull the newest ones.
|
|
|
|
as the initial go will be zero we want to look at the top 1 so we only pull the most recent one.
|
|
|
|
*/
|
|
|
|
const latestAuditId = await db.select().from(prodAuditLogState).limit(1);
|
|
let auditQuery = auditLogQuery.query;
|
|
if (latestAuditId.length === 0) {
|
|
auditQuery = auditQuery
|
|
.replace(
|
|
"DECLARE @lastId BIGINT = [lstId];",
|
|
"--DECLARE @lastId BIGINT = [lstId];",
|
|
)
|
|
.replace("TOP (500)", "TOP (1)")
|
|
.replace("ASC", "DESC")
|
|
.replace("AND Id > @lastId", "--AND Id > @lastId");
|
|
} else {
|
|
auditQuery = auditQuery.replace(
|
|
"[lstId]",
|
|
`${latestAuditId[0]?.lastImportedAuditId}`,
|
|
);
|
|
}
|
|
|
|
const { data: queryRun, error } = await tryCatch(
|
|
prodQuery(auditQuery, `Running auditLog query`),
|
|
);
|
|
|
|
if (error) {
|
|
return returnFunc({
|
|
success: false,
|
|
level: "error",
|
|
module: "system",
|
|
subModule: "auditLog",
|
|
message: `Data for: AuditLog Failed`,
|
|
data: [error],
|
|
notify: false,
|
|
});
|
|
}
|
|
|
|
if (!queryRun.success) {
|
|
return returnFunc({
|
|
success: false,
|
|
level: "error",
|
|
module: "datamart",
|
|
subModule: "query",
|
|
message: queryRun.message,
|
|
data: queryRun.data,
|
|
notify: false,
|
|
});
|
|
}
|
|
|
|
const safeJsonParse = (value: unknown) => {
|
|
if (typeof value !== "string") return value;
|
|
|
|
try {
|
|
return JSON.parse(value);
|
|
} catch {
|
|
return { raw: value };
|
|
}
|
|
};
|
|
|
|
// remap everything lst logs to keep it easy to read
|
|
|
|
if (queryRun.data.length > 0) {
|
|
const auditRows = queryRun.data.map((r) => ({
|
|
auditId: r.Id,
|
|
actorName: r.ActorName,
|
|
auditCreatedDate: r.CreatedDateTime,
|
|
message: r.Message,
|
|
content: safeJsonParse(r.Content),
|
|
status: "pending",
|
|
processed: false,
|
|
retryCount: 0,
|
|
})) as NewProdAuditLog[];
|
|
|
|
await db.insert(prodAuditLog).values(auditRows).onConflictDoNothing();
|
|
|
|
const newestAuditId = queryRun.data.at(-1).Id;
|
|
|
|
await db
|
|
.insert(prodAuditLogState)
|
|
.values({
|
|
id: 1,
|
|
lastImportedAuditId: newestAuditId,
|
|
updatedAt: new Date(),
|
|
})
|
|
.onConflictDoUpdate({
|
|
target: prodAuditLogState.id,
|
|
set: {
|
|
lastImportedAuditId: newestAuditId,
|
|
updatedAt: new Date(),
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
export const bufferProcess = async (_data: unknown) => {
|
|
if (bufferProcessInProgress) {
|
|
log.debug({}, "[bufferProcess] already running, skipping trigger");
|
|
return;
|
|
}
|
|
|
|
bufferProcessInProgress = true;
|
|
|
|
try {
|
|
log.debug({}, "[bufferProcess] started");
|
|
|
|
while (true) {
|
|
const row = await db.query.prodAuditLog.findFirst({
|
|
where: (audit, { eq }) => eq(audit.processed, false),
|
|
orderBy: (audit, { asc }) => [asc(audit.auditId)],
|
|
});
|
|
|
|
if (!row) {
|
|
log.debug({}, "[bufferProcess] no pending rows");
|
|
break;
|
|
}
|
|
|
|
log.debug({}, "[bufferProcess] processing audit row", row.auditId);
|
|
|
|
// tiny delay so you can visually validate the flow
|
|
await delay(250);
|
|
|
|
// TODO add in case statement to do things, if thing returns good then say good if fails then we set to error and let it retry later.
|
|
// for items that don't fall in the case will auto set status to success
|
|
// console.log(row.message.split("."));
|
|
await db
|
|
.update(prodAuditLog)
|
|
.set({
|
|
processed: true,
|
|
status: "success",
|
|
processedAt: new Date(),
|
|
updatedAt: new Date(),
|
|
})
|
|
.where(eq(prodAuditLog.id, row.id));
|
|
}
|
|
} catch (error) {
|
|
log.error({ stack: error }, "[bufferProcess] failed");
|
|
} finally {
|
|
bufferProcessInProgress = false;
|
|
log.debug({}, "[bufferProcess] finished");
|
|
}
|
|
};
|