diff --git a/.env.example b/.env.example index 731ce225..72ca412a 100644 --- a/.env.example +++ b/.env.example @@ -106,6 +106,23 @@ CODE_OUTPUT_DIR=generated-code # Use a URL-encoded password if it contains @ (e.g. p%40ssword for p@ssword). # BLUEPRINT_GENERATED_DATABASE_URL=postgresql://user:pass@localhost:5432/dbname +# --- Infra auto-provisioning (kickoff) --- +# Postgres/Redis: one Dokploy container per generated app. Set both to enable. +# DOKPLOY_URL=https://dokploy.example.com +# DOKPLOY_TOKEN= +# +# S3 object storage: ONE shared bucket for all generated apps; each app gets an +# isolated folder (key prefix = app slug). Set BLUEPRINT_S3_BUCKET to enable; +# the kickoff "service detector" then auto-wires AWS_S3_* into the app's .env +# whenever the PRD/TRD implies file/image/media upload. Works with AWS S3 or any +# S3-compatible store (MinIO, Cloudflare R2) via the optional endpoint vars. +# BLUEPRINT_S3_BUCKET=my-shared-test-bucket +# BLUEPRINT_S3_ACCESS_KEY_ID= +# BLUEPRINT_S3_SECRET_ACCESS_KEY= +# BLUEPRINT_S3_REGION=us-east-1 +# BLUEPRINT_S3_ENDPOINT= # e.g. https://.r2.cloudflarestorage.com (S3-compatible only) +# BLUEPRINT_S3_FORCE_PATH_STYLE= # set to 1 for MinIO / path-style addressing + # --- Project kick-off: Git / Jira (optional; webhooks OR direct API) --- # If a webhook URL is set, it is used instead of the direct API for that integration. # diff --git a/src/app/api/agents/coding/route.ts b/src/app/api/agents/coding/route.ts index 339c8a50..48a4117e 100644 --- a/src/app/api/agents/coding/route.ts +++ b/src/app/api/agents/coding/route.ts @@ -35,6 +35,7 @@ import { resolveBackendPort, upsertBackendPrivyAppIdMirror, resolvePrivyAppIdMirrorFromFilledResources, + upsertEnvVars, } from "@/lib/pipeline/generated-code-env"; import { normalizeProjectTier, @@ -48,6 +49,7 @@ import { readKickoffInfraMetadata, databaseUrlFrom, redisUrlFrom, + s3EnvFrom, } from "@/lib/pipeline/kickoff-infra"; import { readResourceRequirements, @@ -1401,7 +1403,10 @@ export async function POST(request: NextRequest) { const withRedisUrl = redisUrlForEnv ? upsertRedisUrlEnv(withDbUrl, redisUrlForEnv) : withDbUrl; - const withJwt = upsertJwtEnvVars(withRedisUrl); + // S3 credential bundle (shared bucket + per-app folder) from kickoff infra. + const s3Env = s3EnvFrom(kickoffInfra); + const withS3 = s3Env ? upsertEnvVars(withRedisUrl, s3Env) : withRedisUrl; + const withJwt = upsertJwtEnvVars(withS3); const withPort = upsertBackendPortEnv(withJwt); const withResources = upsertResourceEnvVars(withPort, backendResources); const privyMirror = diff --git a/src/components/kickoff/InfraSection.tsx b/src/components/kickoff/InfraSection.tsx index 9b7d67ec..d5bf06bb 100644 --- a/src/components/kickoff/InfraSection.tsx +++ b/src/components/kickoff/InfraSection.tsx @@ -3,7 +3,7 @@ import { useCallback, useEffect, useState } from "react"; export interface InfraServiceMeta { - kind: "postgres" | "redis"; + kind: "postgres" | "redis" | "s3"; appName: string; externalPort: number; publicUrl: string; @@ -27,11 +27,13 @@ interface Props { const KIND_ICON: Record = { postgres: "🐘", redis: "🟥", + s3: "🪣", }; const KIND_LABEL: Record = { postgres: "Postgres", redis: "Redis", + s3: "S3", }; type PingStatus = @@ -72,9 +74,12 @@ export default function InfraSection({ infra, dokployBaseUrl }: Props) { setPings((p) => ({ ...p, [key]: result })); }, []); - // Initial probe — fires once per service when the panel mounts. + // Initial probe — fires once per service when the panel mounts. S3 is an + // external bucket reached over HTTPS with credentials, not a pingable TCP + // endpoint, so we skip the reachability probe for it. useEffect(() => { for (const svc of services) { + if (svc.kind === "s3") continue; const key = `${svc.kind}-${svc.appName}`; runPing(key, svc.publicUrl); } @@ -156,13 +161,17 @@ export default function InfraSection({ infra, dokployBaseUrl }: Props) { {KIND_LABEL[svc.kind]} - - :{svc.externalPort} - - runPing(key, svc.publicUrl)} - /> + {svc.kind !== "s3" && ( + + :{svc.externalPort} + + )} + {svc.kind !== "s3" && ( + runPing(key, svc.publicUrl)} + /> + )}