TKK_E32230469/web_ups/src/lib/db.ts

190 lines
4.5 KiB
TypeScript

import { supabase } from './supabase';
// Types
export interface HistoryRow {
id?: number;
created_at?: string;
v_batt: number;
battery_pct: number;
v_in?: number;
v_out?: number;
line_state: number;
power_on: boolean;
}
export interface EventRow {
id?: number;
created_at?: string;
message: string;
severity: 'info' | 'success' | 'warning' | 'error';
}
let dbUnavailable = false;
let warnedUnavailable = false;
let retryTimeoutId: ReturnType<typeof setTimeout> | null = null;
function isNetworkError(error: unknown) {
const message = error instanceof Error ? error.message : String(error ?? '');
return /failed to fetch|fetch failed|networkerror|load failed/i.test(message);
}
function logDbIssue(operation: string, error: unknown) {
const message = error instanceof Error ? error.message : String(error ?? 'Unknown error');
if (isNetworkError(error)) {
dbUnavailable = true;
if (!warnedUnavailable) {
warnedUnavailable = true;
console.warn(
`[DB] Supabase tidak bisa dijangkau (${message}). ` +
'Dashboard tetap berjalan tanpa sinkronisasi database. ' +
'Periksa NEXT_PUBLIC_SUPABASE_URL dan koneksi jaringan.'
);
}
// Auto-retry after 60 seconds
if (!retryTimeoutId) {
retryTimeoutId = setTimeout(() => {
dbUnavailable = false;
warnedUnavailable = false;
retryTimeoutId = null;
console.info('[DB] Retrying Supabase connection...');
}, 60000);
}
return;
}
console.warn(`[DB] ${operation} error:`, message);
}
function shouldSkipDb() {
return dbUnavailable;
}
// History
/**
* Simpan satu titik data history ke Supabase.
*/
export async function insertHistoryPoint(row: HistoryRow) {
if (shouldSkipDb()) return;
try {
const { error } = await supabase.from('ups_history').insert([row]);
if (error) logDbIssue('insertHistoryPoint', error);
} catch (error) {
logDbIssue('insertHistoryPoint', error);
}
}
/**
* Ambil N data history terakhir dari Supabase, diurutkan dari terlama ke terbaru.
*/
export async function fetchHistory(limit = 100): Promise<HistoryRow[]> {
if (shouldSkipDb()) return [];
try {
const { data, error } = await supabase
.from('ups_history')
.select('*')
.order('created_at', { ascending: false })
.limit(limit);
if (error) {
logDbIssue('fetchHistory', error);
return [];
}
return (data as HistoryRow[]).reverse();
} catch (error) {
logDbIssue('fetchHistory', error);
return [];
}
}
/**
* Ambil data history dari Supabase dalam rentang tanggal tertentu.
* @param from ISO string (misal "2024-05-01T00:00:00.000Z")
* @param to ISO string (misal "2024-05-07T23:59:59.999Z")
* @param limit maksimum jumlah baris yang diambil
*/
export async function fetchHistoryByRange(from: string, to: string, limit = 2000): Promise<HistoryRow[]> {
if (shouldSkipDb()) return [];
try {
const { data, error } = await supabase
.from('ups_history')
.select('*')
.gte('created_at', from)
.lte('created_at', to)
.order('created_at', { ascending: true })
.limit(limit);
if (error) {
logDbIssue('fetchHistoryByRange', error);
return [];
}
return data as HistoryRow[];
} catch (error) {
logDbIssue('fetchHistoryByRange', error);
return [];
}
}
// Events
/**
* Simpan satu event log ke Supabase.
*/
export async function insertEvent(row: EventRow) {
if (shouldSkipDb()) return;
try {
const { error } = await supabase.from('ups_events').insert([row]);
if (error) logDbIssue('insertEvent', error);
} catch (error) {
logDbIssue('insertEvent', error);
}
}
/**
* Ambil N event log terakhir dari Supabase, diurutkan dari terbaru ke terlama.
*/
export async function fetchEvents(limit = 200): Promise<EventRow[]> {
if (shouldSkipDb()) return [];
try {
const { data, error } = await supabase
.from('ups_events')
.select('*')
.order('created_at', { ascending: false })
.limit(limit);
if (error) {
logDbIssue('fetchEvents', error);
return [];
}
return data as EventRow[];
} catch (error) {
logDbIssue('fetchEvents', error);
return [];
}
}
/**
* Hapus semua event log dari Supabase.
*/
export async function clearAllEvents() {
if (shouldSkipDb()) return;
try {
const { error } = await supabase
.from('ups_events')
.delete()
.gte('id', 0); // delete all rows
if (error) logDbIssue('clearAllEvents', error);
} catch (error) {
logDbIssue('clearAllEvents', error);
}
}