refactor: consolidate blockchain explorer into single app and update backup ignore patterns

- Remove standalone explorer-web app (README, HTML, package files)
- Add /web endpoint to blockchain-explorer for web interface access
- Update .gitignore to exclude application backup archives (*.tar.gz, *.zip)
- Add backup documentation files to .gitignore (BACKUP_INDEX.md, README.md)
- Consolidate explorer functionality into main blockchain-explorer application
This commit is contained in:
oib
2026-03-06 18:14:49 +01:00
parent dc1561d457
commit bb5363bebc
295 changed files with 35501 additions and 3734 deletions

View File

@@ -1,363 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AITBC Exchange Admin - Live Treasury Dashboard</title>
<link rel="stylesheet" href="/assets/css/aitbc.css">
<script src="/assets/js/axios.min.js"></script>
<script src="/assets/js/lucide.js"></script>
<style>
.stat-card {
background: white;
border-radius: 12px;
padding: 24px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
transition: transform 0.2s;
}
.stat-card:hover {
transform: translateY(-2px);
}
.stat-value {
font-size: 2.5rem;
font-weight: bold;
color: #f97316;
}
.stat-label {
color: #6b7280;
margin-top: 8px;
}
.wallet-balance {
background: linear-gradient(135deg, #f97316 0%, #ea580c 100%);
color: white;
padding: 30px;
border-radius: 16px;
margin-bottom: 30px;
}
.wallet-address {
font-family: monospace;
background: rgba(255, 255, 255, 0.2);
padding: 8px 12px;
border-radius: 6px;
display: inline-block;
margin-top: 10px;
}
.payment-list {
max-height: 400px;
overflow-y: auto;
}
.payment-item {
border-left: 4px solid #e5e7eb;
padding: 12px;
margin-bottom: 8px;
background: #f9fafb;
border-radius: 0 8px 8px 0;
}
.payment-item.pending {
border-left-color: #f59e0b;
}
.payment-item.confirmed {
border-left-color: #10b981;
}
.payment-item.expired {
border-left-color: #ef4444;
}
.refresh-btn {
position: fixed;
bottom: 30px;
right: 30px;
width: 60px;
height: 60px;
background: #f97316;
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 12px rgba(249, 115, 22, 0.4);
cursor: pointer;
transition: all 0.3s;
}
.refresh-btn:hover {
transform: scale(1.1);
}
.refresh-btn.spinning {
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
</style>
</head>
<body class="bg-gray-50">
<header class="bg-white shadow-sm border-b">
<div class="bg-green-100 text-green-800 text-center py-2 text-sm">
✅ LIVE MODE - Connected to AITBC Blockchain with Real Treasury Balance
</div>
<div class="container mx-auto px-4 py-4">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-4">
<i data-lucide="trending-up" class="w-8 h-8 text-orange-600"></i>
<h1 class="text-2xl font-bold">Exchange Admin Dashboard</h1>
</div>
<div class="flex items-center space-x-4">
<span class="text-sm text-gray-600">Bank Director Portal</span>
<button onclick="logout()" class="text-gray-500 hover:text-gray-700">
<i data-lucide="log-out" class="w-5 h-5"></i>
</button>
</div>
</div>
</div>
</header>
<main class="container mx-auto px-4 py-8">
<!-- Market Statistics -->
<section class="bg-white rounded-lg shadow p-6 mb-8">
<h2 class="text-xl font-bold mb-4 flex items-center">
<i data-lucide="bar-chart" class="w-5 h-5 mr-2 text-blue-600"></i>
Market Statistics
</h2>
<div class="grid grid-cols-1 md:grid-cols-4 gap-6">
<div>
<div class="text-2xl font-bold text-gray-900" id="totalAitbcSold">0</div>
<div class="text-sm text-gray-600 mt-1">Total AITBC Sold</div>
</div>
<div>
<div class="text-2xl font-bold text-gray-900" id="totalBtcReceived">0 BTC</div>
<div class="text-sm text-gray-600 mt-1">Total BTC Received</div>
</div>
<div>
<div class="text-2xl font-bold text-gray-900" id="pendingPayments">0</div>
<div class="text-sm text-gray-600 mt-1">Pending Payments</div>
</div>
<div>
<div class="text-2xl font-bold text-green-600" id="marketStatus">Market is open</div>
<div class="text-sm text-gray-600 mt-1">Market Status</div>
</div>
</div>
</section>
<!-- Bitcoin Wallet Balance -->
<section class="wallet-balance">
<h2 class="text-3xl font-bold mb-4">Bitcoin Wallet</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<div class="text-sm opacity-90">Current Balance</div>
<div class="text-4xl font-bold" id="btcBalance">0.00000000 BTC</div>
</div>
<div>
<div class="text-sm opacity-90 mb-2">Wallet Address</div>
<div class="wallet-address text-sm" id="walletAddress">tb1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh</div>
</div>
</div>
</section>
<!-- Statistics Grid -->
<section class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
<div class="stat-card">
<i data-lucide="coins" class="w-8 h-8 text-orange-600 mb-2"></i>
<div class="stat-value" id="totalAitbcSold">0</div>
<div class="stat-label">Total AITBC Sold</div>
</div>
<div class="stat-card">
<i data-lucide="bitcoin" class="w-8 h-8 text-orange-600 mb-2"></i>
<div class="stat-value" id="totalBtcReceived">0 BTC</div>
<div class="stat-label">Total BTC Received</div>
</div>
<div class="stat-card">
<i data-lucide="users" class="w-8 h-8 text-orange-600 mb-2"></i>
<div class="stat-value" id="totalUsers">0</div>
<div class="stat-label">Total Users</div>
</div>
<div class="stat-card">
<i data-lucide="clock" class="w-8 h-8 text-orange-600 mb-2"></i>
<div class="stat-value" id="pendingPayments">0</div>
<div class="stat-label">Pending Payments</div>
</div>
</section>
<!-- Available AITBC -->
<section class="bg-white rounded-lg shadow p-6 mb-8">
<h2 class="text-xl font-bold mb-4 flex items-center">
<i data-lucide="package" class="w-5 h-5 mr-2 text-orange-600"></i>
Available AITBC for Sale
</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<div class="text-3xl font-bold text-green-600" id="availableAitbc">Loading...</div>
<div class="text-sm text-gray-600 mt-1">AITBC in Treasury (available for sale)</div>
</div>
<div>
<div class="text-2xl font-semibold text-gray-900" id="estimatedValue">100 BTC</div>
<div class="text-sm text-gray-600 mt-1">Estimated value at current rate</div>
</div>
</div>
</section>
<!-- Recent Payments -->
<section class="bg-white rounded-lg shadow p-6">
<h2 class="text-xl font-bold mb-4 flex items-center">
<i data-lucide="activity" class="w-5 h-5 mr-2 text-orange-600"></i>
Recent Payments
</h2>
<div class="payment-list" id="paymentsList">
<div class="text-gray-500 text-center py-8">Loading payments...</div>
</div>
</section>
</main>
<!-- Refresh Button -->
<div class="refresh-btn" onclick="refreshData()" id="refreshBtn">
<i data-lucide="refresh-cw" class="w-6 h-6"></i>
</div>
<script>
const API_BASE = window.location.origin + '/api';
let refreshInterval;
// Initialize
document.addEventListener('DOMContentLoaded', () => {
lucide.createIcons();
refreshData();
// Auto-refresh every 30 seconds
refreshInterval = setInterval(refreshData, 30000);
});
// Refresh all data
async function refreshData() {
const btn = document.getElementById('refreshBtn');
btn.classList.add('spinning');
try {
await Promise.all([
loadMarketStats(),
loadPayments(),
loadWalletBalance()
]);
} catch (error) {
console.error('Error refreshing data:', error);
} finally {
setTimeout(() => btn.classList.remove('spinning'), 500);
}
}
// Load market statistics
async function loadMarketStats() {
try {
// Get treasury balance instead of hardcoded amount
const treasuryResponse = await axios.get(`${API_BASE}/treasury-balance`);
const treasury = treasuryResponse.data;
const availableAitbc = parseInt(treasury.available_for_sale) / 1000000; // Convert from smallest units
const stats = { price: 0.00001 }; // Default price
// Update elements with defensive checks
const totalSoldEl = document.getElementById('totalAitbcSold');
if (totalSoldEl) totalSoldEl.textContent = (stats.daily_volume || 0).toLocaleString();
const totalBtcEl = document.getElementById('totalBtcReceived');
if (totalBtcEl) totalBtcEl.textContent = (stats.daily_volume_btc || 0).toFixed(8) + ' BTC';
const pendingEl = document.getElementById('pendingPayments');
if (pendingEl) pendingEl.textContent = stats.pending_payments || 0;
// Update available AITBC from treasury
document.getElementById('availableAitbc').textContent =
availableAitbc.toLocaleString();
document.getElementById('estimatedValue').textContent =
(availableAitbc * (stats.price || 0.00001)).toFixed(2) + ' BTC';
// Add source indicator
const supplyElement = document.getElementById('availableAitbc');
if (treasury.source === 'genesis') {
supplyElement.innerHTML += ' <span class="text-xs text-orange-600">(Genesis)</span>';
}
// Update market status
const marketStatus = stats.market_status;
const marketStatusEl = document.getElementById('marketStatus');
if (marketStatusEl) {
if (marketStatus === 'open') {
marketStatusEl.textContent = 'Market is open';
marketStatusEl.classList.remove('text-red-600');
marketStatusEl.classList.add('text-green-600');
} else if (marketStatus === 'closed') {
marketStatusEl.textContent = 'Market is closed';
marketStatusEl.classList.remove('text-green-600');
marketStatusEl.classList.add('text-red-600');
} else {
marketStatusEl.textContent = 'Market status unknown';
marketStatusEl.classList.remove('text-green-600', 'text-red-600');
}
}
} catch (error) {
console.error('Error loading market stats:', error);
}
}
// Load recent payments
async function loadPayments() {
try {
// Since there's no endpoint to list all payments, we'll show a message
document.getElementById('paymentsList').innerHTML =
'<div class="text-gray-500 text-center py-8">Payment history requires database implementation</div>';
} catch (error) {
console.error('Error loading payments:', error);
}
}
// Load wallet balance from API
async function loadWalletBalance() {
try {
const response = await axios.get(`${API_BASE}/exchange/wallet/info`);
const wallet = response.data;
document.getElementById('btcBalance').textContent =
wallet.balance.toFixed(8) + ' BTC';
document.getElementById('walletAddress').textContent =
wallet.address;
// Show wallet type
const balanceElement = document.getElementById('btcBalance');
if (wallet.testnet) {
balanceElement.innerHTML += ' <span style="font-size: 0.5em; opacity: 0.7;">(TESTNET)</span>';
}
// Update wallet info section
updateWalletInfo(wallet);
} catch (error) {
console.error('Error loading wallet balance:', error);
// Fallback to demo data
document.getElementById('btcBalance').textContent = '0.00000000 BTC';
document.getElementById('walletAddress').textContent = 'Wallet API unavailable';
}
}
// Update wallet information display
function updateWalletInfo(wallet) {
// Create or update wallet info display
let walletInfo = document.getElementById('walletInfoDisplay');
if (!walletInfo) {
walletInfo = document.createElement('div');
walletInfo.id = 'walletInfoDisplay';
walletInfo.className = 'mt-4 p-4 bg-gray-100 rounded-lg';
document.querySelector('.wallet-balance').appendChild(walletInfo);
}
walletInfo.innerHTML = `
<div class="text-sm">
<div class="mb-2"><strong>Wallet Type:</strong> ${wallet.wallet_type}</div>
<div class="mb-2"><strong>Network:</strong> ${wallet.testnet ? 'Testnet' : 'Mainnet'}</div>
<div><strong>Recent Transactions:</strong> ${wallet.transactions.length}</div>
</div>
`;
}
// Logout
function logout() {
window.location.href = '/Exchange/';
}
</script>
</body>
</html>

View File

@@ -1,175 +0,0 @@
#!/usr/bin/env python3
"""
Bitcoin Wallet Integration for AITBC Trade Exchange
"""
import os
import json
import hashlib
import hmac
import time
from typing import Dict, Optional, Tuple
from dataclasses import dataclass
import requests
@dataclass
class BitcoinWallet:
"""Bitcoin wallet configuration"""
address: str
private_key: Optional[str] = None
testnet: bool = True
class BitcoinProcessor:
"""Bitcoin payment processor"""
def __init__(self, config: Dict):
self.config = config
self.testnet = config.get('testnet', True)
self.api_key = config.get('api_key')
self.webhook_secret = config.get('webhook_secret')
def generate_payment_address(self, user_id: str, amount_btc: float) -> str:
"""Generate a unique payment address for each transaction"""
# In production, use HD wallet to generate unique addresses
# For demo, we'll use a fixed address with payment tracking
# Create payment hash
payment_data = f"{user_id}:{amount_btc}:{int(time.time())}"
hash_bytes = hashlib.sha256(payment_data.encode()).hexdigest()
# For demo, return the main wallet address
# In production, generate unique address from HD wallet
return self.config['main_address']
def check_payment(self, address: str, amount_btc: float) -> Tuple[bool, float]:
"""Check if payment has been received"""
# In production, integrate with blockchain API
# For demo, simulate payment check
# Mock API call to check blockchain
if self.testnet:
# Testnet blockchain API
api_url = f"https://blockstream.info/testnet/api/address/{address}"
else:
# Mainnet blockchain API
api_url = f"https://blockstream.info/api/address/{address}"
try:
response = requests.get(api_url, timeout=5)
if response.status_code == 200:
data = response.json()
# Check recent transactions
# In production, implement proper transaction verification
return False, 0.0
except Exception as e:
print(f"Error checking payment: {e}")
return False, 0.0
def verify_webhook(self, payload: str, signature: str) -> bool:
"""Verify webhook signature from payment processor"""
if not self.webhook_secret:
return True # Skip verification if no secret
expected_signature = hmac.new(
self.webhook_secret.encode(),
payload.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected_signature, signature)
class WalletManager:
"""Manages Bitcoin wallet operations"""
def __init__(self):
self.config = self.load_config()
self.processor = BitcoinProcessor(self.config)
def load_config(self) -> Dict:
"""Load wallet configuration"""
return {
'testnet': os.getenv('BITCOIN_TESTNET', 'true').lower() == 'true',
'main_address': os.getenv('BITCOIN_ADDRESS', 'tb1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh'),
'private_key': os.getenv('BITCOIN_PRIVATE_KEY'),
'api_key': os.getenv('BLOCKCHAIN_API_KEY'),
'webhook_secret': os.getenv('WEBHOOK_SECRET'),
'min_confirmations': int(os.getenv('MIN_CONFIRMATIONS', '1')),
'exchange_rate': float(os.getenv('BTC_TO_AITBC_RATE', '100000')) # 1 BTC = 100,000 AITBC
}
def create_payment_request(self, user_id: str, aitbc_amount: float) -> Dict:
"""Create a new payment request"""
btc_amount = aitbc_amount / self.config['exchange_rate']
payment_request = {
'user_id': user_id,
'aitbc_amount': aitbc_amount,
'btc_amount': btc_amount,
'payment_address': self.processor.generate_payment_address(user_id, btc_amount),
'created_at': int(time.time()),
'status': 'pending',
'expires_at': int(time.time()) + 3600 # 1 hour expiry
}
# Save payment request
self.save_payment_request(payment_request)
return payment_request
def save_payment_request(self, request: Dict):
"""Save payment request to storage"""
payments_file = 'payments.json'
payments = []
if os.path.exists(payments_file):
with open(payments_file, 'r') as f:
payments = json.load(f)
payments.append(request)
with open(payments_file, 'w') as f:
json.dump(payments, f, indent=2)
def get_payment_status(self, payment_id: str) -> Optional[Dict]:
"""Get payment status"""
payments_file = 'payments.json'
if not os.path.exists(payments_file):
return None
with open(payments_file, 'r') as f:
payments = json.load(f)
for payment in payments:
if payment.get('payment_id') == payment_id:
return payment
return None
def update_payment_status(self, payment_id: str, status: str, tx_hash: str = None):
"""Update payment status"""
payments_file = 'payments.json'
if not os.path.exists(payments_file):
return False
with open(payments_file, 'r') as f:
payments = json.load(f)
for payment in payments:
if payment.get('payment_id') == payment_id:
payment['status'] = status
payment['updated_at'] = int(time.time())
if tx_hash:
payment['tx_hash'] = tx_hash
with open(payments_file, 'w') as f:
json.dump(payments, f, indent=2)
return True
return False
# Global wallet manager
wallet_manager = WalletManager()

View File

@@ -1,67 +0,0 @@
#!/usr/bin/env python3
"""
Build script for AITBC Trade Exchange
Combines CSS and HTML for production deployment
"""
import os
import shutil
def build_html():
"""Build production HTML with embedded CSS"""
print("🔨 Building AITBC Exchange for production...")
# Read CSS file
css_path = "styles.css"
html_path = "index.html"
output_path = "index.html"
# Backup original
if os.path.exists(html_path):
shutil.copy(html_path, "index.dev.html")
print("✓ Backed up original index.html to index.dev.html")
# Read the template
with open("index.template.html", "r") as f:
template = f.read()
# Read CSS
with open(css_path, "r") as f:
css_content = f.read()
# Replace placeholder with CSS
html_content = template.replace("<!-- CSS_PLACEHOLDER -->", f"<style>\n{css_content}\n </style>")
# Write production HTML
with open(output_path, "w") as f:
f.write(html_content)
print(f"✓ Built production HTML: {output_path}")
print("✓ CSS is now embedded in HTML")
def create_template():
"""Create a template file for future use"""
template = """<!DOCTYPE html>
<html lang="en" class="h-full">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AITBC Trade Exchange - Buy & Sell AITBC</title>
<script src="https://unpkg.com/lucide@latest"></script>
<!-- CSS_PLACEHOLDER -->
</head>
<body>
<!-- Body content will be added here -->
</body>
</html>"""
with open("index.template.html", "w") as f:
f.write(template)
print("✓ Created template file: index.template.html")
if __name__ == "__main__":
if not os.path.exists("index.template.html"):
create_template()
build_html()

View File

@@ -1,49 +0,0 @@
#!/usr/bin/env python3
"""
Database configuration for the AITBC Trade Exchange
"""
import os
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, Session
from sqlalchemy.pool import StaticPool
from models import Base
# Database configuration
DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///./exchange.db")
# Create engine
if DATABASE_URL.startswith("sqlite"):
engine = create_engine(
DATABASE_URL,
connect_args={"check_same_thread": False},
poolclass=StaticPool,
echo=False # Set to True for SQL logging
)
else:
engine = create_engine(DATABASE_URL, echo=False)
# Create session factory
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# Create tables
def init_db():
"""Initialize database tables"""
Base.metadata.create_all(bind=engine)
def get_db() -> Session:
"""Get database session"""
db = SessionLocal()
try:
yield db
finally:
db.close()
# Dependency for FastAPI
def get_db_session():
"""Get database session for FastAPI dependency"""
db = SessionLocal()
try:
return db
finally:
pass # Don't close here, let the caller handle it

View File

@@ -1,54 +0,0 @@
#!/bin/bash
# Deploy Real AITBC Trade Exchange
echo "🚀 Deploying Real AITBC Trade Exchange..."
# Install Python dependencies
echo "📦 Installing Python dependencies..."
pip3 install -r requirements.txt
# Kill existing services
echo "🔄 Stopping existing services..."
pkill -f "server.py --port 3002" || true
pkill -f "exchange_api.py" || true
# Start the Exchange API server
echo "🔥 Starting Exchange API server on port 3003..."
nohup python3 exchange_api.py > exchange_api.log 2>&1 &
sleep 2
# Start the frontend with real trading
echo "🌐 Starting Exchange frontend with real trading..."
nohup python3 server.py --port 3002 > exchange_frontend.log 2>&1 &
sleep 2
# Check if services are running
echo "✅ Checking services..."
if pgrep -f "exchange_api.py" > /dev/null; then
echo "✓ Exchange API is running on port 3003"
else
echo "✗ Exchange API failed to start"
fi
if pgrep -f "server.py --port 3002" > /dev/null; then
echo "✓ Exchange frontend is running on port 3002"
else
echo "✗ Exchange frontend failed to start"
fi
echo ""
echo "🎉 Real Exchange Deployment Complete!"
echo ""
echo "📍 Access the exchange at:"
echo " Frontend: https://aitbc.bubuit.net/Exchange"
echo " API: http://localhost:3003"
echo ""
echo "📊 API Endpoints:"
echo " GET /api/trades/recent - Get recent trades"
echo " GET /api/orders/orderbook - Get order book"
echo " POST /api/orders - Place new order"
echo " GET /api/health - Health check"
echo ""
echo "📝 Logs:"
echo " API: tail -f exchange_api.log"
echo " Frontend: tail -f exchange_frontend.log"

View File

@@ -1,54 +0,0 @@
#!/bin/bash
# Deploy Simple Real AITBC Trade Exchange
echo "🚀 Deploying Simple Real AITBC Trade Exchange..."
# Kill existing services
echo "🔄 Stopping existing services..."
pkill -f "server.py --port 3002" || true
pkill -f "exchange_api.py" || true
pkill -f "simple_exchange_api.py" || true
# Start the Simple Exchange API server
echo "🔥 Starting Simple Exchange API server on port 3003..."
nohup python3 simple_exchange_api.py > simple_exchange_api.log 2>&1 &
sleep 2
# Replace the frontend with real trading version
echo "🌐 Updating frontend to use real trading..."
cp index.real.html index.html
# Start the frontend
echo "🌐 Starting Exchange frontend..."
nohup python3 server.py --port 3002 > exchange_frontend.log 2>&1 &
sleep 2
# Check if services are running
echo "✅ Checking services..."
if pgrep -f "simple_exchange_api.py" > /dev/null; then
echo "✓ Simple Exchange API is running on port 3003"
else
echo "✗ Simple Exchange API failed to start"
echo " Check log: tail -f simple_exchange_api.log"
fi
if pgrep -f "server.py --port 3002" > /dev/null; then
echo "✓ Exchange frontend is running on port 3002"
else
echo "✗ Exchange frontend failed to start"
fi
echo ""
echo "🎉 Simple Real Exchange Deployment Complete!"
echo ""
echo "📍 Access the exchange at:"
echo " https://aitbc.bubuit.net/Exchange"
echo ""
echo "📊 The exchange now shows REAL trades from the database!"
echo " - Recent trades are loaded from the database"
echo " - Order book shows live orders"
echo " - You can place real buy/sell orders"
echo ""
echo "📝 Logs:"
echo " API: tail -f simple_exchange_api.log"
echo " Frontend: tail -f exchange_frontend.log"

View File

@@ -1,356 +0,0 @@
#!/usr/bin/env python3
"""
FastAPI backend for the AITBC Trade Exchange
"""
from datetime import datetime, timedelta
from typing import List, Optional
from fastapi import FastAPI, Depends, HTTPException, status, Header
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from sqlalchemy import desc, func, and_
from sqlalchemy.orm import Session
import hashlib
import time
from typing import Annotated
from database import init_db, get_db_session
from models import User, Order, Trade, Balance
# Initialize FastAPI app
app = FastAPI(title="AITBC Trade Exchange API", version="1.0.0")
# In-memory session storage (use Redis in production)
user_sessions = {}
def verify_session_token(token: str = Header(..., alias="Authorization")) -> int:
"""Verify session token and return user_id"""
# Remove "Bearer " prefix if present
if token.startswith("Bearer "):
token = token[7:]
if token not in user_sessions:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid or expired token"
)
session = user_sessions[token]
# Check if expired
if int(time.time()) > session["expires_at"]:
del user_sessions[token]
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Token expired"
)
return session["user_id"]
def optional_auth(token: Optional[str] = Header(None, alias="Authorization")) -> Optional[int]:
"""Optional authentication - returns user_id if token is valid, None otherwise"""
if not token:
return None
try:
return verify_session_token(token)
except HTTPException:
return None
# Type annotations for dependencies
UserDep = Annotated[int, Depends(verify_session_token)]
OptionalUserDep = Annotated[Optional[int], Depends(optional_auth)]
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=[
"http://localhost:3000",
"http://localhost:8080",
"http://localhost:8000",
"http://localhost:3003"
],
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allow_headers=["*"], # Allow all headers for auth tokens
)
# Pydantic models
class OrderCreate(BaseModel):
order_type: str # 'BUY' or 'SELL'
amount: float
price: float
class OrderResponse(BaseModel):
id: int
order_type: str
amount: float
price: float
total: float
filled: float
remaining: float
status: str
created_at: datetime
class Config:
from_attributes = True
class TradeResponse(BaseModel):
id: int
amount: float
price: float
total: float
created_at: datetime
class Config:
from_attributes = True
class OrderBookResponse(BaseModel):
buys: List[OrderResponse]
sells: List[OrderResponse]
# Initialize database on startup
@app.on_event("startup")
async def startup_event():
init_db()
# Create mock data if database is empty
db = get_db_session()
try:
# Check if we have any trades
if db.query(Trade).count() == 0:
create_mock_trades(db)
finally:
db.close()
def create_mock_trades(db: Session):
"""Create some mock trades for demonstration"""
import random
# Create mock trades over the last hour
now = datetime.utcnow()
trades = []
for i in range(20):
# Generate random trade data
amount = random.uniform(10, 500)
price = random.uniform(0.000009, 0.000012)
total = amount * price
trade = Trade(
buyer_id=1, # Mock user ID
seller_id=2, # Mock user ID
order_id=1, # Mock order ID
amount=amount,
price=price,
total=total,
trade_hash=f"mock_tx_{i:04d}",
created_at=now - timedelta(minutes=random.randint(0, 60))
)
trades.append(trade)
db.add_all(trades)
db.commit()
print(f"Created {len(trades)} mock trades")
@app.get("/api/trades/recent", response_model=List[TradeResponse])
def get_recent_trades(limit: int = 20, db: Session = Depends(get_db_session)):
"""Get recent trades"""
trades = db.query(Trade).order_by(desc(Trade.created_at)).limit(limit).all()
return trades
@app.get("/api/orders", response_model=List[OrderResponse])
def get_orders(
status_filter: Optional[str] = None,
user_only: bool = False,
db: Session = Depends(get_db_session),
user_id: OptionalUserDep = None
):
"""Get all orders with optional status filter"""
query = db.query(Order)
# Filter by user if requested and authenticated
if user_only and user_id:
query = query.filter(Order.user_id == user_id)
if status_filter:
query = query.filter(Order.status == status_filter.upper())
orders = query.order_by(Order.created_at.desc()).all()
return orders
@app.get("/api/my/orders", response_model=List[OrderResponse])
def get_my_orders(
user_id: UserDep,
status_filter: Optional[str] = None,
db: Session = Depends(get_db_session)
):
"""Get current user's orders"""
query = db.query(Order).filter(Order.user_id == user_id)
if status_filter:
query = query.filter(Order.status == status_filter.upper())
orders = query.order_by(Order.created_at.desc()).all()
return orders
@app.get("/api/orders/orderbook", response_model=OrderBookResponse)
def get_orderbook(db: Session = Depends(get_db_session)):
"""Get current order book"""
# Get open buy orders (sorted by price descending)
buys = db.query(Order).filter(
and_(Order.order_type == 'BUY', Order.status == 'OPEN')
).order_by(desc(Order.price)).limit(20).all()
# Get open sell orders (sorted by price ascending)
sells = db.query(Order).filter(
and_(Order.order_type == 'SELL', Order.status == 'OPEN')
).order_by(Order.price).limit(20).all()
return OrderBookResponse(buys=buys, sells=sells)
@app.post("/api/orders", response_model=OrderResponse)
def create_order(
order: OrderCreate,
db: Session = Depends(get_db_session),
user_id: UserDep
):
"""Create a new order"""
# Validate order type
if order.order_type not in ['BUY', 'SELL']:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Order type must be 'BUY' or 'SELL'"
)
# Create order
total = order.amount * order.price
db_order = Order(
user_id=user_id, # Use authenticated user_id
order_type=order.order_type,
amount=order.amount,
price=order.price,
total=total,
remaining=order.amount
)
db.add(db_order)
db.commit()
db.refresh(db_order)
# Try to match the order
try_match_order(db_order, db)
return db_order
def try_match_order(order: Order, db: Session):
"""Try to match an order with existing orders"""
if order.order_type == 'BUY':
# Match with sell orders at same or lower price
matching_orders = db.query(Order).filter(
and_(
Order.order_type == 'SELL',
Order.status == 'OPEN',
Order.price <= order.price
)
).order_by(Order.price).all()
else:
# Match with buy orders at same or higher price
matching_orders = db.query(Order).filter(
and_(
Order.order_type == 'BUY',
Order.status == 'OPEN',
Order.price >= order.price
)
).order_by(desc(Order.price)).all()
for match in matching_orders:
if order.remaining <= 0:
break
# Calculate trade amount
trade_amount = min(order.remaining, match.remaining)
trade_total = trade_amount * match.price
# Create trade record
trade = Trade(
buyer_id=order.user_id if order.order_type == 'BUY' else match.user_id,
seller_id=match.user_id if order.order_type == 'BUY' else order.user_id,
order_id=order.id,
amount=trade_amount,
price=match.price,
total=trade_total,
trade_hash=f"trade_{datetime.utcnow().timestamp()}"
)
db.add(trade)
# Update orders
order.filled += trade_amount
order.remaining -= trade_amount
match.filled += trade_amount
match.remaining -= trade_amount
# Update order statuses
if order.remaining <= 0:
order.status = 'FILLED'
else:
order.status = 'PARTIALLY_FILLED'
if match.remaining <= 0:
match.status = 'FILLED'
else:
match.status = 'PARTIALLY_FILLED'
db.commit()
@app.post("/api/auth/login")
def login_user(wallet_address: str, db: Session = Depends(get_db_session)):
"""Login with wallet address"""
# Find or create user
user = db.query(User).filter(User.wallet_address == wallet_address).first()
if not user:
user = User(
wallet_address=wallet_address,
email=f"{wallet_address}@aitbc.local",
is_active=True
)
db.add(user)
db.commit()
db.refresh(user)
# Create session token
token_data = f"{user.id}:{int(time.time())}"
token = hashlib.sha256(token_data.encode()).hexdigest()
# Store session
user_sessions[token] = {
"user_id": user.id,
"created_at": int(time.time()),
"expires_at": int(time.time()) + 86400 # 24 hours
}
return {"token": token, "user_id": user.id}
@app.post("/api/auth/logout")
def logout_user(token: str = Header(..., alias="Authorization")):
"""Logout user"""
if token.startswith("Bearer "):
token = token[7:]
if token in user_sessions:
del user_sessions[token]
return {"message": "Logged out successfully"}
@app.get("/api/health")
def health_check():
"""Health check endpoint"""
return {"status": "ok", "timestamp": datetime.utcnow()}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=3003)

