feat: add new file

This commit is contained in:
Daniel
2026-03-01 19:23:48 +08:00
parent d705fd6c83
commit c07fc681dd
24 changed files with 2711 additions and 166 deletions

107
server/db.js Normal file
View File

@@ -0,0 +1,107 @@
const Database = require('better-sqlite3')
const path = require('path')
const dbPath = path.join(__dirname, 'data.db')
const db = new Database(dbPath)
// 启用外键
db.pragma('journal_mode = WAL')
// 建表
db.exec(`
CREATE TABLE IF NOT EXISTS situation (
id INTEGER PRIMARY KEY CHECK (id = 1),
data TEXT NOT NULL,
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS force_summary (
side TEXT PRIMARY KEY CHECK (side IN ('us', 'iran')),
total_assets INTEGER NOT NULL,
personnel INTEGER NOT NULL,
naval_ships INTEGER NOT NULL,
aircraft INTEGER NOT NULL,
ground_units INTEGER NOT NULL,
uav INTEGER NOT NULL,
missile_consumed INTEGER NOT NULL,
missile_stock INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS power_index (
side TEXT PRIMARY KEY CHECK (side IN ('us', 'iran')),
overall INTEGER NOT NULL,
military_strength INTEGER NOT NULL,
economic_power INTEGER NOT NULL,
geopolitical_influence INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS force_asset (
id TEXT PRIMARY KEY,
side TEXT NOT NULL CHECK (side IN ('us', 'iran')),
name TEXT NOT NULL,
type TEXT NOT NULL,
count INTEGER NOT NULL,
status TEXT NOT NULL CHECK (status IN ('active', 'standby', 'alert')),
lat REAL,
lng REAL
);
CREATE TABLE IF NOT EXISTS key_location (
id INTEGER PRIMARY KEY AUTOINCREMENT,
side TEXT NOT NULL CHECK (side IN ('us', 'iran')),
name TEXT NOT NULL,
lat REAL NOT NULL,
lng REAL NOT NULL,
type TEXT,
region TEXT
);
CREATE TABLE IF NOT EXISTS combat_losses (
side TEXT PRIMARY KEY CHECK (side IN ('us', 'iran')),
bases_destroyed INTEGER NOT NULL,
bases_damaged INTEGER NOT NULL,
personnel_killed INTEGER NOT NULL,
personnel_wounded INTEGER NOT NULL,
aircraft INTEGER NOT NULL,
warships INTEGER NOT NULL,
armor INTEGER NOT NULL,
vehicles INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS wall_street_trend (
id INTEGER PRIMARY KEY AUTOINCREMENT,
time TEXT NOT NULL,
value INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS retaliation_current (
id INTEGER PRIMARY KEY CHECK (id = 1),
value INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS retaliation_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
time TEXT NOT NULL,
value INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS situation_update (
id TEXT PRIMARY KEY,
timestamp TEXT NOT NULL,
category TEXT NOT NULL,
summary TEXT NOT NULL,
severity TEXT NOT NULL
);
`)
// 迁移:为已有 key_location 表添加 type、region、status、damage_level 列
try {
const cols = db.prepare('PRAGMA table_info(key_location)').all()
const names = cols.map((c) => c.name)
if (!names.includes('type')) db.exec('ALTER TABLE key_location ADD COLUMN type TEXT')
if (!names.includes('region')) db.exec('ALTER TABLE key_location ADD COLUMN region TEXT')
if (!names.includes('status')) db.exec('ALTER TABLE key_location ADD COLUMN status TEXT DEFAULT "operational"')
if (!names.includes('damage_level')) db.exec('ALTER TABLE key_location ADD COLUMN damage_level INTEGER')
} catch (_) {}
module.exports = db

34
server/index.js Normal file
View File

