pablohernandezb/umbral-monitor
GitHub: pablohernandezb/umbral-monitor
一个开源政治分析平台,通过整合多维数据源监测委内瑞拉的政权转型与民主侵蚀动态。
Stars: 4 | Forks: 0

# Umbral
一个开源分析平台,用于监控委内瑞拉的政权转型动态。Umbral 通过实时数据、历史指数、精选新闻和专家情景分析来追踪民主侵蚀情况。
## 功能特性
- **情景分析** — 五种基于证据的政权转型情景,由专家和公众按照李克特 1–5 级量表进行评级,并附有聚合概率指标
- **STAR 投票共识** — 使用 STAR 投票(评分然后自动决选)每日计算的专家和公众共识情景面板;作为每日快照存储在 Supabase 中
- **历史轨迹** — 涵盖 1900–2024 年的 V-Dem 风格民主指数图表
- **实时新闻源** — 聚合新闻,支持分类过滤、来源过滤、全文搜索以及针对每个情景的投票
- **政治犯追踪器** — 汇总拘留统计数据,包含按报告组织划分的人口统计细分
- **交互式时间线** — 民主事件数据集 (DEED),包含双语(西班牙语/英语)事件
- **阅览室** — 精选的书籍、文章、报告和新闻档案
- **事实核查源** — 来自三个委内瑞拉事实核查账户 ([@cazamosfakenews](https://x.com/cazamosfakenews), [@cotejoinfo](https://x.com/cotejoinfo), [@Factchequeado](https://x.com/Factchequeado)) 的推文,每日更新
- **GDELT 媒体信号** — 来自 GDELT 的每日存档不稳定指数、媒体基调 和文章数量,并带有注释的关键事件时间线
- **互联网连接监控(国家级)** — 针对委内瑞拉整体的、由 IODA 驱动的 BGP、主动探测 和网络望远镜 信号图表;每日 cron 任务将数据存档至 Supabase
- **互联网连接监控(次国家级)** — 州级 IODA 仪表板,包含水平热力图、Leaflet 等值线图(25 个州 + 圭亚那埃塞奎博)和排名中断分数列表
- **预测市场** — 针对委内瑞拉相关市场的 Polymarket 合约仪表板
- **参与** — 供专家和公众提交情景概率评估的多步骤调查
- **双语** — 全面支持西班牙语/英语国际化
- **隐私优先分析** — 须经明确 Cookie 同意后方可启用的 GA4 集成
## 技术栈
| 层级 | 技术 |
|---|---|
| 框架 | Next.js 15 (App Router) |
| 语言 | TypeScript |
| 样式 | Tailwind CSS, Framer Motion |
| 数据库 | Supabase (PostgreSQL) 配合行级安全 (Row Level Security) |
| 图表 | Recharts |
| 地图 | Leaflet + react-leaflet 4.x (兼容 React 18) |
| AI | Anthropic SDK (Claude Haiku 用于新闻翻译) |
| 爬取 | RSS Parser (rss-parser) |
| 图标 | Lucide React |
## 入门指南
### 前置条件
- Node.js 18+
- npm
- 一个 [Supabase](https://supabase.com) 项目(可选 — 如果没有,应用将使用本地模拟数据运行)
### 安装
```
git clone https://github.com/pablohernandezb/umbral-project.git
cd umbral-project
npm install
```
### 环境变量
在项目根目录创建一个 `.env.local` 文件。如果没有此文件,应用将使用本地样本数据在 **Mock Mode (模拟模式)** 下运行 — 无需数据库。
```
# Supase (实时数据必需)
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key # seed script only, never expose client-side
# Google Analytics (可选)
NEXT_PUBLIC_GA_ID=G-XXXXXXXXXX
# Anthropic (可选,用于 AI 功能)
ANTHROPIC_API_KEY=your-anthropic-key
# X (Twitter) API — 事实核查 Feed 必需 (Basic plan bearer token)
X_BEARER_TOKEN=your-x-bearer-token
# Cron job 授权密钥
CRON_SECRET=your-cron-secret
```
### 运行开发服务器
```
npm run dev
```
打开 [http://localhost:3000](http://localhost:3000)。
## Mock Mode (模拟模式) vs. Supabase Mode (Supabase 模式)
应用会自动检测使用哪种模式:
| 模式 | 条件 | 数据来源 |
|---|---|---|
| **Mock Mode (模拟模式)** | 没有有效的 `.env.local` | `data/mock.ts` (静态本地数据) |
| **Supabase Mode (Supabase 模式)** | `.env.local` 中有有效的 Supabase 凭证 | 通过 Supabase 的 PostgreSQL |
通过添加或删除 `.env.local` 来切换模式,然后重启开发服务器。
在 Supabase 模式下,国家级 IODA 仪表板专门从数据库读取(由每日 cron 填充)。次国家级仪表板始终从 IODA 实时获取数据。
## 数据库设置
如果您想连接真实的 Supabase 数据库:
1. **部署模式** — 将 `lib/supabase.ts` 中的 `SCHEMA_SQL` 导出内容复制到 Supabase SQL 编辑器中并运行
2. **配置凭证** — 将您的 Supabase URL 和密钥添加到 `.env.local`
3. **填充数据库** — 运行种子脚本:
```
npm run seed
```
该模式创建了 16 个带有 RLS 策略的表。针对 `news_feed`、`political_prisoners` 和 `scenarios` 启用了实时订阅。
## Cron Jobs (定时任务)
自动化数据收集运行在 Vercel 的调度器上(在 `vercel.json` 中配置)。所有任务通过使用并行获取来遵守 Vercel Hobby 版的 10 秒函数限制。
| 时间表 (UTC) | 端点 | 用途 |
|---|---|---|
| 每日 04:59 | `/api/gdelt?force=true` | 存档 GDELT 媒体信号(120 天滚动窗口) |
| 每日 06:00 | `/api/ioda/sync` | 存档国家级 IODA 连接信号 + 事件 |
| 每日 08:00 | `/api/ioda/sync-subnational` | 存档次国家级 IODA 区域信号 + 中断分数 |
| 每日 10:00 | `/api/fact-check/refresh` | 获取 3 个事实核查账户的最新推文 |
| 每日 12:00 | `/api/news/scrape` | 爬取并存储最新新闻文章 |
| 每日 14:00 | `/api/analytics/snapshot` | 计算并存储 STAR 投票 + 提交平均值快照 |
您可以在开发期间手动触发任何 cron 任务:
```
# 在 macOS/Linux 上
curl "http://localhost:3000/api/analytics/snapshot?secret="
curl "http://localhost:3000/api/news/scrape?secret="
curl "http://localhost:3000/api/fact-check/refresh?secret="
curl "http://localhost:3000/api/gdelt?force=true"
# 在 Windows PowerShell 上使用 curl.exe
curl.exe "http://localhost:3000/api/analytics/snapshot?secret="
```
## 项目结构
```
umbral-project/
├── app/ # Next.js App Router pages and API routes
│ ├── page.tsx # Landing page (Command Center)
│ ├── about/
│ ├── how-did-we-get-here/
│ ├── news/
│ ├── participate/
│ ├── reading-room/
│ ├── privacy-terms/
│ ├── admin/ # Protected admin dashboard
│ └── api/
│ ├── fact-check/refresh/ # Cron: X fact-checking tweets
│ ├── gdelt/ # Cron + on-demand: GDELT media signals
│ ├── ioda/ # IODA proxy + cron sync + batch endpoints
│ │ ├── route.ts # Proxy to IODA API (never call IODA directly from client)
│ │ ├── sync/ # Cron: archive national signals + events to Supabase
│ │ ├── regions/ # Batch signals for all 25 VE regions
│ │ └── outages/ # Batch outage scores for all 25 VE regions
│ ├── news/scrape/ # Cron: news scraping
│ └── analytics/snapshot/ # Cron: STAR voting + averages snapshots
├── components/
│ ├── layout/ # Header (with share menu), Footer
│ ├── ui/ # ScenarioCard, NewsCard, GdeltDashboard, PolymarketDashboard, etc.
│ ├── ioda/ # IODA connectivity dashboards (national + subnational)
│ │ ├── IodaDashboard.tsx # National: 3 signal charts + outage events
│ │ ├── SubnationalDashboard.tsx # State-level: heatmap + map + score list
│ │ ├── StateHeatmap.tsx # Horizon heatmap (25 states × time)
│ │ ├── VenezuelaMap.tsx # Leaflet choropleth (25 states + Esequibo)
│ │ ├── OutageScoreList.tsx # Ranked outage scores
│ │ ├── OutageEventList.tsx # Discrete outage events
│ │ ├── SignalChart.tsx # Single-signal AreaChart card
│ │ ├── StatusBadge.tsx # Connectivity status pill
│ │ └── RegionSelector.tsx # Region dropdown
│ ├── charts/ # TrajectoryChart, GdeltSignalChart
│ ├── GoogleAnalytics.tsx
│ ├── CookieBanner.tsx
│ └── CookiePreferences.tsx
├── data/
│ ├── mock.ts # Local mock data for all tables
│ ├── seed.ts # Database seed script
│ ├── venezuela-states.ts # 25 VE states with IODA numeric codes (4482–4506)
│ └── gdelt-annotations.ts # Key political events for GDELT chart overlay
├── hooks/
│ └── useIoda.ts # Generic IODA data-fetching hook with auto-refresh
├── i18n/
│ ├── es/common.json # Spanish translations (default)
│ └── en/common.json # English translations
├── lib/
│ ├── supabase.ts # DB client, IS_MOCK_MODE flag, SCHEMA_SQL
│ ├── data.ts # Data access layer with mock fallback
│ ├── ioda.ts # IODA API functions, severity utilities, score computation
│ ├── x-api.ts # X API client for fact-checking tweets
│ └── cookie-consent.ts # Cookie consent context
├── public/data/
│ └── venezuela-geo.json # GADM 4.1 GeoJSON — 26 features, 319KB, full precision
├── types/
│ ├── index.ts # Core database TypeScript interfaces
│ └── ioda.ts # IODA-specific types (signals, outages, severity, etc.)
└── vercel.json # Cron job schedule
```
## 常用命令
```
npm run dev # Start development server
npm run build # Production build (type-checks + optimizes)
npm run start # Start production server
npm run lint # ESLint
npm run seed # Seed Supabase database
```
## 国际化
应用默认为西班牙语并支持英语。通过页眉切换语言。所有翻译字符串位于 `i18n/es/common.json` 和 `i18n/en/common.json` 中,并使用点表示法 通过 `useTranslation()` hook 访问:
```
const { t } = useTranslation()
t('scenarios.democraticTransition.title')
t('ioda.status.outage')
t('ioda.events.detected', { count: 4 })
```
## 贡献
欢迎贡献。请在提交 Pull Request 之前开启一个 issue 来讨论建议的更改。
1. Fork 本仓库
2. 创建一个功能分支 (`git checkout -b feature/your-feature`)
3. 提交您的更改
4. 推送到分支并开启一个 Pull Request
## 许可证
[Apache License 2.0](LICENSE)
标签:BGP, ESC4, GDELT, IODA, Leaflet, OSINT, Polymarket, PostgreSQL, React, STAR投票, Supabase, Syscalls, V-Dem, 事实核查, 互联网连接监控, 交互式时间轴, 代码示例, 仪表盘, 信息聚合, 公共数据, 历史指数, 双语支持, 地图可视化, 场景分析, 委内瑞拉, 媒体信号, 实时新闻, 政权转型, 政治犯追踪, 政治监控, 数据分析, 数据泄露防护, 民主测评, 社会动态, 索引导航, 网络中断检测, 网络探测, 自动化攻击, 英语, 西班牙语, 选举共识, 预测市场