File diff suppressed because it is too large Load Diff

View File

@@ -1,620 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AITBC Trade Exchange - Buy AITBC with Bitcoin</title>
<base href="/Exchange/">
<!-- Production: Use local assets -->
<script src="/assets/js/tailwind.js"></script>
<script>
tailwind.config = {
darkMode: 'class'
}
</script>
<script src="/assets/js/axios.min.js"></script>
<script src="/assets/js/lucide.js"></script>
<style>
.gradient-bg {
background: linear-gradient(135deg, #f97316 0%, #ea580c 100%);
}
.card-hover {
transition: all 0.3s ease;
}
.card-hover:hover {
transform: translateY(-4px);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
.pulse-animation {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.5; }
100% { opacity: 1; }
}
</style>
</head>
<body class="bg-gray-50 dark:bg-gray-900 transition-colors duration-300">
<!-- Header -->
<header class="gradient-bg text-white shadow-lg">
<div class="container mx-auto px-4 py-6">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-3">
<i data-lucide="trending-up" class="w-8 h-8"></i>
<h1 class="text-2xl font-bold">AITBC Trade Exchange</h1>
</div>
<nav class="flex items-center space-x-6">
<button onclick="showSection('trade')" class="hover:text-orange-200 transition">Trade</button>
<button onclick="showSection('marketplace')" class="hover:text-orange-200 transition">Marketplace</button>
<button onclick="showSection('wallet')" class="hover:text-orange-200 transition">Wallet</button>
<button onclick="toggleDarkMode()" class="hover:text-orange-200 transition" title="Toggle dark mode">
<i data-lucide="moon" class="w-5 h-5" id="darkModeIcon"></i>
</button>
<button id="navConnectBtn" onclick="connectWallet()" class="bg-white text-orange-600 px-4 py-2 rounded-lg hover:bg-orange-100 transition">
<i data-lucide="wallet" class="w-4 h-4 inline mr-2"></i>Connect Wallet
</button>
<div id="navUserInfo" class="hidden flex items-center space-x-3">
<span class="text-sm" id="navUsername">-</span>
<button onclick="showSection('wallet')" class="text-white hover:text-orange-200 transition">
<i data-lucide="user" class="w-5 h-5"></i>
</button>
<button onclick="logout()" class="text-white hover:text-orange-200 transition">
<i data-lucide="log-out" class="w-5 h-5"></i>
</button>
</div>
</nav>
</div>
</div>
</header>
<!-- Price Ticker -->
<section class="bg-white dark:bg-gray-800 border-b dark:border-gray-700">
<div class="container mx-auto px-4 py-4">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-6">
<div class="flex items-center space-x-2">
<span class="text-gray-600 dark:text-gray-400">AITBC/BTC:</span>
<span class="text-2xl font-bold text-green-600 dark:text-green-400" id="aitbcBtcPrice">0.00001</span>
<span class="text-sm text-green-500 dark:text-green-400">+5.2%</span>
</div>
<div class="flex items-center space-x-2">
<span class="text-gray-600 dark:text-gray-400">24h Volume:</span>
<span class="font-semibold text-gray-900 dark:text-white">1,234 AITBC</span>
</div>
</div>
<div class="text-sm text-gray-500 dark:text-gray-400">
Last updated: <span id="lastUpdated">Just now</span>
</div>
</div>
</div>
</section>
<!-- Main Content -->
<main class="container mx-auto px-4 py-8">
<!-- Trade Section -->
<section id="tradeSection" class="section">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
<!-- Buy AITBC -->
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6">
<h2 class="text-xl font-bold mb-6 flex items-center text-gray-900 dark:text-white">
<i data-lucide="arrow-down-left" class="w-5 h-5 mr-2 text-green-600 dark:text-green-400"></i>
Buy AITBC with Bitcoin
</h2>
<div id="tradeConnectPrompt" class="bg-orange-50 dark:bg-orange-900/20 border border-orange-200 dark:border-orange-800 rounded-lg p-4 mb-4">
<p class="text-sm text-orange-800 dark:text-orange-200 mb-3">
<i data-lucide="wallet" class="w-4 h-4 inline mr-1"></i>
Connect your wallet to start trading
</p>
<button onclick="connectWallet()" class="bg-orange-600 text-white px-4 py-2 rounded-lg hover:bg-orange-700 transition">
<i data-lucide="wallet" class="w-4 h-4 inline mr-2"></i>Connect Wallet
</button>
</div>
<div id="tradeForm" class="hidden">
<div class="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-4 mb-4">
<p class="text-sm text-blue-800 dark:text-blue-200">
<i data-lucide="info" class="w-4 h-4 inline mr-1"></i>
Send Bitcoin to the generated address. Your AITBC will be credited after 1 confirmation.
</p>
</div>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Pay with Bitcoin</label>
<div class="relative">
<input type="number" id="btcAmount" class="w-full border dark:border-gray-600 rounded-lg px-4 py-3 pr-12 bg-white dark:bg-gray-700 text-gray-900 dark:text-white" placeholder="0.001" step="0.00001">
<span class="absolute right-3 top-3 text-gray-500 dark:text-gray-400">BTC</span>
</div>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">Available: 0.12345 BTC</p>
</div>
<div class="flex justify-center">
<button onclick="swapCurrencies()" class="p-2 bg-gray-100 dark:bg-gray-700 rounded-full hover:bg-gray-200 dark:hover:bg-gray-600 transition">
<i data-lucide="arrow-up-down" class="w-5 h-5 text-gray-700 dark:text-gray-300"></i>
</button>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">You will receive</label>
<div class="relative">
<input type="number" id="aitbcAmount" class="w-full border dark:border-gray-600 rounded-lg px-4 py-3 pr-16 bg-white dark:bg-gray-700 text-gray-900 dark:text-white" placeholder="100" step="0.01">
<span class="absolute right-3 top-3 text-gray-500 dark:text-gray-400">AITBC</span>
</div>
</div>
<div class="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-4">
<div class="flex justify-between text-sm mb-2 text-gray-700 dark:text-gray-300">
<span>Price</span>
<span>0.00001 BTC/AITBC</span>
</div>
<div class="flex justify-between text-sm mb-2 text-gray-700 dark:text-gray-300">
<span>Fee (0.5%)</span>
<span>0.000005 BTC</span>
</div>
<div class="flex justify-between text-sm font-semibold text-gray-900 dark:text-white">
<span>Total</span>
<span>0.001005 BTC</span>
</div>
</div>
<button onclick="createPaymentRequest()" class="w-full bg-green-600 text-white py-3 rounded-lg hover:bg-green-700 transition font-semibold">
Create Payment Request
</button>
</div>
</div>
<!-- Order Book -->
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6">
<h2 class="text-xl font-bold mb-6 flex items-center text-gray-900 dark:text-white">
<i data-lucide="book-open" class="w-5 h-5 mr-2 text-blue-600 dark:text-blue-400"></i>
Order Book
</h2>
<div class="space-y-4">
<div class="border-b dark:border-gray-700 pb-2">
<h3 class="text-sm font-semibold text-gray-600 dark:text-gray-400 mb-2">Buy Orders</h3>
<div class="space-y-1">
<div class="flex justify-between text-sm">
<span class="text-green-600 dark:text-green-400">100 AITBC</span>
<span class="text-gray-700 dark:text-gray-300">0.001 BTC</span>
</div>
<div class="flex justify-between text-sm">
<span class="text-green-600 dark:text-green-400">50 AITBC</span>
<span class="text-gray-700 dark:text-gray-300">0.0005 BTC</span>
</div>
</div>
</div>
<div>
<h3 class="text-sm font-semibold text-gray-600 dark:text-gray-400 mb-2">Sell Orders</h3>
<div class="space-y-1">
<div class="flex justify-between text-sm">
<span class="text-red-600 dark:text-red-400">200 AITBC</span>
<span class="text-gray-700 dark:text-gray-300">0.002 BTC</span>
</div>
<div class="flex justify-between text-sm">
<span class="text-red-600 dark:text-red-400">150 AITBC</span>
<span class="text-gray-700 dark:text-gray-300">0.0015 BTC</span>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Marketplace Section -->
<section id="marketplaceSection" class="section hidden">
<h2 class="text-2xl font-bold mb-6 text-gray-900 dark:text-white">GPU Marketplace</h2>
<div id="gpuOffers" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- GPU offers will be loaded here -->
</div>
</section>
<!-- Wallet Section -->
<section id="walletSection" class="section hidden">
<h2 class="text-2xl font-bold mb-6 text-gray-900 dark:text-white">My Wallet</h2>
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<h3 class="text-lg font-semibold mb-4 text-gray-900 dark:text-white">Wallet Information</h3>
<div class="space-y-3">
<div>
<span class="text-sm text-gray-600 dark:text-gray-400">Address:</span>
<p class="font-mono text-sm bg-gray-100 dark:bg-gray-700 p-2 rounded" id="walletAddress">-</p>
</div>
<div>
<span class="text-sm text-gray-600 dark:text-gray-400">Username:</span>
<p id="walletUsername">-</p>
</div>
</div>
</div>
<div>
<h3 class="text-lg font-semibold mb-4 text-gray-900 dark:text-white">Balances</h3>
<div class="space-y-3">
<div>
<span class="text-sm text-gray-600 dark:text-gray-400">AITBC Balance:</span>
<p class="text-2xl font-bold text-green-600 dark:text-green-400" id="aitbcBalance">0 AITBC</p>
</div>
</div>
</div>
</div>
</div>
</section>
</main>
<!-- QR Code Modal -->
<div id="qrModal" class="fixed inset-0 bg-black bg-opacity-50 hidden flex items-center justify-center z-50">
<div class="bg-white dark:bg-gray-800 rounded-lg p-6 max-w-md w-full mx-4">
<h3 class="text-lg font-bold mb-4 text-gray-900 dark:text-white">Send Bitcoin to this Address</h3>
<div class="bg-gray-100 dark:bg-gray-700 p-4 rounded-lg mb-4">
<div id="qrCode" class="w-64 h-64 mx-auto mb-4 bg-white rounded"></div>
<p class="font-mono text-sm break-all" id="paymentAddress">-</p>
</div>
<div class="grid grid-cols-2 gap-4 mb-4 text-sm">
<div>
<p class="text-gray-600 dark:text-gray-400">Amount to Send:</p>
<p class="font-semibold" id="paymentAmount">0 BTC</p>
</div>
<div>
<p class="text-gray-600 dark:text-gray-400">You'll Receive:</p>
<p class="font-semibold text-green-600" id="receiveAmount">0 AITBC</p>
</div>
</div>
<div class="flex items-center justify-center mb-4">
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-green-600" id="paymentSpinner"></div>
<span class="ml-2 text-sm text-gray-600">Waiting for payment...</span>
</div>
<div class="flex space-x-3">
<button onclick="closeQRModal()" class="flex-1 bg-gray-200 text-gray-800 py-2 rounded-lg hover:bg-gray-300 transition">
Cancel
</button>
<button onclick="checkPaymentStatus()" class="flex-1 bg-blue-600 text-white py-2 rounded-lg hover:bg-blue-700 transition">
Check Payment
</button>
</div>
</div>
</div>
<script>
// API Configuration
const API_BASE = window.location.origin + '/api';
const BLOCKCHAIN_API = window.location.origin + '/rpc';
const EXCHANGE_RATE = 0.00001; // 1 AITBC = 0.00001 BTC
let walletAddress = null;
let currentUser = null;
let sessionToken = null;
let aitbcBalance = 0;
// Initialize
document.addEventListener('DOMContentLoaded', () => {
lucide.createIcons();
updatePrices();
loadGPUOffers();
// Auto-refresh prices every 30 seconds
setInterval(updatePrices, 30000);
// Input handlers
document.getElementById('btcAmount').addEventListener('input', updateAITBCAmount);
document.getElementById('aitbcAmount').addEventListener('input', updateBTCAmount);
// Check for saved dark mode preference
if (localStorage.getItem('darkMode') === 'true' ||
(!localStorage.getItem('darkMode') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark');
updateDarkModeIcon(true);
}
});
// Dark mode toggle
function toggleDarkMode() {
const isDark = document.documentElement.classList.toggle('dark');
localStorage.setItem('darkMode', isDark);
updateDarkModeIcon(isDark);
}
function updateDarkModeIcon(isDark) {
const icon = document.getElementById('darkModeIcon');
if (isDark) {
icon.setAttribute('data-lucide', 'sun');
} else {
icon.setAttribute('data-lucide', 'moon');
}
lucide.createIcons();
}
// Section Navigation
function showSection(section) {
document.querySelectorAll('.section').forEach(s => s.classList.add('hidden'));
document.getElementById(section + 'Section').classList.remove('hidden');
if (section === 'marketplace') {
loadGPUOffers();
}
}
// Connect Wallet
async function connectWallet() {
try {
// Generate a random wallet address for demo
walletAddress = 'aitbc1' + Array(39).fill(0).map(() => Math.random().toString(36).substr(2, 1)).join('');
// Login or register via API
const response = await axios.post(`${API_BASE}/users/login`, {
wallet_address: walletAddress
});
currentUser = response.data;
sessionToken = response.data.session_token;
// Update UI
document.getElementById('tradeConnectPrompt').classList.add('hidden');
document.getElementById('tradeForm').classList.remove('hidden');
document.getElementById('navConnectBtn').classList.add('hidden');
document.getElementById('navUserInfo').classList.remove('hidden');
document.getElementById('navUsername').textContent = currentUser.username;
document.getElementById('walletAddress').textContent = walletAddress;
document.getElementById('walletUsername').textContent = currentUser.username;
// Load wallet balance
await loadWalletBalance();
showToast('Wallet connected successfully!', 'success');
} catch (error) {
console.error('Failed to connect wallet:', error);
showToast('Failed to connect wallet', 'error');
}
}
// Load Wallet Balance
async function loadWalletBalance() {
try {
const response = await axios.get(
`${API_BASE}/users/${currentUser.user_id}/balance`,
{ headers: { 'Authorization': `Bearer ${sessionToken}` } }
);
aitbcBalance = response.data.balance || 0;
document.getElementById('aitbcBalance').textContent = aitbcBalance + ' AITBC';
} catch (error) {
console.error('Failed to load balance:', error);
}
}
// Logout
async function logout() {
try {
if (sessionToken) {
await axios.post(`${API_BASE}/users/logout`, {}, {
headers: { 'Authorization': `Bearer ${sessionToken}` }
});
}
} catch (error) {
console.error('Logout error:', error);
}
// Reset state
walletAddress = null;
currentUser = null;
sessionToken = null;
aitbcBalance = 0;
// Update UI
document.getElementById('tradeConnectPrompt').classList.remove('hidden');
document.getElementById('tradeForm').classList.add('hidden');
document.getElementById('navConnectBtn').classList.remove('hidden');
document.getElementById('navUserInfo').classList.add('hidden');
showToast('Logged out successfully', 'success');
}
// Update Prices
function updatePrices() {
// Simulate price updates
const variation = (Math.random() - 0.5) * 0.000001;
const newPrice = EXCHANGE_RATE + variation;
document.getElementById('aitbcBtcPrice').textContent = newPrice.toFixed(5);
document.getElementById('lastUpdated').textContent = 'Just now';
}
// Currency Conversion
function updateAITBCAmount() {
const btcAmount = parseFloat(document.getElementById('btcAmount').value) || 0;
const aitbcAmount = btcAmount / EXCHANGE_RATE;
document.getElementById('aitbcAmount').value = aitbcAmount.toFixed(2);
}
function updateBTCAmount() {
const aitbcAmount = parseFloat(document.getElementById('aitbcAmount').value) || 0;
const btcAmount = aitbcAmount * EXCHANGE_RATE;
document.getElementById('btcAmount').value = btcAmount.toFixed(5);
}
function swapCurrencies() {
const btcInput = document.getElementById('btcAmount');
const aitbcInput = document.getElementById('aitbcAmount');
const temp = btcInput.value;
btcInput.value = aitbcInput.value;
aitbcInput.value = temp;
}
// Create Payment Request
async function createPaymentRequest() {
const btcAmount = document.getElementById('btcAmount').value;
if (!btcAmount || btcAmount <= 0) {
showToast('Please enter a valid amount', 'error');
return;
}
try {
const response = await axios.post(`${API_BASE}/exchange/create-payment`, {
amount: parseFloat(btcAmount),
currency: 'BTC'
}, {
headers: { 'Authorization': `Bearer ${sessionToken}` }
});
const payment = response.data;
showQRModal(payment);
} catch (error) {
console.error('Failed to create payment:', error);
showToast('Failed to create payment request', 'error');
}
}
// Show QR Modal
function showQRModal(payment) {
const modal = document.getElementById('qrModal');
const addressEl = document.getElementById('paymentAddress');
const amountEl = document.getElementById('paymentAmount');
const receiveEl = document.getElementById('receiveAmount');
addressEl.textContent = payment.address;
amountEl.textContent = payment.amount + ' BTC';
receiveEl.textContent = (payment.amount / EXCHANGE_RATE).toFixed(2) + ' AITBC';
// Generate QR code (simplified - in production use a proper QR library)
const qrDiv = document.getElementById('qrCode');
qrDiv.innerHTML = `
<div class="w-full h-full flex items-center justify-center border-2 border-gray-300 rounded">
<div class="text-center">
<i data-lucide="qr-code" class="w-32 h-32 mx-auto mb-2"></i>
<p class="text-sm">QR Code for ${payment.address}</p>
</div>
</div>
`;
lucide.createIcons();
modal.classList.remove('hidden');
window.currentPaymentId = payment.payment_id;
}
// Close QR Modal
function closeQRModal() {
document.getElementById('qrModal').classList.add('hidden');
window.currentPaymentId = null;
}
// Check Payment Status
async function checkPaymentStatus() {
if (!window.currentPaymentId) return;
try {
const response = await axios.get(
`${API_BASE}/exchange/payment-status/${window.currentPaymentId}`,
{ headers: { 'Authorization': `Bearer ${sessionToken}` } }
);
const status = response.data.status;
if (status === 'completed') {
showToast('Payment received! AITBC credited to your wallet.', 'success');
closeQRModal();
await loadWalletBalance();
} else if (status === 'pending') {
showToast('Payment still pending...', 'info');
} else {
showToast('Payment not found', 'error');
}
} catch (error) {
console.error('Failed to check payment:', error);
showToast('Failed to check payment status', 'error');
}
}
// Load GPU Offers
async function loadGPUOffers() {
try {
const response = await axios.get(`${API_BASE}/marketplace/offers`);
displayGPUOffers(response.data);
} catch (error) {
console.error('Failed to load GPU offers:', error);
// Display demo offers
displayGPUOffers([
{
id: 'demo-1',
provider: 'Demo Provider 1',
capacity: 'RTX 4090',
price: 0.01,
status: 'available'
},
{
id: 'demo-2',
provider: 'Demo Provider 2',
capacity: 'A100 80GB',
price: 0.05,
status: 'available'
}
]);
}
}
// Display GPU Offers
function displayGPUOffers(offers) {
const container = document.getElementById('gpuOffers');
if (offers.length === 0) {
container.innerHTML = '<p class="text-gray-500 dark:text-gray-400">No GPU offers available at the moment.</p>';
return;
}
container.innerHTML = offers.map(offer => `
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6 card-hover">
<div class="flex items-center justify-between mb-4">
<span class="text-sm font-semibold text-blue-600 dark:text-blue-400">${offer.capacity}</span>
<span class="text-xs px-2 py-1 bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200 rounded">
${offer.status || 'Available'}
</span>
</div>
<h3 class="font-semibold mb-2 text-gray-900 dark:text-white">${offer.provider}</h3>
<p class="text-2xl font-bold text-gray-900 dark:text-white mb-4">${offer.price} BTC/hour</p>
<button onclick="rentGPU('${offer.id}')" class="w-full bg-blue-600 text-white py-2 rounded-lg hover:bg-blue-700 transition">
Rent Now
</button>
</div>
`).join('');
}
// Rent GPU
function rentGPU(gpuId) {
if (!currentUser) {
showToast('Please connect your wallet first', 'error');
showSection('trade');
return;
}
showToast(`Renting GPU ${gpuId}...`, 'info');
}
// Toast Notification
function showToast(message, type = 'info') {
const toast = document.createElement('div');
toast.className = `fixed bottom-4 right-4 px-6 py-3 rounded-lg shadow-lg transform transition-all duration-300 z-50`;
if (type === 'success') {
toast.classList.add('bg-green-500', 'text-white');
} else if (type === 'error') {
toast.classList.add('bg-red-500', 'text-white');
} else {
toast.classList.add('bg-blue-500', 'text-white');
}
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(() => {
toast.classList.add('translate-y-full', 'opacity-0');
setTimeout(() => toast.remove(), 300);
}, 3000);
}
</script>
</body>
</html>

View File

@@ -1,410 +0,0 @@
<!DOCTYPE html>
<html lang="en" class="h-full">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AITBC Trade Exchange - Buy & Sell AITBC</title>
<link rel="stylesheet" href="./styles.css">
<script src="https://unpkg.com/lucide@latest"></script>
</head>
<body class="h-full bg-gray-50 dark:bg-gray-900">
<div class="min-h-full">
<!-- Navigation -->
<nav class="bg-white dark:bg-gray-800 shadow">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-16">
<div class="flex items-center">
<div class="flex-shrink-0 flex items-center">
<h1 class="text-xl font-bold text-gray-900 dark:text-white">AITBC Exchange</h1>
</div>
<div class="hidden sm:ml-8 sm:flex sm:space-x-8">
<a href="#" class="border-primary-500 text-gray-900 dark:text-white inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">
Trade
</a>
<a href="#" class="border-transparent text-gray-500 dark:text-gray-300 hover:border-gray-300 hover:text-gray-700 dark:hover:text-gray-200 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">
Orders
</a>
<a href="#" class="border-transparent text-gray-500 dark:text-gray-300 hover:border-gray-300 hover:text-gray-700 dark:hover:text-gray-200 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">
History
</a>
</div>
</div>
<div class="flex items-center space-x-4">
<button onclick="toggleDarkMode()" class="p-2 rounded-md text-gray-500 dark:text-gray-300 hover:text-gray-700 dark:hover:text-gray-200">
<i data-lucide="moon" id="darkModeIcon" class="h-5 w-5"></i>
</button>
<div class="flex items-center space-x-2">
<span class="text-sm text-gray-700 dark:text-gray-300">Balance:</span>
<span class="text-sm font-medium text-gray-900 dark:text-white" id="userBalance">0 BTC | 0 AITBC</span>
</div>
<button class="bg-primary-600 hover:bg-primary-700 text-white px-4 py-2 rounded-md text-sm font-medium">
Connect Wallet
</button>
</div>
</div>
</div>
</nav>
<!-- Main Content -->
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<!-- Price Ticker -->
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 mb-6">
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div>
<p class="text-sm text-gray-600 dark:text-gray-400">Current Price</p>
<p class="text-2xl font-bold text-gray-900 dark:text-white" id="currentPrice">0.000010 BTC</p>
<p class="text-sm text-green-600" id="priceChange">+5.2%</p>
</div>
<div>
<p class="text-sm text-gray-600 dark:text-gray-400">24h Volume</p>
<p class="text-2xl font-bold text-gray-900 dark:text-white" id="volume24h">12,345 AITBC</p>
<p class="text-sm text-gray-500 dark:text-gray-400">≈ 0.12345 BTC</p>
</div>
<div>
<p class="text-sm text-gray-600 dark:text-gray-400">24h High / Low</p>
<p class="text-2xl font-bold text-gray-900 dark:text-white" id="highLow">0.000011 / 0.000009</p>
<p class="text-sm text-gray-500 dark:text-gray-400">BTC</p>
</div>
</div>
</div>
<!-- Trading Interface -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Order Book -->
<div class="bg-white dark:bg-gray-800 rounded-lg shadow">
<div class="p-4 border-b dark:border-gray-700">
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">Order Book</h2>
</div>
<div class="p-4">
<div class="space-y-2">
<div class="grid grid-cols-3 text-xs font-medium text-gray-500 dark:text-gray-400 pb-2">
<span>Price (BTC)</span>
<span class="text-right">Amount (AITBC)</span>
<span class="text-right">Total (BTC)</span>
</div>
<!-- Sell Orders -->
<div id="sellOrders" class="space-y-1">
<!-- Dynamically populated -->
</div>
<div class="border-t dark:border-gray-700 my-2"></div>
<!-- Buy Orders -->
<div id="buyOrders" class="space-y-1">
<!-- Dynamically populated -->
</div>
</div>
</div>
</div>
<!-- Trade Form -->
<div class="bg-white dark:bg-gray-800 rounded-lg shadow">
<div class="p-4 border-b dark:border-gray-700">
<div class="flex space-x-4">
<button onclick="setTradeType('BUY')" id="buyTab" class="flex-1 py-2 px-4 text-center font-medium text-white bg-green-600 rounded-md">
Buy AITBC
</button>
<button onclick="setTradeType('SELL')" id="sellTab" class="flex-1 py-2 px-4 text-center font-medium text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 rounded-md">
Sell AITBC
</button>
</div>
</div>
<div class="p-4">
<form id="tradeForm" onsubmit="placeOrder(event)">
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Price (BTC)</label>
<input type="number" id="orderPrice" step="0.000001" class="mt-1 block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-white shadow-sm focus:border-primary-500 focus:ring-primary-500" value="0.000010">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Amount (AITBC)</label>
<input type="number" id="orderAmount" step="0.01" class="mt-1 block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-white shadow-sm focus:border-primary-500 focus:ring-primary-500" placeholder="0.00">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Total (BTC)</label>
<input type="number" id="orderTotal" step="0.000001" readonly class="mt-1 block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-white bg-gray-50 dark:bg-gray-600" placeholder="0.00">
</div>
<button type="submit" id="submitOrder" class="w-full bg-green-600 hover:bg-green-700 text-white font-medium py-2 px-4 rounded-md">
Place Buy Order
</button>
</div>
</form>
</div>
</div>
<!-- Recent Trades -->
<div class="bg-white dark:bg-gray-800 rounded-lg shadow">
<div class="p-4 border-b dark:border-gray-700">
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">Recent Trades</h2>
</div>
<div class="p-4">
<div class="space-y-2">
<div class="grid grid-cols-3 text-xs font-medium text-gray-500 dark:text-gray-400 pb-2">
<span>Price (BTC)</span>
<span class="text-right">Amount</span>
<span class="text-right">Time</span>
</div>
<div id="recentTrades" class="space-y-1">
<!-- Dynamically populated -->
</div>
</div>
</div>
</div>
</div>
</main>
</div>
<script>
// API Configuration
const API_BASE = window.location.origin + '/api';
const EXCHANGE_API_BASE = window.location.origin; // Use same domain with nginx routing
let tradeType = 'BUY';
// Initialize
document.addEventListener('DOMContentLoaded', () => {
lucide.createIcons();
// Load initial data
loadRecentTrades();
loadOrderBook();
updatePriceTicker();
// Auto-refresh every 5 seconds
setInterval(() => {
loadRecentTrades();
loadOrderBook();
updatePriceTicker();
}, 5000);
// Input handlers
document.getElementById('orderAmount').addEventListener('input', updateOrderTotal);
document.getElementById('orderPrice').addEventListener('input', updateOrderTotal);
// Check for saved dark mode preference
if (localStorage.getItem('darkMode') === 'true' ||
(!localStorage.getItem('darkMode') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark');
updateDarkModeIcon(true);
}
});
// Dark mode toggle
function toggleDarkMode() {
const isDark = document.documentElement.classList.toggle('dark');
localStorage.setItem('darkMode', isDark);
updateDarkModeIcon(isDark);
}
function updateDarkModeIcon(isDark) {
const icon = document.getElementById('darkModeIcon');
if (isDark) {
icon.setAttribute('data-lucide', 'sun');
} else {
icon.setAttribute('data-lucide', 'moon');
}
lucide.createIcons();
}
// Update price ticker with real data
async function updatePriceTicker() {
try {
// Get recent trades to calculate price statistics
const response = await fetch(`${EXCHANGE_API_BASE}/api/trades/recent?limit=100`);
if (!response.ok) return;
const trades = await response.json();
if (trades.length === 0) {
console.log('No trades to calculate price from');
return;
}
// Calculate 24h volume (sum of all trades in last 24h)
const now = new Date();
const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
const recentTrades = trades.filter(trade =>
new Date(trade.created_at) > yesterday
);
const totalVolume = recentTrades.reduce((sum, trade) => sum + trade.amount, 0);
const totalBTC = recentTrades.reduce((sum, trade) => sum + trade.total, 0);
// Calculate current price (price of last trade)
const currentPrice = trades[0].price;
// Calculate 24h high/low
const prices = recentTrades.map(t => t.price);
const high24h = Math.max(...prices);
const low24h = Math.min(...prices);
// Calculate price change (compare with price 24h ago)
const price24hAgo = trades[trades.length - 1]?.price || currentPrice;
const priceChange = ((currentPrice - price24hAgo) / price24hAgo) * 100;
// Update UI
document.getElementById('currentPrice').textContent = `${currentPrice.toFixed(6)} BTC`;
document.getElementById('volume24h').textContent = `${totalVolume.toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g, ",")} AITBC`;
document.getElementById('volume24h').nextElementSibling.textContent = `${totalBTC.toFixed(5)} BTC`;
document.getElementById('highLow').textContent = `${high24h.toFixed(6)} / ${low24h.toFixed(6)}`;
// Update price change with color
const changeElement = document.getElementById('priceChange');
changeElement.textContent = `${priceChange >= 0 ? '+' : ''}${priceChange.toFixed(2)}%`;
changeElement.className = `text-sm ${priceChange >= 0 ? 'text-green-600' : 'text-red-600'}`;
} catch (error) {
console.error('Failed to update price ticker:', error);
}
}
// Trade type
function setTradeType(type) {
tradeType = type;
const buyTab = document.getElementById('buyTab');
const sellTab = document.getElementById('sellTab');
const submitBtn = document.getElementById('submitOrder');
if (type === 'BUY') {
buyTab.className = 'flex-1 py-2 px-4 text-center font-medium text-white bg-green-600 rounded-md';
sellTab.className = 'flex-1 py-2 px-4 text-center font-medium text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 rounded-md';
submitBtn.className = 'w-full bg-green-600 hover:bg-green-700 text-white font-medium py-2 px-4 rounded-md';
submitBtn.textContent = 'Place Buy Order';
} else {
sellTab.className = 'flex-1 py-2 px-4 text-center font-medium text-white bg-red-600 rounded-md';
buyTab.className = 'flex-1 py-2 px-4 text-center font-medium text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 rounded-md';
submitBtn.className = 'w-full bg-red-600 hover:bg-red-700 text-white font-medium py-2 px-4 rounded-md';
submitBtn.textContent = 'Place Sell Order';
}
}
// Order calculations
function updateOrderTotal() {
const price = parseFloat(document.getElementById('orderPrice').value) || 0;
const amount = parseFloat(document.getElementById('orderAmount').value) || 0;
const total = price * amount;
document.getElementById('orderTotal').value = total.toFixed(8);
}
// Load recent trades
async function loadRecentTrades() {
try {
const response = await fetch(`${EXCHANGE_API_BASE}/api/trades/recent?limit=15`);
if (response.ok) {
const trades = await response.json();
displayRecentTrades(trades);
}
} catch (error) {
console.error('Failed to load recent trades:', error);
}
}
function displayRecentTrades(trades) {
const container = document.getElementById('recentTrades');
container.innerHTML = '';
trades.forEach(trade => {
const div = document.createElement('div');
div.className = 'flex justify-between text-sm';
const time = new Date(trade.created_at).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
const priceClass = trade.id % 2 === 0 ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400';
div.innerHTML = `
<span class="${priceClass}">${trade.price.toFixed(6)}</span>
<span class="text-gray-600 dark:text-gray-400 text-right">${trade.amount.toFixed(2)}</span>
<span class="text-gray-500 dark:text-gray-400 text-right">${time}</span>
`;
container.appendChild(div);
});
}
// Load order book
async function loadOrderBook() {
try {
const response = await fetch(`${EXCHANGE_API_BASE}/api/orders/orderbook`);
if (response.ok) {
const orderbook = await response.json();
displayOrderBook(orderbook);
}
} catch (error) {
console.error('Failed to load order book:', error);
}
}
function displayOrderBook(orderbook) {
const sellContainer = document.getElementById('sellOrders');
const buyContainer = document.getElementById('buyOrders');
// Display sell orders (highest to lowest)
sellContainer.innerHTML = '';
orderbook.sells.slice(0, 8).reverse().forEach(order => {
const div = document.createElement('div');
div.className = 'flex justify-between text-sm';
div.innerHTML = `
<span class="text-red-600 dark:text-red-400">${order.price.toFixed(6)}</span>
<span class="text-gray-600 dark:text-gray-400 text-right">${order.remaining.toFixed(2)}</span>
<span class="text-gray-500 dark:text-gray-400 text-right">${(order.remaining * order.price).toFixed(4)}</span>
`;
sellContainer.appendChild(div);
});
// Display buy orders (highest to lowest)
buyContainer.innerHTML = '';
orderbook.buys.slice(0, 8).forEach(order => {
const div = document.createElement('div');
div.className = 'flex justify-between text-sm';
div.innerHTML = `
<span class="text-green-600 dark:text-green-400">${order.price.toFixed(6)}</span>
<span class="text-gray-600 dark:text-gray-400 text-right">${order.remaining.toFixed(2)}</span>
<span class="text-gray-500 dark:text-gray-400 text-right">${(order.remaining * order.price).toFixed(4)}</span>
`;
buyContainer.appendChild(div);
});
}
// Place order
async function placeOrder(event) {
event.preventDefault();
const price = parseFloat(document.getElementById('orderPrice').value);
const amount = parseFloat(document.getElementById('orderAmount').value);
if (!price || !amount) {
alert('Please enter valid price and amount');
return;
}
try {
const response = await fetch(`${EXCHANGE_API_BASE}/api/orders`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
order_type: tradeType,
price: price,
amount: amount
})
});
if (response.ok) {
const order = await response.json();
alert(`${tradeType} order placed successfully! Order ID: ${order.id}`);
// Clear form
document.getElementById('orderAmount').value = '';
document.getElementById('orderTotal').value = '';
// Reload order book
loadOrderBook();
} else {
const error = await response.json();
alert(`Failed to place order: ${error.detail || 'Unknown error'}`);
}
} catch (error) {
console.error('Failed to place order:', error);
alert('Failed to place order. Please try again.');
}
}
</script>
</body>
</html>

