feat: add AirShelf core implementation
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
import type {
|
||||
AITask,
|
||||
Asset,
|
||||
AuthPayload,
|
||||
BillingSummary,
|
||||
Ledger,
|
||||
ModelConfig,
|
||||
Paginated,
|
||||
Product,
|
||||
Project,
|
||||
ScriptVersion,
|
||||
Team,
|
||||
TeamMember,
|
||||
User
|
||||
} from "./types";
|
||||
|
||||
const API_BASE = import.meta.env.VITE_API_BASE_URL || "";
|
||||
const TOKEN_KEY = "airshelf_token";
|
||||
|
||||
export class ApiError extends Error {
|
||||
status: number;
|
||||
|
||||
constructor(status: number, message: string) {
|
||||
super(message);
|
||||
this.name = "ApiError";
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
|
||||
export function getToken() {
|
||||
return localStorage.getItem(TOKEN_KEY);
|
||||
}
|
||||
|
||||
export function setToken(token: string | null) {
|
||||
if (token) localStorage.setItem(TOKEN_KEY, token);
|
||||
else localStorage.removeItem(TOKEN_KEY);
|
||||
}
|
||||
|
||||
async function request<T>(path: string, options: RequestInit = {}): Promise<T> {
|
||||
const token = getToken();
|
||||
const headers = new Headers(options.headers);
|
||||
if (!(options.body instanceof FormData)) {
|
||||
headers.set("Content-Type", "application/json");
|
||||
}
|
||||
if (token) headers.set("Authorization", `Token ${token}`);
|
||||
|
||||
const response = await fetch(`${API_BASE}${path}`, { ...options, headers });
|
||||
if (!response.ok) {
|
||||
const text = await response.text();
|
||||
throw new ApiError(response.status, text || `${response.status} ${response.statusText}`);
|
||||
}
|
||||
if (response.status === 204) return undefined as T;
|
||||
return response.json() as Promise<T>;
|
||||
}
|
||||
|
||||
export const api = {
|
||||
register(payload: { username: string; password: string; email?: string; team_name?: string }) {
|
||||
return request<AuthPayload>("/api/auth/register/", { method: "POST", body: JSON.stringify(payload) });
|
||||
},
|
||||
login(payload: { username: string; password: string }) {
|
||||
return request<AuthPayload>("/api/auth/login/", { method: "POST", body: JSON.stringify(payload) });
|
||||
},
|
||||
me() {
|
||||
return request<{ user: User; team: Team }>("/api/auth/me/");
|
||||
},
|
||||
logout() {
|
||||
return request<void>("/api/auth/logout/", { method: "POST" });
|
||||
},
|
||||
teamMembers() {
|
||||
return request<TeamMember[]>("/api/auth/team/members/");
|
||||
},
|
||||
products() {
|
||||
return request<Paginated<Product>>("/api/products/");
|
||||
},
|
||||
product(id: string) {
|
||||
return request<Product>(`/api/products/${id}/`);
|
||||
},
|
||||
createProduct(payload: {
|
||||
title?: string;
|
||||
brand?: string;
|
||||
category?: string;
|
||||
target_audience?: string;
|
||||
description?: string;
|
||||
specs?: Record<string, unknown>;
|
||||
selling_points?: Array<{ title: string; detail: string; sort_order: number }>;
|
||||
}) {
|
||||
return request<Product>("/api/products/", { method: "POST", body: JSON.stringify(payload) });
|
||||
},
|
||||
updateProduct(id: string, payload: Partial<Product>) {
|
||||
return request<Product>(`/api/products/${id}/`, { method: "PATCH", body: JSON.stringify(payload) });
|
||||
},
|
||||
deleteProduct(id: string) {
|
||||
return request<void>(`/api/products/${id}/`, { method: "DELETE" });
|
||||
},
|
||||
projects() {
|
||||
return request<Paginated<Project>>("/api/projects/");
|
||||
},
|
||||
project(id: string) {
|
||||
return request<Project>(`/api/projects/${id}/`);
|
||||
},
|
||||
createProject(payload: { name: string; product: string }) {
|
||||
return request<Project>("/api/projects/", { method: "POST", body: JSON.stringify(payload) });
|
||||
},
|
||||
deleteProject(id: string) {
|
||||
return request<void>(`/api/projects/${id}/`, { method: "DELETE" });
|
||||
},
|
||||
generateScript(projectId: string, payload: { prompt: string; selling_point_ids?: string[] }) {
|
||||
return request<ScriptVersion>(`/api/projects/${projectId}/generate-script/`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
},
|
||||
adoptScript(projectId: string, script_version_id: string) {
|
||||
return request<ScriptVersion>(`/api/projects/${projectId}/adopt-script/`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ script_version_id })
|
||||
});
|
||||
},
|
||||
generateBaseAsset(projectId: string, payload: { kind: "product" | "person" | "scene"; prompt: string }) {
|
||||
return request(`/api/projects/${projectId}/generate-base-asset/`, { method: "POST", body: JSON.stringify(payload) });
|
||||
},
|
||||
adoptBaseAsset(projectId: string, payload: { group_id: string; asset_id: string }) {
|
||||
return request(`/api/projects/${projectId}/adopt-base-asset/`, { method: "POST", body: JSON.stringify(payload) });
|
||||
},
|
||||
generateStoryboard(projectId: string, payload: { prompt: string }) {
|
||||
return request(`/api/projects/${projectId}/generate-storyboard/`, { method: "POST", body: JSON.stringify(payload) });
|
||||
},
|
||||
skipStoryboard(projectId: string) {
|
||||
return request<Project>(`/api/projects/${projectId}/skip-storyboard/`, { method: "POST" });
|
||||
},
|
||||
submitVideo(projectId: string, payload: { video_segment_id: string; prompt: string }) {
|
||||
return request<Project>(`/api/projects/${projectId}/submit-video-segment/`, { method: "POST", body: JSON.stringify(payload) });
|
||||
},
|
||||
pollVideo(projectId: string, video_segment_id: string) {
|
||||
return request(`/api/projects/${projectId}/poll-video-segment/`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ video_segment_id })
|
||||
});
|
||||
},
|
||||
submitExport(projectId: string) {
|
||||
return request(`/api/projects/${projectId}/submit-export/`, { method: "POST" });
|
||||
},
|
||||
assets() {
|
||||
return request<Paginated<Asset>>("/api/assets/");
|
||||
},
|
||||
uploadAsset(formData: FormData) {
|
||||
return request<Asset>("/api/assets/upload/", { method: "POST", body: formData });
|
||||
},
|
||||
billingSummary() {
|
||||
return request<BillingSummary>("/api/billing/summary/");
|
||||
},
|
||||
ledgers() {
|
||||
return request<Ledger[]>("/api/billing/ledgers/");
|
||||
},
|
||||
modelConfigs() {
|
||||
return request<Paginated<ModelConfig>>("/api/ai/models/");
|
||||
},
|
||||
aiTasks() {
|
||||
return request<Paginated<AITask>>("/api/ai/tasks/");
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user