chore: build

This commit is contained in:
2026-06-14 17:50:35 +02:00
parent 3c92a0371f
commit 3a0f725159
93 changed files with 5311 additions and 4224 deletions
+198 -23
View File
@@ -1,9 +1,3 @@
// ============================================
// api/api.ts - VERSION CORRIGÉE
// ============================================
// ✅ AuthResponse inclut access_token
// ✅ loginUser et registerUser retournent AuthResponse
// ✅ sessionStorage (pas localStorage)
const API_URL = "/api/v1";
const BACKEND_URL = "";
export function getMediaUrl(url: string): string {
@@ -28,19 +22,15 @@ import type {
CancelCommandResponse,
PenaltiesResponse,
} from "./api_types";
// ============================================
// 🔐 TYPES - AUTHRESPONSE COMPLETE
// ============================================
/**
* ✅ TYPE CORRECT - Inclut access_token!
*/
export interface AuthResponse {
success: boolean;
message?: string;
access_token?: string; // ✅ CRITICAL!
access_token?: string;
token_type?: string;
expires_in?: number;
requires_2fa?: boolean;
session_token?: string;
user?: {
id: number;
username: string;
@@ -53,17 +43,8 @@ export interface AuthResponse {
};
}
// ============================================
// 🔐 GESTION CENTRALISÉE DU JWT
// ============================================
/**
* ✅ Extraire username du JWT
* Source de vérité UNIQUE pour le username
*/
export const extractUsernameFromToken = (): string | null => {
try {
// ✅ CRITICAL: sessionStorage (pas localStorage!)
const token = sessionStorage.getItem("token");
if (!token) {
@@ -210,6 +191,15 @@ export const loginUser = async (
const data = await safeJson(response);
console.log("📋 [LOGIN] Réponse:", data);
// 2FA requis — retourner sans token
if (data.requires_2fa) {
return {
success: true,
requires_2fa: true,
session_token: data.session_token,
};
}
// ✅ Vérifier access_token
if (!data.access_token) {
console.error("❌ [LOGIN] Pas de access_token");
@@ -728,6 +718,9 @@ export const createCheckout = async (checkoutData: CheckoutData) => {
pay_currency: data.pay_currency as string | undefined,
price_amount: data.price_amount as number | undefined,
price_currency: data.price_currency as string | undefined,
referral_used: data.referral_used as number | undefined,
referral_balance: data.referral_balance as number | undefined,
client_order_number: data.client_order_number as number | undefined,
};
} catch (error) {
console.error("❌ [CHECKOUT] Erreur:", error);
@@ -804,8 +797,9 @@ export interface Product {
category: string;
unit?: string;
stock: number;
prices?: Array<{ quantity: number; price: number }>;
prices?: Array<{ quantity: number; price: number; active_price?: boolean }>;
media?: MediaItem[]; // ✅ CHANGÉ: string[] → MediaItem[]
coming_soon?: boolean;
}
export interface Category {
@@ -1866,6 +1860,9 @@ export interface PublicSettings {
crypto_payment_enabled: boolean;
crypto_only: boolean;
nowpayments_currencies: string[];
shop_name: string;
two_fa_enabled: boolean;
contact_telegram: string;
}
export const getPublicSettings = async (): Promise<PublicSettings> => {
@@ -1880,6 +1877,9 @@ export const getPublicSettings = async (): Promise<PublicSettings> => {
crypto_payment_enabled: false,
crypto_only: false,
nowpayments_currencies: [],
shop_name: "Milieu-Nantais",
two_fa_enabled: false,
contact_telegram: "",
};
try {
const response = await fetch(`${API_URL}/app-settings`);
@@ -1901,12 +1901,93 @@ export const getPublicSettings = async (): Promise<PublicSettings> => {
nowpayments_currencies: Array.isArray(data.nowpayments_currencies)
? data.nowpayments_currencies
: [],
shop_name: data.shop_name || "Milieu-Nantais",
two_fa_enabled: data.two_fa_enabled ?? false,
contact_telegram: data.contact_telegram || "",
};
} catch {
return defaults;
}
};
export const verify2FA = async (
sessionToken: string,
code: string,
): Promise<AuthResponse> => {
try {
const response = await fetch(`${API_URL}/auth/2fa/verify`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ session_token: sessionToken, code }),
});
const data = await safeJson(response);
if (!response.ok) {
return { success: false, message: data.error || "Code invalide" };
}
sessionStorage.setItem("token", data.access_token);
syncUsernameFromJWT();
return {
success: true,
access_token: data.access_token,
token_type: data.token_type,
expires_in: data.expires_in,
user: data.user,
};
} catch (error) {
return {
success: false,
message: error instanceof Error ? error.message : "Erreur",
};
}
};
export const get2FAStatus = async (): Promise<{
two_fa_enabled: boolean;
telegram_linked: boolean;
admin_2fa_enabled: boolean;
}> => {
const token = sessionStorage.getItem("token");
try {
const response = await fetch(`${API_URL}/two-fa/status`, {
headers: { Authorization: `Bearer ${token}` },
});
if (!response.ok)
return {
two_fa_enabled: false,
telegram_linked: false,
admin_2fa_enabled: false,
};
return await safeJson(response);
} catch {
return {
two_fa_enabled: false,
telegram_linked: false,
admin_2fa_enabled: false,
};
}
};
export const toggle2FA = async (
enabled: boolean,
): Promise<{ success: boolean; error?: string }> => {
const token = sessionStorage.getItem("token");
try {
const response = await fetch(`${API_URL}/two-fa/toggle`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({ enabled }),
});
const data = await safeJson(response);
if (!response.ok) return { success: false, error: data.error };
return { success: true };
} catch {
return { success: false, error: "Erreur réseau" };
}
};
export interface CryptoPaymentStatus {
command_id: number;
client_order_number?: number;
@@ -2058,3 +2139,97 @@ export const unlinkTelegram = async (): Promise<void> => {
/* silencieux */
}
};
// ============================================
// 🏆 POINTS — RÉCOMPENSES
// ============================================
export type RewardCategoryConfig = {
category: string;
all_products: boolean;
product_ids: number[];
product_names: string[];
amount: number;
};
export type PointsPoolInfo = {
key: string;
name: string;
points: number;
rewards_earned: number;
rewards_claimed: number;
rewards_available: number;
eligible_configs: RewardCategoryConfig[];
};
export type RewardItemConfig = {
product_id: number;
product_name: string;
quantity: number;
price: number;
};
export type PointsRewardConfig = {
threshold: number;
type: string;
description: string;
reward_items: RewardItemConfig[];
};
export const getMyPointsRewards = async (): Promise<{
success: boolean;
enabled: boolean;
pools: PointsPoolInfo[];
reward: PointsRewardConfig | null;
}> => {
const token = getAuthToken();
if (!token) return { success: false, enabled: false, pools: [], reward: null };
try {
const response = await fetch(`${API_URL}/points/rewards`, {
headers: { Authorization: `Bearer ${token}` },
});
if (!response.ok) return { success: false, enabled: false, pools: [], reward: null };
const data = await safeJson(response);
return {
success: true,
enabled: data.enabled ?? false,
pools: data.pools ?? [],
reward: data.reward ?? null,
};
} catch {
return { success: false, enabled: false, pools: [], reward: null };
}
};
export const claimMyReward = async (poolKey: string): Promise<{
success: boolean;
description?: string;
remaining_rewards?: number;
product_added?: boolean;
product_name?: string;
error?: string;
}> => {
const token = getAuthToken();
if (!token) return { success: false, error: "Non authentifié" };
try {
const response = await fetch(`${API_URL}/points/claim`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({ pool_key: poolKey }),
});
const data = await safeJson(response);
if (!response.ok) return { success: false, error: data.error || "Erreur" };
return {
success: true,
description: data.description,
remaining_rewards: data.remaining_rewards,
product_added: data.product_added,
product_name: data.product_name,
};
} catch {
return { success: false, error: "Erreur de connexion" };
}
};