Newer
Older
bugtrail / packages / web / src / api.ts
import {
  type AnnotationShape,
  type Project,
  type ProjectDetail,
  type ReportCreateIn,
  type ReportDetail,
  type ReportList,
  type ReportUpdateIn,
  type TokenOut,
  type UploadResult,
  type User,
  createHttpClient,
  type HttpClient,
} from "@ltt/shared";

export type { Project, ProjectDetail, ReportDetail, ReportList, UploadResult, User };

/** Same-origin client: auth rides the httpOnly session cookie. */
export const http: HttpClient = createHttpClient({
  onUnauthorized: () => {
    // lazy import to avoid a store <-> api cycle
    import("./stores/auth").then(({ useAuth }) => useAuth().clear());
  },
});

export async function register(nickname: string, email: string, password: string): Promise<User> {
  return http.post<User>("/api/auth/register", { nickname, email, password });
}

export async function login(email: string, password: string): Promise<User> {
  return http.post<User>("/api/auth/login", { email, password });
}

export async function loginExtension(email: string, password: string): Promise<TokenOut> {
  return http.post<TokenOut>("/api/auth/login", { email, password }, );
}

export async function logout(): Promise<void> {
  await http.post("/api/auth/logout");
}

export async function me(): Promise<User> {
  return http.get<User>("/api/auth/me");
}

export async function listProjects(): Promise<Project[]> {
  return http.get<Project[]>("/api/projects");
}

export async function createProject(name: string, description: string | null): Promise<Project> {
  return http.post<Project>("/api/projects", { name, description });
}

export async function getProjectByToken(token: string): Promise<ProjectDetail> {
  return http.get<ProjectDetail>(`/api/projects/by-token/${encodeURIComponent(token)}`);
}

export async function rotateProjectShare(projectId: string): Promise<Project> {
  return http.post<Project>(`/api/projects/${projectId}/share/rotate`);
}

export async function deleteProject(projectId: string): Promise<void> {
  await http.del(`/api/projects/${projectId}`);
}

export interface ReportListParams {
  limit?: number;
  offset?: number;
  report_type?: string | null;
  report_status?: string | null;
}

export async function listProjectReports(projectId: string, params: ReportListParams = {}): Promise<ReportList> {
  const query = new URLSearchParams();
  query.set("limit", String(params.limit ?? 50));
  query.set("offset", String(params.offset ?? 0));
  if (params.report_type) query.set("report_type", params.report_type);
  if (params.report_status) query.set("report_status", params.report_status);
  return http.get<ReportList>(`/api/projects/${projectId}/reports?${query}`);
}

export async function createReport(body: ReportCreateIn): Promise<ReportDetail> {
  return http.post<ReportDetail>("/api/reports", body);
}

export async function getReport(token: string): Promise<ReportDetail> {
  return http.get<ReportDetail>(`/api/reports/${encodeURIComponent(token)}`);
}

export async function updateReport(token: string, body: ReportUpdateIn): Promise<ReportDetail> {
  return http.patch<ReportDetail>(`/api/reports/${encodeURIComponent(token)}`, body);
}

export async function deleteReport(token: string): Promise<void> {
  await http.del(`/api/reports/${encodeURIComponent(token)}`);
}

export async function rotateReportShare(token: string): Promise<ReportDetail> {
  return http.post<ReportDetail>(`/api/reports/${encodeURIComponent(token)}/share/rotate`);
}

export async function updateAttachmentShapes(
  token: string,
  attachmentId: string,
  shapes: AnnotationShape[] | null
): Promise<void> {
  await http.patch(`/api/reports/${encodeURIComponent(token)}/attachments/${attachmentId}`, {
    annotation_shapes: shapes,
  });
}

export async function uploadFile(file: File): Promise<UploadResult> {
  return http.upload<UploadResult>("/api/uploads", file);
}

export function reportFileUrl(reportToken: string, fileId: string): string {
  return `/api/reports/by-token/${encodeURIComponent(reportToken)}/files/${fileId}`;
}

export function projectShareUrl(project: Project): string {
  return `${window.location.origin}/p/${project.share_token}`;
}

export function reportShareUrl(report: { share_token: string }): string {
  return `${window.location.origin}/r/${report.share_token}`;
}