nguyenminhduc9988/situroom
GitHub: nguyenminhduc9988/situroom
一个无需服务器和 API 密钥的纯静态全球态势监测仪表盘,在单一页面上实时聚合地震、灾害、冲突新闻与市场行情数据。
Stars: 0 | Forks: 0
# SITUROOM
**零后端的全球态势室 —— 在一个静态页面中实时呈现地震、冲突、灾害与市场动态。无需服务器。无需 API 密钥。无需付费墙。**
[](LICENSE)
[](#contributing)
[](package.json)
[](#architecture)

**在线演示:** https://nguyenminhduc9988.github.io/situroom/
## 为什么
大多数“世界监测”情报仪表盘都是托管型产品:由庞大的服务器集群渲染画面,评分机制是不透明的黑盒,而真正有用的部分(API、agent 访问权限)则被付费密钥挡在门外。SITUROOM 则反其道而行之 —— 一个态势仪表盘所需的一切都可以从免费、无需密钥、对 CORS 友好的公共 API 获取,因此整个产品完全可以是**一个静态文件夹**。
| | SITUROOM | 典型的托管型情报仪表盘 |
|---|---|---|
| **License** | MIT,无商业版层级 | AGPL + 商业授权推销 |
| **Backend** | 无 —— 纯静态文件 | 大量的 edge functions + Redis + relays |
| **API / agent 访问权限** | 免费的本地 MCP server,内置 | 需付费 API 密钥 |
| **评分** | 有文档记录的客户端公式 ([js/score.js](js/score.js)) —— 可在 devtools 中重新计算 | 不透明的服务端计算 |
| **离线** | PWA + 内置数据快照兜底 | 仅限在线 |
| **配置** | 打开一个 URL,或运行 `python3 -m http.server` | 云端部署 |
目前提供六个免费数据源,[欢迎提交 PR 添加更多](#contributing)。
## 快速开始
**1. 直接打开** —— https://nguyenminhduc9988.github.io/situroom/
**2. 本地运行**(任何静态服务器均可;无需安装,无需构建):
```
git clone https://github.com/nguyenminhduc9988/situroom
cd situroom
python3 -m http.server 8080
# 打开 http://localhost:8080
```
**3. 自行托管** —— fork 该仓库,启用 GitHub Pages(从 `main` 分支的根目录部署)。完成:它只是静态文件。
## 功能
- **Canvas 世界地图** —— 内置 d3-geo 投影、经纬网、国家悬停、震级缩放的地震标记、按类别着色的自然事件、紧张局势热力斑块。图层可切换,脉冲动画(在 `prefers-reduced-motion` 下禁用)。
- **紧张局势指数** —— 基于当前新闻窗口的透明国家级评分,前 10 名的柱状图按绿→黄→红颜色显示。公式在[下方](#the-tension-index-formula)和 UI 中("formula?" 按钮)均有文档说明。

- **新闻头条** —— 来自 GDELT 的冲突类世界新闻(默认为英语来源,设置中可切换至全语言)。
- **地震 / 事件 / 科技面板** —— USGS 地震、NASA EONET 自然事件、Hacker News 首页。
- **市场行情** —— BTC/ETH/SOL 及其 24 小时涨跌幅,美元外汇参考汇率。
- **各数据源的状态标签** —— LIVE / STALE(已缓存)/ SNAPSHOT / ERROR,让你随时了解当前查看的数据状态。
- **关注列表** —— 将你关注的国家置顶到紧张局势排行榜上。
- **响应式** —— 面板在窄视口下自动堆叠;六个状态标签会折叠成一个汇总标签。
## 数据源
全部免费、无需密钥,直接从你的浏览器获取(对 CORS 友好)。鸣谢与感谢:
| 来源 | 数据 | 刷新频率 |
|---|---|---|
| [USGS Earthquake Hazards Program](https://earthquake.usgs.gov/) | 过去 24 小时的地震 | 5 分钟 |
| [NASA EONET](https://eonet.gsfc.nasa.gov/) | 公开的自然事件(野火、风暴、火山等) | 10 分钟 |
| [GDELT Project](https://www.gdeltproject.org/) DOC 2.0 | 冲突/军事/制裁/抗议相关的新闻头条,约 6 小时窗口 | 15 分钟 |
| [CoinGecko](https://www.coingecko.com/) | BTC / ETH / SOL 现价及 24 小时涨跌幅 | 2 分钟 |
| [Frankfurter](https://frankfurter.dev/) | USD→EUR/JPY/GBP/CNY 参考汇率 | 60 分钟 |
| [HN Algolia](https://hn.algolia.com/api) | Hacker News 首页 | 10 分钟 |
每个数据源的降级机制都是独立的:实时获取 → 来自 localStorage 的最后一次有效响应(**STALE**)→ [`data/snapshot.json`](data/snapshot.json) 中内置的快照(**SNAPSHOT**)→ **ERROR** 标签。仪表盘永远不会显示为空白。
## 紧张局势指数公式
没有黑盒 —— 整个计算过程都在你的浏览器中基于原始的 GDELT 文章列表执行,位于 [`js/score.js`](js/score.js):
- **volume(C)** —— 在国家 C 发布或标题中提及 C(或常见别名)的文章数量
- **severity(C)** —— 标题关键词权重的平均值:`3` 战争/打击/攻击/导弹等,`2` 制裁/军事/核武等,`1` 抗议/动荡/冲突等,下限为 `0.5`
- **score(C)** = `100 × norm(0.6·log1p(volume) + 0.4·severity)` —— 当前热度最高的国家得分为 100,其他所有国家均为相对得分
诚信说明:GDELT 的文章列表 API 并未公开其单篇文章的 tone 字段,因此严重度采用的是一个有文档记录的关键词代理指标 —— 虽然比 GDELT 的 tone 更粗略,但完全可审计,这正是我们的初衷。完整的国家/别名专有名词表和关键词对照表都是位于文件顶部的纯数据;[`test/score.test.js`](test/score.test.js) 中的单元测试锁定了其行为。
## AI 态势简报(可选,自带密钥)
默认关闭且完全不强制使用。如果你将设置(齿轮图标)指向任何兼容 OpenAI 的 endpoint —— 包括运行在 `http://localhost:11434/v1` 的本地 Ollama —— 就会出现一个 **AI SITREP** 按钮,可将当前的新闻头条和紧张局势排行榜总结为包含 5 个要点的简报。你的 endpoint、密钥和模型**仅保存在你浏览器的 localStorage 中**,除了发往你配置的 endpoint 外,不会发送到任何其他地方。

## MCP server(agent 免费获取数据订阅)
[`mcp/situroom_mcp.py`](mcp/situroom_mcp.py) 是一个零依赖的 Python 3.10+ stdio MCP server,将相同的数据源作为工具公开:`get_quakes`、`get_events`、`get_headlines`、`get_tension`、`get_markets` —— 全部标准化为与 Web 应用相同的结构,紧张局势公式也如实从 `js/score.js` 移植了过来。针对 Claude Desktop/Code 和 Cursor 的配置代码片段见 [`mcp/README.md`](mcp/README.md)。
```
claude mcp add situroom -- python3 /path/to/situroom/mcp/situroom_mcp.py
```
## 架构
原生的 ES modules。没有框架,没有构建步骤,没有 npm install。唯一的第三方代码已经内置并提交:位于 [`vendor/`](vendor/) 的 d3 v7 + topojson-client(外加 world-atlas 110m 拓扑数据),因此该应用在完全无法访问任何 CDN 的情况下也能运行。
```
index.html
├── css/app.css dark ops theme (system fonts only)
├── js/app.js boot, source registry, 60s scheduler, panels
│ ├── js/map.js canvas map (offscreen-cached base + marker layer)
│ ├── js/score.js tension index (pure, documented, tested)
│ ├── js/store.js localStorage cache + settings
│ ├── js/brief.js optional BYOK sitrep
│ └── js/sources/*.js one module per feed: fetchX() + pure normalizeX()
├── sw.js PWA: precached shell, network-first API fallback
└── data/snapshot.json committed real capture — the floor of the chain:
live fetch ──fail──▶ localStorage last-good ──miss──▶ committed snapshot ──miss──▶ ERROR chip
(LIVE) (STALE) (SNAPSHOT)
```
运行测试(仅限纯函数,完全离线):
```
node --test test/*.test.js # or: npm test — 44 tests, zero dependencies
```
## License
[MIT](LICENSE) © 2026 Minh-Duc Nguyen
由 [BigWinner.work](https://bigwinner.work) 开发 —— AI 工具与咨询
## 数据源
全部免费、无需密钥,直接从你的浏览器获取(对 CORS 友好)。鸣谢与感谢:
| 来源 | 数据 | 刷新频率 |
|---|---|---|
| [USGS Earthquake Hazards Program](https://earthquake.usgs.gov/) | 过去 24 小时的地震 | 5 分钟 |
| [NASA EONET](https://eonet.gsfc.nasa.gov/) | 公开的自然事件(野火、风暴、火山等) | 10 分钟 |
| [GDELT Project](https://www.gdeltproject.org/) DOC 2.0 | 冲突/军事/制裁/抗议相关的新闻头条,约 6 小时窗口 | 15 分钟 |
| [CoinGecko](https://www.coingecko.com/) | BTC / ETH / SOL 现价及 24 小时涨跌幅 | 2 分钟 |
| [Frankfurter](https://frankfurter.dev/) | USD→EUR/JPY/GBP/CNY 参考汇率 | 60 分钟 |
| [HN Algolia](https://hn.algolia.com/api) | Hacker News 首页 | 10 分钟 |
每个数据源的降级机制都是独立的:实时获取 → 来自 localStorage 的最后一次有效响应(**STALE**)→ [`data/snapshot.json`](data/snapshot.json) 中内置的快照(**SNAPSHOT**)→ **ERROR** 标签。仪表盘永远不会显示为空白。
## 紧张局势指数公式
没有黑盒 —— 整个计算过程都在你的浏览器中基于原始的 GDELT 文章列表执行,位于 [`js/score.js`](js/score.js):
- **volume(C)** —— 在国家 C 发布或标题中提及 C(或常见别名)的文章数量
- **severity(C)** —— 标题关键词权重的平均值:`3` 战争/打击/攻击/导弹等,`2` 制裁/军事/核武等,`1` 抗议/动荡/冲突等,下限为 `0.5`
- **score(C)** = `100 × norm(0.6·log1p(volume) + 0.4·severity)` —— 当前热度最高的国家得分为 100,其他所有国家均为相对得分
诚信说明:GDELT 的文章列表 API 并未公开其单篇文章的 tone 字段,因此严重度采用的是一个有文档记录的关键词代理指标 —— 虽然比 GDELT 的 tone 更粗略,但完全可审计,这正是我们的初衷。完整的国家/别名专有名词表和关键词对照表都是位于文件顶部的纯数据;[`test/score.test.js`](test/score.test.js) 中的单元测试锁定了其行为。
## AI 态势简报(可选,自带密钥)
默认关闭且完全不强制使用。如果你将设置(齿轮图标)指向任何兼容 OpenAI 的 endpoint —— 包括运行在 `http://localhost:11434/v1` 的本地 Ollama —— 就会出现一个 **AI SITREP** 按钮,可将当前的新闻头条和紧张局势排行榜总结为包含 5 个要点的简报。你的 endpoint、密钥和模型**仅保存在你浏览器的 localStorage 中**,除了发往你配置的 endpoint 外,不会发送到任何其他地方。

## MCP server(agent 免费获取数据订阅)
[`mcp/situroom_mcp.py`](mcp/situroom_mcp.py) 是一个零依赖的 Python 3.10+ stdio MCP server,将相同的数据源作为工具公开:`get_quakes`、`get_events`、`get_headlines`、`get_tension`、`get_markets` —— 全部标准化为与 Web 应用相同的结构,紧张局势公式也如实从 `js/score.js` 移植了过来。针对 Claude Desktop/Code 和 Cursor 的配置代码片段见 [`mcp/README.md`](mcp/README.md)。
```
claude mcp add situroom -- python3 /path/to/situroom/mcp/situroom_mcp.py
```
## 架构
原生的 ES modules。没有框架,没有构建步骤,没有 npm install。唯一的第三方代码已经内置并提交:位于 [`vendor/`](vendor/) 的 d3 v7 + topojson-client(外加 world-atlas 110m 拓扑数据),因此该应用在完全无法访问任何 CDN 的情况下也能运行。
```
index.html
├── css/app.css dark ops theme (system fonts only)
├── js/app.js boot, source registry, 60s scheduler, panels
│ ├── js/map.js canvas map (offscreen-cached base + marker layer)
│ ├── js/score.js tension index (pure, documented, tested)
│ ├── js/store.js localStorage cache + settings
│ ├── js/brief.js optional BYOK sitrep
│ └── js/sources/*.js one module per feed: fetchX() + pure normalizeX()
├── sw.js PWA: precached shell, network-first API fallback
└── data/snapshot.json committed real capture — the floor of the chain:
live fetch ──fail──▶ localStorage last-good ──miss──▶ committed snapshot ──miss──▶ ERROR chip
(LIVE) (STALE) (SNAPSHOT)
```
运行测试(仅限纯函数,完全离线):
```
node --test test/*.test.js # or: npm test — 44 tests, zero dependencies
```
## License
[MIT](LICENSE) © 2026 Minh-Duc Nguyen
由 [BigWinner.work](https://bigwinner.work) 开发 —— AI 工具与咨询标签:HTTP/HTTPS抓包, MCP, PWA, 态势感知, 自定义脚本, 静态网站