refactor: restructure

monorepo with apps/ and services/
  directories
This commit is contained in:
Wuesteon 2025-11-26 03:03:24 +01:00
parent 25824ed0ac
commit ff80aeec1f
4062 changed files with 2592 additions and 1278 deletions

View file

@ -0,0 +1 @@
export * from './supabase'

View file

@ -0,0 +1,8 @@
import { createClient } from '@supabase/supabase-js'
import type { Database } from '../types/database.types'
export function createSupabaseClient(supabaseUrl: string, supabaseAnonKey: string) {
return createClient<Database>(supabaseUrl, supabaseAnonKey)
}
export type SupabaseClient = ReturnType<typeof createSupabaseClient>

View file

@ -0,0 +1,4 @@
export * from './types'
export * from './api'
export * from './utils'
export * from './queue'

View file

@ -0,0 +1,534 @@
/**
* Job Queue Helper Functions
*
* Provides client-side utilities for interacting with the async job queue system.
* Uses Supabase database functions to enqueue jobs and monitor status.
*/
import type { SupabaseClient } from '@supabase/supabase-js';
import type { Database } from './types';
// ============================================================================
// TYPES
// ============================================================================
export type JobType = 'generate-image' | 'download-image' | 'process-webhook' | 'cleanup-storage';
export type JobStatus = 'pending' | 'processing' | 'completed' | 'failed' | 'cancelled';
export interface JobQueueRow {
id: string;
job_type: JobType;
payload: Record<string, any>;
status: JobStatus;
attempts: number;
max_attempts: number;
scheduled_at: string;
started_at: string | null;
completed_at: string | null;
error_message: string | null;
error_details: Record<string, any> | null;
created_by: string | null;
priority: number;
created_at: string;
updated_at: string;
}
export interface EnqueueJobParams {
jobType: JobType;
payload: Record<string, any>;
priority?: number;
scheduledAt?: Date;
maxAttempts?: number;
}
export interface JobStats {
total: number;
pending: number;
processing: number;
completed: number;
failed: number;
avgDurationSeconds: number;
}
// ============================================================================
// QUEUE FUNCTIONS
// ============================================================================
/**
* Enqueue a new job for background processing
*
* @example
* ```typescript
* const jobId = await enqueueJob(supabase, {
* jobType: 'generate-image',
* payload: { prompt: 'A beautiful sunset', model_id: 'flux-dev' },
* priority: 10
* });
* ```
*/
export async function enqueueJob(
supabase: SupabaseClient<Database>,
params: EnqueueJobParams
): Promise<string> {
const {
jobType,
payload,
priority = 0,
scheduledAt = new Date(),
maxAttempts = 3
} = params;
const { data, error } = await supabase.rpc('enqueue_job', {
p_job_type: jobType,
p_payload: payload as any,
p_priority: priority,
p_scheduled_at: scheduledAt.toISOString(),
p_max_attempts: maxAttempts
});
if (error) {
console.error('Failed to enqueue job:', error);
throw new Error(`Failed to enqueue job: ${error.message}`);
}
return data as string;
}
/**
* Get job by ID
*/
export async function getJob(
supabase: SupabaseClient<Database>,
jobId: string
): Promise<JobQueueRow | null> {
const { data, error } = await supabase
.from('job_queue')
.select('*')
.eq('id', jobId)
.single();
if (error) {
if (error.code === 'PGRST116') {
return null; // Not found
}
throw error;
}
return data as JobQueueRow;
}
/**
* Get all jobs for current user
*/
export async function getUserJobs(
supabase: SupabaseClient<Database>,
options?: {
status?: JobStatus;
limit?: number;
offset?: number;
}
): Promise<JobQueueRow[]> {
let query = supabase
.from('job_queue')
.select('*')
.order('created_at', { ascending: false });
if (options?.status) {
query = query.eq('status', options.status);
}
if (options?.limit) {
query = query.limit(options.limit);
}
if (options?.offset) {
query = query.range(options.offset, options.offset + (options.limit || 10) - 1);
}
const { data, error } = await query;
if (error) {
throw error;
}
return (data || []) as JobQueueRow[];
}
/**
* Cancel a pending job
*/
export async function cancelJob(
supabase: SupabaseClient<Database>,
jobId: string
): Promise<void> {
const { error } = await supabase
.from('job_queue')
.update({ status: 'cancelled', updated_at: new Date().toISOString() })
.eq('id', jobId)
.eq('status', 'pending'); // Only cancel pending jobs
if (error) {
throw new Error(`Failed to cancel job: ${error.message}`);
}
}
/**
* Get queue health statistics
*/
export async function getQueueStats(
supabase: SupabaseClient<Database>,
jobType?: JobType
): Promise<JobStats> {
const { data, error } = await supabase
.from('queue_health')
.select('*');
if (error) {
throw error;
}
// Aggregate stats
let stats: JobStats = {
total: 0,
pending: 0,
processing: 0,
completed: 0,
failed: 0,
avgDurationSeconds: 0
};
const filtered = jobType
? data?.filter(row => row.job_type === jobType)
: data;
filtered?.forEach(row => {
const count = row.count || 0;
stats.total += count;
switch (row.status) {
case 'pending':
stats.pending += count;
break;
case 'processing':
stats.processing += count;
break;
case 'completed':
stats.completed += count;
break;
case 'failed':
stats.failed += count;
break;
}
if (row.avg_duration_seconds) {
stats.avgDurationSeconds = row.avg_duration_seconds;
}
});
return stats;
}
/**
* Get failed jobs (last 24 hours)
*/
export async function getRecentFailedJobs(
supabase: SupabaseClient<Database>
): Promise<JobQueueRow[]> {
const { data, error } = await supabase
.from('failed_jobs_recent')
.select('*');
if (error) {
throw error;
}
return (data || []) as JobQueueRow[];
}
// ============================================================================
// REALTIME SUBSCRIPTION HELPERS
// ============================================================================
export type JobUpdateCallback = (job: JobQueueRow) => void;
/**
* Subscribe to job updates via Realtime
*
* @example
* ```typescript
* const unsubscribe = subscribeToJob(supabase, jobId, (job) => {
* console.log('Job updated:', job.status);
* if (job.status === 'completed') {
* unsubscribe();
* }
* });
* ```
*/
export function subscribeToJob(
supabase: SupabaseClient<Database>,
jobId: string,
callback: JobUpdateCallback
): () => void {
const channel = supabase
.channel(`job:${jobId}`)
.on(
'postgres_changes',
{
event: 'UPDATE',
schema: 'public',
table: 'job_queue',
filter: `id=eq.${jobId}`
},
(payload) => {
callback(payload.new as JobQueueRow);
}
)
.subscribe();
return () => {
channel.unsubscribe();
};
}
/**
* Subscribe to all job updates for current user
*/
export function subscribeToUserJobs(
supabase: SupabaseClient<Database>,
userId: string,
callback: JobUpdateCallback
): () => void {
const channel = supabase
.channel(`user-jobs:${userId}`)
.on(
'postgres_changes',
{
event: '*',
schema: 'public',
table: 'job_queue',
filter: `created_by=eq.${userId}`
},
(payload) => {
if (payload.new) {
callback(payload.new as JobQueueRow);
}
}
)
.subscribe();
return () => {
channel.unsubscribe();
};
}
// ============================================================================
// IMAGE GENERATION HELPERS (Convenience wrappers)
// ============================================================================
export interface GenerateImageJobParams {
prompt: string;
model_id: string;
model_version?: string;
width?: number;
height?: number;
num_inference_steps?: number;
guidance_scale?: number;
seed?: number;
negative_prompt?: string;
source_image_url?: string;
strength?: number;
style?: string;
}
/**
* Start an image generation job (high-level wrapper)
*
* @example
* ```typescript
* const { generationId, jobId } = await startImageGeneration(supabase, {
* prompt: 'A beautiful sunset over mountains',
* model_id: 'black-forest-labs/flux-dev'
* });
*
* // Subscribe to updates
* subscribeToGeneration(supabase, generationId, (generation) => {
* console.log('Status:', generation.status);
* });
* ```
*/
export async function startImageGeneration(
supabase: SupabaseClient<Database>,
params: GenerateImageJobParams
): Promise<{ generationId: string; jobId: string }> {
// Call start-generation Edge Function
const { data, error } = await supabase.functions.invoke('start-generation', {
body: params
});
if (error) {
throw new Error(`Failed to start generation: ${error.message}`);
}
if (!data.success) {
throw new Error(data.error || 'Failed to start generation');
}
return {
generationId: data.generation_id,
jobId: data.job_id
};
}
/**
* Subscribe to generation updates via Realtime
*/
export function subscribeToGeneration(
supabase: SupabaseClient<Database>,
generationId: string,
callback: (generation: any) => void
): () => void {
const channel = supabase
.channel(`generation:${generationId}`)
.on(
'postgres_changes',
{
event: 'UPDATE',
schema: 'public',
table: 'image_generations',
filter: `id=eq.${generationId}`
},
(payload) => {
callback(payload.new);
}
)
.subscribe();
return () => {
channel.unsubscribe();
};
}
/**
* Combined helper: Start generation and subscribe to updates
*
* @example
* ```typescript
* const { generationId, unsubscribe } = await generateImageWithUpdates(
* supabase,
* { prompt: 'A sunset', model_id: 'flux-dev' },
* (generation) => {
* console.log('Status:', generation.status);
* if (generation.status === 'completed') {
* console.log('Image ready!', generation.id);
* unsubscribe();
* }
* }
* );
* ```
*/
export async function generateImageWithUpdates(
supabase: SupabaseClient<Database>,
params: GenerateImageJobParams,
onUpdate: (generation: any) => void
): Promise<{ generationId: string; jobId: string; unsubscribe: () => void }> {
// Start generation
const { generationId, jobId } = await startImageGeneration(supabase, params);
// Subscribe to updates
const unsubscribe = subscribeToGeneration(supabase, generationId, onUpdate);
return { generationId, jobId, unsubscribe };
}
// ============================================================================
// POLLING HELPERS (fallback if Realtime not available)
// ============================================================================
/**
* Poll for job status (fallback for environments without Realtime)
*/
export async function pollJobUntilComplete(
supabase: SupabaseClient<Database>,
jobId: string,
options: {
maxAttempts?: number;
intervalMs?: number;
onUpdate?: JobUpdateCallback;
} = {}
): Promise<JobQueueRow> {
const {
maxAttempts = 60,
intervalMs = 2000,
onUpdate
} = options;
let attempts = 0;
while (attempts < maxAttempts) {
const job = await getJob(supabase, jobId);
if (!job) {
throw new Error('Job not found');
}
if (onUpdate) {
onUpdate(job);
}
if (job.status === 'completed' || job.status === 'failed' || job.status === 'cancelled') {
return job;
}
await new Promise(resolve => setTimeout(resolve, intervalMs));
attempts++;
}
throw new Error('Job polling timeout');
}
/**
* Poll for generation completion
*/
export async function pollGenerationUntilComplete(
supabase: SupabaseClient<Database>,
generationId: string,
options: {
maxAttempts?: number;
intervalMs?: number;
onUpdate?: (generation: any) => void;
} = {}
): Promise<any> {
const {
maxAttempts = 120,
intervalMs = 2000,
onUpdate
} = options;
let attempts = 0;
while (attempts < maxAttempts) {
const { data: generation, error } = await supabase
.from('image_generations')
.select('*')
.eq('id', generationId)
.single();
if (error) {
throw error;
}
if (onUpdate) {
onUpdate(generation);
}
if (generation.status === 'completed' || generation.status === 'failed') {
return generation;
}
await new Promise(resolve => setTimeout(resolve, intervalMs));
attempts++;
}
throw new Error('Generation polling timeout');
}

