feat: storage page with sizes recorded during sync (#480)

- Record each mirror's destination size (Git plus LFS) and its release asset bytes and count during sync, from responses the sync already fetches, so no new requests. Four nullable columns on repositories, migration 0021.
- GET /api/storage and a Storage page with cards, bars by destination owner and for the largest repositories, and a sortable table with search, an owner filter and a release limit hint.
- Run sources.test.ts in an isolated child process so leaked @/lib/sources mocks cannot reach it.

Closes #479

Co-authored-by: Florian Zevedei <florian.dahn@fdsoftware.de>
This commit is contained in:
ARUNAVO RAYandFlorian Zevedei authored and GitHub committed 2026-10-29 10:59:54 +05:30
1 parent 4e91237d99
commit 372d04dee2
27 files changed
+4529 -12

No files matched your search

+15
View File
@@ -156,6 +156,21 @@ When several sources are connected, an optional `sourceId` picks which one the o
`PATCH /api/organizations/:id` with `{ "sourceId": "…" }` re-pins an existing organization to another source, and `{ "sourceId": null }` clears the pin so the organization follows every source's repositories of the same name again. A pin whose source was removed falls back the same way.
### Storage
`GET /api/storage` returns what the Storage page shows: how much space the account's mirrors take on the destination (issue #479).
```json
{
"success": true,
"totals": { "total": 3554304, "destinationSize": 3030016, "releaseAssetsSize": 524288, "releaseAssetsCount": 1, "measured": 1, "mirrored": 1 },
"owners": [{ "owner": "archive", "destinationSize": 3030016, "releaseAssetsSize": 524288, "total": 3554304, "repositories": 1 }],
"repositories": [{ "id": "…", "fullName": "upstream/app", "location": "archive/app", "owner": "archive", "destinationSize": 3030016, "releaseAssetsSize": 524288, "releaseAssetsCount": 1, "total": 3554304, "checkedAt": "2026-10-29T03:32:01.000Z" }]
}
```
Sizes are bytes. `destinationSize` is Git and LFS together as Gitea or Forgejo reports it, and the release fields cover the assets on the destination's releases. Each sync records them from responses it already fetches, so this endpoint never contacts the destination. A repository is listed once a sync has measured it, and a field not measured yet is `null`. `mirrored` counts the repositories with a mirror on a Gitea or Forgejo destination and `measured` the ones with a size so far; push destinations (GitHub, GitLab) are not measured.
### Export as CSV
`GET /api/repositories/export` and `GET /api/organizations/export` return everything the account tracks as a CSV file, so the list can be shared with people who do not have a login here (issue #428).