@@ -0,0 +1,34 @@
const http = require('http')
const express = require('express')
const cors = require('cors')
const { WebSocketServer } = require('ws')
const routes = require('./routes')
const { getSituation } = require('./situationData')
const app = express()
const PORT = process.env.API_PORT || 3001
app.use(cors())
app.use(express.json())
app.use('/api', routes)
app.get('/api/health', (_, res) => res.json({ ok: true }))
const server = http.createServer(app)
const wss = new WebSocketServer({ server, path: '/ws' })
wss.on('connection', (ws) => {
ws.send(JSON.stringify({ type: 'situation', data: getSituation() }))
})
function broadcastSituation() {
try {
const data = JSON.stringify({ type: 'situation', data: getSituation() })
wss.clients.forEach((c) => {
if (c.readyState === 1) c.send(data)
})
} catch (_) {}
}
setInterval(broadcastSituation, 5000)
server.listen(PORT, () => {
console.log(`API + WebSocket running at http://localhost:${PORT}`)
})

5
server/package.json Normal file
View File

@@ -0,0 +1,5 @@
{
"name": "usa-api",
"private": true,
"type": "commonjs"
}

15
server/routes.js Normal file
View File

@@ -0,0 +1,15 @@
const express = require('express')
const { getSituation } = require('./situationData')
const router = express.Router()
router.get('/situation', (req, res) => {
try {
res.json(getSituation())
} catch (err) {
console.error(err)
res.status(500).json({ error: err.message })
}
})
module.exports = router

174
server/seed.js Normal file
View File

