59 lines
2.0 KiB
TypeScript
59 lines
2.0 KiB
TypeScript
"use server";
|
|
|
|
import { getBackendConfig } from "@/lib/config";
|
|
import { defaultFeatureFlags, type FeatureFlags } from "@/lib/state/slices/features/types";
|
|
import type { RuntimeFlagsResponse } from "@/lib/types/runtime-flags";
|
|
|
|
const { apiBaseUrl } = getBackendConfig();
|
|
|
|
// Server-side feature-flag fetch. Called once per request from the root
|
|
// layout so every page renders with the deployment's flags already in the
|
|
// store. Falls back to defaults (all-off) when the backend is unreachable,
|
|
// so a dev-server restart doesn't crash page rendering.
|
|
//
|
|
// Parsing is type-driven via `defaultFeatureFlags`: every declared key is read
|
|
// from the response and only actual booleans are accepted; missing or
|
|
// non-boolean values remain all-off. Adding a flag is therefore one entry in
|
|
// the declaration — no edit here.
|
|
//
|
|
// See docs/decisions/0007-runtime-feature-flags.md.
|
|
export async function getFeatureFlagsAction(): Promise<FeatureFlags> {
|
|
const url = `${apiBaseUrl}/api/v1/features`;
|
|
const defaults: FeatureFlags = { ...defaultFeatureFlags };
|
|
try {
|
|
const response = await fetch(url, { cache: "no-store" });
|
|
if (!response.ok) {
|
|
return defaults;
|
|
}
|
|
const data = (await response.json()) as Record<string, unknown>;
|
|
return normalizeFlags(data, defaults);
|
|
} catch {
|
|
return defaults;
|
|
}
|
|
}
|
|
|
|
export async function getRuntimeDebugModeAction(): Promise<boolean> {
|
|
const url = `${apiBaseUrl}/api/v1/runtime-flags`;
|
|
try {
|
|
const response = await fetch(url, { cache: "no-store" });
|
|
if (!response.ok) {
|
|
return false;
|
|
}
|
|
const data = (await response.json()) as RuntimeFlagsResponse;
|
|
return data.flags.some((flag) => flag.key === "debug.devMode" && flag.effective_value);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function normalizeFlags(data: Record<string, unknown>, defaults: FeatureFlags): FeatureFlags {
|
|
const out = { ...defaults };
|
|
for (const key of Object.keys(out) as (keyof FeatureFlags)[]) {
|
|
const value = data[key];
|
|
if (typeof value === "boolean") {
|
|
out[key] = value;
|
|
}
|
|
}
|
|
return out;
|
|
}
|