Files
supabase/apps/studio/components/layouts/Navigation/NavigationBar/NavigationBar.utils.tsx
Alaister YoungandAlaister Young 5f6060197e [COM-205] feat(studio): add logs:all flag to hide all logs (#45202)
Adds a top-level `logs:all` flag (default `true`) so self-hosted and
local setups can hide the logs pages in Studio when Logflare isn't
configured — no separate Studio build required. The flag itself works
everywhere; the additional `ENABLED_FEATURES_LOGS_ALL` env-var override
(from FE-3036) is the self-hosted escape hatch so deployers can flip it
without a custom build — that part is a no-op on `IS_PLATFORM` because
hosted feature gating flows through `profile.disabled_features` instead.

Addresses
[COM-205](https://linear.app/supabase/issue/COM-205/add-feature-flag-to-disable-all-logs-in-studio).

**Added:**
- `logs:all` feature flag in `enabled-features.json` + schema

**Changed:**
- Sidebar "Logs" nav entry is hidden when `logs:all` is off (same
pattern as `reports:all` / `billing:all`)
- Cmd-K "Logs Explorer" / "Auth Logs" / etc. routes are hidden when the
flag is off
- `LogsLayout` renders `<UnknownInterface />` (soft-404) when the flag
is off — covers all ~18 logs pages in one spot
- `/logs/index.tsx` applies the same soft-404 for the unified-logs entry
point

## To test

Needs to be tested locally (preview doesn't let you flip the flag —
hosted gating is profile-driven, not env-driven). Two ways:
- Temporarily edit `"logs:all": false` in
`packages/common/enabled-features/enabled-features.json` and run `pnpm
dev:studio`, or
- Run Studio locally with `ENABLED_FEATURES_LOGS_ALL=false` (env-var
path, same as how self-hosted deployers would use it)

With the flag **off**:
- Sidebar "Logs" entry is hidden
- Cmd-K search for "Logs" / "Auth Logs" / "Postgres Logs" etc. returns
nothing
- Direct navigation to `/project/<ref>/logs`,
`/project/<ref>/logs/explorer`, `/project/<ref>/logs/auth-logs`,
`/project/<ref>/logs/postgres-logs` (etc.) all render the "Looking for
something?" soft-404 with a Head back button

With the flag **on** (default): everything works as it does today.
**Check on the preview deploy too** — nothing should change, no
behaviour difference on hosted.

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
2026-04-27 23:13:42 +08:00

229 lines
7.2 KiB
TypeScript

import { Auth, Database, EdgeFunctions, Realtime, SqlEditor, Storage, TableEditor } from 'icons'
import { Blocks, Lightbulb, List, Settings, Telescope } from 'lucide-react'
import { ICON_SIZE, ICON_STROKE_WIDTH } from '@/components/interfaces/Sidebar'
import type { Route } from '@/components/ui/ui.types'
import { EditorIndexPageLink } from '@/data/prefetchers/project.$ref.editor'
import type { Project } from '@/data/projects/project-detail-query'
import { IS_PLATFORM, PROJECT_STATUS } from '@/lib/constants'
import { SHORTCUT_IDS } from '@/state/shortcuts/registry'
interface RouteContext {
ref?: string
isProjectActive: boolean
isProjectBuilding: boolean
buildingUrl: string
}
interface ProductFeatures {
auth?: boolean
edgeFunctions?: boolean
storage?: boolean
realtime?: boolean
authOverviewPage?: boolean
}
interface OtherFeatures {
isPlatform?: boolean
unifiedLogs?: boolean
showReports?: boolean
showLogs?: boolean
}
interface SettingsFeatures {
isPlatform?: boolean
}
function getRouteContext(ref?: string, project?: Project): RouteContext {
return {
ref,
isProjectActive: project?.status === PROJECT_STATUS.ACTIVE_HEALTHY,
isProjectBuilding: project?.status === PROJECT_STATUS.COMING_UP,
buildingUrl: `/project/${ref}`,
}
}
export const generateToolRoutes = (ref?: string, project?: Project): Route[] => {
const { isProjectActive, isProjectBuilding, buildingUrl } = getRouteContext(ref, project)
return [
{
key: 'editor',
label: 'Table Editor',
disabled: !isProjectActive,
icon: <TableEditor size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/editor`),
linkElement: <EditorIndexPageLink projectRef={ref} />,
shortcutId: SHORTCUT_IDS.NAV_TABLE_EDITOR,
},
{
key: 'sql',
label: 'SQL Editor',
disabled: !isProjectActive,
icon: <SqlEditor size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/sql`),
shortcutId: SHORTCUT_IDS.NAV_SQL_EDITOR,
},
]
}
export const generateProductRoutes = (
ref?: string,
project?: Project,
features?: ProductFeatures
): Route[] => {
const { isProjectActive, isProjectBuilding, buildingUrl } = getRouteContext(ref, project)
const authEnabled = features?.auth ?? true
const edgeFunctionsEnabled = features?.edgeFunctions ?? true
const storageEnabled = features?.storage ?? true
const realtimeEnabled = features?.realtime ?? true
const authOverviewPageEnabled = features?.authOverviewPage ?? false
return [
{
key: 'database',
label: 'Database',
disabled: !isProjectActive,
icon: <Database size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link:
ref &&
(isProjectBuilding
? buildingUrl
: isProjectActive
? `/project/${ref}/database/schemas`
: `/project/${ref}/database/backups/scheduled`),
shortcutId: SHORTCUT_IDS.NAV_DATABASE,
},
...(authEnabled
? [
{
key: 'auth',
label: 'Authentication',
disabled: !isProjectActive,
icon: <Auth size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link:
ref &&
(isProjectBuilding
? buildingUrl
: authOverviewPageEnabled
? `/project/${ref}/auth/overview`
: `/project/${ref}/auth/users`),
shortcutId: SHORTCUT_IDS.NAV_AUTH,
},
]
: []),
...(storageEnabled
? [
{
key: 'storage',
label: 'Storage',
disabled: !isProjectActive,
icon: <Storage size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/storage/files`),
shortcutId: SHORTCUT_IDS.NAV_STORAGE,
},
]
: []),
...(edgeFunctionsEnabled
? [
{
key: 'functions',
label: 'Edge Functions',
disabled: false,
icon: <EdgeFunctions size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && `/project/${ref}/functions`,
shortcutId: SHORTCUT_IDS.NAV_FUNCTIONS,
},
]
: []),
...(realtimeEnabled
? [
{
key: 'realtime',
label: 'Realtime',
disabled: !isProjectActive,
icon: <Realtime size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/realtime/inspector`),
shortcutId: SHORTCUT_IDS.NAV_REALTIME,
},
]
: []),
]
}
export const generateOtherRoutes = (
ref?: string,
project?: Project,
features?: OtherFeatures
): Route[] => {
const { isProjectActive, isProjectBuilding, buildingUrl } = getRouteContext(ref, project)
const isPlatform = features?.isPlatform ?? IS_PLATFORM
const unifiedLogsEnabled = features?.unifiedLogs ?? false
const reportsEnabled = features?.showReports ?? true
const logsEnabled = features?.showLogs ?? true
return [
{
key: 'advisors',
label: 'Advisors',
disabled: !isProjectActive,
icon: <Lightbulb size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/advisors/security`),
shortcutId: SHORTCUT_IDS.NAV_ADVISORS,
},
// Observability is only available on the platform, not for self-hosted/CLI
...(isPlatform && reportsEnabled
? [
{
key: 'observability',
label: 'Observability',
disabled: !isProjectActive,
icon: <Telescope size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/observability`),
shortcutId: SHORTCUT_IDS.NAV_OBSERVABILITY,
},
]
: []),
...(logsEnabled
? [
{
key: 'logs',
label: 'Logs',
disabled: false,
icon: <List size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link:
ref &&
(unifiedLogsEnabled ? `/project/${ref}/logs` : `/project/${ref}/logs/explorer`),
shortcutId: SHORTCUT_IDS.NAV_LOGS,
},
]
: []),
{
key: 'integrations',
label: 'Integrations',
disabled: !isProjectActive,
icon: <Blocks size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/integrations`),
shortcutId: SHORTCUT_IDS.NAV_INTEGRATIONS,
},
]
}
export const generateSettingsRoutes = (ref?: string, features?: SettingsFeatures): Route[] => {
const isPlatform = features?.isPlatform ?? IS_PLATFORM
return [
{
key: 'settings',
label: 'Project Settings',
icon: <Settings size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link:
ref &&
(isPlatform ? `/project/${ref}/settings/general` : `/project/${ref}/settings/log-drains`),
disabled: false,
shortcutId: SHORTCUT_IDS.NAV_SETTINGS,
},
]
}