fix: replace createClient with shared supabase singleton in useAdminStats and errorLogger

This commit is contained in:
root 2026-05-27 10:59:37 +00:00
parent 2fea387d43
commit 58a96355f1
2 changed files with 32 additions and 31 deletions

View File

@ -1,13 +1,13 @@
import { useState, useEffect, useCallback } from "react"; import { useState, useEffect, useCallback } from "react";
import { supabase, hasSupabaseConfig } from "../supabaseClient";
const PERIOD_DAYS = { today: 1, "7d": 7, "30d": 30, all: 0 }; const PERIOD_DAYS = { today: 1, "7d": 7, "30d": 30, all: 0 };
const rpcCall = async (fnName, params) => { const rpcCall = async (fnName, params) => {
const { createClient } = await import("@supabase/supabase-js"); if (!hasSupabaseConfig || !supabase) {
const supabaseUrl = window.__SUPABASE_URL__ || import.meta.env.VITE_SUPABASE_URL; throw new Error("Supabase не сконфигурирован");
const supabaseAnonKey = window.__SUPABASE_ANON_KEY__ || import.meta.env.VITE_SUPABASE_ANON_KEY; }
const client = createClient(supabaseUrl, supabaseAnonKey); const { data, error } = await supabase.rpc(fnName, params);
const { data, error } = await client.rpc(fnName, params);
if (error) throw error; if (error) throw error;
return data; return data;
}; };

View File

@ -1,25 +1,27 @@
import { createClient } from '@supabase/supabase-js'; import { supabase, hasSupabaseConfig } from "../supabaseClient";
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;
const supabase = createClient(supabaseUrl, supabaseAnonKey);
// Debounce tracking: message -> last timestamp // Debounce tracking: message -> last timestamp
const recentErrors = new Map(); const recentErrors = new Map();
const DEBOUNCE_MS = 10000; const DEBOUNCE_MS = 10000;
function getUserId() { function getUserId() {
// Try to get from Supabase auth session in localStorage // Try to get from Supabase auth session in sessionStorage
try { try {
const keys = Object.keys(localStorage); const keys = Object.keys(sessionStorage);
const authKey = keys.find( const authKey = keys.find(
(k) => k.startsWith('sb-') && k.endsWith('-auth-token') (k) => k.startsWith("sb-") && k.endsWith("-auth-token")
); );
if (authKey) { if (authKey) {
const data = JSON.parse(localStorage.getItem(authKey)); const data = JSON.parse(sessionStorage.getItem(authKey));
return data?.user?.id || null; return data?.user?.id || null;
} }
// Also check supersam secure storage format
const ssKey = keys.find((k) => k === "supersam-auth");
if (ssKey) {
const data = JSON.parse(sessionStorage.getItem(ssKey));
// The secure storage obfuscates values, but the key structure is known
// Fall through to window.__supersam_user_id__ set by AuthContext
}
} catch { } catch {
// ignore // ignore
} }
@ -46,19 +48,20 @@ function isDebounced(message) {
async function insertErrorLog(entry) { async function insertErrorLog(entry) {
try { try {
await supabase.from('client_error_logs').insert([entry]); if (!hasSupabaseConfig || !supabase) return;
await supabase.from("client_error_logs").insert([entry]);
} catch { } catch {
// Fire-and-forget — swallow insertion errors // Fire-and-forget — swallow insertion errors
} }
} }
function logError(error, componentInfo) { function logError(error, componentInfo) {
if (!(error instanceof Error) && typeof error !== 'object' && typeof error !== 'string') { if (!(error instanceof Error) && typeof error !== "object" && typeof error !== "string") {
return; return;
} }
const message = const message =
typeof error === 'string' typeof error === "string"
? error ? error
: error?.message || String(error); : error?.message || String(error);
@ -66,7 +69,7 @@ function logError(error, componentInfo) {
if (isDebounced(message)) return; if (isDebounced(message)) return;
const stack = const stack =
typeof error === 'object' && error !== null ? error.stack || null : null; typeof error === "object" && error !== null ? error.stack || null : null;
let line_number = null; let line_number = null;
let column_number = null; let column_number = null;
@ -83,16 +86,16 @@ function logError(error, componentInfo) {
} }
const entry = { const entry = {
user_id: getUserId(), user_id: getUserId() || window.__supersam_user_id__ || null,
message, message,
stack, stack,
url, url,
line_number, line_number,
column_number, column_number,
error_type: error_type:
typeof error === 'object' && error !== null typeof error === "object" && error !== null
? error.constructor?.name || 'Error' ? error.constructor?.name || "Error"
: 'String', : "String",
component: componentInfo?.component || null, component: componentInfo?.component || null,
props: componentInfo?.props props: componentInfo?.props
? JSON.stringify(componentInfo.props) ? JSON.stringify(componentInfo.props)
@ -106,16 +109,14 @@ function logError(error, componentInfo) {
} }
function initErrorLogging(userId) { function initErrorLogging(userId) {
// If a userId is provided, override the getUserId logic // If a userId is provided, store it for future logError calls
if (userId) { if (userId) {
const origGetUserId = getUserId;
// We store it so future logError calls can use it
window.__supersam_user_id__ = userId; window.__supersam_user_id__ = userId;
} }
// Catch synchronous errors // Catch synchronous errors
window.onerror = function (message, source, lineno, colno, error) { window.onerror = function (message, source, lineno, colno, error) {
const msg = typeof message === 'string' ? message : String(message); const msg = typeof message === "string" ? message : String(message);
if (isDebounced(msg)) return; if (isDebounced(msg)) return;
@ -126,7 +127,7 @@ function initErrorLogging(userId) {
url: source || null, url: source || null,
line_number: lineno || null, line_number: lineno || null,
column_number: colno || null, column_number: colno || null,
error_type: error?.constructor?.name || 'Error', error_type: error?.constructor?.name || "Error",
component: null, component: null,
props: null, props: null,
user_agent: navigator.userAgent, user_agent: navigator.userAgent,
@ -142,7 +143,7 @@ function initErrorLogging(userId) {
const message = const message =
error instanceof Error error instanceof Error
? error.message ? error.message
: typeof error === 'string' : typeof error === "string"
? error ? error
: String(error); : String(error);
@ -170,8 +171,8 @@ function initErrorLogging(userId) {
column_number, column_number,
error_type: error_type:
error instanceof Error error instanceof Error
? error.constructor?.name || 'Error' ? error.constructor?.name || "Error"
: 'UnhandledRejection', : "UnhandledRejection",
component: null, component: null,
props: null, props: null,
user_agent: navigator.userAgent, user_agent: navigator.userAgent,