Skip to content

Comments

feat: repository used space#551

Merged
nicotsx merged 2 commits intomainfrom
02-19-feat_repository_used_space
Feb 21, 2026
Merged

feat: repository used space#551
nicotsx merged 2 commits intomainfrom
02-19-feat_repository_used_space

Conversation

@nicotsx
Copy link
Owner

@nicotsx nicotsx commented Feb 19, 2026

Summary by CodeRabbit

Release Notes

  • New Features
    • Repository statistics now available in the repository details page, displaying compression and storage metrics
    • Statistics show compression ratio, space saved, total stored size, uncompressed size, snapshot count, and compression progress percentage
    • New statistics panel provides real-time visibility into repository storage optimization and compression efficiency
    • Helpful for monitoring repository backup storage usage and optimization progress

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 19, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

This PR introduces repository statistics functionality with a new API endpoint (GET /api/v1/repositories/{shortId}/stats), frontend query utilities and UI component to display compression stats, and refactored centralized cache key management across the backend.

Changes

Cohort / File(s) Summary
API Client Types & Query Setup
app/client/api-client/types.gen.ts, app/client/api-client/sdk.gen.ts, app/client/api-client/@tanstack/react-query.gen.ts, app/client/api-client/index.ts
Added types for repository stats endpoint (GetRepositoryStatsData, GetRepositoryStatsResponses) and generated React Query query key/options helpers; exported new types and SDK function.
Frontend UI
app/client/modules/repositories/components/compression-stats-chart.tsx, app/client/modules/repositories/tabs/info.tsx
Created new CompressionStatsChart component to display storage/compression statistics; integrated component into repository info tab and refactored layout with updated action buttons and additional info fields.
Backend API & DTOs
app/server/modules/repositories/repositories.dto.ts, app/server/modules/repositories/repositories.controller.ts, app/server/modules/repositories/__tests__/repositories.controller.test.ts
Added GetRepositoryStatsDto type and route descriptor; implemented GET /:shortId/stats endpoint; added endpoint tests for authentication and 404 scenarios.
Service Layer & Cache Management
app/server/modules/repositories/repositories.service.ts, app/server/utils/cache.ts, app/server/modules/backups/backups.execution.ts, app/server/modules/system/system.service.ts
Introduced getRepositoryStats service method with caching; added centralized cacheKeys utility for structured cache key generation; refactored all cache operations to use cacheKeys throughout multiple services.
Schema & Restic Utilities
app/schemas/restic-dto.ts, app/server/utils/restic.ts
Added resticStatsSchema with numeric fields (total_size, compression_ratio, etc.); implemented stats function in restic utility to fetch and validate repository statistics via restic stats command.

Possibly related PRs

  • PR #295: Modifies server-side cache management and repository cache patterns in app/server/utils/cache.ts and repositories.service.ts, aligning with this PR's cache key refactoring.
  • PR #132: Updates repositories info tab UI in app/client/modules/repositories/tabs/info.tsx, overlapping on tab rendering and field display logic.
  • PR #505: Extends Restic schema and utilities layer (app/schemas/restic-dto.ts, app/server/utils/restic.ts), complementing this PR's stats schema and restic function additions.
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: repository used space' is clear and directly related to the main changes in the PR, which adds repository statistics capability with compression stats display.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Owner Author

nicotsx commented Feb 19, 2026

@nicotsx nicotsx force-pushed the 02-19-feat_export_snap_as_tar_file branch from 7b1e719 to 3345fe2 Compare February 19, 2026 19:14
@nicotsx nicotsx force-pushed the 02-19-feat_repository_used_space branch from 2de6e21 to 562e7d7 Compare February 19, 2026 19:14
@nicotsx nicotsx force-pushed the 02-19-feat_export_snap_as_tar_file branch from 3345fe2 to eb3ecd8 Compare February 19, 2026 20:01
@nicotsx nicotsx force-pushed the 02-19-feat_repository_used_space branch 2 times, most recently from 584762f to 29ee163 Compare February 20, 2026 17:38
@nicotsx nicotsx force-pushed the 02-19-feat_export_snap_as_tar_file branch from eb3ecd8 to 49abcc5 Compare February 20, 2026 17:38
@nicotsx nicotsx force-pushed the 02-19-feat_repository_used_space branch 3 times, most recently from 04c299c to 80ee9c9 Compare February 21, 2026 09:15
Base automatically changed from 02-19-feat_export_snap_as_tar_file to main February 21, 2026 09:19
refactor: use a smarter cache key logic for easier invalidations
@nicotsx nicotsx force-pushed the 02-19-feat_repository_used_space branch from 80ee9c9 to 438ea1c Compare February 21, 2026 09:20
@nicotsx nicotsx marked this pull request as ready for review February 21, 2026 09:24
@dosubot
Copy link

dosubot bot commented Feb 21, 2026

