Fix apps.list: filter compose services by environment_id, not non-existent project field
Coolify's /api/v1/services response does not include a `project` field. Services belong to environments and environments belong to projects. The old filter checked s.project.uuid (always undefined) and silently dropped every service from the result, so compose-stack apps like Twenty CRM never showed up in apps.list. Now we resolve the project's environment IDs via getProject() and filter services where environment_id is in that set. Also surface the public service's fqdn in the response (extracted from s.applications) so the AI can immediately tell the user where the app lives. Made-with: Cursor
This commit is contained in:
@@ -43,6 +43,7 @@ import {
|
||||
listApplicationDeployments,
|
||||
listApplicationEnvs,
|
||||
listApplicationsInProject,
|
||||
getProject,
|
||||
projectUuidOf,
|
||||
TenantError,
|
||||
upsertApplicationEnv,
|
||||
@@ -418,18 +419,28 @@ async function toolAppsList(principal: Principal) {
|
||||
// Fetch Applications and Services in parallel.
|
||||
// Services are compose stacks created via the composeRaw pathway;
|
||||
// they live at /services not /applications.
|
||||
const [apps, allServices] = await Promise.allSettled([
|
||||
// Coolify's /services response does NOT include a `project` field — services
|
||||
// belong to environments, and environments belong to projects. So we resolve
|
||||
// the project's environment IDs first, then filter services by environment_id.
|
||||
const [apps, allServices, project] = await Promise.allSettled([
|
||||
listApplicationsInProject(projectUuid),
|
||||
listAllServices(),
|
||||
getProject(projectUuid),
|
||||
]);
|
||||
|
||||
const appList = apps.status === 'fulfilled' ? apps.value : [];
|
||||
const projectEnvIds = new Set<number>(
|
||||
project.status === 'fulfilled'
|
||||
? (project.value.environments ?? []).map((e) => e.id)
|
||||
: [],
|
||||
);
|
||||
|
||||
const serviceList = (allServices.status === 'fulfilled' && Array.isArray(allServices.value)
|
||||
? (allServices.value as Array<Record<string, unknown>>)
|
||||
: []
|
||||
).filter(s => {
|
||||
const proj = s.project as Record<string, unknown> | undefined;
|
||||
return proj?.uuid === projectUuid;
|
||||
).filter((s) => {
|
||||
const envId = typeof s.environment_id === 'number' ? s.environment_id : Number(s.environment_id);
|
||||
return projectEnvIds.has(envId);
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
@@ -443,15 +454,21 @@ async function toolAppsList(principal: Principal) {
|
||||
gitBranch: a.git_branch ?? null,
|
||||
resourceType: 'application',
|
||||
})),
|
||||
...serviceList.map(s => ({
|
||||
uuid: String(s.uuid),
|
||||
name: String(s.name ?? ''),
|
||||
status: String(s.status ?? 'unknown'),
|
||||
fqdn: null,
|
||||
gitRepository: null,
|
||||
gitBranch: null,
|
||||
resourceType: 'service',
|
||||
})),
|
||||
...serviceList.map((s) => {
|
||||
// Try to extract a usable URL from the service's applications array
|
||||
// (compose stacks expose their public service's fqdn there).
|
||||
const apps = (s.applications as Array<Record<string, unknown>>) || [];
|
||||
const publicApp = apps.find((a) => a.fqdn);
|
||||
return {
|
||||
uuid: String(s.uuid),
|
||||
name: String(s.name ?? ''),
|
||||
status: String(s.status ?? 'unknown'),
|
||||
fqdn: publicApp?.fqdn ? String(publicApp.fqdn) : null,
|
||||
gitRepository: null,
|
||||
gitBranch: null,
|
||||
resourceType: 'service',
|
||||
};
|
||||
}),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user