8.6 KiB
起始页:天气 + 定位(已实现)
导航起始页问候语下方展示「天气图标 · 城市 · 天气 · 气温」。 前端不请求浏览器定位权限。定位只由后端按访问 IP 调高德 IP 定位完成; 查不到位置时不再调用和风,前端静默不显示天气。天气数据始终来自和风实时天气 (私钥只在后端)。
架构
浏览器
└─ GET /api/web/weather (后端)
├─ 高德 /v3/ip?ip=<访问者IP> → 城市名 + 城市中心坐标
├─ 查不到位置 → 返回 failed,前端不展示天气
└─ 查到位置 → 和风 /v7/weather/now?location= → 天气文字 / 气温 / 图标码
- 后端:
Server/src/weather.rs(服务 + 限流 + 缓存,get()只接收访问 IP)、 路由Server/src/routes/web.rs的/api/web/weather(不接收前端位置参数)。 - 前端:
Client/src/api/weather.ts、Client/src/hooks/useWeather.ts(直接请求后端,30 分钟刷新)Client/src/components/StartPage.tsx:统一持有天气状态,派生昼夜背景与天气动效,并把天气传给GreetingClient/src/components/start/Greeting.tsx:展示「天气图标 · 城市 · 天气 · 气温」Client/src/components/start/WeatherIcon.tsx:天气文字旁的静态图标Client/src/components/start/WeatherFx.tsx:雨/雪 canvas 动态层
限流与缓存(后端内置)
- 高德 300 次/天、和风 900 次/天,各自独立计数,北京时间 0 点归零。
- 任一服务商额度用尽 → 接口返回
{ ok:false, reason:"quota_exceeded", provider }, 前端显示「天气请求次数已用完,明日 0 点恢复」。 - 缓存 10 分钟:按访问者
IP缓存;前端每 30 分钟刷新一次,正常用量远低于额度。 - 计数只在“真的调用了上游”时增加(命中缓存不计数)。
环境变量
后端
后端启动时读取(缺和风 JWT 时接口返回 not_configured,前端静默不展示;
缺高德 REST Key 时接口返回 not_configured,前端静默不展示):
| 变量 | 必填 | 说明 |
|---|---|---|
AMAP_KEY |
是 | 高德 Web服务(REST) Key,用于按访问 IP 定位城市 |
QWEATHER_HOST |
是 | 和风专属 API Host(控制台给的,如 https://xxxx.qweatherapi.com) |
QWEATHER_PROJECT_ID |
是 | 和风项目 ID → JWT 的 sub |
QWEATHER_KEY_ID |
是 | 和风凭据 ID → JWT 头的 kid |
QWEATHER_PRIVATE_KEY_PATH |
二选一 | Ed25519 私钥(PKCS#8 PEM)文件路径(推荐,多行 PEM 用文件最省事) |
QWEATHER_PRIVATE_KEY |
二选一 | 直接给私钥 PEM 内容(可用 \n 转义换行) |
AMAP_DAILY_LIMIT |
否 | 默认 300 |
QWEATHER_DAILY_LIMIT |
否 | 默认 900 |
WEATHER_CACHE_SECS |
否 | 缓存秒数,默认 600 |
WEATHER_DEFAULT_LOCATION |
否 | 内网/本地 IP 定位失败时的兜底坐标 经度,纬度(如 116.40,39.90) |
WEATHER_DEFAULT_CITY |
否 | 兜底坐标对应的城市名 |
和风认证用 JWT(EdDSA/Ed25519):后端用私钥签发 15 分钟有效的 token, 以
Authorization: Bearer调用。私钥只放后端,绝不进前端/仓库。
定位失败的处理:高德对数据中心 / DNS / 部分移动·CGNAT / 境外 IP 会返回空。 若不设
WEATHER_DEFAULT_LOCATION,定位失败时接口直接返回failed→ 不再调和风(省额度)→ 前端静默不显示天气。线上有意不设兜底,就是这个效果。本地开发时访问者 IP 是
127.0.0.1,高德无法定位 → 想在本地看到天气,可临时设WEATHER_DEFAULT_LOCATION+WEATHER_DEFAULT_CITY(仅本地weather.env,别带上线)。
本地运行
后端(在 Server/,WSL 或 Windows 均可):
AMAP_KEY=xxx \
QWEATHER_HOST=https://xxxx.qweatherapi.com \
QWEATHER_PROJECT_ID=项目ID QWEATHER_KEY_ID=凭据ID \
QWEATHER_PRIVATE_KEY_PATH=./qweather-ed25519.pem \
WEATHER_DEFAULT_LOCATION=116.40,39.90 WEATHER_DEFAULT_CITY=北京 \
PORT=8080 cargo run
前端 cd Client && npm run dev,打开起始页即可看到天气行。
部署:改了后端时(重新编译)
改了 Server/ 下代码时上线需要重新编译 Rust 二进制:
- WSL 里
cd /home/cc/InternetProject/Server && cargo build --release - scp 覆盖服务器
/opt/internet-project/RustServer+ 前端dist/* - 私钥与密钥建议写进服务器上的一个文件再统一加载,避免出现在命令历史里。
例如服务器放
/opt/internet-project/qweather.pem和/opt/internet-project/weather.env:
# weather.env 内容(不进仓库)
AMAP_KEY=xxx
QWEATHER_HOST=https://xxxx.qweatherapi.com
QWEATHER_PROJECT_ID=项目ID
QWEATHER_KEY_ID=凭据ID
QWEATHER_PRIVATE_KEY_PATH=/opt/internet-project/qweather.pem
PORT=8548
# 登录 / 入口门走 MySQL(见 docs/auth-login-gate.md)
DATABASE_URL=mysql://KarCharon:******@127.0.0.1:3306/InternetProject
启动:
ssh root@8.130.143.54 "pkill RustServer; cd /opt/internet-project && \
set -a && . ./weather.env && set +a && nohup ./RustServer > server.log 2>&1 &"
天气图标方案
未引入图标库/字体,而是把和风 now.icon 码归成 7 类
(晴日/晴夜/多云阴/雨/雷/雪/雾霾),各画一个极简线性 SVG(WeatherIcon.tsx),
stroke=currentColor 跟随文字颜色,矢量可缩放、离线可用,和玻璃风统一。
要更精细可换成和风官方图标集,但当前方案零依赖、足够清爽。
昼夜背景切换
背景图按昼夜在 light.* / dark.* 之间自动切换(图片放
Client/src/assets/startpage/,详见该目录 README):
- 昼夜判断(
StartPage.tsx):优先用now.icon——150/151/152/153/350/351/456/457判为夜、100/101/102/103/300/301/400/401判为昼;其余无昼夜之分的天气码用 本地时间兜底(18:00–06:00 为夜)。每分钟一拍,跨时段会自动翻。 - 回退:选中的图缺失 → 另一张 → 目录里排序第一张 → 中性渐变。
天气动态效果
WeatherFx.tsx 用一个轻量 canvas 粒子层按天气渲染(克制风格、低透明度、
尊重系统「减少动态效果」、kind='none' 不挂载):
天气(now.icon) |
效果 |
|---|---|
雨类 300–399 |
斜向落雨;其中雷阵雨 302/303/304 叠偶发闪电一闪 |
雪类 400–499 及冷 901 |
缓慢飘落雪花 |
| 其余 | 无(静态) |
层级:背景图(-z-20) → 渐变 scrim + 半透明黑遮罩(-z-10) → 天气动效(z-0) → 内容(z-10)。
本地预览开关(仅 npm run dev)
不开后端 / 不等真实天气也能预览昼夜背景与动效,加 URL 参数即可:
?bg=light/?bg=dark—— 强制白天/夜晚背景?fx=rain/?fx=snow/?fx=thunder/?fx=none—— 强制动效
例:http://localhost:5173/?bg=dark&fx=snow。这些参数 import.meta.env.DEV 为真才生效,线上无效,线上完全由真实天气驱动。
再次部署的两种情况
- 只改前端(动效、背景、样式等):前端单独重发即可——
npm run build→ 清空服务器static/→ 上传dist/*。后端进程不用动(实时读磁盘静态文件)。 - 改了后端(
Server/下任何代码):需在 WSLcargo build --release→ 覆盖/opt/internet-project/RustServer→ 带环境变量重启(见上「部署:改了后端时」)。
Git 远程与推送
仓库现在只有一个远程(2026-06-23 起移除了 GitHub origin):
| 远程 | 地址 | 协议 / 认证 | 在 WSL 里能否直接推 |
|---|---|---|---|
gitea |
ssh://gitea@8.130.143.54:2222/cc/PersonalWebApplication.git |
SSH(已配密钥) | ✅ 可直接 git push gitea main |
-
gitea:WSL 里 SSH 密钥已就绪,
git push gitea main直接成功。 -
GitHub(已移除):原
origin=https://github.com/KarCharon/MyOwnWebPage.git, HTTPS 在非交互环境无凭据会报could not read Username for 'https://github.com', 需手动用 Personal Access Token,因此已删除。若以后要恢复,可重新添加并改用 SSH:git remote add origin git@github.com:KarCharon/MyOwnWebPage.git git push origin main
行尾:仓库历史里部分文件曾从 Windows 带入 CRLF。在 WSL(LF)下编辑会让这些文件 整片显示为「已改动」,其实只是行尾差异(
git diff --ignore-all-space为空)。 提交前注意别把这类行尾噪声混进功能提交;要彻底消除可加.gitattributes(* text=auto eol=lf)。