mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 19:41:09 +02:00
fix(memoro): migrate web services + add credits balance endpoint
- server: add GET /api/v1/credits/balance (proxies to mana-credits via getBalance) - web/creditService: rewrite to new paths (pricing, balance, retry-transcription, retry-headline) - web/questionService: use authStore.getAccessToken() + new /api/v1/memos/:id/question path - web/audioUploadService: fix accessToken param name for triggerTranscription Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a893b07b70
commit
8e496ff417
4 changed files with 94 additions and 333 deletions
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
import { Hono } from 'hono';
|
||||
import { validateCredits, consumeCredits, COSTS } from '../lib/credits';
|
||||
import { getBalance } from '@manacore/shared-hono';
|
||||
|
||||
export const creditRoutes = new Hono();
|
||||
|
||||
|
|
@ -12,6 +13,18 @@ creditRoutes.get('/pricing', (c) => {
|
|||
return c.json({ costs: COSTS });
|
||||
});
|
||||
|
||||
// GET /balance — authenticated, returns user's credit balance
|
||||
creditRoutes.get('/balance', async (c) => {
|
||||
const userId = c.get('userId') as string;
|
||||
try {
|
||||
const balance = await getBalance(userId);
|
||||
return c.json({ credits: balance.balance, totalEarned: balance.totalEarned, totalSpent: balance.totalSpent });
|
||||
} catch (err) {
|
||||
console.error('[credits] Balance error:', err);
|
||||
return c.json({ error: 'Failed to fetch balance' }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
// POST /check — validate credits (requires auth via parent router)
|
||||
creditRoutes.post('/check', async (c) => {
|
||||
const userId = c.get('userId') as string;
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ export async function uploadAndProcessAudio({
|
|||
blueprintId,
|
||||
recordingLanguages,
|
||||
enableDiarization,
|
||||
appToken,
|
||||
accessToken: appToken,
|
||||
mediaType,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
/**
|
||||
* Credit Service for Memoro Web
|
||||
* Handles credit operations and pricing
|
||||
*
|
||||
* Pattern adapted from memoro_app/features/credits/creditService.ts
|
||||
* Handles credit operations and pricing via memoro-server (Hono/Bun).
|
||||
*/
|
||||
|
||||
import { env } from '$lib/config/env';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
|
||||
const SERVER_URL = () => env.server.memoroUrl.replace(/\/$/, '');
|
||||
|
||||
export interface CreditCheckResponse {
|
||||
hasEnoughCredits: boolean;
|
||||
|
|
@ -16,347 +17,156 @@ export interface CreditCheckResponse {
|
|||
estimatedCostPerHour?: number;
|
||||
}
|
||||
|
||||
export interface CreditConsumptionResponse {
|
||||
success: boolean;
|
||||
message: string;
|
||||
creditsConsumed: number;
|
||||
creditType: 'user' | 'space';
|
||||
durationMinutes?: number;
|
||||
}
|
||||
|
||||
export interface OperationCreditResponse {
|
||||
success: boolean;
|
||||
message: string;
|
||||
creditsConsumed: number;
|
||||
creditType: 'user' | 'space';
|
||||
operation: string;
|
||||
}
|
||||
|
||||
export interface PricingResponse {
|
||||
operationCosts: {
|
||||
TRANSCRIPTION_PER_HOUR: number;
|
||||
costs: {
|
||||
TRANSCRIPTION_PER_MINUTE: number;
|
||||
HEADLINE_GENERATION: number;
|
||||
MEMORY_CREATION: number;
|
||||
BLUEPRINT_PROCESSING: number;
|
||||
QUESTION_MEMO: number;
|
||||
NEW_MEMORY: number;
|
||||
MEMO_COMBINE: number;
|
||||
MEMO_SHARING: number;
|
||||
SPACE_OPERATION: number;
|
||||
MEETING_RECORDING_PER_MINUTE: number;
|
||||
};
|
||||
transcriptionPerHour: number;
|
||||
lastUpdated: string;
|
||||
}
|
||||
|
||||
type OperationType =
|
||||
| 'HEADLINE_GENERATION'
|
||||
| 'MEMORY_CREATION'
|
||||
| 'BLUEPRINT_PROCESSING'
|
||||
| 'MEMO_SHARING'
|
||||
| 'SPACE_OPERATION'
|
||||
| 'QUESTION_MEMO'
|
||||
| 'NEW_MEMORY'
|
||||
| 'MEMO_COMBINE';
|
||||
|
||||
const FALLBACK_COSTS: Record<OperationType, number> = {
|
||||
HEADLINE_GENERATION: 10,
|
||||
MEMORY_CREATION: 10,
|
||||
BLUEPRINT_PROCESSING: 5,
|
||||
QUESTION_MEMO: 5,
|
||||
NEW_MEMORY: 5,
|
||||
MEMO_COMBINE: 5,
|
||||
};
|
||||
|
||||
class CreditService {
|
||||
private readonly memoroServiceUrl: string;
|
||||
private readonly manaServiceUrl: string;
|
||||
private creditUpdateCallbacks: ((creditsConsumed: number) => void)[] = [];
|
||||
private cachedPricing: PricingResponse | null = null;
|
||||
private pricingLastFetched: number = 0;
|
||||
private readonly PRICING_CACHE_DURATION = 30 * 60 * 1000; // 30 minutes
|
||||
private pricingLastFetched = 0;
|
||||
private readonly PRICING_CACHE_DURATION = 30 * 60 * 1000;
|
||||
|
||||
constructor() {
|
||||
// Use memoro service URL for all endpoints (including auth proxy)
|
||||
this.memoroServiceUrl = env.middleware.memoroUrl.replace(/\/$/, '');
|
||||
// manaServiceUrl now points to memoro service (auth proxy handles routing)
|
||||
this.manaServiceUrl = this.memoroServiceUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the credit service by preloading pricing
|
||||
* Call this during app startup
|
||||
*/
|
||||
async initialize(): Promise<void> {
|
||||
try {
|
||||
await this.getPricing();
|
||||
console.log('CreditService initialized with backend pricing');
|
||||
} catch (error) {
|
||||
console.warn('CreditService initialization failed, using fallback pricing:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be notified when credits are consumed
|
||||
*/
|
||||
onCreditUpdate(callback: (creditsConsumed: number) => void): () => void {
|
||||
this.creditUpdateCallbacks.push(callback);
|
||||
|
||||
// Return unsubscribe function
|
||||
return () => {
|
||||
const index = this.creditUpdateCallbacks.indexOf(callback);
|
||||
if (index > -1) {
|
||||
this.creditUpdateCallbacks.splice(index, 1);
|
||||
}
|
||||
const i = this.creditUpdateCallbacks.indexOf(callback);
|
||||
if (i > -1) this.creditUpdateCallbacks.splice(i, 1);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify all registered callbacks about credit consumption
|
||||
*/
|
||||
private notifyCreditUpdate(creditsConsumed: number): void {
|
||||
this.creditUpdateCallbacks.forEach((callback) => {
|
||||
try {
|
||||
callback(creditsConsumed);
|
||||
} catch (error) {
|
||||
console.error('Error in credit update callback:', error);
|
||||
}
|
||||
triggerCreditUpdate(creditsConsumed: number): void {
|
||||
this.creditUpdateCallbacks.forEach((cb) => {
|
||||
try { cb(creditsConsumed); } catch {}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Public method to manually trigger credit update notifications
|
||||
*/
|
||||
triggerCreditUpdate(creditsConsumed: number): void {
|
||||
this.notifyCreditUpdate(creditsConsumed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch pricing information from backend with caching
|
||||
*/
|
||||
async getPricing(): Promise<PricingResponse> {
|
||||
const now = Date.now();
|
||||
|
||||
// Return cached pricing if still valid
|
||||
if (this.cachedPricing && now - this.pricingLastFetched < this.PRICING_CACHE_DURATION) {
|
||||
return this.cachedPricing;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${this.memoroServiceUrl}/memoro/credits/pricing`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const response = await fetch(`${SERVER_URL()}/api/v1/credits/pricing`);
|
||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||
const pricing = await response.json();
|
||||
this.cachedPricing = pricing;
|
||||
this.pricingLastFetched = now;
|
||||
|
||||
return pricing;
|
||||
} catch (error) {
|
||||
console.error('Error fetching pricing:', error);
|
||||
|
||||
// Fallback to cached pricing if available
|
||||
if (this.cachedPricing) {
|
||||
return this.cachedPricing;
|
||||
}
|
||||
|
||||
// Ultimate fallback
|
||||
if (this.cachedPricing) return this.cachedPricing;
|
||||
return {
|
||||
operationCosts: {
|
||||
TRANSCRIPTION_PER_HOUR: 120,
|
||||
costs: {
|
||||
TRANSCRIPTION_PER_MINUTE: 2,
|
||||
HEADLINE_GENERATION: 10,
|
||||
MEMORY_CREATION: 10,
|
||||
BLUEPRINT_PROCESSING: 5,
|
||||
QUESTION_MEMO: 5,
|
||||
NEW_MEMORY: 5,
|
||||
MEMO_COMBINE: 5,
|
||||
MEMO_SHARING: 1,
|
||||
SPACE_OPERATION: 2,
|
||||
MEETING_RECORDING_PER_MINUTE: 2,
|
||||
},
|
||||
transcriptionPerHour: 120,
|
||||
lastUpdated: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user credits directly from mana-core-middleware
|
||||
*/
|
||||
async getUserCredits(appToken: string): Promise<{ credits: number } | null> {
|
||||
async getUserCredits(token: string): Promise<{ credits: number } | null> {
|
||||
try {
|
||||
console.log(
|
||||
'[CreditService] Fetching user credits from:',
|
||||
`${this.manaServiceUrl}/auth/credits`
|
||||
);
|
||||
|
||||
if (!appToken) {
|
||||
console.error('[CreditService] No authentication token available for credits fetch');
|
||||
throw new Error('No authentication token available');
|
||||
}
|
||||
|
||||
const response = await fetch(`${this.manaServiceUrl}/auth/credits`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${appToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
if (!token) throw new Error('No authentication token available');
|
||||
const response = await fetch(`${SERVER_URL()}/api/v1/credits/balance`, {
|
||||
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
|
||||
});
|
||||
|
||||
console.log('[CreditService] Credits response status:', response.status);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
console.error('[CreditService] Credits fetch error:', errorData);
|
||||
throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log('[CreditService] Credits data received:', data);
|
||||
|
||||
// Handle wrapped response structure from backend
|
||||
if (data.data && typeof data.data.credits === 'number') {
|
||||
return { credits: data.data.credits };
|
||||
}
|
||||
|
||||
// Fallback to direct structure if already in correct format
|
||||
return data;
|
||||
if (!response.ok) return null;
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('[CreditService] Error fetching user credits:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get estimated cost for operations using backend pricing
|
||||
*/
|
||||
async getOperationCost(operation: OperationType): Promise<number> {
|
||||
try {
|
||||
const pricing = await this.getPricing();
|
||||
return pricing.operationCosts[operation];
|
||||
} catch (error) {
|
||||
console.error('Error getting operation cost:', error);
|
||||
// Fallback to hardcoded costs
|
||||
const fallbackCosts: Record<OperationType, number> = {
|
||||
HEADLINE_GENERATION: 10,
|
||||
MEMORY_CREATION: 10,
|
||||
BLUEPRINT_PROCESSING: 5,
|
||||
MEMO_SHARING: 1,
|
||||
SPACE_OPERATION: 2,
|
||||
QUESTION_MEMO: 5,
|
||||
NEW_MEMORY: 5,
|
||||
MEMO_COMBINE: 5,
|
||||
};
|
||||
return fallbackCosts[operation];
|
||||
return pricing.costs[operation] ?? FALLBACK_COSTS[operation];
|
||||
} catch {
|
||||
return FALLBACK_COSTS[operation];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate cost for memo combination based on number of memos
|
||||
*/
|
||||
async calculateMemoCombineCost(memoCount: number): Promise<number> {
|
||||
const costPerMemo = await this.getOperationCost('MEMO_COMBINE');
|
||||
return memoCount * costPerMemo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronous version for immediate UI display (uses cached values)
|
||||
*/
|
||||
getOperationCostSync(operation: OperationType): number {
|
||||
if (this.cachedPricing) {
|
||||
return this.cachedPricing.operationCosts[operation];
|
||||
}
|
||||
if (this.cachedPricing) return this.cachedPricing.costs[operation] ?? FALLBACK_COSTS[operation];
|
||||
return FALLBACK_COSTS[operation];
|
||||
}
|
||||
|
||||
// Fallback to hardcoded costs if no cache
|
||||
const fallbackCosts: Record<OperationType, number> = {
|
||||
HEADLINE_GENERATION: 10,
|
||||
MEMORY_CREATION: 10,
|
||||
BLUEPRINT_PROCESSING: 5,
|
||||
MEMO_SHARING: 1,
|
||||
SPACE_OPERATION: 2,
|
||||
QUESTION_MEMO: 5,
|
||||
NEW_MEMORY: 5,
|
||||
MEMO_COMBINE: 5,
|
||||
};
|
||||
return fallbackCosts[operation];
|
||||
async calculateMemoCombineCost(memoCount: number): Promise<number> {
|
||||
return memoCount * (await this.getOperationCost('MEMO_COMBINE'));
|
||||
}
|
||||
|
||||
calculateMemoCombineCostSync(memoCount: number): number {
|
||||
return memoCount * this.getOperationCostSync('MEMO_COMBINE');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retry transcription for a failed memo using the reprocess-memo endpoint
|
||||
*/
|
||||
async retryTranscription(
|
||||
memoId: string,
|
||||
appToken: string
|
||||
): Promise<{ success: boolean; message: string }> {
|
||||
try {
|
||||
if (!appToken) {
|
||||
throw new Error('No authentication token available');
|
||||
}
|
||||
|
||||
const response = await fetch(`${this.memoroServiceUrl}/memoro/reprocess-memo`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${appToken}`,
|
||||
},
|
||||
body: JSON.stringify({ memoId }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: result.message || 'Memo reprocessing started successfully',
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error reprocessing memo:', error);
|
||||
throw error;
|
||||
async retryTranscription(memoId: string, token: string): Promise<{ success: boolean; message: string }> {
|
||||
if (!token) throw new Error('No authentication token available');
|
||||
const response = await fetch(`${SERVER_URL()}/api/v1/memos/${memoId}/retry-transcription`, {
|
||||
method: 'POST',
|
||||
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
|
||||
});
|
||||
if (!response.ok) {
|
||||
const d = await response.json().catch(() => ({}));
|
||||
throw new Error(d.error || `HTTP ${response.status}`);
|
||||
}
|
||||
return { success: true, message: 'Memo reprocessing started successfully' };
|
||||
}
|
||||
|
||||
/**
|
||||
* Retry headline generation for a failed memo
|
||||
*/
|
||||
async retryHeadline(
|
||||
memoId: string,
|
||||
appToken: string
|
||||
): Promise<{ success: boolean; message: string }> {
|
||||
try {
|
||||
if (!appToken) {
|
||||
throw new Error('No authentication token available');
|
||||
}
|
||||
|
||||
const response = await fetch(`${this.memoroServiceUrl}/memoro/retry-headline`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${appToken}`,
|
||||
},
|
||||
body: JSON.stringify({ memoId }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: result.message || 'Headline generation retry initiated successfully',
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error retrying headline generation:', error);
|
||||
throw error;
|
||||
async retryHeadline(memoId: string, token: string): Promise<{ success: boolean; message: string }> {
|
||||
if (!token) throw new Error('No authentication token available');
|
||||
const response = await fetch(`${SERVER_URL()}/api/v1/memos/${memoId}/retry-headline`, {
|
||||
method: 'POST',
|
||||
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
|
||||
});
|
||||
if (!response.ok) {
|
||||
const d = await response.json().catch(() => ({}));
|
||||
throw new Error(d.error || `HTTP ${response.status}`);
|
||||
}
|
||||
return { success: true, message: 'Headline generation retry initiated successfully' };
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton instance
|
||||
export const creditService = new CreditService();
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
/**
|
||||
* Question Service for memoro-web
|
||||
* Handles Q&A functionality for memos
|
||||
* Handles Q&A functionality for memos via memoro-server (Hono/Bun).
|
||||
*/
|
||||
|
||||
import { env } from '$lib/config/env';
|
||||
import { tokenManager } from './tokenManager';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { createAuthClient } from '$lib/supabaseClient';
|
||||
|
||||
const SERVER_URL = () => env.server.memoroUrl.replace(/\/$/, '');
|
||||
|
||||
export interface QuestionResult {
|
||||
success: boolean;
|
||||
answer?: string;
|
||||
memoryId?: string;
|
||||
error?: string;
|
||||
creditsConsumed?: number;
|
||||
|
|
@ -22,113 +25,50 @@ export interface Memory {
|
|||
}
|
||||
|
||||
class QuestionService {
|
||||
/**
|
||||
* Ask a question about a memo
|
||||
* This calls the memoro middleware service to generate an AI answer
|
||||
*/
|
||||
async askQuestion(memoId: string, question: string): Promise<QuestionResult> {
|
||||
if (!memoId || !question.trim()) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Invalid memo ID or question',
|
||||
};
|
||||
return { success: false, error: 'Invalid memo ID or question' };
|
||||
}
|
||||
|
||||
try {
|
||||
// Get a valid token
|
||||
const token = await tokenManager.getValidToken();
|
||||
const token = await authStore.getAccessToken();
|
||||
if (!token) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Nicht authentifiziert. Bitte melden Sie sich erneut an.',
|
||||
};
|
||||
return { success: false, error: 'Nicht authentifiziert. Bitte melden Sie sich erneut an.' };
|
||||
}
|
||||
|
||||
// Get the memoro service URL
|
||||
const memoroServiceUrl = env.middleware.memoroUrl?.replace(/\/$/, '');
|
||||
if (!memoroServiceUrl) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Memoro service URL nicht konfiguriert',
|
||||
};
|
||||
}
|
||||
|
||||
// Call the memoro service
|
||||
const response = await fetch(`${memoroServiceUrl}/memoro/question-memo`, {
|
||||
const response = await fetch(`${SERVER_URL()}/api/v1/memos/${memoId}/question`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
memo_id: memoId,
|
||||
question: question.trim(),
|
||||
}),
|
||||
body: JSON.stringify({ question: question.trim() }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
|
||||
// Handle specific error codes
|
||||
if (response.status === 402) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Nicht genügend Mana. Bitte laden Sie Ihr Konto auf.',
|
||||
};
|
||||
return { success: false, error: 'Nicht genügend Mana. Bitte laden Sie Ihr Konto auf.' };
|
||||
}
|
||||
|
||||
if (response.status === 401) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Sitzung abgelaufen. Bitte melden Sie sich erneut an.',
|
||||
};
|
||||
return { success: false, error: 'Sitzung abgelaufen. Bitte melden Sie sich erneut an.' };
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: errorData.message || `Fehler: ${response.status} ${response.statusText}`,
|
||||
};
|
||||
const d = await response.json().catch(() => ({}));
|
||||
return { success: false, error: d.error || `Fehler: ${response.status}` };
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data?.success && data?.memory_id) {
|
||||
return {
|
||||
success: true,
|
||||
memoryId: data.memory_id,
|
||||
creditsConsumed: data.creditsConsumed,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: data?.error || 'Unbekannter Fehler bei der Verarbeitung',
|
||||
};
|
||||
return { success: true, answer: data.answer, creditsConsumed: data.creditsConsumed };
|
||||
} catch (error) {
|
||||
console.error('Error asking question:', error);
|
||||
|
||||
// Check for network errors
|
||||
if (error instanceof TypeError && error.message.includes('fetch')) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Netzwerkfehler. Bitte überprüfen Sie Ihre Internetverbindung.',
|
||||
};
|
||||
return { success: false, error: 'Netzwerkfehler. Bitte überprüfen Sie Ihre Internetverbindung.' };
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unbekannter Fehler',
|
||||
};
|
||||
return { success: false, error: error instanceof Error ? error.message : 'Unbekannter Fehler' };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load memories for a memo
|
||||
*/
|
||||
async loadMemories(memoId: string): Promise<Memory[]> {
|
||||
try {
|
||||
const supabase = await createAuthClient();
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('memories')
|
||||
.select('id, title, content, metadata')
|
||||
|
|
@ -140,7 +80,6 @@ class QuestionService {
|
|||
console.error('Error loading memories:', error);
|
||||
return [];
|
||||
}
|
||||
|
||||
return data || [];
|
||||
} catch (error) {
|
||||
console.error('Error loading memories:', error);
|
||||
|
|
@ -149,5 +88,4 @@ class QuestionService {
|
|||
}
|
||||
}
|
||||
|
||||
// Export singleton instance
|
||||
export const questionService = new QuestionService();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue