mirror of
https://github.com/christianvidalwolf-prog/Craze-Data-check.git
synced 2026-08-03 13:35:25 +02:00
Auth: Implement automatic session refresh logic to prevent 'JWT expired' errors. The app now handles token renewal in the background, allowing users to save changes without interruption.
This commit is contained in:
+43
-31
@@ -1,11 +1,36 @@
|
||||
import { refreshSession, getStoredSession } from './auth';
|
||||
|
||||
const SUPABASE_URL = 'https://hwithddwaapyhnfwcesj.supabase.co';
|
||||
const SUPABASE_ANON_KEY = 'sb_publishable_fGXkh0bSrAOqSk2jWKAzSg_NJD9YPCv';
|
||||
|
||||
function authHeaders(token?: string): Record<string, string> {
|
||||
return {
|
||||
async function safeFetch(url: string, options: RequestInit = {}): Promise<Response> {
|
||||
const session = getStoredSession();
|
||||
const token = session?.access_token || SUPABASE_ANON_KEY;
|
||||
|
||||
const headers = {
|
||||
...options.headers,
|
||||
'apikey': SUPABASE_ANON_KEY,
|
||||
'Authorization': `Bearer ${token ?? SUPABASE_ANON_KEY}`,
|
||||
'Authorization': `Bearer ${token}`,
|
||||
};
|
||||
|
||||
let response = await fetch(url, { ...options, headers });
|
||||
|
||||
if (response.status === 401 && session?.refresh_token) {
|
||||
console.log('JWT expired, attempting refresh...');
|
||||
try {
|
||||
const newSession = await refreshSession(session.refresh_token);
|
||||
const newHeaders = {
|
||||
...options.headers,
|
||||
'apikey': SUPABASE_ANON_KEY,
|
||||
'Authorization': `Bearer ${newSession.access_token}`,
|
||||
};
|
||||
response = await fetch(url, { ...options, headers: newHeaders });
|
||||
} catch (refreshError) {
|
||||
console.error('Session refresh failed:', refreshError);
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
export interface ExcelRow extends Array<any> {}
|
||||
@@ -15,14 +40,11 @@ export interface SyncedRow {
|
||||
status?: 'pending' | 'synced';
|
||||
}
|
||||
|
||||
export async function getAllSyncedRows(token?: string): Promise<Record<string, SyncedRow>> {
|
||||
export async function getAllSyncedRows(): Promise<Record<string, SyncedRow>> {
|
||||
try {
|
||||
const response = await fetch(
|
||||
const response = await safeFetch(
|
||||
`${SUPABASE_URL}/rest/v1/products?select=product_id,data,status&order=updated_at.desc`,
|
||||
{
|
||||
cache: 'no-store',
|
||||
headers: authHeaders(token),
|
||||
}
|
||||
{ cache: 'no-store' }
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -42,15 +64,14 @@ export async function getAllSyncedRows(token?: string): Promise<Record<string, S
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveRowToSupabase(articleNo: string, rowData: ExcelRow, token?: string): Promise<{ success: boolean; error?: string }> {
|
||||
export async function saveRowToSupabase(articleNo: string, rowData: ExcelRow): Promise<{ success: boolean; error?: string }> {
|
||||
try {
|
||||
const response = await fetch(
|
||||
const response = await safeFetch(
|
||||
`${SUPABASE_URL}/rest/v1/products`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...authHeaders(token),
|
||||
'Prefer': 'resolution=merge-duplicates',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
@@ -92,17 +113,15 @@ export async function saveHistoryEntry(
|
||||
articleName: string,
|
||||
oldData: ExcelRow,
|
||||
newData: ExcelRow,
|
||||
changedBy: string,
|
||||
token?: string
|
||||
changedBy: string
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
try {
|
||||
const response = await fetch(
|
||||
const response = await safeFetch(
|
||||
`${SUPABASE_URL}/rest/v1/products_history`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...authHeaders(token),
|
||||
'Prefer': 'return=minimal',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
@@ -131,14 +150,11 @@ export async function saveHistoryEntry(
|
||||
}
|
||||
}
|
||||
|
||||
export async function getHistory(token?: string): Promise<HistoryEntry[]> {
|
||||
export async function getHistory(): Promise<HistoryEntry[]> {
|
||||
try {
|
||||
const response = await fetch(
|
||||
const response = await safeFetch(
|
||||
`${SUPABASE_URL}/rest/v1/products_history?select=*&order=changed_at.desc&limit=100`,
|
||||
{
|
||||
cache: 'no-store',
|
||||
headers: authHeaders(token),
|
||||
}
|
||||
{ cache: 'no-store' }
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -168,14 +184,11 @@ export async function getHistory(token?: string): Promise<HistoryEntry[]> {
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteHistoryEntry(id: string, token?: string): Promise<boolean> {
|
||||
export async function deleteHistoryEntry(id: string): Promise<boolean> {
|
||||
try {
|
||||
const response = await fetch(
|
||||
const response = await safeFetch(
|
||||
`${SUPABASE_URL}/rest/v1/products_history?id=eq.${encodeURIComponent(id)}`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
headers: authHeaders(token),
|
||||
}
|
||||
{ method: 'DELETE' }
|
||||
);
|
||||
|
||||
return response.ok;
|
||||
@@ -185,15 +198,14 @@ export async function deleteHistoryEntry(id: string, token?: string): Promise<bo
|
||||
}
|
||||
}
|
||||
|
||||
export async function resetAllPendingRows(token?: string): Promise<boolean> {
|
||||
export async function resetAllPendingRows(): Promise<boolean> {
|
||||
try {
|
||||
const response = await fetch(
|
||||
const response = await safeFetch(
|
||||
`${SUPABASE_URL}/rest/v1/products?status=eq.pending`,
|
||||
{
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...authHeaders(token),
|
||||
'Prefer': 'return=minimal',
|
||||
},
|
||||
body: JSON.stringify({ status: 'synced' })
|
||||
|
||||
Reference in New Issue
Block a user