diff --git a/Dockerfile b/Dockerfile index ad33bd9..36ad71c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,14 +15,15 @@ WORKDIR /app ENV DENO_DIR=/deno-dir # Layer cache: Pre-download dependencies (only invalidates if deps change) -COPY deno.json deno.lock* ./ -COPY auth-yes/deno.json ./auth-yes/ -COPY auth-yes/deps.ts ./auth-yes/ -RUN deno cache auth-yes/deps.ts +COPY deno.json deno.lock* deps.ts ./ +COPY server/deno.json ./server/ +COPY sdk/deno.json ./sdk/ +COPY ui/deno.json ./ui/ +RUN deno cache deps.ts # Source code layer COPY . . -RUN deno cache auth-yes/server/main.ts +RUN deno cache server/main.ts # Stage 3: Runner FROM denoland/deno:debian-2.9.4 @@ -36,10 +37,9 @@ ENV DENO_DIR=/deno-dir # Copy the Rust library COPY --from=rust-builder --chown=deno:deno /usr/src/app/spire_ffi/target/release/libspire_ffi.so /app/libspire_ffi.so -COPY --from=deno-builder --chown=deno:deno /app/deno.json /app/deno.lock ./ -COPY --from=deno-builder --chown=deno:deno /app/auth-yes/ ./auth-yes/ +COPY --from=deno-builder --chown=deno:deno /app ./ EXPOSE 8000 # Added --allow-ffi for Deno.dlopen and --allow-read for dlopen path resolution -CMD ["run", "--allow-net", "--allow-env", "--allow-ffi=./libspire_ffi.so", "--allow-read=.,/var/run/spire/agent.sock", "--unstable-ffi", "auth-yes/server/main.ts"] +CMD ["run", "--allow-net", "--allow-env", "--allow-ffi=./libspire_ffi.so", "--allow-read=.,/var/run/spire/agent.sock", "--unstable-ffi", "server/main.ts"] diff --git a/infra/setup.ts b/infra/setup.ts index 491e685..05542c5 100644 --- a/infra/setup.ts +++ b/infra/setup.ts @@ -78,7 +78,7 @@ export function generateDockerCompose(): string { services: auth-api: image: \${REG}/library/auth-yes-api:latest - env_file: stack.env + env_file: .env labels: - "traefik.enable=true" - "traefik.docker.network=traefik-net" @@ -104,7 +104,7 @@ services: - POSTGRES_PASSWORD=\${POSTGRES_PASSWORD} - POSTGRES_DB=\${POSTGRES_DB} volumes: - - auth-db-data:/var/lib/postgresql + - auth-db-data:/var/lib/postgresql/data networks: - default @@ -180,7 +180,7 @@ export function generateProtobufCompilationCommands(): string[] { "generate", "server/auth.proto", "--template", - '{"version":"v1","plugins":[{"plugin":"buf.build/bufbuild/es:v1.10.0","out":"server/gen","opt":"target=ts,import_extension=.ts"},{"plugin":"buf.build/connectrpc/es:v1.4.0","out":"server/gen","opt":"target=ts,import_extension=.ts"}]}', + '{"version":"v1","plugins":[{"plugin":"buf.build/bufbuild/es:v1.10.0","out":"sdk/gen","opt":"target=ts,import_extension=.ts"},{"plugin":"buf.build/connectrpc/es:v1.4.0","out":"sdk/gen","opt":"target=ts,import_extension=.ts"}]}', ]; } @@ -208,14 +208,14 @@ export async function downloadWorkloadProto(): Promise { throw new Error(`Failed to download workload.proto: ${res.statusText}`); } const text = await res.text(); - await Deno.mkdir("../spire_ffi/proto", { recursive: true }); - await Deno.writeTextFile("../spire_ffi/proto/workload.proto", text); + await Deno.mkdir("spire_ffi/proto", { recursive: true }); + await Deno.writeTextFile("spire_ffi/proto/workload.proto", text); console.log(colors.green("✓ workload.proto downloaded successfully.")); } export function generateBuildCommands(reg: string): string[] { return [ - `podman build -t ${reg}/library/auth-yes-api:latest -f Dockerfile ..`, + `podman build -t ${reg}/library/auth-yes-api:latest -f Dockerfile .`, `podman push ${reg}/library/auth-yes-api:latest`, ]; } diff --git a/server/db.ts b/server/db.ts index 961ec0b..539d94e 100644 --- a/server/db.ts +++ b/server/db.ts @@ -203,6 +203,35 @@ export async function initDb(): Promise { ON CONFLICT (spiffe_id) DO NOTHING `; + // Seed initial bootstrap admin invite if no users exist in the database + try { + const userCount = await sql`SELECT count(*)::int as count FROM users`.then( + (res) => res[0]?.count || 0, + ); + if (userCount === 0) { + const inviteCount = await sql` + SELECT count(*)::int as count FROM invites + WHERE role = 'admin' AND expires_at > NOW() + `.then((res) => res[0]?.count || 0); + + if (inviteCount === 0) { + const bootstrapCode = Deno.env.get("BOOTSTRAP_INVITE_CODE") || + "bootstrap-admin"; + const expiresAt = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000); // 30 days + await sql` + INSERT INTO invites (code, app_id, role, max_uses, uses_count, auto_activate, expires_at) + VALUES (${bootstrapCode}, NULL, 'admin', 1, 0, TRUE, ${expiresAt}) + ON CONFLICT (code) DO NOTHING + `; + console.log( + `[Auth DB] Initial bootstrap admin invite code seeded: '${bootstrapCode}'`, + ); + } + } + } catch (err) { + console.warn("[Auth DB] Bootstrap invite check skipped:", err); + } + console.log("[Auth DB] Central identity database schema initialized."); }