-
Notifications
You must be signed in to change notification settings - Fork 1
Implement personal access tokens, storage profiles, and deployments management #35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
krishna-santosh
wants to merge
16
commits into
main
Choose a base branch
from
dev
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+3,754
−933
Open
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
a7410b9
feat(db): add tokens, deployments, and storage profiles tables
krishna-santosh 95f16b4
feat(auth): introduce granular token scopes with TokenScope enum
krishna-santosh b1e2edd
feat(auth): add personal access token (PAT) authentication and manage…
krishna-santosh 26cf1a2
feat: register new modules and wire services
krishna-santosh 65fbf3b
feat(storage): add storage profile management
krishna-santosh dd5b1f3
feat(backup): support storage profile destination for backups
krishna-santosh 9098456
feat(deploy): add deployment triggers and job management
krishna-santosh 0582072
feat(mcp): add list_sites tool and enforce required site_id
krishna-santosh 30b7d21
feat(dashboard): update API client with new endpoints and types
krishna-santosh ca2b3a3
feat(dashboard): add personal access tokens UI
krishna-santosh f23f7b6
feat(dashboard): update storage settings and site creation with stora…
krishna-santosh d6d50e1
feat(auth): wire X-VCMS-Site through GraphQL and add scope checks to …
krishna-santosh 434500c
feat(dashboard): add deployments page, MCP settings, storage profile …
krishna-santosh 1288964
fix(db): add unique partial index to prevent concurrent active deploy…
krishna-santosh c46d4a6
feat(webhook): extract build_protected_client and integrate into depl…
krishna-santosh f2c3f28
fix(auth): verify site access before resolving site for personal toke…
krishna-santosh File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
16 changes: 16 additions & 0 deletions
16
apps/backend/migrations/mysql/20260721000000_tokens_deployments_storage.sql
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| CREATE TABLE IF NOT EXISTS personal_access_tokens (id VARCHAR(36) PRIMARY KEY,user_id VARCHAR(36) NOT NULL,name VARCHAR(255) NOT NULL,token_hash TEXT NOT NULL,token_hmac VARCHAR(128) NOT NULL,token_prefix VARCHAR(32) NOT NULL,scopes_json TEXT NOT NULL,created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,last_used_at TIMESTAMP NULL,expires_at TIMESTAMP NULL,revoked_at TIMESTAMP NULL,FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE); | ||
| CREATE INDEX idx_pat_prefix ON personal_access_tokens(token_prefix); | ||
| ALTER TABLE access_tokens ADD COLUMN scopes_json TEXT NOT NULL DEFAULT ('[]'); | ||
| CREATE TABLE IF NOT EXISTS storage_profiles (id VARCHAR(36) PRIMARY KEY,name VARCHAR(255) NOT NULL UNIQUE,kind VARCHAR(20) NOT NULL,endpoint TEXT,region VARCHAR(255),bucket VARCHAR(255),public_url TEXT,credentials_encrypted TEXT,enabled BOOLEAN NOT NULL DEFAULT TRUE,immutable BOOLEAN NOT NULL DEFAULT FALSE,created_by VARCHAR(36),created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,FOREIGN KEY(created_by) REFERENCES users(id)); | ||
| INSERT IGNORE INTO storage_profiles(id,name,kind,enabled,immutable) VALUES ('local-filesystem','Local Filesystem','filesystem',TRUE,TRUE); | ||
| ALTER TABLE sites ADD COLUMN storage_profile_id VARCHAR(36) NULL; | ||
| UPDATE sites SET storage_profile_id='local-filesystem' WHERE storage_profile_id IS NULL; | ||
| ALTER TABLE backups ADD COLUMN storage_profile_id VARCHAR(36) NULL; | ||
| ALTER TABLE backup_schedules ADD COLUMN storage_profile_id VARCHAR(36) NULL; | ||
| ALTER TABLE backups ADD CONSTRAINT fk_backups_storage_profile FOREIGN KEY (storage_profile_id) REFERENCES storage_profiles(id); | ||
| ALTER TABLE backup_schedules ADD CONSTRAINT fk_backup_schedules_storage_profile FOREIGN KEY (storage_profile_id) REFERENCES storage_profiles(id); | ||
| CREATE TABLE IF NOT EXISTS deployment_triggers (id VARCHAR(36) PRIMARY KEY,site_id VARCHAR(36) NOT NULL,label VARCHAR(255) NOT NULL,provider VARCHAR(32) NOT NULL,url_encrypted TEXT NOT NULL,headers_encrypted TEXT NOT NULL,enabled BOOLEAN NOT NULL DEFAULT TRUE,is_primary BOOLEAN NOT NULL DEFAULT FALSE,cooldown_seconds BIGINT NOT NULL DEFAULT 60,daily_quota BIGINT NOT NULL DEFAULT 20,created_by VARCHAR(36),created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,FOREIGN KEY(site_id) REFERENCES sites(id) ON DELETE CASCADE,FOREIGN KEY(created_by) REFERENCES users(id)); | ||
| CREATE TABLE IF NOT EXISTS deployment_jobs (id VARCHAR(36) PRIMARY KEY,trigger_id VARCHAR(36) NOT NULL,site_id VARCHAR(36) NOT NULL,status VARCHAR(32) NOT NULL,status_code INTEGER,error_category VARCHAR(32),response_body TEXT,retry_after_seconds BIGINT,duration_ms BIGINT,triggered_by VARCHAR(36),created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,started_at TIMESTAMP NULL,finished_at TIMESTAMP NULL,FOREIGN KEY(trigger_id) REFERENCES deployment_triggers(id) ON DELETE CASCADE,FOREIGN KEY(site_id) REFERENCES sites(id) ON DELETE CASCADE); | ||
| CREATE INDEX idx_deployment_jobs_trigger ON deployment_jobs(trigger_id,created_at); | ||
| ALTER TABLE deployment_jobs ADD COLUMN active_trigger_id VARCHAR(36) GENERATED ALWAYS AS (CASE WHEN status IN ('queued','running') THEN trigger_id ELSE NULL END) STORED; | ||
| CREATE UNIQUE INDEX idx_deployment_jobs_active_trigger ON deployment_jobs(active_trigger_id); |
14 changes: 14 additions & 0 deletions
14
apps/backend/migrations/postgres/20260721000000_tokens_deployments_storage.sql
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| CREATE TABLE IF NOT EXISTS personal_access_tokens (id TEXT PRIMARY KEY,user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,name TEXT NOT NULL,token_hash TEXT NOT NULL,token_hmac TEXT NOT NULL,token_prefix TEXT NOT NULL,scopes_json TEXT NOT NULL,created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),last_used_at TIMESTAMPTZ,expires_at TIMESTAMPTZ,revoked_at TIMESTAMPTZ); | ||
| CREATE INDEX IF NOT EXISTS idx_pat_prefix ON personal_access_tokens(token_prefix); | ||
| ALTER TABLE access_tokens ADD COLUMN IF NOT EXISTS scopes_json TEXT NOT NULL DEFAULT '[]'; | ||
| CREATE TABLE IF NOT EXISTS storage_profiles (id TEXT PRIMARY KEY,name TEXT NOT NULL UNIQUE,kind TEXT NOT NULL CHECK(kind IN ('filesystem','s3')),endpoint TEXT,region TEXT,bucket TEXT,public_url TEXT,credentials_encrypted TEXT,enabled BOOLEAN NOT NULL DEFAULT TRUE,immutable BOOLEAN NOT NULL DEFAULT FALSE,created_by TEXT REFERENCES users(id),created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()); | ||
| INSERT INTO storage_profiles(id,name,kind,enabled,immutable) VALUES ('local-filesystem','Local Filesystem','filesystem',TRUE,TRUE) ON CONFLICT DO NOTHING; | ||
| ALTER TABLE sites ADD COLUMN IF NOT EXISTS storage_profile_id TEXT REFERENCES storage_profiles(id); | ||
| UPDATE sites SET storage_profile_id='local-filesystem' WHERE storage_profile_id IS NULL; | ||
| ALTER TABLE backups ADD COLUMN IF NOT EXISTS storage_profile_id TEXT REFERENCES storage_profiles(id); | ||
| ALTER TABLE backup_schedules ADD COLUMN IF NOT EXISTS storage_profile_id TEXT REFERENCES storage_profiles(id); | ||
| CREATE TABLE IF NOT EXISTS deployment_triggers (id TEXT PRIMARY KEY,site_id TEXT NOT NULL REFERENCES sites(id) ON DELETE CASCADE,label TEXT NOT NULL,provider TEXT NOT NULL,url_encrypted TEXT NOT NULL,headers_encrypted TEXT NOT NULL,enabled BOOLEAN NOT NULL DEFAULT TRUE,is_primary BOOLEAN NOT NULL DEFAULT FALSE,cooldown_seconds BIGINT NOT NULL DEFAULT 60,daily_quota BIGINT NOT NULL DEFAULT 20,created_by TEXT REFERENCES users(id),created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()); | ||
| CREATE UNIQUE INDEX IF NOT EXISTS idx_deployment_primary ON deployment_triggers(site_id) WHERE is_primary=TRUE; | ||
| CREATE TABLE IF NOT EXISTS deployment_jobs (id TEXT PRIMARY KEY,trigger_id TEXT NOT NULL REFERENCES deployment_triggers(id) ON DELETE CASCADE,site_id TEXT NOT NULL REFERENCES sites(id) ON DELETE CASCADE,status TEXT NOT NULL,status_code INTEGER,error_category TEXT,response_body TEXT,retry_after_seconds BIGINT,duration_ms BIGINT,triggered_by TEXT,created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),started_at TIMESTAMPTZ,finished_at TIMESTAMPTZ); | ||
| CREATE INDEX IF NOT EXISTS idx_deployment_jobs_trigger ON deployment_jobs(trigger_id,created_at DESC); | ||
| CREATE UNIQUE INDEX IF NOT EXISTS idx_deployment_jobs_active_trigger ON deployment_jobs(trigger_id) WHERE status IN ('queued','running'); |
42 changes: 42 additions & 0 deletions
42
apps/backend/migrations/sqlite/20260721000000_tokens_deployments_storage.sql
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| CREATE TABLE IF NOT EXISTS personal_access_tokens ( | ||
| id TEXT PRIMARY KEY, user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, | ||
| name TEXT NOT NULL, token_hash TEXT NOT NULL, token_hmac TEXT NOT NULL, | ||
| token_prefix TEXT NOT NULL, scopes_json TEXT NOT NULL, | ||
| created_at TEXT NOT NULL DEFAULT (datetime('now')), last_used_at TEXT, | ||
| expires_at TEXT, revoked_at TEXT | ||
| ); | ||
| CREATE INDEX IF NOT EXISTS idx_pat_prefix ON personal_access_tokens(token_prefix); | ||
| ALTER TABLE access_tokens ADD COLUMN scopes_json TEXT NOT NULL DEFAULT '[]'; | ||
|
|
||
| CREATE TABLE IF NOT EXISTS storage_profiles ( | ||
| id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, kind TEXT NOT NULL CHECK(kind IN ('filesystem','s3')), | ||
| endpoint TEXT, region TEXT, bucket TEXT, public_url TEXT, credentials_encrypted TEXT, | ||
| enabled INTEGER NOT NULL DEFAULT 1, immutable INTEGER NOT NULL DEFAULT 0, | ||
| created_by TEXT REFERENCES users(id), created_at TEXT NOT NULL DEFAULT (datetime('now')), | ||
| updated_at TEXT NOT NULL DEFAULT (datetime('now')) | ||
| ); | ||
| INSERT OR IGNORE INTO storage_profiles(id,name,kind,enabled,immutable) VALUES ('local-filesystem','Local Filesystem','filesystem',1,1); | ||
| ALTER TABLE sites ADD COLUMN storage_profile_id TEXT REFERENCES storage_profiles(id); | ||
| UPDATE sites SET storage_profile_id = 'local-filesystem' WHERE storage_profile_id IS NULL; | ||
| ALTER TABLE backups ADD COLUMN storage_profile_id TEXT REFERENCES storage_profiles(id); | ||
| ALTER TABLE backup_schedules ADD COLUMN storage_profile_id TEXT REFERENCES storage_profiles(id); | ||
|
|
||
| CREATE TABLE IF NOT EXISTS deployment_triggers ( | ||
| id TEXT PRIMARY KEY, site_id TEXT NOT NULL REFERENCES sites(id) ON DELETE CASCADE, | ||
| label TEXT NOT NULL, provider TEXT NOT NULL, url_encrypted TEXT NOT NULL, headers_encrypted TEXT NOT NULL, | ||
| enabled INTEGER NOT NULL DEFAULT 1, is_primary INTEGER NOT NULL DEFAULT 0, | ||
| cooldown_seconds INTEGER NOT NULL DEFAULT 60, daily_quota INTEGER NOT NULL DEFAULT 20, | ||
| created_by TEXT REFERENCES users(id), created_at TEXT NOT NULL DEFAULT (datetime('now')), | ||
| updated_at TEXT NOT NULL DEFAULT (datetime('now')) | ||
| ); | ||
| CREATE UNIQUE INDEX IF NOT EXISTS idx_deployment_primary ON deployment_triggers(site_id) WHERE is_primary = 1; | ||
| CREATE TABLE IF NOT EXISTS deployment_jobs ( | ||
| id TEXT PRIMARY KEY, trigger_id TEXT NOT NULL REFERENCES deployment_triggers(id) ON DELETE CASCADE, | ||
| site_id TEXT NOT NULL REFERENCES sites(id) ON DELETE CASCADE, status TEXT NOT NULL, | ||
| status_code INTEGER, error_category TEXT, response_body TEXT, retry_after_seconds INTEGER, | ||
| duration_ms INTEGER, triggered_by TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')), | ||
| started_at TEXT, finished_at TEXT | ||
| ); | ||
| CREATE INDEX IF NOT EXISTS idx_deployment_jobs_trigger ON deployment_jobs(trigger_id, created_at DESC); | ||
| CREATE UNIQUE INDEX IF NOT EXISTS idx_deployment_jobs_active_trigger | ||
| ON deployment_jobs(trigger_id) WHERE status IN ('queued', 'running'); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.