View File

@@ -1,398 +0,0 @@
<!DOCTYPE html>
<html lang="en" class="h-full">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AITBC Trade Exchange - Buy & Sell AITBC</title>
<script src="https://unpkg.com/lucide@latest"></script>
<style>
/* Production CSS for AITBC Trade Exchange */
/* Dark mode variables */
:root {
--bg-primary: #ffffff;
--bg-secondary: #f9fafb;
--bg-tertiary: #f3f4f6;
--text-primary: #111827;
--text-secondary: #6b7280;
--text-tertiary: #9ca3af;
--border-color: #e5e7eb;
--primary-50: #eff6ff;
--primary-500: #3b82f6;
--primary-600: #2563eb;
--primary-700: #1d4ed8;
}
.dark {
--bg-primary: #1f2937;
--bg-secondary: #111827;
--bg-tertiary: #374151;
--text-primary: #f9fafb;
--text-secondary: #d1d5db;
--text-tertiary: #9ca3af;
--border-color: #4b5563;
}
/* Base styles */
* {
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background-color: var(--bg-secondary);
color: var(--text-primary);
margin: 0;
padding: 0;
}
/* Layout */
.h-full {
height: 100%;
}
.min-h-full {
min-height: 100%;
}
.max-w-7xl {
max-width: 1280px;
}
.mx-auto {
margin-left: auto;
margin-right: auto;
}
.px-4 {
padding-left: 1rem;
padding-right: 1rem;
}
.py-8 {
padding-top: 2rem;
padding-bottom: 2rem;
}
/* Navigation */
nav {
background-color: var(--bg-primary);
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
}
nav > div {
display: flex;
justify-content: space-between;
height: 4rem;
align-items: center;
}
nav .flex {
display: flex;
}
nav .items-center {
align-items: center;
}
nav .space-x-8 > * + * {
margin-left: 2rem;
}
nav .space-x-4 > * + * {
margin-left: 1rem;
}
nav .text-xl {
font-size: 1.25rem;
line-height: 1.75rem;
}
nav .font-bold {
font-weight: 700;
}
nav .text-sm {
font-size: 0.875rem;
line-height: 1.25rem;
}
nav .font-medium {
font-weight: 500;
}
/* Links */
a {
color: inherit;
text-decoration: none;
}
a:hover {
color: var(--primary-600);
}
/* Cards */
.bg-white {
background-color: var(--bg-primary);
}
.dark .bg-white {
background-color: var(--bg-primary);
}
.rounded-lg {
border-radius: 0.5rem;
}
.shadow {
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
}
.p-4 {
padding: 1rem;
}
.p-6 {
padding: 1.5rem;
}
.mb-6 {
margin-bottom: 1.5rem;
}
/* Grid */
.grid {
display: grid;
}
.grid-cols-1 {
grid-template-columns: repeat(1, minmax(0, 1fr));
}
.grid-cols-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.gap-6 {
gap: 1.5rem;
}
@media (min-width: 1024px) {
.lg\:grid-cols-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
/* Typography */
.text-sm {
font-size: 0.875rem;
line-height: 1.25rem;
}
.text-2xl {
font-size: 1.5rem;
line-height: 2rem;
}
.text-lg {
font-size: 1.125rem;
line-height: 1.75rem;
}
.font-semibold {
font-weight: 600;
}
.font-bold {
font-weight: 700;
}
.text-gray-600 {
color: var(--text-secondary);
}
.text-gray-900 {
color: var(--text-primary);
}
.text-gray-500 {
color: var(--text-tertiary);
}
.dark .text-gray-300 {
color: #d1d5db;
}
.dark .text-gray-400 {
color: #9ca3af;
}
.dark .text-white {
color: #ffffff;
}
/* Buttons */
button {
cursor: pointer;
border: none;
border-radius: 0.375rem;
padding: 0.5rem 1rem;
font-size: 0.875rem;
font-weight: 500;
transition: all 0.15s ease-in-out;
}
.bg-primary-600 {
background-color: var(--primary-600);
}
.bg-primary-600:hover {
background-color: var(--primary-700);
}
.text-white {
color: #ffffff;
}
.bg-green-600 {
background-color: #059669;
}
.bg-green-600:hover {
background-color: #047857;
}
.bg-red-600 {
background-color: #dc2626;
}
.bg-red-600:hover {
background-color: #b91c1c;
}
.bg-gray-100 {
background-color: var(--bg-tertiary);
}
/* Forms */
input {
width: 100%;
padding: 0.5rem 0.75rem;
border: 1px solid var(--border-color);
border-radius: 0.375rem;
background-color: var(--bg-primary);
color: var(--text-primary);
}
input:focus {
outline: none;
border-color: var(--primary-500);
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
.dark input {
background-color: var(--bg-tertiary);
border-color: var(--border-color);
}
.dark input:focus {
border-color: var(--primary-500);
}
/* Tables */
.space-y-2 > * + * {
margin-top: 0.5rem;
}
.space-y-1 > * + * {
margin-top: 0.25rem;
}
.justify-between {
justify-content: space-between;
}
.text-right {
text-align: right;
}
.text-green-600 {
color: #059669;
}
.text-red-600 {
color: #dc2626;
}
/* Borders */
.border-b {
border-bottom: 1px solid var(--border-color);
}
.border-t {
border-top: 1px solid var(--border-color);
}
/* Width */
.w-full {
width: 100%;
}
/* Flex */
.flex {
display: flex;
}
.flex-1 {
flex: 1 1 0%;
}
/* Colors */
.bg-gray-50 {
background-color: var(--bg-secondary);
}
.dark .bg-gray-600 {
background-color: #4b5563;
}
.dark .bg-gray-700 {
background-color: #374151;
}
/* Dark mode toggle */
.p-2 {
padding: 0.5rem;
}
.rounded-md {
border-radius: 0.375rem;
}
/* Hover states */
.hover\:text-gray-700:hover {
color: var(--text-primary);
}
.dark .hover\:text-gray-200:hover {
color: #e5e7eb;
}
/* Order book colors */
.text-red-600 {
color: #dc2626;
}
.dark .text-red-400 {
color: #f87171;
}
.text-green-600 {
color: #059669;
}
.dark .text-green-400 {
color: #4ade80;
}
</style>
</head>

View File

@@ -1,398 +0,0 @@
<!DOCTYPE html>
<html lang="en" class="h-full">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AITBC Trade Exchange - Buy & Sell AITBC</title>
<script src="https://unpkg.com/lucide@latest"></script>
<style>
/* Production CSS for AITBC Trade Exchange */
/* Dark mode variables */
:root {
--bg-primary: #ffffff;
--bg-secondary: #f9fafb;
--bg-tertiary: #f3f4f6;
--text-primary: #111827;
--text-secondary: #6b7280;
--text-tertiary: #9ca3af;
--border-color: #e5e7eb;
--primary-50: #eff6ff;
--primary-500: #3b82f6;
--primary-600: #2563eb;
--primary-700: #1d4ed8;
}
.dark {
--bg-primary: #1f2937;
--bg-secondary: #111827;
--bg-tertiary: #374151;
--text-primary: #f9fafb;
--text-secondary: #d1d5db;
--text-tertiary: #9ca3af;
--border-color: #4b5563;
}
/* Base styles */
* {
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background-color: var(--bg-secondary);
color: var(--text-primary);
margin: 0;
padding: 0;
}
/* Layout */
.h-full {
height: 100%;
}
.min-h-full {
min-height: 100%;
}
.max-w-7xl {
max-width: 1280px;
}
.mx-auto {
margin-left: auto;
margin-right: auto;
}
.px-4 {
padding-left: 1rem;
padding-right: 1rem;
}
.py-8 {
padding-top: 2rem;
padding-bottom: 2rem;
}
/* Navigation */
nav {
background-color: var(--bg-primary);
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
}
nav > div {
display: flex;
justify-content: space-between;
height: 4rem;
align-items: center;
}
nav .flex {
display: flex;
}
nav .items-center {
align-items: center;
}
nav .space-x-8 > * + * {
margin-left: 2rem;
}
nav .space-x-4 > * + * {
margin-left: 1rem;
}
nav .text-xl {
font-size: 1.25rem;
line-height: 1.75rem;
}
nav .font-bold {
font-weight: 700;
}
nav .text-sm {
font-size: 0.875rem;
line-height: 1.25rem;
}
nav .font-medium {
font-weight: 500;
}
/* Links */
a {
color: inherit;
text-decoration: none;
}
a:hover {
color: var(--primary-600);
}
/* Cards */
.bg-white {
background-color: var(--bg-primary);
}
.dark .bg-white {
background-color: var(--bg-primary);
}
.rounded-lg {
border-radius: 0.5rem;
}
.shadow {
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
}
.p-4 {
padding: 1rem;
}
.p-6 {
padding: 1.5rem;
}
.mb-6 {
margin-bottom: 1.5rem;
}
/* Grid */
.grid {
display: grid;
}
.grid-cols-1 {
grid-template-columns: repeat(1, minmax(0, 1fr));
}
.grid-cols-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.gap-6 {
gap: 1.5rem;
}
@media (min-width: 1024px) {
.lg\:grid-cols-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
/* Typography */
.text-sm {
font-size: 0.875rem;
line-height: 1.25rem;
}
.text-2xl {
font-size: 1.5rem;
line-height: 2rem;
}
.text-lg {
font-size: 1.125rem;
line-height: 1.75rem;
}
.font-semibold {
font-weight: 600;
}
.font-bold {
font-weight: 700;
}
.text-gray-600 {
color: var(--text-secondary);
}
.text-gray-900 {
color: var(--text-primary);
}
.text-gray-500 {
color: var(--text-tertiary);
}
.dark .text-gray-300 {
color: #d1d5db;
}
.dark .text-gray-400 {
color: #9ca3af;
}
.dark .text-white {
color: #ffffff;
}
/* Buttons */
button {
cursor: pointer;
border: none;
border-radius: 0.375rem;
padding: 0.5rem 1rem;
font-size: 0.875rem;
font-weight: 500;
transition: all 0.15s ease-in-out;
}
.bg-primary-600 {
background-color: var(--primary-600);
}
.bg-primary-600:hover {
background-color: var(--primary-700);
}
.text-white {
color: #ffffff;
}
.bg-green-600 {
background-color: #059669;
}
.bg-green-600:hover {
background-color: #047857;
}
.bg-red-600 {
background-color: #dc2626;
}
.bg-red-600:hover {
background-color: #b91c1c;
}
.bg-gray-100 {
background-color: var(--bg-tertiary);
}
/* Forms */
input {
width: 100%;
padding: 0.5rem 0.75rem;
border: 1px solid var(--border-color);
border-radius: 0.375rem;
background-color: var(--bg-primary);
color: var(--text-primary);
}
input:focus {
outline: none;
border-color: var(--primary-500);
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
.dark input {
background-color: var(--bg-tertiary);
border-color: var(--border-color);
}
.dark input:focus {
border-color: var(--primary-500);
}
/* Tables */
.space-y-2 > * + * {
margin-top: 0.5rem;
}
.space-y-1 > * + * {
margin-top: 0.25rem;
}
.justify-between {
justify-content: space-between;
}
.text-right {
text-align: right;
}
.text-green-600 {
color: #059669;
}
.text-red-600 {
color: #dc2626;
}
/* Borders */
.border-b {
border-bottom: 1px solid var(--border-color);
}
.border-t {
border-top: 1px solid var(--border-color);
}
/* Width */
.w-full {
width: 100%;
}
/* Flex */
.flex {
display: flex;
}
.flex-1 {
flex: 1 1 0%;
}
/* Colors */
.bg-gray-50 {
background-color: var(--bg-secondary);
}
.dark .bg-gray-600 {
background-color: #4b5563;
}
.dark .bg-gray-700 {
background-color: #374151;
}
/* Dark mode toggle */
.p-2 {
padding: 0.5rem;
}
.rounded-md {
border-radius: 0.375rem;
}
/* Hover states */
.hover\:text-gray-700:hover {
color: var(--text-primary);
}
.dark .hover\:text-gray-200:hover {
color: #e5e7eb;
}
/* Order book colors */
.text-red-600 {
color: #dc2626;
}
.dark .text-red-400 {
color: #f87171;
}
.text-green-600 {
color: #059669;
}
.dark .text-green-400 {
color: #4ade80;
}
</style>
</head>

View File

@@ -1,109 +0,0 @@
#!/usr/bin/env python3
"""
Database models for the AITBC Trade Exchange
"""
from datetime import datetime
from typing import Optional
from sqlalchemy import Column, Integer, String, Float, DateTime, Boolean, ForeignKey, Index
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
Base = declarative_base()
class User(Base):
"""User account for trading"""
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
username = Column(String(50), unique=True, index=True, nullable=False)
email = Column(String(100), unique=True, index=True, nullable=False)
password_hash = Column(String(255), nullable=False)
bitcoin_address = Column(String(100), unique=True, nullable=False)
aitbc_address = Column(String(100), unique=True, nullable=False)
created_at = Column(DateTime, default=datetime.utcnow)
is_active = Column(Boolean, default=True)
# Relationships
orders = relationship("Order", back_populates="user")
trades = relationship("Trade", back_populates="buyer")
def __repr__(self):
return f"<User(username='{self.username}')>"
class Order(Base):
"""Trading order (buy or sell)"""
__tablename__ = "orders"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
order_type = Column(String(4), nullable=False) # 'BUY' or 'SELL'
amount = Column(Float, nullable=False) # Amount of AITBC
price = Column(Float, nullable=False) # Price in BTC
total = Column(Float, nullable=False) # Total in BTC (amount * price)
filled = Column(Float, default=0.0) # Amount filled
remaining = Column(Float, nullable=False) # Amount remaining to fill
status = Column(String(20), default='OPEN') # OPEN, PARTIALLY_FILLED, FILLED, CANCELLED
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relationships
user = relationship("User", back_populates="orders")
trades = relationship("Trade", back_populates="order")
__table_args__ = (
Index('idx_order_type_status', 'order_type', 'status'),
Index('idx_price_status', 'price', 'status'),
)
def __repr__(self):
return f"<Order(type='{self.order_type}', amount={self.amount}, price={self.price})>"
class Trade(Base):
"""Completed trade record"""
__tablename__ = "trades"
id = Column(Integer, primary_key=True, index=True)
buyer_id = Column(Integer, ForeignKey("users.id"), nullable=False)
seller_id = Column(Integer, ForeignKey("users.id"), nullable=False)
order_id = Column(Integer, ForeignKey("orders.id"), nullable=False)
amount = Column(Float, nullable=False) # Amount of AITBC traded
price = Column(Float, nullable=False) # Trade price in BTC
total = Column(Float, nullable=False) # Total value in BTC
trade_hash = Column(String(100), unique=True, nullable=False) # Blockchain transaction hash
created_at = Column(DateTime, default=datetime.utcnow)
# Relationships
buyer = relationship("User", back_populates="trades", foreign_keys=[buyer_id])
seller = relationship("User", foreign_keys=[seller_id])
order = relationship("Order", back_populates="trades")
__table_args__ = (
Index('idx_created_at', 'created_at'),
Index('idx_price', 'price'),
)
def __repr__(self):
return f"<Trade(amount={self.amount}, price={self.price}, total={self.total})>"
class Balance(Base):
"""User balance tracking"""
__tablename__ = "balances"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.id"), unique=True, nullable=False)
btc_balance = Column(Float, default=0.0)
aitbc_balance = Column(Float, default=0.0)
btc_locked = Column(Float, default=0.0) # Locked in open orders
aitbc_locked = Column(Float, default=0.0) # Locked in open orders
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relationship
user = relationship("User")
def __repr__(self):
return f"<Balance(btc={self.btc_balance}, aitbc={self.aitbc_balance})>"

View File

@@ -1,20 +0,0 @@
# Exchange API Routes - Add this to the existing nginx config
# Exchange API Routes
location /api/trades/ {
proxy_pass http://127.0.0.1:3003/api/trades/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
}
location /api/orders {
proxy_pass http://127.0.0.1:3003/api/orders;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
}

View File

@@ -1,8 +0,0 @@
# AITBC Trade Exchange Requirements
# Compatible with Python 3.13+
fastapi>=0.111.0
uvicorn[standard]>=0.30.0
sqlalchemy>=2.0.30
pydantic>=2.7.0
python-multipart>=0.0.6

View File

@@ -1,250 +0,0 @@
#!/usr/bin/env python3
"""Migration script from SQLite to PostgreSQL for AITBC Exchange"""
import os
import sys
from pathlib import Path
# Add the src directory to the path
sys.path.insert(0, str(Path(__file__).parent / "src"))
import sqlite3
import psycopg2
from psycopg2.extras import RealDictCursor
from datetime import datetime
from decimal import Decimal
# Database configurations
SQLITE_DB = "exchange.db"
PG_CONFIG = {
"host": "localhost",
"database": "aitbc_exchange",
"user": "aitbc_user",
"password": "aitbc_password",
"port": 5432
}
def create_pg_schema():
"""Create PostgreSQL schema with optimized types"""
conn = psycopg2.connect(**PG_CONFIG)
cursor = conn.cursor()
print("Creating PostgreSQL schema...")
# Drop existing tables
cursor.execute("DROP TABLE IF EXISTS trades CASCADE")
cursor.execute("DROP TABLE IF EXISTS orders CASCADE")
# Create trades table with proper types
cursor.execute("""
CREATE TABLE trades (
id SERIAL PRIMARY KEY,
amount NUMERIC(20, 8) NOT NULL,
price NUMERIC(20, 8) NOT NULL,
total NUMERIC(20, 8) NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
tx_hash VARCHAR(66),
maker_address VARCHAR(66),
taker_address VARCHAR(66)
)
""")
# Create orders table with proper types
cursor.execute("""
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
order_type VARCHAR(4) NOT NULL CHECK (order_type IN ('BUY', 'SELL')),
amount NUMERIC(20, 8) NOT NULL,
price NUMERIC(20, 8) NOT NULL,
total NUMERIC(20, 8) NOT NULL,
remaining NUMERIC(20, 8) NOT NULL,
filled NUMERIC(20, 8) DEFAULT 0,
status VARCHAR(20) DEFAULT 'OPEN' CHECK (status IN ('OPEN', 'FILLED', 'CANCELLED')),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
user_address VARCHAR(66),
tx_hash VARCHAR(66)
)
""")
# Create indexes for performance
print("Creating indexes...")
cursor.execute("CREATE INDEX idx_trades_created_at ON trades(created_at DESC)")
cursor.execute("CREATE INDEX idx_trades_price ON trades(price)")
cursor.execute("CREATE INDEX idx_orders_type ON orders(order_type)")
cursor.execute("CREATE INDEX idx_orders_price ON orders(price)")
cursor.execute("CREATE INDEX idx_orders_status ON orders(status)")
cursor.execute("CREATE INDEX idx_orders_created_at ON orders(created_at DESC)")
cursor.execute("CREATE INDEX idx_orders_user ON orders(user_address)")
conn.commit()
conn.close()
print("✅ PostgreSQL schema created successfully!")
def migrate_data():
"""Migrate data from SQLite to PostgreSQL"""
print("\nStarting data migration...")
# Connect to SQLite
sqlite_conn = sqlite3.connect(SQLITE_DB)
sqlite_conn.row_factory = sqlite3.Row
sqlite_cursor = sqlite_conn.cursor()
# Connect to PostgreSQL
pg_conn = psycopg2.connect(**PG_CONFIG)
pg_cursor = pg_conn.cursor()
# Migrate trades
print("Migrating trades...")
sqlite_cursor.execute("SELECT * FROM trades")
trades = sqlite_cursor.fetchall()
trades_count = 0
for trade in trades:
pg_cursor.execute("""
INSERT INTO trades (amount, price, total, created_at, tx_hash, maker_address, taker_address)
VALUES (%s, %s, %s, %s, %s, %s, %s)
""", (
Decimal(str(trade['amount'])),
Decimal(str(trade['price'])),
Decimal(str(trade['total'])),
trade['created_at'],
trade.get('tx_hash'),
trade.get('maker_address'),
trade.get('taker_address')
))
trades_count += 1
# Migrate orders
print("Migrating orders...")
sqlite_cursor.execute("SELECT * FROM orders")
orders = sqlite_cursor.fetchall()
orders_count = 0
for order in orders:
pg_cursor.execute("""
INSERT INTO orders (order_type, amount, price, total, remaining, filled, status,
created_at, updated_at, user_address, tx_hash)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
""", (
order['order_type'],
Decimal(str(order['amount'])),
Decimal(str(order['price'])),
Decimal(str(order['total'])),
Decimal(str(order['remaining'])),
Decimal(str(order['filled'])),
order['status'],
order['created_at'],
order['updated_at'],
order.get('user_address'),
order.get('tx_hash')
))
orders_count += 1
pg_conn.commit()
print(f"\n✅ Migration complete!")
print(f" - Migrated {trades_count} trades")
print(f" - Migrated {orders_count} orders")
sqlite_conn.close()
pg_conn.close()
def update_exchange_config():
"""Update exchange configuration to use PostgreSQL"""
config_file = Path("simple_exchange_api.py")
if not config_file.exists():
print("❌ simple_exchange_api.py not found!")
return
print("\nUpdating exchange configuration...")
# Read the current file
content = config_file.read_text()
# Add PostgreSQL configuration
pg_config = """
# PostgreSQL Configuration
PG_CONFIG = {
"host": "localhost",
"database": "aitbc_exchange",
"user": "aitbc_user",
"password": "aitbc_password",
"port": 5432
}
def get_pg_connection():
\"\"\"Get PostgreSQL connection\"\"\"
return psycopg2.connect(**PG_CONFIG)
"""
# Replace SQLite init with PostgreSQL
new_init = """
def init_db():
\"\"\"Initialize PostgreSQL database\"\"\"
try:
conn = get_pg_connection()
cursor = conn.cursor()
# Check if tables exist
cursor.execute(\"\"\"
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_name IN ('trades', 'orders')
)
\"\"\")
if not cursor.fetchone()[0]:
print("Creating PostgreSQL tables...")
create_pg_schema()
conn.close()
except Exception as e:
print(f"Database initialization error: {e}")
"""
# Update the file
content = content.replace("import sqlite3", "import sqlite3\nimport psycopg2\nfrom psycopg2.extras import RealDictCursor")
content = content.replace("def init_db():", new_init)
content = content.replace("conn = sqlite3.connect('exchange.db')", "conn = get_pg_connection()")
content = content.replace("cursor = conn.cursor()", "cursor = conn.cursor(cursor_factory=RealDictCursor)")
# Write back
config_file.write_text(content)
print("✅ Configuration updated to use PostgreSQL!")
def main():
"""Main migration process"""
print("=" * 60)
print("AITBC Exchange SQLite to PostgreSQL Migration")
print("=" * 60)
# Check if SQLite DB exists
if not Path(SQLITE_DB).exists():
print(f"❌ SQLite database '{SQLITE_DB}' not found!")
return
# Create PostgreSQL schema
create_pg_schema()
# Migrate data
migrate_data()
# Update configuration
update_exchange_config()
print("\n" + "=" * 60)
print("Migration completed successfully!")
print("=" * 60)
print("\nNext steps:")
print("1. Install PostgreSQL dependencies: pip install psycopg2-binary")
print("2. Restart the exchange service")
print("3. Verify data integrity")
print("4. Backup and remove SQLite database")
if __name__ == "__main__":
main()

View File

@@ -1,54 +0,0 @@
#!/usr/bin/env python3
"""Seed initial market price for the exchange"""
import sqlite3
from datetime import datetime
def seed_initial_price():
"""Create initial trades to establish market price"""
conn = sqlite3.connect('exchange.db')
cursor = conn.cursor()
# Create some initial trades at different price points
initial_trades = [
(1000, 0.00001), # 1000 AITBC at 0.00001 BTC each
(500, 0.0000105), # 500 AITBC at slightly higher
(750, 0.0000095), # 750 AITBC at slightly lower
(2000, 0.00001), # 2000 AITBC at base price
(1500, 0.000011), # 1500 AITBC at higher price
]
for amount, price in initial_trades:
total = amount * price
cursor.execute('''
INSERT INTO trades (amount, price, total, created_at)
VALUES (?, ?, ?, ?)
''', (amount, price, total, datetime.utcnow()))
# Also create some initial orders for liquidity
initial_orders = [
('BUY', 5000, 0.0000095), # Buy order
('BUY', 3000, 0.00001), # Buy order
('SELL', 2000, 0.0000105), # Sell order
('SELL', 4000, 0.000011), # Sell order
]
for order_type, amount, price in initial_orders:
total = amount * price
cursor.execute('''
INSERT INTO orders (order_type, amount, price, total, remaining, user_address)
VALUES (?, ?, ?, ?, ?, ?)
''', (order_type, amount, price, total, amount, 'aitbcexchange00000000000000000000000000000000'))
conn.commit()
conn.close()
print("✅ Seeded initial market data:")
print(f" - Created {len(initial_trades)} historical trades")
print(f" - Created {len(initial_orders)} liquidity orders")
print(f" - Initial price range: 0.0000095 - 0.000011 BTC")
print(" The exchange should now show real prices!")
if __name__ == "__main__":
seed_initial_price()

View File

@@ -1,37 +0,0 @@
#!/bin/bash
echo "=== PostgreSQL Setup for AITBC Exchange ==="
echo ""
# Install PostgreSQL if not already installed
if ! command -v psql &> /dev/null; then
echo "Installing PostgreSQL..."
sudo apt-get update
sudo apt-get install -y postgresql postgresql-contrib
fi
# Start PostgreSQL service
sudo systemctl start postgresql
sudo systemctl enable postgresql
# Create database and user
echo "Creating database and user..."
sudo -u postgres psql -c "CREATE DATABASE aitbc_exchange;"
sudo -u postgres psql -c "CREATE USER aitbc_user WITH PASSWORD 'aitbc_password';"
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE aitbc_exchange TO aitbc_user;"
# Test connection
echo "Testing connection..."
sudo -u postgres psql -c "\l" | grep aitbc_exchange
echo ""
echo "✅ PostgreSQL setup complete!"
echo ""
echo "Connection details:"
echo " Host: localhost"
echo " Port: 5432"
echo " Database: aitbc_exchange"
echo " User: aitbc_user"
echo " Password: aitbc_password"
echo ""
echo "You can now run the migration script."

View File

@@ -1,54 +0,0 @@
#!/usr/bin/env python3
"""
Simple HTTP server for the AITBC Trade Exchange
"""
import os
import sys
from http.server import HTTPServer, SimpleHTTPRequestHandler
import argparse
class CORSHTTPRequestHandler(SimpleHTTPRequestHandler):
def end_headers(self):
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
self.send_header('Access-Control-Allow-Headers', 'Content-Type, X-Api-Key')
super().end_headers()
def do_OPTIONS(self):
self.send_response(200)
self.end_headers()
def run_server(port=3002, directory=None):
"""Run the HTTP server"""
if directory:
os.chdir(directory)
server_address = ('', port)
httpd = HTTPServer(server_address, CORSHTTPRequestHandler)
print(f"""
╔═══════════════════════════════════════╗
║ AITBC Trade Exchange Server ║
╠═══════════════════════════════════════╣
║ Server running at: ║
║ http://localhost:{port}
║ ║
║ Buy AITBC with Bitcoin! ║
║ Press Ctrl+C to stop ║
╚═══════════════════════════════════════╝
""")
try:
httpd.serve_forever()
except KeyboardInterrupt:
print("\nShutting down server...")
httpd.server_close()
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Run the AITBC Trade Exchange server')
parser.add_argument('--port', type=int, default=3002, help='Port to run the server on')
parser.add_argument('--dir', type=str, default='.', help='Directory to serve from')
args = parser.parse_args()
run_server(port=args.port, directory=args.dir)

View File

@@ -1,608 +0,0 @@
#!/usr/bin/env python3
"""
Simple FastAPI backend for the AITBC Trade Exchange (Python 3.13 compatible)
"""
import sqlite3
import json
from datetime import datetime
from http.server import HTTPServer, BaseHTTPRequestHandler
import urllib.parse
import random
# Database setup
def init_db():
"""Initialize SQLite database"""
conn = sqlite3.connect('exchange.db')
cursor = conn.cursor()
# Create tables
cursor.execute('''
CREATE TABLE IF NOT EXISTS trades (
id INTEGER PRIMARY KEY AUTOINCREMENT,
amount REAL NOT NULL,
price REAL NOT NULL,
total REAL NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
cursor.execute('''
CREATE TABLE IF NOT EXISTS orders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
order_type TEXT NOT NULL CHECK(order_type IN ('BUY', 'SELL')),
amount REAL NOT NULL,
price REAL NOT NULL,
total REAL NOT NULL,
filled REAL DEFAULT 0,
remaining REAL NOT NULL,
status TEXT DEFAULT 'open' CHECK(status IN ('open', 'filled', 'cancelled')),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
user_address TEXT,
tx_hash TEXT
)
''')
# Add columns if they don't exist (for existing databases)
try:
cursor.execute('ALTER TABLE orders ADD COLUMN user_address TEXT')
except:
pass
try:
cursor.execute('ALTER TABLE orders ADD COLUMN tx_hash TEXT')
except:
pass
conn.commit()
conn.close()
def create_mock_trades():
"""Create some mock trades"""
conn = sqlite3.connect('exchange.db')
cursor = conn.cursor()
# Check if we have trades
cursor.execute('SELECT COUNT(*) FROM trades')
if cursor.fetchone()[0] > 0:
conn.close()
return
# Create mock trades
now = datetime.utcnow()
for i in range(20):
amount = random.uniform(10, 500)
price = random.uniform(0.000009, 0.000012)
total = amount * price
created_at = now - timedelta(minutes=random.randint(0, 60))
cursor.execute('''
INSERT INTO trades (amount, price, total, created_at)
VALUES (?, ?, ?, ?)
''', (amount, price, total, created_at))
conn.commit()
conn.close()
class ExchangeAPIHandler(BaseHTTPRequestHandler):
def do_GET(self):
"""Handle GET requests"""
if self.path == '/api/health':
self.health_check()
elif self.path.startswith('/api/trades/recent'):
parsed = urllib.parse.urlparse(self.path)
self.get_recent_trades(parsed)
elif self.path.startswith('/api/orders/orderbook'):
self.get_orderbook()
elif self.path.startswith('/api/wallet/balance'):
self.handle_wallet_balance()
elif self.path == '/api/total-supply':
self.handle_total_supply()
elif self.path == '/api/treasury-balance':
self.handle_treasury_balance()
else:
self.send_error(404, "Not Found")
def do_POST(self):
"""Handle POST requests"""
if self.path == '/api/orders':
self.handle_place_order()
elif self.path == '/api/wallet/connect':
self.handle_wallet_connect()
else:
self.send_error(404, "Not Found")
def get_recent_trades(self, parsed):
"""Get recent trades"""
query = urllib.parse.parse_qs(parsed.query)
limit = int(query.get('limit', [20])[0])
conn = sqlite3.connect('exchange.db')
cursor = conn.cursor()
cursor.execute('''
SELECT id, amount, price, total, created_at
FROM trades
ORDER BY created_at DESC
LIMIT ?
''', (limit,))
trades = []
for row in cursor.fetchall():
trades.append({
'id': row[0],
'amount': row[1],
'price': row[2],
'total': row[3],
'created_at': row[4]
})
conn.close()
self.send_json_response(trades)
def get_orderbook(self):
"""Get order book"""
conn = sqlite3.connect('exchange.db')
cursor = conn.cursor()
# Get sell orders
cursor.execute('''
SELECT id, order_type, amount, price, total, filled, remaining, status, created_at
FROM orders
WHERE order_type = 'SELL' AND status = 'OPEN'
ORDER BY price ASC
LIMIT 20
''')
sells = []
for row in cursor.fetchall():
sells.append({
'id': row[0],
'order_type': row[1],
'amount': row[2],
'price': row[3],
'total': row[4],
'filled': row[5],
'remaining': row[6],
'status': row[7],
'created_at': row[8]
})
# Get buy orders
cursor.execute('''
SELECT id, order_type, amount, price, total, filled, remaining, status, created_at
FROM orders
WHERE order_type = 'BUY' AND status = 'OPEN'
ORDER BY price DESC
LIMIT 20
''')
buys = []
for row in cursor.fetchall():
buys.append({
'id': row[0],
'order_type': row[1],
'amount': row[2],
'price': row[3],
'total': row[4],
'filled': row[5],
'remaining': row[6],
'status': row[7],
'created_at': row[8]
})
conn.close()
self.send_json_response({'buys': buys, 'sells': sells})
def handle_place_order(self):
"""Place a new order on the blockchain"""
content_length = int(self.headers.get('Content-Length', 0))
post_data = self.rfile.read(content_length)
try:
data = json.loads(post_data.decode('utf-8'))
order_type = data.get('order_type')
amount = data.get('amount')
price = data.get('price')
user_address = data.get('user_address')
if not all([order_type, amount, price, user_address]):
self.send_error(400, "Missing required fields")
return
if order_type not in ['BUY', 'SELL']:
self.send_error(400, "Invalid order type")
return
# Create order transaction on blockchain
try:
import urllib.request
import urllib.parse
# Prepare transaction data
tx_data = {
"from": user_address,
"type": "ORDER",
"order_type": order_type,
"amount": str(amount),
"price": str(price),
"nonce": 0 # Would get actual nonce from wallet
}
# Send transaction to blockchain
tx_url = "http://localhost:9080/rpc/sendTx"
encoded_data = urllib.parse.urlencode(tx_data).encode('utf-8')
req = urllib.request.Request(
tx_url,
data=encoded_data,
headers={'Content-Type': 'application/x-www-form-urlencoded'}
)
with urllib.request.urlopen(req) as response:
tx_result = json.loads(response.read().decode())
# Store order in local database for orderbook
total = amount * price
conn = sqlite3.connect('exchange.db')
cursor = conn.cursor()
cursor.execute('''
INSERT INTO orders (order_type, amount, price, total, remaining, user_address, tx_hash)
VALUES (?, ?, ?, ?, ?, ?, ?)
''', (order_type, amount, price, total, amount, user_address, tx_result.get('tx_hash', '')))
order_id = cursor.lastrowid
conn.commit()
# Get the created order
cursor.execute('SELECT * FROM orders WHERE id = ?', (order_id,))
row = cursor.fetchone()
order = {
'id': row[0],
'order_type': row[1],
'amount': row[2],
'price': row[3],
'total': row[4],
'filled': row[5],
'remaining': row[6],
'status': row[7],
'created_at': row[8],
'user_address': row[9],
'tx_hash': row[10]
}
conn.close()
# Try to match orders
self.match_orders(order)
self.send_json_response(order)
except Exception as e:
# Fallback to database-only if blockchain is down
total = amount * price
conn = sqlite3.connect('exchange.db')
cursor = conn.cursor()
cursor.execute('''
INSERT INTO orders (order_type, amount, price, total, remaining, user_address)
VALUES (?, ?, ?, ?, ?, ?)
''', (order_type, amount, price, total, amount, user_address))
order_id = cursor.lastrowid
conn.commit()
# Get the created order
cursor.execute('SELECT * FROM orders WHERE id = ?', (order_id,))
row = cursor.fetchone()
order = {
'id': row[0],
'order_type': row[1],
'amount': row[2],
'price': row[3],
'total': row[4],
'filled': row[5],
'remaining': row[6],
'status': row[7],
'created_at': row[8],
'user_address': row[9] if len(row) > 9 else None
}
conn.close()
# Try to match orders
self.match_orders(order)
self.send_json_response(order)
except Exception as e:
# Fallback to hardcoded values if blockchain is down
self.send_json_response({
"total_supply": "21000000",
"circulating_supply": "1000000",
"treasury_balance": "0",
"source": "fallback",
"error": str(e)
})
# Match with sell orders
cursor.execute('''
SELECT * FROM orders
WHERE order_type = 'SELL' AND status = 'OPEN' AND price <= ?
ORDER BY price ASC, created_at ASC
''', (new_order['price'],))
else:
# Match with buy orders
cursor.execute('''
SELECT * FROM orders
WHERE order_type = 'BUY' AND status = 'OPEN' AND price >= ?
ORDER BY price DESC, created_at ASC
''', (new_order['price'],))
matching_orders = cursor.fetchall()
for order_row in matching_orders:
if new_order['remaining'] <= 0:
break
# Calculate trade amount
trade_amount = min(new_order['remaining'], order_row[6]) # remaining
if trade_amount > 0:
# Create trade on blockchain
try:
import urllib.request
import urllib.parse
trade_price = order_row[3] # Use the existing order's price
trade_data = {
"type": "TRADE",
"buy_order": new_order['id'] if new_order['order_type'] == 'BUY' else order_row[0],
"sell_order": order_row[0] if new_order['order_type'] == 'BUY' else new_order['id'],
"amount": str(trade_amount),
"price": str(trade_price)
}
# Record trade in database
cursor.execute('''
INSERT INTO trades (amount, price, total)
VALUES (?, ?, ?)
''', (trade_amount, trade_price, trade_amount * trade_price))
# Update orders
new_order['remaining'] -= trade_amount
new_order['filled'] = new_order.get('filled', 0) + trade_amount
# Update matching order
new_remaining = order_row[6] - trade_amount
cursor.execute('''
UPDATE orders SET remaining = ?, filled = filled + ?
WHERE id = ?
''', (new_remaining, trade_amount, order_row[0]))
# Close order if fully filled
if new_remaining <= 0:
cursor.execute('''
UPDATE orders SET status = 'FILLED' WHERE id = ?
''', (order_row[0],))
except Exception as e:
print(f"Failed to create trade on blockchain: {e}")
# Still record the trade in database
cursor.execute('''
INSERT INTO trades (amount, price, total)
VALUES (?, ?, ?)
''', (trade_amount, order_row[3], trade_amount * order_row[3]))
# Update new order in database
if new_order['remaining'] <= 0:
cursor.execute('''
UPDATE orders SET status = 'FILLED', remaining = 0, filled = ?
WHERE id = ?
''', (new_order['filled'], new_order['id']))
else:
cursor.execute('''
UPDATE orders SET remaining = ?, filled = ?
WHERE id = ?
''', (new_order['remaining'], new_order['filled'], new_order['id']))
conn.commit()
conn.close()
def handle_treasury_balance(self):
"""Get exchange treasury balance from blockchain"""
try:
import urllib.request
import json
# Treasury address from genesis
treasury_address = "aitbcexchange00000000000000000000000000000000"
blockchain_url = f"http://localhost:9080/rpc/getBalance/{treasury_address}"
try:
with urllib.request.urlopen(blockchain_url) as response:
balance_data = json.loads(response.read().decode())
treasury_balance = balance_data.get('balance', 0)
self.send_json_response({
"address": treasury_address,
"balance": str(treasury_balance),
"available_for_sale": str(treasury_balance), # All treasury tokens available
"source": "blockchain"
})
except Exception as e:
# If blockchain query fails, show the genesis amount
self.send_json_response({
"address": treasury_address,
"balance": "10000000000000", # 10 million in smallest units
"available_for_sale": "10000000000000",
"source": "genesis",
"note": "Genesis amount - blockchain may need restart"
})
except Exception as e:
self.send_error(500, str(e))
def health_check(self):
"""Health check"""
self.send_json_response({
'status': 'ok',
'timestamp': datetime.utcnow().isoformat()
})
def handle_wallet_balance(self):
"""Handle wallet balance request"""
from urllib.parse import urlparse, parse_qs
parsed = urlparse(self.path)
params = parse_qs(parsed.query)
address = params.get('address', [''])[0]
if not address:
self.send_json_response({
"btc": "0.00000000",
"aitbc": "0.00",
"address": "unknown"
})
return
try:
# Query real blockchain for balance
import urllib.request
import json
# Get AITBC balance from blockchain
blockchain_url = f"http://localhost:9080/rpc/getBalance/{address}"
with urllib.request.urlopen(blockchain_url) as response:
balance_data = json.loads(response.read().decode())
# For BTC, we'll query a Bitcoin API (simplified for now)
# In production, you'd integrate with a real Bitcoin node API
btc_balance = "0.00000000" # Placeholder - would query real Bitcoin network
self.send_json_response({
"btc": btc_balance,
"aitbc": str(balance_data.get('balance', 0)),
"address": address,
"nonce": balance_data.get('nonce', 0)
})
except Exception as e:
# Fallback to error if blockchain is down
self.send_json_response({
"btc": "0.00000000",
"aitbc": "0.00",
"address": address,
"error": "Failed to fetch balance from blockchain"
})
def handle_wallet_connect(self):
"""Handle wallet connection request"""
import secrets
content_length = int(self.headers.get('Content-Length', 0))
post_data = self.rfile.read(content_length)
mock_address = "aitbc" + secrets.token_hex(20)
self.send_json_response({
"address": mock_address,
"status": "connected"
})
def send_json_response(self, data, status=200):
"""Send JSON response"""
self.send_response(status)
self.send_header('Content-Type', 'application/json')
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
self.send_header('Access-Control-Allow-Headers', 'Content-Type')
self.end_headers()
self.wfile.write(json.dumps(data, default=str).encode())
def do_OPTIONS(self):
"""Handle OPTIONS requests for CORS"""
self.send_response(200)
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
self.send_header('Access-Control-Allow-Headers', 'Content-Type')
self.end_headers()
class WalletAPIHandler(BaseHTTPRequestHandler):
"""Handle wallet API requests"""
def do_GET(self):
"""Handle GET requests"""
if self.path.startswith('/api/wallet/balance'):
# Parse address from query params
from urllib.parse import urlparse, parse_qs
parsed = urlparse(self.path)
params = parse_qs(parsed.query)
address = params.get('address', [''])[0]
# Return mock balance for now
self.send_json_response({
"btc": "0.12345678",
"aitbc": "1000.50",
"address": address or "unknown"
})
else:
self.send_error(404)
def do_POST(self):
"""Handle POST requests"""
if self.path == '/wallet/connect':
import secrets
mock_address = "aitbc" + secrets.token_hex(20)
self.send_json_response({
"address": mock_address,
"status": "connected"
})
else:
self.send_error(404)
def send_json_response(self, data, status=200):
"""Send JSON response"""
self.send_response(status)
self.send_header('Content-Type', 'application/json')
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
self.send_header('Access-Control-Allow-Headers', 'Content-Type')
self.end_headers()
self.wfile.write(json.dumps(data, default=str).encode())
def do_OPTIONS(self):
"""Handle OPTIONS requests for CORS"""
self.send_response(200)
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
self.send_header('Access-Control-Allow-Headers', 'Content-Type')
self.end_headers()
def run_server(port=8001):
"""Run the server"""
init_db()
# Removed mock trades - now using only real blockchain data
server = HTTPServer(('localhost', port), ExchangeAPIHandler)
print(f"""
╔═══════════════════════════════════════╗
║ AITBC Exchange API Server ║
╠═══════════════════════════════════════╣
║ Server running at: ║
║ http://localhost:{port}
║ ║
║ Real trading API active! ║
║ Press Ctrl+C to stop ║
╚═══════════════════════════════════════╝
""")
try:
server.serve_forever()
except KeyboardInterrupt:
print("\nShutting down server...")
server.server_close()
if __name__ == "__main__":
run_server()

View File

@@ -1,369 +0,0 @@
#!/usr/bin/env python3
"""AITBC Exchange API with PostgreSQL Support"""
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs
import json
import urllib.request
import psycopg2
from psycopg2.extras import RealDictCursor
from datetime import datetime
from decimal import Decimal
import random
# PostgreSQL Configuration
PG_CONFIG = {
"host": "localhost",
"database": "aitbc_exchange",
"user": "aitbc_user",
"password": "aitbc_password",
"port": 5432
}
def get_pg_connection():
"""Get PostgreSQL connection"""
return psycopg2.connect(**PG_CONFIG)
def init_db():
"""Initialize PostgreSQL database"""
try:
conn = get_pg_connection()
cursor = conn.cursor()
# Check if tables exist
cursor.execute("""
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_name IN ('trades', 'orders')
)
""")
if not cursor.fetchone()[0]:
print("Creating PostgreSQL tables...")
create_pg_schema()
conn.close()
except Exception as e:
print(f"Database initialization error: {e}")
def create_pg_schema():
"""Create PostgreSQL schema"""
conn = get_pg_connection()
cursor = conn.cursor()
# Create trades table
cursor.execute("""
CREATE TABLE trades (
id SERIAL PRIMARY KEY,
amount NUMERIC(20, 8) NOT NULL,
price NUMERIC(20, 8) NOT NULL,
total NUMERIC(20, 8) NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
tx_hash VARCHAR(66),
maker_address VARCHAR(66),
taker_address VARCHAR(66)
)
""")
# Create orders table
cursor.execute("""
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
order_type VARCHAR(4) NOT NULL CHECK (order_type IN ('BUY', 'SELL')),
amount NUMERIC(20, 8) NOT NULL,
price NUMERIC(20, 8) NOT NULL,
total NUMERIC(20, 8) NOT NULL,
remaining NUMERIC(20, 8) NOT NULL,
filled NUMERIC(20, 8) DEFAULT 0,
status VARCHAR(20) DEFAULT 'OPEN' CHECK (status IN ('OPEN', 'FILLED', 'CANCELLED')),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
user_address VARCHAR(66),
tx_hash VARCHAR(66)
)
""")
# Create indexes
cursor.execute("CREATE INDEX idx_trades_created_at ON trades(created_at DESC)")
cursor.execute("CREATE INDEX idx_orders_type ON orders(order_type)")
cursor.execute("CREATE INDEX idx_orders_price ON orders(price)")
cursor.execute("CREATE INDEX idx_orders_status ON orders(status)")
conn.commit()
conn.close()
class ExchangeAPIHandler(BaseHTTPRequestHandler):
def send_json_response(self, data, status=200):
"""Send JSON response"""
self.send_response(status)
self.send_header('Content-Type', 'application/json')
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()
self.wfile.write(json.dumps(data, default=str).encode())
def do_OPTIONS(self):
"""Handle OPTIONS requests for CORS"""
self.send_response(200)
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
self.send_header('Access-Control-Allow-Headers', 'Content-Type')
self.end_headers()
def do_GET(self):
"""Handle GET requests"""
if self.path == '/api/health':
self.health_check()
elif self.path.startswith('/api/trades/recent'):
parsed = urlparse(self.path)
self.get_recent_trades(parsed)
elif self.path.startswith('/api/orders/orderbook'):
self.get_orderbook()
elif self.path.startswith('/api/wallet/balance'):
self.handle_wallet_balance()
elif self.path == '/api/treasury-balance':
self.handle_treasury_balance()
else:
self.send_error(404)
def do_POST(self):
"""Handle POST requests"""
if self.path == '/api/orders':
self.handle_place_order()
elif self.path == '/api/wallet/connect':
self.handle_wallet_connect()
else:
self.send_error(404)
def health_check(self):
"""Health check"""
try:
conn = get_pg_connection()
cursor = conn.cursor()
cursor.execute("SELECT 1")
cursor.close()
conn.close()
self.send_json_response({
'status': 'ok',
'database': 'postgresql',
'timestamp': datetime.utcnow().isoformat()
})
except Exception as e:
self.send_json_response({
'status': 'error',
'error': str(e)
}, 500)
def get_recent_trades(self, parsed):
"""Get recent trades from PostgreSQL"""
try:
conn = get_pg_connection()
cursor = conn.cursor(cursor_factory=RealDictCursor)
# Get limit from query params
params = parse_qs(parsed.query)
limit = int(params.get('limit', [10])[0])
cursor.execute("""
SELECT * FROM trades
ORDER BY created_at DESC
LIMIT %s
""", (limit,))
trades = []
for row in cursor.fetchall():
trades.append({
'id': row['id'],
'amount': float(row['amount']),
'price': float(row['price']),
'total': float(row['total']),
'created_at': row['created_at'].isoformat(),
'tx_hash': row['tx_hash']
})
cursor.close()
conn.close()
self.send_json_response(trades)
except Exception as e:
self.send_error(500, str(e))
def get_orderbook(self):
"""Get order book from PostgreSQL"""
try:
conn = get_pg_connection()
cursor = conn.cursor(cursor_factory=RealDictCursor)
# Get sell orders (asks)
cursor.execute("""
SELECT * FROM orders
WHERE order_type = 'SELL' AND status = 'OPEN' AND remaining > 0
ORDER BY price ASC, created_at ASC
LIMIT 20
""")
sells = []
for row in cursor.fetchall():
sells.append({
'id': row['id'],
'amount': float(row['remaining']),
'price': float(row['price']),
'total': float(row['remaining'] * row['price'])
})
# Get buy orders (bids)
cursor.execute("""
SELECT * FROM orders
WHERE order_type = 'BUY' AND status = 'OPEN' AND remaining > 0
ORDER BY price DESC, created_at ASC
LIMIT 20
""")
buys = []
for row in cursor.fetchall():
buys.append({
'id': row['id'],
'amount': float(row['remaining']),
'price': float(row['price']),
'total': float(row['remaining'] * row['price'])
})
cursor.close()
conn.close()
self.send_json_response({
'buys': buys,
'sells': sells
})
except Exception as e:
self.send_error(500, str(e))
def handle_wallet_connect(self):
"""Handle wallet connection"""
# Generate a mock wallet address for demo
address = f"aitbc{''.join(random.choices('0123456789abcdef', k=64))}"
self.send_json_response({
"address": address,
"status": "connected"
})
def handle_wallet_balance(self):
"""Handle wallet balance request"""
from urllib.parse import urlparse, parse_qs
parsed = urlparse(self.path)
params = parse_qs(parsed.query)
address = params.get('address', [''])[0]
try:
# Query blockchain for balance
blockchain_url = f"http://localhost:9080/rpc/getBalance/{address}"
with urllib.request.urlopen(blockchain_url) as response:
balance_data = json.loads(response.read().decode())
aitbc_balance = balance_data.get('balance', 0)
nonce = balance_data.get('nonce', 0)
except:
aitbc_balance = 0
nonce = 0
self.send_json_response({
"btc": "0.00000000",
"aitbc": str(aitbc_balance),
"address": address or "unknown",
"nonce": nonce
})
def handle_treasury_balance(self):
"""Get exchange treasury balance"""
try:
treasury_address = "aitbcexchange00000000000000000000000000000000"
blockchain_url = f"http://localhost:9080/rpc/getBalance/{treasury_address}"
with urllib.request.urlopen(blockchain_url) as response:
balance_data = json.loads(response.read().decode())
treasury_balance = balance_data.get('balance', 0)
self.send_json_response({
"address": treasury_address,
"balance": str(treasury_balance),
"available_for_sale": str(treasury_balance),
"source": "blockchain"
})
except Exception as e:
self.send_error(500, str(e))
def handle_place_order(self):
"""Handle placing an order"""
try:
content_length = int(self.headers['Content-Length'])
post_data = self.rfile.read(content_length)
order_data = json.loads(post_data.decode())
# Validate order data
required_fields = ['order_type', 'amount', 'price']
for field in required_fields:
if field not in order_data:
self.send_json_response({
"error": f"Missing required field: {field}"
}, 400)
return
# Insert order into PostgreSQL
conn = get_pg_connection()
cursor = conn.cursor()
cursor.execute("""
INSERT INTO orders (order_type, amount, price, total, remaining, user_address)
VALUES (%s, %s, %s, %s, %s, %s)
RETURNING id, created_at
""", (
order_data['order_type'],
Decimal(str(order_data['amount'])),
Decimal(str(order_data['price'])),
Decimal(str(order_data['amount'] * order_data['price'])),
Decimal(str(order_data['amount'])),
order_data.get('user_address', 'aitbcexchange00000000000000000000000000000000')
))
result = cursor.fetchone()
order_id = result[0]
created_at = result[1]
conn.commit()
cursor.close()
conn.close()
self.send_json_response({
"id": order_id,
"order_type": order_data['order_type'],
"amount": order_data['amount'],
"price": order_data['price'],
"status": "OPEN",
"created_at": created_at.isoformat()
})
except Exception as e:
self.send_json_response({
"error": str(e)
}, 500)
def run_server(port=3003):
"""Run the server"""
init_db()
server = HTTPServer(('localhost', port), ExchangeAPIHandler)
print(f"""
╔═══════════════════════════════════════╗
║ AITBC Exchange API Server ║
╠═══════════════════════════════════════╣
║ Server running at: ║
║ http://localhost:{port}
║ ║
║ Database: PostgreSQL ║
║ Real trading API active! ║
╚═══════════════════════════════════════╝
""")
server.serve_forever()
if __name__ == "__main__":
run_server()

View File

@@ -1,388 +0,0 @@
/* Production CSS for AITBC Trade Exchange */
/* Dark mode variables */
:root {
--bg-primary: #ffffff;
--bg-secondary: #f9fafb;
--bg-tertiary: #f3f4f6;
--text-primary: #111827;
--text-secondary: #6b7280;
--text-tertiary: #9ca3af;
--border-color: #e5e7eb;
--primary-50: #eff6ff;
--primary-500: #3b82f6;
--primary-600: #2563eb;
--primary-700: #1d4ed8;
}
.dark {
--bg-primary: #1f2937;
--bg-secondary: #111827;
--bg-tertiary: #374151;
--text-primary: #f9fafb;
--text-secondary: #d1d5db;
--text-tertiary: #9ca3af;
--border-color: #4b5563;
}
/* Base styles */
* {
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background-color: var(--bg-secondary);
color: var(--text-primary);
margin: 0;
padding: 0;
}
/* Layout */
.h-full {
height: 100%;
}
.min-h-full {
min-height: 100%;
}
.max-w-7xl {
max-width: 1280px;
}
.mx-auto {
margin-left: auto;
margin-right: auto;
}
.px-4 {
padding-left: 1rem;
padding-right: 1rem;
}
.py-8 {
padding-top: 2rem;
padding-bottom: 2rem;
}
/* Navigation */
nav {
background-color: var(--bg-primary);
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
}
nav > div {
display: flex;
justify-content: space-between;
height: 4rem;
align-items: center;
}
nav .flex {
display: flex;
}
nav .items-center {
align-items: center;
}
nav .space-x-8 > * + * {
margin-left: 2rem;
}
nav .space-x-4 > * + * {
margin-left: 1rem;
}
nav .text-xl {
font-size: 1.25rem;
line-height: 1.75rem;
}
nav .font-bold {
font-weight: 700;
}
nav .text-sm {
font-size: 0.875rem;
line-height: 1.25rem;
}
nav .font-medium {
font-weight: 500;
}
/* Links */
a {
color: inherit;
text-decoration: none;
}
a:hover {
color: var(--primary-600);
}
/* Cards */
.bg-white {
background-color: var(--bg-primary);
}
.dark .bg-white {
background-color: var(--bg-primary);
}
.rounded-lg {
border-radius: 0.5rem;
}
.shadow {
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
}
.p-4 {
padding: 1rem;
}
.p-6 {
padding: 1.5rem;
}
.mb-6 {
margin-bottom: 1.5rem;
}
/* Grid */
.grid {
display: grid;
}
.grid-cols-1 {
grid-template-columns: repeat(1, minmax(0, 1fr));
}
.grid-cols-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.gap-6 {
gap: 1.5rem;
}
@media (min-width: 1024px) {
.lg\:grid-cols-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
/* Typography */
.text-sm {
font-size: 0.875rem;
line-height: 1.25rem;
}
.text-2xl {
font-size: 1.5rem;
line-height: 2rem;
}
.text-lg {
font-size: 1.125rem;
line-height: 1.75rem;
}
.font-semibold {
font-weight: 600;
}
.font-bold {
font-weight: 700;
}
.text-gray-600 {
color: var(--text-secondary);
}
.text-gray-900 {
color: var(--text-primary);
}
.text-gray-500 {
color: var(--text-tertiary);
}
.dark .text-gray-300 {
color: #d1d5db;
}
.dark .text-gray-400 {
color: #9ca3af;
}
.dark .text-white {
color: #ffffff;
}
/* Buttons */
button {
cursor: pointer;
border: none;
border-radius: 0.375rem;
padding: 0.5rem 1rem;
font-size: 0.875rem;
font-weight: 500;
transition: all 0.15s ease-in-out;
}
.bg-primary-600 {
background-color: var(--primary-600);
}
.bg-primary-600:hover {
background-color: var(--primary-700);
}
.text-white {
color: #ffffff;
}
.bg-green-600 {
background-color: #059669;
}
.bg-green-600:hover {
background-color: #047857;
}
.bg-red-600 {
background-color: #dc2626;
}
.bg-red-600:hover {
background-color: #b91c1c;
}
.bg-gray-100 {
background-color: var(--bg-tertiary);
}
/* Forms */
input {
width: 100%;
padding: 0.5rem 0.75rem;
border: 1px solid var(--border-color);
border-radius: 0.375rem;
background-color: var(--bg-primary);
color: var(--text-primary);
}
input:focus {
outline: none;
border-color: var(--primary-500);
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
.dark input {
background-color: var(--bg-tertiary);
border-color: var(--border-color);
}
.dark input:focus {
border-color: var(--primary-500);
}
/* Tables */
.space-y-2 > * + * {
margin-top: 0.5rem;
}
.space-y-1 > * + * {
margin-top: 0.25rem;
}
.justify-between {
justify-content: space-between;
}
.text-right {
text-align: right;
}
.text-green-600 {
color: #059669;
}
.text-red-600 {
color: #dc2626;
}
/* Borders */
.border-b {
border-bottom: 1px solid var(--border-color);
}
.border-t {
border-top: 1px solid var(--border-color);
}
/* Width */
.w-full {
width: 100%;
}
/* Flex */
.flex {
display: flex;
}
.flex-1 {
flex: 1 1 0%;
}
/* Colors */
.bg-gray-50 {
background-color: var(--bg-secondary);
}
.dark .bg-gray-600 {
background-color: #4b5563;
}
.dark .bg-gray-700 {
background-color: #374151;
}
/* Dark mode toggle */
.p-2 {
padding: 0.5rem;
}
.rounded-md {
border-radius: 0.375rem;
}
/* Hover states */
.hover\:text-gray-700:hover {
color: var(--text-primary);
}
.dark .hover\:text-gray-200:hover {
color: #e5e7eb;
}
/* Order book colors */
.text-red-600 {
color: #dc2626;
}
.dark .text-red-400 {
color: #f87171;
}
.text-green-600 {
color: #059669;
}
.dark .text-green-400 {
color: #4ade80;
}

View File

@@ -1,58 +0,0 @@
// Add this function to index.real.html to update price ticker with real data
async function updatePriceTicker() {
try {
// Get recent trades to calculate price statistics
const response = await fetch(`${EXCHANGE_API_BASE}/api/trades/recent?limit=100`);
if (!response.ok) return;
const trades = await response.json();
if (trades.length === 0) {
console.log('No trades to calculate price from');
return;
}
// Calculate 24h volume (sum of all trades in last 24h)
const now = new Date();
const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
const recentTrades = trades.filter(trade =>
new Date(trade.created_at) > yesterday
);
const totalVolume = recentTrades.reduce((sum, trade) => sum + trade.amount, 0);
const totalBTC = recentTrades.reduce((sum, trade) => sum + trade.total, 0);
// Calculate current price (price of last trade)
const currentPrice = trades[0].price;
// Calculate 24h high/low
const prices = recentTrades.map(t => t.price);
const high24h = Math.max(...prices);
const low24h = Math.min(...prices);
// Calculate price change (compare with price 24h ago)
const price24hAgo = trades[trades.length - 1]?.price || currentPrice;
const priceChange = ((currentPrice - price24hAgo) / price24hAgo) * 100;
// Update UI
document.getElementById('currentPrice').textContent = `${currentPrice.toFixed(6)} BTC`;
document.getElementById('volume24h').textContent = `${totalVolume.toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g, ",")} AITBC`;
document.getElementById('volume24h').nextElementSibling.textContent = `${totalBTC.toFixed(5)} BTC`;
document.getElementById('highLow').textContent = `${high24h.toFixed(6)} / ${low24h.toFixed(6)}`;
// Update price change with color
const changeElement = document.getElementById('priceChange');
changeElement.textContent = `${priceChange >= 0 ? '+' : ''}${priceChange.toFixed(2)}%`;
changeElement.className = `text-sm ${priceChange >= 0 ? 'text-green-600' : 'text-red-600'}`;
} catch (error) {
console.error('Failed to update price ticker:', error);
}
}
// Call this function in the DOMContentLoaded event
// Add to existing initialization:
// updatePriceTicker();
// setInterval(updatePriceTicker, 30000); // Update every 30 seconds