Add mirror health telemetry to settings API

swadhinbiswas committed 8 months ago
2cc7f45
1 parent 608805b
commit 2cc7f45
4 files changed
+35 -3
▼doc/feature_audit.md
+1-1
28
28
| :--- | :---: | :--- |
29
29
| Full Git protocol (SSH/HTTPS) | ✅ | `ssh.ts`, `git-server.ts` fully implemented |
30
30
| Repository hosting | ✅ | Multiple storage adapters (S3, R2, local, GDrive) |
31
-| Repository mirroring | ⚠️ | Background sync cron plus repository mirror config/manual-sync APIs are implemented (`settings/mirror`, `settings/mirror/sync`); scheduling/monitoring depth remains partial |
31
+| Repository mirroring | ⚠️ | Background sync cron plus repository mirror config/manual-sync APIs are implemented (`settings/mirror`, `settings/mirror/sync`) with derived health telemetry (`isHealthy`, `isStale`, `lastSyncAgeMinutes`); scheduling/monitoring automation depth remains partial |
32
32
| Forks & pull-based workflows | ✅ | `fork.ts` API, UI implemented |
33
33
| Monorepo support | ⚠️ | Basic support, no advanced path scoping |
34
34
| Git LFS | ✅ | `lfs.ts` with batch API (upload/download) |
35
35
▼src/lib/openapi.ts
+1-0
868
868
get: {
869
869
tags: ["Repositories"],
870
870
summary: "Get repository mirror settings",
871
+ description: "Returns mirror configuration and derived health fields (`isHealthy`, `isStale`, `lastSyncAgeMinutes`).",
871
872
security: [{ bearerAuth: [] }],
872
873
parameters: [
873
874
{ name: "owner", in: "path", required: true, schema: { type: "string" } },
874
875
▼src/pages/api/repos/[owner]/[repo]/settings/mirror.ts
+31-1
27
27
});
28
28
}
29
29
30
+const MIRROR_STALE_MINUTES = 24 * 60;
31
+
32
+function deriveMirrorHealth(repository: {
33
+ isMirror: boolean | null;
34
+ mirrorSyncStatus: string | null;
35
+ lastMirrorSyncAt: Date | null;
36
+}) {
37
+ if (!repository.isMirror) {
38
+ return {
39
+ lastSyncAgeMinutes: null as number | null,
40
+ isStale: false,
41
+ isHealthy: false,
42
+ };
43
+ }
44
+
45
+ const nowMs = Date.now();
46
+ const lastSyncMs = repository.lastMirrorSyncAt ? repository.lastMirrorSyncAt.getTime() : null;
47
+ const lastSyncAgeMinutes = lastSyncMs ? Math.max(0, Math.floor((nowMs - lastSyncMs) / 60000)) : null;
48
+ const isStale = lastSyncAgeMinutes === null ? true : lastSyncAgeMinutes > MIRROR_STALE_MINUTES;
49
+ const isHealthy = repository.mirrorSyncStatus === "success" && !isStale;
50
+
51
+ return {
52
+ lastSyncAgeMinutes,
53
+ isStale,
54
+ isHealthy,
55
+ };
56
+}
57
+
30
58
export const GET: APIRoute = withErrorHandler(async ({ params, request }) => {
31
59
const owner = params.owner;
32
60
const repoName = params.repo;
42
70
return notFound("Repository not found");
43
71
}
44
72
73
+ const health = deriveMirrorHealth(repository);
74
+
45
75
return success({
46
76
isMirror: repository.isMirror,
47
77
mirrorUrl: repository.mirrorUrl,
48
78
mirrorSyncStatus: repository.mirrorSyncStatus,
49
79
lastMirrorSyncAt: repository.lastMirrorSyncAt,
80
+ ...health,
50
81
});
51
82
});
52
83
101
132
102
133
return success({ configured: false });
103
134
});
104
-
105
135
▼tests/integration/repo-mirror-settings-route.test.ts
+2-1
95
95
const body = await readJson(response);
96
96
expect(response.status).toBe(200);
97
97
expect(body?.data?.isMirror).toBe(true);
98
+ expect(typeof body?.data?.isHealthy).toBe("boolean");
99
+ expect(typeof body?.data?.isStale).toBe("boolean");
98
100
});
99
101
100
102
it("configures mirror for repo admins", async () => {
139
141
expect(disableMirrorMock).toHaveBeenCalledWith("repo-1");
140
142
});
141
143
});
142
-
143
144