Migration 0034: two tables. repo_connections carries the workspace's per-provider config (owner, base_url, label, last_synced_at, last_sync_error) and points at an app_connections row for the PAT. repos is the per-connection cache with (connection_id, external_id) unique so upsert is idempotent across re-syncs. Cascading deletes clean up cleanly on connection removal. cm-secrets grows a FetchAuthorized op — GET with the stored PAT injected as bearer, returns status + JSON body without ever exposing the credential to cm-api. This is the least-privilege door for read-only provider APIs (list repos), distinct from the InvokeHttp path that still requires a single-use approval grant for outbound writes. cm-api::routes::repos wires: - POST /api/repos/connections (broker store_secret + insert both rows + initial sync + mark_synced) - GET /api/repos/connections - DELETE /api/repos/connections/:id - POST /api/repos/connections/:id/sync - GET /api/repos (500 cap, newest provider_updated first) - GET /api/repos/:id (full detail incl. clone_url + html_url) GitHub provider inline for v1 — paginated pull of /orgs/:owner/repos (when owner set) or /user/repos (when absent), 100/page, capped at 20 pages (~2k repos) to keep first-sync latency bounded. Non-2xx surface back to the caller as sync_error; parse failures are best-effort per repo (skipped, logged, don't abort the batch). Gitea + GitLab providers land in a follow-up — mostly URL swap + response-shape adapter.
64 lines
2.9 KiB
SQL
64 lines
2.9 KiB
SQL
-- Repos: workspace-scoped cache of the repositories pulled from each
|
|
-- connected provider (GitHub / Gitea / GitLab). One row per (connection,
|
|
-- external_id). The provider PAT itself never lives here — it's already
|
|
-- in the secret broker via app_connections.secret_ref (see 0007).
|
|
--
|
|
-- Sync flow:
|
|
-- POST /api/repos/connections/:id/sync → provider API list → upsert.
|
|
-- Removed-upstream rows stay for now (v1); a future flag can prune them.
|
|
--
|
|
-- repo_connections wraps an app_connections row with the repo-specific
|
|
-- config: which owner (org/user) to pull from, provider base URL for
|
|
-- self-hosted Gitea/GitLab instances, and last-sync bookkeeping.
|
|
|
|
CREATE TABLE repo_connections (
|
|
id UUID PRIMARY KEY,
|
|
workspace_id UUID NOT NULL REFERENCES workspaces (id) ON DELETE CASCADE,
|
|
app_connection_id UUID NOT NULL REFERENCES app_connections (id) ON DELETE CASCADE,
|
|
provider TEXT NOT NULL, -- 'github' | 'gitea' | 'gitlab'
|
|
-- Optional owner (org or user). When NULL, the provider fetches "all
|
|
-- repos the token can see" (e.g. GitHub /user/repos).
|
|
owner TEXT,
|
|
-- Optional base URL for self-hosted providers (e.g. https://git.redclaw.dev).
|
|
base_url TEXT,
|
|
label TEXT NOT NULL DEFAULT '',
|
|
last_synced_at TIMESTAMPTZ,
|
|
last_sync_error TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX repo_connections_workspace_idx
|
|
ON repo_connections (workspace_id, created_at DESC);
|
|
|
|
CREATE TABLE repos (
|
|
id UUID PRIMARY KEY,
|
|
workspace_id UUID NOT NULL REFERENCES workspaces (id) ON DELETE CASCADE,
|
|
connection_id UUID NOT NULL REFERENCES repo_connections (id) ON DELETE CASCADE,
|
|
provider TEXT NOT NULL, -- 'github' | 'gitea' | 'gitlab'
|
|
-- Provider-side stable id (numeric on GitHub, integer on Gitea/GitLab).
|
|
-- Stored as TEXT so we don't care which flavor the provider uses.
|
|
external_id TEXT NOT NULL,
|
|
owner TEXT NOT NULL, -- org or user login
|
|
name TEXT NOT NULL, -- repo slug
|
|
description TEXT,
|
|
default_branch TEXT,
|
|
clone_url TEXT,
|
|
html_url TEXT,
|
|
private BOOLEAN NOT NULL DEFAULT false,
|
|
stars INTEGER NOT NULL DEFAULT 0,
|
|
forks INTEGER NOT NULL DEFAULT 0,
|
|
-- Provider's own `updated_at` (last push / edit), NOT the sync time.
|
|
provider_updated_at TIMESTAMPTZ,
|
|
last_synced_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
|
|
UNIQUE (connection_id, external_id)
|
|
);
|
|
|
|
CREATE INDEX repos_workspace_idx
|
|
ON repos (workspace_id, provider_updated_at DESC NULLS LAST);
|
|
-- Fast per-connection list (sidebar grouping).
|
|
CREATE INDEX repos_connection_idx
|
|
ON repos (connection_id, name);
|