View file

@ -0,0 +1,969 @@
export type Json =
| string
| number
| boolean
| null
| { [key: string]: Json | undefined }
| Json[]
export type Database = {
// Allows to automatically instantiate createClient with right options
// instead of createClient<Database, { PostgrestVersion: 'XX' }>(URL, KEY)
__InternalSupabase: {
PostgrestVersion: "13.0.4"
}
public: {
Tables: {
batch_generations: {
Row: {
completed_at: string | null
completed_count: number | null
created_at: string | null
failed_count: number | null
guidance_scale: number | null
height: number | null
id: string
is_multi_generation: boolean | null
model_id: string | null
model_version: string | null
multi_count: number | null
name: string | null
status: string | null
steps: number | null
total_count: number
user_id: string | null
width: number | null
}
Insert: {
completed_at?: string | null
completed_count?: number | null
created_at?: string | null
failed_count?: number | null
guidance_scale?: number | null
height?: number | null
id?: string
is_multi_generation?: boolean | null
model_id?: string | null
model_version?: string | null
multi_count?: number | null
name?: string | null
status?: string | null
steps?: number | null
total_count: number
user_id?: string | null
width?: number | null
}
Update: {
completed_at?: string | null
completed_count?: number | null
created_at?: string | null
failed_count?: number | null
guidance_scale?: number | null
height?: number | null
id?: string
is_multi_generation?: boolean | null
model_id?: string | null
model_version?: string | null
multi_count?: number | null
name?: string | null
status?: string | null
steps?: number | null
total_count?: number
user_id?: string | null
width?: number | null
}
Relationships: []
}
generation_errors: {
Row: {
batch_id: string | null
created_at: string | null
error_details: Json | null
error_message: string | null
error_type: string | null
generation_id: string | null
id: string
max_retries: number | null
recovered_at: string | null
recovery_action: string | null
retry_after: string | null
retry_count: number | null
user_id: string | null
}
Insert: {
batch_id?: string | null
created_at?: string | null
error_details?: Json | null
error_message?: string | null
error_type?: string | null
generation_id?: string | null
id?: string
max_retries?: number | null
recovered_at?: string | null
recovery_action?: string | null
retry_after?: string | null
retry_count?: number | null
user_id?: string | null
}
Update: {
batch_id?: string | null
created_at?: string | null
error_details?: Json | null
error_message?: string | null
error_type?: string | null
generation_id?: string | null
id?: string
max_retries?: number | null
recovered_at?: string | null
recovery_action?: string | null
retry_after?: string | null
retry_count?: number | null
user_id?: string | null
}
Relationships: [
{
foreignKeyName: "generation_errors_batch_id_fkey"
columns: ["batch_id"]
isOneToOne: false
referencedRelation: "batch_generations"
referencedColumns: ["id"]
},
{
foreignKeyName: "generation_errors_batch_id_fkey"
columns: ["batch_id"]
isOneToOne: false
referencedRelation: "batch_progress"
referencedColumns: ["id"]
},
{
foreignKeyName: "generation_errors_generation_id_fkey"
columns: ["generation_id"]
isOneToOne: false
referencedRelation: "image_generations"
referencedColumns: ["id"]
},
]
}
generation_performance: {
Row: {
batch_id: string | null
completed_at: string | null
created_at: string | null
error_message: string | null
generation_id: string | null
height: number | null
id: string
model_id: string | null
model_version: string | null
processing_time_ms: number | null
queue_time_ms: number | null
retry_count: number | null
started_at: string | null
status: string | null
steps: number | null
total_time_ms: number | null
user_id: string | null
width: number | null
}
Insert: {
batch_id?: string | null
completed_at?: string | null
created_at?: string | null
error_message?: string | null
generation_id?: string | null
height?: number | null
id?: string
model_id?: string | null
model_version?: string | null
processing_time_ms?: number | null
queue_time_ms?: number | null
retry_count?: number | null
started_at?: string | null
status?: string | null
steps?: number | null
total_time_ms?: number | null
user_id?: string | null
width?: number | null
}
Update: {
batch_id?: string | null
completed_at?: string | null
created_at?: string | null
error_message?: string | null
generation_id?: string | null
height?: number | null
id?: string
model_id?: string | null
model_version?: string | null
processing_time_ms?: number | null
queue_time_ms?: number | null
retry_count?: number | null
started_at?: string | null
status?: string | null
steps?: number | null
total_time_ms?: number | null
user_id?: string | null
width?: number | null
}
Relationships: [
{
foreignKeyName: "generation_performance_batch_id_fkey"
columns: ["batch_id"]
isOneToOne: false
referencedRelation: "batch_generations"
referencedColumns: ["id"]
},
{
foreignKeyName: "generation_performance_batch_id_fkey"
columns: ["batch_id"]
isOneToOne: false
referencedRelation: "batch_progress"
referencedColumns: ["id"]
},
{
foreignKeyName: "generation_performance_generation_id_fkey"
columns: ["generation_id"]
isOneToOne: false
referencedRelation: "image_generations"
referencedColumns: ["id"]
},
]
}
image_generations: {
Row: {
batch_id: string | null
batch_index: number | null
completed_at: string | null
created_at: string | null
error_message: string | null
generation_strength: number | null
generation_time_seconds: number | null
guidance_scale: number | null
height: number | null
id: string
model: string | null
model_id: string | null
multi_group_id: string | null
multi_index: number | null
negative_prompt: string | null
priority: number | null
prompt: string
replicate_prediction_id: string | null
retry_count: number | null
seed: number | null
source_image_url: string | null
status: string | null
steps: number | null
style: string | null
user_id: string | null
width: number | null
}
Insert: {
batch_id?: string | null
batch_index?: number | null
completed_at?: string | null
created_at?: string | null
error_message?: string | null
generation_strength?: number | null
generation_time_seconds?: number | null
guidance_scale?: number | null
height?: number | null
id?: string
model?: string | null
model_id?: string | null
multi_group_id?: string | null
multi_index?: number | null
negative_prompt?: string | null
priority?: number | null
prompt: string
replicate_prediction_id?: string | null
retry_count?: number | null
seed?: number | null
source_image_url?: string | null
status?: string | null
steps?: number | null
style?: string | null
user_id?: string | null
width?: number | null
}
Update: {
batch_id?: string | null
batch_index?: number | null
completed_at?: string | null
created_at?: string | null
error_message?: string | null
generation_strength?: number | null
generation_time_seconds?: number | null
guidance_scale?: number | null
height?: number | null
id?: string
model?: string | null
model_id?: string | null
multi_group_id?: string | null
multi_index?: number | null
negative_prompt?: string | null
priority?: number | null
prompt?: string
replicate_prediction_id?: string | null
retry_count?: number | null
seed?: number | null
source_image_url?: string | null
status?: string | null
steps?: number | null
style?: string | null
user_id?: string | null
width?: number | null
}
Relationships: [
{
foreignKeyName: "image_generations_batch_id_fkey"
columns: ["batch_id"]
isOneToOne: false
referencedRelation: "batch_generations"
referencedColumns: ["id"]
},
{
foreignKeyName: "image_generations_batch_id_fkey"
columns: ["batch_id"]
isOneToOne: false
referencedRelation: "batch_progress"
referencedColumns: ["id"]
},
{
foreignKeyName: "image_generations_model_id_fkey"
columns: ["model_id"]
isOneToOne: false
referencedRelation: "models"
referencedColumns: ["id"]
},
{
foreignKeyName: "image_generations_user_id_fkey"
columns: ["user_id"]
isOneToOne: false
referencedRelation: "profiles"
referencedColumns: ["id"]
},
]
}
image_likes: {
Row: {
created_at: string | null
id: string
image_id: string | null
user_id: string | null
}
Insert: {
created_at?: string | null
id?: string
image_id?: string | null
user_id?: string | null
}
Update: {
created_at?: string | null
id?: string
image_id?: string | null
user_id?: string | null
}
Relationships: [
{
foreignKeyName: "image_likes_image_id_fkey"
columns: ["image_id"]
isOneToOne: false
referencedRelation: "images"
referencedColumns: ["id"]
},
{
foreignKeyName: "image_likes_user_id_fkey"
columns: ["user_id"]
isOneToOne: false
referencedRelation: "profiles"
referencedColumns: ["id"]
},
]
}
image_tags: {
Row: {
added_at: string | null
id: string
image_id: string | null
tag_id: string | null
}
Insert: {
added_at?: string | null
id?: string
image_id?: string | null
tag_id?: string | null
}
Update: {
added_at?: string | null
id?: string
image_id?: string | null
tag_id?: string | null
}
Relationships: [
{
foreignKeyName: "image_tags_image_id_fkey"
columns: ["image_id"]
isOneToOne: false
referencedRelation: "images"
referencedColumns: ["id"]
},
{
foreignKeyName: "image_tags_tag_id_fkey"
columns: ["tag_id"]
isOneToOne: false
referencedRelation: "tags"
referencedColumns: ["id"]
},
]
}
images: {
Row: {
archived_at: string | null
blurhash: string | null
created_at: string | null
download_count: number | null
file_size: number | null
filename: string
format: string | null
generation_id: string | null
generation_strength: number | null
height: number | null
id: string
is_favorite: boolean | null
is_public: boolean | null
model: string | null
negative_prompt: string | null
prompt: string
public_url: string | null
rating: number | null
source_image_id: string | null
storage_path: string
style: string | null
user_id: string | null
width: number | null
}
Insert: {
archived_at?: string | null
blurhash?: string | null
created_at?: string | null
download_count?: number | null
file_size?: number | null
filename: string
format?: string | null
generation_id?: string | null
generation_strength?: number | null
height?: number | null
id?: string
is_favorite?: boolean | null
is_public?: boolean | null
model?: string | null
negative_prompt?: string | null
prompt: string
public_url?: string | null
rating?: number | null
source_image_id?: string | null
storage_path: string
style?: string | null
user_id?: string | null
width?: number | null
}
Update: {
archived_at?: string | null
blurhash?: string | null
created_at?: string | null
download_count?: number | null
file_size?: number | null
filename?: string
format?: string | null
generation_id?: string | null
generation_strength?: number | null
height?: number | null
id?: string
is_favorite?: boolean | null
is_public?: boolean | null
model?: string | null
negative_prompt?: string | null
prompt?: string
public_url?: string | null
rating?: number | null
source_image_id?: string | null
storage_path?: string
style?: string | null
user_id?: string | null
width?: number | null
}
Relationships: [
{
foreignKeyName: "images_generation_id_fkey"
columns: ["generation_id"]
isOneToOne: false
referencedRelation: "image_generations"
referencedColumns: ["id"]
},
{
foreignKeyName: "images_source_image_id_fkey"
columns: ["source_image_id"]
isOneToOne: false
referencedRelation: "images"
referencedColumns: ["id"]
},
{
foreignKeyName: "images_user_id_fkey"
columns: ["user_id"]
isOneToOne: false
referencedRelation: "profiles"
referencedColumns: ["id"]
},
]
}
models: {
Row: {
cost_per_generation: number | null
created_at: string | null
default_guidance_scale: number | null
default_height: number | null
default_steps: number | null
default_strength: number | null
default_width: number | null
description: string | null
display_name: string
estimated_time_seconds: number | null
id: string
is_active: boolean | null
is_default: boolean | null
max_height: number | null
max_steps: number | null
max_width: number | null
min_height: number | null
min_width: number | null
name: string
replicate_id: string
sort_order: number | null
strength_max: number | null
strength_min: number | null
supported_aspect_ratios: Json | null
supports_image_to_image: boolean | null
supports_img2img: boolean | null
supports_negative_prompt: boolean | null
supports_seed: boolean | null
updated_at: string | null
version: string | null
}
Insert: {
cost_per_generation?: number | null
created_at?: string | null
default_guidance_scale?: number | null
default_height?: number | null
default_steps?: number | null
default_strength?: number | null
default_width?: number | null
description?: string | null
display_name: string
estimated_time_seconds?: number | null
id?: string
is_active?: boolean | null
is_default?: boolean | null
max_height?: number | null
max_steps?: number | null
max_width?: number | null
min_height?: number | null
min_width?: number | null
name: string
replicate_id: string
sort_order?: number | null
strength_max?: number | null
strength_min?: number | null
supported_aspect_ratios?: Json | null
supports_image_to_image?: boolean | null
supports_img2img?: boolean | null
supports_negative_prompt?: boolean | null
supports_seed?: boolean | null
updated_at?: string | null
version?: string | null
}
Update: {
cost_per_generation?: number | null
created_at?: string | null
default_guidance_scale?: number | null
default_height?: number | null
default_steps?: number | null
default_strength?: number | null
default_width?: number | null
description?: string | null
display_name?: string
estimated_time_seconds?: number | null
id?: string
is_active?: boolean | null
is_default?: boolean | null
max_height?: number | null
max_steps?: number | null
max_width?: number | null
min_height?: number | null
min_width?: number | null
name?: string
replicate_id?: string
sort_order?: number | null
strength_max?: number | null
strength_min?: number | null
supported_aspect_ratios?: Json | null
supports_image_to_image?: boolean | null
supports_img2img?: boolean | null
supports_negative_prompt?: boolean | null
supports_seed?: boolean | null
updated_at?: string | null
version?: string | null
}
Relationships: []
}
profiles: {
Row: {
avatar_url: string | null
created_at: string | null
email: string | null
id: string
updated_at: string | null
username: string | null
}
Insert: {
avatar_url?: string | null
created_at?: string | null
email?: string | null
id: string
updated_at?: string | null
username?: string | null
}
Update: {
avatar_url?: string | null
created_at?: string | null
email?: string | null
id?: string
updated_at?: string | null
username?: string | null
}
Relationships: []
}
prompt_templates: {
Row: {
category: string | null
created_at: string | null
id: string
is_public: boolean | null
name: string
negative_prompt: string | null
prompt: string
use_count: number | null
user_id: string | null
}
Insert: {
category?: string | null
created_at?: string | null
id?: string
is_public?: boolean | null
name: string
negative_prompt?: string | null
prompt: string
use_count?: number | null
user_id?: string | null
}
Update: {
category?: string | null
created_at?: string | null
id?: string
is_public?: boolean | null
name?: string
negative_prompt?: string | null
prompt?: string
use_count?: number | null
user_id?: string | null
}
Relationships: [
{
foreignKeyName: "prompt_templates_user_id_fkey"
columns: ["user_id"]
isOneToOne: false
referencedRelation: "profiles"
referencedColumns: ["id"]
},
]
}
tags: {
Row: {
color: string | null
created_at: string | null
id: string
name: string
}
Insert: {
color?: string | null
created_at?: string | null
id?: string
name: string
}
Update: {
color?: string | null
created_at?: string | null
id?: string
name?: string
}
Relationships: []
}
user_rate_limits: {
Row: {
daily_generations_count: number | null
daily_generations_limit: number | null
daily_reset_at: string | null
hourly_generations_count: number | null
hourly_generations_limit: number | null
hourly_reset_at: string | null
last_generation_at: string | null
max_batch_size: number | null
max_concurrent_generations: number | null
total_generations_all_time: number | null
updated_at: string | null
user_id: string
}
Insert: {
daily_generations_count?: number | null
daily_generations_limit?: number | null
daily_reset_at?: string | null
hourly_generations_count?: number | null
hourly_generations_limit?: number | null
hourly_reset_at?: string | null
last_generation_at?: string | null
max_batch_size?: number | null
max_concurrent_generations?: number | null
total_generations_all_time?: number | null
updated_at?: string | null
user_id: string
}
Update: {
daily_generations_count?: number | null
daily_generations_limit?: number | null
daily_reset_at?: string | null
hourly_generations_count?: number | null
hourly_generations_limit?: number | null
hourly_reset_at?: string | null
last_generation_at?: string | null
max_batch_size?: number | null
max_concurrent_generations?: number | null
total_generations_all_time?: number | null
updated_at?: string | null
user_id?: string
}
Relationships: []
}
}
Views: {
batch_progress: {
Row: {
completed_count: number | null
created_at: string | null
failed_count: number | null
id: string | null
items: Json | null
name: string | null
pending_count: number | null
processing_count: number | null
status: string | null
total_count: number | null
user_id: string | null
}
Relationships: []
}
multi_generation_groups: {
Row: {
completed_at: string | null
completed_count: number | null
created_at: string | null
failed_count: number | null
images: Json[] | null
model: string | null
multi_group_id: string | null
processing_count: number | null
prompt: string | null
total_count: number | null
user_id: string | null
}
Relationships: [
{
foreignKeyName: "image_generations_user_id_fkey"
columns: ["user_id"]
isOneToOne: false
referencedRelation: "profiles"
referencedColumns: ["id"]
},
]
}
}
Functions: {
check_rate_limit: {
Args: { p_count?: number; p_user_id: string }
Returns: Json
}
create_multi_generation: {
Args: {
p_count: number
p_model: string
p_prompt: string
p_settings: Json
p_user_id: string
}
Returns: string
}
get_error_statistics: {
Args: { p_days?: number; p_user_id?: string }
Returns: Json
}
get_user_limits: {
Args: { p_user_id: string }
Returns: Json
}
process_error_recovery: {
Args: Record<PropertyKey, never>
Returns: Json
}
recover_stale_generations: {
Args: Record<PropertyKey, never>
Returns: number
}
schedule_retry: {
Args: {
p_error_message: string
p_error_type: string
p_generation_id: string
}
Returns: Json
}
}
Enums: {
[_ in never]: never
}
CompositeTypes: {
[_ in never]: never
}
}
}
type DatabaseWithoutInternals = Omit<Database, "__InternalSupabase">
type DefaultSchema = DatabaseWithoutInternals[Extract<keyof Database, "public">]
export type Tables<
DefaultSchemaTableNameOrOptions extends
| keyof (DefaultSchema["Tables"] & DefaultSchema["Views"])
| { schema: keyof DatabaseWithoutInternals },
TableName extends DefaultSchemaTableNameOrOptions extends {
schema: keyof DatabaseWithoutInternals
}
? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] &
DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])
: never = never,
> = DefaultSchemaTableNameOrOptions extends {
schema: keyof DatabaseWithoutInternals
}
? (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] &
DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends {
Row: infer R
}
? R
: never
: DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema["Tables"] &
DefaultSchema["Views"])
? (DefaultSchema["Tables"] &
DefaultSchema["Views"])[DefaultSchemaTableNameOrOptions] extends {
Row: infer R
}
? R
: never
: never
export type TablesInsert<
DefaultSchemaTableNameOrOptions extends
| keyof DefaultSchema["Tables"]
| { schema: keyof DatabaseWithoutInternals },
TableName extends DefaultSchemaTableNameOrOptions extends {
schema: keyof DatabaseWithoutInternals
}
? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"]
: never = never,
> = DefaultSchemaTableNameOrOptions extends {
schema: keyof DatabaseWithoutInternals
}
? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends {
Insert: infer I
}
? I
: never
: DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"]
? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends {
Insert: infer I
}
? I
: never
: never
export type TablesUpdate<
DefaultSchemaTableNameOrOptions extends
| keyof DefaultSchema["Tables"]
| { schema: keyof DatabaseWithoutInternals },
TableName extends DefaultSchemaTableNameOrOptions extends {
schema: keyof DatabaseWithoutInternals
}
? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"]
: never = never,
> = DefaultSchemaTableNameOrOptions extends {
schema: keyof DatabaseWithoutInternals
}
? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends {
Update: infer U
}
? U
: never
: DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"]
? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends {
Update: infer U
}
? U
: never
: never
export type Enums<
DefaultSchemaEnumNameOrOptions extends
| keyof DefaultSchema["Enums"]
| { schema: keyof DatabaseWithoutInternals },
EnumName extends DefaultSchemaEnumNameOrOptions extends {
schema: keyof DatabaseWithoutInternals
}
? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"]
: never = never,
> = DefaultSchemaEnumNameOrOptions extends {
schema: keyof DatabaseWithoutInternals
}
? DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName]
: DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema["Enums"]
? DefaultSchema["Enums"][DefaultSchemaEnumNameOrOptions]
: never
export type CompositeTypes<
PublicCompositeTypeNameOrOptions extends
| keyof DefaultSchema["CompositeTypes"]
| { schema: keyof DatabaseWithoutInternals },
CompositeTypeName extends PublicCompositeTypeNameOrOptions extends {
schema: keyof DatabaseWithoutInternals
}
? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"]
: never = never,
> = PublicCompositeTypeNameOrOptions extends {
schema: keyof DatabaseWithoutInternals
}
? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName]
: PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"]
? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions]
: never
export const Constants = {
public: {
Enums: {},
},
} as const

View file

@ -0,0 +1 @@
export * from './database.types'

View file

@ -0,0 +1,2 @@
// Utility functions will be added here
export {}