diff --git a/.gitignore b/.gitignore index bbfec72..ec8714f 100644 --- a/.gitignore +++ b/.gitignore @@ -23,8 +23,8 @@ dist-ssr *.sln *.sw? -# API database -# server/data.db +# API database(SQLite 文件,部署时应挂载卷持久化,勿提交) +server/data.db # Env(含 token,勿提交) .env diff --git a/crawler/__pycache__/pipeline.cpython-39.pyc b/crawler/__pycache__/pipeline.cpython-39.pyc index b1bab18..022b9c6 100644 Binary files a/crawler/__pycache__/pipeline.cpython-39.pyc and b/crawler/__pycache__/pipeline.cpython-39.pyc differ diff --git a/server/data.db b/server/data.db index e85286d..8e57ced 100644 Binary files a/server/data.db and b/server/data.db differ diff --git a/server/index.js b/server/index.js index 38d6277..e2f0969 100644 --- a/server/index.js +++ b/server/index.js @@ -8,9 +8,6 @@ const db = require('./db') const routes = require('./routes') const { getSituation } = require('./situationData') -const swaggerUi = require('swagger-ui-express') -const openApiSpec = require('./openapi') - const app = express() const PORT = process.env.API_PORT || 3001 @@ -21,9 +18,6 @@ app.set('trust proxy', 1) app.use(cors()) app.use(express.json()) -// Swagger 文档 -app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(openApiSpec)) - app.use('/api', routes) app.get('/api/health', (_, res) => res.json({ ok: true })) app.post('/api/crawler/notify', (req, res) => { diff --git a/src/components/WarMap.tsx b/src/components/WarMap.tsx index 93ff240..c59d218 100644 --- a/src/components/WarMap.tsx +++ b/src/components/WarMap.tsx @@ -155,6 +155,7 @@ export function WarMap() { const fordPathsRef = useRef<[number, number][][]>([]) const israelPathsRef = useRef<[number, number][][]>([]) const hezbollahPathsRef = useRef<[number, number][][]>([]) + const hormuzPathsRef = useRef<[number, number][][]>([]) const situation = useReplaySituation() const { usForces, iranForces, conflictEvents = [] } = situation @@ -220,10 +221,32 @@ export function WarMap() { () => EXTENDED_WAR_ZONES.activeAttacks.map((t) => parabolaPath(HEZBOLLAH_SOURCE, t.coords, 1.5)), [] ) + // 伊朗不同地点 → 霍尔木兹海峡多点攻击(黄色轨迹) + const hormuzTargetPoints = useMemo( + () => + [ + [55.7, 25.6], + [56.0, 26.0], + [56.4, 26.4], + ] as [number, number][], + [] + ) + const hormuzPaths = useMemo(() => { + // 使用更远的伊朗腹地/纵深位置,弧线更明显 + const sources: [number, number][] = [ + TEHRAN_SOURCE, // 德黑兰 + [47.16, 34.35], // 克尔曼沙赫导弹阵地 + [50.88, 34.64], // 库姆附近 + ] + return hormuzTargetPoints.map((target, idx) => + parabolaPath(sources[idx % sources.length], target, 3) + ) + }, [hormuzTargetPoints]) lincolnPathsRef.current = lincolnPaths fordPathsRef.current = fordPaths israelPathsRef.current = israelPaths hezbollahPathsRef.current = hezbollahPaths + hormuzPathsRef.current = hormuzPaths const lincolnLinesGeoJson = useMemo( () => ({ @@ -270,6 +293,18 @@ export function WarMap() { [hezbollahPaths] ) + const hormuzLinesGeoJson = useMemo( + () => ({ + type: 'FeatureCollection' as const, + features: hormuzPaths.map((coords) => ({ + type: 'Feature' as const, + properties: {}, + geometry: { type: 'LineString' as const, coordinates: coords }, + })), + }), + [hormuzPaths] + ) + const attackLinesGeoJson = useMemo( () => ({ type: 'FeatureCollection' as const, @@ -295,6 +330,19 @@ export function WarMap() { [] ) + // 霍尔木兹海峡被持续打击的海面目标(用于脉冲与标记) + const hormuzTargetsGeoJson = useMemo( + () => ({ + type: 'FeatureCollection' as const, + features: hormuzTargetPoints.map((coords, idx) => ({ + type: 'Feature' as const, + properties: { id: `H${idx + 1}` }, + geometry: { type: 'Point' as const, coordinates: coords }, + })), + }), + [hormuzTargetPoints] + ) + // 霍尔木兹海峡交战区 & 真主党势力范围(静态面) const hormuzZone = EXTENDED_WAR_ZONES.hormuzCombatZone const hezbollahZone = EXTENDED_WAR_ZONES.hezbollahZone @@ -461,6 +509,24 @@ export function WarMap() { }) hezSrc.setData({ type: 'FeatureCollection', features }) } + // 伊朗对霍尔木兹海峡:黄色光点,沿海峡方向飞行 + const hormuzSrc = map.getSource('iran-hormuz-dots') as + | { setData: (d: GeoJSON.FeatureCollection) => void } + | undefined + const hormuzPaths = hormuzPathsRef.current + if (hormuzSrc && hormuzPaths.length > 0) { + const features: GeoJSON.Feature[] = hormuzPaths.map((path, i) => { + const progress = + (elapsed / FLIGHT_DURATION_MS + 0.15 + i / Math.max(hormuzPaths.length, 1)) % 1 + const coord = interpolateOnPath(path, progress) + return { + type: 'Feature' as const, + properties: {}, + geometry: { type: 'Point' as const, coordinates: coord }, + } + }) + hormuzSrc.setData({ type: 'FeatureCollection', features }) + } // 伊朗被打击目标:蓝色脉冲 (2s 周期), 半径随 zoom 缩放;phase/r/opacity 钳位 if (map.getLayer('allied-strike-targets-pulse')) { const cycle = 2000 @@ -493,6 +559,15 @@ export function WarMap() { map.setPaintProperty('hezbollah-attack-targets-pulse', 'circle-radius', r) map.setPaintProperty('hezbollah-attack-targets-pulse', 'circle-opacity', opacity) } + // 霍尔木兹海峡被打击目标:琥珀黄脉冲,保持与其他被打击点一致但颜色区分 + if (map.getLayer('iran-hormuz-targets-pulse')) { + const cycle = 2000 + const phase = Math.max(0, Math.min(1, (elapsed % cycle) / cycle)) + const r = Math.max(0, 32 * phase * zoomScale) + const opacity = Math.min(1, Math.max(0, 1 - phase * 1.1)) + map.setPaintProperty('iran-hormuz-targets-pulse', 'circle-radius', r) + map.setPaintProperty('iran-hormuz-targets-pulse', 'circle-opacity', opacity) + } } catch (_) {} } animRef.current = requestAnimationFrame(tick) @@ -646,6 +721,74 @@ export function WarMap() { /> + {/* 伊朗对霍尔木兹海峡的打击路径(黄色轨迹) */} + + + + {/* 伊朗对霍尔木兹的打击光点(黄色) */} + ({ + type: 'Feature' as const, + properties: {}, + geometry: { type: 'Point' as const, coordinates: path[0] }, + })), + }} + > + + + + + {/* 霍尔木兹海峡被打击目标点 + 脉冲(与其他被打击点风格一致,颜色区分为琥珀黄) */} + + + + + diff --git a/start.sh b/start.sh index a0b5eac..88403ea 100755 --- a/start.sh +++ b/start.sh @@ -20,7 +20,14 @@ echo "==> Checking crawler Python deps..." pip install -q -r crawler/requirements.txt 2>/dev/null || true echo "==> Seeding database (if needed)..." -[ ! -f server/data.db ] && npm run api:seed +# 若指定了 DB_PATH,则以 DB_PATH 为准;否则默认使用 server/data.db +DB_FILE_PATH="${DB_PATH:-server/data.db}" +if [ ! -f "$DB_FILE_PATH" ]; then + echo " - DB file not found at $DB_FILE_PATH, running api:seed once" + npm run api:seed +else + echo " - Existing DB detected at $DB_FILE_PATH, skip seeding" +fi echo "==> Starting API (http://localhost:3001)..." npm run api &