fix: update code
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -23,8 +23,8 @@ dist-ssr
|
|||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
# API database
|
# API database(SQLite 文件,部署时应挂载卷持久化,勿提交)
|
||||||
# server/data.db
|
server/data.db
|
||||||
|
|
||||||
# Env(含 token,勿提交)
|
# Env(含 token,勿提交)
|
||||||
.env
|
.env
|
||||||
|
|||||||
Binary file not shown.
BIN
server/data.db
BIN
server/data.db
Binary file not shown.
@@ -8,9 +8,6 @@ const db = require('./db')
|
|||||||
const routes = require('./routes')
|
const routes = require('./routes')
|
||||||
const { getSituation } = require('./situationData')
|
const { getSituation } = require('./situationData')
|
||||||
|
|
||||||
const swaggerUi = require('swagger-ui-express')
|
|
||||||
const openApiSpec = require('./openapi')
|
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
const PORT = process.env.API_PORT || 3001
|
const PORT = process.env.API_PORT || 3001
|
||||||
|
|
||||||
@@ -21,9 +18,6 @@ app.set('trust proxy', 1)
|
|||||||
app.use(cors())
|
app.use(cors())
|
||||||
app.use(express.json())
|
app.use(express.json())
|
||||||
|
|
||||||
// Swagger 文档
|
|
||||||
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(openApiSpec))
|
|
||||||
|
|
||||||
app.use('/api', routes)
|
app.use('/api', routes)
|
||||||
app.get('/api/health', (_, res) => res.json({ ok: true }))
|
app.get('/api/health', (_, res) => res.json({ ok: true }))
|
||||||
app.post('/api/crawler/notify', (req, res) => {
|
app.post('/api/crawler/notify', (req, res) => {
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ export function WarMap() {
|
|||||||
const fordPathsRef = useRef<[number, number][][]>([])
|
const fordPathsRef = useRef<[number, number][][]>([])
|
||||||
const israelPathsRef = useRef<[number, number][][]>([])
|
const israelPathsRef = useRef<[number, number][][]>([])
|
||||||
const hezbollahPathsRef = useRef<[number, number][][]>([])
|
const hezbollahPathsRef = useRef<[number, number][][]>([])
|
||||||
|
const hormuzPathsRef = useRef<[number, number][][]>([])
|
||||||
const situation = useReplaySituation()
|
const situation = useReplaySituation()
|
||||||
const { usForces, iranForces, conflictEvents = [] } = situation
|
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)),
|
() => 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
|
lincolnPathsRef.current = lincolnPaths
|
||||||
fordPathsRef.current = fordPaths
|
fordPathsRef.current = fordPaths
|
||||||
israelPathsRef.current = israelPaths
|
israelPathsRef.current = israelPaths
|
||||||
hezbollahPathsRef.current = hezbollahPaths
|
hezbollahPathsRef.current = hezbollahPaths
|
||||||
|
hormuzPathsRef.current = hormuzPaths
|
||||||
|
|
||||||
const lincolnLinesGeoJson = useMemo(
|
const lincolnLinesGeoJson = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
@@ -270,6 +293,18 @@ export function WarMap() {
|
|||||||
[hezbollahPaths]
|
[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(
|
const attackLinesGeoJson = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
type: 'FeatureCollection' as const,
|
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 hormuzZone = EXTENDED_WAR_ZONES.hormuzCombatZone
|
||||||
const hezbollahZone = EXTENDED_WAR_ZONES.hezbollahZone
|
const hezbollahZone = EXTENDED_WAR_ZONES.hezbollahZone
|
||||||
@@ -461,6 +509,24 @@ export function WarMap() {
|
|||||||
})
|
})
|
||||||
hezSrc.setData({ type: 'FeatureCollection', features })
|
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<GeoJSON.Point>[] = 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 钳位
|
// 伊朗被打击目标:蓝色脉冲 (2s 周期), 半径随 zoom 缩放;phase/r/opacity 钳位
|
||||||
if (map.getLayer('allied-strike-targets-pulse')) {
|
if (map.getLayer('allied-strike-targets-pulse')) {
|
||||||
const cycle = 2000
|
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-radius', r)
|
||||||
map.setPaintProperty('hezbollah-attack-targets-pulse', 'circle-opacity', opacity)
|
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 (_) {}
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
animRef.current = requestAnimationFrame(tick)
|
animRef.current = requestAnimationFrame(tick)
|
||||||
@@ -646,6 +721,74 @@ export function WarMap() {
|
|||||||
/>
|
/>
|
||||||
</Source>
|
</Source>
|
||||||
|
|
||||||
|
{/* 伊朗对霍尔木兹海峡的打击路径(黄色轨迹) */}
|
||||||
|
<Source id="iran-hormuz-lines" type="geojson" data={hormuzLinesGeoJson}>
|
||||||
|
<Layer
|
||||||
|
id="iran-hormuz-lines"
|
||||||
|
type="line"
|
||||||
|
paint={{
|
||||||
|
'line-color': 'rgba(250, 204, 21, 0.55)',
|
||||||
|
'line-width': ['interpolate', ['linear'], ['zoom'], 4, 0.6, 8, 1.2, 12, 2],
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Source>
|
||||||
|
{/* 伊朗对霍尔木兹的打击光点(黄色) */}
|
||||||
|
<Source
|
||||||
|
id="iran-hormuz-dots"
|
||||||
|
type="geojson"
|
||||||
|
data={{
|
||||||
|
type: 'FeatureCollection',
|
||||||
|
features: hormuzPaths.map((path) => ({
|
||||||
|
type: 'Feature' as const,
|
||||||
|
properties: {},
|
||||||
|
geometry: { type: 'Point' as const, coordinates: path[0] },
|
||||||
|
})),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Layer
|
||||||
|
id="iran-hormuz-dots-glow"
|
||||||
|
type="circle"
|
||||||
|
paint={{
|
||||||
|
'circle-radius': ['interpolate', ['linear'], ['zoom'], 4, 3, 8, 5.5, 12, 9],
|
||||||
|
'circle-color': 'rgba(250, 204, 21, 0.65)',
|
||||||
|
'circle-blur': 0.3,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Layer
|
||||||
|
id="iran-hormuz-dots-core"
|
||||||
|
type="circle"
|
||||||
|
paint={{
|
||||||
|
'circle-radius': ['interpolate', ['linear'], ['zoom'], 4, 1.2, 8, 2.2, 12, 3.8],
|
||||||
|
'circle-color': '#facc15',
|
||||||
|
'circle-stroke-width': 0.6,
|
||||||
|
'circle-stroke-color': '#fff',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Source>
|
||||||
|
|
||||||
|
{/* 霍尔木兹海峡被打击目标点 + 脉冲(与其他被打击点风格一致,颜色区分为琥珀黄) */}
|
||||||
|
<Source id="iran-hormuz-targets" type="geojson" data={hormuzTargetsGeoJson}>
|
||||||
|
<Layer
|
||||||
|
id="iran-hormuz-targets-dot"
|
||||||
|
type="circle"
|
||||||
|
paint={{
|
||||||
|
'circle-radius': ['interpolate', ['linear'], ['zoom'], 4, 2, 8, 3.5, 12, 5],
|
||||||
|
'circle-color': '#fbbf24',
|
||||||
|
'circle-stroke-width': 0.5,
|
||||||
|
'circle-stroke-color': '#fff',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Layer
|
||||||
|
id="iran-hormuz-targets-pulse"
|
||||||
|
type="circle"
|
||||||
|
paint={{
|
||||||
|
'circle-radius': 0,
|
||||||
|
'circle-color': 'rgba(251, 191, 36, 0.45)',
|
||||||
|
'circle-opacity': 0,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Source>
|
||||||
|
|
||||||
<Source id="points-us-base-damaged" type="geojson" data={usBaseDamaged}>
|
<Source id="points-us-base-damaged" type="geojson" data={usBaseDamaged}>
|
||||||
<Layer
|
<Layer
|
||||||
id="points-damaged"
|
id="points-damaged"
|
||||||
@@ -1259,14 +1402,14 @@ export function WarMap() {
|
|||||||
type="symbol"
|
type="symbol"
|
||||||
layout={{
|
layout={{
|
||||||
'text-field': ['get', 'name'],
|
'text-field': ['get', 'name'],
|
||||||
// 字体尽量小一些,避免遮挡
|
// 字体进一步调小,避免与该区域多重效果叠加后显得拥挤
|
||||||
'text-size': ['interpolate', ['linear'], ['zoom'], 4, 7, 7, 9, 10, 11],
|
'text-size': ['interpolate', ['linear'], ['zoom'], 4, 5.5, 7, 7.5, 10, 9],
|
||||||
'text-anchor': 'center',
|
'text-anchor': 'center',
|
||||||
}}
|
}}
|
||||||
paint={{
|
paint={{
|
||||||
'text-color': '#FACC15',
|
'text-color': '#FACC15',
|
||||||
'text-halo-color': '#1a1a1a',
|
'text-halo-color': '#1a1a1a',
|
||||||
'text-halo-width': 1,
|
'text-halo-width': 0.8,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Source>
|
</Source>
|
||||||
|
|||||||
9
start.sh
9
start.sh
@@ -20,7 +20,14 @@ echo "==> Checking crawler Python deps..."
|
|||||||
pip install -q -r crawler/requirements.txt 2>/dev/null || true
|
pip install -q -r crawler/requirements.txt 2>/dev/null || true
|
||||||
|
|
||||||
echo "==> Seeding database (if needed)..."
|
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)..."
|
echo "==> Starting API (http://localhost:3001)..."
|
||||||
npm run api &
|
npm run api &
|
||||||
|
|||||||
Reference in New Issue
Block a user