Add multi-tenancy with Teams feature
This commit is contained in:
@@ -36,6 +36,12 @@ import {
|
||||
ArtifactDependenciesResponse,
|
||||
ReverseDependenciesResponse,
|
||||
DependencyResolutionResponse,
|
||||
TeamDetail,
|
||||
TeamMember,
|
||||
TeamCreate,
|
||||
TeamUpdate,
|
||||
TeamMemberCreate,
|
||||
TeamMemberUpdate,
|
||||
} from './types';
|
||||
|
||||
const API_BASE = '/api/v1';
|
||||
@@ -160,7 +166,7 @@ export async function listProjectsSimple(params: ListParams = {}): Promise<Proje
|
||||
return data.items;
|
||||
}
|
||||
|
||||
export async function createProject(data: { name: string; description?: string; is_public?: boolean }): Promise<Project> {
|
||||
export async function createProject(data: { name: string; description?: string; is_public?: boolean; team_id?: string }): Promise<Project> {
|
||||
const response = await fetch(`${API_BASE}/projects`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -562,3 +568,117 @@ export async function getEnsureFile(
|
||||
}
|
||||
return response.text();
|
||||
}
|
||||
|
||||
// Team API
|
||||
export async function listTeams(params: ListParams = {}): Promise<PaginatedResponse<TeamDetail>> {
|
||||
const query = buildQueryString(params as Record<string, unknown>);
|
||||
const response = await fetch(`${API_BASE}/teams${query}`, {
|
||||
credentials: 'include',
|
||||
});
|
||||
return handleResponse<PaginatedResponse<TeamDetail>>(response);
|
||||
}
|
||||
|
||||
export async function createTeam(data: TeamCreate): Promise<TeamDetail> {
|
||||
const response = await fetch(`${API_BASE}/teams`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data),
|
||||
credentials: 'include',
|
||||
});
|
||||
return handleResponse<TeamDetail>(response);
|
||||
}
|
||||
|
||||
export async function getTeam(slug: string): Promise<TeamDetail> {
|
||||
const response = await fetch(`${API_BASE}/teams/${slug}`, {
|
||||
credentials: 'include',
|
||||
});
|
||||
return handleResponse<TeamDetail>(response);
|
||||
}
|
||||
|
||||
export async function updateTeam(slug: string, data: TeamUpdate): Promise<TeamDetail> {
|
||||
const response = await fetch(`${API_BASE}/teams/${slug}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data),
|
||||
credentials: 'include',
|
||||
});
|
||||
return handleResponse<TeamDetail>(response);
|
||||
}
|
||||
|
||||
export async function deleteTeam(slug: string): Promise<void> {
|
||||
const response = await fetch(`${API_BASE}/teams/${slug}`, {
|
||||
method: 'DELETE',
|
||||
credentials: 'include',
|
||||
});
|
||||
if (!response.ok) {
|
||||
const error = await response.json().catch(() => ({ detail: 'Unknown error' }));
|
||||
throw new ApiError(error.detail || `HTTP ${response.status}`, response.status);
|
||||
}
|
||||
}
|
||||
|
||||
export async function listTeamMembers(slug: string): Promise<TeamMember[]> {
|
||||
const response = await fetch(`${API_BASE}/teams/${slug}/members`, {
|
||||
credentials: 'include',
|
||||
});
|
||||
return handleResponse<TeamMember[]>(response);
|
||||
}
|
||||
|
||||
export async function addTeamMember(slug: string, data: TeamMemberCreate): Promise<TeamMember> {
|
||||
const response = await fetch(`${API_BASE}/teams/${slug}/members`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data),
|
||||
credentials: 'include',
|
||||
});
|
||||
return handleResponse<TeamMember>(response);
|
||||
}
|
||||
|
||||
export async function updateTeamMember(
|
||||
slug: string,
|
||||
username: string,
|
||||
data: TeamMemberUpdate
|
||||
): Promise<TeamMember> {
|
||||
const response = await fetch(`${API_BASE}/teams/${slug}/members/${username}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data),
|
||||
credentials: 'include',
|
||||
});
|
||||
return handleResponse<TeamMember>(response);
|
||||
}
|
||||
|
||||
export async function removeTeamMember(slug: string, username: string): Promise<void> {
|
||||
const response = await fetch(`${API_BASE}/teams/${slug}/members/${username}`, {
|
||||
method: 'DELETE',
|
||||
credentials: 'include',
|
||||
});
|
||||
if (!response.ok) {
|
||||
const error = await response.json().catch(() => ({ detail: 'Unknown error' }));
|
||||
throw new ApiError(error.detail || `HTTP ${response.status}`, response.status);
|
||||
}
|
||||
}
|
||||
|
||||
export async function listTeamProjects(
|
||||
slug: string,
|
||||
params: ProjectListParams = {}
|
||||
): Promise<PaginatedResponse<Project>> {
|
||||
const query = buildQueryString(params as Record<string, unknown>);
|
||||
const response = await fetch(`${API_BASE}/teams/${slug}/projects${query}`, {
|
||||
credentials: 'include',
|
||||
});
|
||||
return handleResponse<PaginatedResponse<Project>>(response);
|
||||
}
|
||||
|
||||
// User search (for autocomplete)
|
||||
export interface UserSearchResult {
|
||||
id: string;
|
||||
username: string;
|
||||
is_admin: boolean;
|
||||
}
|
||||
|
||||
export async function searchUsers(query: string, limit: number = 10): Promise<UserSearchResult[]> {
|
||||
const response = await fetch(`${API_BASE}/users/search?q=${encodeURIComponent(query)}&limit=${limit}`, {
|
||||
credentials: 'include',
|
||||
});
|
||||
return handleResponse<UserSearchResult[]>(response);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user