Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion ee/apps/den-api/src/db.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { createDenDb } from "@openwork-ee/den-db"
import { env } from "./env.js"

export const { db } = createDenDb({
export const denDb = createDenDb({
databaseUrl: env.databaseUrl,
mode: env.dbMode,
planetscale: env.planetscale,
})
export const { client: dbClient, db } = denDb
159 changes: 158 additions & 1 deletion ee/apps/den-api/src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,16 @@ const EnvSchema = z.object({
PORT: z.string().optional(),
CORS_ORIGINS: z.string().optional(),
WORKER_PROXY_PORT: z.string().optional(),
PROVISIONER_MODE: z.enum(["stub", "render", "daytona"]).optional(),
PROVISIONER_MODE: z.enum(["stub", "render", "daytona", "static"]).optional(),
WORKER_URL_TEMPLATE: z.string().optional(),
STATIC_WORKER_URLS: z.string().optional(),
STATIC_WORKER_HEALTH_PATH: z.string().optional(),
STATIC_WORKER_HEALTHCHECK_TIMEOUT_MS: z.string().optional(),
STATIC_WORKER_HEALTHCHECK_INTERVAL_MS: z.string().optional(),
STATIC_WORKER_RESERVATION_TTL_MS: z.string().optional(),
STATIC_WORKER_ATTACH_ALLOW_PRIVATE: z.string().optional(),
STATIC_WORKER_ATTACH_ALLOWED_HOSTS: z.string().optional(),
STATIC_WORKER_ATTACH_ALLOWED_CIDRS: z.string().optional(),
WORKER_ACTIVITY_BASE_URL: z.string().optional(),
OPENWORK_DAYTONA_ENV_PATH: z.string().optional(),
RENDER_API_BASE: z.string().optional(),
Expand Down Expand Up @@ -121,6 +129,17 @@ const EnvSchema = z.object({
}
}
}

if (value.PROVISIONER_MODE === "static") {
const staticConfig = parseStaticWorkersEnv(value)
for (const issue of staticConfig.issues) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: issue.message,
path: [issue.path],
})
}
}
})

const parsed = EnvSchema.parse(process.env)
Expand All @@ -145,6 +164,133 @@ function normalizeOrigin(origin: string) {
return value.replace(/\/+$/, "")
}

type StaticWorkersEnvInput = {
STATIC_WORKER_URLS?: string
STATIC_WORKER_HEALTH_PATH?: string
STATIC_WORKER_HEALTHCHECK_TIMEOUT_MS?: string
STATIC_WORKER_HEALTHCHECK_INTERVAL_MS?: string
STATIC_WORKER_RESERVATION_TTL_MS?: string
STATIC_WORKER_ATTACH_ALLOW_PRIVATE?: string
STATIC_WORKER_ATTACH_ALLOWED_HOSTS?: string
STATIC_WORKER_ATTACH_ALLOWED_CIDRS?: string
}

type StaticWorkersEnvIssue = {
path: keyof StaticWorkersEnvInput
message: string
}

function parsePositiveInteger(value: string | undefined, fallback: number) {
const raw = value?.trim()
if (!raw) {
return fallback
}
const parsedValue = Number(raw)
return Number.isInteger(parsedValue) && parsedValue > 0 ? parsedValue : null
}

function normalizeStaticWorkerUrl(value: string) {
const parsedUrl = new URL(value.trim())
parsedUrl.hash = ""
parsedUrl.search = ""
parsedUrl.pathname = parsedUrl.pathname.replace(/\/+$/, "")
const serialized = parsedUrl.toString().replace(/\/+$/, "")
return serialized
}

export function parseStaticWorkersEnv(input: StaticWorkersEnvInput) {
const issues: StaticWorkersEnvIssue[] = []
const urls: string[] = []
const seenUrls = new Set<string>()

for (const rawUrl of splitCsv(input.STATIC_WORKER_URLS)) {
let normalizedUrl: string
try {
const parsedUrl = new URL(rawUrl)
if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
issues.push({
path: "STATIC_WORKER_URLS",
message: "STATIC_WORKER_URLS entries must use http or https URLs",
})
continue
}
normalizedUrl = normalizeStaticWorkerUrl(rawUrl)
} catch {
issues.push({
path: "STATIC_WORKER_URLS",
message: "STATIC_WORKER_URLS entries must be valid URLs",
})
continue
}

if (seenUrls.has(normalizedUrl)) {
issues.push({
path: "STATIC_WORKER_URLS",
message: `STATIC_WORKER_URLS contains duplicate URL ${normalizedUrl}`,
})
continue
}

seenUrls.add(normalizedUrl)
urls.push(normalizedUrl)
}

