Skip to content

Commit b5f705e

Browse files
authored
feat(cloud): use @executor/env for env access (#100)
2 parents 004ac0d + 1b950e4 commit b5f705e

10 files changed

Lines changed: 91 additions & 15 deletions

File tree

‎apps/cloud/package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"@effect-atom/atom-react": "^0.5.0",
99
"@effect/platform": "catalog:",
1010
"@executor/api": "workspace:*",
11+
"@executor/env": "workspace:*",
1112
"@executor/execution": "workspace:*",
1213
"@executor/plugin-google-discovery": "workspace:*",
1314
"@executor/plugin-graphql": "workspace:*",

‎apps/cloud/src/api.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { CloudAuthHandlers, CloudAuthPublicHandlers } from "./auth/handlers";
3131
import { WorkOSAuth } from "./auth/workos";
3232
import { DbService } from "./services/db";
3333
import { createTeamExecutor } from "./services/executor";
34+
import { server } from "./env";
3435

3536
const ProtectedCloudApi = addGroup(OpenApiGroup)
3637
.add(McpGroup)
@@ -104,7 +105,7 @@ const COOKIE_OPTIONS = {
104105
httpOnly: true,
105106
sameSite: "lax" as const,
106107
maxAge: 60 * 60 * 24 * 7,
107-
secure: process.env.NODE_ENV === "production",
108+
secure: server.NODE_ENV === "production",
108109
};
109110

110111
const resolveAuth = (request: Request) =>
@@ -151,7 +152,7 @@ const resolveExecutor = (teamId: string) =>
151152
const users = yield* UserStoreService;
152153
const team = yield* users.use((store) => store.getTeam(teamId));
153154
const teamName = team?.name ?? "Unknown Team";
154-
const encryptionKey = process.env.ENCRYPTION_KEY ?? "local-dev-encryption-key";
155+
const encryptionKey = server.ENCRYPTION_KEY;
155156
return yield* createTeamExecutor(teamId, teamName, encryptionKey);
156157
});
157158

‎apps/cloud/src/auth/handlers.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ import { addGroup } from "@executor/api";
66
import { AUTH_PATHS, CloudAuthApi, CloudAuthPublicApi } from "./api";
77
import { AuthContext, UserStoreService } from "./context";
88
import { WorkOSAuth } from "./workos";
9+
import { server } from "../env";
910

1011
const COOKIE_OPTIONS = {
1112
path: "/",
1213
httpOnly: true,
1314
sameSite: "lax" as const,
1415
maxAge: 60 * 60 * 24 * 7,
15-
secure: process.env.NODE_ENV === "production",
16+
secure: server.NODE_ENV === "production",
1617
};
1718

1819
// ---------------------------------------------------------------------------

‎apps/cloud/src/auth/workos.ts‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import { Context, Effect, Layer } from "effect";
66
import { WorkOS } from "@workos-inc/node";
77
import { WorkOSError } from "./errors";
8+
import { server } from "../env";
89

910
const COOKIE_NAME = "wos-session";
1011

@@ -14,9 +15,9 @@ const COOKIE_NAME = "wos-session";
1415

1516

