Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
a7410b9
feat(db): add tokens, deployments, and storage profiles tables
krishna-santosh Jul 21, 2026
95f16b4
feat(auth): introduce granular token scopes with TokenScope enum
krishna-santosh Jul 21, 2026
b1e2edd
feat(auth): add personal access token (PAT) authentication and manage…
krishna-santosh Jul 21, 2026
26cf1a2
feat: register new modules and wire services
krishna-santosh Jul 21, 2026
65fbf3b
feat(storage): add storage profile management
krishna-santosh Jul 21, 2026
dd5b1f3
feat(backup): support storage profile destination for backups
krishna-santosh Jul 21, 2026
9098456
feat(deploy): add deployment triggers and job management
krishna-santosh Jul 21, 2026
0582072
feat(mcp): add list_sites tool and enforce required site_id
krishna-santosh Jul 21, 2026
30b7d21
feat(dashboard): update API client with new endpoints and types
krishna-santosh Jul 21, 2026
ca2b3a3
feat(dashboard): add personal access tokens UI
krishna-santosh Jul 21, 2026
f23f7b6
feat(dashboard): update storage settings and site creation with stora…
krishna-santosh Jul 21, 2026
d6d50e1
feat(auth): wire X-VCMS-Site through GraphQL and add scope checks to …
krishna-santosh Jul 21, 2026
434500c
feat(dashboard): add deployments page, MCP settings, storage profile …
krishna-santosh Jul 21, 2026
1288964
fix(db): add unique partial index to prevent concurrent active deploy…
krishna-santosh Jul 22, 2026
c46d4a6
feat(webhook): extract build_protected_client and integrate into depl…
krishna-santosh Jul 22, 2026
f2c3f28
fix(auth): verify site access before resolving site for personal toke…
krishna-santosh Jul 22, 2026
81585db
fix(backup): correct storage_profile_id column order in meta inserts
krishna-santosh Jul 22, 2026
f85551c
fix(mysql): harden storage_profiles and backup FK with ENGINE, RESTRI…
krishna-santosh Jul 22, 2026
c475bc5
refactor(grpc): replace require_read/require_write with fine-grained …
krishna-santosh Jul 22, 2026
6397df0
refactor(grpc): update service handlers to use scope-based authorization
krishna-santosh Jul 22, 2026
275d394
test: add site_key_scopes helper for scoped token test creation
krishna-santosh Jul 22, 2026
d534b09
test: update gRPC test context to use scoped tokens
krishna-santosh Jul 22, 2026
4ef0db2
test: update GraphQL tests to use scoped tokens
krishna-santosh Jul 22, 2026
ca80501
test: update REST tests to use scoped tokens
krishna-santosh Jul 22, 2026
d6495e8
test(mcp): add mcp_call_site_tool helper and site_id param to fixtures
krishna-santosh Jul 23, 2026
8f9e41f
test(mcp): pass site_id to all site-scoped MCP tool calls
krishna-santosh Jul 23, 2026
2226534
docs(mcp/stdio): update comments for broader token type acceptance
krishna-santosh Jul 23, 2026
2906abd
docs(readme): document vcms_pat_* token support for MCP proxy
krishna-santosh Jul 23, 2026
9c24179
fix(mcp): update auth error message to reflect broader token types
krishna-santosh Jul 23, 2026
5298cab
feat(mcp): enable list_sites tool in MCP tool registry
krishna-santosh Jul 23, 2026
1253732
rm mysql
krishna-santosh Jul 23, 2026
f7bf29c
temp: add .coderabbit.yaml
krishna-santosh Jul 23, 2026
932f7ae
docs: fix comment typo (three -> two implementations)
krishna-santosh Jul 24, 2026
88639c6
refactor(error): add StorageProfileNotFound variant to SiteError
krishna-santosh Jul 24, 2026
c5d4eba
feat(site): add create_with_storage_profile repository method
krishna-santosh Jul 24, 2026
a6f3b4a
feat(site): support storage_profile_id in site creation service
krishna-santosh Jul 24, 2026
b2d6650
refactor(storage-profile): return AppError from handlers instead of a…
krishna-santosh Jul 24, 2026
ba414f9
feat(storage-profile): use transactional CRUD with registry lifecycle
krishna-santosh Jul 24, 2026
c347fa9
feat(secrets): invalidate storage profile credentials on secrets reset
krishna-santosh Jul 24, 2026
b6146f5
feat(graphql): use async action-aware auth checks instead of token sc…
krishna-santosh Jul 24, 2026
c22b00e
feat(access-token): use site_role_allows_token_scope for personal tok…
krishna-santosh Jul 24, 2026
113ad34
feat(server): wire DeploymentService into startup
krishna-santosh Jul 24, 2026
2ef594a
refactor(backup): extract provider_for_profile helper
krishna-santosh Jul 24, 2026
efbc4f3
test(storage-profile): add REST integration tests for CRUD lifecycle
krishna-santosh Jul 24, 2026
d6b4d9d
test(graphql): verify viewer personal token cannot write via GraphQL
krishna-santosh Jul 24, 2026
cbeef1d
docs(mcp/stdio): fix article typo (a -> an)
krishna-santosh Jul 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
language: en-US
early_access: false
reviews:
path_filters:
- "!apps/backend/migrations/mysql/**"
- "!apps/backend/src/repository/mysql/**"
26 changes: 5 additions & 21 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,9 @@ jobs:
strategy:
fail-fast: false
matrix:
# SQLite runs fully parallel (in-memory, isolated per test). Postgres and
# MySQL run database-per-test against one shared server, so concurrent
# tests are capped to keep the connection demand well under the server's
# ceiling on high-core runners.
# SQLite runs fully parallel (in-memory, isolated per test). Postgres runs
# database-per-test against one shared server, so concurrent tests are
# capped to keep connection demand well under server ceiling.
include:
- db: sqlite
test_database: sqlite
Expand All @@ -147,14 +146,10 @@ jobs:
test_database: postgres
test_database_url: postgres://postgres:postgres@localhost:5432/postgres
test_args: "--test-threads 4"
- db: mysql
test_database: mysql
test_database_url: mysql://root:root@localhost:3306/mysql
test_args: "--test-threads 4"
env:
TEST_DATABASE: ${{ matrix.test_database }}
TEST_DATABASE_URL: ${{ matrix.test_database_url }}
# Both services run for every lane; the sqlite lane simply ignores them
# Service runs for every lane; sqlite lane simply ignores it
# (GitHub Actions can't attach services conditionally per matrix value).
# Service containers are Linux-only, which is why Windows/macOS live in the
# separate `test-os` job (SQLite only).
Expand All @@ -172,17 +167,6 @@ jobs:
--health-interval 2s
--health-timeout 5s
--health-retries 15
mysql:
image: mysql:8
env:
MYSQL_ROOT_PASSWORD: root
ports:
- 3306:3306
options: >-
--health-cmd "mysqladmin ping -h 127.0.0.1 -uroot -proot"
--health-interval 2s
--health-timeout 5s
--health-retries 15
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: arduino/setup-protoc@c65c819552d16ad3c9b72d9dfd5ba5237b9c906b # v3
Expand All @@ -195,7 +179,7 @@ jobs:
with:
# Workspace root is the REPO root (Cargo.lock + target/ live there).
workspaces: ". -> target"
# All 3 db lanes compile an identical binary (db is chosen at runtime
# Both db lanes compile an identical binary (db is chosen at runtime
# via TEST_DATABASE), so one shared cache serves all of them; the
# save race between lanes is harmless. NOT shared with lint: clippy
# saves check-artifacts that are useless for test codegen.
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

