feat(web): api client and pinia store for workspace/worktree

This commit is contained in:
2026-05-02 20:27:51 +08:00
parent 0cadec343c
commit deaa738bf9
4 changed files with 322 additions and 1 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ const BASE = '' // 同源;dev 走 vite proxy
const DEFAULT_TIMEOUT_MS = 30_000
interface RequestOptions {
method?: 'GET' | 'POST' | 'PATCH' | 'DELETE'
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
body?: unknown
signal?: AbortSignal
}
+79
View File
@@ -15,3 +15,82 @@ export class ApiException extends Error {
this.body = body
}
}
export type SyncStatus = 'cloning' | 'idle' | 'syncing' | 'error'
export type WorktreeStatus = 'idle' | 'active' | 'pruning'
export type CredentialKind = 'pat' | 'ssh_key' | 'none'
export interface Workspace {
id: string
project_id: string
slug: string
name: string
description: string
git_remote_url: string
default_branch: string
sync_status: SyncStatus
last_synced_at: string | null
last_sync_error: string
created_at: string
updated_at: string
}
export interface Worktree {
id: string
workspace_id: string
branch: string
path: string
status: WorktreeStatus
active_holder: string
acquired_at: string | null
last_used_at: string
created_at: string
}
export interface CredentialMeta {
kind: CredentialKind
username: string
fingerprint: string
}
export interface CredentialUpsertBody {
kind: CredentialKind
username?: string
secret?: string
}
export interface FileStatus {
path: string
index_x: string
worktree_y: string
}
export interface CreateWorkspaceBody {
slug: string
name: string
description?: string
git_remote_url: string
default_branch?: string
credential: CredentialUpsertBody
}
export interface UpdateWorkspaceBody {
name?: string
description?: string
default_branch?: string
}
export interface CreateWorktreeBody {
branch: string
base_branch?: string
}
export interface CommitBody {
message: string
push?: boolean
add_all?: boolean
}
export interface CommitResp {
sha: string
}
+68
View File
@@ -0,0 +1,68 @@
import { request } from './client'
import type {
Workspace,
Worktree,
CredentialMeta,
CredentialUpsertBody,
FileStatus,
CreateWorkspaceBody,
UpdateWorkspaceBody,
CreateWorktreeBody,
CommitBody,
CommitResp,
} from './types'
const enc = (v: string) => encodeURIComponent(v)
export const workspacesApi = {
// ===== Workspace =====
list: (projectSlug: string) =>
request<Workspace[]>(`/api/v1/projects/${enc(projectSlug)}/workspaces/`),
create: (projectSlug: string, body: CreateWorkspaceBody) =>
request<Workspace>(`/api/v1/projects/${enc(projectSlug)}/workspaces/`, {
method: 'POST',
body,
}),
get: (wsID: string) => request<Workspace>(`/api/v1/workspaces/${enc(wsID)}`),
update: (wsID: string, body: UpdateWorkspaceBody) =>
request<Workspace>(`/api/v1/workspaces/${enc(wsID)}`, { method: 'PATCH', body }),
delete: (wsID: string) =>
request<void>(`/api/v1/workspaces/${enc(wsID)}`, { method: 'DELETE' }),
sync: (wsID: string) =>
request<Workspace>(`/api/v1/workspaces/${enc(wsID)}/sync`, { method: 'POST' }),
// ===== Credential =====
upsertCredential: (wsID: string, body: CredentialUpsertBody) =>
request<CredentialMeta>(`/api/v1/workspaces/${enc(wsID)}/credential`, {
method: 'PUT',
body,
}),
getCredential: (wsID: string) =>
request<CredentialMeta>(`/api/v1/workspaces/${enc(wsID)}/credential`),
// ===== Worktree =====
listWorktrees: (wsID: string) =>
request<Worktree[]>(`/api/v1/workspaces/${enc(wsID)}/worktrees`),
createWorktree: (wsID: string, body: CreateWorktreeBody) =>
request<Worktree>(`/api/v1/workspaces/${enc(wsID)}/worktrees`, { method: 'POST', body }),
deleteWorktree: (wtID: string) =>
request<void>(`/api/v1/worktrees/${enc(wtID)}`, { method: 'DELETE' }),
acquire: (wtID: string) =>
request<Worktree>(`/api/v1/worktrees/${enc(wtID)}/acquire`, { method: 'POST' }),
release: (wtID: string) =>
request<Worktree>(`/api/v1/worktrees/${enc(wtID)}/release`, { method: 'POST' }),
// ===== Git ops =====
statusOnMain: (wsID: string) =>
request<FileStatus[]>(`/api/v1/workspaces/${enc(wsID)}/main/status`),
commitOnMain: (wsID: string, body: CommitBody) =>
request<CommitResp>(`/api/v1/workspaces/${enc(wsID)}/main/commit`, { method: 'POST', body }),
pushOnMain: (wsID: string) =>
request<void>(`/api/v1/workspaces/${enc(wsID)}/main/push`, { method: 'POST' }),
statusOnWorktree: (wtID: string) =>
request<FileStatus[]>(`/api/v1/worktrees/${enc(wtID)}/status`),
commitOnWorktree: (wtID: string, body: CommitBody) =>
request<CommitResp>(`/api/v1/worktrees/${enc(wtID)}/commit`, { method: 'POST', body }),
pushOnWorktree: (wtID: string) =>
request<void>(`/api/v1/worktrees/${enc(wtID)}/push`, { method: 'POST' }),
}