main
UI Deploy (Next-Auth Support) 🎨 / build-and-deploy (push) Has been cancelled

This commit is contained in:
Harun CAN
2026-03-29 12:44:02 +03:00
parent fe9aff3fec
commit 45a540c530
26 changed files with 10706 additions and 86 deletions
+288 -17
View File
@@ -1,22 +1,293 @@
import { clientMap } from '@/lib/api/client-map';
import { Method } from 'axios';
import { createApiClient } from './create-api-client';
interface ApiRequestOptions {
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000/api';
export const apiClient = createApiClient(API_URL);
// ── Type Definitions ─────────────────────────────────────────────────
export interface Project {
id: string;
title: string;
description?: string;
prompt: string;
status: ProjectStatus;
progress: number;
language: string;
aspectRatio: string;
videoStyle: string;
targetDuration: number;
creditsUsed: number;
thumbnailUrl?: string;
finalVideoUrl?: string;
errorMessage?: string;
scriptJson?: ScriptJson;
scriptVersion: number;
scenes?: Scene[];
renderJobs?: RenderJob[];
sourceType?: 'MANUAL' | 'X_TWEET' | 'YOUTUBE';
sourceTweetData?: Record<string, unknown>;
createdAt: string;
updatedAt: string;
completedAt?: string;
}
export type ProjectStatus =
| 'DRAFT'
| 'GENERATING_SCRIPT'
| 'PENDING'
| 'GENERATING_MEDIA'
| 'RENDERING'
| 'COMPLETED'
| 'FAILED';
export interface Scene {
id: string;
order: number;
title?: string;
narrationText: string;
visualPrompt: string;
subtitleText?: string;
duration: number;
transitionType: string;
mediaAssets?: MediaAsset[];
}
export interface MediaAsset {
id: string;
type: string;
url?: string;
fileName?: string;
mimeType?: string;
sizeBytes?: number;
durationMs?: number;
aiProvider?: string;
}
export interface RenderJob {
id: string;
status: string;
currentStage?: string;
attemptNumber: number;
processingTimeMs?: number;
errorMessage?: string;
finalVideoUrl?: string;
createdAt: string;
startedAt?: string;
completedAt?: string;
logs?: RenderLog[];
}
export interface RenderLog {
id: string;
stage: string;
level: string;
message: string;
durationMs?: number;
createdAt: string;
}
export interface ScriptJson {
metadata: {
title: string;
description: string;
totalDurationSeconds: number;
language: string;
hashtags: string[];
};
seo?: {
title: string;
description: string;
keywords: string[];
hashtags: string[];
schemaMarkup: Record<string, unknown>;
};
scenes: Array<{
order: number;
title?: string;
narrationText: string;
visualPrompt: string;
subtitleText: string;
durationSeconds: number;
transitionType: string;
}>;
musicPrompt: string;
voiceStyle: string;
socialContent?: {
youtubeTitle: string;
youtubeDescription: string;
tiktokCaption: string;
instagramCaption: string;
twitterText: string;
};
}
export interface PaginatedResponse<T> {
data: T[];
meta: {
total: number;
page: number;
limit: number;
totalPages: number;
};
}
export interface CreateProjectPayload {
title: string;
description?: string;
prompt: string;
language?: string;
aspectRatio?: string;
videoStyle?: string;
targetDuration?: number;
seoKeywords?: string[];
referenceUrl?: string;
}
export interface CreditBalance {
balance: number;
monthlyUsed: number;
monthlyLimit: number;
}
export interface Template {
id: string;
title: string;
description?: string;
thumbnailUrl?: string;
previewVideoUrl?: string;
category: string;
tags: string[];
language: string;
usageCount: number;
rating: number;
isFeatured: boolean;
}
export interface DashboardStats {
totalProjects: number;
completedVideos: number;
totalCreditsUsed: number;
creditsRemaining: number;
activeRenderJobs: number;
recentProjects: Project[];
}
// Tweet Types
export interface TweetAuthor {
id: string;
name: string;
username: string;
avatarUrl: string;
followersCount: number;
verified: boolean;
}
export interface TweetMetrics {
replies: number;
retweets: number;
likes: number;
views: number;
engagementRate: number;
}
export interface TweetMedia {
type: 'photo' | 'video' | 'gif';
url: string;
client: keyof typeof clientMap;
method?: Method;
data?: any;
params?: Record<string, any>;
thumbnailUrl?: string;
width: number;
height: number;
}
export async function apiRequest<T = any>(options: ApiRequestOptions): Promise<T> {
const { url, client, method = 'get', data, params } = options;
const clientInstance = clientMap[client];
if (!url || !clientInstance) {
throw new Error(`Invalid API request: ${client} - ${url}`);
}
const response = await clientInstance.request<T>({ method, url, data, params });
return response.data;
export interface ParsedTweet {
id: string;
url: string;
text: string;
createdAt: string;
author: TweetAuthor;
metrics: TweetMetrics;
media: TweetMedia[];
quotedTweet?: ParsedTweet;
isThread: boolean;
threadTweets?: ParsedTweet[];
}
export interface TweetPreview {
tweet: ParsedTweet;
suggestedTitle: string;
suggestedPrompt: string;
viralScore: number;
contentType: 'tweet' | 'thread' | 'quote_tweet';
estimatedDuration: number;
}
export interface CreateFromTweetPayload {
tweetUrl: string;
title?: string;
language?: string;
aspectRatio?: string;
videoStyle?: string;
targetDuration?: number;
}
// ── API Functions ────────────────────────────────────────────────────
export const projectsApi = {
list: (params?: { page?: number; limit?: number; status?: string }) =>
apiClient.get<PaginatedResponse<Project>>('/projects', { params }).then((r) => r.data),
get: (id: string) =>
apiClient.get<Project>(`/projects/${id}`).then((r) => r.data),
create: (data: CreateProjectPayload) =>
apiClient.post<Project>('/projects', data).then((r) => r.data),
update: (id: string, data: Partial<CreateProjectPayload>) =>
apiClient.patch<Project>(`/projects/${id}`, data).then((r) => r.data),
delete: (id: string) =>
apiClient.delete(`/projects/${id}`).then((r) => r.data),
generateScript: (id: string) =>
apiClient.post<Project>(`/projects/${id}/generate-script`).then((r) => r.data),
approveAndQueue: (id: string) =>
apiClient.post<{ projectId: string; renderJobId: string; bullJobId: string }>(
`/projects/${id}/approve-and-queue`,
).then((r) => r.data),
createFromTweet: (data: CreateFromTweetPayload) =>
apiClient.post<Project>('/projects/from-tweet', data).then((r) => r.data),
};
export const creditsApi = {
getBalance: () =>
apiClient.get<CreditBalance>('/credits/balance').then((r) => r.data),
getHistory: (params?: { page?: number; limit?: number }) =>
apiClient.get('/credits/history', { params }).then((r) => r.data),
};
export const templatesApi = {
list: (params?: { category?: string; language?: string; page?: number; limit?: number }) =>
apiClient.get<PaginatedResponse<Template>>('/templates', { params }).then((r) => r.data),
get: (id: string) =>
apiClient.get<Template>(`/templates/${id}`).then((r) => r.data),
clone: (id: string) =>
apiClient.post<Project>(`/templates/${id}/clone`).then((r) => r.data),
};
export const dashboardApi = {
getStats: () =>
apiClient.get<DashboardStats>('/dashboard/stats').then((r) => r.data),
};
export const xTwitterApi = {
preview: (tweetUrl: string) =>
apiClient.post<TweetPreview>('/x-twitter/preview', { tweetUrl }).then((r) => r.data),
fetch: (tweetUrl: string) =>
apiClient.post<ParsedTweet>('/x-twitter/fetch', { tweetUrl }).then((r) => r.data),
};