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}`);
}
/** Owner-free variant of listProjectReports: the project page must work
* from a share link without a session (same data, addressed by token). */
export async function listProjectReportsByToken(token: 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/by-token/${encodeURIComponent(token)}/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}`;
}