← back to Handbag Auth Nextjs
arbitrage/server.js
404 lines
const express = require('express');
const helmet = require('helmet');
const Database = require('better-sqlite3');
const cors = require('cors');
const path = require('path');
const app = express();
// Security headers via helmet (added 2026-05-04 overnight YOLO loop)
app.use(helmet({ contentSecurityPolicy: false }));
const PORT = 7600;
app.use(cors());
app.use(express.json());
app.use(express.static(__dirname));
// Database path
const DB_PATH = path.join(__dirname, '..', 'data', 'arbitrage.db');
// Live exchange rates (in production, fetch from API)
const EXCHANGE_RATES = {
JPYUSD: 0.0069, // 1 JPY = $0.0069 (approx ¥145/$1)
HKDUSD: 0.128, // 1 HKD = $0.128 (approx HKD 7.8/$1)
lastUpdated: new Date().toISOString()
};
// Cost assumptions
const COST_ASSUMPTIONS = {
taxRefundPct: 10.0, // Japan consumption tax refund
shippingUSD: {
economy: 50,
express: 100
},
dutyPct: 5.0, // US import duty on luxury goods
paymentFeePct: 3.0, // Credit card/PayPal fees
authenticationUSD: 50, // Authentication service
sellingFeePct: {
ebay: 13.0,
fashionphile: 15.0,
therealreal: 20.0,
rebag: 18.0,
vestiaire: 15.0
}
};
// === ARBITRAGE CALCULATOR ===
function calculateArbitrage(params) {
const {
brand,
model,
japanPriceJPY,
usaPriceUSD = null,
hkPriceHKD = null,
condition = 'excellent',
shippingType = 'economy',
includeAuth = true,
sellingPlatform = 'fashionphile'
} = params;
// Step 1: Japan purchase cost
const taxRefund = japanPriceJPY * (COST_ASSUMPTIONS.taxRefundPct / 100);
const netPurchaseJPY = japanPriceJPY - taxRefund;
const purchaseUSD = netPurchaseJPY * EXCHANGE_RATES.JPYUSD;
// Step 2: Additional costs
const shipping = COST_ASSUMPTIONS.shippingUSD[shippingType];
const duties = purchaseUSD * (COST_ASSUMPTIONS.dutyPct / 100);
const paymentFees = purchaseUSD * (COST_ASSUMPTIONS.paymentFeePct / 100);
const authentication = includeAuth ? COST_ASSUMPTIONS.authenticationUSD : 0;
const totalCostUSD = purchaseUSD + shipping + duties + paymentFees + authentication;
// Step 3: USA revenue (if provided)
let usaResult = null;
if (usaPriceUSD) {
const sellingFee = usaPriceUSD * (COST_ASSUMPTIONS.sellingFeePct[sellingPlatform] / 100);
const netRevenueUSD = usaPriceUSD - sellingFee;
const profitUSD = netRevenueUSD - totalCostUSD;
const profitMarginPct = (profitUSD / totalCostUSD) * 100;
usaResult = {
sellPrice: usaPriceUSD,
sellingFee,
netRevenue: netRevenueUSD,
profit: profitUSD,
profitMargin: profitMarginPct,
profitable: profitUSD > 0,
recommendation: profitMarginPct > 30 ? 'EXCELLENT' : (profitMarginPct > 15 ? 'GOOD' : (profitMarginPct > 0 ? 'MARGINAL' : 'SKIP'))
};
}
// Step 4: HK revenue (if provided)
let hkResult = null;
if (hkPriceHKD) {
const hkPriceUSD = hkPriceHKD * EXCHANGE_RATES.HKDUSD;
const sellingFee = hkPriceUSD * 0.15; // Assume 15% for HK platforms
const netRevenueUSD = hkPriceUSD - sellingFee;
const profitUSD = netRevenueUSD - totalCostUSD;
const profitMarginPct = (profitUSD / totalCostUSD) * 100;
hkResult = {
sellPrice: hkPriceUSD,
sellPriceHKD: hkPriceHKD,
sellingFee,
netRevenue: netRevenueUSD,
profit: profitUSD,
profitMargin: profitMarginPct,
profitable: profitUSD > 0,
recommendation: profitMarginPct > 30 ? 'EXCELLENT' : (profitMarginPct > 15 ? 'GOOD' : (profitMarginPct > 0 ? 'MARGINAL' : 'SKIP'))
};
}
return {
product: { brand, model, condition },
costBreakdown: {
japan: {
purchaseJPY: japanPriceJPY,
taxRefundJPY: taxRefund,
netPurchaseJPY: netPurchaseJPY,
purchaseUSD: Math.round(purchaseUSD * 100) / 100
},
additionalCosts: {
shipping: shipping,
duties: Math.round(duties * 100) / 100,
paymentFees: Math.round(paymentFees * 100) / 100,
authentication: authentication
},
totalCostUSD: Math.round(totalCostUSD * 100) / 100
},
usa: usaResult,
hongkong: hkResult,
exchangeRates: EXCHANGE_RATES,
bestRoute: determineBestRoute(usaResult, hkResult)
};
}
function determineBestRoute(usaResult, hkResult) {
if (!usaResult && !hkResult) return null;
if (!usaResult) return { market: 'Hong Kong', ...hkResult };
if (!hkResult) return { market: 'USA', ...usaResult };
if (usaResult.profit > hkResult.profit) {
return { market: 'USA', ...usaResult };
} else {
return { market: 'Hong Kong', ...hkResult };
}
}
// === API ROUTES ===
// GET /api/arbitrage/rates - Get current exchange rates
app.get('/api/arbitrage/rates', (req, res) => {
res.json({
success: true,
rates: EXCHANGE_RATES
});
});
// POST /api/arbitrage/calculate - Calculate arbitrage for a specific item
app.post('/api/arbitrage/calculate', (req, res) => {
try {
const result = calculateArbitrage(req.body);
res.json({
success: true,
data: result
});
} catch (error) {
res.status(400).json({
success: false,
error: error.message
});
}
});
// GET /api/arbitrage/opportunities - Get saved arbitrage opportunities
app.get('/api/arbitrage/opportunities', (req, res) => {
try {
const db = new Database(DB_PATH, { readonly: true });
const minMargin = parseFloat(req.query.minMargin) || 0;
const limit = parseInt(req.query.limit) || 50;
const brand = req.query.brand || null;
let query = `
SELECT *
FROM arbitrage_opportunities
WHERE is_active = 1 AND profit_margin_pct >= ?
`;
const params = [minMargin];
if (brand) {
query += ` AND brand = ?`;
params.push(brand);
}
query += ` ORDER BY profit_margin_pct DESC LIMIT ?`;
params.push(limit);
const opportunities = db.prepare(query).all(...params);
db.close();
res.json({
success: true,
count: opportunities.length,
data: opportunities
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message,
data: []
});
}
});
// GET /api/arbitrage/stats - Get arbitrage statistics
app.get('/api/arbitrage/stats', (req, res) => {
try {
const db = new Database(DB_PATH, { readonly: true });
const totalOpportunities = db.prepare(`
SELECT COUNT(*) as count FROM arbitrage_opportunities WHERE is_active = 1
`).get();
const avgProfit = db.prepare(`
SELECT AVG(potential_profit_usd) as avg FROM arbitrage_opportunities WHERE is_active = 1
`).get();
const avgMargin = db.prepare(`
SELECT AVG(profit_margin_pct) as avg FROM arbitrage_opportunities WHERE is_active = 1
`).get();
const topBrand = db.prepare(`
SELECT brand, AVG(profit_margin_pct) as avg_margin, COUNT(*) as count
FROM arbitrage_opportunities
WHERE is_active = 1
GROUP BY brand
ORDER BY avg_margin DESC
LIMIT 1
`).get();
const profitDistribution = db.prepare(`
SELECT
SUM(CASE WHEN profit_margin_pct >= 40 THEN 1 ELSE 0 END) as excellent,
SUM(CASE WHEN profit_margin_pct >= 25 AND profit_margin_pct < 40 THEN 1 ELSE 0 END) as good,
SUM(CASE WHEN profit_margin_pct >= 10 AND profit_margin_pct < 25 THEN 1 ELSE 0 END) as marginal,
SUM(CASE WHEN profit_margin_pct < 10 THEN 1 ELSE 0 END) as poor
FROM arbitrage_opportunities
WHERE is_active = 1
`).get();
db.close();
res.json({
success: true,
stats: {
totalOpportunities: totalOpportunities.count,
avgProfit: Math.round((avgProfit.avg || 0) * 100) / 100,
avgMargin: Math.round((avgMargin.avg || 0) * 10) / 10,
topBrand: topBrand || { brand: 'N/A', avg_margin: 0 },
profitDistribution: profitDistribution || { excellent: 0, good: 0, marginal: 0, poor: 0 }
}
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// POST /api/arbitrage/save - Save a new arbitrage opportunity
app.post('/api/arbitrage/save', (req, res) => {
try {
const db = new Database(DB_PATH);
const {
brand, model, condition,
japanPriceJPY, japanSource, japanUrl,
usaPriceUSD, usaSource, usaUrl,
hkPriceHKD, hkSource, hkUrl,
costBreakdown
} = req.body;
// Calculate arbitrage
const calc = calculateArbitrage({
brand, model, japanPriceJPY, usaPriceUSD, hkPriceHKD, condition
});
const profit = calc.bestRoute ? calc.bestRoute.profit : 0;
const margin = calc.bestRoute ? calc.bestRoute.profitMargin : 0;
const stmt = db.prepare(`
INSERT INTO arbitrage_opportunities (
brand, model, condition,
japan_price_jpy, japan_source, japan_url,
usa_price_usd, usa_source, usa_url,
hk_price_hkd, hk_source, hk_url,
exchange_rate_jpy_usd, total_cost_usd,
potential_profit_usd, profit_margin_pct,
cost_breakdown
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
const result = stmt.run(
brand, model, condition || 'excellent',
japanPriceJPY, japanSource || 'Manual', japanUrl || '',
usaPriceUSD || null, usaSource || 'Manual', usaUrl || '',
hkPriceHKD || null, hkSource || null, hkUrl || null,
EXCHANGE_RATES.JPYUSD, calc.costBreakdown.totalCostUSD,
profit, margin,
JSON.stringify(costBreakdown || calc.costBreakdown)
);
db.close();
res.json({
success: true,
id: result.lastInsertRowid,
calculation: calc
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// GET /api/arbitrage/compare - Compare prices across markets
app.get('/api/arbitrage/compare', (req, res) => {
try {
const brand = req.query.brand;
const model = req.query.model;
if (!brand || !model) {
return res.status(400).json({
success: false,
error: 'brand and model parameters required'
});
}
const db = new Database(DB_PATH, { readonly: true });
const opportunities = db.prepare(`
SELECT * FROM arbitrage_opportunities
WHERE brand = ? AND model = ?
ORDER BY created_at DESC
LIMIT 5
`).all(brand, model);
db.close();
if (opportunities.length === 0) {
return res.json({
success: true,
found: false,
message: 'No data found for this model'
});
}
// Aggregate data
const latest = opportunities[0];
const avgJapanPrice = opportunities.reduce((sum, o) => sum + o.japan_price_jpy, 0) / opportunities.length;
const avgUSAPrice = opportunities.reduce((sum, o) => sum + (o.usa_price_usd || 0), 0) / opportunities.filter(o => o.usa_price_usd).length;
res.json({
success: true,
found: true,
brand,
model,
comparison: {
japan: {
latest: latest.japan_price_jpy,
average: Math.round(avgJapanPrice),
latestUSD: Math.round(latest.japan_price_jpy * EXCHANGE_RATES.JPYUSD),
source: latest.japan_source
},
usa: {
latest: latest.usa_price_usd,
average: Math.round(avgUSAPrice),
source: latest.usa_source
},
arbitrage: {
profit: latest.potential_profit_usd,
margin: latest.profit_margin_pct,
recommendation: latest.profit_margin_pct > 30 ? 'EXCELLENT' : (latest.profit_margin_pct > 15 ? 'GOOD' : 'MARGINAL')
}
},
history: opportunities
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
app.listen(PORT, '0.0.0.0', () => {
console.log(`💰 Arbitrage Calculator running on http://45.61.58.125:${PORT}`);
console.log(`📊 Dashboard: http://45.61.58.125:${PORT}/arbitrage.html`);
console.log(`🔌 API: http://45.61.58.125:${PORT}/api/arbitrage/opportunities`);
});