if (urls.length === 0) {
issues.push({
path: "STATIC_WORKER_URLS",
message: "STATIC_WORKER_URLS is required when PROVISIONER_MODE=static",
})
}

const healthPath = optionalString(input.STATIC_WORKER_HEALTH_PATH) ?? "/health"
if (!healthPath.startsWith("/") || healthPath.startsWith("//") || healthPath.includes("?")) {
issues.push({
path: "STATIC_WORKER_HEALTH_PATH",
message: "STATIC_WORKER_HEALTH_PATH must be an absolute path such as /health",
})
}

const healthcheckTimeoutMs = parsePositiveInteger(input.STATIC_WORKER_HEALTHCHECK_TIMEOUT_MS, 10000)
if (healthcheckTimeoutMs === null) {
issues.push({
path: "STATIC_WORKER_HEALTHCHECK_TIMEOUT_MS",
message: "STATIC_WORKER_HEALTHCHECK_TIMEOUT_MS must be a positive integer",
})
}

const healthcheckIntervalMs = parsePositiveInteger(input.STATIC_WORKER_HEALTHCHECK_INTERVAL_MS, 1000)
if (healthcheckIntervalMs === null) {
issues.push({
path: "STATIC_WORKER_HEALTHCHECK_INTERVAL_MS",
message: "STATIC_WORKER_HEALTHCHECK_INTERVAL_MS must be a positive integer",
})
}

const reservationTtlMs = parsePositiveInteger(input.STATIC_WORKER_RESERVATION_TTL_MS, 300000)
if (reservationTtlMs === null) {
issues.push({
path: "STATIC_WORKER_RESERVATION_TTL_MS",
message: "STATIC_WORKER_RESERVATION_TTL_MS must be a positive integer",
})
}

const allowPrivateAttach = (input.STATIC_WORKER_ATTACH_ALLOW_PRIVATE ?? "false").trim().toLowerCase() === "true"
const attachAllowedHosts = splitCsv(input.STATIC_WORKER_ATTACH_ALLOWED_HOSTS).map((host) => host.toLowerCase())
const attachAllowedCidrs = splitCsv(input.STATIC_WORKER_ATTACH_ALLOWED_CIDRS)

return {
urls,
healthPath,
healthcheckTimeoutMs: healthcheckTimeoutMs ?? 10000,
healthcheckIntervalMs: healthcheckIntervalMs ?? 1000,
reservationTtlMs: reservationTtlMs ?? 300000,
allowPrivateAttach,
attachAllowedHosts,
attachAllowedCidrs,
issues,
}
}

const corsOrigins = splitCsv(parsed.CORS_ORIGINS).map((origin) => normalizeOrigin(origin))
const betterAuthTrustedOrigins = splitCsv(parsed.DEN_BETTER_AUTH_TRUSTED_ORIGINS)
.map((origin) => normalizeOrigin(origin))
Expand All @@ -154,6 +300,7 @@ const polarFeatureGateEnabled =

const devMode = (parsed.OPENWORK_DEV_MODE ?? "0").trim() === "1"
const port = Number(parsed.PORT ?? "8790")
const staticWorkers = parseStaticWorkersEnv(parsed)

const daytonaSandboxPublic =
(parsed.DAYTONA_SANDBOX_PUBLIC ?? "false").toLowerCase() === "true"
Expand Down Expand Up @@ -217,6 +364,16 @@ export const env = {
corsOrigins,
provisionerMode: parsed.PROVISIONER_MODE ?? "daytona",
workerUrlTemplate: parsed.WORKER_URL_TEMPLATE,
staticWorkers: {
urls: staticWorkers.urls,
healthPath: staticWorkers.healthPath,
healthcheckTimeoutMs: staticWorkers.healthcheckTimeoutMs,
healthcheckIntervalMs: staticWorkers.healthcheckIntervalMs,
reservationTtlMs: staticWorkers.reservationTtlMs,
allowPrivateAttach: staticWorkers.allowPrivateAttach,
attachAllowedHosts: staticWorkers.attachAllowedHosts,
attachAllowedCidrs: staticWorkers.attachAllowedCidrs,
},
workerActivityBaseUrl:
optionalString(parsed.WORKER_ACTIVITY_BASE_URL) ??
parsed.BETTER_AUTH_URL.trim().replace(/\/+$/, ""),
Expand Down
Loading