mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-15 04:21:09 +02:00
Migrate figgos from single Expo app to multi-app monorepo structure: - Move mobile app to apps/mobile/ - Add apps/web/ (SvelteKit) and apps/backend/ (NestJS) scaffolds - Add packages/shared/ for shared types and constants - Update root package.json with new dev commands - Temporarily skip type-check (run pnpm install first) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
444 lines
14 KiB
TypeScript
444 lines
14 KiB
TypeScript
import { supabase } from './supabase';
|
|
import * as FileSystem from 'expo-file-system';
|
|
import { ExtendedFigureData } from '../components/CreateFigureForm';
|
|
|
|
/**
|
|
* Generates a figure using the Edge Function and stores it in the database
|
|
*/
|
|
export async function generateFigure(formData: ExtendedFigureData, isPublic: boolean = true) {
|
|
try {
|
|
// Convert image to Base64 if available
|
|
let faceImageBase64 = null;
|
|
if (formData.characterImage) {
|
|
// For web
|
|
if (formData.characterImage.startsWith('data:')) {
|
|
faceImageBase64 = formData.characterImage.split(',')[1];
|
|
}
|
|
// For native platforms
|
|
else {
|
|
const base64 = await FileSystem.readAsStringAsync(formData.characterImage, {
|
|
encoding: FileSystem.EncodingType.Base64,
|
|
});
|
|
faceImageBase64 = base64;
|
|
}
|
|
}
|
|
|
|
// Prepare payload for the Edge Function with the new JSONB structure
|
|
// Die Edge-Funktion wird die vollständige JSONB-Struktur generieren, wenn Felder fehlen
|
|
const payload = {
|
|
subject: formData.name,
|
|
rarity: formData.rarity || 'common',
|
|
face_image: faceImageBase64,
|
|
// Wir können optional eine vordefinierte JSONB-Struktur mitgeben, wenn wir bestimmte Werte haben
|
|
character_info: {
|
|
character: {
|
|
image_prompt: formData.characterDescription || '',
|
|
// Diese Felder werden von der Edge-Funktion generiert, wenn sie fehlen
|
|
description: '',
|
|
lore: '',
|
|
},
|
|
items: [
|
|
{
|
|
name: formData.artifacts[0]?.name || '',
|
|
image_prompt: formData.artifacts[0]?.description || '',
|
|
description: '',
|
|
lore: '',
|
|
},
|
|
{
|
|
name: formData.artifacts[1]?.name || '',
|
|
image_prompt: formData.artifacts[1]?.description || '',
|
|
description: '',
|
|
lore: '',
|
|
},
|
|
{
|
|
name: formData.artifacts[2]?.name || '',
|
|
image_prompt: formData.artifacts[2]?.description || '',
|
|
description: '',
|
|
lore: '',
|
|
},
|
|
],
|
|
},
|
|
};
|
|
|
|
// Prepare a complete payload with all necessary data
|
|
const cleanPayload = {
|
|
subject: formData.name, // This is the only required field
|
|
rarity: formData.rarity || 'common',
|
|
face_image: faceImageBase64,
|
|
character_info: {
|
|
character: {
|
|
image_prompt: formData.characterDescription || '',
|
|
description: formData.characterDescription || '',
|
|
lore: '',
|
|
},
|
|
items: formData.artifacts.map((artifact) => ({
|
|
name: artifact.name || '',
|
|
image_prompt: artifact.description || '',
|
|
description: artifact.description || '',
|
|
lore: '',
|
|
})),
|
|
},
|
|
};
|
|
|
|
// Validate payload before sending
|
|
if (!cleanPayload.subject) {
|
|
throw new Error('Error: Name/Subject is required');
|
|
}
|
|
|
|
// Log payload to see what is being sent
|
|
console.log('Sending payload to Edge Function:', JSON.stringify(cleanPayload));
|
|
console.log('Payload as string:', JSON.stringify(cleanPayload));
|
|
|
|
// Call Edge Function with adjusted options for web environments
|
|
console.log('Calling Edge Function...');
|
|
|
|
// Variable for the Edge Function response
|
|
let edgeFunctionResponse;
|
|
|
|
// Use supabase.functions.invoke directly - this handles authentication properly
|
|
console.log('Using supabase.functions.invoke...');
|
|
console.log('Payload being sent to Edge Function:', JSON.stringify(cleanPayload, null, 2));
|
|
|
|
let edgeFunctionData = null;
|
|
let edgeFunctionError = null;
|
|
|
|
try {
|
|
// Stelle sicher, dass wir einen gültigen Supabase-Client haben
|
|
const { data: sessionData } = await supabase.auth.getSession();
|
|
if (!sessionData.session) {
|
|
console.error('No active session found');
|
|
} else {
|
|
console.log('Session found, user is authenticated');
|
|
}
|
|
|
|
// Verwende das vollständige Payload mit allen Informationen
|
|
console.log('Using complete payload with character info');
|
|
|
|
// Get the access token for authorization
|
|
const accessToken = sessionData?.session?.access_token;
|
|
if (!accessToken) {
|
|
throw new Error('No access token available. Please log in again.');
|
|
}
|
|
|
|
// Use the known Supabase URL from the error logs
|
|
const supabaseUrl = 'https://igxexenivpvivtqkweup.supabase.co';
|
|
|
|
// Get the anon key using the auth client
|
|
const {
|
|
data: { session },
|
|
} = await supabase.auth.getSession();
|
|
const supabaseKey = session?.access_token || '';
|
|
|
|
// Use direct fetch approach instead of supabase.functions.invoke
|
|
const functionUrl = `${supabaseUrl}/functions/v1/barbiebox-generator`;
|
|
console.log('Calling Edge Function at URL:', functionUrl);
|
|
|
|
const response = await fetch(functionUrl, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
Authorization: `Bearer ${accessToken}`,
|
|
apikey: supabaseKey,
|
|
},
|
|
body: JSON.stringify(cleanPayload),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text();
|
|
console.error('Edge Function error response:', errorText);
|
|
throw new Error(`Edge Function returned status ${response.status}: ${errorText}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
const error = null;
|
|
|
|
edgeFunctionData = data;
|
|
edgeFunctionError = error;
|
|
|
|
console.log('Edge Function response:', data);
|
|
if (error) {
|
|
console.error('Edge Function error details:', error);
|
|
}
|
|
} catch (invokeError) {
|
|
console.error('Exception during Edge Function invoke:', invokeError);
|
|
throw invokeError;
|
|
}
|
|
|
|
if (edgeFunctionError) {
|
|
console.error('Error calling Edge Function:', edgeFunctionError);
|
|
throw edgeFunctionError;
|
|
}
|
|
|
|
edgeFunctionResponse = edgeFunctionData;
|
|
|
|
// Check if the Edge Function response is valid
|
|
if (!edgeFunctionResponse || !edgeFunctionResponse.image_url) {
|
|
throw new Error('The Edge Function did not return a valid image URL');
|
|
}
|
|
|
|
console.log('Storing figure in database with image URL:', edgeFunctionResponse.image_url);
|
|
|
|
// Get the user ID
|
|
const { data: userData, error: userError } = await supabase.auth.getUser();
|
|
if (userError) {
|
|
console.error('Error retrieving user:', userError);
|
|
throw new Error('User could not be retrieved: ' + userError.message);
|
|
}
|
|
|
|
const userId = userData.user?.id;
|
|
if (!userId) {
|
|
throw new Error('User ID could not be determined');
|
|
}
|
|
|
|
// Verwende die generierten Beschreibungen von der Edge-Funktion
|
|
const generatedDescriptions = edgeFunctionResponse.generated_descriptions;
|
|
|
|
// Ausführliches Debugging der empfangenen Daten
|
|
console.log('FULL Edge Function Response:', JSON.stringify(edgeFunctionResponse, null, 2));
|
|
console.log(
|
|
'Received generated descriptions from Edge Function:',
|
|
JSON.stringify(generatedDescriptions, null, 2)
|
|
);
|
|
|
|
// Prüfe die Struktur der generierten Beschreibungen
|
|
if (generatedDescriptions) {
|
|
console.log('Generated descriptions structure check:');
|
|
console.log('- Has character:', !!generatedDescriptions.character);
|
|
if (generatedDescriptions.character) {
|
|
console.log(' - Character fields:', Object.keys(generatedDescriptions.character));
|
|
console.log(' - Has image_prompt:', !!generatedDescriptions.character.image_prompt);
|
|
console.log(' - Has description:', !!generatedDescriptions.character.description);
|
|
console.log(' - Has lore:', !!generatedDescriptions.character.lore);
|
|
}
|
|
|
|
console.log('- Has items:', !!generatedDescriptions.items);
|
|
if (generatedDescriptions.items && Array.isArray(generatedDescriptions.items)) {
|
|
console.log(' - Items count:', generatedDescriptions.items.length);
|
|
generatedDescriptions.items.forEach((item: any, index: number) => {
|
|
console.log(` - Item ${index + 1} fields:`, Object.keys(item));
|
|
console.log(` - Has name:`, !!item.name);
|
|
console.log(` - Has image_prompt:`, !!item.image_prompt);
|
|
console.log(` - Has description:`, !!item.description);
|
|
console.log(` - Has lore:`, !!item.lore);
|
|
});
|
|
}
|
|
}
|
|
|
|
// Erstelle das character_info JSONB-Objekt basierend auf der neuen Struktur
|
|
let characterInfo: any;
|
|
|
|
// Prüfe, ob die Edge-Funktion die neue JSONB-Struktur zurückgegeben hat
|
|
if (generatedDescriptions && generatedDescriptions.character && generatedDescriptions.items) {
|
|
// Verwende die vollständige JSONB-Struktur von der Edge-Funktion
|
|
console.log('Using new JSONB structure from Edge Function');
|
|
|
|
// Stelle sicher, dass alle erweiterten Felder vorhanden sind
|
|
const character = {
|
|
description:
|
|
generatedDescriptions.character.description || formData.characterDescription || '',
|
|
image_prompt:
|
|
generatedDescriptions.character.image_prompt ||
|
|
generatedDescriptions.character.description ||
|
|
formData.characterDescription ||
|
|
'',
|
|
lore:
|
|
generatedDescriptions.character.lore ||
|
|
`${formData.name} has a rich history and background.`,
|
|
};
|
|
|
|
// Stelle sicher, dass alle Items die erweiterten Felder haben
|
|
const items = generatedDescriptions.items.map((item: any, index: number) => {
|
|
const itemName = item.name || `Item ${index + 1}`;
|
|
const itemDesc = item.description || formData.artifacts[index]?.description || '';
|
|
|
|
return {
|
|
name: itemName,
|
|
description: itemDesc,
|
|
image_prompt: item.image_prompt || itemDesc,
|
|
lore: item.lore || `This item has special significance for ${formData.name}.`,
|
|
};
|
|
});
|
|
|
|
characterInfo = {
|
|
character,
|
|
items,
|
|
style_description: generatedDescriptions.style_description || '',
|
|
};
|
|
|
|
// Logge die finale Struktur
|
|
console.log(
|
|
'Final character_info structure to be saved:',
|
|
JSON.stringify(characterInfo, null, 2)
|
|
);
|
|
} else {
|
|
// Fallback auf die alte Struktur (sollte nicht mehr vorkommen)
|
|
console.log('WARNING: Edge Function returned old format, creating JSONB structure manually');
|
|
characterInfo = {
|
|
character: {
|
|
description:
|
|
formData.characterDescription ||
|
|
(generatedDescriptions ? generatedDescriptions.clothing_description : ''),
|
|
image_prompt:
|
|
formData.characterDescription ||
|
|
(generatedDescriptions ? generatedDescriptions.clothing_description : ''),
|
|
lore: `${formData.name} has a rich history and background.`,
|
|
},
|
|
items: [
|
|
{
|
|
name: 'Item 1',
|
|
description:
|
|
formData.artifacts[0]?.description ||
|
|
(generatedDescriptions ? generatedDescriptions.accessory1_description : ''),
|
|
image_prompt:
|
|
formData.artifacts[0]?.description ||
|
|
(generatedDescriptions ? generatedDescriptions.accessory1_description : ''),
|
|
lore: `This item has special significance for ${formData.name}.`,
|
|
},
|
|
{
|
|
name: 'Item 2',
|
|
description:
|
|
formData.artifacts[1]?.description ||
|
|
(generatedDescriptions ? generatedDescriptions.accessory2_description : ''),
|
|
image_prompt:
|
|
formData.artifacts[1]?.description ||
|
|
(generatedDescriptions ? generatedDescriptions.accessory2_description : ''),
|
|
lore: `This item has special significance for ${formData.name}.`,
|
|
},
|
|
{
|
|
name: 'Item 3',
|
|
description:
|
|
formData.artifacts[2]?.description ||
|
|
(generatedDescriptions ? generatedDescriptions.accessory3_description : ''),
|
|
image_prompt:
|
|
formData.artifacts[2]?.description ||
|
|
(generatedDescriptions ? generatedDescriptions.accessory3_description : ''),
|
|
lore: `This item has special significance for ${formData.name}.`,
|
|
},
|
|
],
|
|
};
|
|
}
|
|
|
|
// Store figure in the database
|
|
const { data: figureData, error: figureError } = await supabase
|
|
.from('figures')
|
|
.insert({
|
|
name: formData.name,
|
|
subject: formData.name,
|
|
image_url: edgeFunctionResponse.image_url,
|
|
enhanced_prompt: edgeFunctionResponse.enhanced_prompt, // Store the enhanced prompt
|
|
rarity: payload.rarity, // Added rarity field
|
|
|
|
character_info: characterInfo, // Verwende das neue JSONB-Feld
|
|
is_public: isPublic,
|
|
is_archived: false, // Added is_archived field
|
|
user_id: userId,
|
|
})
|
|
.select()
|
|
.single();
|
|
|
|
if (figureError) {
|
|
console.error('Error saving the figure:', figureError);
|
|
throw new Error(figureError.message);
|
|
}
|
|
|
|
console.log('Figure successfully saved in the database:', figureData);
|
|
return figureData;
|
|
} catch (error) {
|
|
console.error('Error in generateFigure:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loads a user's figures from the database
|
|
*/
|
|
export async function getUserFigures(userId: string) {
|
|
try {
|
|
const { data, error } = await supabase
|
|
.from('figures')
|
|
.select(
|
|
`
|
|
id,
|
|
name,
|
|
subject,
|
|
image_url,
|
|
likes,
|
|
is_public,
|
|
rarity,
|
|
is_archived,
|
|
character_info
|
|
`
|
|
)
|
|
.eq('user_id', userId)
|
|
.eq('is_archived', false) // Only show non-archived figures
|
|
.order('created_at', { ascending: false });
|
|
|
|
if (error) {
|
|
console.error('Error loading figures:', error);
|
|
throw new Error(error.message);
|
|
}
|
|
|
|
return data || [];
|
|
} catch (error) {
|
|
console.error('Error in getUserFigures:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loads public figures from the database
|
|
*/
|
|
export async function getPublicFigures() {
|
|
try {
|
|
const { data, error } = await supabase
|
|
.from('figures')
|
|
.select(
|
|
`
|
|
id,
|
|
name,
|
|
subject,
|
|
image_url,
|
|
enhanced_prompt,
|
|
likes,
|
|
rarity,
|
|
user_id,
|
|
created_at,
|
|
character_info
|
|
`
|
|
)
|
|
.eq('is_public', true)
|
|
.eq('is_archived', false) // Only show non-archived figures
|
|
.order('created_at', { ascending: false });
|
|
|
|
if (error) {
|
|
console.error('Error loading public figures:', error);
|
|
throw new Error(error.message);
|
|
}
|
|
|
|
return data || [];
|
|
} catch (error) {
|
|
console.error('Error in getPublicFigures:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Aktualisiert die Likes einer Figur
|
|
*/
|
|
export async function likeFigure(figureId: number) {
|
|
try {
|
|
const { data, error } = await supabase.rpc('increment_likes', {
|
|
figure_id: figureId,
|
|
});
|
|
|
|
if (error) {
|
|
console.error('Fehler beim Liken der Figur:', error);
|
|
throw new Error(error.message);
|
|
}
|
|
|
|
return data;
|
|
} catch (error) {
|
|
console.error('Fehler in likeFigure:', error);
|
|
throw error;
|
|
}
|
|
}
|