Documentation Updates

1 document(s) were updated by changes in this PR:

Repositories in Zerobyte: Types, Architecture, and Operations
View Changes
@@ -239,9 +239,14 @@
 
 **Repository Statistics:**
 - [Get repository statistics](https://github.com/nicotsx/zerobyte/blob/bad944a2329540088a51f7699d6f53d9ac82137c/app/server/modules/repositories/repositories.service.ts#L197-L218): Retrieve compression and storage statistics
-  - Endpoint: `GET /api/v1/repositories/{shortId}/stats`
-  - Uses shared repository locks to safely read stats while allowing concurrent operations
-  - Results are cached using `cacheKeys.repository.stats(repositoryId)` to improve performance
+  - Endpoint: `GET /api/v1/repositories/{shortId}/stats` (production-ready as of PR #551)
+  - Uses shared repository locks (`repoMutex.acquireShared`) to safely read stats while allowing concurrent operations
+  - Results are cached using `cacheKeys.repository.stats(repository.id)` to improve performance
+  - Protected by the same authentication and authorization as other repository endpoints
+  - Cache is invalidated when:
+    - Repository configuration is changed
+    - Snapshots are deleted or modified (via `cacheKeys.repository.all()`)
+    - Repository is deleted
   - Returns `ResticStatsDto` with the following fields:
     - `total_size`: Total size of stored data in bytes
     - `total_uncompressed_size`: Total uncompressed size in bytes
@@ -310,11 +315,11 @@
 ## UI Components
 
 ### CompressionStatsChart
-The `CompressionStatsChart` component visualizes repository compression statistics in a card format:
+The `CompressionStatsChart` component visualizes repository compression statistics in a card format and is production-ready as of PR #551.
 
 **Location:** `app/client/modules/repositories/components/compression-stats-chart.tsx`
 
-**Displayed on:** Repository info tab
+**Displayed on:** Repository info tab (rendered within a grid layout alongside the main repository settings card)
 
 **Purpose:** Provides visual feedback on repository storage efficiency and compression effectiveness
 
@@ -325,10 +330,11 @@
 - **Compression Ratio**: How much the data has been compressed (e.g., 2.5x)
 - **Snapshots Count**: Total number of snapshots with compression progress percentage
 
-**Features:**
+**Implementation:**
+- Fetches data from the `/api/v1/repositories/{shortId}/stats` endpoint using React Query (`getRepositoryStatsOptions`)
 - Handles loading states while fetching statistics
 - Displays error messages if statistics cannot be retrieved
 - Shows empty state when no statistics are available yet (e.g., before first backup)
-- Uses the `GET /api/v1/repositories/{shortId}/stats` endpoint
-- Formats byte sizes and percentages for readability
-
+- Formats byte sizes and percentages for readability using the `ByteSize` component
+- Uses safe number parsing to handle edge cases and prevent display of invalid values
+

How did I do? Any feedback?  Join Discord

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
app/server/utils/restic.ts (2)

697-721: restic stats --mode raw-data should have a timeout; it can block indefinitely on large repositories.

Unlike snapshots (metadata only), restic stats --mode raw-data reads every pack file to compute actual sizes. On large repositories or slow/remote backends (S3, R2, GCS, SFTP), this can take several minutes and will block the HTTP request thread with no escape hatch. The exec helper already accepts a timeout option (see init).

Consider adding a configurable timeout — or at minimum a hard cap — so the endpoint degrades gracefully rather than timing out at the HTTP layer.

⏱️ Suggested fix: add a timeout option
-const stats = async (config: RepositoryConfig, options: { organizationId: string }) => {
+const stats = async (config: RepositoryConfig, options: { organizationId: string; timeoutMs?: number }) => {
     const repoUrl = buildRepoUrl(config);
     const env = await buildEnv(config, options.organizationId);

     const args = ["--repo", repoUrl, "stats", "--mode", "raw-data"];
     addCommonArgs(args, env, config);

-    const res = await exec({ command: "restic", args, env });
+    const res = await exec({ command: "restic", args, env, timeout: options.timeoutMs ?? 120_000 });
     await cleanupTemporaryKeys(env);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/server/utils/restic.ts` around lines 697 - 721, The stats function calls
exec({ command: "restic", args, env }) without a timeout; update stats to pass a
timeout to exec (e.g., exec({ command: "restic", args, env, timeout: <ms> }))
and make that timeout configurable (via RepositoryConfig or a service-level
env/config value) with a sensible hard cap, ensure buildEnv/cleanupTemporaryKeys
usage remains unchanged, and handle timeout failures the same way other exec
errors are handled (log with logger.error and throw a ResticError) so
long-running restic stats (--mode raw-data) cannot block indefinitely.

420-420: ResticStats = ResticStatsDto type alias is redundant.

The alias adds no additional constraint and requires consumers to choose between two names for the same type. ResticStatsDto is already exported from ~/schemas/restic-dto and can be used directly.

♻️ Proposed cleanup
-export type ResticStats = ResticStatsDto;

Then import ResticStatsDto directly wherever ResticStats was used.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/server/utils/restic.ts` at line 420, Remove the redundant type alias
ResticStats that simply re-exports ResticStatsDto: delete the line "export type
ResticStats = ResticStatsDto;" and update any imports/usages to reference
ResticStatsDto directly (search for ResticStats and replace with ResticStatsDto,
keeping any existing import from ~/schemas/restic-dto or adding it where
missing).
app/server/modules/repositories/__tests__/repositories.controller.test.ts (1)

119-128: Consider adding a happy-path test with a mocked restic.stats.

The current tests cover auth (401) and missing repo (404), but there is no test verifying the 200 response shape, that stats fields are returned correctly, or that the cache is populated. If restic is spied/mocked in other tests in this suite, a similar pattern could cover the successful path.

Would you like me to draft a test for the 200 case that mocks restic.stats (e.g., using bun:test's mock.module or a spyOn)?

As per coding guidelines, tests should be run with bunx dotenv-cli -e .env.test -- bun test --preload ./app/test/setup.ts.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/server/modules/repositories/__tests__/repositories.controller.test.ts`
around lines 119 - 128, Add a happy-path test that mocks restic.stats and
verifies a 200 response and response shape: in repositories.controller.test.ts
use createTestSession() and getAuthHeaders(token) and spy on or mock
restic.stats (via bun:test mock.module or jest.spyOn equivalent) to return a
predefined stats object, call app.request("/api/v1/repositories/<repo>/stats")
and expect status 200 and the body to contain the same stats fields (e.g., size,
snapshots, etc. as returned by your mock); also assert that the controller's
cache is populated for that repository (check whatever cache API your controller
exposes or the cache key used by the stats endpoint) and that subsequent
requests use the cached value (call the endpoint twice and verify restic.stats
was only invoked once).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/server/utils/cache.ts`:
- Around line 102-114: delByPrefix is building a LIKE pattern from keys (used
with cacheKeys.repository.all) without escaping SQLite wildcards, so
repositoryIds containing % or _ would match incorrectly; fix by escaping '%' '_'
and backslash characters in the prefix inside delByPrefix (replace '\' with
'\\', '%' with '\%', '_' with '\_'), then build the LIKE pattern using the
escaped prefix plus '%' and pass the pattern as a parameter to the SQL query
(and include an ESCAPE '\' clause if using raw SQL) to ensure only keys with the
literal prefix are deleted.

---

Nitpick comments:
In `@app/server/modules/repositories/__tests__/repositories.controller.test.ts`:
- Around line 119-128: Add a happy-path test that mocks restic.stats and
verifies a 200 response and response shape: in repositories.controller.test.ts
use createTestSession() and getAuthHeaders(token) and spy on or mock
restic.stats (via bun:test mock.module or jest.spyOn equivalent) to return a
predefined stats object, call app.request("/api/v1/repositories/<repo>/stats")
and expect status 200 and the body to contain the same stats fields (e.g., size,
snapshots, etc. as returned by your mock); also assert that the controller's
cache is populated for that repository (check whatever cache API your controller
exposes or the cache key used by the stats endpoint) and that subsequent
requests use the cached value (call the endpoint twice and verify restic.stats
was only invoked once).

In `@app/server/utils/restic.ts`:
- Around line 697-721: The stats function calls exec({ command: "restic", args,
env }) without a timeout; update stats to pass a timeout to exec (e.g., exec({
command: "restic", args, env, timeout: <ms> })) and make that timeout
configurable (via RepositoryConfig or a service-level env/config value) with a
sensible hard cap, ensure buildEnv/cleanupTemporaryKeys usage remains unchanged,
and handle timeout failures the same way other exec errors are handled (log with
logger.error and throw a ResticError) so long-running restic stats (--mode
raw-data) cannot block indefinitely.
- Line 420: Remove the redundant type alias ResticStats that simply re-exports
ResticStatsDto: delete the line "export type ResticStats = ResticStatsDto;" and
update any imports/usages to reference ResticStatsDto directly (search for
ResticStats and replace with ResticStatsDto, keeping any existing import from
~/schemas/restic-dto or adding it where missing).

@nicotsx nicotsx force-pushed the 02-19-feat_repository_used_space branch from ea50047 to c19f2f5 Compare February 21, 2026 09:47
@nicotsx nicotsx force-pushed the 02-19-feat_repository_used_space branch from c19f2f5 to 43b5562 Compare February 21, 2026 09:49
@nicotsx nicotsx merged commit 45fd9f9 into main Feb 21, 2026
6 checks passed
@nicotsx nicotsx deleted the 02-19-feat_repository_used_space branch February 21, 2026 09:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant