fix(coolify): strip is_build_time from env writes; add reveal + GCS
Coolify v4's POST/PATCH /applications/{uuid}/envs only accepts key,
value, is_preview, is_literal, is_multiline, is_shown_once. Sending
is_build_time triggers a 422 "This field is not allowed." — it's now
a derived read-only flag (is_buildtime) computed from Dockerfile ARG
usage. Breaks agents trying to upsert env vars.
Three-layer fix so this can't regress:
- lib/coolify.ts: COOLIFY_ENV_WRITE_FIELDS whitelist enforced at the
network boundary, regardless of caller shape
- app/api/workspaces/[slug]/apps/[uuid]/envs: stops forwarding the
field; returns a deprecation warning when callers send it; GET
reads both is_buildtime and is_build_time for version parity
- app/api/mcp/route.ts: same treatment in the MCP dispatcher;
AI_CAPABILITIES.md doc corrected
Also bundles (not related to the above):
- Workspace API keys are now revealable from settings. New
key_encrypted column stores AES-256-GCM(VIBN_SECRETS_KEY, token).
POST /api/workspaces/[slug]/keys/[keyId]/reveal returns plaintext
for session principals only; API-key principals cannot reveal
siblings. Legacy keys stay valid for auth but can't reveal.
- P5.3 Object storage: lib/gcp/storage.ts + lib/workspace-gcs.ts
idempotently provision a per-workspace GCS bucket, service
account, IAM binding and HMAC key. New POST /api/workspaces/
[slug]/storage/buckets endpoint. Migration script + smoke test
included. Proven end-to-end against prod master-ai-484822.
Made-with: Cursor
This commit is contained in:
@@ -61,15 +61,68 @@ export interface CoolifyApplication {
|
||||
environment?: { id?: number; project_uuid?: string; project?: { uuid?: string } };
|
||||
}
|
||||
|
||||
/**
|
||||
* Coolify env var, as returned by GET /applications/{uuid}/envs.
|
||||
*
|
||||
* NOTE on build-time vars: Coolify removed `is_build_time` from the
|
||||
* **write** schema some time ago. The flag is now a derived read-only
|
||||
* attribute (`is_buildtime`, one word) computed from whether the var
|
||||
* is referenced as a Dockerfile ARG. `is_build_time` (underscored) is
|
||||
* kept here only to tolerate very old read responses — never send it
|
||||
* on POST/PATCH. See `COOLIFY_ENV_WRITE_FIELDS` below.
|
||||
*/
|
||||
export interface CoolifyEnvVar {
|
||||
uuid?: string;
|
||||
key: string;
|
||||
value: string;
|
||||
is_preview?: boolean;
|
||||
/** @deprecated read-only, derived server-side. Do not send on write. */
|
||||
is_build_time?: boolean;
|
||||
/** Newer one-word spelling of the same derived read-only flag. */
|
||||
is_buildtime?: boolean;
|
||||
is_runtime?: boolean;
|
||||
is_literal?: boolean;
|
||||
is_multiline?: boolean;
|
||||
is_shown_once?: boolean;
|
||||
is_shared?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* The only fields Coolify v4 accepts on POST/PATCH /applications/{uuid}/envs.
|
||||
* Any other field (notably `is_build_time`) triggers a 422
|
||||
* "This field is not allowed." Build-time vs runtime is no longer a
|
||||
* writable flag — Coolify infers it at build time.
|
||||
*
|
||||
* Source of truth:
|
||||
* https://coolify.io/docs/api-reference/api/operations/update-env-by-application-uuid
|
||||
* https://coolify.io/docs/api-reference/api/operations/create-env-by-application-uuid
|
||||
*/
|
||||
const COOLIFY_ENV_WRITE_FIELDS = [
|
||||
'key',
|
||||
'value',
|
||||
'is_preview',
|
||||
'is_literal',
|
||||
'is_multiline',
|
||||
'is_shown_once',
|
||||
] as const;
|
||||
|
||||
type CoolifyEnvWritePayload = {
|
||||
key: string;
|
||||
value: string;
|
||||
is_preview?: boolean;
|
||||
is_literal?: boolean;
|
||||
is_multiline?: boolean;
|
||||
is_shown_once?: boolean;
|
||||
};
|
||||
|
||||
function toCoolifyEnvWritePayload(env: CoolifyEnvVar): CoolifyEnvWritePayload {
|
||||
const src = env as unknown as Record<string, unknown>;
|
||||
const out: Record<string, unknown> = {};
|
||||
for (const k of COOLIFY_ENV_WRITE_FIELDS) {
|
||||
const v = src[k];
|
||||
if (v !== undefined) out[k] = v;
|
||||
}
|
||||
return out as CoolifyEnvWritePayload;
|
||||
}
|
||||
|
||||
export interface CoolifyPrivateKey {
|
||||
@@ -539,17 +592,22 @@ export async function upsertApplicationEnv(
|
||||
uuid: string,
|
||||
env: CoolifyEnvVar & { is_preview?: boolean }
|
||||
): Promise<CoolifyEnvVar> {
|
||||
// Strip any read-only/derived fields (`is_build_time`, `is_buildtime`,
|
||||
// `is_runtime`, `is_shared`, `uuid`) before sending — Coolify returns
|
||||
// 422 "This field is not allowed." for anything outside the write
|
||||
// schema. See COOLIFY_ENV_WRITE_FIELDS.
|
||||
const payload = toCoolifyEnvWritePayload(env);
|
||||
try {
|
||||
return await coolifyFetch(`/applications/${uuid}/envs`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(env),
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
if (msg.includes('404') || msg.includes('405')) {
|
||||
return coolifyFetch(`/applications/${uuid}/envs`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(env),
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
}
|
||||
throw err;
|
||||
|
||||
Reference in New Issue
Block a user