mirror of
https://github.com/multica-ai/multica.git
synced 2026-06-17 03:38:32 +02:00
* feat(github): GitHub App backend for PR ↔ issue linking - New tables: github_installation (workspace ↔ App install), github_pull_request (mirrored PR state), issue_pull_request (M:N link). - Webhook handler verifies HMAC-SHA256, upserts PR rows, parses issue identifiers from PR title/body/branch and auto-links them. Merging a linked PR moves the issue to done. - Connect/setup endpoints power the zero-config "Connect GitHub" install flow; state token is HMAC-signed so the setup callback can recover the workspace. - Workspace-scoped admin routes for listing/disconnecting installations, plus a per-issue `pull-requests` list endpoint. Co-authored-by: multica-agent <github@multica.ai> * feat(github): UI for connecting GitHub and viewing linked PRs - Settings → Integrations: new tab with Connect GitHub / installations list / disconnect, gated on the deployment having the App configured. - Issue detail sidebar: Pull requests section showing linked PR title, repo, state (open/draft/merged/closed), and author, with deep link to GitHub. - Real-time refresh: github_installation:* and pull_request:* events invalidate the matching TanStack Query caches. Co-authored-by: multica-agent <github@multica.ai> * fix(github): address review — null actor, role gating, configured guard, scoped uninstall broadcast - listeners: use optionalUUID(e.ActorID) so the system actor on the github-driven issue:updated event no longer panics activity / notification listeners; merged-PR → issue done now produces a status_changed activity and inbox entry. - IntegrationsTab: gate the admin-only installations query on canManage so members no longer hit /github/installations 403; the configured/not-configured copy is also scoped to admins. - backend: introduce isGitHubConfigured() requiring both GITHUB_APP_SLUG and GITHUB_WEBHOOK_SECRET, and surface that single flag from list-installations + connect endpoints so the frontend Connect button stays disabled until both are set. - DeleteGitHubInstallationByInstallationID now RETURNs workspace_id; webhook handler publishes github_installation:deleted scoped to the right workspace so already-open Settings tabs invalidate in real time. ErrNoRows on a re-fired delete short-circuits cleanly. - tests: focused webhook integration coverage (auto-link + merge → done, cancelled preservation, uninstall returns workspace). Co-authored-by: multica-agent <github@multica.ai> * fix(github): i18n the new GitHub UI strings to satisfy lint CI flagged every literal string in the Integrations tab, the Pull requests sidebar section, and the per-PR row label. Move them through useT() and add the matching `integrations.*` block to settings.json (en / zh-Hans) plus `detail.section_pull_requests` / `detail.pull_request_state_*` / loading + empty copy under `issues.json`. Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: multica-agent <github@multica.ai>
55 lines
2.2 KiB
SQL
55 lines
2.2 KiB
SQL
-- GitHub App integration: connected installations, mirrored pull request state,
|
|
-- and the link table joining issues ↔ PRs.
|
|
|
|
CREATE TABLE github_installation (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
installation_id BIGINT NOT NULL,
|
|
account_login TEXT NOT NULL,
|
|
account_type TEXT NOT NULL DEFAULT 'User'
|
|
CHECK (account_type IN ('User', 'Organization')),
|
|
account_avatar_url TEXT,
|
|
connected_by_id UUID REFERENCES "user"(id) ON DELETE SET NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
UNIQUE (installation_id)
|
|
);
|
|
|
|
CREATE INDEX idx_github_installation_workspace ON github_installation(workspace_id);
|
|
|
|
CREATE TABLE github_pull_request (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
installation_id BIGINT NOT NULL,
|
|
repo_owner TEXT NOT NULL,
|
|
repo_name TEXT NOT NULL,
|
|
pr_number INTEGER NOT NULL,
|
|
title TEXT NOT NULL,
|
|
state TEXT NOT NULL
|
|
CHECK (state IN ('open', 'closed', 'merged', 'draft')),
|
|
html_url TEXT NOT NULL,
|
|
branch TEXT,
|
|
author_login TEXT,
|
|
author_avatar_url TEXT,
|
|
merged_at TIMESTAMPTZ,
|
|
closed_at TIMESTAMPTZ,
|
|
pr_created_at TIMESTAMPTZ NOT NULL,
|
|
pr_updated_at TIMESTAMPTZ NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
UNIQUE (workspace_id, repo_owner, repo_name, pr_number)
|
|
);
|
|
|
|
CREATE INDEX idx_github_pull_request_workspace ON github_pull_request(workspace_id);
|
|
|
|
CREATE TABLE issue_pull_request (
|
|
issue_id UUID NOT NULL REFERENCES issue(id) ON DELETE CASCADE,
|
|
pull_request_id UUID NOT NULL REFERENCES github_pull_request(id) ON DELETE CASCADE,
|
|
linked_by_type TEXT,
|
|
linked_by_id UUID,
|
|
linked_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
PRIMARY KEY (issue_id, pull_request_id)
|
|
);
|
|
|
|
CREATE INDEX idx_issue_pull_request_pr ON issue_pull_request(pull_request_id);
|