feat: store backup config in DB secret instead of env var

Move backup SSH keys and repo URL from MCPD_BACKUP_REPO env var to a
"backup-ssh" secret in the database. Keys are auto-generated on first
init and stored back into the secret. Also fix ERR_HTTP_HEADERS_SENT
crash caused by reply.send() without return in routes when onSend hook
is registered.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Michal
2026-03-08 13:53:12 +00:00
parent 6bce1431ae
commit af4b3fb702
10 changed files with 193 additions and 136 deletions

View File

@@ -73,13 +73,38 @@ describe('backup command', () => {
expect(log).toHaveBeenCalledWith(expect.stringContaining('5 changes pending'));
});
it('shows SSH public key', async () => {
mockClient.get.mockResolvedValue({ publicKey: 'ssh-ed25519 AAAA... mcpd@mcpctl.local' });
it('shows SSH public key in status when enabled', async () => {
mockClient.get.mockResolvedValue({
enabled: true,
repoUrl: 'ssh://git@host/repo.git',
publicKey: 'ssh-ed25519 AAAA... mcpd@mcpctl.local',
gitReachable: true,
lastSyncAt: null,
lastPushAt: null,
lastError: null,
pendingCount: 0,
});
await makeCmd().parseAsync(['key'], { from: 'user' });
await makeCmd().parseAsync([], { from: 'user' });
expect(mockClient.get).toHaveBeenCalledWith('/api/v1/backup/key');
expect(log).toHaveBeenCalledWith('ssh-ed25519 AAAA... mcpd@mcpctl.local');
expect(log).toHaveBeenCalledWith(expect.stringContaining('ssh-ed25519 AAAA... mcpd@mcpctl.local'));
});
it('shows setup instructions when disabled', async () => {
mockClient.get.mockResolvedValue({
enabled: false,
repoUrl: null,
publicKey: null,
gitReachable: false,
lastSyncAt: null,
lastPushAt: null,
lastError: null,
pendingCount: 0,
});
await makeCmd().parseAsync([], { from: 'user' });
expect(log).toHaveBeenCalledWith(expect.stringContaining('mcpctl create secret backup-ssh'));
});
it('shows commit log', async () => {