mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-23 01:06:42 +02:00
Feat: Refactor postgress
This commit is contained in:
parent
046a0e3fe7
commit
98efa6f6e8
134 changed files with 9459 additions and 1904 deletions
|
|
@ -1,10 +1,6 @@
|
|||
# Mana Core Auth Configuration
|
||||
EXPO_PUBLIC_MANA_CORE_AUTH_URL=http://localhost:3001
|
||||
|
||||
# Supabase Configuration (for database only, not auth)
|
||||
EXPO_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
|
||||
EXPO_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key
|
||||
|
||||
# Chat Backend API
|
||||
# The backend handles AI API calls securely - no API keys needed in the mobile app
|
||||
EXPO_PUBLIC_BACKEND_URL=http://localhost:3002
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { supabase } from '../../utils/supabase';
|
||||
const BACKEND_URL = process.env.EXPO_PUBLIC_BACKEND_URL || 'http://localhost:3001';
|
||||
|
||||
// Definiere den Typ für ein Modell
|
||||
export type Model = {
|
||||
|
|
@ -10,7 +10,7 @@ export type Model = {
|
|||
updated_at?: string;
|
||||
};
|
||||
|
||||
// Fallback-Modelle, falls keine aus der Datenbank geladen werden können
|
||||
// Fallback-Modelle, falls keine aus dem Backend geladen werden können
|
||||
const FALLBACK_MODELS: Model[] = [
|
||||
{
|
||||
id: '550e8400-e29b-41d4-a716-446655440000',
|
||||
|
|
@ -56,28 +56,25 @@ const FALLBACK_MODELS: Model[] = [
|
|||
// GET-Handler für Modelle
|
||||
export async function GET(request: Request) {
|
||||
try {
|
||||
// Versuche, Modelle aus der Supabase-Datenbank zu laden
|
||||
// Versuche, Modelle vom Backend zu laden
|
||||
let models: Model[] = FALLBACK_MODELS;
|
||||
|
||||
// Wenn Supabase konfiguriert ist, versuche die Modelle von dort zu laden
|
||||
|
||||
try {
|
||||
if (supabase) {
|
||||
const { data, error } = await supabase
|
||||
.from('models')
|
||||
.select('*');
|
||||
// Entfernt: .order('created_at', { ascending: false })
|
||||
|
||||
if (error) {
|
||||
console.error('Fehler beim Laden der Modelle aus Supabase:', error);
|
||||
} else if (data && data.length > 0) {
|
||||
const response = await fetch(`${BACKEND_URL}/api/chat/models`);
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
if (data && data.length > 0) {
|
||||
models = data as Model[];
|
||||
}
|
||||
} else {
|
||||
console.error('Fehler beim Laden der Modelle vom Backend:', response.status);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Fehler bei der Supabase-Verbindung:', e);
|
||||
console.error('Fehler bei der Backend-Verbindung:', e);
|
||||
// Fallback zu den vordefinierten Modellen
|
||||
}
|
||||
|
||||
|
||||
return Response.json(models);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Verarbeiten der Anfrage:', error);
|
||||
|
|
@ -90,59 +87,12 @@ export async function GET(request: Request) {
|
|||
}
|
||||
}
|
||||
|
||||
// POST-Handler zum Erstellen eines neuen Modells
|
||||
// POST-Handler zum Erstellen eines neuen Modells (nicht unterstützt ohne Backend-Endpoint)
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
|
||||
// Validiere die Eingabedaten
|
||||
if (!body.name || !body.description) {
|
||||
return new Response(JSON.stringify({ error: 'Name und Beschreibung sind erforderlich' }), {
|
||||
status: 400,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Erstelle ein neues Modell in der Datenbank
|
||||
if (supabase) {
|
||||
const { data, error } = await supabase
|
||||
.from('models')
|
||||
.insert([{
|
||||
name: body.name,
|
||||
description: body.description,
|
||||
parameters: body.parameters || {},
|
||||
}])
|
||||
.select();
|
||||
|
||||
if (error) {
|
||||
console.error('Fehler beim Erstellen des Modells:', error);
|
||||
return new Response(JSON.stringify({ error: 'Fehler beim Erstellen des Modells' }), {
|
||||
status: 500,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return Response.json(data[0]);
|
||||
} else {
|
||||
// Wenn Supabase nicht verfügbar ist, gib einen Fehler zurück
|
||||
return new Response(JSON.stringify({ error: 'Datenbank nicht verfügbar' }), {
|
||||
status: 503,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Verarbeiten der Anfrage:', error);
|
||||
return new Response(JSON.stringify({ error: 'Interner Serverfehler' }), {
|
||||
status: 500,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
}
|
||||
return new Response(JSON.stringify({ error: 'Modell-Erstellung wird über das Backend nicht unterstützt' }), {
|
||||
status: 501,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { supabase } from '../../utils/supabase';
|
||||
// TODO: Implement usage statistics via Backend API
|
||||
// The Backend needs endpoints for user usage statistics
|
||||
|
||||
// Typ für die Token-Nutzung pro Modell
|
||||
export type ModelUsage = {
|
||||
|
|
@ -28,57 +29,31 @@ export type ConversationUsage = {
|
|||
};
|
||||
|
||||
// Handler für GET /api/usage
|
||||
// TODO: Backend-Endpoints für Usage-Statistiken implementieren
|
||||
export async function GET(request: Request) {
|
||||
try {
|
||||
const url = new URL(request.url);
|
||||
const userId = url.searchParams.get('userId');
|
||||
const period = url.searchParams.get('period') || 'month';
|
||||
|
||||
|
||||
if (!userId) {
|
||||
return new Response(JSON.stringify({ error: 'User ID ist erforderlich' }), {
|
||||
status: 400,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
|
||||
// Lade die Tokennutzung nach Modell
|
||||
const { data: modelUsage, error: modelError } = await supabase
|
||||
.rpc('get_user_model_usage', { user_id: userId });
|
||||
|
||||
if (modelError) {
|
||||
console.error('Fehler beim Laden der Modellnutzung:', modelError);
|
||||
return new Response(JSON.stringify({ error: 'Fehler beim Laden der Modellnutzung' }), {
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
|
||||
// Lade die Tokennutzung nach Zeitraum
|
||||
const { data: periodUsage, error: periodError } = await supabase
|
||||
.rpc('get_user_usage_by_period', {
|
||||
user_id: userId,
|
||||
period: period
|
||||
});
|
||||
|
||||
if (periodError) {
|
||||
console.error('Fehler beim Laden der Zeitraumnutzung:', periodError);
|
||||
return new Response(JSON.stringify({ error: 'Fehler beim Laden der Zeitraumnutzung' }), {
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
|
||||
// Berechne Gesamtkosten und Token
|
||||
const totalCost = (modelUsage as ModelUsage[]).reduce((sum, model) => sum + model.total_cost, 0);
|
||||
const totalTokens = (modelUsage as ModelUsage[]).reduce((sum, model) => sum + model.total_tokens, 0);
|
||||
|
||||
|
||||
// Usage-Statistiken sind noch nicht über die Backend-API verfügbar
|
||||
// Gebe leere Daten zurück
|
||||
console.log('Usage-Statistiken: Backend-Endpoints noch nicht implementiert');
|
||||
|
||||
return Response.json({
|
||||
modelUsage,
|
||||
periodUsage,
|
||||
modelUsage: [],
|
||||
periodUsage: [],
|
||||
summary: {
|
||||
totalCost,
|
||||
totalTokens
|
||||
}
|
||||
totalCost: 0,
|
||||
totalTokens: 0
|
||||
},
|
||||
message: 'Usage-Statistiken sind derzeit nicht verfügbar'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Verarbeiten der Anfrage:', error);
|
||||
|
|
@ -90,42 +65,31 @@ export async function GET(request: Request) {
|
|||
}
|
||||
|
||||
// Handler für GET /api/usage/conversation
|
||||
// TODO: Backend-Endpoints für Conversation-Usage implementieren
|
||||
export async function GET_conversation(request: Request) {
|
||||
try {
|
||||
const url = new URL(request.url);
|
||||
const conversationId = url.searchParams.get('conversationId');
|
||||
|
||||
|
||||
if (!conversationId) {
|
||||
return new Response(JSON.stringify({ error: 'Conversation ID ist erforderlich' }), {
|
||||
status: 400,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
|
||||
// Lade die Tokennutzung für die Konversation
|
||||
const { data: conversationUsage, error } = await supabase
|
||||
.rpc('get_conversation_usage', { conversation_id: conversationId });
|
||||
|
||||
if (error) {
|
||||
console.error('Fehler beim Laden der Konversationsnutzung:', error);
|
||||
return new Response(JSON.stringify({ error: 'Fehler beim Laden der Konversationsnutzung' }), {
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
|
||||
// Berechne Gesamtkosten und Token für diese Konversation
|
||||
const usage = conversationUsage as ConversationUsage[];
|
||||
const totalCost = usage.reduce((sum, item) => sum + item.estimated_cost, 0);
|
||||
const totalTokens = usage.reduce((sum, item) => sum + item.total_tokens, 0);
|
||||
|
||||
|
||||
// Usage-Statistiken sind noch nicht über die Backend-API verfügbar
|
||||
// Gebe leere Daten zurück
|
||||
console.log('Conversation-Usage: Backend-Endpoints noch nicht implementiert');
|
||||
|
||||
return Response.json({
|
||||
conversationUsage,
|
||||
conversationUsage: [],
|
||||
summary: {
|
||||
totalCost,
|
||||
totalTokens,
|
||||
messageCount: usage.length
|
||||
}
|
||||
totalCost: 0,
|
||||
totalTokens: 0,
|
||||
messageCount: 0
|
||||
},
|
||||
message: 'Usage-Statistiken sind derzeit nicht verfügbar'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Verarbeiten der Anfrage:', error);
|
||||
|
|
@ -134,4 +98,4 @@ export async function GET_conversation(request: Request) {
|
|||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,13 +15,13 @@ import { Ionicons } from '@expo/vector-icons';
|
|||
import { useAuth } from '../context/AuthProvider';
|
||||
import { useAppTheme } from '../theme/ThemeProvider';
|
||||
import CustomDrawer from '../components/CustomDrawer';
|
||||
import {
|
||||
getArchivedConversations,
|
||||
getMessages,
|
||||
deleteConversation,
|
||||
import {
|
||||
getArchivedConversations,
|
||||
getMessages,
|
||||
deleteConversation,
|
||||
unarchiveConversation
|
||||
} from '../services/conversation';
|
||||
import { supabase } from '../utils/supabase';
|
||||
import { modelApi } from '../services/api';
|
||||
|
||||
// Typendefinitionen für Konversationen
|
||||
type ConversationItem = {
|
||||
|
|
@ -69,18 +69,14 @@ export default function ArchiveScreen() {
|
|||
try {
|
||||
// Lade die Nachrichten der Konversation
|
||||
const messages = await getMessages(conv.id);
|
||||
// Lade das Modell aus der Datenbank
|
||||
const { data: modelData } = await supabase
|
||||
.from('models')
|
||||
.select('name')
|
||||
.eq('id', conv.model_id)
|
||||
.single();
|
||||
|
||||
// Lade das Modell über die Backend API
|
||||
const modelData = await modelApi.getModel(conv.model_id);
|
||||
|
||||
// Finde die letzte Nachricht (die nicht vom System ist)
|
||||
const lastMessage = messages
|
||||
.filter(msg => msg.sender !== 'system')
|
||||
.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0];
|
||||
|
||||
|
||||
if (lastMessage) {
|
||||
conversationItems.push({
|
||||
id: conv.id,
|
||||
|
|
@ -88,7 +84,7 @@ export default function ArchiveScreen() {
|
|||
title: conv.title || 'Unbenannte Konversation',
|
||||
lastMessage: lastMessage.message_text,
|
||||
timestamp: new Date(conv.updated_at),
|
||||
mode: conv.conversation_mode === 'free' ? 'frei' :
|
||||
mode: conv.conversation_mode === 'free' ? 'frei' :
|
||||
conv.conversation_mode === 'guided' ? 'geführt' : 'vorlage'
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import { useTheme } from '@react-navigation/native';
|
|||
import { useRouter, Link } from 'expo-router';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { useAuth } from '../../context/AuthProvider';
|
||||
import { supabase } from '../../utils/supabase';
|
||||
import { useAppTheme } from '../../theme/ThemeProvider';
|
||||
|
||||
export default function LoginScreen() {
|
||||
|
|
@ -24,24 +23,13 @@ export default function LoginScreen() {
|
|||
Alert.alert('Fehler', 'Bitte gib deine E-Mail-Adresse und dein Passwort ein.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
const { error } = await signIn(email, password);
|
||||
|
||||
|
||||
if (error) {
|
||||
console.log('Anmeldung mit Passwort fehlgeschlagen, versuche direkte Anmeldung...');
|
||||
// Wenn die normale Anmeldung fehlschlägt, versuche eine direkte Anmeldung
|
||||
const { error: directError } = await supabase.auth.signInWithPassword({
|
||||
email,
|
||||
password,
|
||||
});
|
||||
|
||||
if (directError) {
|
||||
Alert.alert('Anmeldung fehlgeschlagen', directError.message);
|
||||
} else {
|
||||
router.replace('/');
|
||||
}
|
||||
Alert.alert('Anmeldung fehlgeschlagen', error.message || 'Unbekannter Fehler');
|
||||
} else {
|
||||
// Erfolgreich angemeldet, navigiere zur Hauptseite
|
||||
router.replace('/');
|
||||
|
|
@ -54,37 +42,12 @@ export default function LoginScreen() {
|
|||
}
|
||||
};
|
||||
|
||||
// Magic Link ist derzeit nicht verfügbar (mana-core-auth unterstützt dies nicht)
|
||||
const handleMagicLink = async () => {
|
||||
if (!email) {
|
||||
Alert.alert('Fehler', 'Bitte gib deine E-Mail-Adresse ein.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
const { error } = await supabase.auth.signInWithOtp({
|
||||
email,
|
||||
options: {
|
||||
emailRedirectTo: 'exp://localhost:8081/',
|
||||
},
|
||||
});
|
||||
|
||||
if (error) {
|
||||
Alert.alert('Fehler', error.message);
|
||||
} else {
|
||||
setIsMagicLinkSent(true);
|
||||
Alert.alert(
|
||||
'Magic Link gesendet',
|
||||
'Wir haben dir einen Magic Link an deine E-Mail-Adresse gesendet. Bitte öffne den Link, um dich anzumelden.'
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Senden des Magic Links:', error);
|
||||
Alert.alert('Fehler', 'Beim Senden des Magic Links ist ein Fehler aufgetreten. Bitte versuche es später erneut.');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
Alert.alert(
|
||||
'Nicht verfügbar',
|
||||
'Magic Link Anmeldung ist derzeit nicht verfügbar. Bitte nutze E-Mail und Passwort.'
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -13,8 +13,9 @@ import DocumentVersions from '../../components/DocumentVersions';
|
|||
|
||||
// Import der Konversations- und OpenAI-Services
|
||||
import { createConversation, addMessage, getMessages, sendMessageAndGetResponse, Message as DbMessage } from '../../services/conversation';
|
||||
import { supabase } from '../../utils/supabase';
|
||||
import { conversationApi } from '../../services/api';
|
||||
import { Document, createDocument, createDocumentVersion, getLatestDocument, getAllDocumentVersions, hasDocument, deleteDocumentVersion } from '../../services/document';
|
||||
import { useAuth } from '../../context/AuthProvider';
|
||||
|
||||
// Typdefinition für die Nachrichten in der UI
|
||||
type UIMessage = {
|
||||
|
|
@ -38,10 +39,11 @@ function convertDbToUiMessages(dbMessages: DbMessage[]): UIMessage[] {
|
|||
export default function ConversationScreen() {
|
||||
const { colors } = useTheme();
|
||||
const router = useRouter();
|
||||
const { user } = useAuth();
|
||||
// Hole Parameter aus URL und Query-String
|
||||
const params = useLocalSearchParams();
|
||||
const { id } = params;
|
||||
|
||||
|
||||
// Drawer (Seitenmenü) Status
|
||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||
|
||||
|
|
@ -79,20 +81,15 @@ export default function ConversationScreen() {
|
|||
const [isDocumentLoading, setIsDocumentLoading] = useState<boolean>(false);
|
||||
const [isVersionsModalVisible, setIsVersionsModalVisible] = useState<boolean>(false);
|
||||
|
||||
// Überprüfe den aktuellen Benutzer
|
||||
// Setze userId vom AuthProvider
|
||||
useEffect(() => {
|
||||
const checkUser = async () => {
|
||||
const { data } = await supabase.auth.getUser();
|
||||
if (data?.user) {
|
||||
setUserId(data.user.id);
|
||||
} else {
|
||||
// In einer echten App würden wir hier zur Login-Seite weiterleiten
|
||||
// Für jetzt verwenden wir eine Test-ID
|
||||
setUserId('test-user-id');
|
||||
}
|
||||
};
|
||||
checkUser();
|
||||
}, []);
|
||||
if (user?.id) {
|
||||
setUserId(user.id);
|
||||
} else {
|
||||
// Fallback für Test-Zwecke
|
||||
setUserId('test-user-id');
|
||||
}
|
||||
}, [user]);
|
||||
|
||||
// Lade das Modell
|
||||
useEffect(() => {
|
||||
|
|
@ -117,32 +114,28 @@ export default function ConversationScreen() {
|
|||
}
|
||||
}
|
||||
|
||||
// Wenn kein URL-Modell gefunden wurde oder keins angegeben war,
|
||||
// Wenn kein URL-Modell gefunden wurde oder keins angegeben war,
|
||||
// hole die Konversation, um die Model-ID zu bekommen
|
||||
if (!isNewConversation && conversationId) {
|
||||
console.log("Hole Modell-ID aus Konversation:", conversationId);
|
||||
const { data: conversationData, error: conversationError } = await supabase
|
||||
.from('conversations')
|
||||
.select('model_id, title')
|
||||
.eq('id', conversationId)
|
||||
.single();
|
||||
|
||||
if (conversationData && conversationData.model_id) {
|
||||
console.log("✓ Model-ID aus der Konversation geladen:", conversationData.model_id);
|
||||
const conversationData = await conversationApi.getConversation(conversationId);
|
||||
|
||||
if (conversationData && conversationData.modelId) {
|
||||
console.log("✓ Model-ID aus der Konversation geladen:", conversationData.modelId);
|
||||
// Setze das modelId, wenn wir es aus der Konversation bekommen haben
|
||||
const fetchedModelId = conversationData.model_id;
|
||||
|
||||
const fetchedModelId = conversationData.modelId;
|
||||
|
||||
// Setze den Titel aus der Konversation
|
||||
if (conversationData.title) {
|
||||
console.log("✓ Titel aus der Konversation geladen:", conversationData.title);
|
||||
setConversationTitle(conversationData.title);
|
||||
}
|
||||
|
||||
|
||||
// Hole jetzt das Modell mit der ID
|
||||
const response = await fetch(`/api/models`);
|
||||
const models = await response.json();
|
||||
const model = models.find((m: any) => m.id === fetchedModelId);
|
||||
|
||||
|
||||
if (model) {
|
||||
console.log("✓ Model-Daten aus Konversation geladen:", model.name);
|
||||
setModelName(model.name);
|
||||
|
|
@ -150,8 +143,8 @@ export default function ConversationScreen() {
|
|||
} else {
|
||||
console.warn("Modell mit ID aus Konversation nicht gefunden:", fetchedModelId);
|
||||
}
|
||||
} else if (conversationError) {
|
||||
console.error('Fehler beim Laden der Konversation:', conversationError);
|
||||
} else {
|
||||
console.error('Fehler beim Laden der Konversation oder keine Model-ID gefunden');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -174,13 +167,9 @@ export default function ConversationScreen() {
|
|||
|
||||
// Prüfe, ob es eine bestehende Konversation mit Dokumentmodus ist
|
||||
if (conversationId) {
|
||||
const { data: convData, error: convError } = await supabase
|
||||
.from('conversations')
|
||||
.select('document_mode')
|
||||
.eq('id', conversationId)
|
||||
.single();
|
||||
|
||||
if (convData && convData.document_mode) {
|
||||
const convData = await conversationApi.getConversation(conversationId);
|
||||
|
||||
if (convData && convData.documentMode) {
|
||||
setIsDocumentMode(true);
|
||||
await loadDocumentData(conversationId);
|
||||
}
|
||||
|
|
@ -203,45 +192,21 @@ export default function ConversationScreen() {
|
|||
try {
|
||||
console.log(`[loadDocumentData] Lade Dokumentdaten für Konversation ${convId}`);
|
||||
setIsDocumentLoading(true);
|
||||
|
||||
// Längere Verzögerung zur Sicherstellung, dass Datenbanktransaktionen Zeit haben
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// Direkter Supabase-Aufruf mit Cache-Umgehung
|
||||
console.log('Lade alle Dokumentversionen direkt...');
|
||||
|
||||
// Generiere einen zufälligen String, um Caching zu verhindern
|
||||
const timestamp = new Date().getTime();
|
||||
const randomString = Math.random().toString(36).substring(2, 8);
|
||||
const noCache = `${timestamp}-${randomString}`;
|
||||
|
||||
const { data: freshVersions, error } = await supabase
|
||||
.from('documents')
|
||||
.select(`*,noCacheKey:conversation_id(id)`)
|
||||
.eq('conversation_id', convId)
|
||||
.order('version', { ascending: false })
|
||||
.filter('noCacheKey.id', 'not.is', null)
|
||||
.limit(100);
|
||||
|
||||
if (error) {
|
||||
console.error('Fehler beim direkten Laden der Dokumentversionen:', error);
|
||||
setDocumentVersions([]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Entferne noCacheKey-Feld
|
||||
const cleanVersions = freshVersions.map(v => {
|
||||
const { noCacheKey, ...rest } = v;
|
||||
return rest;
|
||||
});
|
||||
|
||||
console.log(`${cleanVersions.length} Dokumentversionen direkt geladen`);
|
||||
setDocumentVersions(cleanVersions);
|
||||
|
||||
|
||||
// Kurze Verzögerung zur Sicherstellung, dass DB-Transaktionen abgeschlossen sind
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
|
||||
// Lade alle Dokumentversionen über den Service
|
||||
console.log('Lade alle Dokumentversionen über Backend API...');
|
||||
const versions = await getAllDocumentVersions(convId);
|
||||
|
||||
console.log(`${versions.length} Dokumentversionen geladen`);
|
||||
setDocumentVersions(versions);
|
||||
|
||||
// Wenn Versionen existieren, nehme die neueste
|
||||
if (cleanVersions.length > 0) {
|
||||
console.log('Setze neuestes Dokument aus Liste', cleanVersions[0]);
|
||||
setCurrentDocument(cleanVersions[0]);
|
||||
if (versions.length > 0) {
|
||||
console.log('Setze neuestes Dokument aus Liste', versions[0]);
|
||||
setCurrentDocument(versions[0]);
|
||||
} else {
|
||||
// Wenn keine Versionen existieren, setze alles zurück
|
||||
console.log('Keine Dokumentversionen vorhanden, setze null');
|
||||
|
|
@ -322,53 +287,26 @@ export default function ConversationScreen() {
|
|||
console.error('Keine aktuelle Konversations-ID verfügbar');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
console.log(`[handleDeleteVersion] Versuche Dokumentversion ${document.version} (ID: ${document.id}) zu löschen`);
|
||||
setIsDocumentLoading(true);
|
||||
|
||||
|
||||
// Debug-Informationen
|
||||
console.log('Aktuelle Konversation:', actualConversationId);
|
||||
console.log('Aktuelles Dokument:', currentDocument?.id);
|
||||
console.log('Zu löschendes Dokument:', document.id);
|
||||
|
||||
|
||||
// Sicherstellen, dass die zu löschende Version nicht aktuell angezeigt wird
|
||||
const isCurrentlyDisplayed = currentDocument?.id === document.id;
|
||||
|
||||
// Direkter Zugriff auf Supabase
|
||||
console.log('Versuche direkte Löschung mit Supabase via delete');
|
||||
|
||||
// Wir verwenden einen speziellen Trick, um sicherzustellen, dass die Löschung
|
||||
// Zeit hat, vollständig durchgeführt zu werden, bevor wir weitermachen
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
|
||||
try {
|
||||
// Direktes Löschen über die normale DELETE-Methode
|
||||
const { error } = await supabase
|
||||
.from('documents')
|
||||
.delete()
|
||||
.eq('id', document.id);
|
||||
|
||||
if (error) {
|
||||
console.error('Fehler beim direkten Löschen:', error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
console.log('Dokument erfolgreich gelöscht');
|
||||
|
||||
// Warten, damit die Datenbank Zeit hat, sich zu aktualisieren
|
||||
await new Promise(resolve => setTimeout(resolve, 800));
|
||||
} catch (deleteError) {
|
||||
console.error('Fehler beim Löschen:', deleteError);
|
||||
throw deleteError;
|
||||
}
|
||||
|
||||
const success = true; // Wenn wir bis hierher kommen, war es erfolgreich
|
||||
console.log('Löschvorgang Ergebnis: Erfolgreich');
|
||||
|
||||
|
||||
// Löschen über den Service
|
||||
console.log('Lösche Dokument über Backend API...');
|
||||
const success = await deleteDocumentVersion(document.id);
|
||||
|
||||
if (success) {
|
||||
console.log(`Dokumentversion ${document.version} erfolgreich gelöscht`);
|
||||
|
||||
|
||||
// Systemische Nachricht hinzufügen
|
||||
const messageId = await addMessage(
|
||||
actualConversationId,
|
||||
|
|
@ -376,44 +314,33 @@ export default function ConversationScreen() {
|
|||
`Dokumentversion ${document.version} wurde gelöscht.`
|
||||
);
|
||||
console.log('System-Nachricht hinzugefügt:', messageId);
|
||||
|
||||
|
||||
// Nachrichten neu laden
|
||||
const dbMessages = await getMessages(actualConversationId);
|
||||
setMessages(convertDbToUiMessages(dbMessages));
|
||||
|
||||
// Dokumentversionen neu laden mit forcierter Aktualisierung
|
||||
// Zuerst kurz warten, damit die DB-Änderungen sich vollständig auswirken können
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
await loadDocumentData(actualConversationId);
|
||||
|
||||
|
||||
// Dokumentversionen neu laden
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
await loadDocumentData(actualConversationId);
|
||||
|
||||
// Wenn die gerade angezeigte Version gelöscht wurde, zur neuesten wechseln
|
||||
if (isCurrentlyDisplayed) {
|
||||
console.log('Aktuell angezeigte Version wurde gelöscht, wechsle zur neuesten');
|
||||
|
||||
// Direkter Supabase-Aufruf für die aktuellste Version
|
||||
const { data: latestData, error: latestError } = await supabase
|
||||
.from('documents')
|
||||
.select('*')
|
||||
.eq('conversation_id', actualConversationId)
|
||||
.order('version', { ascending: false })
|
||||
.limit(1)
|
||||
.maybeSingle();
|
||||
|
||||
if (latestError) {
|
||||
console.error('Fehler beim Laden des neuesten Dokuments:', latestError);
|
||||
} else if (latestData) {
|
||||
console.log('Setze neues aktuelles Dokument:', latestData.id);
|
||||
setCurrentDocument(latestData);
|
||||
const latestDoc = await getLatestDocument(actualConversationId);
|
||||
|
||||
if (latestDoc) {
|
||||
console.log('Setze neues aktuelles Dokument:', latestDoc.id);
|
||||
setCurrentDocument(latestDoc);
|
||||
} else {
|
||||
console.log('Kein neuestes Dokument gefunden, setze null');
|
||||
setCurrentDocument(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Kurze Pause für bessere Benutzererfahrung
|
||||
setTimeout(() => {
|
||||
setIsVersionsModalVisible(false);
|
||||
|
||||
|
||||
// Erfolgsmeldung anzeigen
|
||||
Alert.alert(
|
||||
"Version gelöscht",
|
||||
|
|
@ -611,27 +538,23 @@ export default function ConversationScreen() {
|
|||
if ((!modelId || modelId === 'undefined') && !modelData && actualConversationId) {
|
||||
try {
|
||||
console.log('Hole Modell aus der Konversation:', actualConversationId);
|
||||
const { data, error } = await supabase
|
||||
.from('conversations')
|
||||
.select('model_id')
|
||||
.eq('id', actualConversationId)
|
||||
.single();
|
||||
|
||||
if (error) {
|
||||
console.error('Fehler beim Laden des Modells aus der Konversation:', error);
|
||||
const convData = await conversationApi.getConversation(actualConversationId);
|
||||
|
||||
if (!convData) {
|
||||
console.error('Fehler beim Laden der Konversation');
|
||||
Alert.alert('Fehler', 'Modell konnte nicht geladen werden.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (data && data.model_id) {
|
||||
console.log('Modell-ID aus der Konversation geladen:', data.model_id);
|
||||
const fetchedModelId = data.model_id;
|
||||
|
||||
|
||||
if (convData.modelId) {
|
||||
console.log('Modell-ID aus der Konversation geladen:', convData.modelId);
|
||||
const fetchedModelId = convData.modelId;
|
||||
|
||||
// Setze das Modell für die nächsten API-Aufrufe
|
||||
const response = await fetch(`/api/models`);
|
||||
const models = await response.json();
|
||||
const model = models.find((m: any) => m.id === fetchedModelId);
|
||||
|
||||
|
||||
if (model) {
|
||||
setModelName(model.name);
|
||||
setModelData(model);
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ import { Ionicons } from '@expo/vector-icons';
|
|||
import { useAuth } from '../context/AuthProvider';
|
||||
import { useAppTheme } from '../theme/ThemeProvider';
|
||||
import CustomDrawer from '../components/CustomDrawer';
|
||||
import {
|
||||
getConversations,
|
||||
getMessages,
|
||||
deleteConversation,
|
||||
archiveConversation
|
||||
import {
|
||||
getConversations,
|
||||
getMessages,
|
||||
deleteConversation,
|
||||
archiveConversation
|
||||
} from '../services/conversation';
|
||||
import { supabase } from '../utils/supabase';
|
||||
import { modelApi } from '../services/api';
|
||||
|
||||
// Typendefinitionen für Konversationen
|
||||
type ConversationItem = {
|
||||
|
|
@ -71,18 +71,14 @@ export default function ConversationsScreen() {
|
|||
try {
|
||||
// Lade die Nachrichten der Konversation
|
||||
const messages = await getMessages(conv.id);
|
||||
// Lade das Modell aus der Datenbank
|
||||
const { data: modelData } = await supabase
|
||||
.from('models')
|
||||
.select('name')
|
||||
.eq('id', conv.model_id)
|
||||
.single();
|
||||
|
||||
// Lade das Modell über die Backend API
|
||||
const modelData = await modelApi.getModel(conv.model_id);
|
||||
|
||||
// Finde die letzte Nachricht (die nicht vom System ist)
|
||||
const lastMessage = messages
|
||||
.filter(msg => msg.sender !== 'system')
|
||||
.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0];
|
||||
|
||||
|
||||
if (lastMessage) {
|
||||
conversationItems.push({
|
||||
id: conv.id,
|
||||
|
|
@ -90,7 +86,7 @@ export default function ConversationsScreen() {
|
|||
title: conv.title || 'Unbenannte Konversation',
|
||||
lastMessage: lastMessage.message_text,
|
||||
timestamp: new Date(conv.updated_at),
|
||||
mode: conv.conversation_mode === 'free' ? 'frei' :
|
||||
mode: conv.conversation_mode === 'free' ? 'frei' :
|
||||
conv.conversation_mode === 'guided' ? 'geführt' : 'vorlage'
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
StyleSheet,
|
||||
ScrollView,
|
||||
TouchableOpacity,
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
StyleSheet,
|
||||
ScrollView,
|
||||
TouchableOpacity,
|
||||
ActivityIndicator,
|
||||
useWindowDimensions,
|
||||
Platform
|
||||
|
|
@ -12,8 +12,9 @@ import {
|
|||
import { useTheme } from '@react-navigation/native';
|
||||
import { useRouter } from 'expo-router';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { Document } from '../services/document';
|
||||
import { supabase } from '../utils/supabase';
|
||||
import { Document, getLatestDocument } from '../services/document';
|
||||
import { conversationApi } from '../services/api';
|
||||
import { useAuth } from '../context/AuthProvider';
|
||||
import Markdown from 'react-native-markdown-display';
|
||||
|
||||
type DocumentWithTitle = Document & {
|
||||
|
|
@ -23,11 +24,11 @@ type DocumentWithTitle = Document & {
|
|||
export default function DocumentsScreen() {
|
||||
const { colors } = useTheme();
|
||||
const router = useRouter();
|
||||
const { user } = useAuth();
|
||||
const { width } = useWindowDimensions();
|
||||
const [documents, setDocuments] = useState<DocumentWithTitle[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [userId, setUserId] = useState<string | null>(null);
|
||||
|
||||
|
||||
// Berechne die Anzahl der Spalten basierend auf der Bildschirmbreite
|
||||
const columnsCount = useMemo(() => {
|
||||
// Mobile (schmaler Bildschirm)
|
||||
|
|
@ -41,7 +42,7 @@ export default function DocumentsScreen() {
|
|||
// Desktop oder großes Tablet
|
||||
return 3;
|
||||
}, [width]);
|
||||
|
||||
|
||||
// Berechne die Breite jeder Karte basierend auf der Spaltenanzahl
|
||||
const cardWidth = useMemo(() => {
|
||||
const padding = 16; // Container-Padding rechts und links
|
||||
|
|
@ -49,85 +50,55 @@ export default function DocumentsScreen() {
|
|||
const contentWidth = width - (padding * 2);
|
||||
const gapTotal = gap * (columnsCount - 1);
|
||||
const availableWidth = contentWidth - gapTotal;
|
||||
|
||||
|
||||
// Verhältnis für schmalere Karten, je nach Spaltenanzahl anpassen
|
||||
const widthRatio = columnsCount === 1 ? 0.95 : // Fast volle Breite bei 1 Spalte
|
||||
columnsCount === 2 ? 0.48 : // Etwas schmaler bei 2 Spalten
|
||||
0.31; // Noch schmaler bei 3 Spalten
|
||||
|
||||
|
||||
return (availableWidth * widthRatio);
|
||||
}, [width, columnsCount]);
|
||||
|
||||
useEffect(() => {
|
||||
const checkUser = async () => {
|
||||
const { data } = await supabase.auth.getUser();
|
||||
if (data?.user) {
|
||||
setUserId(data.user.id);
|
||||
} else {
|
||||
// In einer echten App würden wir hier zur Login-Seite weiterleiten
|
||||
// Für jetzt verwenden wir eine Test-ID
|
||||
setUserId('test-user-id');
|
||||
}
|
||||
};
|
||||
checkUser();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (userId) {
|
||||
if (user?.id) {
|
||||
loadDocuments();
|
||||
}
|
||||
}, [userId]);
|
||||
}, [user]);
|
||||
|
||||
const loadDocuments = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
// Lade alle Konversationen des Benutzers, die im Dokumentmodus sind
|
||||
const { data: conversations, error: convError } = await supabase
|
||||
.from('conversations')
|
||||
.select('id, title, document_mode')
|
||||
.eq('user_id', userId)
|
||||
.eq('document_mode', true);
|
||||
|
||||
if (convError) {
|
||||
console.error('Fehler beim Laden der Konversationen:', convError);
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!conversations || conversations.length === 0) {
|
||||
|
||||
// Lade alle Konversationen des Benutzers über die Backend-API
|
||||
const conversations = await conversationApi.getConversations();
|
||||
|
||||
// Filtere nur Konversationen im Dokumentmodus
|
||||
const documentConversations = conversations.filter(conv => conv.documentMode);
|
||||
|
||||
if (documentConversations.length === 0) {
|
||||
setDocuments([]);
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Für jede Konversation den neuesten Dokumentstand laden
|
||||
const latestDocuments: DocumentWithTitle[] = [];
|
||||
|
||||
for (const conv of conversations) {
|
||||
const { data: docData, error: docError } = await supabase
|
||||
.from('documents')
|
||||
.select('*')
|
||||
.eq('conversation_id', conv.id)
|
||||
.order('version', { ascending: false })
|
||||
.limit(1)
|
||||
.single();
|
||||
|
||||
if (docError) {
|
||||
if (docError.code !== 'PGRST116') { // Ignore "No rows found" error
|
||||
console.error(`Fehler beim Laden des Dokuments für Konversation ${conv.id}:`, docError);
|
||||
|
||||
for (const conv of documentConversations) {
|
||||
try {
|
||||
const docData = await getLatestDocument(conv.id);
|
||||
|
||||
if (docData) {
|
||||
latestDocuments.push({
|
||||
...docData,
|
||||
conversation_title: conv.title || 'Unbenannte Konversation'
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (docData) {
|
||||
latestDocuments.push({
|
||||
...docData,
|
||||
conversation_title: conv.title || 'Unbenannte Konversation'
|
||||
});
|
||||
} catch (docError) {
|
||||
console.error(`Fehler beim Laden des Dokuments für Konversation ${conv.id}:`, docError);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
setDocuments(latestDocuments);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Dokumente:', error);
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import CustomDrawer from '../components/CustomDrawer';
|
|||
import { useAppTheme } from '../theme/ThemeProvider';
|
||||
import { getConversations, getMessages, deleteConversation, archiveConversation } from '../services/conversation';
|
||||
import { getUserSpaces, Space } from '../services/space';
|
||||
import { supabase } from '../utils/supabase';
|
||||
import { modelApi } from '../services/api';
|
||||
|
||||
// Typendefinitionen für Konversationen
|
||||
type ConversationItem = {
|
||||
|
|
@ -75,18 +75,14 @@ export default function HomeScreen() {
|
|||
try {
|
||||
// Lade die Nachrichten der Konversation
|
||||
const messages = await getMessages(conv.id);
|
||||
// Lade das Modell aus der Datenbank
|
||||
const { data: modelData } = await supabase
|
||||
.from('models')
|
||||
.select('name')
|
||||
.eq('id', conv.model_id)
|
||||
.single();
|
||||
|
||||
// Lade das Modell über die Backend API
|
||||
const modelData = await modelApi.getModel(conv.model_id);
|
||||
|
||||
// Finde die letzte Nachricht (die nicht vom System ist)
|
||||
const lastMessage = messages
|
||||
.filter(msg => msg.sender !== 'system')
|
||||
.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0];
|
||||
|
||||
|
||||
if (lastMessage) {
|
||||
conversationItems.push({
|
||||
id: conv.id,
|
||||
|
|
@ -94,7 +90,7 @@ export default function HomeScreen() {
|
|||
title: conv.title || 'Unbenannte Konversation',
|
||||
lastMessage: lastMessage.message_text,
|
||||
timestamp: new Date(conv.updated_at),
|
||||
mode: conv.conversation_mode === 'free' ? 'frei' :
|
||||
mode: conv.conversation_mode === 'free' ? 'frei' :
|
||||
conv.conversation_mode === 'guided' ? 'geführt' : 'vorlage'
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import { useRouter } from 'expo-router';
|
|||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { useAuth } from '../context/AuthProvider';
|
||||
import { useAppTheme } from '../theme/ThemeProvider';
|
||||
import { supabase } from '../utils/supabase';
|
||||
|
||||
// Typendefinitionen für die Token-Nutzung
|
||||
type ModelUsage = {
|
||||
|
|
@ -44,46 +43,18 @@ export default function ProfileScreen() {
|
|||
const [selectedPeriod, setSelectedPeriod] = useState<'day' | 'month' | 'year'>('month');
|
||||
|
||||
// Funktion zum Laden der Token-Nutzungsdaten
|
||||
// TODO: Backend-Endpoints für Usage-Statistiken implementieren
|
||||
const loadUsageData = async () => {
|
||||
if (!user) return;
|
||||
|
||||
|
||||
setIsLoading(true);
|
||||
try {
|
||||
// Lade die Token-Nutzung nach Modell
|
||||
const { data: modelData, error: modelError } = await supabase
|
||||
.rpc('get_user_model_usage', { user_id: user.id });
|
||||
|
||||
if (modelError) {
|
||||
console.error('Fehler beim Laden der Modellnutzung:', modelError);
|
||||
} else if (modelData) {
|
||||
setModelUsage(modelData as ModelUsage[]);
|
||||
}
|
||||
|
||||
// Lade die Token-Nutzung nach Zeitraum
|
||||
const { data: periodData, error: periodError } = await supabase
|
||||
.rpc('get_user_usage_by_period', {
|
||||
user_id: user.id,
|
||||
period: selectedPeriod
|
||||
});
|
||||
|
||||
if (periodError) {
|
||||
console.error('Fehler beim Laden der Zeitraumnutzung:', periodError);
|
||||
} else if (periodData) {
|
||||
setPeriodUsage(periodData as UsageByPeriod[]);
|
||||
}
|
||||
|
||||
// Berechne die Zusammenfassung
|
||||
if (modelData) {
|
||||
const totalCost = (modelData as ModelUsage[]).reduce((sum, model) => sum + model.total_cost, 0);
|
||||
const totalTokens = (modelData as ModelUsage[]).reduce((sum, model) => sum + model.total_tokens, 0);
|
||||
|
||||
setSummary({
|
||||
totalCost,
|
||||
totalTokens,
|
||||
modelCount: (modelData as ModelUsage[]).length,
|
||||
periodCount: periodData ? (periodData as UsageByPeriod[]).length : 0
|
||||
});
|
||||
}
|
||||
// Usage-Statistiken sind noch nicht über die Backend-API verfügbar
|
||||
// Setze leere Daten und zeige Info-Text an
|
||||
console.log('Usage-Statistiken: Backend-Endpoints noch nicht implementiert');
|
||||
setModelUsage([]);
|
||||
setPeriodUsage([]);
|
||||
setSummary(null);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Nutzungsdaten:', error);
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import { useAppTheme } from '../theme/ThemeProvider';
|
|||
import ModelDropdown from './ModelDropdown';
|
||||
import { useRouter } from 'expo-router';
|
||||
import { createConversation, addMessage } from '../services/conversation';
|
||||
import { supabase } from '../utils/supabase';
|
||||
import { useAuth } from '../context/AuthProvider';
|
||||
import { Template, getTemplates } from '../services/template';
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import { useAppTheme } from '../theme/ThemeProvider';
|
|||
import ModelDropdown from './ModelDropdown';
|
||||
import { useRouter } from 'expo-router';
|
||||
import { createConversation, addMessage } from '../services/conversation';
|
||||
import { supabase } from '../utils/supabase';
|
||||
import { useAuth } from '../context/AuthProvider';
|
||||
import { Template, getTemplates } from '../services/template';
|
||||
import { Space, getUserSpaces } from '../services/space';
|
||||
|
|
|
|||
|
|
@ -14,11 +14,7 @@
|
|||
"prebuild": "expo prebuild",
|
||||
"lint": "eslint \"**/*.{js,jsx,ts,tsx}\" && prettier -c \"**/*.{js,jsx,ts,tsx,json}\"",
|
||||
"format": "eslint \"**/*.{js,jsx,ts,tsx}\" --fix && prettier \"**/*.{js,jsx,ts,tsx,json}\" --write",
|
||||
"web": "expo start --web",
|
||||
"supabase:cli": "node --experimental-json-modules scripts/supabase-cli.js",
|
||||
"supabase:update-models": "node --experimental-json-modules scripts/update_models.js",
|
||||
"supabase:setup": "node --experimental-json-modules scripts/setup_supabase.js",
|
||||
"supabase:setup-spaces": "node --experimental-json-modules scripts/spaces/setup_spaces.js"
|
||||
"web": "expo start --web"
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/vector-icons": "^14.0.0",
|
||||
|
|
@ -26,7 +22,6 @@
|
|||
"@react-navigation/bottom-tabs": "^7.0.5",
|
||||
"@react-navigation/drawer": "^7.0.0",
|
||||
"@react-navigation/native": "^7.0.3",
|
||||
"@supabase/supabase-js": "^2.38.4",
|
||||
"expo": "^52.0.39",
|
||||
"expo-constants": "~17.0.8",
|
||||
"expo-dev-client": "~5.0.4",
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
import { createClient } from '@supabase/supabase-js';
|
||||
|
||||
const supabaseUrl = process.env.EXPO_PUBLIC_SUPABASE_URL || '';
|
||||
const supabaseAnonKey = process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY || '';
|
||||
|
||||
// Überprüfe, ob wir in einer Browser-Umgebung sind
|
||||
const isBrowser = typeof window !== 'undefined';
|
||||
|
||||
// Importiere AsyncStorage nur, wenn wir in einer Browser-Umgebung sind
|
||||
let AsyncStorage;
|
||||
if (isBrowser) {
|
||||
AsyncStorage = require('@react-native-async-storage/async-storage').default;
|
||||
}
|
||||
|
||||
// Erstelle Supabase-Client mit unterschiedlichen Konfigurationen je nach Umgebung
|
||||
export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
|
||||
auth: isBrowser
|
||||
? {
|
||||
storage: AsyncStorage,
|
||||
autoRefreshToken: true,
|
||||
persistSession: true,
|
||||
detectSessionInUrl: false,
|
||||
}
|
||||
: {
|
||||
// Dummy-Storage für serverseitiges Rendering
|
||||
storage: {
|
||||
getItem: () => Promise.resolve(null),
|
||||
setItem: () => Promise.resolve(),
|
||||
removeItem: () => Promise.resolve(),
|
||||
},
|
||||
autoRefreshToken: false,
|
||||
persistSession: false,
|
||||
detectSessionInUrl: false,
|
||||
},
|
||||
});
|
||||
|
|
@ -1,9 +1,5 @@
|
|||
# Mana Core Auth Configuration
|
||||
PUBLIC_MANA_CORE_AUTH_URL=http://localhost:3001
|
||||
|
||||
# Supabase Configuration (for database only, not auth)
|
||||
PUBLIC_SUPABASE_URL=https://your-project.supabase.co
|
||||
PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key
|
||||
|
||||
# Chat Backend API
|
||||
PUBLIC_BACKEND_URL=http://localhost:3002
|
||||
|
|
|
|||
|
|
@ -33,14 +33,11 @@
|
|||
"@manacore/shared-branding": "workspace:*",
|
||||
"@manacore/shared-i18n": "workspace:*",
|
||||
"@manacore/shared-icons": "workspace:*",
|
||||
"@manacore/shared-supabase": "workspace:*",
|
||||
"@manacore/shared-tailwind": "workspace:*",
|
||||
"@manacore/shared-theme": "workspace:*",
|
||||
"@manacore/shared-theme-ui": "workspace:*",
|
||||
"@manacore/shared-ui": "workspace:*",
|
||||
"@manacore/shared-utils": "workspace:*",
|
||||
"@supabase/ssr": "^0.6.1",
|
||||
"@supabase/supabase-js": "^2.81.1",
|
||||
"marked": "^17.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
/**
|
||||
* Supabase Client for Chat Web App
|
||||
* Uses the same Supabase instance as the mobile app
|
||||
*/
|
||||
|
||||
import { createClient } from '@supabase/supabase-js';
|
||||
import { createBrowserClient, createServerClient } from '@supabase/ssr';
|
||||
import { env } from '$env/dynamic/public';
|
||||
import type { Cookies } from '@sveltejs/kit';
|
||||
|
||||
const supabaseUrl = env.PUBLIC_SUPABASE_URL || '';
|
||||
const supabaseAnonKey = env.PUBLIC_SUPABASE_ANON_KEY || '';
|
||||
|
||||
/**
|
||||
* Browser client for client-side operations
|
||||
*/
|
||||
export function createSupabaseBrowserClient() {
|
||||
return createBrowserClient(supabaseUrl, supabaseAnonKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Server client for SSR operations
|
||||
*/
|
||||
export function createSupabaseServerClient(cookies: Cookies) {
|
||||
return createServerClient(supabaseUrl, supabaseAnonKey, {
|
||||
cookies: {
|
||||
getAll() {
|
||||
return cookies.getAll();
|
||||
},
|
||||
setAll(cookiesToSet) {
|
||||
cookiesToSet.forEach(({ name, value, options }) => {
|
||||
cookies.set(name, value, { ...options, path: '/' });
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple client for basic operations (no SSR)
|
||||
*/
|
||||
export const supabase = createClient(supabaseUrl, supabaseAnonKey);
|
||||
Loading…
Add table
Add a link
Reference in a new issue