@@ -0,0 +1,174 @@
const db = require('./db')
// 与 src/data/mapLocations.ts 同步62 基地27 被袭 (严重6 中度12 轻度9)
function getUsLocations() {
const naval = [
{ name: '林肯号航母 (CVN-72)', lat: 24.1568, lng: 58.4215, type: 'Aircraft Carrier', region: '北阿拉伯海', status: 'operational', damage_level: null },
{ name: '福特号航母 (CVN-78)', lat: 35.7397, lng: 24.1002, type: 'Aircraft Carrier', region: '东地中海', status: 'operational', damage_level: null },
{ name: '驱逐舰(阿曼湾)', lat: 25.2, lng: 58.0, type: 'Destroyer', region: '阿曼湾', status: 'operational', damage_level: null },
{ name: '海岸警卫队 1', lat: 25.4, lng: 58.2, type: 'Coast Guard', region: '阿曼湾', status: 'operational', damage_level: null },
{ name: '海岸警卫队 2', lat: 25.0, lng: 57.8, type: 'Coast Guard', region: '阿曼湾', status: 'operational', damage_level: null },
{ name: '驱逐舰(波斯湾北部)', lat: 26.5, lng: 51.0, type: 'Destroyer', region: '波斯湾', status: 'operational', damage_level: null },
{ name: '护卫舰 1', lat: 26.7, lng: 50.6, type: 'Frigate', region: '波斯湾', status: 'operational', damage_level: null },
{ name: '护卫舰 2', lat: 27.0, lng: 50.2, type: 'Frigate', region: '波斯湾', status: 'operational', damage_level: null },
{ name: '护卫舰 3', lat: 26.3, lng: 50.8, type: 'Frigate', region: '波斯湾', status: 'operational', damage_level: null },
{ name: '辅助舰 1', lat: 26.0, lng: 51.2, type: 'Auxiliary', region: '波斯湾', status: 'operational', damage_level: null },
{ name: '辅助舰 2', lat: 25.8, lng: 51.5, type: 'Auxiliary', region: '波斯湾', status: 'operational', damage_level: null },
{ name: '辅助舰 3', lat: 26.2, lng: 50.9, type: 'Auxiliary', region: '波斯湾', status: 'operational', damage_level: null },
]
const attacked = [
{ name: '阿萨德空军基地', lat: 33.785, lng: 42.441, type: 'Base', region: '伊拉克', status: 'attacked', damage_level: 3 },
{ name: '巴格达外交支援中心', lat: 33.315, lng: 44.366, type: 'Base', region: '伊拉克', status: 'attacked', damage_level: 3 },
{ name: '乌代德空军基地', lat: 25.117, lng: 51.314, type: 'Base', region: '卡塔尔', status: 'attacked', damage_level: 3 },
{ name: '埃尔比勒空军基地', lat: 36.237, lng: 43.963, type: 'Base', region: '伊拉克', status: 'attacked', damage_level: 3 },
{ name: '因吉尔利克空军基地', lat: 37.002, lng: 35.425, type: 'Base', region: '土耳其', status: 'attacked', damage_level: 3 },
{ name: '苏尔坦亲王空军基地', lat: 24.062, lng: 47.58, type: 'Base', region: '沙特', status: 'attacked', damage_level: 3 },
{ name: '塔吉军营', lat: 33.556, lng: 44.256, type: 'Base', region: '伊拉克', status: 'attacked', damage_level: 2 },
{ name: '阿因·阿萨德', lat: 33.8, lng: 42.45, type: 'Base', region: '伊拉克', status: 'attacked', damage_level: 2 },
{ name: '坦夫驻军', lat: 33.49, lng: 38.618, type: 'Base', region: '叙利亚', status: 'attacked', damage_level: 2 },
{ name: '沙达迪基地', lat: 36.058, lng: 40.73, type: 'Base', region: '叙利亚', status: 'attacked', damage_level: 2 },
{ name: '康诺克气田基地', lat: 35.336, lng: 40.295, type: 'Base', region: '叙利亚', status: 'attacked', damage_level: 2 },
{ name: '尔梅兰着陆区', lat: 37.015, lng: 41.885, type: 'Base', region: '叙利亚', status: 'attacked', damage_level: 2 },
{ name: '阿里夫坚军营', lat: 28.832, lng: 47.799, type: 'Base', region: '科威特', status: 'attacked', damage_level: 2 },
{ name: '阿里·萨勒姆空军基地', lat: 29.346, lng: 47.52, type: 'Base', region: '科威特', status: 'attacked', damage_level: 2 },
{ name: '巴林海军支援站', lat: 26.236, lng: 50.608, type: 'Base', region: '巴林', status: 'attacked', damage_level: 2 },
{ name: '达夫拉空军基地', lat: 24.248, lng: 54.547, type: 'Base', region: '阿联酋', status: 'attacked', damage_level: 2 },
{ name: '埃斯康村', lat: 24.774, lng: 46.738, type: 'Base', region: '沙特', status: 'attacked', damage_level: 2 },
{ name: '内瓦提姆空军基地', lat: 31.208, lng: 35.012, type: 'Base', region: '以色列', status: 'attacked', damage_level: 2 },
{ name: '布林军营', lat: 29.603, lng: 47.456, type: 'Base', region: '科威特', status: 'attacked', damage_level: 1 },
{ name: '赛利耶军营', lat: 25.275, lng: 51.52, type: 'Base', region: '卡塔尔', status: 'attacked', damage_level: 1 },
{ name: '拉蒙空军基地', lat: 30.776, lng: 34.666, type: 'Base', region: '以色列', status: 'attacked', damage_level: 1 },
{ name: '穆瓦法克·萨尔蒂空军基地', lat: 32.356, lng: 36.259, type: 'Base', region: '约旦', status: 'attacked', damage_level: 1 },
{ name: '屈雷吉克雷达站', lat: 38.354, lng: 37.794, type: 'Base', region: '土耳其', status: 'attacked', damage_level: 1 },
{ name: '苏姆莱特空军基地', lat: 17.666, lng: 54.024, type: 'Base', region: '阿曼', status: 'attacked', damage_level: 1 },
{ name: '马西拉空军基地', lat: 20.675, lng: 58.89, type: 'Base', region: '阿曼', status: 'attacked', damage_level: 1 },
{ name: '西开罗空军基地', lat: 30.915, lng: 30.298, type: 'Base', region: '埃及', status: 'attacked', damage_level: 1 },
{ name: '勒莫尼耶军营', lat: 11.547, lng: 43.159, type: 'Base', region: '吉布提', status: 'attacked', damage_level: 1 },
]
const newBases = [
{ name: '多哈后勤中心', lat: 25.29, lng: 51.53, type: 'Base', region: '卡塔尔', status: 'operational', damage_level: null },
{ name: '贾法勒海军站', lat: 26.22, lng: 50.62, type: 'Base', region: '巴林', status: 'operational', damage_level: null },
{ name: '阿兹祖尔前方作战点', lat: 29.45, lng: 47.9, type: 'Base', region: '科威特', status: 'operational', damage_level: null },
{ name: '艾哈迈迪后勤枢纽', lat: 29.08, lng: 48.09, type: 'Base', region: '科威特', status: 'operational', damage_level: null },
{ name: '富查伊拉港站', lat: 25.13, lng: 56.35, type: 'Base', region: '阿联酋', status: 'operational', damage_level: null },
{ name: '哈伊马角前方点', lat: 25.79, lng: 55.94, type: 'Base', region: '阿联酋', status: 'operational', damage_level: null },
{ name: '利雅得联络站', lat: 24.71, lng: 46.68, type: 'Base', region: '沙特', status: 'operational', damage_level: null },
{ name: '朱拜勒港支援点', lat: 27.0, lng: 49.65, type: 'Base', region: '沙特', status: 'operational', damage_level: null },
{ name: '塔布克空军前哨', lat: 28.38, lng: 36.6, type: 'Base', region: '沙特', status: 'operational', damage_level: null },
{ name: '拜莱德空军基地', lat: 33.94, lng: 44.36, type: 'Base', region: '伊拉克', status: 'operational', damage_level: null },
{ name: '巴士拉后勤站', lat: 30.5, lng: 47.78, type: 'Base', region: '伊拉克', status: 'operational', damage_level: null },
{ name: '基尔库克前哨', lat: 35.47, lng: 44.35, type: 'Base', region: '伊拉克', status: 'operational', damage_level: null },
{ name: '摩苏尔支援点', lat: 36.34, lng: 43.14, type: 'Base', region: '伊拉克', status: 'operational', damage_level: null },
{ name: '哈塞克联络站', lat: 36.5, lng: 40.75, type: 'Base', region: '叙利亚', status: 'operational', damage_level: null },
{ name: '代尔祖尔前哨', lat: 35.33, lng: 40.14, type: 'Base', region: '叙利亚', status: 'operational', damage_level: null },
{ name: '安曼协调中心', lat: 31.95, lng: 35.93, type: 'Base', region: '约旦', status: 'operational', damage_level: null },
{ name: '伊兹密尔支援站', lat: 38.42, lng: 27.14, type: 'Base', region: '土耳其', status: 'operational', damage_level: null },
{ name: '哈泽瑞姆空军基地', lat: 31.07, lng: 34.84, type: 'Base', region: '以色列', status: 'operational', damage_level: null },
{ name: '杜古姆港站', lat: 19.66, lng: 57.76, type: 'Base', region: '阿曼', status: 'operational', damage_level: null },
{ name: '塞拉莱前方点', lat: 17.01, lng: 54.1, type: 'Base', region: '阿曼', status: 'operational', damage_level: null },
{ name: '亚历山大港联络站', lat: 31.2, lng: 29.9, type: 'Base', region: '埃及', status: 'operational', damage_level: null },
{ name: '卢克索前哨', lat: 25.69, lng: 32.64, type: 'Base', region: '埃及', status: 'operational', damage_level: null },
{ name: '吉布提港支援点', lat: 11.59, lng: 43.15, type: 'Base', region: '吉布提', status: 'operational', damage_level: null },
{ name: '卡塔尔应急医疗站', lat: 25.22, lng: 51.45, type: 'Base', region: '卡塔尔', status: 'operational', damage_level: null },
{ name: '沙特哈立德国王基地', lat: 24.96, lng: 46.7, type: 'Base', region: '沙特', status: 'operational', damage_level: null },
{ name: '伊拉克巴拉德联勤站', lat: 33.75, lng: 44.25, type: 'Base', region: '伊拉克', status: 'operational', damage_level: null },
{ name: '叙利亚奥马尔油田站', lat: 36.22, lng: 40.45, type: 'Base', region: '叙利亚', status: 'operational', damage_level: null },
{ name: '约旦侯赛因国王基地', lat: 31.72, lng: 36.01, type: 'Base', region: '约旦', status: 'operational', damage_level: null },
{ name: '土耳其巴特曼站', lat: 37.88, lng: 41.13, type: 'Base', region: '土耳其', status: 'operational', damage_level: null },
{ name: '以色列帕尔马欣站', lat: 31.9, lng: 34.95, type: 'Base', region: '以色列', status: 'operational', damage_level: null },
{ name: '阿曼杜古姆扩建点', lat: 19.55, lng: 57.8, type: 'Base', region: '阿曼', status: 'operational', damage_level: null },
{ name: '埃及纳特龙湖站', lat: 30.37, lng: 30.2, type: 'Base', region: '埃及', status: 'operational', damage_level: null },
{ name: '吉布提查贝尔达站', lat: 11.73, lng: 42.9, type: 'Base', region: '吉布提', status: 'operational', damage_level: null },
{ name: '阿联酋迪拜港联络', lat: 25.27, lng: 55.3, type: 'Base', region: '阿联酋', status: 'operational', damage_level: null },
{ name: '伊拉克尼尼微前哨', lat: 36.22, lng: 43.1, type: 'Base', region: '伊拉克', status: 'operational', damage_level: null },
]
return [...naval, ...attacked, ...newBases]
}
function seed() {
db.exec(`
INSERT OR REPLACE INTO force_summary (side, total_assets, personnel, naval_ships, aircraft, ground_units, uav, missile_consumed, missile_stock) VALUES
('us', 1245, 185000, 292, 1862, 18, 418, 1056, 2840),
('iran', 7850, 2350000, 4250, 8200, 350, 750, 3720, 13800);
`)
db.exec(`
INSERT OR REPLACE INTO power_index (side, overall, military_strength, economic_power, geopolitical_influence) VALUES
('us', 94, 96, 98, 97),
('iran', 42, 58, 28, 35);
`)
const insertAsset = db.prepare(`
INSERT OR REPLACE INTO force_asset (id, side, name, type, count, status, lat, lng) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
`)
const usAssets = [
['us-1', 'us', '双航母打击群 (CVN-72 & CVN-78)', '航母', 2, 'active', null, null],
['us-2', 'us', '阿利·伯克级驱逐舰', '驱逐舰', 4, 'active', null, null],
['us-3', 'us', 'F/A-18 中队', '战机', 48, 'active', null, null],
['us-4', 'us', 'F-35 中队', '战机', 48, 'active', null, null],
['us-5', 'us', 'F-22 猛禽', '战机', 12, 'active', null, null],
['us-6', 'us', 'B-2 幽灵', '轰炸机', 2, 'alert', null, null],
['us-7', 'us', '爱国者防空系统', '防空', 3, 'active', null, null],
['us-8', 'us', 'MQ-9 死神', '无人机', 28, 'active', null, null],
['us-9', 'us', 'MQ-1C 灰鹰', '无人机', 45, 'active', null, null],
]
const iranAssets = [
['ir-1', 'iran', '护卫舰', '水面舰艇', 6, 'active', null, null],
['ir-2', 'iran', '快攻艇', '海军', 100, 'active', null, null],
['ir-3', 'iran', 'F-4 Phantom', '战机', 62, 'standby', null, null],
['ir-4', 'iran', 'F-14 Tomcat', '战机', 24, 'active', null, null],
['ir-5', 'iran', '弹道导弹', '导弹', 3400, 'alert', null, null],
['ir-6', 'iran', '伊斯兰革命卫队海军', '准军事', 25000, 'active', null, null],
['ir-7', 'iran', '沙希德-136', '无人机', 750, 'alert', null, null],
['ir-8', 'iran', '法塔赫 (Fattah)', '导弹', 12, 'alert', null, null],
['ir-9', 'iran', '穆哈杰-6', '无人机', 280, 'active', null, null],
]
;[...usAssets, ...iranAssets].forEach((row) => insertAsset.run(...row))
const insertLoc = db.prepare(`
INSERT INTO key_location (side, name, lat, lng, type, region, status, damage_level) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
`)
db.exec('DELETE FROM key_location')
for (const loc of getUsLocations()) {
insertLoc.run('us', loc.name, loc.lat, loc.lng, loc.type, loc.region, loc.status, loc.damage_level)
}
const iranLocs = [
['iran', '阿巴斯港', 27.1832, 56.2666, 'Port', '伊朗', null, null],
['iran', '德黑兰', 35.6892, 51.389, 'Capital', '伊朗', null, null],
['iran', '布什尔', 28.9681, 50.838, 'Base', '伊朗', null, null],
]
iranLocs.forEach((r) => insertLoc.run(...r))
db.exec(`
INSERT OR REPLACE INTO combat_losses (side, bases_destroyed, bases_damaged, personnel_killed, personnel_wounded, aircraft, warships, armor, vehicles) VALUES
('us', 0, 27, 127, 384, 2, 0, 0, 8),
('iran', 3, 8, 2847, 5620, 24, 12, 18, 42);
`)
db.exec('DELETE FROM wall_street_trend')
const trendRows = [['2025-03-01T00:00:00', 82], ['2025-03-01T03:00:00', 85], ['2025-03-01T06:00:00', 88], ['2025-03-01T09:00:00', 90], ['2025-03-01T12:00:00', 92], ['2025-03-01T15:00:00', 94], ['2025-03-01T18:00:00', 95], ['2025-03-01T21:00:00', 96], ['2025-03-01T23:00:00', 98]]
const insertTrend = db.prepare('INSERT INTO wall_street_trend (time, value) VALUES (?, ?)')
trendRows.forEach(([t, v]) => insertTrend.run(t, v))
db.exec('INSERT OR REPLACE INTO retaliation_current (id, value) VALUES (1, 78)')
db.exec('DELETE FROM retaliation_history')
const retRows = [['2025-03-01T00:00:00', 42], ['2025-03-01T03:00:00', 48], ['2025-03-01T06:00:00', 55], ['2025-03-01T09:00:00', 61], ['2025-03-01T12:00:00', 58], ['2025-03-01T15:00:00', 65], ['2025-03-01T18:00:00', 72], ['2025-03-01T21:00:00', 76], ['2025-03-01T23:00:00', 78]]
const insertRet = db.prepare('INSERT INTO retaliation_history (time, value) VALUES (?, ?)')
retRows.forEach(([t, v]) => insertRet.run(t, v))
db.exec('DELETE FROM situation_update')
const updateRows = [
['u1', new Date(Date.now() - 3600000).toISOString(), 'deployment', '美军航母打击群在阿拉伯海重新部署', 'medium'],
['u2', new Date(Date.now() - 7200000).toISOString(), 'alert', '霍尔木兹海峡海军巡逻活动加强', 'high'],
['u3', new Date(Date.now() - 10800000).toISOString(), 'intel', '卫星图像显示阿巴斯港活动增加', 'low'],
['u4', new Date(Date.now() - 14400000).toISOString(), 'diplomatic', '阿曼间接谈判进行中', 'low'],
]
const insertUpdate = db.prepare('INSERT INTO situation_update (id, timestamp, category, summary, severity) VALUES (?, ?, ?, ?, ?)')
updateRows.forEach((row) => insertUpdate.run(...row))
db.prepare("INSERT OR REPLACE INTO situation (id, data, updated_at) VALUES (1, '{}', ?)").run('2026-03-01T11:45:00.000Z')
console.log('Seed completed.')
}
seed()

