mirror of
https://github.com/AeThex-Corporation/AeThex-OS.git
synced 2026-04-25 01:17:20 +00:00
Add shared update guard and reliable site deletion count
This commit is contained in:
parent
cf620a3e24
commit
cd39dc06c5
1 changed files with 121 additions and 78 deletions
|
|
@ -1,4 +1,12 @@
|
||||||
import { type Profile, type Project, type ChatMessage } from "../shared/schema.js";
|
import {
|
||||||
|
type Profile,
|
||||||
|
type Project,
|
||||||
|
type ChatMessage,
|
||||||
|
type AethexSite,
|
||||||
|
type Application,
|
||||||
|
type Achievement,
|
||||||
|
type AethexAlert,
|
||||||
|
} from "../shared/schema.js";
|
||||||
import { supabase } from "./supabase.js";
|
import { supabase } from "./supabase.js";
|
||||||
|
|
||||||
export interface IStorage {
|
export interface IStorage {
|
||||||
|
|
@ -14,21 +22,24 @@ export interface IStorage {
|
||||||
getProject(id: string): Promise<Project | undefined>;
|
getProject(id: string): Promise<Project | undefined>;
|
||||||
|
|
||||||
// Sites
|
// Sites
|
||||||
getSites(): Promise<any[]>;
|
getSites(): Promise<AethexSite[]>;
|
||||||
|
createSite(site: Partial<AethexSite>): Promise<AethexSite>;
|
||||||
|
updateSite(id: string, updates: Partial<AethexSite>): Promise<AethexSite>;
|
||||||
|
deleteSite(id: string): Promise<boolean>;
|
||||||
|
|
||||||
// Auth Logs
|
// Auth Logs
|
||||||
getAuthLogs(): Promise<any[]>;
|
getAuthLogs(): Promise<any[]>;
|
||||||
|
|
||||||
// Achievements
|
// Achievements
|
||||||
getAchievements(): Promise<any[]>;
|
getAchievements(): Promise<Achievement[]>;
|
||||||
|
|
||||||
// Applications
|
// Applications
|
||||||
getApplications(): Promise<any[]>;
|
getApplications(): Promise<Application[]>;
|
||||||
updateApplication(id: string, updates: any): Promise<any>;
|
updateApplication(id: string, updates: Partial<Application>): Promise<Application>;
|
||||||
|
|
||||||
// Alerts
|
// Alerts
|
||||||
getAlerts(): Promise<any[]>;
|
getAlerts(): Promise<AethexAlert[]>;
|
||||||
updateAlert(id: string, updates: any): Promise<any>;
|
updateAlert(id: string, updates: Partial<AethexAlert>): Promise<AethexAlert>;
|
||||||
|
|
||||||
// Notifications (for WebSocket)
|
// Notifications (for WebSocket)
|
||||||
getNotifications(): Promise<any[]>;
|
getNotifications(): Promise<any[]>;
|
||||||
|
|
@ -50,38 +61,54 @@ export interface IStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SupabaseStorage implements IStorage {
|
export class SupabaseStorage implements IStorage {
|
||||||
// Create a new site
|
private filterDefined<T extends Record<string, any>>(updates: Partial<T>): Partial<T> {
|
||||||
async createSite(site: any): Promise<any> {
|
return Object.fromEntries(
|
||||||
const { data, error } = await supabase
|
Object.entries(updates).filter(([, value]) => value !== undefined)
|
||||||
.from('aethex_sites')
|
) as Partial<T>;
|
||||||
.insert(site)
|
}
|
||||||
.select()
|
|
||||||
.single();
|
|
||||||
if (error) throw new Error(error.message);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update a site
|
private ensureUpdates<T extends Record<string, any>>(updates: Partial<T>, entity: string): asserts updates is Partial<T> & Record<string, any> {
|
||||||
async updateSite(id: string, updates: any): Promise<any> {
|
if (Object.keys(updates).length === 0) {
|
||||||
const { data, error } = await supabase
|
throw new Error(`No ${entity} fields provided for update`);
|
||||||
.from('aethex_sites')
|
|
||||||
.update({ ...updates, updated_at: new Date().toISOString() })
|
|
||||||
.eq('id', id)
|
|
||||||
.select()
|
|
||||||
.single();
|
|
||||||
if (error) throw new Error(error.message);
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Delete a site
|
// Create a new site
|
||||||
async deleteSite(id: string): Promise<boolean> {
|
async createSite(site: Partial<AethexSite>): Promise<AethexSite> {
|
||||||
const { error, count } = await supabase
|
const cleanSite = this.filterDefined<AethexSite>(site);
|
||||||
.from('aethex_sites')
|
const { data, error } = await supabase
|
||||||
.delete({ count: 'exact' })
|
.from('aethex_sites')
|
||||||
.eq('id', id);
|
.insert(cleanSite)
|
||||||
if (error) throw new Error(error.message);
|
.select()
|
||||||
return (count ?? 0) > 0;
|
.single();
|
||||||
}
|
if (error) throw new Error(error.message);
|
||||||
|
return data as AethexSite;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update a site
|
||||||
|
async updateSite(id: string, updates: Partial<AethexSite>): Promise<AethexSite> {
|
||||||
|
const cleanUpdates = this.filterDefined<AethexSite>(updates);
|
||||||
|
this.ensureUpdates(cleanUpdates, 'site');
|
||||||
|
const { data, error } = await supabase
|
||||||
|
.from('aethex_sites')
|
||||||
|
.update({ ...cleanUpdates, updated_at: new Date().toISOString() })
|
||||||
|
.eq('id', id)
|
||||||
|
.select()
|
||||||
|
.single();
|
||||||
|
if (error) throw new Error(error.message);
|
||||||
|
return data as AethexSite;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete a site
|
||||||
|
async deleteSite(id: string): Promise<boolean> {
|
||||||
|
const { error, data } = await supabase
|
||||||
|
.from('aethex_sites')
|
||||||
|
.delete()
|
||||||
|
.eq('id', id)
|
||||||
|
.select('id');
|
||||||
|
if (error) throw new Error(error.message);
|
||||||
|
return (data?.length ?? 0) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
async getProfiles(): Promise<Profile[]> {
|
async getProfiles(): Promise<Profile[]> {
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
|
|
@ -116,9 +143,11 @@ export class SupabaseStorage implements IStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateProfile(id: string, updates: Partial<Profile>): Promise<Profile | undefined> {
|
async updateProfile(id: string, updates: Partial<Profile>): Promise<Profile | undefined> {
|
||||||
|
const cleanUpdates = this.filterDefined<Profile>(updates);
|
||||||
|
this.ensureUpdates(cleanUpdates, 'profile');
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('profiles')
|
.from('profiles')
|
||||||
.update({ ...updates, updated_at: new Date().toISOString() })
|
.update({ ...cleanUpdates, updated_at: new Date().toISOString() })
|
||||||
.eq('id', id)
|
.eq('id', id)
|
||||||
.select()
|
.select()
|
||||||
.single();
|
.single();
|
||||||
|
|
@ -127,6 +156,16 @@ export class SupabaseStorage implements IStorage {
|
||||||
return data as Profile;
|
return data as Profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getLeadershipProfiles(): Promise<Profile[]> {
|
||||||
|
const { data, error } = await supabase
|
||||||
|
.from('profiles')
|
||||||
|
.select('*')
|
||||||
|
.in('role', ['oversee', 'admin']);
|
||||||
|
|
||||||
|
if (error || !data) return [];
|
||||||
|
return data as Profile[];
|
||||||
|
}
|
||||||
|
|
||||||
async getProjects(): Promise<Project[]> {
|
async getProjects(): Promise<Project[]> {
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('projects')
|
.from('projects')
|
||||||
|
|
@ -148,14 +187,14 @@ export class SupabaseStorage implements IStorage {
|
||||||
return data as Project;
|
return data as Project;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSites(): Promise<any[]> {
|
async getSites(): Promise<AethexSite[]> {
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('aethex_sites')
|
.from('aethex_sites')
|
||||||
.select('*')
|
.select('*')
|
||||||
.order('last_check', { ascending: false });
|
.order('last_check', { ascending: false });
|
||||||
|
|
||||||
if (error) return [];
|
if (error || !data) return [];
|
||||||
return data || [];
|
return data as AethexSite[];
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAuthLogs(): Promise<any[]> {
|
async getAuthLogs(): Promise<any[]> {
|
||||||
|
|
@ -169,42 +208,45 @@ export class SupabaseStorage implements IStorage {
|
||||||
return data || [];
|
return data || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAchievements(): Promise<any[]> {
|
async getAchievements(): Promise<Achievement[]> {
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('achievements')
|
.from('achievements')
|
||||||
.select('*')
|
.select('*')
|
||||||
.order('name', { ascending: true });
|
.order('name', { ascending: true });
|
||||||
|
|
||||||
if (error) return [];
|
if (error || !data) return [];
|
||||||
return data || [];
|
return data as Achievement[];
|
||||||
}
|
}
|
||||||
|
|
||||||
async getApplications(): Promise<any[]> {
|
async getApplications(): Promise<Application[]> {
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('applications')
|
.from('applications')
|
||||||
.select('*')
|
.select('*')
|
||||||
.order('submitted_at', { ascending: false });
|
.order('submitted_at', { ascending: false });
|
||||||
|
|
||||||
if (error) return [];
|
if (error || !data) return [];
|
||||||
return data || [];
|
return data as Application[];
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAlerts(): Promise<any[]> {
|
async getAlerts(): Promise<AethexAlert[]> {
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('aethex_alerts')
|
.from('aethex_alerts')
|
||||||
.select('*')
|
.select('*')
|
||||||
.order('created_at', { ascending: false })
|
.order('created_at', { ascending: false })
|
||||||
.limit(50);
|
.limit(50);
|
||||||
|
|
||||||
if (error) return [];
|
if (error || !data) return [];
|
||||||
return data || [];
|
return data as AethexAlert[];
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateAlert(id: string, updates: any): Promise<any> {
|
async updateAlert(id: string, updates: Partial<AethexAlert>): Promise<AethexAlert> {
|
||||||
const updateData: any = {};
|
const updateData = this.filterDefined<AethexAlert>({
|
||||||
if ('is_resolved' in updates) {
|
message: updates.message,
|
||||||
updateData.is_resolved = updates.is_resolved;
|
severity: updates.severity,
|
||||||
}
|
is_resolved: updates.is_resolved,
|
||||||
|
resolved_at: updates.resolved_at,
|
||||||
|
});
|
||||||
|
this.ensureUpdates(updateData, 'alert');
|
||||||
|
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('aethex_alerts')
|
.from('aethex_alerts')
|
||||||
|
|
@ -217,14 +259,15 @@ export class SupabaseStorage implements IStorage {
|
||||||
console.error('Update alert error:', error);
|
console.error('Update alert error:', error);
|
||||||
throw new Error(error.message);
|
throw new Error(error.message);
|
||||||
}
|
}
|
||||||
return data;
|
return data as AethexAlert;
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateApplication(id: string, updates: any): Promise<any> {
|
async updateApplication(id: string, updates: Partial<Application>): Promise<Application> {
|
||||||
const updateData: any = {};
|
const updateData = this.filterDefined<Application>({
|
||||||
if ('status' in updates) {
|
status: updates.status,
|
||||||
updateData.status = updates.status;
|
response_message: updates.response_message,
|
||||||
}
|
});
|
||||||
|
this.ensureUpdates(updateData, 'application');
|
||||||
|
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('applications')
|
.from('applications')
|
||||||
|
|
@ -237,7 +280,7 @@ export class SupabaseStorage implements IStorage {
|
||||||
console.error('Update application error:', error);
|
console.error('Update application error:', error);
|
||||||
throw new Error(error.message);
|
throw new Error(error.message);
|
||||||
}
|
}
|
||||||
return data;
|
return data as Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chat Messages (AI memory)
|
// Chat Messages (AI memory)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue