modified: .env

This commit is contained in:
MrPiglr 2025-12-22 22:33:48 +00:00
parent be2ddda4d5
commit 0f7f5704da
15 changed files with 2642 additions and 24 deletions

5
.env
View file

@ -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_URL=https://kmdeisowhtsalsekkzqd.supabase.co
SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImttZGVpc293aHRzYWxzZWtrenFkIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTM3Mzc2NTIsImV4cCI6MjA2OTMxMzY1Mn0.2mvk-rDZnHOzdx6Cgcysh51a3cflOlRWO6OA1Z5YWuQ SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImttZGVpc293aHRzYWxzZWtrenFkIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTM3Mzc2NTIsImV4cCI6MjA2OTMxMzY1Mn0.2mvk-rDZnHOzdx6Cgcysh51a3cflOlRWO6OA1Z5YWuQ
AI_INTEGRATIONS_OPENAI_BASE_URL=https://placeholder.openai.com 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
View 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}"
}
]
}

View file

@ -45,14 +45,15 @@
<body> <body>
<div id="root"></div> <div id="root"></div>
<script type="module" src="/src/main.tsx"></script> <script type="module" src="/src/main.tsx"></script>
<script> <!-- Service Worker disabled in development to prevent CORS issues -->
if ('serviceWorker' in navigator) { <!-- <script>
if ('serviceWorker' in navigator && import.meta.env.PROD) {
window.addEventListener('load', () => { window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js') navigator.serviceWorker.register('/sw.js')
.then(reg => console.log('SW registered:', reg.scope)) .then(reg => console.log('SW registered:', reg.scope))
.catch(err => console.error('SW registration failed:', err)); .catch(err => console.error('SW registration failed:', err));
}); });
} }
</script> </script> -->
</body> </body>
</html> </html>

View file

