Prefer error_code when present and route projectMembers/freeformDoc/ taskRuns/chatSessions failures through the shared structured parser. Co-authored-by: Cursor <cursoragent@cursor.com>
87 lines
2.6 KiB
TypeScript
87 lines
2.6 KiB
TypeScript
import { getBearerAuthHeaders } from './authHeaders';
|
|
import { projectSettingsErrorFromResponse } from './projectSettingsError';
|
|
|
|
export type ApiProjectMemberRole = 'owner' | 'member' | 'viewer';
|
|
export type ApiProjectMemberStatus = 'active' | 'exited';
|
|
export type ApiDocumentDuty = 'author' | 'reviewer' | 'approver' | '';
|
|
|
|
export interface ApiProjectMemberRecord {
|
|
id?: number;
|
|
project_id?: number;
|
|
user_id: number;
|
|
username?: string;
|
|
name?: string;
|
|
project_role: ApiProjectMemberRole;
|
|
document_duty?: ApiDocumentDuty;
|
|
status: ApiProjectMemberStatus;
|
|
joined_at?: string;
|
|
}
|
|
|
|
export interface ListProjectMembersResponse {
|
|
members: ApiProjectMemberRecord[];
|
|
}
|
|
|
|
export interface UpsertProjectMemberPayload {
|
|
user_id: number;
|
|
project_role: ApiProjectMemberRole;
|
|
document_duty?: ApiDocumentDuty;
|
|
status?: ApiProjectMemberStatus;
|
|
}
|
|
|
|
async function memberApiError(res: Response, fallback: string) {
|
|
const text = await res.text();
|
|
return projectSettingsErrorFromResponse(res.status, text, fallback);
|
|
}
|
|
|
|
export async function listProjectMembers(projectId: number): Promise<ApiProjectMemberRecord[]> {
|
|
const res = await fetch(`/api/v1/projects/${projectId}/members`, {
|
|
headers: getBearerAuthHeaders(),
|
|
});
|
|
if (!res.ok) {
|
|
throw await memberApiError(res, `获取项目成员失败: ${res.status}`);
|
|
}
|
|
const data: ListProjectMembersResponse = await res.json();
|
|
return data.members ?? [];
|
|
}
|
|
|
|
export async function addProjectMember(
|
|
projectId: number,
|
|
payload: UpsertProjectMemberPayload
|
|
): Promise<void> {
|
|
const res = await fetch(`/api/v1/projects/${projectId}/members`, {
|
|
method: 'POST',
|
|
headers: getBearerAuthHeaders('application/json'),
|
|
body: JSON.stringify(payload),
|
|
});
|
|
if (!res.ok) {
|
|
throw await memberApiError(res, `添加项目成员失败: ${res.status}`);
|
|
}
|
|
}
|
|
|
|
export async function updateProjectMember(
|
|
projectId: number,
|
|
payload: UpsertProjectMemberPayload
|
|
): Promise<void> {
|
|
const res = await fetch(`/api/v1/projects/${projectId}/members`, {
|
|
method: 'PUT',
|
|
headers: getBearerAuthHeaders('application/json'),
|
|
body: JSON.stringify(payload),
|
|
});
|
|
if (!res.ok) {
|
|
throw await memberApiError(res, `更新项目成员失败: ${res.status}`);
|
|
}
|
|
}
|
|
|
|
export async function removeProjectMember(projectId: number, userId: number): Promise<void> {
|
|
const res = await fetch(
|
|
`/api/v1/projects/${projectId}/members?user_id=${encodeURIComponent(String(userId))}`,
|
|
{
|
|
method: 'DELETE',
|
|
headers: getBearerAuthHeaders(),
|
|
}
|
|
);
|
|
if (!res.ok) {
|
|
throw await memberApiError(res, `移出项目成员失败: ${res.status}`);
|
|
}
|
|
}
|