Repository navigation
Expand file tree
/
Copy pathenv.ts
More file actions
119 lines (102 loc) · 4.26 KB
/
Copy pathenv.ts
File metadata and controls
119 lines (102 loc) · 4.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/** Runtime environment resolver for Supabase. */
import { E2E_SUPABASE_ANON_KEY, E2E_SUPABASE_URL } from "./e2e-guard";
import { STAGING_PREVIEW_PROXY_PATH, SUPABASE_PROJECTS } from "./projects";
export type SupabaseEnv = "local" | "production" | "staging";
type EnvironmentInput = {
VITE_E2E?: string;
VITE_E2E_ALLOW_PROD?: string;
VITE_LOCAL_PREVIEW?: string;
VITE_LOCAL_SUPABASE_ANON_KEY?: string;
VITE_LOCAL_SUPABASE_URL?: string;
VITE_STAGING_PREVIEW?: string;
VITE_SUPABASE_ENV?: string;
};
type StorageReader = { getItem(key: string): string | null };
/** `pinned` means the environment was fixed at build time and the toggle can't change it. */
type SupabaseConfig = { anonKey: string; env: SupabaseEnv; pinned?: true; url: string };
export const ENV_STORAGE_KEY = "debate-supabase-env";
const ENVS = SUPABASE_PROJECTS;
export function resolveSupabaseConfig(
environment: EnvironmentInput,
storage: StorageReader,
currentOrigin?: string,
): SupabaseConfig {
const localPreview = environment.VITE_LOCAL_PREVIEW === "true";
const stagingPreview = environment.VITE_STAGING_PREVIEW === "true";
const e2e = environment.VITE_E2E === "1";
const allowProduction = environment.VITE_E2E_ALLOW_PROD === "1";
if (localPreview && stagingPreview) {
throw new Error("Local preview and staging preview cannot both be enabled");
}
if (localPreview) {
const url = environment.VITE_LOCAL_SUPABASE_URL?.trim();
const anonKey = environment.VITE_LOCAL_SUPABASE_ANON_KEY?.trim();
if (!url || !anonKey) throw new Error("Local preview configuration is incomplete");
const parsedUrl = new URL(url);
if (Object.values(ENVS).some((remote) => remote.url === parsedUrl.origin)) {
throw new Error("Local preview cannot use a remote Supabase project");
}
return { anonKey, env: "local", url: parsedUrl.href.replace(/\/$/, "") };
}
// Tailnet previews: always staging, reached through the dev server's proxy on
// this origin. Never production, whatever the toggle says.
if (stagingPreview) {
if (!currentOrigin) throw new Error("Staging preview needs the page origin");
return {
anonKey: ENVS.staging.anonKey,
env: "staging",
pinned: true,
url: `${new URL(currentOrigin).origin}${STAGING_PREVIEW_PROXY_PATH}`,
};
}
// Hosted builds can pin one project; the staging Vercel project sets
// VITE_SUPABASE_ENV=staging so each PR gets a staging preview.
const pinned = environment.VITE_SUPABASE_ENV?.trim();
if (pinned) {
if (pinned !== "production" && pinned !== "staging") {
throw new Error(`Unknown VITE_SUPABASE_ENV: ${pinned}`);
}
if (e2e && pinned === "production" && !allowProduction) {
throw new Error("E2E refuses to target the production Supabase project. Set VITE_E2E_ALLOW_PROD=1 to force it.");
}
return { ...ENVS[pinned], env: pinned, pinned: true };
}
const selected = storage.getItem(ENV_STORAGE_KEY) === "staging"
? { ...ENVS.staging, env: "staging" as const }
: { ...ENVS.production, env: "production" as const };
// Playwright sets VITE_E2E. The saved toggle still selects staging, and the
// production fallback goes to a name that does not resolve, so a stub
// session cannot present a malformed token to the production project.
if (e2e && !allowProduction && selected.env === "production") {
return { anonKey: E2E_SUPABASE_ANON_KEY, env: "production", url: E2E_SUPABASE_URL };
}
return selected;
}
const storageReader: StorageReader = {
getItem(key) {
try {
return localStorage.getItem(key);
} catch {
return null;
}
},
};
const active = resolveSupabaseConfig(
import.meta.env,
storageReader,
typeof window === "undefined" ? undefined : window.location.origin,
);
export function getActiveEnv(): SupabaseEnv {
return active.env;
}
/** True when the build fixed the environment (local or staging preview). */
export function isEnvPinned(): boolean {
return active.env === "local" || active.pinned === true;
}
export function setActiveEnv(env: Exclude<SupabaseEnv, "local">): void {
if (isEnvPinned()) return;
if (env === "production") localStorage.removeItem(ENV_STORAGE_KEY);
else localStorage.setItem(ENV_STORAGE_KEY, env);
}
export const SUPABASE_URL = active.url;
export const SUPABASE_ANON_KEY = active.anonKey;