108
server/situationData.js Normal file
View File

@@ -0,0 +1,108 @@
const db = require('./db')
function toAsset(row) {
return {
id: row.id,
name: row.name,
type: row.type,
count: row.count,
status: row.status,
...(row.lat != null && { location: { lat: row.lat, lng: row.lng } }),
}
}
function toLosses(row) {
return {
bases: { destroyed: row.bases_destroyed, damaged: row.bases_damaged },
personnelCasualties: { killed: row.personnel_killed, wounded: row.personnel_wounded },
aircraft: row.aircraft,
warships: row.warships,
armor: row.armor,
vehicles: row.vehicles,
}
}
const defaultLosses = {
bases: { destroyed: 0, damaged: 0 },
personnelCasualties: { killed: 0, wounded: 0 },
aircraft: 0,
warships: 0,
armor: 0,
vehicles: 0,
}
function getSituation() {
const summaryUs = db.prepare('SELECT * FROM force_summary WHERE side = ?').get('us')
const summaryIr = db.prepare('SELECT * FROM force_summary WHERE side = ?').get('iran')
const powerUs = db.prepare('SELECT * FROM power_index WHERE side = ?').get('us')
const powerIr = db.prepare('SELECT * FROM power_index WHERE side = ?').get('iran')
const assetsUs = db.prepare('SELECT * FROM force_asset WHERE side = ? ORDER BY id').all('us')
const assetsIr = db.prepare('SELECT * FROM force_asset WHERE side = ? ORDER BY id').all('iran')
const locUs = db.prepare('SELECT id, name, lat, lng, type, region, status, damage_level FROM key_location WHERE side = ?').all('us')
const locIr = db.prepare('SELECT id, name, lat, lng, type, region FROM key_location WHERE side = ?').all('iran')
const lossesUs = db.prepare('SELECT * FROM combat_losses WHERE side = ?').get('us')
const lossesIr = db.prepare('SELECT * FROM combat_losses WHERE side = ?').get('iran')
const trend = db.prepare('SELECT time, value FROM wall_street_trend ORDER BY time').all()
const retaliationCur = db.prepare('SELECT value FROM retaliation_current WHERE id = 1').get()
const retaliationHist = db.prepare('SELECT time, value FROM retaliation_history ORDER BY time').all()
const updates = db.prepare('SELECT * FROM situation_update ORDER BY timestamp DESC').all()
const meta = db.prepare('SELECT updated_at FROM situation WHERE id = 1').get()
return {
lastUpdated: meta?.updated_at || new Date().toISOString(),
usForces: {
summary: {
totalAssets: summaryUs?.total_assets ?? 0,
personnel: summaryUs?.personnel ?? 0,
navalShips: summaryUs?.naval_ships ?? 0,
aircraft: summaryUs?.aircraft ?? 0,
groundUnits: summaryUs?.ground_units ?? 0,
uav: summaryUs?.uav ?? 0,
missileConsumed: summaryUs?.missile_consumed ?? 0,
missileStock: summaryUs?.missile_stock ?? 0,
},
powerIndex: {
overall: powerUs?.overall ?? 0,
militaryStrength: powerUs?.military_strength ?? 0,
economicPower: powerUs?.economic_power ?? 0,
geopoliticalInfluence: powerUs?.geopolitical_influence ?? 0,
},
assets: (assetsUs || []).map(toAsset),
keyLocations: locUs || [],
combatLosses: lossesUs ? toLosses(lossesUs) : defaultLosses,
wallStreetInvestmentTrend: trend || [],
},
iranForces: {
summary: {
totalAssets: summaryIr?.total_assets ?? 0,
personnel: summaryIr?.personnel ?? 0,
navalShips: summaryIr?.naval_ships ?? 0,
aircraft: summaryIr?.aircraft ?? 0,
groundUnits: summaryIr?.ground_units ?? 0,
uav: summaryIr?.uav ?? 0,
missileConsumed: summaryIr?.missile_consumed ?? 0,
missileStock: summaryIr?.missile_stock ?? 0,
},
powerIndex: {
overall: powerIr?.overall ?? 0,
militaryStrength: powerIr?.military_strength ?? 0,
economicPower: powerIr?.economic_power ?? 0,
geopoliticalInfluence: powerIr?.geopolitical_influence ?? 0,
},
assets: (assetsIr || []).map(toAsset),
keyLocations: locIr || [],
combatLosses: lossesIr ? toLosses(lossesIr) : defaultLosses,
retaliationSentiment: retaliationCur?.value ?? 0,
retaliationSentimentHistory: retaliationHist || [],
},
recentUpdates: (updates || []).map((u) => ({
id: u.id,
timestamp: u.timestamp,
category: u.category,
summary: u.summary,
severity: u.severity,
})),
}
}
module.exports = { getSituation }