## Architecture

- **Backend**: Rust + Axum HTTP server, `SQLx` + (SQLite | PostgreSQL | MySQL), `rust-embed` (static assets)
- **Backend**: Rust + Axum HTTP server, `SQLx` + (SQLite | PostgreSQL), `rust-embed` (static assets)
- **Frontend**: React in `apps/dashboard/` with Tanstack Router, Tanstack Query, shadcn/ui
- **gRPC**: Separate server on port 50051 (compiled from `libs/proto/*.proto` via `tonic-build`)
- **Build**: Nx orchestrates `dashboard:build` → `backend:build`. `build.rs` compiles proto files only.
Expand Down Expand Up @@ -222,7 +222,7 @@ A logical backup/restore subsystem lives in `apps/backend/src/services/backup/`:
- `schema.rs` — table registry + cross-backend dump/restore. Every value is
normalized to **text** (Postgres casts `::text`/`::int`; restore casts back with
`::jsonb`/`::timestamptz`/`::bigint`/`::int::boolean`), so a backup is a portable,
DB-agnostic set of NDJSON rows that restores into any of the three backends.
DB-agnostic set of NDJSON rows that restores into either backend.
- `mod.rs` — `BackupService`: a snapshot read (REPEATABLE READ / WAL) dumps tables →
tar → zstd → optional AES-256-GCM, written to a destination `StorageProvider`.
Restore is full-replace within the chosen scope in one transaction (site filter,
Expand Down
6 changes: 3 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Architecture

- **Backend**: Rust + Axum HTTP server, `SQLx` + (SQLite | PostgreSQL | MySQL), `rust-embed` (static assets)
- **Backend**: Rust + Axum HTTP server, `SQLx` + (SQLite | PostgreSQL), `rust-embed` (static assets)
- **Frontend**: React in `apps/dashboard/` with Tanstack Router, Tanstack Query, shadcn/ui
- **gRPC**: Separate server on port 50051 (compiled from `libs/proto/*.proto` via `tonic-build`)
- **Build**: Nx orchestrates `dashboard:build` → `backend:build`. `build.rs` compiles proto files only.
Expand Down Expand Up @@ -225,8 +225,8 @@ a backup taken on a **newer** schema and relies on additive migrations otherwise

Entry search is backed by an embedded [Tantivy](https://github.com/quickwit-oss/tantivy)
index in `apps/backend/src/services/search/` — one engine that gives ranked,
tokenized, stemmed search identically across SQLite/Postgres/MySQL (native per-DB
FTS would mean three implementations). The DB stays the source of truth; the index
tokenized, stemmed search identically across SQLite/Postgres (native per-DB
FTS would mean two implementations). The DB stays the source of truth; the index
is **derived** and fully rebuildable.

**Reads vs writes are split** so the single-writer limit never blocks *searching*:
Expand Down
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Upload, organize, and serve images and files with automatic thumbnail generation

### 💾 Backups & Disaster Recovery

Create on-demand or scheduled backups of a single site or the whole instance. Backups are compressed, optionally encrypted (AES-256-GCM), and stored on local disk or a separate S3 bucket — keep the last N automatically. Restore in place or import a site as a copy, from the dashboard or offline with `vcms restore` when the server won't even boot. Backups are a portable logical dump, so you can move data between SQLite, PostgreSQL, and MySQL.
Create on-demand or scheduled backups of a single site or the whole instance. Backups are compressed, optionally encrypted (AES-256-GCM), and stored on local disk or a separate S3 bucket — keep the last N automatically. Restore in place or import a site as a copy, from the dashboard or offline with `vcms restore` when the server won't even boot. Backups are a portable logical dump, so you can move data between SQLite and PostgreSQL.

### 🔐 Secure & Scalable

Expand Down Expand Up @@ -103,10 +103,11 @@ that the client process can't read.
VCMS_MCP_TOKEN=vcms_site_... VCMS_MCP_URL=http://127.0.0.1:3000 vcms mcp stdio
```

It needs only two env vars: `VCMS_MCP_TOKEN` (a `vcms_site_*` access token, forwarded
as the `Authorization: Bearer` credential) and `VCMS_MCP_URL` (the running server's
base URL, default `http://127.0.0.1:3000`; the proxy posts to `{url}/mcp`). A `vcms
serve` instance must be running. MCP protocol messages use stdout; logs use stderr.
It needs only two env vars: `VCMS_MCP_TOKEN` (a `vcms_site_*` or `vcms_pat_*`
access token with the `mcp.use` scope, forwarded as the `Authorization: Bearer`
credential) and `VCMS_MCP_URL` (the running server's base URL, default
`http://127.0.0.1:3000`; the proxy posts to `{url}/mcp`). A `vcms serve` instance
must be running. MCP protocol messages use stdout; logs use stderr.

```jsonc
// Example MCP client config
Expand Down Expand Up @@ -166,7 +167,7 @@ Built by developers, for developers. The API is predictable, the documentation i

### Database Flexibility

Use SQLite for simple deployments or connect to PostgreSQL or MySQL for production workloads. The same binary works with all three.
Use SQLite for simple deployments or connect to PostgreSQL for production workloads. The same binary works with both.

### Built for Teams

Expand Down
2 changes: 1 addition & 1 deletion apps/backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ tower = { version = "0.5", features = ["full"] }
tower-http = { version = "0.7", features = ["cors", "limit", "trace"] }
rust-embed = { version = "8", optional = true }
mime_guess = "2"
sqlx = { version = "0.9", features = ["runtime-tokio", "macros", "postgres", "mysql", "sqlite", "chrono", "uuid", "migrate"] }
sqlx = { version = "0.9", features = ["runtime-tokio", "macros", "postgres", "sqlite", "chrono", "uuid", "migrate"] }
thiserror = "2"
async-trait = "0.1"
uuid = { version = "1", features = ["v4", "v7", "serde"] }
Expand Down
178 changes: 0 additions & 178 deletions apps/backend/migrations/mysql/20260501000000_initial_schema.sql

This file was deleted.

This file was deleted.

48 changes: 0 additions & 48 deletions apps/backend/migrations/mysql/20260610000000_auth_rbac.sql

This file was deleted.

This file was deleted.

13 changes: 0 additions & 13 deletions apps/backend/migrations/mysql/20260616000000_roles_v2.sql

This file was deleted.

Loading
Loading