From c6a7ff286dc1495b0ea058f911e17a3fcfbacbbb Mon Sep 17 00:00:00 2001 From: cawcenter Date: Sun, 14 Dec 2025 17:55:01 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=B1=20VALHALLA:=20Added=20standalone?= =?UTF-8?q?=20God=20Mode=20app=20(independent=20container)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- GOD_MODE_IMPLEMENTATION_PLAN.md | 90 ++++++ god-mode/Dockerfile | 42 +++ god-mode/astro.config.mjs | 23 ++ god-mode/package.json | 99 ++++++ god-mode/src/pages/api/god/[...action].ts | 187 +++++++++++ god-mode/src/pages/index.astro | 367 ++++++++++++++++++++++ god-mode/tailwind.config.mjs | 116 +++++++ god-mode/tsconfig.json | 14 + 8 files changed, 938 insertions(+) create mode 100644 GOD_MODE_IMPLEMENTATION_PLAN.md create mode 100644 god-mode/Dockerfile create mode 100644 god-mode/astro.config.mjs create mode 100644 god-mode/package.json create mode 100644 god-mode/src/pages/api/god/[...action].ts create mode 100644 god-mode/src/pages/index.astro create mode 100644 god-mode/tailwind.config.mjs create mode 100644 god-mode/tsconfig.json diff --git a/GOD_MODE_IMPLEMENTATION_PLAN.md b/GOD_MODE_IMPLEMENTATION_PLAN.md new file mode 100644 index 0000000..caa2340 --- /dev/null +++ b/GOD_MODE_IMPLEMENTATION_PLAN.md @@ -0,0 +1,90 @@ +# God Mode (Valhalla) Implementation Plan + +## 1. Overview +We are extracting the "God Mode" diagnostics console into a completely standalone application ("Valhalla"). This ensures that even if the main Spark Platform crashes (e.g., Directus API failure, Container exhaustion), the diagnostics tools remain available to troubleshoot and fix the system. + +## 2. Architecture +- **Repo:** Monorepo strategy (`/god-mode` folder in `jumpstartscaling/net`). +- **Framework:** Astro + React (matching the main frontend stack). +- **Runtime:** Node.js 20 on Alpine Linux. +- **Database:** DIRECT connection to PostgreSQL (bypassing Directus). +- **Deployment:** Separate Coolify Application pointing to `/god-mode` base directory. + +## 3. Dependencies +To ensure full compatibility and future-proofing, we are including the **Standard Spark Feature Set** in the dependencies. This allows us to port *any* component from the main app to God Mode without missing libraries. + +**Core Stack:** +- `astro`, `@astrojs/node`, `@astrojs/react` +- `react`, `react-dom` +- `tailwindcss`, `shadcn` (radix-ui) + +**Data Layer:** +- `pg` (Postgres Client) - **CRITICAL** +- `ioredis` (Redis Client) - **CRITICAL** +- `@directus/sdk` (For future API repairs) +- `@tanstack/react-query` (Data fetching) + +**UI/Visualization:** +- `@tremor/react` (Dashboards) +- `recharts` (Metrics) +- `lucide-react` (Icons) +- `framer-motion` (Animations) + +## 4. File Structure +``` +/god-mode +├── Dockerfile (Standard Node 20 build) +├── package.json (Full dependency list) +├── astro.config.mjs (Node adapter config) +├── tsconfig.json (TypeScript config) +├── tailwind.config.cjs (Shared design system) +└── src + ├── lib + │ └── godMode.ts (Core logic: DB connection) + ├── pages + │ ├── index.astro (Main Dashboard) + │ └── api + │ └── god + │ └── [...action].ts (API Endpoints) + └── components + └── ... (Ported from frontend) +``` + +## 5. Implementation Steps + +### Step 1: Initialize Workspace +- Create `/god-mode` directory. +- Create `package.json` with the full dependency list. +- Create `Dockerfile` optimized for `npm install`. + +### Step 2: Configuration +- Copy `tailwind.config.mjs` (or cjs) from frontend to ensure design parity. +- Configure `astro.config.mjs` for Node.js SSR. + +### Step 3: Logic Porting +- Copy `/frontend/src/lib/godMode.ts` -> Update to use `process.env` directly. +- Copy `/frontend/src/pages/god.astro` -> `/god-mode/src/pages/index.astro`. +- Copy `/frontend/src/pages/api/god/[...action].ts` -> `/god-mode/src/pages/api/god/[...action].ts`. + +### Step 4: Verification +- Build locally (`npm run build`). +- Verify DB connection with explicit connection string. + +### Step 5: Deployment +- User creates new App in Coolify: + - **Repo:** `jumpstartscaling/net` + - **Base Directory:** `/god-mode` + - **Env Vars:** `DATABASE_URL`, `GOD_MODE_TOKEN` + +## 6. Coolify Env Vars +```bash +# Internal Connection String (from Coolify PostgreSQL) +DATABASE_URL=postgres://postgres:PASSWORD@host:5432/postgres + +# Security Token +GOD_MODE_TOKEN=jmQXoeyxWoBsB7eHzG7FmnH90f22JtaYBxXHoorhfZ-v4tT3VNEr9vvmwHqYHCDoWXHSU4DeZXApCP-Gha-YdA + +# Server Port +PORT=4321 +HOST=0.0.0.0 +``` diff --git a/god-mode/Dockerfile b/god-mode/Dockerfile new file mode 100644 index 0000000..f0d14c8 --- /dev/null +++ b/god-mode/Dockerfile @@ -0,0 +1,42 @@ +# God Mode (Valhalla) Dockerfile +# Optimized for reliable builds with full dependencies + +# 1. Base Image +FROM node:20-alpine AS base +WORKDIR /app +# Install libc6-compat for sharp/performance +RUN apk add --no-cache libc6-compat + +# 2. Dependencies +FROM base AS deps +WORKDIR /app +COPY package.json package-lock.json* ./ +# Use npm install for robustness (npm ci can fail if lockfile is out of sync) +RUN npm install --legacy-peer-deps + +# 3. Builder +FROM base AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . +RUN npm run build + +# 4. Runner +FROM base AS runner +WORKDIR /app +ENV NODE_ENV=production +ENV HOST=0.0.0.0 +ENV PORT=4321 + +# Create non-root user +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 astro + +COPY --from=builder /app/dist ./dist +COPY --from=deps /app/node_modules ./node_modules +COPY package.json ./ + +USER astro +EXPOSE 4321 + +CMD ["node", "./dist/server/entry.mjs"] diff --git a/god-mode/astro.config.mjs b/god-mode/astro.config.mjs new file mode 100644 index 0000000..ec63791 --- /dev/null +++ b/god-mode/astro.config.mjs @@ -0,0 +1,23 @@ +import { defineConfig } from 'astro/config'; +import node from '@astrojs/node'; +import react from '@astrojs/react'; +import tailwind from '@astrojs/tailwind'; + +// https://astro.build/config +export default defineConfig({ + output: 'server', + adapter: node({ + mode: 'standalone' + }), + integrations: [ + react(), + tailwind({ + applyBaseStyles: false, + }), + ], + vite: { + ssr: { + noExternal: ['path-to-regexp'] + } + } +}); diff --git a/god-mode/package.json b/god-mode/package.json new file mode 100644 index 0000000..f32c9ea --- /dev/null +++ b/god-mode/package.json @@ -0,0 +1,99 @@ +{ + "name": "spark-god-mode", + "type": "module", + "version": "1.0.0", + "scripts": { + "dev": "astro dev", + "start": "node ./dist/server/entry.mjs", + "build": "astro build", + "preview": "astro preview", + "astro": "astro" + }, + "dependencies": { + "@astrojs/node": "^8.2.6", + "@astrojs/partytown": "^2.1.4", + "@astrojs/react": "^3.2.0", + "@astrojs/sitemap": "^3.6.0", + "@astrojs/tailwind": "^5.1.0", + "@bull-board/api": "^6.15.0", + "@bull-board/express": "^6.15.0", + "@craftjs/core": "^0.2.12", + "@craftjs/utils": "^0.2.5", + "@directus/sdk": "^17.0.0", + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", + "@hookform/resolvers": "^5.2.2", + "@nanostores/react": "^1.0.0", + "@radix-ui/react-dialog": "^1.0.5", + "@radix-ui/react-dropdown-menu": "^2.0.6", + "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-select": "^2.0.0", + "@radix-ui/react-slot": "^1.0.2", + "@radix-ui/react-tabs": "^1.0.4", + "@radix-ui/react-toast": "^1.1.5", + "@tanstack/react-query": "^5.90.12", + "@tanstack/react-table": "^8.21.3", + "@tanstack/react-virtual": "^3.13.13", + "@tiptap/extension-placeholder": "^3.13.0", + "@tiptap/react": "^3.13.0", + "@tiptap/starter-kit": "^3.13.0", + "@tremor/react": "^3.18.7", + "@turf/turf": "^7.3.1", + "@types/leaflet": "^1.9.21", + "@types/lodash-es": "^4.17.12", + "@types/papaparse": "^5.5.2", + "@types/react-syntax-highlighter": "^15.5.13", + "@vite-pwa/astro": "^1.2.0", + "astro": "^4.7.0", + "astro-imagetools": "^0.9.0", + "bullmq": "^5.66.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cmdk": "^1.1.1", + "date-fns": "^4.1.0", + "framer-motion": "^12.23.26", + "html-to-image": "^1.11.13", + "immer": "^11.0.1", + "ioredis": "^5.8.2", + "leaflet": "^1.9.4", + "lodash-es": "^4.17.21", + "lucide-react": "^0.346.0", + "lzutf8": "^0.6.3", + "nanoid": "^5.0.5", + "nanostores": "^1.1.0", + "papaparse": "^5.5.3", + "pdfmake": "^0.2.20", + "pg": "^8.16.3", + "react": "^18.3.1", + "react-contenteditable": "^3.3.7", + "react-diff-viewer-continued": "^3.4.0", + "react-dom": "^18.3.1", + "react-dropzone": "^14.3.8", + "react-flow-renderer": "^10.3.17", + "react-hook-form": "^7.68.0", + "react-leaflet": "^4.2.1", + "react-markdown": "^10.1.0", + "react-syntax-highlighter": "^16.1.0", + "reactflow": "^11.11.4", + "recharts": "^3.5.1", + "remark-gfm": "^4.0.1", + "sonner": "^2.0.7", + "tailwind-merge": "^2.6.0", + "tailwindcss": "^3.4.0", + "tailwindcss-animate": "^1.0.7", + "zod": "^3.25.76", + "zustand": "^5.0.9" + }, + "devDependencies": { + "@types/node": "^20.11.0", + "@types/react": "^18.2.48", + "@types/react-dom": "^18.2.18", + "autoprefixer": "^10.4.18", + "postcss": "^8.4.35", + "rollup-plugin-visualizer": "^6.0.5", + "sharp": "^0.33.3", + "typescript": "^5.4.0", + "vite-plugin-compression": "^0.5.1", + "vite-plugin-inspect": "^11.3.3" + } +} \ No newline at end of file diff --git a/god-mode/src/pages/api/god/[...action].ts b/god-mode/src/pages/api/god/[...action].ts new file mode 100644 index 0000000..30d076c --- /dev/null +++ b/god-mode/src/pages/api/god/[...action].ts @@ -0,0 +1,187 @@ +/** + * 🔱 GOD MODE BACKDOOR - Direct PostgreSQL Access + * Standalone Version (Valhalla) + */ + +import type { APIRoute } from 'astro'; +import pg from 'pg'; // Default import for some environments +const { Pool } = pg; +import Redis from 'ioredis'; + +// Database connection +// Coolify provides DATABASE_URL in format: postgres://user:pass@host:5432/db +const pool = new Pool({ + connectionString: process.env.DATABASE_URL || process.env.DB_CONNECTION_STRING, + max: 3, + idleTimeoutMillis: 30000, + connectionTimeoutMillis: 5000, + ssl: process.env.DATABASE_URL?.includes('sslmode=require') ? { rejectUnauthorized: false } : undefined +}); + +// Redis connection +const REDIS_URL = process.env.REDIS_URL || 'redis://redis:6379'; + +// Service URLs +const DIRECTUS_URL = process.env.PUBLIC_DIRECTUS_URL || 'http://directus:8055'; + +// Token validation +function validateGodToken(request: Request): boolean { + const token = request.headers.get('X-God-Token') || + request.headers.get('Authorization')?.replace('Bearer ', '') || + new URL(request.url).searchParams.get('token'); + + const godToken = process.env.GOD_MODE_TOKEN; + + if (!godToken) { + // In standalone mode, we force a token for security + console.warn('⚠️ GOD_MODE_TOKEN not set in env!'); + return false; + } + + return token === godToken; +} + +function json(data: object, status = 200) { + return new Response(JSON.stringify(data, null, 2), { + status, + headers: { 'Content-Type': 'application/json' } + }); +} + +// GET Handler +export const GET: APIRoute = async ({ request, url }) => { + // Quick health check (no auth needed for load balancers) + if (url.pathname.endsWith('/healthz')) { + return new Response('OK', { status: 200 }); + } + + if (!validateGodToken(request)) { + return json({ error: 'Unauthorized - Invalid God Mode Token' }, 401); + } + + const action = url.pathname.split('/').pop(); + + try { + switch (action) { + case 'health': return await getHealth(); + case 'services': return await getServices(); + case 'db-status': return await getDbStatus(); + case 'tables': return await getTables(); + case 'logs': return await getLogs(); + default: return json({ + message: '🔱 Valhalla Active', + timestamp: new Date().toISOString() + }); + } + } catch (error: any) { + return json({ error: error.message, stack: error.stack }, 500); + } +}; + +// POST Handler +export const POST: APIRoute = async ({ request, url }) => { + if (!validateGodToken(request)) { + return json({ error: 'Unauthorized' }, 401); + } + + const action = url.pathname.split('/').pop(); + + if (action === 'sql') { + try { + const body = await request.json(); + if (!body.query) return json({ error: 'Missing query' }, 400); + + const result = await pool.query(body.query); + return json({ + success: true, + rowCount: result.rowCount, + rows: result.rows, + fields: result.fields?.map(f => f.name) + }); + } catch (error: any) { + return json({ error: error.message }, 500); + } + } + + return json({ error: 'Method not allowed' }, 405); +}; + +// --- Helpers --- + +async function getServices() { + const services: any = { timestamp: new Date().toISOString() }; + + // DB Check + try { + const start = Date.now(); + await pool.query('SELECT 1'); + services.postgresql = { status: '✅ RUNNING', latency: Date.now() - start }; + } catch (e: any) { + services.postgresql = { status: '❌ DOWN', error: e.message }; + } + + // Redis Check + try { + const redis = new Redis(REDIS_URL, { maxRetriesPerRequest: 1, connectTimeout: 2000 }); + const start = Date.now(); + await redis.ping(); + services.redis = { status: '✅ RUNNING', latency: Date.now() - start }; + redis.disconnect(); + } catch (e: any) { + services.redis = { status: '❌ DOWN', error: e.message }; + } + + // Directus Check + try { + const start = Date.now(); + const res = await fetch(`${DIRECTUS_URL}/server/health`, { signal: AbortSignal.timeout(5000) }); + services.directus = { + status: res.ok ? '✅ RUNNING' : `⚠️ HTTP ${res.status}`, + latency: Date.now() - start + }; + } catch (e: any) { + services.directus = { status: '❌ DOWN', error: e.message }; + } + + return json(services); +} + +async function getHealth() { + // Full system health (similar to getServices but more detail) + return getServices(); +} + +async function getDbStatus() { + try { + const res = await pool.query(` + SELECT current_database() as db, version(), + (SELECT count(*) FROM pg_stat_activity) as connections + `); + return json({ status: 'connected', ...res.rows[0] }); + } catch (e: any) { + return json({ status: 'error', error: e.message }); + } +} + +async function getTables() { + try { + const res = await pool.query(` + SELECT table_name + FROM information_schema.tables + WHERE table_schema = 'public' + ORDER BY table_name + `); + return json({ total: res.rows.length, tables: res.rows }); + } catch (e: any) { + return json({ error: e.message }); + } +} + +async function getLogs() { + try { + const res = await pool.query('SELECT * FROM work_log ORDER BY timestamp DESC LIMIT 50'); + return json({ logs: res.rows }); + } catch (e: any) { + return json({ error: e.message }); + } +} diff --git a/god-mode/src/pages/index.astro b/god-mode/src/pages/index.astro new file mode 100644 index 0000000..f716608 --- /dev/null +++ b/god-mode/src/pages/index.astro @@ -0,0 +1,367 @@ +--- +/** + * 🔱 GOD PANEL - System Diagnostics Dashboard + * + * This page is COMPLETELY STANDALONE: + * - No middleware + * - No Directus dependency + * - No redirects + * - Works even when everything else is broken + */ +export const prerender = false; +--- + + + + + + + 🔱 🔱 Valhalla - Spark God Mode + + + + + + +
+ + + + diff --git a/god-mode/tailwind.config.mjs b/god-mode/tailwind.config.mjs new file mode 100644 index 0000000..f25345b --- /dev/null +++ b/god-mode/tailwind.config.mjs @@ -0,0 +1,116 @@ +/** @type {import('tailwindcss').Config} */ +import defaultTheme from 'tailwindcss/defaultTheme' + +export default { + darkMode: ['class'], + content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'], + theme: { + extend: { + colors: { + // === THE TITANIUM PRO SYSTEM === + // The Void (Base Layer - Pure Black) + void: '#000000', + + // Surface Staircase (Hard-Edge Layers) + titanium: '#121212', // Level 1: Main panels + graphite: '#1C1C1E', // Level 2: Inputs/Secondary + jet: '#27272A', // Level 3: Popups/Modals + + // Edge System (Borders & Separators) + edge: { + subtle: 'rgba(255, 255, 255, 0.10)', // Standard borders + normal: 'rgba(255, 255, 255, 0.15)', // Card borders + bright: 'rgba(255, 255, 255, 0.30)', // Active borders + gold: '#D4AF37', // Selected state + }, + + // The Luxury (Gold Accent System) + gold: { + 100: '#FEF3C7', // Lightest + 200: '#FDE68A', + 300: '#FDE047', // Glowing text/data + 400: '#EAB308', // Button highlight + 500: '#D4AF37', // Antique Brass (primary) + 600: '#B49428', // Button dark + 700: '#A16207', + 800: '#854D0E', + 900: '#422006', // Deep shadow + }, + + // The Tech (Electric Blue - for live indicators) + electric: { + 400: '#38BDF8', + 500: '#0EA5E9', + }, + + // Text System (High Contrast Only) + silver: '#D1D5DB', // text-silver (darkest allowed) + + // shadcn/ui compatibility + border: 'rgba(255, 255, 255, 0.15)', + input: 'rgba(255, 255, 255, 0.10)', + ring: '#D4AF37', + background: '#000000', + foreground: '#FFFFFF', + primary: { + DEFAULT: '#D4AF37', + foreground: '#000000', + }, + secondary: { + DEFAULT: '#1C1C1E', + foreground: '#FFFFFF', + }, + destructive: { + DEFAULT: '#EF4444', + foreground: '#FFFFFF', + }, + muted: { + DEFAULT: '#121212', + foreground: '#D1D5DB', + }, + accent: { + DEFAULT: '#27272A', + foreground: '#FFFFFF', + }, + popover: { + DEFAULT: '#27272A', + foreground: '#FFFFFF', + }, + card: { + DEFAULT: '#121212', + foreground: '#FFFFFF', + }, + }, + + fontFamily: { + sans: ['Inter', ...defaultTheme.fontFamily.sans], + mono: ['JetBrains Mono', 'Consolas', ...defaultTheme.fontFamily.mono], + }, + + backgroundImage: { + 'gold-gradient': 'linear-gradient(to bottom, #EAB308, #CA8A04)', + 'gold-gradient-r': 'linear-gradient(to right, #EAB308, #CA8A04)', + 'metal-shine': 'linear-gradient(45deg, transparent 25%, rgba(255,255,255,0.05) 50%, transparent 75%)', + 'dot-grid': 'radial-gradient(rgba(255,255,255,0.1) 1px, transparent 1px)', + }, + + boxShadow: { + 'glow-gold': '0 0 20px -5px rgba(212, 175, 55, 0.3)', + 'glow-blue': '0 0 20px -5px rgba(56, 189, 248, 0.3)', + 'hard': '0 4px 0 0 #1c1c1e', // Block shadow for cards + 'hard-gold': '0 4px 0 0 rgba(212, 175, 55, 0.3)', + }, + + borderRadius: { + lg: 'var(--radius)', + md: 'calc(var(--radius) - 2px)', + sm: 'calc(--radius) - 4px)', + }, + + backgroundSize: { + 'dot-size': '20px 20px', + }, + }, + }, + plugins: [require('tailwindcss-animate')], +} diff --git a/god-mode/tsconfig.json b/god-mode/tsconfig.json new file mode 100644 index 0000000..b307633 --- /dev/null +++ b/god-mode/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "astro/tsconfigs/strict", + "compilerOptions": { + "moduleResolution": "node", + "jsx": "react-jsx", + "jsxImportSource": "react", + "baseUrl": ".", + "paths": { + "@/*": [ + "src/*" + ] + } + } +} \ No newline at end of file