diff --git a/server/private/lib/cache.ts b/server/private/lib/cache.ts index 1d8c24537..e8c03ba3d 100644 --- a/server/private/lib/cache.ts +++ b/server/private/lib/cache.ts @@ -24,23 +24,31 @@ setInterval(() => { */ class AdaptiveCache { private useRedis(): boolean { - return redisManager.isRedisEnabled() && redisManager.getHealthStatus().isHealthy; + return ( + redisManager.isRedisEnabled() && + redisManager.getHealthStatus().isHealthy + ); } /** * Set a value in the cache * @param key - Cache key * @param value - Value to cache (will be JSON stringified for Redis) - * @param ttl - Time to live in seconds (0 = no expiration) + * @param ttl - Time to live in seconds (0 = no expiration; omit = 3600s for Redis) * @returns boolean indicating success */ async set(key: string, value: any, ttl?: number): Promise { const effectiveTtl = ttl === 0 ? undefined : ttl; + const redisTtl = ttl === 0 ? undefined : (ttl ?? 3600); if (this.useRedis()) { try { const serialized = JSON.stringify(value); - const success = await redisManager.set(key, serialized, effectiveTtl); + const success = await redisManager.set( + key, + serialized, + redisTtl + ); if (success) { logger.debug(`Set key in Redis: ${key}`); @@ -48,7 +56,9 @@ class AdaptiveCache { } // Redis failed, fall through to local cache - logger.debug(`Redis set failed for key ${key}, falling back to local cache`); + logger.debug( + `Redis set failed for key ${key}, falling back to local cache` + ); } catch (error) { logger.error(`Redis set error for key ${key}:`, error); // Fall through to local cache @@ -120,9 +130,14 @@ class AdaptiveCache { } // Some Redis deletes failed, fall through to local cache - logger.debug(`Some Redis deletes failed, falling back to local cache`); + logger.debug( + `Some Redis deletes failed, falling back to local cache` + ); } catch (error) { - logger.error(`Redis del error for keys ${keys.join(", ")}:`, error); + logger.error( + `Redis del error for keys ${keys.join(", ")}:`, + error + ); // Fall through to local cache deletedCount = 0; } @@ -195,7 +210,9 @@ class AdaptiveCache { */ async flushAll(): Promise { if (this.useRedis()) { - logger.warn("Adaptive cache flushAll called - Redis flush not implemented, only local cache will be flushed"); + logger.warn( + "Adaptive cache flushAll called - Redis flush not implemented, only local cache will be flushed" + ); } localCache.flushAll(); @@ -239,7 +256,9 @@ class AdaptiveCache { getTtl(key: string): number { // Note: This only works for local cache, Redis TTL is not supported if (this.useRedis()) { - logger.warn(`getTtl called for key ${key} but Redis TTL lookup is not implemented`); + logger.warn( + `getTtl called for key ${key} but Redis TTL lookup is not implemented` + ); } const ttl = localCache.getTtl(key); @@ -255,7 +274,9 @@ class AdaptiveCache { */ keys(): string[] { if (this.useRedis()) { - logger.warn("keys() called but Redis keys are not included, only local cache keys returned"); + logger.warn( + "keys() called but Redis keys are not included, only local cache keys returned" + ); } return localCache.keys(); } diff --git a/server/private/routers/hybrid.ts b/server/private/routers/hybrid.ts index c626e07ba..a38385b0c 100644 --- a/server/private/routers/hybrid.ts +++ b/server/private/routers/hybrid.ts @@ -15,6 +15,7 @@ import { verifySessionRemoteExitNodeMiddleware } from "#private/middlewares/veri import { Router } from "express"; import { db, + logsDb, exitNodes, Resource, ResourcePassword, @@ -1885,7 +1886,7 @@ hybridRouter.post( const batchSize = 100; for (let i = 0; i < logEntries.length; i += batchSize) { const batch = logEntries.slice(i, i + batchSize); - await db.insert(requestAuditLog).values(batch); + await logsDb.insert(requestAuditLog).values(batch); } return response(res, { diff --git a/server/routers/client/listClients.ts b/server/routers/client/listClients.ts index 84226a6dc..3b7adf2d5 100644 --- a/server/routers/client/listClients.ts +++ b/server/routers/client/listClients.ts @@ -70,7 +70,7 @@ async function getLatestOlmVersion(): Promise { tags = tags.filter((version) => !version.name.includes("rc")); const latestVersion = tags[0].name; - olmVersionCache.set("latestOlmVersion", latestVersion); + olmVersionCache.set("latestOlmVersion", latestVersion, 3600); return latestVersion; } catch (error: any) { diff --git a/server/routers/client/listUserDevices.ts b/server/routers/client/listUserDevices.ts index eb63812c6..e99760b91 100644 --- a/server/routers/client/listUserDevices.ts +++ b/server/routers/client/listUserDevices.ts @@ -71,7 +71,7 @@ async function getLatestOlmVersion(): Promise { tags = tags.filter((version) => !version.name.includes("rc")); const latestVersion = tags[0].name; - olmVersionCache.set("latestOlmVersion", latestVersion); + olmVersionCache.set("latestOlmVersion", latestVersion, 3600); return latestVersion; } catch (error: any) { diff --git a/server/routers/site/listSites.ts b/server/routers/site/listSites.ts index 9ff7a6933..a87ad3daf 100644 --- a/server/routers/site/listSites.ts +++ b/server/routers/site/listSites.ts @@ -55,7 +55,7 @@ async function getLatestNewtVersion(): Promise { tags = tags.filter((version) => !version.name.includes("rc")); const latestVersion = tags[0].name; - await cache.set("latestNewtVersion", latestVersion); + await cache.set("latestNewtVersion", latestVersion, 3600); return latestVersion; } catch (error: any) { @@ -180,7 +180,7 @@ registry.registerPath({ method: "get", path: "/org/{orgId}/sites", description: "List all sites in an organization", - tags: [OpenAPITags.Site], + tags: [OpenAPITags.Org, OpenAPITags.Site], request: { params: listSitesParamsSchema, query: listSitesSchema diff --git a/server/routers/user/inviteUser.ts b/server/routers/user/inviteUser.ts index 06d9512fb..b0632da9e 100644 --- a/server/routers/user/inviteUser.ts +++ b/server/routers/user/inviteUser.ts @@ -201,7 +201,7 @@ export async function inviteUser( ); } - await cache.set(email, attempts + 1); + await cache.set("regenerateInvite:" + email, attempts + 1, 3600); const inviteId = existingInvite[0].inviteId; // Retrieve the original inviteId const token = generateRandomString( diff --git a/src/components/OrgSelector.tsx b/src/components/OrgSelector.tsx index db43b1e65..fcbc700a2 100644 --- a/src/components/OrgSelector.tsx +++ b/src/components/OrgSelector.tsx @@ -29,6 +29,7 @@ import { usePathname, useRouter } from "next/navigation"; import { useMemo, useState } from "react"; import { useUserContext } from "@app/hooks/useUserContext"; import { useTranslations } from "next-intl"; +import { build } from "@server/build"; interface OrgSelectorProps { orgId?: string; @@ -50,6 +51,11 @@ export function OrgSelector({ const selectedOrg = orgs?.find((org) => org.orgId === orgId); + let canCreateOrg = !env.flags.disableUserCreateOrg || user.serverAdmin; + if (build === "saas" && user.type !== "internal") { + canCreateOrg = false; + } + const sortedOrgs = useMemo(() => { if (!orgs?.length) return orgs ?? []; return [...orgs].sort((a, b) => { @@ -161,7 +167,7 @@ export function OrgSelector({ - {(!env.flags.disableUserCreateOrg || user.serverAdmin) && ( + {canCreateOrg && (