diff --git a/.env b/.env index fd075aa..6076ac3 100644 --- a/.env +++ b/.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 \ No newline at end of file +AI_INTEGRATIONS_OPENAI_API_KEY=placeholder_key +SESSION_SECRET=3a44273587118f6f2926b9b6839f2ddea3aea4f75ccb78b9510611b399a42823 \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..ae2aa66 --- /dev/null +++ b/.vscode/launch.json @@ -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}" + } + ] +} \ No newline at end of file diff --git a/client/index.html b/client/index.html index d89c158..5ed491e 100644 --- a/client/index.html +++ b/client/index.html @@ -45,14 +45,15 @@
- --> diff --git a/client/src/pages/admin-sites.tsx b/client/src/pages/admin-sites.tsx index f1ea039..b2ef9cb 100644 --- a/client/src/pages/admin-sites.tsx +++ b/client/src/pages/admin-sites.tsx @@ -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(null); + const [showForm, setShowForm] = useState(false); + const [formLoading, setFormLoading] = useState(false); + const [formError, setFormError] = useState(null); + const [formSuccess, setFormSuccess] = useState(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) => { + 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() {
-
-

- AeThex Sites -

-

- {sites?.length || 0} monitored sites -

+
+
+

+ AeThex Sites +

+

+ {sites?.length || 0} monitored sites +

+
+
+ {formError &&
{formError}
} + {formSuccess &&
{formSuccess}
} + + {showForm && ( +
+

{editingSite ? "Edit Site" : "Add Site"}

+
+ + + + + + + +
+
+ + +
+
+ )} +
{isLoading ? (
@@ -85,12 +176,24 @@ export default function AdminSites() {

{site.name}

-
- {getStatusIcon(site.status)} - {site.status || 'unknown'} +
+ +
- +
+ {getStatusIcon(site.status)} + {site.status || 'unknown'} +
{site.url && ( )} -
Uptime
@@ -120,7 +222,6 @@ export default function AdminSites() {
{site.requests || 0}
- {site.last_check && (
Last check: {new Date(site.last_check).toLocaleString()} diff --git a/migrations/0000_worried_mastermind.sql b/migrations/0000_worried_mastermind.sql new file mode 100644 index 0000000..ffe3190 --- /dev/null +++ b/migrations/0000_worried_mastermind.sql @@ -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") +); diff --git a/migrations/meta/0000_snapshot.json b/migrations/meta/0000_snapshot.json new file mode 100644 index 0000000..0f33fe6 --- /dev/null +++ b/migrations/meta/0000_snapshot.json @@ -0,0 +1,1591 @@ +{ + "id": "dc913f26-9555-42c9-9477-e5eeb5f39a01", + "prevId": "00000000-0000-0000-0000-000000000000", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.achievements": { + "name": "achievements", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "points_reward": { + "name": "points_reward", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "badge_color": { + "name": "badge_color", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rarity": { + "name": "rarity", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "xp_reward": { + "name": "xp_reward", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "category": { + "name": "category", + "type": "varchar", + "primaryKey": false, + "notNull": true, + "default": "'milestone'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "achievements_name_unique": { + "name": "achievements_name_unique", + "nullsNotDistinct": false, + "columns": [ + "name" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.aethex_alerts": { + "name": "aethex_alerts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "site_id": { + "name": "site_id", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "severity": { + "name": "severity", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_resolved": { + "name": "is_resolved", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "resolved_at": { + "name": "resolved_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.aethex_applications": { + "name": "aethex_applications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "creator_id": { + "name": "creator_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "opportunity_id": { + "name": "opportunity_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'submitted'" + }, + "cover_letter": { + "name": "cover_letter", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "response_message": { + "name": "response_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applied_at": { + "name": "applied_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.aethex_creators": { + "name": "aethex_creators", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bio": { + "name": "bio", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "skills": { + "name": "skills", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'[]'::json" + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "experience_level": { + "name": "experience_level", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "arm_affiliations": { + "name": "arm_affiliations", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'[]'::json" + }, + "primary_arm": { + "name": "primary_arm", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_discoverable": { + "name": "is_discoverable", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "allow_recommendations": { + "name": "allow_recommendations", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "devconnect_linked": { + "name": "devconnect_linked", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "aethex_creators_username_unique": { + "name": "aethex_creators_username_unique", + "nullsNotDistinct": false, + "columns": [ + "username" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.aethex_events": { + "name": "aethex_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "site_id": { + "name": "site_id", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "date": { + "name": "date", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "time": { + "name": "time", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "capacity": { + "name": "capacity", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "image_url": { + "name": "image_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "price": { + "name": "price", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "featured": { + "name": "featured", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "speakers": { + "name": "speakers", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "agenda": { + "name": "agenda", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "full_description": { + "name": "full_description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "map_url": { + "name": "map_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ticket_types": { + "name": "ticket_types", + "type": "json", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.aethex_opportunities": { + "name": "aethex_opportunities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "job_type": { + "name": "job_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "salary_min": { + "name": "salary_min", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "salary_max": { + "name": "salary_max", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "experience_level": { + "name": "experience_level", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "arm_affiliation": { + "name": "arm_affiliation", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "posted_by_id": { + "name": "posted_by_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'open'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.aethex_passports": { + "name": "aethex_passports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "aethex_passports_user_id_unique": { + "name": "aethex_passports_user_id_unique", + "nullsNotDistinct": false, + "columns": [ + "user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.aethex_projects": { + "name": "aethex_projects", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "creator_id": { + "name": "creator_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "image_url": { + "name": "image_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tags": { + "name": "tags", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'[]'::json" + }, + "is_featured": { + "name": "is_featured", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.aethex_sites": { + "name": "aethex_sites", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uptime": { + "name": "uptime", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "response_time": { + "name": "response_time", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "users": { + "name": "users", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "requests": { + "name": "requests", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_check": { + "name": "last_check", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "services": { + "name": "services", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "metrics": { + "name": "metrics", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "metrics_history": { + "name": "metrics_history", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "api_key_hash": { + "name": "api_key_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "handshake_token": { + "name": "handshake_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "handshake_token_expires_at": { + "name": "handshake_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "aethex_sites_name_unique": { + "name": "aethex_sites_name_unique", + "nullsNotDistinct": false, + "columns": [ + "name" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.applications": { + "name": "applications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "full_name": { + "name": "full_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role_interest": { + "name": "role_interest", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "primary_skill": { + "name": "primary_skill", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "experience_level": { + "name": "experience_level", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "availability": { + "name": "availability", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "portfolio_url": { + "name": "portfolio_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resume_url": { + "name": "resume_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "interests": { + "name": "interests", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'new'" + }, + "submitted_at": { + "name": "submitted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_messages": { + "name": "chat_messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.profiles": { + "name": "profiles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'member'" + }, + "onboarded": { + "name": "onboarded", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "bio": { + "name": "bio", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "skills": { + "name": "skills", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banner_url": { + "name": "banner_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "social_links": { + "name": "social_links", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "loyalty_points": { + "name": "loyalty_points", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user_type": { + "name": "user_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'community_member'" + }, + "experience_level": { + "name": "experience_level", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'beginner'" + }, + "full_name": { + "name": "full_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "total_xp": { + "name": "total_xp", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "level": { + "name": "level", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 1 + }, + "aethex_passport_id": { + "name": "aethex_passport_id", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'offline'" + }, + "is_verified": { + "name": "is_verified", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.projects": { + "name": "projects", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "owner_id": { + "name": "owner_id", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'planning'" + }, + "github_url": { + "name": "github_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user_id": { + "name": "user_id", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "engine": { + "name": "engine", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "priority": { + "name": "priority", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'medium'" + }, + "progress": { + "name": "progress", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "live_url": { + "name": "live_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "technologies": { + "name": "technologies", + "type": "json", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_achievements": { + "name": "user_achievements", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "achievement_id": { + "name": "achievement_id", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "site_id": { + "name": "site_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "unlocked_at": { + "name": "unlocked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "earned_at": { + "name": "earned_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_profiles": { + "name": "user_profiles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "full_name": { + "name": "full_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_type": { + "name": "user_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "experience_level": { + "name": "experience_level", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'beginner'" + }, + "bio": { + "name": "bio", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "website_url": { + "name": "website_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "github_url": { + "name": "github_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "twitter_url": { + "name": "twitter_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "linkedin_url": { + "name": "linkedin_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "total_xp": { + "name": "total_xp", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "level": { + "name": "level", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 1 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "current_streak": { + "name": "current_streak", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "longest_streak": { + "name": "longest_streak", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "last_streak_at": { + "name": "last_streak_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "loyalty_points": { + "name": "loyalty_points", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "reputation_score": { + "name": "reputation_score", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "wallet_address": { + "name": "wallet_address", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "show_in_creator_directory": { + "name": "show_in_creator_directory", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "arms": { + "name": "arms", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'[]'::json" + }, + "roles": { + "name": "roles", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'[]'::json" + }, + "last_active_at": { + "name": "last_active_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "streak_days": { + "name": "streak_days", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "roblox_user_id": { + "name": "roblox_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "roblox_username": { + "name": "roblox_username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "unity_player_id": { + "name": "unity_player_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "unreal_player_id": { + "name": "unreal_player_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "godot_player_id": { + "name": "godot_player_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "merged_to_user_id": { + "name": "merged_to_user_id", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "aethex_domain": { + "name": "aethex_domain", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discord_id": { + "name": "discord_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discord_username": { + "name": "discord_username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_architect": { + "name": "is_architect", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "xp": { + "name": "xp", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "daily_streak": { + "name": "daily_streak", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "last_daily": { + "name": "last_daily", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_xp_message": { + "name": "last_xp_message", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "badges": { + "name": "badges", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'[]'::json" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_profiles_username_unique": { + "name": "user_profiles_username_unique", + "nullsNotDistinct": false, + "columns": [ + "username" + ] + }, + "user_profiles_wallet_address_unique": { + "name": "user_profiles_wallet_address_unique", + "nullsNotDistinct": false, + "columns": [ + "wallet_address" + ] + }, + "user_profiles_roblox_user_id_unique": { + "name": "user_profiles_roblox_user_id_unique", + "nullsNotDistinct": false, + "columns": [ + "roblox_user_id" + ] + }, + "user_profiles_unity_player_id_unique": { + "name": "user_profiles_unity_player_id_unique", + "nullsNotDistinct": false, + "columns": [ + "unity_player_id" + ] + }, + "user_profiles_unreal_player_id_unique": { + "name": "user_profiles_unreal_player_id_unique", + "nullsNotDistinct": false, + "columns": [ + "unreal_player_id" + ] + }, + "user_profiles_godot_player_id_unique": { + "name": "user_profiles_godot_player_id_unique", + "nullsNotDistinct": false, + "columns": [ + "godot_player_id" + ] + }, + "user_profiles_discord_id_unique": { + "name": "user_profiles_discord_id_unique", + "nullsNotDistinct": false, + "columns": [ + "discord_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/migrations/meta/_journal.json b/migrations/meta/_journal.json new file mode 100644 index 0000000..b210484 --- /dev/null +++ b/migrations/meta/_journal.json @@ -0,0 +1,13 @@ +{ + "version": "7", + "dialect": "postgresql", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1766373949237, + "tag": "0000_worried_mastermind", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 848f352..ba8a01d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 5578171..5873071 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/server/index.ts b/server/index.ts index 5418ead..cbf6447 100644 --- a/server/index.ts +++ b/server/index.ts @@ -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; }); diff --git a/server/routes.ts b/server/routes.ts index ebe73c7..5b65698 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -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) => { diff --git a/server/storage.ts b/server/storage.ts index f3079f0..4638dee 100644 --- a/server/storage.ts +++ b/server/storage.ts @@ -30,6 +30,9 @@ export interface IStorage { getAlerts(): Promise; updateAlert(id: string, updates: any): Promise; + // Notifications (for WebSocket) + getNotifications(): Promise; + // Chat Messages (AI memory) getChatHistory(userId: string, limit?: number): Promise; saveChatMessage(id: string, userId: string, role: string, content: string): Promise; @@ -47,6 +50,38 @@ export interface IStorage { } export class SupabaseStorage implements IStorage { + // Create a new site + async createSite(site: any): Promise { + 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 { + 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 { + 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 { const { data, error } = await supabase @@ -246,6 +281,38 @@ export class SupabaseStorage implements IStorage { } } + async getNotifications(): Promise { + // 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(); diff --git a/server/websocket.ts b/server/websocket.ts new file mode 100644 index 0000000..ad956a4 --- /dev/null +++ b/server/websocket.ts @@ -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; +} diff --git a/shared/schema.ts b/shared/schema.ts index 72c5bd6..c006a00 100644 --- a/shared/schema.ts +++ b/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; 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(), + metrics: json("metrics"), + created_at: timestamp("created_at").defaultNow(), + metrics_history: json("metrics_history").$type(), + 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; +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; +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; +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().default([]), + avatar_url: text("avatar_url"), + experience_level: text("experience_level"), + arm_affiliations: json("arm_affiliations").$type().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; +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; +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().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; +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().default([]), + roles: json("roles").$type().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; +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; +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; +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(), + 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; +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; +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(), + 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; +export type AethexEvent = typeof aethex_events.$inferSelect; diff --git a/vite.config.ts b/vite.config.ts index d825862..b20b6aa 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -42,6 +42,9 @@ export default defineConfig({ }, server: { host: "0.0.0.0", + hmr: { + clientPort: 443, + }, allowedHosts: true, fs: { strict: true,