projects.get: auto-enrich with possibleDeployments[] from fuzzy app/service name match
Project records and Coolify deployments live in separate worlds — nothing writes the Coolify UUID back into fs_projects.data on deploy. Now projects.get also scans apps + services in the workspace and returns any whose name fuzzy-matches the project (lowercased token overlap), plus any explicitly-linked one. Self-healing forever; the AI can immediately tell the user what's running for a project even when the link was never stored. Made-with: Cursor
This commit is contained in:
@@ -382,19 +382,94 @@ async function toolProjectsGet(principal: Principal, params: Record<string, any>
|
||||
}
|
||||
const r = rows[0];
|
||||
const d = r.data || {};
|
||||
// Return a clean summary rather than the raw JSONB, which contains noisy
|
||||
// internal scaffold fields (product/website/admin/storybook sub-app configs)
|
||||
// that the AI tends to misread as live deployed services.
|
||||
const projectName = d.productName || d.name || d.title || 'Untitled';
|
||||
|
||||
// Auto-enrich: if no Coolify link is stored yet, scan apps + services in
|
||||
// the workspace and surface any whose name fuzzy-matches the project. Lets
|
||||
// the AI tell the user "this is probably your deployment" even when the
|
||||
// backend never wrote the link.
|
||||
let possibleDeployments: Array<{
|
||||
uuid: string;
|
||||
name: string;
|
||||
status: string;
|
||||
fqdn: string | null;
|
||||
resourceType: 'application' | 'service';
|
||||
}> = [];
|
||||
|
||||
const linkedUuid = d.coolifyAppUuid || d.coolifyServiceUuid || null;
|
||||
const projectUuid = principal.workspace.coolify_project_uuid;
|
||||
if (projectUuid) {
|
||||
try {
|
||||
const [appsRes, servicesRes, projectRes] = await Promise.allSettled([
|
||||
listApplicationsInProject(projectUuid),
|
||||
listAllServices(),
|
||||
getProject(projectUuid),
|
||||
]);
|
||||
const envIds = new Set<number>(
|
||||
projectRes.status === 'fulfilled'
|
||||
? (projectRes.value.environments ?? []).map((e) => e.id)
|
||||
: [],
|
||||
);
|
||||
const apps = appsRes.status === 'fulfilled' ? appsRes.value : [];
|
||||
const services = (servicesRes.status === 'fulfilled' && Array.isArray(servicesRes.value)
|
||||
? (servicesRes.value as Array<Record<string, unknown>>)
|
||||
: []
|
||||
).filter((s) => envIds.has(Number(s.environment_id)));
|
||||
|
||||
// Build searchable tokens from the project name (lowercased words, length >= 3)
|
||||
const tokens = projectName
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9 ]/g, ' ')
|
||||
.split(/\s+/)
|
||||
.filter((t: string) => t.length >= 3);
|
||||
|
||||
const matches = (name: string) => {
|
||||
const n = name.toLowerCase();
|
||||
return tokens.some((t: string) => n.includes(t));
|
||||
};
|
||||
|
||||
for (const a of apps) {
|
||||
if (matches(a.name) || a.uuid === linkedUuid) {
|
||||
possibleDeployments.push({
|
||||
uuid: a.uuid,
|
||||
name: a.name,
|
||||
status: a.status,
|
||||
fqdn: a.fqdn ?? null,
|
||||
resourceType: 'application',
|
||||
});
|
||||
}
|
||||
}
|
||||
for (const s of services) {
|
||||
const name = String(s.name ?? '');
|
||||
const uuid = String(s.uuid);
|
||||
if (matches(name) || uuid === linkedUuid) {
|
||||
const subApps = (s.applications as Array<Record<string, unknown>>) || [];
|
||||
const publicApp = subApps.find((a) => a.fqdn);
|
||||
possibleDeployments.push({
|
||||
uuid,
|
||||
name,
|
||||
status: String(s.status ?? 'unknown'),
|
||||
fqdn: publicApp?.fqdn ? String(publicApp.fqdn) : null,
|
||||
resourceType: 'service',
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Best-effort enrichment — never let it block the project read
|
||||
}
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
result: {
|
||||
id: r.id,
|
||||
name: d.productName || d.name || d.title || 'Untitled',
|
||||
name: projectName,
|
||||
status: d.status || 'defining',
|
||||
vision: d.productVision || d.vision || null,
|
||||
domain: d.domain || d.customDomain || null,
|
||||
coolifyAppUuid: d.coolifyAppUuid || d.coolifyServiceUuid || null,
|
||||
coolifyAppUuid: linkedUuid,
|
||||
coolifyDomain: d.coolifyDomain || null,
|
||||
repositoryUrl: d.repositoryUrl || null,
|
||||
possibleDeployments,
|
||||
createdAt: r.created_at,
|
||||
updatedAt: r.updated_at,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user