1617
const make = Effect.gen(function* () {
17-
const apiKey = process.env.WORKOS_API_KEY!;
18-
const clientId = process.env.WORKOS_CLIENT_ID!;
19-
const cookiePassword = process.env.WORKOS_COOKIE_PASSWORD!;
18+
const apiKey = server.WORKOS_API_KEY;
19+
const clientId = server.WORKOS_CLIENT_ID;
20+
const cookiePassword = server.WORKOS_COOKIE_PASSWORD;
2021

2122
if (!cookiePassword || cookiePassword.length < 32) {
2223
return yield* Effect.die(new Error("WORKOS_COOKIE_PASSWORD must be at least 32 characters"));

‎apps/cloud/src/env.ts‎

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { createEnv, Env } from "@executor/env";
2+
3+
const sharedShape = {
4+
NODE_ENV: Env.literalOr(
5+
"NODE_ENV",
6+
"development",
7+
"development",
8+
"test",
9+
"production",
10+
),
11+
};
12+
13+
const serverShape = {
14+
DATABASE_URL: Env.stringOr("DATABASE_URL", ""),
15+
PGLITE_DATA_DIR: Env.stringOr("PGLITE_DATA_DIR", ".pglite"),
16+
ENCRYPTION_KEY: Env.stringOr(
17+
"ENCRYPTION_KEY",
18+
"local-dev-encryption-key",
19+
),
20+
WORKOS_API_KEY: Env.string("WORKOS_API_KEY"),
21+
WORKOS_CLIENT_ID: Env.string("WORKOS_CLIENT_ID"),
22+
WORKOS_COOKIE_PASSWORD: Env.string("WORKOS_COOKIE_PASSWORD"),
23+
};
24+
25+
type SharedEnv = Readonly<{
26+
NODE_ENV: "development" | "test" | "production";
27+
}>;
28+
29+
type ServerEnv = SharedEnv & Readonly<{
30+
DATABASE_URL: string;
31+
PGLITE_DATA_DIR: string;
32+
ENCRYPTION_KEY: string;
33+
WORKOS_API_KEY: string;
34+
WORKOS_CLIENT_ID: string;
35+
WORKOS_COOKIE_PASSWORD: string;
36+
}>;
37+
38+
type WebEnv = Readonly<Record<string, never>>;
39+
40+
export const shared = createEnv(sharedShape, {
41+
runtimeEnv: process.env,
42+
emptyStringAsUndefined: true,
43+
}) as SharedEnv;
44+
45+
export const web = createEnv({}, {
46+
prefix: "PUBLIC_",
47+
runtimeEnv: process.env,
48+
emptyStringAsUndefined: true,
49+
}) as WebEnv;
50+
51+
export const server = createEnv(serverShape, {
52+
extends: [shared],
53+
runtimeEnv: process.env,
54+
emptyStringAsUndefined: true,
55+
}) as ServerEnv;

‎apps/cloud/src/services/db.ts‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { resolve } from "node:path";
77
import * as sharedSchema from "@executor/storage-postgres/schema";
88
import * as cloudSchema from "./schema";
99
import type { DrizzleDb } from "@executor/storage-postgres";
10+
import { server } from "../env";
1011

1112
const schema = { ...sharedSchema, ...cloudSchema };
1213

@@ -23,11 +24,11 @@ type DbResource = {
2324
};
2425

2526
const createDbResource = async (): Promise<DbResource> => {
26-
if (process.env.DATABASE_URL) {
27+
if (server.DATABASE_URL) {
2728
const { drizzle } = await import("drizzle-orm/node-postgres");
2829
const { migrate } = await import("drizzle-orm/node-postgres/migrator");
2930
const { Pool } = await import("pg");
30-
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
31+
const pool = new Pool({ connectionString: server.DATABASE_URL });
3132
const db = drizzle(pool, { schema }) as DrizzleDb;
3233
await migrate(db as any, { migrationsFolder: MIGRATIONS_DIR });
3334
return {
@@ -39,7 +40,7 @@ const createDbResource = async (): Promise<DbResource> => {
3940
const { PGlite } = await import("@electric-sql/pglite");
4041
const { drizzle } = await import("drizzle-orm/pglite");
4142
const { migrate } = await import("drizzle-orm/pglite/migrator");
42-
const dataDir = process.env.PGLITE_DATA_DIR ?? ".pglite";
43+
const dataDir = server.PGLITE_DATA_DIR;
4344
const client = new PGlite(dataDir);
4445
const db = drizzle(client, { schema }) as DrizzleDb;
4546
await migrate(db, { migrationsFolder: MIGRATIONS_DIR });

‎apps/cloud/src/web/auth.tsx‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ type AuthTeam = {
2323
// Auth atom — typed query against CloudAuthApi
2424
// ---------------------------------------------------------------------------
2525

26-
export const authAtom = CloudApiClient.query("cloudAuth", "me", {
27-
timeToLive: "5 minutes",
28-
});
26+
export const authAtom =
27+
CloudApiClient.query("cloudAuth", "me", {
28+
timeToLive: "5 minutes",
29+
});
2930

3031
// ---------------------------------------------------------------------------
3132
// Provider + hook

‎apps/cloud/tsconfig.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
"skipLibCheck": true,
99
"outDir": "dist",
1010
"rootDir": ".",
11-
"declaration": true,
12-
"declarationMap": true,
11+
"declaration": false,
12+
"declarationMap": false,
1313
"sourceMap": true,
1414
"jsx": "react-jsx",
1515
"plugins": [

‎apps/cloud/vite.config.ts‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,24 @@ import { defineConfig } from "vite";
22
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
33
import react from "@vitejs/plugin-react";
44
import tailwindcss from "@tailwindcss/vite";
5+
import { createEnv, Env } from "@executor/env";
6+
7+
const server = {
8+
PORT: Env.numberOr("PORT", 5173),
9+
};
10+
11+
type ViteEnv = Readonly<{
12+
PORT: number;
13+
}>;
14+
15+
const viteEnv = createEnv(server, {
16+
runtimeEnv: process.env,
17+
emptyStringAsUndefined: true,
18+
}) as ViteEnv;
519

620
export default defineConfig({
721
server: {
8-
port: parseInt(process.env.PORT ?? "5173", 10),
22+
port: viteEnv.PORT,
923
host: "127.0.0.1",
1024
},
1125
resolve: { tsconfigPaths: true },

‎bun.lock‎

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)