fix(coolify): list project databases across per-flavor arrays

GET /projects/{uuid}/{envName} returns databases split into
postgresqls/mysqls/mariadbs/mongodbs/redis/keydbs/dragonflies/clickhouses
sibling arrays instead of a unified `databases` list. Combine all of
them in listDatabasesInProject. Also normalize setApplicationDomains
to prepend https:// on bare hostnames (Coolify validates as URL).

Made-with: Cursor
This commit is contained in:
2026-04-21 12:30:36 -07:00
parent b1670c7035
commit 62c52747f5

View File

@@ -402,8 +402,14 @@ export async function setApplicationDomains(
domains: string[], domains: string[],
opts: { forceOverride?: boolean } = {} opts: { forceOverride?: boolean } = {}
): Promise<{ uuid: string }> { ): Promise<{ uuid: string }> {
// Coolify validates each entry as a URL, so bare hostnames need a scheme.
const normalized = domains.map(d => {
const trimmed = d.trim();
if (/^https?:\/\//i.test(trimmed)) return trimmed;
return `https://${trimmed}`;
});
return updateApplication(uuid, { return updateApplication(uuid, {
domains: domains.join(','), domains: normalized.join(','),
force_domain_override: opts.forceOverride ?? true, force_domain_override: opts.forceOverride ?? true,
is_force_https_enabled: true, is_force_https_enabled: true,
}); });
@@ -710,16 +716,37 @@ export async function getServiceInProject(
return svc; return svc;
} }
/** Response shape of GET /projects/{uuid}/{envName}. */ /**
* Response shape of GET /projects/{uuid}/{envName}.
* Coolify splits databases by flavor across sibling arrays.
*/
interface CoolifyProjectEnvResources { interface CoolifyProjectEnvResources {
id: number; id: number;
uuid: string; uuid: string;
name: string; name: string;
applications?: CoolifyApplication[]; applications?: CoolifyApplication[];
databases?: CoolifyDatabase[];
services?: CoolifyService[]; services?: CoolifyService[];
postgresqls?: CoolifyDatabase[];
mysqls?: CoolifyDatabase[];
mariadbs?: CoolifyDatabase[];
mongodbs?: CoolifyDatabase[];
redis?: CoolifyDatabase[];
keydbs?: CoolifyDatabase[];
dragonflies?: CoolifyDatabase[];
clickhouses?: CoolifyDatabase[];
} }
const DB_ARRAY_KEYS: Array<keyof CoolifyProjectEnvResources> = [
'postgresqls',
'mysqls',
'mariadbs',
'mongodbs',
'redis',
'keydbs',
'dragonflies',
'clickhouses',
];
async function getProjectEnvResources( async function getProjectEnvResources(
projectUuid: string, projectUuid: string,
envName: string envName: string
@@ -727,22 +754,15 @@ async function getProjectEnvResources(
return coolifyFetch(`/projects/${projectUuid}/${encodeURIComponent(envName)}`); return coolifyFetch(`/projects/${projectUuid}/${encodeURIComponent(envName)}`);
} }
/** async function forEachEnv<T>(
* List all apps/dbs/services across all environments of a project.
* Uses one `/projects/{uuid}` call + one call per environment.
*/
async function listResourcesInProject<K extends 'applications' | 'databases' | 'services'>(
projectUuid: string, projectUuid: string,
key: K collect: (envResources: CoolifyProjectEnvResources) => T[]
): Promise<NonNullable<CoolifyProjectEnvResources[K]>> { ): Promise<T[]> {
const project = await getProject(projectUuid); const project = await getProject(projectUuid);
const out: NonNullable<CoolifyProjectEnvResources[K]> = [] as never; const out: T[] = [];
for (const env of project.environments ?? []) { for (const env of project.environments ?? []) {
const envResources = await getProjectEnvResources(projectUuid, env.name); const envResources = await getProjectEnvResources(projectUuid, env.name);
const list = envResources[key]; out.push(...collect(envResources));
if (Array.isArray(list)) {
(out as unknown[]).push(...list);
}
} }
return out; return out;
} }
@@ -750,19 +770,26 @@ async function listResourcesInProject<K extends 'applications' | 'databases' | '
export async function listApplicationsInProject( export async function listApplicationsInProject(
projectUuid: string projectUuid: string
): Promise<CoolifyApplication[]> { ): Promise<CoolifyApplication[]> {
return listResourcesInProject(projectUuid, 'applications'); return forEachEnv(projectUuid, r => r.applications ?? []);
} }
export async function listDatabasesInProject( export async function listDatabasesInProject(
projectUuid: string projectUuid: string
): Promise<CoolifyDatabase[]> { ): Promise<CoolifyDatabase[]> {
return listResourcesInProject(projectUuid, 'databases'); return forEachEnv(projectUuid, r => {
const out: CoolifyDatabase[] = [];
for (const k of DB_ARRAY_KEYS) {
const arr = r[k];
if (Array.isArray(arr)) out.push(...(arr as CoolifyDatabase[]));
}
return out;
});
} }
export async function listServicesInProject( export async function listServicesInProject(
projectUuid: string projectUuid: string
): Promise<CoolifyService[]> { ): Promise<CoolifyService[]> {
return listResourcesInProject(projectUuid, 'services'); return forEachEnv(projectUuid, r => r.services ?? []);
} }
/** @deprecated Use getApplicationInProject / ensureResourceInProject instead. */ /** @deprecated Use getApplicationInProject / ensureResourceInProject instead. */