Merge branch 'dev' into audit-logs

This commit is contained in:
Owen
2025-10-24 11:15:39 -07:00
9 changed files with 35 additions and 51 deletions

View File

@@ -1,5 +1,4 @@
import { eq, sql, and } from "drizzle-orm";
import NodeCache from "node-cache";
import { v4 as uuidv4 } from "uuid";
import { PutObjectCommand } from "@aws-sdk/client-s3";
import * as fs from "fs/promises";
@@ -20,6 +19,7 @@ import logger from "@server/logger";
import { sendToClient } from "#dynamic/routers/ws";
import { build } from "@server/build";
import { s3Client } from "@server/lib/s3";
import cache from "@server/lib/cache";
interface StripeEvent {
identifier?: string;
@@ -43,7 +43,6 @@ export function noop() {
}
export class UsageService {
private cache: NodeCache;
private bucketName: string | undefined;
private currentEventFile: string | null = null;
private currentFileStartTime: number = 0;
@@ -51,7 +50,6 @@ export class UsageService {
private uploadingFiles: Set<string> = new Set();
constructor() {
this.cache = new NodeCache({ stdTTL: 300 }); // 5 minute TTL
if (noop()) {
return;
}
@@ -399,7 +397,7 @@ export class UsageService {
featureId: FeatureId
): Promise<string | null> {
const cacheKey = `customer_${orgId}_${featureId}`;
const cached = this.cache.get<string>(cacheKey);
const cached = cache.get<string>(cacheKey);
if (cached) {
return cached;
@@ -422,7 +420,7 @@ export class UsageService {
const customerId = customer.customerId;
// Cache the result
this.cache.set(cacheKey, customerId);
cache.set(cacheKey, customerId, 300); // 5 minute TTL
return customerId;
} catch (error) {
@@ -700,10 +698,6 @@ export class UsageService {
await this.uploadFileToS3();
}
public clearCache(): void {
this.cache.flushAll();
}
/**
* Scan the events directory for files older than 1 minute and upload them if not empty.
*/

5
server/lib/cache.ts Normal file
View File

@@ -0,0 +1,5 @@
import NodeCache from "node-cache";
export const cache = new NodeCache({ stdTTL: 3600, checkperiod: 120 });
export default cache;

View File

@@ -16,8 +16,8 @@ import { certificates, db } from "@server/db";
import { and, eq, isNotNull, or, inArray, sql } from "drizzle-orm";
import { decryptData } from "@server/lib/encryption";
import * as fs from "fs";
import NodeCache from "node-cache";
import logger from "@server/logger";
import cache from "@server/lib/cache";
let encryptionKeyPath = "";
let encryptionKeyHex = "";
@@ -51,9 +51,6 @@ export type CertificateResult = {
updatedAt?: number | null;
};
// --- In-Memory Cache Implementation ---
const certificateCache = new NodeCache({ stdTTL: 180 }); // Cache for 3 minutes (180 seconds)
export async function getValidCertificatesForDomains(
domains: Set<string>,
useCache: boolean = true
@@ -67,7 +64,8 @@ export async function getValidCertificatesForDomains(
// 1. Check cache first if enabled
if (useCache) {
for (const domain of domains) {
const cachedCert = certificateCache.get<CertificateResult>(domain);
const cacheKey = `cert:${domain}`;
const cachedCert = cache.get<CertificateResult>(cacheKey);
if (cachedCert) {
finalResults.push(cachedCert); // Valid cache hit
} else {
@@ -180,7 +178,8 @@ export async function getValidCertificatesForDomains(
// Add to cache for future requests, using the *requested domain* as the key
if (useCache) {
certificateCache.set(domain, resultCert);
const cacheKey = `cert:${domain}`;
cache.set(cacheKey, resultCert, 180);
}
}
}

View File

@@ -24,7 +24,6 @@ import logger from "@server/logger";
import HttpCode from "@server/types/HttpCode";
import { NextFunction, Request, Response } from "express";
import createHttpError from "http-errors";
import NodeCache from "node-cache";
import { z } from "zod";
import { fromError } from "zod-validation-error";
import { getCountryCodeForIp } from "@server/lib/geoip";
@@ -32,11 +31,7 @@ import { getOrgTierData } from "#dynamic/lib/billing";
import { TierId } from "@server/lib/billing/tiers";
import { verifyPassword } from "@server/auth/password";
import { logRequestAudit } from "./logRequestAudit";
// We'll see if this speeds anything up
const cache = new NodeCache({
stdTTL: 5 // seconds
});
import cache from "@server/lib/cache";
const verifyResourceSessionSchema = z.object({
sessions: z.record(z.string()).optional(),
@@ -165,7 +160,7 @@ export async function verifyResourceSession(
}
resourceData = result;
cache.set(resourceCacheKey, resourceData);
cache.set(resourceCacheKey, resourceData, 5);
}
const { resource, pincode, password, headerAuth } = resourceData;
@@ -425,7 +420,7 @@ export async function verifyResourceSession(
headerAuth.headerAuthHash
)
) {
cache.set(clientHeaderAuthKey, clientHeaderAuth);
cache.set(clientHeaderAuthKey, clientHeaderAuth, 5);
logger.debug("Resource allowed because header auth is valid");
logRequestAudit(
@@ -524,7 +519,7 @@ export async function verifyResourceSession(
);
resourceSession = result?.resourceSession;
cache.set(sessionCacheKey, resourceSession);
cache.set(sessionCacheKey, resourceSession, 5);
}
if (resourceSession?.isRequestToken) {
@@ -651,7 +646,7 @@ export async function verifyResourceSession(
resource
);
cache.set(userAccessCacheKey, allowedUserData);
cache.set(userAccessCacheKey, allowedUserData, 5);
}
if (
@@ -885,7 +880,7 @@ async function checkRules(
if (!rules) {
rules = await getResourceRules(resourceId);
cache.set(ruleCacheKey, rules);
cache.set(ruleCacheKey, rules, 5);
}
if (rules.length === 0) {

View File

@@ -1,10 +1,5 @@
import NodeCache from "node-cache";
import { sendToClient } from "#dynamic/routers/ws";
export const dockerSocketCache = new NodeCache({
stdTTL: 3600 // seconds
});
export function fetchContainers(newtId: string) {
const payload = {
type: `newt/socket/fetch`,

View File

@@ -1,8 +1,8 @@
import { MessageHandler } from "@server/routers/ws";
import logger from "@server/logger";
import { dockerSocketCache } from "./dockerSocket";
import { Newt } from "@server/db";
import { applyNewtDockerBlueprint } from "@server/lib/blueprints/applyNewtDockerBlueprint";
import cache from "@server/lib/cache";
export const handleDockerStatusMessage: MessageHandler = async (context) => {
const { message, client, sendToClient } = context;
@@ -24,8 +24,8 @@ export const handleDockerStatusMessage: MessageHandler = async (context) => {
if (available) {
logger.info(`Newt ${newt.newtId} has Docker socket access`);
dockerSocketCache.set(`${newt.newtId}:socketPath`, socketPath, 0);
dockerSocketCache.set(`${newt.newtId}:isAvailable`, available, 0);
cache.set(`${newt.newtId}:socketPath`, socketPath, 0);
cache.set(`${newt.newtId}:isAvailable`, available, 0);
} else {
logger.warn(`Newt ${newt.newtId} does not have Docker socket access`);
}
@@ -54,7 +54,7 @@ export const handleDockerContainersMessage: MessageHandler = async (
);
if (containers && containers.length > 0) {
dockerSocketCache.set(`${newt.newtId}:dockerContainers`, containers, 0);
cache.set(`${newt.newtId}:dockerContainers`, containers, 0);
} else {
logger.warn(`Newt ${newt.newtId} does not have Docker containers`);
}

View File

@@ -9,14 +9,12 @@ import createHttpError from "http-errors";
import { z } from "zod";
import { fromError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi";
import NodeCache from "node-cache";
import semver from "semver";
const newtVersionCache = new NodeCache({ stdTTL: 3600 }); // 1 hours in seconds
import cache from "@server/lib/cache";
async function getLatestNewtVersion(): Promise<string | null> {
try {
const cachedVersion = newtVersionCache.get<string>("latestNewtVersion");
const cachedVersion = cache.get<string>("latestNewtVersion");
if (cachedVersion) {
return cachedVersion;
}
@@ -48,7 +46,7 @@ async function getLatestNewtVersion(): Promise<string | null> {
const latestVersion = tags[0].name;
newtVersionCache.set("latestNewtVersion", latestVersion);
cache.set("latestNewtVersion", latestVersion);
return latestVersion;
} catch (error: any) {

View File

@@ -12,9 +12,9 @@ import stoi from "@server/lib/stoi";
import { sendToClient } from "#dynamic/routers/ws";
import {
fetchContainers,
dockerSocketCache,
dockerSocket
} from "../newt/dockerSocket";
import cache from "@server/lib/cache";
export interface ContainerNetwork {
networkId: string;
@@ -157,7 +157,7 @@ async function triggerFetch(siteId: number) {
// clear the cache for this Newt ID so that the site has to keep asking for the containers
// this is to ensure that the site always gets the latest data
dockerSocketCache.del(`${newt.newtId}:dockerContainers`);
cache.del(`${newt.newtId}:dockerContainers`);
return { siteId, newtId: newt.newtId };
}
@@ -165,7 +165,7 @@ async function triggerFetch(siteId: number) {
async function queryContainers(siteId: number) {
const { newt } = await getSiteAndNewt(siteId);
const result = dockerSocketCache.get(
const result = cache.get(
`${newt.newtId}:dockerContainers`
) as Container[];
if (!result) {
@@ -182,7 +182,7 @@ async function isDockerAvailable(siteId: number): Promise<boolean> {
const { newt } = await getSiteAndNewt(siteId);
const key = `${newt.newtId}:isAvailable`;
const isAvailable = dockerSocketCache.get(key);
const isAvailable = cache.get(key);
return !!isAvailable;
}
@@ -196,8 +196,8 @@ async function getDockerStatus(
const mappedKeys = keys.map((x) => `${newt.newtId}:${x}`);
const result = {
isAvailable: dockerSocketCache.get(mappedKeys[0]) as boolean,
socketPath: dockerSocketCache.get(mappedKeys[1]) as string | undefined
isAvailable: cache.get(mappedKeys[0]) as boolean,
socketPath: cache.get(mappedKeys[1]) as string | undefined
};
return result;

View File

@@ -1,4 +1,3 @@
import NodeCache from "node-cache";
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
@@ -20,8 +19,7 @@ import { UserType } from "@server/types/UserTypes";
import { usageService } from "@server/lib/billing/usageService";
import { FeatureId } from "@server/lib/billing";
import { build } from "@server/build";
const regenerateTracker = new NodeCache({ stdTTL: 3600, checkperiod: 600 });
import cache from "@server/lib/cache";
const inviteUserParamsSchema = z
.object({
@@ -182,7 +180,7 @@ export async function inviteUser(
}
if (existingInvite.length) {
const attempts = regenerateTracker.get<number>(email) || 0;
const attempts = cache.get<number>(email) || 0;
if (attempts >= 3) {
return next(
createHttpError(
@@ -192,7 +190,7 @@ export async function inviteUser(
);
}
regenerateTracker.set(email, attempts + 1);
cache.set(email, attempts + 1);
const inviteId = existingInvite[0].inviteId; // Retrieve the original inviteId
const token = generateRandomString(