mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-13 04:06:59 +00:00
fix(git-sources): return 200 for stacks without a Git source (#1294)
The dashboard probes GET /api/stacks/<name>/git-source for every stack to
decide whether to show a Git badge. For a stack with no Git source attached
the endpoint answered 404, so a fleet of unlinked stacks painted a red 404
per stack in the browser console.
Return 200 { linked: false } when the stack exists but has no Git source,
and reserve 404 for the genuine "stack does not exist" case (mirroring the
existence guard the PUT handler already uses). The two consumers that read
this endpoint now treat the { linked: false } sentinel as unlinked rather
than as a configured source.
This commit is contained in:
@@ -253,6 +253,41 @@ describe('git-source routes — invalid stack names', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /api/stacks/:stackName/git-source', () => {
|
||||
it('returns 200 { linked: false } when the stack exists but has no Git source', async () => {
|
||||
const composeDir = process.env.COMPOSE_DIR!;
|
||||
fs.mkdirSync(path.join(composeDir, 'unlinked-stack'), { recursive: true });
|
||||
fs.writeFileSync(path.join(composeDir, 'unlinked-stack', 'compose.yaml'), 'services:\n x:\n image: nginx\n');
|
||||
const res = await request(app)
|
||||
.get('/api/stacks/unlinked-stack/git-source')
|
||||
.set('Authorization', `Bearer ${adminToken()}`);
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body).toEqual({ linked: false });
|
||||
});
|
||||
|
||||
it('returns 404 when the stack does not exist on the active node', async () => {
|
||||
const res = await request(app)
|
||||
.get('/api/stacks/ghost-stack-get/git-source')
|
||||
.set('Authorization', `Bearer ${adminToken()}`);
|
||||
expect(res.status).toBe(404);
|
||||
expect(res.body.error).toMatch(/stack not found/i);
|
||||
});
|
||||
|
||||
it('returns 200 with the source object when a Git source is configured', async () => {
|
||||
const composeDir = process.env.COMPOSE_DIR!;
|
||||
fs.mkdirSync(path.join(composeDir, 'linked-stack'), { recursive: true });
|
||||
fs.writeFileSync(path.join(composeDir, 'linked-stack', 'compose.yaml'), 'services:\n x:\n image: nginx\n');
|
||||
seedGitSource('linked-stack');
|
||||
const res = await request(app)
|
||||
.get('/api/stacks/linked-stack/git-source')
|
||||
.set('Authorization', `Bearer ${adminToken()}`);
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body.stack_name).toBe('linked-stack');
|
||||
expect(res.body.repo_url).toBe('https://github.com/example/repo.git');
|
||||
expect(res.body.linked).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /api/stacks/from-git', () => {
|
||||
const validBody = {
|
||||
stack_name: 'route-from-git',
|
||||
|
||||
@@ -49,11 +49,21 @@ stackGitSourceRouter.get('/:stackName/git-source', async (req: Request, res: Res
|
||||
if (!requirePermission(req, res, 'stack:read', 'stack', stackName)) return;
|
||||
try {
|
||||
const source = GitSourceService.getInstance().get(stackName);
|
||||
if (!source) {
|
||||
res.status(404).json({ error: 'No Git source configured for this stack' });
|
||||
if (source) {
|
||||
res.json(source);
|
||||
return;
|
||||
}
|
||||
res.json(source);
|
||||
// No source row. A non-existent stack is a genuine 404, but an existing
|
||||
// stack with no Git source attached is a normal, non-error state. The
|
||||
// dashboard probes this endpoint for every stack, so returning 404 here
|
||||
// would paint a console error for every unlinked stack; answer 200 with
|
||||
// a discriminator instead and reserve 404 for the stack-not-found case.
|
||||
const stacks = await FileSystemService.getInstance(req.nodeId).getStacks();
|
||||
if (!stacks.includes(stackName)) {
|
||||
res.status(404).json({ error: 'Stack not found' });
|
||||
return;
|
||||
}
|
||||
res.json({ linked: false });
|
||||
} catch (error) {
|
||||
sendGitSourceError(res, error);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user