@ -1,4 +1,5 @@
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { useState } from "react";
import { Link, useLocation } from "wouter"; import { Link, useLocation } from "wouter";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { useAuth } from "@/lib/auth"; import { useAuth } from "@/lib/auth";
@ -11,7 +12,13 @@ export default function AdminSites() {
const { user, logout } = useAuth(); const { user, logout } = useAuth();
const [, setLocation] = useLocation(); 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"], queryKey: ["sites"],
queryFn: async () => { queryFn: async () => {
const res = await fetch("/api/sites"); const res = await fetch("/api/sites");
@ -19,6 +26,58 @@ export default function AdminSites() {
return res.json(); 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 () => { const handleLogout = async () => {
await logout(); await logout();
@ -49,15 +108,47 @@ export default function AdminSites() {
<div className="flex-1 overflow-auto"> <div className="flex-1 overflow-auto">
<div className="p-8"> <div className="p-8">
<div className="mb-8"> <div className="mb-8 flex items-center justify-between">
<h2 className="text-2xl font-display font-bold text-white uppercase tracking-wider"> <div>
AeThex Sites <h2 className="text-2xl font-display font-bold text-white uppercase tracking-wider">
</h2> AeThex Sites
<p className="text-muted-foreground text-sm mt-1"> </h2>
{sites?.length || 0} monitored sites <p className="text-muted-foreground text-sm mt-1">
</p> {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> </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"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{isLoading ? ( {isLoading ? (
<div className="col-span-full text-center text-muted-foreground py-12"> <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" /> <Globe className="w-5 h-5 text-primary" />
<h3 className="font-display text-white uppercase text-sm">{site.name}</h3> <h3 className="font-display text-white uppercase text-sm">{site.name}</h3>
</div> </div>
<div className={`flex items-center gap-1 text-xs ${getStatusColor(site.status)}`}> <div className="flex items-center gap-2">
{getStatusIcon(site.status)} <button
{site.status || 'unknown'} 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> </div>
<div className={`flex items-center gap-1 text-xs ${getStatusColor(site.status)}`}>
{getStatusIcon(site.status)}
{site.status || 'unknown'}
</div>
{site.url && ( {site.url && (
<a <a
href={site.url} href={site.url}
@ -101,7 +204,6 @@ export default function AdminSites() {
{site.url} <ExternalLink className="w-3 h-3" /> {site.url} <ExternalLink className="w-3 h-3" />
</a> </a>
)} )}
<div className="grid grid-cols-2 gap-4 text-xs"> <div className="grid grid-cols-2 gap-4 text-xs">
<div> <div>
<div className="text-muted-foreground">Uptime</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 className="text-white font-bold">{site.requests || 0}</div>
</div> </div>
</div> </div>
{site.last_check && ( {site.last_check && (
<div className="mt-4 pt-4 border-t border-white/5 text-xs text-muted-foreground"> <div className="mt-4 pt-4 border-t border-white/5 text-xs text-muted-foreground">
Last check: {new Date(site.last_check).toLocaleString()} Last check: {new Date(site.last_check).toLocaleString()}

View 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")
);

File diff suppressed because it is too large Load diff

View 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
View file

@ -68,6 +68,7 @@
"react-hook-form": "^7.66.0", "react-hook-form": "^7.66.0",
"react-resizable-panels": "^2.1.9", "react-resizable-panels": "^2.1.9",
"recharts": "^2.15.4", "recharts": "^2.15.4",
"socket.io": "^4.8.2",
"sonner": "^2.0.7", "sonner": "^2.0.7",
"tailwind-merge": "^3.3.1", "tailwind-merge": "^3.3.1",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
@ -4020,6 +4021,12 @@
"win32" "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": { "node_modules/@supabase/auth-js": {
"version": "2.87.3", "version": "2.87.3",
"resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.87.3.tgz", "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.87.3.tgz",
@ -4485,6 +4492,15 @@
"@types/pg": "*" "@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": { "node_modules/@types/d3-array": {
"version": "3.2.1", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
@ -4653,6 +4669,7 @@
"integrity": "sha512-/2WmmBXHLsfRqzfHW7BNZ8SbYzE8OSk7i3WjFYvfgRHj7S1xj+16Je5fUKv3lVdVzk/zn9TXOqf+avFCFIE0yQ==", "integrity": "sha512-/2WmmBXHLsfRqzfHW7BNZ8SbYzE8OSk7i3WjFYvfgRHj7S1xj+16Je5fUKv3lVdVzk/zn9TXOqf+avFCFIE0yQ==",
"devOptional": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@types/node": "*", "@types/node": "*",
"pg-protocol": "*", "pg-protocol": "*",
@ -4823,6 +4840,15 @@
"postcss": "^8.1.0" "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": { "node_modules/baseline-browser-mapping": {
"version": "2.8.21", "version": "2.8.21",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.21.tgz", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.21.tgz",
@ -5066,6 +5092,19 @@
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
"license": "MIT" "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": { "node_modules/csstype": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
@ -5209,9 +5248,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/debug": { "node_modules/debug": {
"version": "4.3.7", "version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"ms": "^2.1.3" "ms": "^2.1.3"
@ -5495,6 +5534,44 @@
"node": ">= 0.8" "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": { "node_modules/enhanced-resolve": {
"version": "5.18.3", "version": "5.18.3",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
@ -7513,6 +7590,102 @@
"url": "https://github.com/sponsors/ljharb" "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": { "node_modules/sonner": {
"version": "2.0.7", "version": "2.0.7",
"resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz", "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz",

View file

@ -71,6 +71,7 @@
"react-hook-form": "^7.66.0", "react-hook-form": "^7.66.0",
"react-resizable-panels": "^2.1.9", "react-resizable-panels": "^2.1.9",
"recharts": "^2.15.4", "recharts": "^2.15.4",
"socket.io": "^4.8.2",
"sonner": "^2.0.7", "sonner": "^2.0.7",
"tailwind-merge": "^3.3.1", "tailwind-merge": "^3.3.1",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",

View file

@ -8,6 +8,7 @@ import session from "express-session";
import { registerRoutes } from "./routes"; import { registerRoutes } from "./routes";
import { serveStatic } from "./static"; import { serveStatic } from "./static";
import { createServer } from "http"; import { createServer } from "http";
import { setupWebSocket } from "./websocket";
const app = express(); const app = express();
const httpServer = createServer(app); const httpServer = createServer(app);
@ -91,13 +92,16 @@ app.use((req, res, next) => {
next(); next();
}); });
(async () => { (async () => {
await registerRoutes(httpServer, app); 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) => { app.use((err: any, _req: Request, res: Response, _next: NextFunction) => {
const status = err.status || err.statusCode || 500; const status = err.status || err.statusCode || 500;
const message = err.message || "Internal Server Error"; const message = err.message || "Internal Server Error";
res.status(status).json({ message }); res.status(status).json({ message });
throw err; throw err;
}); });

View file

@ -350,6 +350,7 @@ export async function registerRoutes(
// ========== NEW ADMIN ROUTES ========== // ========== NEW ADMIN ROUTES ==========
// Get all aethex sites (admin only) // Get all aethex sites (admin only)
// List all sites
app.get("/api/sites", requireAdmin, async (req, res) => { app.get("/api/sites", requireAdmin, async (req, res) => {
try { try {
const sites = await storage.getSites(); const sites = await storage.getSites();
@ -358,6 +359,42 @@ export async function registerRoutes(
res.status(500).json({ error: err.message }); 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) // Get auth logs (admin only)
app.get("/api/auth-logs", requireAdmin, async (req, res) => { app.get("/api/auth-logs", requireAdmin, async (req, res) => {

View file

@ -30,6 +30,9 @@ export interface IStorage {
getAlerts(): Promise<any[]>; getAlerts(): Promise<any[]>;
updateAlert(id: string, updates: any): Promise<any>; updateAlert(id: string, updates: any): Promise<any>;
// Notifications (for WebSocket)
getNotifications(): Promise<any[]>;
// Chat Messages (AI memory) // Chat Messages (AI memory)
getChatHistory(userId: string, limit?: number): Promise<ChatMessage[]>; getChatHistory(userId: string, limit?: number): Promise<ChatMessage[]>;
saveChatMessage(id: string, userId: string, role: string, content: string): Promise<void>; saveChatMessage(id: string, userId: string, role: string, content: string): Promise<void>;
@ -47,6 +50,38 @@ export interface IStorage {
} }
export class SupabaseStorage implements 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[]> { async getProfiles(): Promise<Profile[]> {
const { data, error } = await supabase 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<{ async getMetrics(): Promise<{
totalProfiles: number; totalProfiles: number;
totalProjects: number; totalProjects: number;
@ -276,3 +343,7 @@ export class SupabaseStorage implements IStorage {
} }
export const storage = new SupabaseStorage(); 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
View 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;
}

View file

@ -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 { createInsertSchema } from "drizzle-zod";
import { z } from "zod"; import { z } from "zod";
@ -95,3 +95,305 @@ export const insertChatMessageSchema = createInsertSchema(chatMessages).omit({
export type InsertChatMessage = z.infer<typeof insertChatMessageSchema>; export type InsertChatMessage = z.infer<typeof insertChatMessageSchema>;
export type ChatMessage = typeof chatMessages.$inferSelect; 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;

View file

@ -42,6 +42,9 @@ export default defineConfig({
}, },
server: { server: {
host: "0.0.0.0", host: "0.0.0.0",
hmr: {
clientPort: 443,
},
allowedHosts: true, allowedHosts: true,
fs: { fs: {
strict: true, strict: true,