From 114592add89833f51674c48d177d18916bf2cabc Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 19 Aug 2026 16:16:05 -0400 Subject: [PATCH] Show a warning if the logs are disabled to reduce confusion --- messages/en-US.json | 3 ++ server/db/pg/schema/schema.ts | 2 +- server/lib/readConfigFile.ts | 6 ++- server/private/lib/config.ts | 28 +++++++++---- server/private/lib/readConfigFile.ts | 11 +++-- .../private/lib/traefik/getTraefikConfig.ts | 3 +- src/app/[orgId]/settings/logs/access/page.tsx | 10 +++++ src/app/[orgId]/settings/logs/action/page.tsx | 10 +++++ src/app/[orgId]/settings/logs/ai/page.tsx | 11 +++++ .../[orgId]/settings/logs/connection/page.tsx | 10 +++++ src/app/[orgId]/settings/logs/layout.tsx | 14 ++++++- .../[orgId]/settings/logs/request/page.tsx | 11 +++++ src/components/LogRetentionWarning.tsx | 40 +++++++++++++++++++ 13 files changed, 143 insertions(+), 16 deletions(-) create mode 100644 src/components/LogRetentionWarning.tsx diff --git a/messages/en-US.json b/messages/en-US.json index 89d8b1c4b..02c022e35 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -3547,6 +3547,9 @@ "sidebarLogsAction": "Admin Action Logs", "logRetention": "Log Retention", "logRetentionDescription": "Manage how long different types of logs are retained for this organization or disable them", + "logRetentionDisabledWarningTitle": "Log Retention Disabled", + "logRetentionDisabledWarningDescription": "{logType} are not being retained for this organization, so new activity will not appear here. Enable retention in security settings to start collecting these logs.", + "logRetentionDisabledWarningButton": "Go to Security Settings", "requestLogsDescription": "View detailed request logs for HTTPS resources in this organization", "aiSessionLogs": "AI Gateway Session Logs", "aiSessionLogsDescription": "View prompt and response transcripts for AI gateway requests in this organization", diff --git a/server/db/pg/schema/schema.ts b/server/db/pg/schema/schema.ts index f9863c154..1a4013f0f 100644 --- a/server/db/pg/schema/schema.ts +++ b/server/db/pg/schema/schema.ts @@ -69,7 +69,7 @@ export const orgs = pgTable("orgs", { "settingsLogRetentionDaysAISessions" ) // where 0 = dont keep logs and -1 = keep forever and 9001 = end of the following year .notNull() - .default(7), + .default(0), sshCaPrivateKey: text("sshCaPrivateKey"), // Encrypted SSH CA private key (PEM format) sshCaPublicKey: text("sshCaPublicKey"), // SSH CA public key (OpenSSH format) isBillingOrg: boolean("isBillingOrg"), diff --git a/server/lib/readConfigFile.ts b/server/lib/readConfigFile.ts index 5f6939675..68dc7cea7 100644 --- a/server/lib/readConfigFile.ts +++ b/server/lib/readConfigFile.ts @@ -443,7 +443,11 @@ export const configSchema = z disable_config_managed_domains: z.boolean().optional(), disable_product_help_banners: z.boolean().optional(), disable_enterprise_features: z.boolean().optional(), - enable_acme_cert_sync: z.boolean().optional().default(true) + enable_acme_cert_sync: z.boolean().optional().default(true), + disable_private_http_placeholder: z + .boolean() + .optional() + .default(false) }) .optional(), acme: z diff --git a/server/private/lib/config.ts b/server/private/lib/config.ts index 278d9d636..e819b1281 100644 --- a/server/private/lib/config.ts +++ b/server/private/lib/config.ts @@ -48,7 +48,7 @@ export class PrivateConfig { this.rawPrivateConfig = parsedPrivateConfig; - this.migrateDeprecatedAcmeConfig(privateEnvironment); + this.migrateDeprecatedConfig(privateEnvironment); process.env.BRANDING_HIDE_AUTH_LAYOUT_FOOTER = this.rawPrivateConfig.branding?.hide_auth_layout_footer === true @@ -152,12 +152,12 @@ export class PrivateConfig { return this.rawPrivateConfig; } - // `flags.enable_acme_cert_sync` and `acme` used to live in the private - // config file. They now live in the public config file. If an operator - // still has them set in the private config and hasn't moved them over to - // the public config, pull them forward so behavior doesn't silently - // change out from under them. - private migrateDeprecatedAcmeConfig(privateEnvironment: any) { + // `flags.enable_acme_cert_sync`, `flags.disable_private_http_placeholder`, + // and `acme` used to live in the private config file. They now live in + // the public config file. If an operator still has them set in the + // private config and hasn't moved them over to the public config, pull + // them forward so behavior doesn't silently change out from under them. + private migrateDeprecatedConfig(privateEnvironment: any) { const publicEnvironment: any = readPublicConfigFile(); const rawConfig: any = config.getRawConfig(); @@ -182,6 +182,20 @@ export class PrivateConfig { ); rawConfig.acme = this.rawPrivateConfig.acme; } + + if ( + privateEnvironment?.flags?.disable_private_http_placeholder !== + undefined && + publicEnvironment?.flags?.disable_private_http_placeholder === + undefined + ) { + logger.warn( + "`flags.disable_private_http_placeholder` is deprecated in the private config file and has moved to the public config file. Using the value from the private config file for now, but please move it to the public config." + ); + rawConfig.flags = rawConfig.flags ?? {}; + rawConfig.flags.disable_private_http_placeholder = + this.rawPrivateConfig.flags.disable_private_http_placeholder; + } } } diff --git a/server/private/lib/readConfigFile.ts b/server/private/lib/readConfigFile.ts index 8be68672d..363c9b782 100644 --- a/server/private/lib/readConfigFile.ts +++ b/server/private/lib/readConfigFile.ts @@ -115,10 +115,13 @@ export const privateConfigSchema = z // any value set here is migrated into the public config at // startup by PrivateConfig (server/private/lib/config.ts). enable_acme_cert_sync: z.boolean().optional(), - disable_private_http_placeholder: z - .boolean() - .optional() - .default(false) + // @deprecated Moved to the public config file as + // `flags.disable_private_http_placeholder` + // (server/lib/readConfigFile.ts). Kept here only so existing + // private config files keep parsing; any value set here is + // migrated into the public config at startup by PrivateConfig + // (server/private/lib/config.ts). + disable_private_http_placeholder: z.boolean().optional() }) .optional() .prefault({}), diff --git a/server/private/lib/traefik/getTraefikConfig.ts b/server/private/lib/traefik/getTraefikConfig.ts index ecbf1d68d..0515b8265 100644 --- a/server/private/lib/traefik/getTraefikConfig.ts +++ b/server/private/lib/traefik/getTraefikConfig.ts @@ -329,8 +329,7 @@ export async function getTraefikConfig( }[] = []; if ( build == "enterprise" && - !privateConfig.getRawPrivateConfig().flags - .disable_private_http_placeholder + !config.getRawConfig().flags?.disable_private_http_placeholder ) { // we dont want to do this on the cloud // Query siteResources in HTTP mode with SSL enabled and aliases - cert generation / HTTPS edge diff --git a/src/app/[orgId]/settings/logs/access/page.tsx b/src/app/[orgId]/settings/logs/access/page.tsx index a6fa601e9..48c5d11d1 100644 --- a/src/app/[orgId]/settings/logs/access/page.tsx +++ b/src/app/[orgId]/settings/logs/access/page.tsx @@ -19,6 +19,8 @@ import { getPrivateResourceSettingsHref } from "@app/lib/launcherResourceAdminHr import axios from "axios"; import { useStoredPageSize } from "@app/hooks/useStoredPageSize"; import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert"; +import LogRetentionWarning from "@app/components/LogRetentionWarning"; +import { useOrgContext } from "@app/hooks/useOrgContext"; import { usePaidStatus } from "@app/hooks/usePaidStatus"; import { tierMatrix } from "@server/lib/billing/tierMatrix"; import { logQueries } from "@app/lib/queries"; @@ -32,6 +34,7 @@ export default function GeneralPage() { const t = useTranslations(); const { orgId } = useParams(); + const { org } = useOrgContext(); const { isPaidUser } = usePaidStatus(); const [isExporting, startTransition] = useTransition(); @@ -529,6 +532,13 @@ export default function GeneralPage() { + {org.org.settingsLogRetentionDaysAccess === 0 && ( + + )} + + {org.org.settingsLogRetentionDaysAction === 0 && ( + + )} + (0); @@ -641,6 +645,13 @@ export default function AiSessionLogsPage() { description={t("aiSessionLogsDescription")} /> + {org.org.settingsLogRetentionDaysAISessions === 0 && ( + + )} + + {org.org.settingsLogRetentionDaysConnection === 0 && ( + + )} + {children}; } diff --git a/src/app/[orgId]/settings/logs/request/page.tsx b/src/app/[orgId]/settings/logs/request/page.tsx index 732bad3ad..d5ecdbfab 100644 --- a/src/app/[orgId]/settings/logs/request/page.tsx +++ b/src/app/[orgId]/settings/logs/request/page.tsx @@ -2,9 +2,11 @@ import { ColumnFilter } from "@app/components/ColumnFilter"; import { DateTimeValue } from "@app/components/DateTimePicker"; import { LogDataTable } from "@app/components/LogDataTable"; +import LogRetentionWarning from "@app/components/LogRetentionWarning"; import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; import { Button } from "@app/components/ui/button"; import { useEnvContext } from "@app/hooks/useEnvContext"; +import { useOrgContext } from "@app/hooks/useOrgContext"; import { toast } from "@app/hooks/useToast"; import { createApiClient } from "@app/lib/api"; import { useTranslations } from "next-intl"; @@ -29,6 +31,8 @@ export default function GeneralPage() { const { orgId } = useParams(); const searchParams = useSearchParams(); + const { org } = useOrgContext(); + const [isExporting, startTransition] = useTransition(); const [currentPage, setCurrentPage] = useState(0); @@ -714,6 +718,13 @@ export default function GeneralPage() { description={t("requestLogsDescription")} /> + {org.org.settingsLogRetentionDaysRequest === 0 && ( + + )} + } + description={t("logRetentionDisabledWarningDescription", { + logType: logTypeLabel + })} + actions={ + + + + } + /> + ); +} + +export default LogRetentionWarning;