402 lines
14 KiB
TypeScript
402 lines
14 KiB
TypeScript
|
|
import { BookProject, Chapter, Entity, Idea, WorkflowData, UserProfile } from '../types';
|
|
|
|
// --- CONFIGURATION ---
|
|
const AUTH_API_ROOT = '/api/user-auth';
|
|
const DATA_API_ROOT = '/api/data';
|
|
const INSTANCE_ID = '54770_plumeia_db';
|
|
|
|
// --- HELPERS ---
|
|
|
|
// --- HELPERS ---
|
|
|
|
const getHeaders = () => {
|
|
/*const token = localStorage.getItem('ncb_session_token');
|
|
const sessionData = localStorage.getItem('ncb_session_data');
|
|
console.log("[API] Token:", token);
|
|
console.log("[API] Session Data:", sessionData);*/
|
|
const headers: Record<string, string> = {
|
|
'Content-Type': 'application/json',
|
|
'X-Database-Instance': INSTANCE_ID,
|
|
// 'credentials': 'include'
|
|
};
|
|
/*
|
|
if (token) {
|
|
// Fallback standard auth
|
|
//headers['Authorization'] = `Bearer ${token}`;
|
|
|
|
// User-requested specific Cookie format
|
|
// Note: Browsers typically block manual "Cookie" header setting in fetch.
|
|
// This is implemented per user request, but relies on the environment allowing it
|
|
// or using credentials: 'include' for actual cookies.
|
|
let cookieString = `better-auth.session_token=${token}`;
|
|
if (sessionData) {
|
|
cookieString += `; better-auth.session_data=${sessionData}`;
|
|
}
|
|
headers['Cookie'] = cookieString;
|
|
|
|
console.log("[API] Cookie:", cookieString);
|
|
}
|
|
|
|
// Debug headers
|
|
console.log("[API] Generated Headers:", headers);
|
|
*/
|
|
return headers;
|
|
};
|
|
/*
|
|
const handleAuthResponse = async (res: Response) => {
|
|
const data = await res.json();
|
|
console.log("[API] Auth Response:", data);
|
|
|
|
if (!res.ok) {
|
|
throw new Error(data.message || 'Authentication failed');
|
|
}
|
|
|
|
// Token extraction strategy
|
|
const token =
|
|
data.user?.token ||
|
|
data.token ||
|
|
data.session?.token ||
|
|
data.auth_token ||
|
|
data.accessToken ||
|
|
data._token;
|
|
|
|
// Extract session data if available (often needed for Better Auth)
|
|
const sessionData = data.session_data || data.session?.data || data.user?.session_data;
|
|
|
|
if (token) {
|
|
console.log("[API] Token extracted and saved:", token);
|
|
localStorage.setItem('ncb_session_token', token);
|
|
|
|
if (sessionData) {
|
|
console.log("[API] Session Data extracted and saved");
|
|
localStorage.setItem('ncb_session_data', sessionData);
|
|
}
|
|
} else {
|
|
console.warn("[API] No token found in successful auth response!");
|
|
}
|
|
|
|
return data;
|
|
};*/
|
|
|
|
const handleAuthResponse = async (res: Response) => {
|
|
const data = await res.json();
|
|
console.log("[API] Raw Response Data:", data);
|
|
|
|
if (!res.ok) {
|
|
throw new Error(data.message || 'Authentication failed');
|
|
}
|
|
|
|
// --- LOGIQUE DES SETTEURS ---
|
|
|
|
// 1. Extraction du Token (selon la structure Better-Auth)
|
|
const token = data.session?.token || data.token;
|
|
|
|
// 2. Extraction du Session Data
|
|
// On prend l'objet session complet et on le stringifie pour le stockage
|
|
const sessionData = data.session ? JSON.stringify(data.session) : null;
|
|
|
|
if (token) {
|
|
localStorage.setItem('ncb_session_token', token);
|
|
console.log("[Auth] Token saved to LocalStorage");
|
|
}
|
|
|
|
if (sessionData) {
|
|
localStorage.setItem('ncb_session_data', sessionData);
|
|
console.log("[Auth] Session Data saved to LocalStorage");
|
|
}
|
|
|
|
return data;
|
|
};
|
|
|
|
// --- AUTH SERVICE ---
|
|
|
|
export const authService = {
|
|
/*async getSession() {
|
|
const token = localStorage.getItem('ncb_session_token');
|
|
// Note: Even if we use cookies, we keep token logic as fallback or for UI state
|
|
// if (!token) return null;
|
|
|
|
try {
|
|
const url = `${AUTH_API_ROOT}/get-session?Instance=${INSTANCE_ID}`;
|
|
console.log(`[API] GET session ${url}`);
|
|
|
|
const res = await fetch(url, {
|
|
method: 'GET',
|
|
//headers: getHeaders(),
|
|
credentials: 'include' // IMPORTANT: Send cookies
|
|
});
|
|
|
|
if (!res.ok) {
|
|
console.log(`[API] getSession failed with status: ${res.status}`);
|
|
if (res.status === 401) {
|
|
localStorage.removeItem('ncb_session_token');
|
|
}
|
|
return null;
|
|
}
|
|
|
|
const data = await res.json();
|
|
console.log("[API] getSession success:", data);
|
|
return data.user || data;
|
|
} catch (err) {
|
|
console.error("[Auth] getSession error:", err);
|
|
return null;
|
|
}
|
|
},*/
|
|
|
|
async getSession() {
|
|
console.log("[getSession] démarrage");
|
|
const token = localStorage.getItem('ncb_session_token');
|
|
try {
|
|
const url = `${AUTH_API_ROOT}/get-session?Instance=${INSTANCE_ID}`;
|
|
const headers: Record<string, string> = {
|
|
'Content-Type': 'application/json',
|
|
'X-Database-Instance': INSTANCE_ID
|
|
};
|
|
|
|
// Si on a un token mais pas encore la session complète, on l'envoie
|
|
if (token) {
|
|
headers['Cookie'] = `better-auth.session_token=${token}`;
|
|
// On peut aussi essayer le header Authorization au cas où
|
|
headers['Authorization'] = `Bearer ${token}`;
|
|
}
|
|
|
|
const res = await fetch(url, {
|
|
method: 'GET',
|
|
headers: headers,
|
|
credentials: 'include'
|
|
});
|
|
|
|
if (res.ok) {
|
|
const data = await res.json();
|
|
console.log("[getSession] getSession success:", data);
|
|
// --- LES SETTEURS ICI ---
|
|
if (data.session) {
|
|
localStorage.setItem('ncb_session_token', data.session.token);
|
|
localStorage.setItem('ncb_session_data', JSON.stringify(data.session));
|
|
console.log("[getSession] Données récupérées depuis getSession");
|
|
}
|
|
|
|
return data.user || data;
|
|
}
|
|
return null;
|
|
} catch (err) {
|
|
console.error("[Auth] getSession error:", err);
|
|
return null;
|
|
}
|
|
},
|
|
|
|
|
|
async signIn(email: string, password: string) {
|
|
try {
|
|
// Force X-Database-Instance header as URL param is insufficient for some NCB versions
|
|
const url = `${AUTH_API_ROOT}/sign-in/email?Instance=${INSTANCE_ID}`;
|
|
console.log(`[API] POST ${url}`, { email, password: '***' });
|
|
|
|
const res = await fetch(url, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-Database-Instance': INSTANCE_ID
|
|
},
|
|
body: JSON.stringify({ email, password }),
|
|
credentials: 'include' // IMPORTANT: Receive & Save cookies
|
|
});
|
|
const authData = await handleAuthResponse(res);
|
|
await this.getSession();
|
|
console.log("sign in", res);
|
|
|
|
return await authData;
|
|
} catch (err: any) {
|
|
console.error("[Auth] signIn error:", err);
|
|
return { error: err.message || 'Connection failed' };
|
|
}
|
|
},
|
|
|
|
async signUp(email: string, password: string, name: string) {
|
|
try {
|
|
const url = `${AUTH_API_ROOT}/sign-up/email?Instance=${INSTANCE_ID}`;
|
|
console.log(`[API] POST ${url}`, { email, name });
|
|
|
|
const res = await fetch(url, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-Database-Instance': INSTANCE_ID
|
|
},
|
|
body: JSON.stringify({ email, password, name }),
|
|
credentials: 'include' // IMPORTANT: Receive & Save cookies
|
|
});
|
|
|
|
return await handleAuthResponse(res);
|
|
} catch (err: any) {
|
|
console.error("[Auth] signUp error:", err);
|
|
return { error: err.message || 'Registration failed' };
|
|
}
|
|
},
|
|
|
|
async signOut() {
|
|
try {
|
|
const url = `${AUTH_API_ROOT}/sign-out?Instance=${INSTANCE_ID}`;
|
|
console.log(`[API] POST ${url}`);
|
|
|
|
await fetch(url, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
credentials: 'include' // IMPORTANT: Clear cookies
|
|
});
|
|
} catch (err) {
|
|
console.error("[Auth] signOut error:", err);
|
|
} finally {
|
|
localStorage.removeItem('ncb_session_token');
|
|
}
|
|
}
|
|
};
|
|
|
|
// --- DATA SERVICE ---
|
|
|
|
export const dataService = {
|
|
|
|
// -- PROFILES --
|
|
async getProfile(userId: string) {
|
|
try {
|
|
const encodedId = encodeURIComponent(userId);
|
|
const url = `${DATA_API_ROOT}/read/profiles?Instance=${INSTANCE_ID}&user_id=${encodedId}`;
|
|
console.log(`[API] GET ${url}`);
|
|
|
|
const res = await fetch(url, {
|
|
headers: getHeaders(),
|
|
credentials: 'include'
|
|
});
|
|
const json = await res.json();
|
|
console.log("[Data] getProfile result:", json);
|
|
return json.data?.[0] || null;
|
|
} catch (err) {
|
|
console.error("[Data] getProfile error:", err);
|
|
return null;
|
|
}
|
|
},
|
|
|
|
async createProfile(profileData: any) {
|
|
return this.createItem('profiles', profileData);
|
|
},
|
|
|
|
// -- PROJECTS --
|
|
async getProjects(userId: string) {
|
|
try {
|
|
const encodedId = encodeURIComponent(userId);
|
|
const url = `${DATA_API_ROOT}/read/projects?Instance=${INSTANCE_ID}&user_id=${encodedId}`;
|
|
console.log(`[API] GET ${url}`);
|
|
|
|
const res = await fetch(url, {
|
|
headers: getHeaders(),
|
|
credentials: 'include'
|
|
});
|
|
const json = await res.json();
|
|
console.log(`[Data] getProjects found ${json.data?.length || 0} items`);
|
|
return json.data || [];
|
|
} catch (err) {
|
|
console.error("[Data] getProjects error:", err);
|
|
return [];
|
|
}
|
|
},
|
|
|
|
async createProject(projectData: Partial<BookProject> & { user_id: string }) {
|
|
// Map Frontend types to Database Columns explicitly if needed, but names match mostly
|
|
// DB: title, author, genre, sub_genre, target_audience, tone, pov, tense, synopsis, themes, style_guide
|
|
return this.createItem('projects', projectData);
|
|
},
|
|
|
|
// -- GENERIC CRUD --
|
|
|
|
async getRelatedData(table: string, projectId: number) {
|
|
try {
|
|
const url = `${DATA_API_ROOT}/read/${table}?Instance=${INSTANCE_ID}&project_id=${projectId}`;
|
|
console.log(`[API] GET ${url}`);
|
|
|
|
const res = await fetch(url, {
|
|
headers: getHeaders(),
|
|
credentials: 'include'
|
|
});
|
|
const json = await res.json();
|
|
console.log(`[Data] getRelatedData ${table} found ${json.data?.length || 0} items`);
|
|
return json.data || [];
|
|
} catch (err) {
|
|
console.error(`[Data] getRelatedData ${table} error:`, err);
|
|
return [];
|
|
}
|
|
},
|
|
|
|
async createItem(table: string, data: any) {
|
|
try {
|
|
console.log(`[Data] Creating item in ${table}...`, data);
|
|
const url = `${DATA_API_ROOT}/create/${table}?Instance=${INSTANCE_ID}`;
|
|
console.log(`[API] POST ${url}`, data);
|
|
|
|
const res = await fetch(url, {
|
|
method: 'POST',
|
|
headers: getHeaders(),
|
|
body: JSON.stringify(data),
|
|
credentials: 'include'
|
|
});
|
|
|
|
const result = await res.json();
|
|
console.log(`[Data] Create ${table} response:`, result);
|
|
|
|
if (!res.ok) {
|
|
console.error(`[Data] Create ${table} failed:`, result);
|
|
return { status: 'error', message: result.message || 'Creation failed' };
|
|
}
|
|
return { status: 'success', ...result };
|
|
} catch (err) {
|
|
console.error(`[Data] Create ${table} network error:`, err);
|
|
return { status: 'error', message: err };
|
|
}
|
|
},
|
|
|
|
async updateItem(table: string, id: number, data: any) {
|
|
try {
|
|
const url = `${DATA_API_ROOT}/update/${table}/${id}?Instance=${INSTANCE_ID}`;
|
|
console.log(`[API] PUT ${url}`, data);
|
|
|
|
const res = await fetch(url, {
|
|
method: 'PUT',
|
|
headers: getHeaders(),
|
|
body: JSON.stringify(data),
|
|
credentials: 'include'
|
|
});
|
|
|
|
if (!res.ok) {
|
|
const err = await res.json();
|
|
console.error(`[Data] Update ${table} failed:`, err);
|
|
} else {
|
|
console.log(`[Data] Update ${table} success`);
|
|
}
|
|
} catch (err) {
|
|
console.error(`[Data] Update ${table} error:`, err);
|
|
}
|
|
},
|
|
|
|
async deleteItem(table: string, id: number) {
|
|
try {
|
|
const url = `${DATA_API_ROOT}/delete/${table}/${id}?Instance=${INSTANCE_ID}`;
|
|
console.log(`[API] DELETE ${url}`);
|
|
|
|
const res = await fetch(url, {
|
|
method: 'DELETE',
|
|
headers: getHeaders(),
|
|
credentials: 'include'
|
|
});
|
|
|
|
if (!res.ok) {
|
|
const err = await res.json();
|
|
console.error(`[Data] Delete ${table} failed:`, err);
|
|
} else {
|
|
console.log(`[Data] Delete ${table} success`);
|
|
}
|
|
} catch (err) {
|
|
console.error(`[Data] Delete ${table} error:`, err);
|
|
}
|
|
}
|
|
};
|