diff --git a/server/private/routers/external.ts b/server/private/routers/external.ts index bd4d232de..df8ea8cbb 100644 --- a/server/private/routers/external.ts +++ b/server/private/routers/external.ts @@ -515,6 +515,6 @@ authenticated.post( verifyOrgAccess, verifyLimits, verifyUserHasAction(ActionsEnum.signSshKey), - logActionAudit(ActionsEnum.signSshKey), + // logActionAudit(ActionsEnum.signSshKey), // it is handled inside of the function below so we can include more metadata ssh.signSshKey ); diff --git a/server/private/routers/ssh/signSshKey.ts b/server/private/routers/ssh/signSshKey.ts index e70951812..5cffb4a34 100644 --- a/server/private/routers/ssh/signSshKey.ts +++ b/server/private/routers/ssh/signSshKey.ts @@ -14,7 +14,9 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { + actionAuditLog, db, + logsDb, newts, roles, roundTripMessageTracker, @@ -34,6 +36,7 @@ import { canUserAccessSiteResource } from "@server/auth/canUserAccessSiteResourc import { signPublicKey, getOrgCAKeys } from "@server/lib/sshCA"; import config from "@server/lib/config"; import { sendToClient } from "#private/routers/ws"; +import { ActionsEnum } from "@server/auth/actions"; const paramsSchema = z.strictObject({ orgId: z.string().nonempty() @@ -446,6 +449,20 @@ export async function signSshKey( sshHost = resource.destination; } + await logsDb.insert(actionAuditLog).values({ + timestamp: Math.floor(Date.now() / 1000), + orgId: orgId, + actorType: "user", + actor: req.user?.username ?? "", + actorId: req.user?.userId ?? "", + action: ActionsEnum.signSshKey, + metadata: JSON.stringify({ + resourceId: resource.siteResourceId, + resource: resource.name, + siteId: resource.siteId, + }) + }); + return response(res, { data: { certificate: cert.certificate, diff --git a/server/private/routers/ws/ws.ts b/server/private/routers/ws/ws.ts index eec9cfe89..4bfda5da8 100644 --- a/server/private/routers/ws/ws.ts +++ b/server/private/routers/ws/ws.ts @@ -197,6 +197,12 @@ const connectedClients: Map = new Map(); // Config version tracking map (local to this node, resets on server restart) const clientConfigVersions: Map = new Map(); +// Tracks the last Unix timestamp (seconds) at which a ping was flushed to the +// DB for a given siteId. Resets on server restart which is fine – the first +// ping after startup will always write, re-establishing the online state. +const lastPingDbWrite: Map = new Map(); +const PING_DB_WRITE_INTERVAL = 45; // seconds + // Recovery tracking let isRedisRecoveryInProgress = false; @@ -855,12 +861,16 @@ const setupConnection = async ( const newtClient = client as Newt; ws.on("ping", async () => { if (!newtClient.siteId) return; + const now = Math.floor(Date.now() / 1000); + const lastWrite = lastPingDbWrite.get(newtClient.siteId) ?? 0; + if (now - lastWrite < PING_DB_WRITE_INTERVAL) return; + lastPingDbWrite.set(newtClient.siteId, now); try { await db .update(sites) .set({ online: true, - lastPing: Math.floor(Date.now() / 1000) + lastPing: now }) .where(eq(sites.siteId, newtClient.siteId)); } catch (error) {