aibanking.dev / server.js
admin08077's picture
Update server.js
465920d verified
raw
history blame
7.36 kB
import express from 'express';
import cors from 'cors';
import { GoogleGenAI } from '@google/genai';
import path from 'path';
import { fileURLToPath } from 'url';
import sqlite3 from 'sqlite3';
import bcrypt from 'bcryptjs';
const app = express();
// Hugging Face Spaces require port 7860
const port = process.env.PORT || 7860;
// --- Database Initialization ---
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const dbPath = path.join(__dirname, 'database.db');
const db = new sqlite3.Database(dbPath);
db.serialize(() => {
// Users table
db.run(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
role TEXT DEFAULT 'Standard Node',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`);
// Messages table for Neural Memory
db.run(`
CREATE TABLE IF NOT EXISTS messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER,
role TEXT NOT NULL,
content TEXT NOT NULL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(user_id) REFERENCES users(id)
)
`);
});
// --- Simulated In-Memory Session Storage ---
let activeSession = null;
// --- Mock Citi Financial Data ---
const CITI_ACCOUNTS = {
accountGroupDetails: [
{
accountGroup: "CHECKING",
checkingAccountsDetails: [
{
productName: "Corporate Mastery Checking",
accountNickname: "Main Ops Node",
accountDescription: "Corporate Mastery Checking - 9594",
balanceType: "ASSET",
displayAccountNumber: "XXXXXX9594",
accountId: "citi_acc_99201",
currencyCode: "USD",
accountStatus: "ACTIVE",
currentBalance: 1245000.50,
availableBalance: 1240000.00
}
],
totalCurrentBalance: { localCurrencyCode: "USD", localCurrencyBalanceAmount: 1245000.50 },
totalAvailableBalance: { localCurrencyCode: "USD", localCurrencyBalanceAmount: 1240000.00 }
}
],
customer: {
customerId: "citi_cust_884102"
}
};
const CITI_TRANSACTIONS = {
"citi_acc_99201": {
checkingAccountTransactions: [
{
accountId: "citi_acc_99201",
currencyCode: "USD",
transactionAmount: -25000.00,
transactionDate: "2024-03-31",
transactionDescription: "NEURAL_NETWORK_COMPUTE_Q1_ALLOCATION",
transactionId: "TXN_C_884102",
transactionStatus: "POSTED",
transactionType: "PAYMENT",
displayAccountNumber: "XXXXXX9594"
}
]
}
};
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// --- Authentication Endpoints ---
app.get('/api/auth/me', (req, res) => {
if (activeSession) {
res.json({ isAuthenticated: true, user: activeSession });
} else {
res.status(401).json({ isAuthenticated: false, user: null });
}
});
app.post('/api/auth/register', async (req, res) => {
const { username, password } = req.body;
if (!username || !password) return res.status(400).json({ error: 'Identity credentials missing.' });
try {
const hash = await bcrypt.hash(password, 10);
db.run(
'INSERT INTO users (username, password_hash, role) VALUES (?, ?, ?)',
[username, hash, 'Root Admin'],
function(err) {
if (err) {
if (err.message.includes('UNIQUE')) {
return res.status(409).json({ error: 'Identity node already registered.' });
}
return res.status(500).json({ error: 'Registry write failure.' });
}
res.status(201).json({ success: true, userId: this.lastID });
}
);
} catch (error) {
res.status(500).json({ error: 'Encryption engine failure.' });
}
});
app.post('/api/auth/login', (req, res) => {
const { username, password } = req.body;
db.get('SELECT * FROM users WHERE username = ?', [username], async (err, user) => {
if (err || !user) {
return res.status(401).json({ success: false, message: 'Identity node rejected credentials.' });
}
const match = await bcrypt.compare(password, user.password_hash);
if (match) {
activeSession = {
id: `USR-${user.id}`,
dbId: user.id, // Internal database ID for relations
name: user.username,
role: user.role,
lastLogin: new Date().toISOString()
};
res.json({ success: true, user: activeSession });
} else {
res.status(401).json({ success: false, message: 'Identity node rejected credentials.' });
}
});
});
app.post('/api/auth/logout', (req, res) => {
activeSession = null;
res.json({ success: true });
});
// --- Chat History Endpoints ---
app.get('/api/chat/history', (req, res) => {
if (!activeSession) return res.status(401).json({ error: 'Session parity lost.' });
db.all(
'SELECT * FROM messages WHERE user_id = ? ORDER BY timestamp ASC',
[activeSession.dbId],
(err, rows) => {
if (err) return res.status(500).json({ error: 'Neural memory retrieval failure.' });
res.json(rows);
}
);
});
// --- Gemini Proxy ---
app.post('/api/gemini/generate', async (req, res) => {
try {
const apiKey = process.env.API_KEY;
if (!apiKey) return res.status(500).json({ error: 'API Key missing' });
const { model, contents, config, saveToMemory } = req.body;
const ai = new GoogleGenAI({ apiKey });
const response = await ai.models.generateContent({
model: model || 'gemini-3-flash-preview',
contents,
config: config || {}
});
const aiText = response.text;
// Persist to memory if requested and authenticated
if (activeSession && saveToMemory && contents[0]?.parts[0]?.text) {
const userPrompt = contents[0].parts[0].text;
// Save user prompt
db.run('INSERT INTO messages (user_id, role, content) VALUES (?, ?, ?)', [activeSession.dbId, 'user', userPrompt]);
// Save AI response
db.run('INSERT INTO messages (user_id, role, content) VALUES (?, ?, ?)', [activeSession.dbId, 'assistant', aiText]);
}
res.json({ text: aiText, candidates: response.candidates });
} catch (error) {
res.status(500).json({ error: 'AI communication failure', details: error.message });
}
});
const CITI_BASE = '/api/accounts/account-transactions/partner/v1';
app.get(`${CITI_BASE}/accounts/details`, (req, res) => res.json(CITI_ACCOUNTS));
app.get(`${CITI_BASE}/accounts/:accountId/transactions`, (req, res) => {
const { accountId } = req.params;
res.json(CITI_TRANSACTIONS[accountId] || { checkingAccountTransactions: [] });
});
// Serve frontend files from the dist directory
app.use(express.static(path.join(__dirname, 'dist')));
// SPA Fallback: Catch any request that didn't match an API route or a static file.
// In Express 5, using a middleware function without a path is the safest way
// to implement a catch-all fallback for Single Page Applications.
app.use((req, res) => {
// Safeguard: Do not serve index.html for missing API routes
if (req.path.startsWith('/api')) {
return res.status(404).json({ error: 'API endpoint not found' });
}
res.sendFile(path.join(__dirname, 'dist', 'index.html'));
});
app.listen(port, '0.0.0.0', () => console.log(`Financial Node Proxy Operational on port ${port}`));