admin08077 commited on
Commit
084aac3
·
verified ·
1 Parent(s): e253bc0

Upload 11 files

Browse files
Files changed (11) hide show
  1. App.tsx +231 -0
  2. Dockerfile +27 -0
  3. README.md +20 -11
  4. index.html +119 -0
  5. index.tsx +16 -0
  6. metadata.json +7 -0
  7. package.json +32 -0
  8. server.js +225 -0
  9. tsconfig.json +29 -0
  10. types.ts +154 -0
  11. vite.config.ts +21 -0
App.tsx ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useState, useEffect } from 'react';
3
+ /* Fix: react-router-dom exports may be flaky in this environment, using standard v6 imports */
4
+ import { HashRouter, Routes, Route, Link, useLocation, Navigate } from 'react-router-dom';
5
+ import {
6
+ Leaf,
7
+ Bell,
8
+ LogOut,
9
+ Activity,
10
+ ChevronRight,
11
+ Cpu,
12
+ Settings as SettingsIcon,
13
+ Terminal,
14
+ Loader2
15
+ } from 'lucide-react';
16
+ /* Fix: removed .ts extensions from imports */
17
+ import { routes } from './views/routes';
18
+ import Login from './views/Login';
19
+ import Landing from './views/Landing';
20
+ import PrivacyPolicy from './views/PrivacyPolicy';
21
+ import Documentation from './views/Documentation';
22
+ import Airdrop from './views/Airdrop';
23
+ import { apiClient } from './services/api';
24
+ import { UserSession } from './types/index';
25
+
26
+ const SidebarItem: React.FC<{ icon: any, label: string, path: string, active: boolean }> = ({ icon: Icon, label, path, active }) => (
27
+ <Link
28
+ to={path}
29
+ className={`flex items-center justify-between px-6 py-4 rounded-2xl transition-all duration-500 group ${
30
+ active
31
+ ? 'bg-white text-black shadow-2xl'
32
+ : 'text-zinc-500 hover:text-white hover:bg-zinc-900/50'
33
+ }`}
34
+ >
35
+ <div className="flex items-center space-x-4">
36
+ <Icon size={18} className={`${active ? 'text-black' : 'text-zinc-600 group-hover:text-blue-500'} transition-colors duration-500`} />
37
+ <span className="font-black text-[10px] uppercase tracking-[0.2em]">{label}</span>
38
+ </div>
39
+ {active && <ChevronRight size={14} />}
40
+ </Link>
41
+ );
42
+
43
+ const Header = ({ user, onLogout }: { user: UserSession, onLogout: () => void }) => {
44
+ const isEsgNeutral = localStorage.getItem('esg_neutral') === 'true';
45
+ const location = useLocation();
46
+ const currentRoute = routes.find(r => r.path === location.pathname);
47
+ const [typedTitle, setTypedTitle] = useState('');
48
+
49
+ useEffect(() => {
50
+ if (!currentRoute) return;
51
+ let i = 0;
52
+ const fullText = currentRoute.label;
53
+ setTypedTitle('');
54
+ const timer = setInterval(() => {
55
+ setTypedTitle(fullText.substring(0, i + 1));
56
+ i++;
57
+ if (i >= fullText.length) clearInterval(timer);
58
+ }, 40);
59
+ return () => clearInterval(timer);
60
+ }, [location.pathname, currentRoute]);
61
+
62
+ return (
63
+ <header className="h-24 flex items-center justify-between px-10 bg-transparent print:hidden relative z-50">
64
+ <div className="flex items-center space-x-6">
65
+ <div className="hidden md:flex flex-col">
66
+ <h1 className="text-xl font-black tracking-tighter text-white italic leading-none uppercase">
67
+ {typedTitle} <span className="text-blue-500 not-italic block md:inline">NODE</span>
68
+ </h1>
69
+ <p className="text-[10px] text-zinc-600 font-black uppercase tracking-[0.3em] mt-1 flex items-center gap-2">
70
+ <Terminal size={10} />
71
+ ROOT_IDENTIFIER: {user.name.toUpperCase().replace(' ', '_')}
72
+ </p>
73
+ </div>
74
+
75
+ {isEsgNeutral && (
76
+ <div className="px-4 py-1.5 bg-emerald-500/10 border border-emerald-500/20 rounded-full flex items-center gap-2 shadow-2xl shadow-emerald-500/5">
77
+ <Leaf size={12} className="text-emerald-500" />
78
+ <span className="text-[10px] font-black text-emerald-500 uppercase tracking-widest">ESG Offset Verified</span>
79
+ </div>
80
+ )}
81
+ </div>
82
+
83
+ <div className="flex items-center space-x-8">
84
+ <div className="flex items-center space-x-6">
85
+ <button className="p-3 text-zinc-500 hover:text-white transition-all relative group bg-zinc-950 rounded-xl border border-zinc-900 shadow-xl">
86
+ <Bell size={18} />
87
+ <span className="absolute top-3 right-3 w-2 h-2 bg-blue-500 rounded-full shadow-[0_0_10px_rgba(59,130,246,0.8)]"></span>
88
+ </button>
89
+ <div className="h-8 w-px bg-zinc-900"></div>
90
+ <div className="flex items-center space-x-4 group cursor-pointer" onClick={onLogout}>
91
+ <div className="text-right">
92
+ <p className="text-xs font-black text-white uppercase italic tracking-tighter">{user.name}</p>
93
+ <p className="text-[9px] text-zinc-600 uppercase tracking-[0.2em] font-bold">{user.role}</p>
94
+ </div>
95
+ <div className="w-12 h-12 rounded-2xl bg-zinc-950 border border-zinc-900 flex items-center justify-center group-hover:border-rose-500/50 transition-all shadow-2xl">
96
+ <LogOut size={18} className="text-zinc-600 group-hover:text-rose-500 transition-colors" />
97
+ </div>
98
+ </div>
99
+ </div>
100
+ </div>
101
+ </header>
102
+ );
103
+ };
104
+
105
+ const App: React.FC = () => {
106
+ const [currentUser, setCurrentUser] = useState<UserSession | null>(null);
107
+ const [isAuthChecked, setIsAuthChecked] = useState<boolean>(false);
108
+
109
+ const checkAuthStatus = async () => {
110
+ const { user } = await apiClient.auth.me();
111
+ setCurrentUser(user);
112
+ setIsAuthChecked(true);
113
+ };
114
+
115
+ useEffect(() => {
116
+ checkAuthStatus();
117
+ window.addEventListener('auth-update', checkAuthStatus);
118
+ return () => window.removeEventListener('auth-update', checkAuthStatus);
119
+ }, []);
120
+
121
+ const handleLogout = async () => {
122
+ await apiClient.auth.logout();
123
+ setCurrentUser(null);
124
+ window.dispatchEvent(new Event('auth-update'));
125
+ };
126
+
127
+ if (!isAuthChecked) {
128
+ return (
129
+ <div className="min-h-screen bg-[#020202] flex flex-col items-center justify-center space-y-8">
130
+ <Loader2 className="animate-spin text-blue-500" size={48} />
131
+ <p className="text-[10px] font-black text-zinc-600 uppercase tracking-[0.4em] animate-pulse">Initializing Neural Link...</p>
132
+ </div>
133
+ );
134
+ }
135
+
136
+ if (!currentUser) {
137
+ return (
138
+ <HashRouter>
139
+ <Routes>
140
+ <Route path="/" element={<Landing />} />
141
+ <Route path="/login" element={<Login />} />
142
+ <Route path="/airdrop" element={<Airdrop />} />
143
+ <Route path="/manifesto" element={<PrivacyPolicy />} />
144
+ <Route path="/documentation" element={<Documentation />} />
145
+ <Route path="*" element={<Navigate to="/" replace />} />
146
+ </Routes>
147
+ </HashRouter>
148
+ );
149
+ }
150
+
151
+ return (
152
+ <HashRouter>
153
+ <div className="flex min-h-screen bg-[#020202] text-zinc-400 antialiased selection:bg-blue-500/30 selection:text-blue-200 font-sans">
154
+ <div className="fixed inset-0 pointer-events-none opacity-[0.03] z-0 overflow-hidden">
155
+ <div className="absolute inset-0 bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:40px_40px]"></div>
156
+ </div>
157
+
158
+ <aside className="w-80 fixed h-full bg-[#050505] border-r border-zinc-900 p-8 flex flex-col print:hidden z-50">
159
+ <div className="mb-14 px-4 flex items-center space-x-4">
160
+ <div className="w-12 h-12 bg-white rounded-2xl flex items-center justify-center shadow-2xl shadow-white/5">
161
+ <Cpu size={24} className="text-black" />
162
+ </div>
163
+ <div>
164
+ <h1 className="text-lg font-black italic tracking-tighter text-white uppercase leading-none">
165
+ Lumina <span className="text-blue-500 not-italic">Quantum</span>
166
+ </h1>
167
+ <p className="text-[9px] uppercase tracking-[0.4em] font-bold text-zinc-600 mt-1">Institutional Ledger</p>
168
+ </div>
169
+ </div>
170
+
171
+ <nav className="flex-1 space-y-2 overflow-y-auto custom-scrollbar pr-2 pb-10">
172
+ <NavigationLinks />
173
+ </nav>
174
+
175
+ <div className="mt-auto pt-10 border-t border-zinc-900">
176
+ <SidebarItem icon={SettingsIcon} label="System Config" path="/settings" active={window.location.hash.includes('settings')} />
177
+ <div className="mt-8 p-6 bg-zinc-950 border border-zinc-900 rounded-[2rem] flex items-center justify-between group cursor-help shadow-2xl">
178
+ <div className="flex items-center gap-3">
179
+ <Activity size={16} className="text-emerald-500" />
180
+ <span className="text-[9px] font-black uppercase tracking-widest text-zinc-500">Node Reachable</span>
181
+ </div>
182
+ <div className="w-2 h-2 bg-emerald-500 rounded-full animate-pulse shadow-[0_0_8px_rgba(16,185,129,0.5)]"></div>
183
+ </div>
184
+ </div>
185
+ </aside>
186
+
187
+ <main className="flex-1 ml-80 min-h-screen flex flex-col relative z-10 print:ml-0 print:bg-white">
188
+ <Header user={currentUser} onLogout={handleLogout} />
189
+ <div className="px-10 pb-20 print:p-0">
190
+ <Routes>
191
+ {routes.map((route) => (
192
+ <Route key={route.path} path={route.path} element={<route.component />} />
193
+ ))}
194
+ <Route path="*" element={<Navigate to="/" replace />} />
195
+ </Routes>
196
+ </div>
197
+ </main>
198
+ </div>
199
+ </HashRouter>
200
+ );
201
+ };
202
+
203
+ const NavigationLinks = () => {
204
+ const location = useLocation();
205
+ const categories = ['core', 'registry', 'finance', 'intelligence', 'system', 'admin'];
206
+
207
+ return (
208
+ <div className="space-y-10">
209
+ {categories.map(cat => {
210
+ const catRoutes = routes.filter(r => r.showInSidebar && r.category === cat);
211
+ if (catRoutes.length === 0) return null;
212
+ return (
213
+ <div key={cat} className="space-y-3">
214
+ <p className="px-6 text-[8px] font-black uppercase text-zinc-700 tracking-[0.5em] mb-4">{cat.toUpperCase()}_V6_SEGMENT</p>
215
+ {catRoutes.map((route) => (
216
+ <SidebarItem
217
+ key={route.path}
218
+ icon={route.icon}
219
+ label={route.label}
220
+ path={route.path}
221
+ active={location.pathname === route.path}
222
+ />
223
+ ))}
224
+ </div>
225
+ );
226
+ })}
227
+ </div>
228
+ );
229
+ };
230
+
231
+ export default App;
Dockerfile ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use an official Node.js runtime as the base image
2
+ FROM node:20
3
+
4
+ # Set the working directory in the container
5
+ WORKDIR /app
6
+
7
+ # Copy package.json and install all dependencies
8
+ COPY package.json ./
9
+ RUN npm install
10
+
11
+ # Copy the rest of the application code
12
+ COPY . .
13
+
14
+ # Build the React frontend into the 'dist' folder
15
+ # This is the folder server.js will serve
16
+ RUN npm run build
17
+
18
+ # Create an empty SQLite database file and set permissions
19
+ # This ensures the application has write access in the container
20
+ RUN touch database.db && chmod 777 database.db
21
+
22
+ # Hugging Face Spaces use port 7860 by default
23
+ ENV PORT=7860
24
+ EXPOSE 7860
25
+
26
+ # Start the application using the start script defined in package.json
27
+ CMD ["npm", "start"]
README.md CHANGED
@@ -1,11 +1,20 @@
1
- ---
2
- title: Aibanking.dev
3
- emoji: 👀
4
- colorFrom: indigo
5
- colorTo: pink
6
- sdk: docker
7
- pinned: false
8
- license: other
9
- ---
10
-
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
1
+ <div align="center">
2
+ <img width="1200" height="475" alt="GHBanner" src="https://github.com/user-attachments/assets/0aa67016-6eaf-458a-adb2-6e31a0763ed6" />
3
+ </div>
4
+
5
+ # Run and deploy your AI Studio app
6
+
7
+ This contains everything you need to run your app locally.
8
+
9
+ View your app in AI Studio: https://ai.studio/apps/drive/1lIFV2uT_kj0gG9PM3NWzaKh059u7b3nt
10
+
11
+ ## Run Locally
12
+
13
+ **Prerequisites:** Node.js
14
+
15
+
16
+ 1. Install dependencies:
17
+ `npm install`
18
+ 2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key
19
+ 3. Run the app:
20
+ `npm run dev`
index.html ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Lumina Quantum Ledger</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
9
+ <style>
10
+ body {
11
+ font-family: 'Inter', sans-serif;
12
+ background-color: #050505;
13
+ color: #e5e5e5;
14
+ }
15
+ .mono {
16
+ font-family: 'JetBrains Mono', monospace;
17
+ }
18
+ ::-webkit-scrollbar {
19
+ width: 6px;
20
+ }
21
+ ::-webkit-scrollbar-track {
22
+ background: #111;
23
+ }
24
+ ::-webkit-scrollbar-thumb {
25
+ background: #333;
26
+ border-radius: 10px;
27
+ }
28
+ ::-webkit-scrollbar-thumb:hover {
29
+ background: #444;
30
+ }
31
+
32
+ @keyframes matrix-scan {
33
+ 0% { top: -10%; }
34
+ 100% { top: 110%; }
35
+ }
36
+
37
+ .matrix-line {
38
+ position: absolute;
39
+ width: 100%;
40
+ height: 2px;
41
+ background: linear-gradient(90deg, transparent, #f43f5e, transparent);
42
+ opacity: 0.3;
43
+ animation: matrix-scan 3s linear infinite;
44
+ }
45
+
46
+ @media print {
47
+ @page {
48
+ margin: 0;
49
+ size: auto;
50
+ }
51
+ body {
52
+ background: white !important;
53
+ color: black !important;
54
+ margin: 0 !important;
55
+ padding: 0 !important;
56
+ }
57
+ #root {
58
+ padding: 0 !important;
59
+ }
60
+ .print-hidden {
61
+ display: none !important;
62
+ }
63
+ aside, header, nav, button {
64
+ display: none !important;
65
+ }
66
+ main {
67
+ margin-left: 0 !important;
68
+ padding: 0 !important;
69
+ width: 100% !important;
70
+ }
71
+ .print\:p-0 {
72
+ padding: 0 !important;
73
+ }
74
+ .print\:m-0 {
75
+ margin: 0 !important;
76
+ }
77
+ .print\:shadow-none {
78
+ box-shadow: none !important;
79
+ }
80
+ .print\:border-none {
81
+ border: none !important;
82
+ }
83
+ .print\:rounded-none {
84
+ border-radius: 0 !important;
85
+ }
86
+ * {
87
+ -webkit-print-color-adjust: exact !important;
88
+ print-color-adjust: exact !important;
89
+ }
90
+ }
91
+ </style>
92
+ <script type="importmap">
93
+ {
94
+ "imports": {
95
+ "sqlite3": "https://esm.sh/sqlite3@^5.1.7",
96
+ "lucide-react": "https://esm.sh/lucide-react@^0.563.0",
97
+ "react-dom/": "https://esm.sh/react-dom@^19.2.4/",
98
+ "react-router-dom": "https://esm.sh/react-router-dom@^7.13.0",
99
+ "@google/genai": "https://esm.sh/@google/genai@^1.41.0",
100
+ "@vitejs/plugin-react": "https://esm.sh/@vitejs/plugin-react@^5.1.4",
101
+ "react/": "https://esm.sh/react@^19.2.4/",
102
+ "react": "https://esm.sh/react@^19.2.4",
103
+ "path": "https://esm.sh/path@^0.12.7",
104
+ "vite": "https://esm.sh/vite@^7.3.1",
105
+ "recharts": "https://esm.sh/recharts@^3.7.0",
106
+ "bcryptjs": "https://esm.sh/bcryptjs@^3.0.3",
107
+ "url": "https://esm.sh/url@^0.11.4",
108
+ "express": "https://esm.sh/express@^5.2.1",
109
+ "cors": "https://esm.sh/cors@^2.8.6"
110
+ }
111
+ }
112
+ </script>
113
+ <link rel="stylesheet" href="/index.css">
114
+ </head>
115
+ <body>
116
+ <div id="root"></div>
117
+ <script type="module" src="/index.tsx"></script>
118
+ </body>
119
+ </html>
index.tsx ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+ import ReactDOM from 'react-dom/client';
4
+ import App from './App';
5
+
6
+ const rootElement = document.getElementById('root');
7
+ if (!rootElement) {
8
+ throw new Error("Could not find root element to mount to");
9
+ }
10
+
11
+ const root = ReactDOM.createRoot(rootElement);
12
+ root.render(
13
+ <React.StrictMode>
14
+ <App />
15
+ </React.StrictMode>
16
+ );
metadata.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Quantum Ledger Intelligence",
3
+ "description": "A high-performance financial dashboard integrating AI-driven forecasting, sustainability metrics, and corporate asset management using the Gemini API.",
4
+ "requestFramePermissions": [
5
+ "microphone"
6
+ ]
7
+ }
package.json ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "quantum-ledger-intelligence",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview",
10
+ "start": "node server.js"
11
+ },
12
+ "dependencies": {
13
+ "@google/genai": "^1.41.0",
14
+ "bcryptjs": "^3.0.3",
15
+ "cors": "^2.8.6",
16
+ "express": "^5.2.1",
17
+ "lucide-react": "^0.563.0",
18
+ "path": "^0.12.7",
19
+ "react": "^19.2.4",
20
+ "react-dom": "^19.2.4",
21
+ "react-router-dom": "^7.13.0",
22
+ "recharts": "^3.7.0",
23
+ "sqlite3": "^5.1.7"
24
+ },
25
+ "devDependencies": {
26
+ "@types/react": "^19.2.4",
27
+ "@types/react-dom": "^19.2.4",
28
+ "@vitejs/plugin-react": "^5.1.4",
29
+ "typescript": "^5.7.3",
30
+ "vite": "^7.3.1"
31
+ }
32
+ }
server.js ADDED
@@ -0,0 +1,225 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import express from 'express';
2
+ import cors from 'cors';
3
+ import { GoogleGenAI } from '@google/genai';
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import sqlite3 from 'sqlite3';
7
+ import bcrypt from 'bcryptjs';
8
+
9
+ const app = express();
10
+ // Hugging Face Spaces require port 7860
11
+ const port = process.env.PORT || 7860;
12
+
13
+ // --- Database Initialization ---
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = path.dirname(__filename);
16
+ const dbPath = path.join(__dirname, 'database.db');
17
+ const db = new sqlite3.Database(dbPath);
18
+
19
+ db.serialize(() => {
20
+ // Users table
21
+ db.run(`
22
+ CREATE TABLE IF NOT EXISTS users (
23
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
24
+ username TEXT UNIQUE NOT NULL,
25
+ password_hash TEXT NOT NULL,
26
+ role TEXT DEFAULT 'Standard Node',
27
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
28
+ )
29
+ `);
30
+
31
+ // Messages table for Neural Memory
32
+ db.run(`
33
+ CREATE TABLE IF NOT EXISTS messages (
34
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
35
+ user_id INTEGER,
36
+ role TEXT NOT NULL,
37
+ content TEXT NOT NULL,
38
+ timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
39
+ FOREIGN KEY(user_id) REFERENCES users(id)
40
+ )
41
+ `);
42
+ });
43
+
44
+ // --- Simulated In-Memory Session Storage ---
45
+ let activeSession = null;
46
+
47
+ // --- Mock Citi Financial Data ---
48
+ const CITI_ACCOUNTS = {
49
+ accountGroupDetails: [
50
+ {
51
+ accountGroup: "CHECKING",
52
+ checkingAccountsDetails: [
53
+ {
54
+ productName: "Corporate Mastery Checking",
55
+ accountNickname: "Main Ops Node",
56
+ accountDescription: "Corporate Mastery Checking - 9594",
57
+ balanceType: "ASSET",
58
+ displayAccountNumber: "XXXXXX9594",
59
+ accountId: "citi_acc_99201",
60
+ currencyCode: "USD",
61
+ accountStatus: "ACTIVE",
62
+ currentBalance: 1245000.50,
63
+ availableBalance: 1240000.00
64
+ }
65
+ ],
66
+ totalCurrentBalance: { localCurrencyCode: "USD", localCurrencyBalanceAmount: 1245000.50 },
67
+ totalAvailableBalance: { localCurrencyCode: "USD", localCurrencyBalanceAmount: 1240000.00 }
68
+ }
69
+ ],
70
+ customer: {
71
+ customerId: "citi_cust_884102"
72
+ }
73
+ };
74
+
75
+ const CITI_TRANSACTIONS = {
76
+ "citi_acc_99201": {
77
+ checkingAccountTransactions: [
78
+ {
79
+ accountId: "citi_acc_99201",
80
+ currencyCode: "USD",
81
+ transactionAmount: -25000.00,
82
+ transactionDate: "2024-03-31",
83
+ transactionDescription: "NEURAL_NETWORK_COMPUTE_Q1_ALLOCATION",
84
+ transactionId: "TXN_C_884102",
85
+ transactionStatus: "POSTED",
86
+ transactionType: "PAYMENT",
87
+ displayAccountNumber: "XXXXXX9594"
88
+ }
89
+ ]
90
+ }
91
+ };
92
+
93
+ app.use(cors());
94
+ app.use(express.json());
95
+ app.use(express.urlencoded({ extended: true }));
96
+
97
+ // --- Authentication Endpoints ---
98
+
99
+ app.get('/api/auth/me', (req, res) => {
100
+ if (activeSession) {
101
+ res.json({ isAuthenticated: true, user: activeSession });
102
+ } else {
103
+ res.status(401).json({ isAuthenticated: false, user: null });
104
+ }
105
+ });
106
+
107
+ app.post('/api/auth/register', async (req, res) => {
108
+ const { username, password } = req.body;
109
+ if (!username || !password) return res.status(400).json({ error: 'Identity credentials missing.' });
110
+
111
+ try {
112
+ const hash = await bcrypt.hash(password, 10);
113
+ db.run(
114
+ 'INSERT INTO users (username, password_hash, role) VALUES (?, ?, ?)',
115
+ [username, hash, 'Root Admin'],
116
+ function(err) {
117
+ if (err) {
118
+ if (err.message.includes('UNIQUE')) {
119
+ return res.status(409).json({ error: 'Identity node already registered.' });
120
+ }
121
+ return res.status(500).json({ error: 'Registry write failure.' });
122
+ }
123
+ res.status(201).json({ success: true, userId: this.lastID });
124
+ }
125
+ );
126
+ } catch (error) {
127
+ res.status(500).json({ error: 'Encryption engine failure.' });
128
+ }
129
+ });
130
+
131
+ app.post('/api/auth/login', (req, res) => {
132
+ const { username, password } = req.body;
133
+
134
+ db.get('SELECT * FROM users WHERE username = ?', [username], async (err, user) => {
135
+ if (err || !user) {
136
+ return res.status(401).json({ success: false, message: 'Identity node rejected credentials.' });
137
+ }
138
+
139
+ const match = await bcrypt.compare(password, user.password_hash);
140
+ if (match) {
141
+ activeSession = {
142
+ id: `USR-${user.id}`,
143
+ dbId: user.id, // Internal database ID for relations
144
+ name: user.username,
145
+ role: user.role,
146
+ lastLogin: new Date().toISOString()
147
+ };
148
+ res.json({ success: true, user: activeSession });
149
+ } else {
150
+ res.status(401).json({ success: false, message: 'Identity node rejected credentials.' });
151
+ }
152
+ });
153
+ });
154
+
155
+ app.post('/api/auth/logout', (req, res) => {
156
+ activeSession = null;
157
+ res.json({ success: true });
158
+ });
159
+
160
+ // --- Chat History Endpoints ---
161
+
162
+ app.get('/api/chat/history', (req, res) => {
163
+ if (!activeSession) return res.status(401).json({ error: 'Session parity lost.' });
164
+
165
+ db.all(
166
+ 'SELECT * FROM messages WHERE user_id = ? ORDER BY timestamp ASC',
167
+ [activeSession.dbId],
168
+ (err, rows) => {
169
+ if (err) return res.status(500).json({ error: 'Neural memory retrieval failure.' });
170
+ res.json(rows);
171
+ }
172
+ );
173
+ });
174
+
175
+ // --- Gemini Proxy ---
176
+ app.post('/api/gemini/generate', async (req, res) => {
177
+ try {
178
+ const apiKey = process.env.API_KEY;
179
+ if (!apiKey) return res.status(500).json({ error: 'API Key missing' });
180
+
181
+ const { model, contents, config, saveToMemory } = req.body;
182
+ const ai = new GoogleGenAI({ apiKey });
183
+ const response = await ai.models.generateContent({
184
+ model: model || 'gemini-3-flash-preview',
185
+ contents,
186
+ config: config || {}
187
+ });
188
+
189
+ const aiText = response.text;
190
+
191
+ // Persist to memory if requested and authenticated
192
+ if (activeSession && saveToMemory && contents[0]?.parts[0]?.text) {
193
+ const userPrompt = contents[0].parts[0].text;
194
+
195
+ // Save user prompt
196
+ db.run('INSERT INTO messages (user_id, role, content) VALUES (?, ?, ?)', [activeSession.dbId, 'user', userPrompt]);
197
+ // Save AI response
198
+ db.run('INSERT INTO messages (user_id, role, content) VALUES (?, ?, ?)', [activeSession.dbId, 'assistant', aiText]);
199
+ }
200
+
201
+ res.json({ text: aiText, candidates: response.candidates });
202
+ } catch (error) {
203
+ res.status(500).json({ error: 'AI communication failure', details: error.message });
204
+ }
205
+ });
206
+
207
+ const CITI_BASE = '/api/accounts/account-transactions/partner/v1';
208
+ app.get(`${CITI_BASE}/accounts/details`, (req, res) => res.json(CITI_ACCOUNTS));
209
+ app.get(`${CITI_BASE}/accounts/:accountId/transactions`, (req, res) => {
210
+ const { accountId } = req.params;
211
+ res.json(CITI_TRANSACTIONS[accountId] || { checkingAccountTransactions: [] });
212
+ });
213
+
214
+ // Serve frontend files from the dist directory
215
+ app.use(express.static(path.join(__dirname, 'dist')));
216
+
217
+ // SPA Fallback: Redirect all non-API requests to index.html
218
+ app.get('*', (req, res) => {
219
+ if (req.path.startsWith('/api')) {
220
+ return res.status(404).json({ error: 'API endpoint not found' });
221
+ }
222
+ res.sendFile(path.join(__dirname, 'dist', 'index.html'));
223
+ });
224
+
225
+ app.listen(port, '0.0.0.0', () => console.log(`Financial Node Proxy Operational on port ${port}`));
tsconfig.json ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "experimentalDecorators": true,
5
+ "useDefineForClassFields": false,
6
+ "module": "ESNext",
7
+ "lib": [
8
+ "ES2022",
9
+ "DOM",
10
+ "DOM.Iterable"
11
+ ],
12
+ "skipLibCheck": true,
13
+ "types": [
14
+ "node"
15
+ ],
16
+ "moduleResolution": "bundler",
17
+ "isolatedModules": true,
18
+ "moduleDetection": "force",
19
+ "allowJs": true,
20
+ "jsx": "react-jsx",
21
+ "paths": {
22
+ "@/*": [
23
+ "./*"
24
+ ]
25
+ },
26
+ "allowImportingTsExtensions": true,
27
+ "noEmit": true
28
+ }
29
+ }
types.ts ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ export interface Account {
3
+ id: string;
4
+ name: string;
5
+ type: 'quantum_checking' | 'elite_savings' | 'high_yield_vault';
6
+ balance: number;
7
+ currency: string;
8
+ institution: string;
9
+ }
10
+
11
+ export interface Transaction {
12
+ id: string;
13
+ amount: number;
14
+ currency: string;
15
+ date: string;
16
+ description: string;
17
+ category: string;
18
+ status: 'POSTED' | 'PENDING';
19
+ type: string;
20
+ }
21
+
22
+ export interface InternalAccount {
23
+ id: string;
24
+ productName: string;
25
+ displayAccountNumber: string;
26
+ currency: string;
27
+ status: 'ACTIVE' | 'INACTIVE' | 'CLOSED';
28
+ currentBalance: number;
29
+ availableBalance: number;
30
+ institutionName: string;
31
+ connectionId: string;
32
+ }
33
+
34
+ export interface Counterparty {
35
+ id: string;
36
+ name: string;
37
+ email: string;
38
+ status: 'ACTIVE' | 'PENDING' | 'INACTIVE';
39
+ createdAt: string;
40
+ metadata?: Record<string, any>;
41
+ accounts?: Array<{
42
+ id: string;
43
+ accountType: string;
44
+ accountNumber: string;
45
+ }>;
46
+ }
47
+
48
+ export interface Connection {
49
+ id: string;
50
+ vendorCustomerId: string;
51
+ entity: string;
52
+ status: 'CONNECTED' | 'DISCONNECTED' | 'ERROR';
53
+ lastSyncedAt: string;
54
+ }
55
+
56
+ export interface Document {
57
+ id: string;
58
+ documentableId: string;
59
+ documentableType: string;
60
+ documentType: string;
61
+ fileName: string;
62
+ size: number;
63
+ createdAt: string;
64
+ format: string;
65
+ }
66
+
67
+ export interface SimulationResult {
68
+ outcomeNarrative: string;
69
+ projectedValue: number;
70
+ confidenceScore: number;
71
+ status: string;
72
+ simulationId: string;
73
+ keyRisks?: string[];
74
+ }
75
+
76
+ export interface Payee {
77
+ payeeId: string;
78
+ displayName: string;
79
+ merchantName: string;
80
+ status: string;
81
+ address?: {
82
+ line1: string;
83
+ city: string;
84
+ region: string;
85
+ postalCode: string;
86
+ };
87
+ }
88
+
89
+ export interface Payment {
90
+ paymentId: string;
91
+ amount: number;
92
+ status: string;
93
+ dueDate: string;
94
+ toPayeeId: string;
95
+ fromAccountId: string;
96
+ }
97
+
98
+ export interface CustomerProfileResponse {
99
+ firstName: string;
100
+ lastName: string;
101
+ middleName: string;
102
+ fullName: string;
103
+ companyName: string;
104
+ emails: Array<{
105
+ emailAddress: string;
106
+ preferenceType: string;
107
+ }>;
108
+ addressList: Array<{
109
+ addressLine1: string;
110
+ city: string;
111
+ countryCode: string;
112
+ postalCode: string;
113
+ addressType: string;
114
+ }>;
115
+ phones: Array<{
116
+ phoneType: string;
117
+ fullPhoneNumber: string;
118
+ preferenceType: string;
119
+ }>;
120
+ }
121
+
122
+ export interface ClientRegisterResponse {
123
+ client_id: string;
124
+ client_secret: string;
125
+ client_name: string;
126
+ appId?: string;
127
+ redirect_uris: string[];
128
+ scope: string[];
129
+ token_endpoint_auth_method?: string;
130
+ }
131
+
132
+ export interface VirtualAccount {
133
+ id: string;
134
+ name: string;
135
+ description: string | null;
136
+ status: 'ACTIVE' | 'PENDING' | 'CLOSED';
137
+ internal_account_id: string;
138
+ counterparty_id: string | null;
139
+ created_at: string;
140
+ }
141
+
142
+ export interface LineItem {
143
+ id: string;
144
+ amount: number;
145
+ currency: string;
146
+ description: string;
147
+ ledger_account_id: string;
148
+ createdAt: string;
149
+ }
150
+
151
+ export interface LineItemUpdateRequest {
152
+ description?: string;
153
+ metadata?: Record<string, any>;
154
+ }
vite.config.ts ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import { defineConfig } from 'vite';
3
+ import react from '@vitejs/plugin-react';
4
+
5
+ export default defineConfig({
6
+ plugins: [react()],
7
+ server: {
8
+ proxy: {
9
+ '/api': {
10
+ target: 'http://localhost:3001',
11
+ changeOrigin: true,
12
+ secure: false,
13
+ },
14
+ '/openapi': {
15
+ target: 'http://localhost:3001',
16
+ changeOrigin: true,
17
+ secure: false,
18
+ },
19
+ },
20
+ },
21
+ });