feat: add AirShelf core implementation

This commit is contained in:
zyc
2026-06-05 10:21:40 +08:00
parent 2ba1058329
commit cfdcd84a30
252 changed files with 70828 additions and 0 deletions
+161
View File
@@ -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/");
}
};