mirror of
https://github.com/AeThex-Corporation/AeThex-OS.git
synced 2026-04-17 22:27:19 +00:00
modified: .env
This commit is contained in:
parent
be2ddda4d5
commit
0f7f5704da
15 changed files with 2642 additions and 24 deletions
5
.env
5
.env
|
|
@ -1,5 +1,6 @@
|
|||
DATABASE_URL=postgresql://postgres:[YOUR_PASSWORD]@db.kmdeisowhtsalsekkzqd.supabase.co:5432/postgres
|
||||
DATABASE_URL=postgresql://postgres:Max!FTW2023!@db.kmdeisowhtsalsekkzqd.supabase.co:5432/postgres
|
||||
SUPABASE_URL=https://kmdeisowhtsalsekkzqd.supabase.co
|
||||
SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImttZGVpc293aHRzYWxzZWtrenFkIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTM3Mzc2NTIsImV4cCI6MjA2OTMxMzY1Mn0.2mvk-rDZnHOzdx6Cgcysh51a3cflOlRWO6OA1Z5YWuQ
|
||||
AI_INTEGRATIONS_OPENAI_BASE_URL=https://placeholder.openai.com
|
||||
AI_INTEGRATIONS_OPENAI_API_KEY=placeholder_key
|
||||
AI_INTEGRATIONS_OPENAI_API_KEY=placeholder_key
|
||||
SESSION_SECRET=3a44273587118f6f2926b9b6839f2ddea3aea4f75ccb78b9510611b399a42823
|
||||
24
.vscode/launch.json
vendored
Normal file
24
.vscode/launch.json
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
|
||||
{
|
||||
"name": "Attach to Chrome",
|
||||
"port": 9222,
|
||||
"request": "attach",
|
||||
"type": "chrome",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
},
|
||||
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "Launch Chrome against localhost",
|
||||
"url": "http://localhost:8080",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -45,14 +45,15 @@
|
|||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
<!-- Service Worker disabled in development to prevent CORS issues -->
|
||||
<!-- <script>
|
||||
if ('serviceWorker' in navigator && import.meta.env.PROD) {
|
||||
window.addEventListener('load', () => {
|
||||
navigator.serviceWorker.register('/sw.js')
|
||||
.then(reg => console.log('SW registered:', reg.scope))
|
||||
.catch(err => console.error('SW registration failed:', err));
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</script> -->
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { motion } from "framer-motion";
|
||||
import { useState } from "react";
|
||||
import { Link, useLocation } from "wouter";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useAuth } from "@/lib/auth";
|
||||
|
|
@ -11,7 +12,13 @@ export default function AdminSites() {
|
|||
const { user, logout } = useAuth();
|
||||
const [, setLocation] = useLocation();
|
||||
|
||||
const { data: sites, isLoading } = useQuery({
|
||||
const [editingSite, setEditingSite] = useState<any | null>(null);
|
||||
const [showForm, setShowForm] = useState(false);
|
||||
const [formLoading, setFormLoading] = useState(false);
|
||||
const [formError, setFormError] = useState<string | null>(null);
|
||||
const [formSuccess, setFormSuccess] = useState<string | null>(null);
|
||||
|
||||
const { data: sites, isLoading, refetch } = useQuery({
|
||||
queryKey: ["sites"],
|
||||
queryFn: async () => {
|
||||
const res = await fetch("/api/sites");
|
||||
|
|
@ -19,6 +26,58 @@ export default function AdminSites() {
|
|||
return res.json();
|
||||
},
|
||||
});
|
||||
// Add or update site
|
||||
const handleFormSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
setFormLoading(true);
|
||||
setFormError(null);
|
||||
setFormSuccess(null);
|
||||
const form = e.currentTarget;
|
||||
const formData = new FormData(form);
|
||||
const payload: any = Object.fromEntries(formData.entries());
|
||||
try {
|
||||
let res;
|
||||
if (editingSite) {
|
||||
res = await fetch(`/api/sites/${editingSite.id}`, {
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
} else {
|
||||
res = await fetch("/api/sites", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
}
|
||||
if (!res.ok) throw new Error("Failed to save site");
|
||||
setFormSuccess(editingSite ? "Site updated!" : "Site created!");
|
||||
setShowForm(false);
|
||||
setEditingSite(null);
|
||||
form.reset();
|
||||
await refetch();
|
||||
} catch (err: any) {
|
||||
setFormError(err.message || "Error");
|
||||
} finally {
|
||||
setFormLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Delete site
|
||||
const handleDelete = async (id: string) => {
|
||||
if (!window.confirm("Delete this site?")) return;
|
||||
setFormLoading(true);
|
||||
setFormError(null);
|
||||
try {
|
||||
const res = await fetch(`/api/sites/${id}`, { method: "DELETE" });
|
||||
if (!res.ok) throw new Error("Failed to delete site");
|
||||
await refetch();
|
||||
} catch (err: any) {
|
||||
setFormError(err.message || "Error");
|
||||
} finally {
|
||||
setFormLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleLogout = async () => {
|
||||
await logout();
|
||||
|
|
@ -49,15 +108,47 @@ export default function AdminSites() {
|
|||
|
||||
<div className="flex-1 overflow-auto">
|
||||
<div className="p-8">
|
||||
<div className="mb-8">
|
||||
<h2 className="text-2xl font-display font-bold text-white uppercase tracking-wider">
|
||||
AeThex Sites
|
||||
</h2>
|
||||
<p className="text-muted-foreground text-sm mt-1">
|
||||
{sites?.length || 0} monitored sites
|
||||
</p>
|
||||
<div className="mb-8 flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-2xl font-display font-bold text-white uppercase tracking-wider">
|
||||
AeThex Sites
|
||||
</h2>
|
||||
<p className="text-muted-foreground text-sm mt-1">
|
||||
{sites?.length || 0} monitored sites
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
className="bg-primary text-white px-4 py-2 rounded font-bold hover:bg-primary/80 transition"
|
||||
onClick={() => { setShowForm(true); setEditingSite(null); }}
|
||||
>
|
||||
+ Add Site
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{formError && <div className="mb-4 text-red-500">{formError}</div>}
|
||||
{formSuccess && <div className="mb-4 text-green-500">{formSuccess}</div>}
|
||||
|
||||
{showForm && (
|
||||
<form className="mb-8 bg-card/50 border border-white/10 p-6 rounded" onSubmit={handleFormSubmit}>
|
||||
<h3 className="font-bold text-white mb-2">{editingSite ? "Edit Site" : "Add Site"}</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<input name="name" defaultValue={editingSite?.name || ""} placeholder="Name" required className="p-2 rounded bg-background/50 border border-white/10 text-white" />
|
||||
<input name="url" defaultValue={editingSite?.url || ""} placeholder="URL" className="p-2 rounded bg-background/50 border border-white/10 text-white" />
|
||||
<input name="status" defaultValue={editingSite?.status || "online"} placeholder="Status" className="p-2 rounded bg-background/50 border border-white/10 text-white" />
|
||||
<input name="uptime" defaultValue={editingSite?.uptime || ""} placeholder="Uptime (%)" type="number" step="0.01" className="p-2 rounded bg-background/50 border border-white/10 text-white" />
|
||||
<input name="response_time" defaultValue={editingSite?.response_time || ""} placeholder="Response Time (ms)" type="number" className="p-2 rounded bg-background/50 border border-white/10 text-white" />
|
||||
<input name="users" defaultValue={editingSite?.users || ""} placeholder="Users" type="number" className="p-2 rounded bg-background/50 border border-white/10 text-white" />
|
||||
<input name="requests" defaultValue={editingSite?.requests || ""} placeholder="Requests" type="number" className="p-2 rounded bg-background/50 border border-white/10 text-white" />
|
||||
</div>
|
||||
<div className="mt-4 flex gap-2">
|
||||
<button type="submit" className="bg-primary text-white px-4 py-2 rounded font-bold hover:bg-primary/80 transition" disabled={formLoading}>
|
||||
{formLoading ? "Saving..." : (editingSite ? "Update" : "Create")}
|
||||
</button>
|
||||
<button type="button" className="px-4 py-2 rounded border border-white/10 text-white" onClick={() => { setShowForm(false); setEditingSite(null); }}>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{isLoading ? (
|
||||
<div className="col-span-full text-center text-muted-foreground py-12">
|
||||
|
|
@ -85,12 +176,24 @@ export default function AdminSites() {
|
|||
<Globe className="w-5 h-5 text-primary" />
|
||||
<h3 className="font-display text-white uppercase text-sm">{site.name}</h3>
|
||||
</div>
|
||||
<div className={`flex items-center gap-1 text-xs ${getStatusColor(site.status)}`}>
|
||||
{getStatusIcon(site.status)}
|
||||
{site.status || 'unknown'}
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
className="text-xs text-blue-400 hover:underline"
|
||||
onClick={() => { setEditingSite(site); setShowForm(true); }}
|
||||
title="Edit"
|
||||
>Edit</button>
|
||||
<button
|
||||
className="text-xs text-red-400 hover:underline"
|
||||
onClick={() => handleDelete(site.id)}
|
||||
title="Delete"
|
||||
disabled={formLoading}
|
||||
>Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={`flex items-center gap-1 text-xs ${getStatusColor(site.status)}`}>
|
||||
{getStatusIcon(site.status)}
|
||||
{site.status || 'unknown'}
|
||||
</div>
|
||||
{site.url && (
|
||||
<a
|
||||
href={site.url}
|
||||
|
|
@ -101,7 +204,6 @@ export default function AdminSites() {
|
|||
{site.url} <ExternalLink className="w-3 h-3" />
|
||||
</a>
|
||||
)}
|
||||
|
||||
<div className="grid grid-cols-2 gap-4 text-xs">
|
||||
<div>
|
||||
<div className="text-muted-foreground">Uptime</div>
|
||||
|
|
@ -120,7 +222,6 @@ export default function AdminSites() {
|
|||
<div className="text-white font-bold">{site.requests || 0}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{site.last_check && (
|
||||
<div className="mt-4 pt-4 border-t border-white/5 text-xs text-muted-foreground">
|
||||
Last check: {new Date(site.last_check).toLocaleString()}
|
||||
|
|
|
|||
260
migrations/0000_worried_mastermind.sql
Normal file
260
migrations/0000_worried_mastermind.sql
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
CREATE TABLE "achievements" (
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"name" text NOT NULL,
|
||||
"description" text,
|
||||
"icon" text,
|
||||
"points_reward" integer DEFAULT 0,
|
||||
"badge_color" text,
|
||||
"rarity" text,
|
||||
"xp_reward" integer DEFAULT 0,
|
||||
"category" varchar DEFAULT 'milestone' NOT NULL,
|
||||
CONSTRAINT "achievements_name_unique" UNIQUE("name")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "aethex_alerts" (
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"site_id" varchar,
|
||||
"type" text NOT NULL,
|
||||
"severity" text NOT NULL,
|
||||
"message" text NOT NULL,
|
||||
"is_resolved" boolean DEFAULT false,
|
||||
"created_at" timestamp DEFAULT now(),
|
||||
"resolved_at" timestamp
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "aethex_applications" (
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"creator_id" varchar NOT NULL,
|
||||
"opportunity_id" varchar NOT NULL,
|
||||
"status" text DEFAULT 'submitted',
|
||||
"cover_letter" text,
|
||||
"response_message" text,
|
||||
"applied_at" timestamp DEFAULT now(),
|
||||
"updated_at" timestamp DEFAULT now()
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "aethex_creators" (
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar NOT NULL,
|
||||
"username" text NOT NULL,
|
||||
"bio" text,
|
||||
"skills" json DEFAULT '[]'::json,
|
||||
"avatar_url" text,
|
||||
"experience_level" text,
|
||||
"arm_affiliations" json DEFAULT '[]'::json,
|
||||
"primary_arm" text,
|
||||
"is_discoverable" boolean DEFAULT true,
|
||||
"allow_recommendations" boolean DEFAULT true,
|
||||
"devconnect_linked" boolean DEFAULT false,
|
||||
"created_at" timestamp DEFAULT now(),
|
||||
"updated_at" timestamp DEFAULT now(),
|
||||
CONSTRAINT "aethex_creators_username_unique" UNIQUE("username")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "aethex_events" (
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"site_id" varchar,
|
||||
"title" text NOT NULL,
|
||||
"description" text,
|
||||
"date" timestamp NOT NULL,
|
||||
"time" text NOT NULL,
|
||||
"location" text,
|
||||
"capacity" integer,
|
||||
"image_url" text,
|
||||
"created_at" timestamp DEFAULT now(),
|
||||
"updated_at" timestamp,
|
||||
"category" text,
|
||||
"price" numeric,
|
||||
"featured" boolean,
|
||||
"speakers" json,
|
||||
"agenda" json,
|
||||
"full_description" text,
|
||||
"map_url" text,
|
||||
"ticket_types" json
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "aethex_opportunities" (
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"title" text NOT NULL,
|
||||
"description" text NOT NULL,
|
||||
"job_type" text NOT NULL,
|
||||
"salary_min" integer,
|
||||
"salary_max" integer,
|
||||
"experience_level" text,
|
||||
"arm_affiliation" text NOT NULL,
|
||||
"posted_by_id" varchar NOT NULL,
|
||||
"status" text DEFAULT 'open',
|
||||
"created_at" timestamp DEFAULT now(),
|
||||
"updated_at" timestamp DEFAULT now()
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "aethex_passports" (
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar NOT NULL,
|
||||
"created_at" timestamp DEFAULT now(),
|
||||
CONSTRAINT "aethex_passports_user_id_unique" UNIQUE("user_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "aethex_projects" (
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"creator_id" varchar NOT NULL,
|
||||
"title" text NOT NULL,
|
||||
"description" text,
|
||||
"url" text,
|
||||
"image_url" text,
|
||||
"tags" json DEFAULT '[]'::json,
|
||||
"is_featured" boolean DEFAULT false,
|
||||
"created_at" timestamp DEFAULT now(),
|
||||
"updated_at" timestamp DEFAULT now()
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "aethex_sites" (
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"name" text NOT NULL,
|
||||
"url" text,
|
||||
"status" text,
|
||||
"uptime" numeric,
|
||||
"response_time" integer,
|
||||
"users" integer,
|
||||
"requests" integer,
|
||||
"last_check" timestamp,
|
||||
"services" json,
|
||||
"metrics" json,
|
||||
"created_at" timestamp DEFAULT now(),
|
||||
"metrics_history" json,
|
||||
"owner_id" varchar,
|
||||
"api_key_hash" text,
|
||||
"handshake_token" text,
|
||||
"handshake_token_expires_at" timestamp,
|
||||
CONSTRAINT "aethex_sites_name_unique" UNIQUE("name")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "applications" (
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"type" text NOT NULL,
|
||||
"full_name" text NOT NULL,
|
||||
"email" text NOT NULL,
|
||||
"location" text,
|
||||
"role_interest" text,
|
||||
"primary_skill" text,
|
||||
"experience_level" text,
|
||||
"availability" text,
|
||||
"portfolio_url" text,
|
||||
"resume_url" text,
|
||||
"interests" json,
|
||||
"message" text,
|
||||
"status" text DEFAULT 'new' NOT NULL,
|
||||
"submitted_at" timestamp DEFAULT now()
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "chat_messages" (
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar NOT NULL,
|
||||
"role" text NOT NULL,
|
||||
"content" text NOT NULL,
|
||||
"created_at" timestamp DEFAULT now()
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "profiles" (
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"username" text,
|
||||
"role" text DEFAULT 'member',
|
||||
"onboarded" boolean DEFAULT false,
|
||||
"bio" text,
|
||||
"skills" json,
|
||||
"avatar_url" text,
|
||||
"banner_url" text,
|
||||
"social_links" json,
|
||||
"loyalty_points" integer DEFAULT 0,
|
||||
"email" text,
|
||||
"created_at" timestamp DEFAULT now(),
|
||||
"updated_at" timestamp DEFAULT now(),
|
||||
"user_type" text DEFAULT 'community_member',
|
||||
"experience_level" text DEFAULT 'beginner',
|
||||
"full_name" text,
|
||||
"location" text,
|
||||
"total_xp" integer DEFAULT 0,
|
||||
"level" integer DEFAULT 1,
|
||||
"aethex_passport_id" varchar,
|
||||
"status" text DEFAULT 'offline',
|
||||
"is_verified" boolean DEFAULT false
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "projects" (
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"owner_id" varchar,
|
||||
"title" text NOT NULL,
|
||||
"description" text,
|
||||
"status" text DEFAULT 'planning',
|
||||
"github_url" text,
|
||||
"created_at" timestamp DEFAULT now(),
|
||||
"updated_at" timestamp DEFAULT now(),
|
||||
"user_id" varchar,
|
||||
"engine" text,
|
||||
"priority" text DEFAULT 'medium',
|
||||
"progress" integer DEFAULT 0,
|
||||
"live_url" text,
|
||||
"technologies" json
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "user_achievements" (
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar,
|
||||
"achievement_id" varchar,
|
||||
"site_id" text,
|
||||
"created_at" timestamp DEFAULT now(),
|
||||
"unlocked_at" timestamp DEFAULT now(),
|
||||
"earned_at" timestamp DEFAULT now()
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "user_profiles" (
|
||||
"id" varchar PRIMARY KEY NOT NULL,
|
||||
"username" text,
|
||||
"full_name" text,
|
||||
"avatar_url" text,
|
||||
"user_type" text NOT NULL,
|
||||
"experience_level" text DEFAULT 'beginner',
|
||||
"bio" text,
|
||||
"location" text,
|
||||
"website_url" text,
|
||||
"github_url" text,
|
||||
"twitter_url" text,
|
||||
"linkedin_url" text,
|
||||
"total_xp" integer DEFAULT 0,
|
||||
"level" integer DEFAULT 1,
|
||||
"created_at" timestamp DEFAULT now(),
|
||||
"updated_at" timestamp DEFAULT now(),
|
||||
"current_streak" integer DEFAULT 0,
|
||||
"longest_streak" integer DEFAULT 0,
|
||||
"last_streak_at" timestamp,
|
||||
"loyalty_points" integer DEFAULT 0,
|
||||
"reputation_score" integer DEFAULT 0,
|
||||
"wallet_address" varchar,
|
||||
"show_in_creator_directory" boolean DEFAULT false,
|
||||
"arms" json DEFAULT '[]'::json,
|
||||
"roles" json DEFAULT '[]'::json,
|
||||
"last_active_at" timestamp DEFAULT now(),
|
||||
"streak_days" integer DEFAULT 0,
|
||||
"roblox_user_id" text,
|
||||
"roblox_username" text,
|
||||
"unity_player_id" text,
|
||||
"unreal_player_id" text,
|
||||
"godot_player_id" text,
|
||||
"merged_to_user_id" varchar,
|
||||
"aethex_domain" text,
|
||||
"discord_id" text,
|
||||
"discord_username" text,
|
||||
"is_architect" boolean DEFAULT false,
|
||||
"xp" integer DEFAULT 0,
|
||||
"daily_streak" integer DEFAULT 0,
|
||||
"last_daily" timestamp,
|
||||
"last_xp_message" timestamp,
|
||||
"badges" json DEFAULT '[]'::json,
|
||||
CONSTRAINT "user_profiles_username_unique" UNIQUE("username"),
|
||||
CONSTRAINT "user_profiles_wallet_address_unique" UNIQUE("wallet_address"),
|
||||
CONSTRAINT "user_profiles_roblox_user_id_unique" UNIQUE("roblox_user_id"),
|
||||
CONSTRAINT "user_profiles_unity_player_id_unique" UNIQUE("unity_player_id"),
|
||||
CONSTRAINT "user_profiles_unreal_player_id_unique" UNIQUE("unreal_player_id"),
|
||||
CONSTRAINT "user_profiles_godot_player_id_unique" UNIQUE("godot_player_id"),
|
||||
CONSTRAINT "user_profiles_discord_id_unique" UNIQUE("discord_id")
|
||||
);
|
||||
1591
migrations/meta/0000_snapshot.json
Normal file
1591
migrations/meta/0000_snapshot.json
Normal file
File diff suppressed because it is too large
Load diff
13
migrations/meta/_journal.json
Normal file
13
migrations/meta/_journal.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"entries": [
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "7",
|
||||
"when": 1766373949237,
|
||||
"tag": "0000_worried_mastermind",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
179
package-lock.json
generated
179
package-lock.json
generated
|
|
@ -68,6 +68,7 @@
|
|||
"react-hook-form": "^7.66.0",
|
||||
"react-resizable-panels": "^2.1.9",
|
||||
"recharts": "^2.15.4",
|
||||
"socket.io": "^4.8.2",
|
||||
"sonner": "^2.0.7",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
|
|
@ -4020,6 +4021,12 @@
|
|||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@socket.io/component-emitter": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
||||
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@supabase/auth-js": {
|
||||
"version": "2.87.3",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.87.3.tgz",
|
||||
|
|
@ -4485,6 +4492,15 @@
|
|||
"@types/pg": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/cors": {
|
||||
"version": "2.8.19",
|
||||
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz",
|
||||
"integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-array": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
|
||||
|
|
@ -4653,6 +4669,7 @@
|
|||
"integrity": "sha512-/2WmmBXHLsfRqzfHW7BNZ8SbYzE8OSk7i3WjFYvfgRHj7S1xj+16Je5fUKv3lVdVzk/zn9TXOqf+avFCFIE0yQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"pg-protocol": "*",
|
||||
|
|
@ -4823,6 +4840,15 @@
|
|||
"postcss": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/base64id": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
|
||||
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^4.5.0 || >= 5.9"
|
||||
}
|
||||
},
|
||||
"node_modules/baseline-browser-mapping": {
|
||||
"version": "2.8.21",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.21.tgz",
|
||||
|
|
@ -5066,6 +5092,19 @@
|
|||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cors": {
|
||||
"version": "2.8.5",
|
||||
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
||||
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"object-assign": "^4",
|
||||
"vary": "^1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
|
|
@ -5209,9 +5248,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
|
|
@ -5495,6 +5534,44 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io": {
|
||||
"version": "6.6.5",
|
||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.5.tgz",
|
||||
"integrity": "sha512-2RZdgEbXmp5+dVbRm0P7HQUImZpICccJy7rN7Tv+SFa55pH+lxnuw6/K1ZxxBfHoYpSkHLAO92oa8O4SwFXA2A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/node": ">=10.0.0",
|
||||
"accepts": "~1.3.4",
|
||||
"base64id": "2.0.0",
|
||||
"cookie": "~0.7.2",
|
||||
"cors": "~2.8.5",
|
||||
"debug": "~4.4.1",
|
||||
"engine.io-parser": "~5.2.1",
|
||||
"ws": "~8.18.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-parser": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
|
||||
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io/node_modules/cookie": {
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
||||
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/enhanced-resolve": {
|
||||
"version": "5.18.3",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
|
||||
|
|
@ -7513,6 +7590,102 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io": {
|
||||
"version": "4.8.2",
|
||||
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.2.tgz",
|
||||
"integrity": "sha512-wMAICvNHJNtnd3Jq97xROyRyFjMQ2G8QsVF6V+K6+6lztP3GaTcIaos+6E7+8jD/NoY++/vCvU9AI+bvRBNXVw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.4",
|
||||
"base64id": "~2.0.0",
|
||||
"cors": "~2.8.5",
|
||||
"debug": "~4.4.1",
|
||||
"engine.io": "~6.6.0",
|
||||
"socket.io-adapter": "~2.5.2",
|
||||
"socket.io-parser": "~4.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-adapter": {
|
||||
"version": "2.5.5",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz",
|
||||
"integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "~4.3.4",
|
||||
"ws": "~8.17.1"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-adapter/node_modules/debug": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-adapter/node_modules/ws": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-parser": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-parser/node_modules/debug": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/sonner": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz",
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@
|
|||
"react-hook-form": "^7.66.0",
|
||||
"react-resizable-panels": "^2.1.9",
|
||||
"recharts": "^2.15.4",
|
||||
"socket.io": "^4.8.2",
|
||||
"sonner": "^2.0.7",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import session from "express-session";
|
|||
import { registerRoutes } from "./routes";
|
||||
import { serveStatic } from "./static";
|
||||
import { createServer } from "http";
|
||||
import { setupWebSocket } from "./websocket";
|
||||
|
||||
const app = express();
|
||||
const httpServer = createServer(app);
|
||||
|
|
@ -91,13 +92,16 @@ app.use((req, res, next) => {
|
|||
next();
|
||||
});
|
||||
|
||||
|
||||
(async () => {
|
||||
await registerRoutes(httpServer, app);
|
||||
|
||||
// Setup WebSocket server for real-time notifications and Aegis alerts
|
||||
setupWebSocket(httpServer);
|
||||
|
||||
app.use((err: any, _req: Request, res: Response, _next: NextFunction) => {
|
||||
const status = err.status || err.statusCode || 500;
|
||||
const message = err.message || "Internal Server Error";
|
||||
|
||||
res.status(status).json({ message });
|
||||
throw err;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -350,6 +350,7 @@ export async function registerRoutes(
|
|||
// ========== NEW ADMIN ROUTES ==========
|
||||
|
||||
// Get all aethex sites (admin only)
|
||||
// List all sites
|
||||
app.get("/api/sites", requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const sites = await storage.getSites();
|
||||
|
|
@ -358,6 +359,42 @@ export async function registerRoutes(
|
|||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Create a new site
|
||||
app.post("/api/sites", requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const site = await storage.createSite(req.body);
|
||||
res.status(201).json(site);
|
||||
} catch (err: any) {
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Update a site
|
||||
app.patch("/api/sites/:id", requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const site = await storage.updateSite(req.params.id, req.body);
|
||||
if (!site) {
|
||||
return res.status(404).json({ error: "Site not found" });
|
||||
}
|
||||
res.json(site);
|
||||
} catch (err: any) {
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Delete a site
|
||||
app.delete("/api/sites/:id", requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const deleted = await storage.deleteSite(req.params.id);
|
||||
if (!deleted) {
|
||||
return res.status(404).json({ error: "Site not found" });
|
||||
}
|
||||
res.json({ success: true });
|
||||
} catch (err: any) {
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Get auth logs (admin only)
|
||||
app.get("/api/auth-logs", requireAdmin, async (req, res) => {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ export interface IStorage {
|
|||
getAlerts(): Promise<any[]>;
|
||||
updateAlert(id: string, updates: any): Promise<any>;
|
||||
|
||||
// Notifications (for WebSocket)
|
||||
getNotifications(): Promise<any[]>;
|
||||
|
||||
// Chat Messages (AI memory)
|
||||
getChatHistory(userId: string, limit?: number): Promise<ChatMessage[]>;
|
||||
saveChatMessage(id: string, userId: string, role: string, content: string): Promise<void>;
|
||||
|
|
@ -47,6 +50,38 @@ export interface IStorage {
|
|||
}
|
||||
|
||||
export class SupabaseStorage implements IStorage {
|
||||
// Create a new site
|
||||
async createSite(site: any): Promise<any> {
|
||||
const { data, error } = await supabase
|
||||
.from('aethex_sites')
|
||||
.insert(site)
|
||||
.select()
|
||||
.single();
|
||||
if (error) throw new Error(error.message);
|
||||
return data;
|
||||
}
|
||||
|
||||
// Update a site
|
||||
async updateSite(id: string, updates: any): Promise<any> {
|
||||
const { data, error } = await supabase
|
||||
.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
|
||||
async deleteSite(id: string): Promise<boolean> {
|
||||
const { error, count } = await supabase
|
||||
.from('aethex_sites')
|
||||
.delete({ count: 'exact' })
|
||||
.eq('id', id);
|
||||
if (error) throw new Error(error.message);
|
||||
return (count ?? 0) > 0;
|
||||
}
|
||||
|
||||
async getProfiles(): Promise<Profile[]> {
|
||||
const { data, error } = await supabase
|
||||
|
|
@ -246,6 +281,38 @@ export class SupabaseStorage implements IStorage {
|
|||
}
|
||||
}
|
||||
|
||||
async getNotifications(): Promise<any[]> {
|
||||
// Get recent activity - applications, alerts, etc.
|
||||
const [applications, alerts] = await Promise.all([
|
||||
this.getApplications(),
|
||||
this.getAlerts()
|
||||
]);
|
||||
|
||||
// Transform into notification format
|
||||
const notifications = [
|
||||
...applications.slice(0, 5).map(app => ({
|
||||
id: app.id,
|
||||
type: 'application',
|
||||
message: `New application from ${app.full_name}`,
|
||||
timestamp: app.submitted_at,
|
||||
unread: true
|
||||
})),
|
||||
...alerts.filter(a => !a.is_resolved).slice(0, 5).map(alert => ({
|
||||
id: alert.id,
|
||||
type: 'alert',
|
||||
message: alert.message,
|
||||
severity: alert.severity,
|
||||
timestamp: alert.created_at,
|
||||
unread: true
|
||||
}))
|
||||
];
|
||||
|
||||
// Sort by timestamp desc
|
||||
return notifications.sort((a, b) =>
|
||||
new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
|
||||
);
|
||||
}
|
||||
|
||||
async getMetrics(): Promise<{
|
||||
totalProfiles: number;
|
||||
totalProjects: number;
|
||||
|
|
@ -276,3 +343,7 @@ export class SupabaseStorage implements IStorage {
|
|||
}
|
||||
|
||||
export const storage = new SupabaseStorage();
|
||||
|
||||
// Export helper functions for WebSocket
|
||||
export const getAlerts = () => storage.getAlerts();
|
||||
export const getNotifications = () => storage.getNotifications();
|
||||
|
|
|
|||
36
server/websocket.ts
Normal file
36
server/websocket.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import { Server } from "http";
|
||||
import { Server as SocketIOServer } from "socket.io";
|
||||
import { getAlerts, getNotifications } from "./storage";
|
||||
|
||||
export function setupWebSocket(httpServer: Server) {
|
||||
const io = new SocketIOServer(httpServer, {
|
||||
cors: {
|
||||
origin: "*",
|
||||
methods: ["GET", "POST"],
|
||||
},
|
||||
});
|
||||
|
||||
io.on("connection", (socket) => {
|
||||
// Send initial notifications and alerts
|
||||
Promise.all([getNotifications(), getAlerts()]).then(([notifications, alerts]) => {
|
||||
socket.emit("notifications", notifications);
|
||||
socket.emit("alerts", alerts);
|
||||
});
|
||||
|
||||
// Listen for alert resolution events
|
||||
socket.on("resolveAlert", async (alertId) => {
|
||||
// You'd call your alert resolution logic here
|
||||
// After resolving, broadcast updated alerts
|
||||
const alerts = await getAlerts();
|
||||
io.emit("alerts", alerts);
|
||||
});
|
||||
|
||||
// Listen for request to refresh notifications
|
||||
socket.on("refreshNotifications", async () => {
|
||||
const notifications = await getNotifications();
|
||||
socket.emit("notifications", notifications);
|
||||
});
|
||||
});
|
||||
|
||||
return io;
|
||||
}
|
||||
304
shared/schema.ts
304
shared/schema.ts
|
|
@ -1,4 +1,4 @@
|
|||
import { pgTable, text, varchar, boolean, integer, timestamp, json } from "drizzle-orm/pg-core";
|
||||
import { pgTable, text, varchar, boolean, integer, timestamp, json, decimal } from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema } from "drizzle-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
|
|
@ -95,3 +95,305 @@ export const insertChatMessageSchema = createInsertSchema(chatMessages).omit({
|
|||
|
||||
export type InsertChatMessage = z.infer<typeof insertChatMessageSchema>;
|
||||
export type ChatMessage = typeof chatMessages.$inferSelect;
|
||||
|
||||
// AeThex Sites table
|
||||
export const aethex_sites = pgTable("aethex_sites", {
|
||||
id: varchar("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
name: text("name").notNull().unique(),
|
||||
url: text("url"),
|
||||
status: text("status"),
|
||||
uptime: decimal("uptime"),
|
||||
response_time: integer("response_time"),
|
||||
users: integer("users"),
|
||||
requests: integer("requests"),
|
||||
last_check: timestamp("last_check"),
|
||||
services: json("services").$type<string[] | null>(),
|
||||
metrics: json("metrics"),
|
||||
created_at: timestamp("created_at").defaultNow(),
|
||||
metrics_history: json("metrics_history").$type<any[] | null>(),
|
||||
owner_id: varchar("owner_id"),
|
||||
api_key_hash: text("api_key_hash"),
|
||||
handshake_token: text("handshake_token"),
|
||||
handshake_token_expires_at: timestamp("handshake_token_expires_at"),
|
||||
});
|
||||
|
||||
export const insertAethexSiteSchema = createInsertSchema(aethex_sites).omit({
|
||||
created_at: true,
|
||||
});
|
||||
|
||||
export type InsertAethexSite = z.infer<typeof insertAethexSiteSchema>;
|
||||
export type AethexSite = typeof aethex_sites.$inferSelect;
|
||||
|
||||
// AeThex Alerts table
|
||||
export const aethex_alerts = pgTable("aethex_alerts", {
|
||||
id: varchar("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
site_id: varchar("site_id"),
|
||||
type: text("type").notNull(),
|
||||
severity: text("severity").notNull(),
|
||||
message: text("message").notNull(),
|
||||
is_resolved: boolean("is_resolved").default(false),
|
||||
created_at: timestamp("created_at").defaultNow(),
|
||||
resolved_at: timestamp("resolved_at"),
|
||||
});
|
||||
|
||||
export const insertAethexAlertSchema = createInsertSchema(aethex_alerts).omit({
|
||||
created_at: true,
|
||||
});
|
||||
|
||||
export type InsertAethexAlert = z.infer<typeof insertAethexAlertSchema>;
|
||||
export type AethexAlert = typeof aethex_alerts.$inferSelect;
|
||||
|
||||
// AeThex Applications table
|
||||
export const aethex_applications = pgTable("aethex_applications", {
|
||||
id: varchar("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
creator_id: varchar("creator_id").notNull(),
|
||||
opportunity_id: varchar("opportunity_id").notNull(),
|
||||
status: text("status").default("submitted"),
|
||||
cover_letter: text("cover_letter"),
|
||||
response_message: text("response_message"),
|
||||
applied_at: timestamp("applied_at").defaultNow(),
|
||||
updated_at: timestamp("updated_at").defaultNow(),
|
||||
});
|
||||
|
||||
export const insertAethexApplicationSchema = createInsertSchema(aethex_applications).omit({
|
||||
applied_at: true,
|
||||
updated_at: true,
|
||||
});
|
||||
|
||||
export type InsertAethexApplication = z.infer<typeof insertAethexApplicationSchema>;
|
||||
export type AethexApplication = typeof aethex_applications.$inferSelect;
|
||||
|
||||
// AeThex Creators table
|
||||
export const aethex_creators = pgTable("aethex_creators", {
|
||||
id: varchar("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
user_id: varchar("user_id").notNull(),
|
||||
username: text("username").notNull().unique(),
|
||||
bio: text("bio"),
|
||||
skills: json("skills").$type<string[]>().default([]),
|
||||
avatar_url: text("avatar_url"),
|
||||
experience_level: text("experience_level"),
|
||||
arm_affiliations: json("arm_affiliations").$type<string[]>().default([]),
|
||||
primary_arm: text("primary_arm"),
|
||||
is_discoverable: boolean("is_discoverable").default(true),
|
||||
allow_recommendations: boolean("allow_recommendations").default(true),
|
||||
devconnect_linked: boolean("devconnect_linked").default(false),
|
||||
created_at: timestamp("created_at").defaultNow(),
|
||||
updated_at: timestamp("updated_at").defaultNow(),
|
||||
});
|
||||
|
||||
export const insertAethexCreatorSchema = createInsertSchema(aethex_creators).omit({
|
||||
created_at: true,
|
||||
updated_at: true,
|
||||
});
|
||||
|
||||
export type InsertAethexCreator = z.infer<typeof insertAethexCreatorSchema>;
|
||||
export type AethexCreator = typeof aethex_creators.$inferSelect;
|
||||
|
||||
// AeThex Passports table
|
||||
export const aethex_passports = pgTable("aethex_passports", {
|
||||
id: varchar("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
user_id: varchar("user_id").notNull().unique(),
|
||||
created_at: timestamp("created_at").defaultNow(),
|
||||
});
|
||||
|
||||
export const insertAethexPassportSchema = createInsertSchema(aethex_passports).omit({
|
||||
created_at: true,
|
||||
});
|
||||
|
||||
export type InsertAethexPassport = z.infer<typeof insertAethexPassportSchema>;
|
||||
export type AethexPassport = typeof aethex_passports.$inferSelect;
|
||||
|
||||
// AeThex Projects table
|
||||
export const aethex_projects = pgTable("aethex_projects", {
|
||||
id: varchar("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
creator_id: varchar("creator_id").notNull(),
|
||||
title: text("title").notNull(),
|
||||
description: text("description"),
|
||||
url: text("url"),
|
||||
image_url: text("image_url"),
|
||||
tags: json("tags").$type<string[]>().default([]),
|
||||
is_featured: boolean("is_featured").default(false),
|
||||
created_at: timestamp("created_at").defaultNow(),
|
||||
updated_at: timestamp("updated_at").defaultNow(),
|
||||
});
|
||||
|
||||
export const insertAethexProjectSchema = createInsertSchema(aethex_projects).omit({
|
||||
created_at: true,
|
||||
updated_at: true,
|
||||
});
|
||||
|
||||
export type InsertAethexProject = z.infer<typeof insertAethexProjectSchema>;
|
||||
export type AethexProject = typeof aethex_projects.$inferSelect;
|
||||
|
||||
// User Profiles table (extended profiles)
|
||||
export const user_profiles = pgTable("user_profiles", {
|
||||
id: varchar("id").primaryKey(),
|
||||
username: text("username").unique(),
|
||||
full_name: text("full_name"),
|
||||
avatar_url: text("avatar_url"),
|
||||
user_type: text("user_type").notNull(),
|
||||
experience_level: text("experience_level").default("beginner"),
|
||||
bio: text("bio"),
|
||||
location: text("location"),
|
||||
website_url: text("website_url"),
|
||||
github_url: text("github_url"),
|
||||
twitter_url: text("twitter_url"),
|
||||
linkedin_url: text("linkedin_url"),
|
||||
total_xp: integer("total_xp").default(0),
|
||||
level: integer("level").default(1),
|
||||
created_at: timestamp("created_at").defaultNow(),
|
||||
updated_at: timestamp("updated_at").defaultNow(),
|
||||
current_streak: integer("current_streak").default(0),
|
||||
longest_streak: integer("longest_streak").default(0),
|
||||
last_streak_at: timestamp("last_streak_at"),
|
||||
loyalty_points: integer("loyalty_points").default(0),
|
||||
reputation_score: integer("reputation_score").default(0),
|
||||
wallet_address: varchar("wallet_address").unique(),
|
||||
show_in_creator_directory: boolean("show_in_creator_directory").default(false),
|
||||
arms: json("arms").$type<string[]>().default([]),
|
||||
roles: json("roles").$type<string[]>().default([]),
|
||||
last_active_at: timestamp("last_active_at").defaultNow(),
|
||||
streak_days: integer("streak_days").default(0),
|
||||
roblox_user_id: text("roblox_user_id").unique(),
|
||||
roblox_username: text("roblox_username"),
|
||||
unity_player_id: text("unity_player_id").unique(),
|
||||
unreal_player_id: text("unreal_player_id").unique(),
|
||||
godot_player_id: text("godot_player_id").unique(),
|
||||
merged_to_user_id: varchar("merged_to_user_id"),
|
||||
aethex_domain: text("aethex_domain"),
|
||||
discord_id: text("discord_id").unique(),
|
||||
discord_username: text("discord_username"),
|
||||
is_architect: boolean("is_architect").default(false),
|
||||
xp: integer("xp").default(0),
|
||||
daily_streak: integer("daily_streak").default(0),
|
||||
last_daily: timestamp("last_daily"),
|
||||
last_xp_message: timestamp("last_xp_message"),
|
||||
badges: json("badges").default([]),
|
||||
});
|
||||
|
||||
export const insertUserProfileSchema = createInsertSchema(user_profiles).omit({
|
||||
created_at: true,
|
||||
updated_at: true,
|
||||
last_active_at: true,
|
||||
});
|
||||
|
||||
export type InsertUserProfile = z.infer<typeof insertUserProfileSchema>;
|
||||
export type UserProfile = typeof user_profiles.$inferSelect;
|
||||
|
||||
// Achievements table
|
||||
export const achievements = pgTable("achievements", {
|
||||
id: varchar("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
name: text("name").notNull().unique(),
|
||||
description: text("description"),
|
||||
icon: text("icon"),
|
||||
points_reward: integer("points_reward").default(0),
|
||||
badge_color: text("badge_color"),
|
||||
rarity: text("rarity"),
|
||||
xp_reward: integer("xp_reward").default(0),
|
||||
category: varchar("category").notNull().default("milestone"),
|
||||
});
|
||||
|
||||
export const insertAchievementSchema = createInsertSchema(achievements);
|
||||
|
||||
export type InsertAchievement = z.infer<typeof insertAchievementSchema>;
|
||||
export type Achievement = typeof achievements.$inferSelect;
|
||||
|
||||
// User Achievements table
|
||||
export const user_achievements = pgTable("user_achievements", {
|
||||
id: varchar("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
user_id: varchar("user_id"),
|
||||
achievement_id: varchar("achievement_id"),
|
||||
site_id: text("site_id"),
|
||||
created_at: timestamp("created_at").defaultNow(),
|
||||
unlocked_at: timestamp("unlocked_at").defaultNow(),
|
||||
earned_at: timestamp("earned_at").defaultNow(),
|
||||
});
|
||||
|
||||
export const insertUserAchievementSchema = createInsertSchema(user_achievements).omit({
|
||||
created_at: true,
|
||||
unlocked_at: true,
|
||||
earned_at: true,
|
||||
});
|
||||
|
||||
export type InsertUserAchievement = z.infer<typeof insertUserAchievementSchema>;
|
||||
export type UserAchievement = typeof user_achievements.$inferSelect;
|
||||
|
||||
// Applications table
|
||||
export const applications = pgTable("applications", {
|
||||
id: varchar("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
type: text("type").notNull(),
|
||||
full_name: text("full_name").notNull(),
|
||||
email: text("email").notNull(),
|
||||
location: text("location"),
|
||||
role_interest: text("role_interest"),
|
||||
primary_skill: text("primary_skill"),
|
||||
experience_level: text("experience_level"),
|
||||
availability: text("availability"),
|
||||
portfolio_url: text("portfolio_url"),
|
||||
resume_url: text("resume_url"),
|
||||
interests: json("interests").$type<string[] | null>(),
|
||||
message: text("message"),
|
||||
status: text("status").notNull().default("new"),
|
||||
submitted_at: timestamp("submitted_at").defaultNow(),
|
||||
});
|
||||
|
||||
export const insertApplicationSchema = createInsertSchema(applications).omit({
|
||||
submitted_at: true,
|
||||
});
|
||||
|
||||
export type InsertApplication = z.infer<typeof insertApplicationSchema>;
|
||||
export type Application = typeof applications.$inferSelect;
|
||||
|
||||
// AeThex Opportunities table
|
||||
export const aethex_opportunities = pgTable("aethex_opportunities", {
|
||||
id: varchar("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
title: text("title").notNull(),
|
||||
description: text("description").notNull(),
|
||||
job_type: text("job_type").notNull(),
|
||||
salary_min: integer("salary_min"),
|
||||
salary_max: integer("salary_max"),
|
||||
experience_level: text("experience_level"),
|
||||
arm_affiliation: text("arm_affiliation").notNull(),
|
||||
posted_by_id: varchar("posted_by_id").notNull(),
|
||||
status: text("status").default("open"),
|
||||
created_at: timestamp("created_at").defaultNow(),
|
||||
updated_at: timestamp("updated_at").defaultNow(),
|
||||
});
|
||||
|
||||
export const insertAethexOpportunitySchema = createInsertSchema(aethex_opportunities).omit({
|
||||
created_at: true,
|
||||
updated_at: true,
|
||||
});
|
||||
|
||||
export type InsertAethexOpportunity = z.infer<typeof insertAethexOpportunitySchema>;
|
||||
export type AethexOpportunity = typeof aethex_opportunities.$inferSelect;
|
||||
|
||||
// AeThex Events table
|
||||
export const aethex_events = pgTable("aethex_events", {
|
||||
id: varchar("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||
site_id: varchar("site_id"),
|
||||
title: text("title").notNull(),
|
||||
description: text("description"),
|
||||
date: timestamp("date").notNull(), // Note: date is timestamp in Drizzle
|
||||
time: text("time").notNull(), // time as text
|
||||
location: text("location"),
|
||||
capacity: integer("capacity"),
|
||||
image_url: text("image_url"),
|
||||
created_at: timestamp("created_at").defaultNow(),
|
||||
updated_at: timestamp("updated_at"),
|
||||
category: text("category"),
|
||||
price: decimal("price"),
|
||||
featured: boolean("featured"),
|
||||
speakers: json("speakers").$type<string[] | null>(),
|
||||
agenda: json("agenda"),
|
||||
full_description: text("full_description"),
|
||||
map_url: text("map_url"),
|
||||
ticket_types: json("ticket_types"),
|
||||
});
|
||||
|
||||
export const insertAethexEventSchema = createInsertSchema(aethex_events).omit({
|
||||
created_at: true,
|
||||
});
|
||||
|
||||
export type InsertAethexEvent = z.infer<typeof insertAethexEventSchema>;
|
||||
export type AethexEvent = typeof aethex_events.$inferSelect;
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@ export default defineConfig({
|
|||
},
|
||||
server: {
|
||||
host: "0.0.0.0",
|
||||
hmr: {
|
||||
clientPort: 443,
|
||||
},
|
||||
allowedHosts: true,
|
||||
fs: {
|
||||
strict: true,
|
||||
|
|
|
|||
Loading…
Reference in a new issue