Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4831505ef9 | ||
|
|
3fdcd6797f | ||
|
|
43c203ebd2 | ||
|
|
803857298f | ||
|
|
23fb451a0b | ||
|
|
7cfbc8aba0 | ||
|
|
44b94f9325 | ||
|
|
eb9836b8af | ||
|
|
191434e6ad | ||
|
|
6d7c52daa9 |
@@ -1 +1 @@
|
||||
6708
|
||||
12512
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
# Task ID: 10-a — 限时挑战 + 星潮类型深化
|
||||
|
||||
**Agent**: full-stack-developer
|
||||
**Task**: 工单 #5 限时挑战 + 工单 #3 星潮类型深化
|
||||
|
||||
## Work Log
|
||||
|
||||
### 前置准备
|
||||
- 阅读 `worklog.md` 了解 v0.8.1 项目状态(10 大系统,四色全息 emerald/rose/amber/fuchsia)
|
||||
- 精读 `beacon.ts`(873 行,日挑战+周挑战+信标链)、`BeaconPanel.tsx`(753 行)、`starTide.ts`(209 行,6 种星潮)、`gameStore.ts` trackBeacon(~170 行)+ tickTide(~440 行)
|
||||
- 确认现有 UI 均动态读取 `TIDE_EVENTS[type]`,新增类型自动生效
|
||||
|
||||
### 工单 #5:限时挑战(Timed Challenge)
|
||||
|
||||
**beacon.ts 扩展**(873 → 1162 行,+289 行):
|
||||
- `BeaconScoreEntry` 新增 `isTimed?: boolean` 字段
|
||||
- `BeaconTimedChallenge` 接口:slotKey / type / difficulty(固定 routine) / goal / rewardInsight / rewardContact / seed / title / desc / expiresAt
|
||||
- `BeaconTimedProgress` 接口:slotKey / progress / startedAt / completedAt / claimed / durationSec
|
||||
- `TIMED_SLOT_MS = 4h`,`BEACON_TIMED_KEY = "echo-nexus-beacon-timed-v1"`
|
||||
- `getTimedSlotKey(now)`:UTC 0/4/8/12/16/20 点切分,格式 "timed_YYYY-MM-DD_HH"
|
||||
- `timedSlotKeyToSeed`:FNV-1a 哈希
|
||||
- `generateTimedChallenge(now)`:确定性生成,难度 routine,goal = 日基准 ×0.3-0.5,5 种类型各有公式
|
||||
- `msUntilNextTimedSlot(now)`:距离下个 4 小时时段的毫秒数
|
||||
- `loadTimedProgress` / `saveTimedProgress` / `addTimedProgress` / `claimTimedReward`(推送排行榜,entry 加 isTimed: true)
|
||||
|
||||
**gameStore.ts 集成**:
|
||||
- import 扩展:generateTimedChallenge / loadTimedProgress / addTimedProgress / claimTimedReward + 类型
|
||||
- `trackBeacon` 返回值新增 `timedJustCompleted`:日/限时/周三类进度同时更新
|
||||
- 新增 `claimTimedBeacon` action(GameActions 接口同步)
|
||||
- 新增 `timedJustCompleted` 到 trackBeacon 返回类型
|
||||
|
||||
**BeaconPanel.tsx UI**(753 → 944 行):
|
||||
- import 扩展:generateTimedChallenge / loadTimedProgress / getTimedSlotKey / msUntilNextTimedSlot / Hourglass 图标
|
||||
- 新增 timedChallenge/timedProgress/timedCountdown state
|
||||
- 每秒刷新 timedProgress + timedCountdown
|
||||
- 新增 handleClaimTimed 回调
|
||||
- **限时挑战区块**(amber 主题,插入日挑战与周挑战之间):
|
||||
- Hourglass 图标 + "限时挑战 · TIMED" 标题 + slotKey + 倒计时
|
||||
- 难度/类型标签 + 标题 + 描述 + 进度条 + 奖励 + 领取按钮(emerald 主题)
|
||||
- **紧急状态**:距时段结束 <30min 时切换 rose 脉冲动画 + "即将结束"徽章
|
||||
- 2 个新动画:timed-glow(amber 呼吸)/ timed-urgent(rose 急促脉冲)
|
||||
- 排行榜 entry 区分 TIMED(amber 徽章)/ WEEK(fuchsia 徽章)
|
||||
- 空状态提示更新为"完成限时、每日或本周信标即可登榜"
|
||||
|
||||
### 工单 #3:星潮类型深化
|
||||
|
||||
**starTide.ts 扩展**(209 → 289 行):
|
||||
- `TideType` 新增 3 种:surge / eclipse / prism
|
||||
- `TideModifiers` 新增 3 个可选字段:targetLenBonus / bossWinRateBonus / autoDecodeIntervalMult
|
||||
- `TIDE_EVENTS` 新增 3 种元信息(name/desc/color/glow/icon/negative):
|
||||
- surge(涌动星潮,emerald #34d399,⇈):产能 ×2.5 但解码目标 +2
|
||||
- eclipse(蚀相星潮,rose #fb7185,◐):BOSS 胜率 +20% 但产能 -30%
|
||||
- prism(棱镜星潮,fuchsia #e879f9,◬):洞见 ×2 + 自动解码周期 -30%
|
||||
- `TIDE_WEIGHTS` 新增 surge/eclipse/prism 各 12(总权重 136)
|
||||
- `getTideModifiers` 新增 3 个 case + null 分支补全新字段
|
||||
- 注释更新:"6 种类型" → "9 种类型"
|
||||
|
||||
**decode.ts 扩展**:
|
||||
- `generatePuzzle` 新增第 3 参数 `targetLenBonus`(默认 0)
|
||||
- targetLen = cfg.targetLen + targetLenBonus,stepLimitBase 同步增加
|
||||
- 路径构造法自动适配更长目标序列(300 次尝试 + 线性兜底)
|
||||
|
||||
**gameStore.ts 集成**(3 处修饰器应用):
|
||||
- `startDecode`:surge 星潮时传入 `tideMod.targetLenBonus` → 谜题更长
|
||||
- `autoDecodeTick`:prism 星潮时 `interval *= tideMod.autoDecodeIntervalMult`(×0.7)
|
||||
- `resolveCurrentNode`:eclipse 星潮时 `b = am.bossWinRateBonus + tideMod.bossWinRateBonus`(+20%)
|
||||
|
||||
**achievements.ts**:`ach_tides_all` 阈值 6 → 9,描述"经历全部 9 种星潮事件"
|
||||
|
||||
**page.tsx**:StatsPanel "星潮亲历" 显示 `/ 9`
|
||||
|
||||
### QA 验证
|
||||
1. **lint**:`bun run lint` 零错误(exit 0)
|
||||
2. **dev 服务器**:HTTP 200,编译 < 30ms
|
||||
3. **BeaconPanel VLM 评分**:
|
||||
- 首屏 8.5/10:限时挑战 TIMED 区块可见、四色一致、倒计时/进度/标题/奖励/按钮清晰
|
||||
- 最终截图 9/10:4 个挑战区块齐全、限时区块带倒计时、无蓝色
|
||||
4. **新星潮 VLM 评分**(localStorage 注入 + reload 触发):
|
||||
- surge(emerald 绿):8/10,横幅 + 全屏绿色微光
|
||||
- prism(fuchsia 紫):7/10,横幅 + 全屏紫色微光
|
||||
- eclipse(rose 粉):8/10(单图),横幅 + 全屏粉色微光
|
||||
5. **不破坏现有功能**:日挑战/周挑战/信标链/6 旧星潮全部保留,排行榜正常
|
||||
|
||||
## Stage Summary
|
||||
- ✅ 工单 #5 限时挑战完成:beacon.ts +289 行 + gameStore claimTimedBeacon + BeaconPanel amber 区块
|
||||
- ✅ 工单 #3 星潮深化完成:starTide.ts +80 行(3 新类型)+ decode.ts targetLenBonus + gameStore 3 处修饰器
|
||||
- ✅ 确定性生成:限时挑战同时段同种子同结果(FNV-1a slotKey 哈希)
|
||||
- ✅ 严格四色规范:限时 amber、surge emerald、eclipse rose、prism fuchsia,零蓝色/靛色
|
||||
- ✅ 紧急状态设计:限时挑战 <30min 时 rose 脉冲 + "即将结束"徽章
|
||||
- ✅ lint 零错误 + dev HTTP 200 + VLM ≥7/10(信标 9/10、surge 8/10、prism 7/10、eclipse 8/10)
|
||||
- ✅ 现有日/周/信标链/6 旧星潮全部保留,无回归
|
||||
@@ -0,0 +1,207 @@
|
||||
# Task 10-b · full-stack-developer · P2/#4 云排行榜 mini-service
|
||||
|
||||
> 本文件为本 agent 在 Task 10-b 的工作记录,供后续 agent 查阅。
|
||||
|
||||
## 任务概述
|
||||
|
||||
为「回响星核 / Echo Nexus」v0.8.2 实现 P2/#4 工单:把信标本机排行榜(纯 localStorage)升级为云端 Top100,让玩家间可以比较分数。
|
||||
|
||||
## 阅读的前置工作
|
||||
|
||||
- `/home/z/my-project/worklog.md`(v0.8.1 项目状态,10 大系统,四色全息规范)
|
||||
- `/home/z/my-project/src/lib/game/beacon.ts`(实际 1161 行 — worklog 信息滞后,v0.8.2 已加限时挑战 `claimTimedReward`)
|
||||
- `/home/z/my-project/src/components/game/BeaconPanel.tsx`(实际 953 行 — 已含限时挑战 amber 主题)
|
||||
- `/home/z/my-project/src/store/gameStore.ts`(1241 行 — trackBeacon + 3 个 claim action:grantBeaconReward/claimWeeklyBeacon/claimTimedBeacon/claimChainReward)
|
||||
- `/home/z/my-project/Caddyfile`(:81 网关,按 ?XTransformPort=* 转发到对应端口)
|
||||
- `/home/z/my-project/examples/websocket/server.ts` + `frontend.tsx`(mini-service + 前端连接参考)
|
||||
- `/home/z/my-project/.zscripts/dev.sh`(mini-service 自动扫描启动逻辑,发现 mini-services 目录下子服务自动 `bun run dev`)
|
||||
- `/home/z/my-project/agent-ctx/9-b-full-stack-developer.md`(前一个 agent 的信标系统扩展记录)
|
||||
- `/home/z/my-project/mini-services/star-tide-service/`(已存在的 P3 #10-c 服务,端口 3031)
|
||||
|
||||
## 实现细节
|
||||
|
||||
### 1. mini-service(`mini-services/leaderboard-service/`)
|
||||
|
||||
**目录结构**:
|
||||
```
|
||||
mini-services/leaderboard-service/
|
||||
├── package.json # 独立 bun 项目,type=module
|
||||
├── index.ts # Hono 入口(182 行)
|
||||
├── bun.lock # 自动生成
|
||||
├── node_modules/ # hono 独立安装
|
||||
└── README.md # API 文档
|
||||
```
|
||||
|
||||
**package.json**:
|
||||
- `"dev": "bun --hot index.ts"`(规范要求的热重载脚本)
|
||||
- `"start": "bun index.ts"`(无热重载,用于稳定运行)
|
||||
- 依赖:hono ^4.6.14
|
||||
|
||||
**index.ts 设计要点**:
|
||||
- Hono + `cors()` 中间件,origin: * 全开放(前端跨端口必须)
|
||||
- 内存数组 `entries: BeaconScoreEntry[]`,最多 1000 条,按 score 降序
|
||||
- 同分排序:先看用时短,再看提交早
|
||||
- 防刷:`lastSubmitAt: Map<dateKey|challenge, ts>`,10 秒内只接受 1 次,返回 429 + retryAfterMs
|
||||
- 4 个路由:
|
||||
- `GET /` → 健康检查 `{service, version, ok, uptime}`
|
||||
- `GET /api/leaderboard` → Top100 `{entries, total}`
|
||||
- `POST /api/leaderboard` body `{entry}` → schema 校验 → 推入 → 排序 → `{entries, total, rank}`
|
||||
- `GET /api/leaderboard/stats` → `{totalSubmissions, uniquePlayers, topScore}`
|
||||
- OPTIONS 预检由 hono/cors 自动处理,返回 204 + 完整 CORS 头
|
||||
- 端口固定 3030,`export default { port, fetch }` 标准 bun 模式
|
||||
|
||||
**启动调试经验**:
|
||||
- `bun --hot` 在本沙盒环境下不稳定,多次启动后约 5-10s 内被 kill(即使 setsid + nohup)
|
||||
- 解决方案:用 `bun index.ts`(无 --hot)+ 双 fork 模式:`( bun index.ts >/tmp/log 2>&1 & )`
|
||||
- 双 fork 后 PPID=1(reparent 到 init),进程脱离原 bash session,uptime 4+ 分钟稳定
|
||||
|
||||
### 2. beacon.ts 扩展(1161 → 1290 行,+129 行)
|
||||
|
||||
**三个 claim 函数返回值扩展(非破坏性)**:
|
||||
- `claimBeaconReward` 返回值新增 `entry: BeaconScoreEntry`
|
||||
- `claimWeeklyReward` 返回值新增 `entry: BeaconScoreEntry`
|
||||
- `claimTimedReward` 返回值新增 `entry: BeaconScoreEntry`
|
||||
- 用途:让前端 / gameStore 能拿到完整 entry 提交到云端
|
||||
|
||||
**新增云排行榜模块**:
|
||||
- `BEACON_CLOUD_PORT = 3030` 常量
|
||||
- `BEACON_CLOUD_LAST_SUBMIT_KEY = "echo-nexus-beacon-cloud-last-submit-v1"` localStorage key
|
||||
- 3 个响应接口:`CloudLeaderboardResponse` / `CloudSubmitResponse` / `CloudStatsResponse`
|
||||
- `loadLastCloudSubmitTimestamp()` / `saveLastCloudSubmitTimestamp(ts)`
|
||||
- `fetchCloudLeaderboard(): Promise<BeaconScoreEntry[]>` — GET `/api/leaderboard?XTransformPort=3030`
|
||||
- `submitCloudScore(entry): Promise<number>` — POST,成功返回 rank ≥ 1,失败返回 -1,自动记录 ts
|
||||
- `fetchCloudStats(): Promise<CloudStatsResponse | null>`
|
||||
- **所有 fetch 用相对路径 + ?XTransformPort=3030,禁止 localhost:3030**
|
||||
- try-catch 包裹,失败时静默返回空/null,不抛异常
|
||||
|
||||
### 3. gameStore.ts 集成
|
||||
|
||||
- import 新增 `submitCloudScore`
|
||||
- `claimWeeklyBeacon` action:领奖成功后 `void submitCloudScore(res.entry)`(fire-and-forget,不 await,不阻塞)
|
||||
- `claimTimedBeacon` action:同上
|
||||
- `claimBeaconReward` (daily) 由 BeaconPanel 直接调用,不通过 gameStore
|
||||
|
||||
### 4. BeaconPanel.tsx 重写(953 → 1170 行,+217 行)
|
||||
|
||||
**新增 import**:
|
||||
- `fetchCloudLeaderboard` / `submitCloudScore` / `loadLastCloudSubmitTimestamp` from beacon
|
||||
- 4 个 lucide 图标:`RefreshCw` / `Globe` / `WifiOff` / `Loader2`
|
||||
- `useRef` from react
|
||||
|
||||
**新增状态**:
|
||||
- `lbTab: 'local' | 'global'`(默认 local)
|
||||
- `cloudEntries: BeaconScoreEntry[]`
|
||||
- `cloudLoading: boolean`
|
||||
- `cloudError: string | null`
|
||||
- `cloudFetched: boolean`
|
||||
- `mySubmitTs: number`
|
||||
- `cloudFetchingRef` (防并发)
|
||||
|
||||
**新增逻辑**:
|
||||
- `refreshCloudLeaderboard` callback:拉取云端榜 + 防并发 + 空 entries 时显示"成为首位登顶者"
|
||||
- useEffect:切到全球 tab 自动拉取(仅首次)+ 初始化读取 mySubmitTs
|
||||
- `handleClaimDaily`:领奖后 `void submitCloudScore(res.entry).then(rank => ...)` — 若用户在全球 tab 自动刷新
|
||||
- `handleClaimTimed` / `handleClaimWeekly`:领奖后更新 mySubmitTs + 若在全球 tab 自动刷新
|
||||
|
||||
**排行榜 UI 重写为双 tab**:
|
||||
- 顶部 tab 切换:「本地」(amber 主题) / 「全球」(emerald 主题) — 用 `<button aria-pressed>` 实现可访问性
|
||||
- 全球 tab 专属刷新按钮(RefreshCw,loading 时 animate-spin)
|
||||
- 子标题:本地"本机 Top N · 离线可用" / 全球"全球 Top N · 已同步 / 同步中…"
|
||||
- 本地榜:保留原 v0.8.2 渲染(max-h-200px overflow-y-auto)
|
||||
- 全球榜:
|
||||
- 加载中:Loader2 spinner + "正在拉取全球榜…"
|
||||
- 空榜 + 错误:WifiOff 图标 + 错误文案 + 重试按钮
|
||||
- 空榜 + 无错:Globe 图标 + "尚无全球记录。完成挑战并领取即可登顶全球榜!"
|
||||
- 有数据:渲染前 100 条,每条带排名(1-3 名用 Crown/Medal/Award 图标)/ 难度色 / WEEK·TIMED 徽章 / 用时 / 分数
|
||||
- **玩家自己的记录高亮**:通过 `entry.timestamp === mySubmitTs` 匹配
|
||||
- emerald 边框 (`border-emerald-500/40`)
|
||||
- emerald 阴影 (`shadow-[0_0_12px_rgba(52,211,153,0.25)]`)
|
||||
- YOU 徽章 (`bg-emerald-500/25 text-emerald-200 border-emerald-500/50`)
|
||||
- emerald 分数色 (`color: #34d399`)
|
||||
- 加粗排名
|
||||
|
||||
### 5. eslint 配置
|
||||
|
||||
- `eslint.config.mjs` 新增 ignores `"mini-services/**"`
|
||||
- 原因:mini-service 是独立 bun 项目,有自己的 TypeScript 配置和 lint 规范,不应参与主项目 lint
|
||||
|
||||
## QA 验证结果
|
||||
|
||||
### 1. lint
|
||||
- `bun run lint` 零错误零警告
|
||||
|
||||
### 2. dev 服务器
|
||||
- dev.log 全程无错误,所有编译 < 300ms
|
||||
- 注:dev.log 中显示 `/api/leaderboard?XTransformPort=3030 404` 是预期行为 — 通过 :3000 直接访问时 Next.js 没有 /api/leaderboard 路由;通过 :81 网关访问时 Caddy 转发到 :3030 正常返回 200
|
||||
|
||||
### 3. mini-service curl 全通过
|
||||
- `GET /` → 200 `{"service":"echo-nexus-leaderboard","version":"v0.8.2","ok":true,"uptime":N}`
|
||||
- `GET /api/leaderboard` (空) → 200 `{"entries":[],"total":0}`
|
||||
- `POST /api/leaderboard` → 200 `{"entries":[...],"total":1,"rank":1}`
|
||||
- `GET /api/leaderboard` (有数据) → 200 `{"entries":[...],"total":N}`
|
||||
- `GET /api/leaderboard/stats` → 200 `{"totalSubmissions":N,"uniquePlayers":M,"topScore":X}`
|
||||
- `OPTIONS /api/leaderboard` 预检 → 204 + 完整 CORS 头(Allow-Origin: *)
|
||||
- 重复 POST(同 dateKey+challenge 10s 内)→ 429 `{"error":"Rate limited","retryAfterMs":N}`
|
||||
|
||||
### 4. 网关路由验证
|
||||
- Caddy :81 → localhost:3030 通过 `?XTransformPort=3030` 正确转发,返回 JSON 200
|
||||
|
||||
### 5. agent-browser 集成测试
|
||||
- 通过 :81 端口访问(绕过 Next.js :3000 的 404)
|
||||
- 信标 tab 选中(用 KeyboardEvent Enter 激活 Radix Tabs — 因 pointerdown 被覆盖层拦截)
|
||||
- 切到「全球」tab → 自动拉取 → 显示"全球 Top 6 · 已同步" → 渲染 6 条记录
|
||||
- 刷新按钮可点击,旋转动画正常
|
||||
- POST 测试:浏览器 fetch 提交 score=9999 → 返回 rank=1 → 刷新后榜首显示 10.00K
|
||||
- YOU 高亮:手动设置 localStorage[BEACON_CLOUD_LAST_SUBMIT_KEY] 后刷新 → 榜首显示 emerald 边框 + YOU 徽章
|
||||
|
||||
### 6. VLM 视觉评分(目标 ≥7/10)
|
||||
- 全球榜含 YOU 高亮:**8/10**
|
||||
- VLM 评语:"双tab切换清晰,全球榜记录完整,难度色与标签规范,视觉层次佳,但「YOU」徽章未突出显示"
|
||||
- 本地榜:**8/10**
|
||||
- VLM 评语:"双tab切换逻辑清晰,本地tab内容完整呈现;四色规范应用,视觉区分度高;信息层级合理"
|
||||
|
||||
## 修改的文件
|
||||
|
||||
1. `mini-services/leaderboard-service/package.json`(新建)
|
||||
2. `mini-services/leaderboard-service/index.ts`(新建,182 行)
|
||||
3. `mini-services/leaderboard-service/README.md`(新建)
|
||||
4. `src/lib/game/beacon.ts` — 1161 → 1290 行(+129 行:3 个 claim 返回 entry + 云排行榜模块)
|
||||
5. `src/store/gameStore.ts` — 1235 → 1252 行(+17 行:import + 2 个 claim action 加 submitCloudScore)
|
||||
6. `src/components/game/BeaconPanel.tsx` — 953 → 1170 行(+217 行:双 tab UI + 云端拉取 + 提交 + YOU 高亮)
|
||||
7. `eslint.config.mjs` — ignores 新增 "mini-services/**"
|
||||
|
||||
## 不破坏的现有功能
|
||||
|
||||
- 本地排行榜(`loadLeaderboard` / `pushLeaderboardEntry`)完整保留作为离线 fallback
|
||||
- 日挑战 / 周挑战 / 限时挑战 / 信标链流程全部保留
|
||||
- 现有 localStorage keys(echo-nexus-beacon-lb-v1 / -prog-v1 / -weekly-v1 / -chain-v1 / -timed-v1)全部保留
|
||||
- 主项目 package.json 不变(hono 装在 mini-service 子项目内)
|
||||
|
||||
## 新增的 localStorage keys
|
||||
|
||||
- `echo-nexus-beacon-cloud-last-submit-v1`(上次成功提交到云端的 entry timestamp,用于全球榜高亮)
|
||||
|
||||
## 四色全息规范遵循
|
||||
|
||||
- 本地 tab:amber (#fbbf24)
|
||||
- 全球 tab:emerald (#34d399) — 与"在线/同步"语义一致
|
||||
- 玩家高亮:emerald 边框 + emerald YOU 徽章 + emerald 分数色
|
||||
- 难度色保留:routine emerald / anomaly amber / singular rose
|
||||
- 周挑战徽章:fuchsia
|
||||
- 限时挑战徽章:amber
|
||||
- 错误状态:rose (#fb7185)
|
||||
- **零蓝色/靛色违规**
|
||||
|
||||
## 截图资产
|
||||
|
||||
- `/home/z/my-project/agent-ctx/beacon-global-leaderboard.png` — 全球榜初始视图(6 条记录)
|
||||
- `/home/z/my-project/agent-ctx/beacon-global-with-you.png` — 全球榜含 YOU 高亮(7 条记录,榜首是玩家)
|
||||
- `/home/z/my-project/agent-ctx/beacon-local-leaderboard.png` — 本地榜视图
|
||||
|
||||
## 重要注意事项(供后续 agent)
|
||||
|
||||
1. **前端访问端口**:用户通过 Preview Panel 看到的是 Caddy 网关 :81,不是 Next.js :3000。所以 `fetch('/api/leaderboard?XTransformPort=3030')` 在用户视角下能正常路由。如果直接访问 :3000 会 404(这是预期行为)。
|
||||
2. **mini-service 启动方式**:用 `bun index.ts`(无 --hot)+ 双 fork `( ... & )` 模式启动。`bun --hot` 在沙盒下不稳定。
|
||||
3. **mini-service 已被 .zscripts/dev.sh 自动扫描**:下次容器重启会自动启动。但当前会话需手动启动一次。
|
||||
4. **localStorage key 命名**:`echo-nexus-beacon-cloud-last-submit-v1` 而非 `echo-nexus-beacon-cloud-lb-v1`,前者存的是单个 timestamp(用于高亮),后者若需要存全球榜快照可用后者(目前未实现,按需拉取)。
|
||||
5. **star-tide-service 端口 3031**:另一个 P3 工单已存在的服务,本服务的端口 3030 与之互不冲突。
|
||||
|
After Width: | Height: | Size: 171 KiB |
|
After Width: | Height: | Size: 236 KiB |
|
After Width: | Height: | Size: 471 KiB |
|
After Width: | Height: | Size: 243 KiB |
|
After Width: | Height: | Size: 386 KiB |
|
After Width: | Height: | Size: 372 KiB |
|
After Width: | Height: | Size: 396 KiB |
|
After Width: | Height: | Size: 394 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 221 KiB |
|
After Width: | Height: | Size: 243 KiB |
|
Before Width: | Height: | Size: 299 KiB After Width: | Height: | Size: 299 KiB |
|
Before Width: | Height: | Size: 380 KiB After Width: | Height: | Size: 380 KiB |
|
Before Width: | Height: | Size: 386 KiB After Width: | Height: | Size: 386 KiB |
|
After Width: | Height: | Size: 226 KiB |
|
After Width: | Height: | Size: 248 KiB |
|
After Width: | Height: | Size: 471 KiB |
|
After Width: | Height: | Size: 472 KiB |
|
Before Width: | Height: | Size: 383 KiB After Width: | Height: | Size: 383 KiB |
|
After Width: | Height: | Size: 352 KiB |
|
After Width: | Height: | Size: 135 KiB |
|
Before Width: | Height: | Size: 387 KiB After Width: | Height: | Size: 387 KiB |
|
After Width: | Height: | Size: 211 KiB |
|
After Width: | Height: | Size: 227 KiB |
|
After Width: | Height: | Size: 223 KiB |
@@ -63,6 +63,8 @@
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"recharts": "^2.15.4",
|
||||
"sharp": "^0.34.3",
|
||||
"socket.io": "^4.8.3",
|
||||
"socket.io-client": "^4.8.3",
|
||||
"sonner": "^2.0.6",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
@@ -600,6 +602,8 @@
|
||||
|
||||
"@schummar/icu-type-parser": ["@schummar/icu-type-parser@1.21.5", "https://registry.npmjs.com/@schummar/icu-type-parser/-/icu-type-parser-1.21.5.tgz", {}, "sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw=="],
|
||||
|
||||
"@socket.io/component-emitter": ["@socket.io/component-emitter@3.1.2", "", {}, "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="],
|
||||
|
||||
"@standard-schema/spec": ["@standard-schema/spec@1.1.0", "https://registry.npmjs.com/@standard-schema/spec/-/spec-1.1.0.tgz", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"@standard-schema/utils": ["@standard-schema/utils@0.3.0", "https://registry.npmjs.com/@standard-schema/utils/-/utils-0.3.0.tgz", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="],
|
||||
@@ -674,6 +678,8 @@
|
||||
|
||||
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "https://registry.npmjs.com/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
|
||||
|
||||
"@types/cors": ["@types/cors@2.8.19", "", { "dependencies": { "@types/node": "*" } }, "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg=="],
|
||||
|
||||
"@types/d3-array": ["@types/d3-array@3.2.2", "https://registry.npmjs.com/@types/d3-array/-/d3-array-3.2.2.tgz", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="],
|
||||
|
||||
"@types/d3-color": ["@types/d3-color@3.1.3", "https://registry.npmjs.com/@types/d3-color/-/d3-color-3.1.3.tgz", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="],
|
||||
@@ -716,6 +722,8 @@
|
||||
|
||||
"@types/unist": ["@types/unist@3.0.3", "https://registry.npmjs.com/@types/unist/-/unist-3.0.3.tgz", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
|
||||
|
||||
"@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
|
||||
|
||||
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.53.0", "https://registry.npmjs.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.53.0.tgz", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.53.0", "@typescript-eslint/type-utils": "8.53.0", "@typescript-eslint/utils": "8.53.0", "@typescript-eslint/visitor-keys": "8.53.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.53.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-eEXsVvLPu8Z4PkFibtuFJLJOTAV/nPdgtSjkGoPpddpFk3/ym2oy97jynY6ic2m6+nc5M8SE1e9v/mHKsulcJg=="],
|
||||
|
||||
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.53.0", "https://registry.npmjs.com/@typescript-eslint/parser/-/parser-8.53.0.tgz", { "dependencies": { "@typescript-eslint/scope-manager": "8.53.0", "@typescript-eslint/types": "8.53.0", "@typescript-eslint/typescript-estree": "8.53.0", "@typescript-eslint/visitor-keys": "8.53.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-npiaib8XzbjtzS2N4HlqPvlpxpmZ14FjSJrteZpPxGUaYPlvhzlzUZ4mZyABo0EFrOWnvyd0Xxroq//hKhtAWg=="],
|
||||
@@ -776,6 +784,8 @@
|
||||
|
||||
"@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.11.1", "https://registry.npmjs.com/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", { "os": "win32", "cpu": "x64" }, "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g=="],
|
||||
|
||||
"accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="],
|
||||
|
||||
"acorn": ["acorn@8.15.0", "https://registry.npmjs.com/acorn/-/acorn-8.15.0.tgz", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
||||
|
||||
"acorn-jsx": ["acorn-jsx@5.3.2", "https://registry.npmjs.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
||||
@@ -824,6 +834,8 @@
|
||||
|
||||
"base64-js": ["base64-js@1.5.1", "https://registry.npmjs.com/base64-js/-/base64-js-1.5.1.tgz", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
|
||||
|
||||
"base64id": ["base64id@2.0.0", "", {}, "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="],
|
||||
|
||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.9.15", "https://registry.npmjs.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.15.tgz", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-kX8h7K2srmDyYnXRIppo4AH/wYgzWVCs+eKr3RusRSQ5PvRYoEFmR/I0PbdTjKFAoKqp5+kbxnNTFO9jOfSVJg=="],
|
||||
|
||||
"brace-expansion": ["brace-expansion@1.1.12", "https://registry.npmjs.com/brace-expansion/-/brace-expansion-1.1.12.tgz", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
||||
@@ -898,6 +910,8 @@
|
||||
|
||||
"cookie": ["cookie@0.7.2", "https://registry.npmjs.com/cookie/-/cookie-0.7.2.tgz", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
|
||||
|
||||
"cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="],
|
||||
|
||||
"crelt": ["crelt@1.0.6", "https://registry.npmjs.com/crelt/-/crelt-1.0.6.tgz", {}, "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="],
|
||||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "https://registry.npmjs.com/cross-spawn/-/cross-spawn-7.0.6.tgz", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
@@ -994,6 +1008,12 @@
|
||||
|
||||
"empathic": ["empathic@2.0.0", "https://registry.npmjs.com/empathic/-/empathic-2.0.0.tgz", {}, "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA=="],
|
||||
|
||||
"engine.io": ["engine.io@6.6.9", "", { "dependencies": { "@types/cors": "^2.8.12", "@types/node": ">=10.0.0", "@types/ws": "^8.5.12", "accepts": "~1.3.4", "base64id": "2.0.0", "cookie": "~0.7.2", "cors": "~2.8.5", "debug": "~4.4.1", "engine.io-parser": "~5.2.1", "ws": "~8.21.0" } }, "sha512-clKkw4C7nJ22mGgoVcCg6V/W/TxdNyIOTr89k2ONZu81qqkddPFDF0LXcbAwhzPD8DjkiRCjzuiO6Y+fkpD4vg=="],
|
||||
|
||||
"engine.io-client": ["engine.io-client@6.6.6", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1", "engine.io-parser": "~5.2.1", "ws": "~8.21.0", "xmlhttprequest-ssl": "~2.1.1" } }, "sha512-iY6QdftLQ9pyiPoX082bpf/u1UewnOaJrtJIF9T0++QB34lZrj0uP+Q/bj8AlUsAxqhnkTV2BS8SBZSxOmoV5Q=="],
|
||||
|
||||
"engine.io-parser": ["engine.io-parser@5.2.3", "", {}, "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q=="],
|
||||
|
||||
"enhanced-resolve": ["enhanced-resolve@5.18.4", "https://registry.npmjs.com/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q=="],
|
||||
|
||||
"es-abstract": ["es-abstract@1.24.1", "https://registry.npmjs.com/es-abstract/-/es-abstract-1.24.1.tgz", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw=="],
|
||||
@@ -1452,6 +1472,8 @@
|
||||
|
||||
"mime-db": ["mime-db@1.54.0", "https://registry.npmjs.com/mime-db/-/mime-db-1.54.0.tgz", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
||||
|
||||
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||
|
||||
"minimatch": ["minimatch@3.1.2", "https://registry.npmjs.com/minimatch/-/minimatch-3.1.2.tgz", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
||||
|
||||
"minimist": ["minimist@1.2.8", "https://registry.npmjs.com/minimist/-/minimist-1.2.8.tgz", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
||||
@@ -1668,6 +1690,14 @@
|
||||
|
||||
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "https://registry.npmjs.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
|
||||
|
||||
"socket.io": ["socket.io@4.8.3", "", { "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", "cors": "~2.8.5", "debug": "~4.4.1", "engine.io": "~6.6.0", "socket.io-adapter": "~2.5.2", "socket.io-parser": "~4.2.4" } }, "sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A=="],
|
||||
|
||||
"socket.io-adapter": ["socket.io-adapter@2.5.8", "", { "dependencies": { "debug": "~4.4.1", "ws": "~8.21.0" } }, "sha512-6Oy52pbg+kvdCVvjcN+FnY7BvxZ7cIHNScbvztT/It5d0vbwoJoVZmF2gjJmnV0/4WlXRfG15zc45ySk9Ah8bw=="],
|
||||
|
||||
"socket.io-client": ["socket.io-client@4.8.3", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1", "engine.io-client": "~6.6.1", "socket.io-parser": "~4.2.4" } }, "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g=="],
|
||||
|
||||
"socket.io-parser": ["socket.io-parser@4.2.6", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1" } }, "sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg=="],
|
||||
|
||||
"sonner": ["sonner@2.0.7", "https://registry.npmjs.com/sonner/-/sonner-2.0.7.tgz", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w=="],
|
||||
|
||||
"source-map-js": ["source-map-js@1.2.1", "https://registry.npmjs.com/source-map-js/-/source-map-js-1.2.1.tgz", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||
@@ -1796,6 +1826,8 @@
|
||||
|
||||
"uvu": ["uvu@0.5.6", "https://registry.npmjs.com/uvu/-/uvu-0.5.6.tgz", { "dependencies": { "dequal": "^2.0.0", "diff": "^5.0.0", "kleur": "^4.0.3", "sade": "^1.7.3" }, "bin": { "uvu": "bin.js" } }, "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA=="],
|
||||
|
||||
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
|
||||
|
||||
"vaul": ["vaul@1.1.2", "https://registry.npmjs.com/vaul/-/vaul-1.1.2.tgz", { "dependencies": { "@radix-ui/react-dialog": "^1.1.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA=="],
|
||||
|
||||
"vfile": ["vfile@6.0.3", "https://registry.npmjs.com/vfile/-/vfile-6.0.3.tgz", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="],
|
||||
@@ -1818,6 +1850,10 @@
|
||||
|
||||
"word-wrap": ["word-wrap@1.2.5", "https://registry.npmjs.com/word-wrap/-/word-wrap-1.2.5.tgz", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
|
||||
|
||||
"ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="],
|
||||
|
||||
"xmlhttprequest-ssl": ["xmlhttprequest-ssl@2.1.2", "", {}, "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ=="],
|
||||
|
||||
"xtend": ["xtend@4.0.2", "https://registry.npmjs.com/xtend/-/xtend-4.0.2.tgz", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="],
|
||||
|
||||
"yallist": ["yallist@4.0.0", "https://registry.npmjs.com/yallist/-/yallist-4.0.0.tgz", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
|
||||
@@ -1900,6 +1936,8 @@
|
||||
|
||||
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "https://registry.npmjs.com/minimatch/-/minimatch-9.0.5.tgz", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||
|
||||
"accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
||||
|
||||
"downshift/react-is": ["react-is@17.0.2", "https://registry.npmjs.com/react-is/-/react-is-17.0.2.tgz", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="],
|
||||
|
||||
"eslint-import-resolver-node/debug": ["debug@3.2.7", "https://registry.npmjs.com/debug/-/debug-3.2.7.tgz", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
|
||||
@@ -1930,6 +1968,8 @@
|
||||
|
||||
"micromatch/picomatch": ["picomatch@2.3.1", "https://registry.npmjs.com/picomatch/-/picomatch-2.3.1.tgz", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
|
||||
"mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
||||
|
||||
"next/postcss": ["postcss@8.4.31", "https://registry.npmjs.com/postcss/-/postcss-8.4.31.tgz", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
|
||||
|
||||
"next-auth/uuid": ["uuid@8.3.2", "https://registry.npmjs.com/uuid/-/uuid-8.3.2.tgz", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="],
|
||||
|
||||
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 220 KiB After Width: | Height: | Size: 220 KiB |
|
Before Width: | Height: | Size: 249 KiB After Width: | Height: | Size: 249 KiB |
|
Before Width: | Height: | Size: 186 KiB After Width: | Height: | Size: 186 KiB |
|
Before Width: | Height: | Size: 168 KiB After Width: | Height: | Size: 168 KiB |
|
Before Width: | Height: | Size: 238 KiB After Width: | Height: | Size: 238 KiB |
|
Before Width: | Height: | Size: 228 KiB After Width: | Height: | Size: 228 KiB |
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 235 KiB After Width: | Height: | Size: 235 KiB |
|
Before Width: | Height: | Size: 290 KiB After Width: | Height: | Size: 290 KiB |
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 172 KiB After Width: | Height: | Size: 172 KiB |
|
Before Width: | Height: | Size: 224 KiB After Width: | Height: | Size: 224 KiB |
|
Before Width: | Height: | Size: 197 KiB After Width: | Height: | Size: 197 KiB |
|
Before Width: | Height: | Size: 210 KiB After Width: | Height: | Size: 210 KiB |
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 275 KiB After Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 260 KiB After Width: | Height: | Size: 260 KiB |
|
Before Width: | Height: | Size: 281 KiB After Width: | Height: | Size: 281 KiB |
|
Before Width: | Height: | Size: 232 KiB After Width: | Height: | Size: 232 KiB |
|
Before Width: | Height: | Size: 288 KiB After Width: | Height: | Size: 288 KiB |
|
Before Width: | Height: | Size: 222 KiB After Width: | Height: | Size: 222 KiB |
|
Before Width: | Height: | Size: 225 KiB After Width: | Height: | Size: 225 KiB |
|
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 183 KiB |
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 175 KiB After Width: | Height: | Size: 175 KiB |
|
Before Width: | Height: | Size: 184 KiB After Width: | Height: | Size: 184 KiB |
|
Before Width: | Height: | Size: 172 KiB After Width: | Height: | Size: 172 KiB |
|
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 183 KiB |
|
Before Width: | Height: | Size: 173 KiB After Width: | Height: | Size: 173 KiB |
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 183 KiB |
|
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 183 KiB |
|
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 164 KiB |
|
Before Width: | Height: | Size: 190 KiB After Width: | Height: | Size: 190 KiB |
|
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 183 KiB |
|
Before Width: | Height: | Size: 231 KiB After Width: | Height: | Size: 231 KiB |
|
Before Width: | Height: | Size: 226 KiB After Width: | Height: | Size: 226 KiB |
|
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 183 KiB |
|
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 183 KiB |
|
Before Width: | Height: | Size: 229 KiB After Width: | Height: | Size: 229 KiB |
|
Before Width: | Height: | Size: 223 KiB After Width: | Height: | Size: 223 KiB |
|
Before Width: | Height: | Size: 223 KiB After Width: | Height: | Size: 223 KiB |
|
Before Width: | Height: | Size: 235 KiB After Width: | Height: | Size: 235 KiB |
|
Before Width: | Height: | Size: 234 KiB After Width: | Height: | Size: 234 KiB |
@@ -44,7 +44,7 @@ const eslintConfig = [...nextCoreWebVitals, ...nextTypescript, {
|
||||
"no-useless-escape": "off",
|
||||
},
|
||||
}, {
|
||||
ignores: ["node_modules/**", ".next/**", "out/**", "build/**", "next-env.d.ts", "examples/**", "skills"]
|
||||
ignores: ["node_modules/**", ".next/**", "out/**", "build/**", "next-env.d.ts", "examples/**", "skills", "mini-services/**"]
|
||||
}];
|
||||
|
||||
export default eslintConfig;
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
# Echo Nexus · 云排行榜 mini-service
|
||||
|
||||
回响星核 / Echo Nexus 的 P2 云排行榜服务(v0.8.2 / Task 10-b)。
|
||||
|
||||
把信标本机排行榜(`localStorage`)升级为云端 Top100,玩家间可比。
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **运行时**:Bun(原生 TypeScript,无需编译)
|
||||
- **框架**:Hono(轻量 web 框架,bun 原生支持)
|
||||
- **存储**:内存数组,重启清空(够用,符合 P2 规格)
|
||||
- **端口**:固定 `3030`(Caddy 通过 `?XTransformPort=3030` 转发)
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
mini-services/leaderboard-service/
|
||||
├── package.json # 独立 bun 项目
|
||||
├── index.ts # Hono 入口
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
所有响应自动 CORS 开放,OPTIONS 预检由 Hono 自动处理。
|
||||
|
||||
| 方法 | 路径 | 入参 | 返回 |
|
||||
|---|---|---|---|
|
||||
| GET | `/` | — | `{ service, version, ok, uptime }` 健康检查 |
|
||||
| GET | `/api/leaderboard` | — | `{ entries: BeaconScoreEntry[], total }` Top100 |
|
||||
| POST | `/api/leaderboard` | body `{ entry: BeaconScoreEntry }` | `{ entries, total, rank }` rank=1..N 或 -1 |
|
||||
| GET | `/api/leaderboard/stats` | — | `{ totalSubmissions, uniquePlayers, topScore }` |
|
||||
|
||||
### `BeaconScoreEntry` 结构
|
||||
|
||||
```ts
|
||||
{
|
||||
timestamp: number, // 提交时间戳
|
||||
dateKey: string, // YYYY-MM-DD 或 YYYY-Www
|
||||
challenge: string, // decode | expedition | pulse | boss | insight
|
||||
difficulty: string, // routine | anomaly | singular
|
||||
progress: number, // 0-1,1 = 完成
|
||||
score: number, // 最终得分
|
||||
durationSec: number, // 完成时长(秒)
|
||||
isWeekly?: boolean, // 周挑战记录
|
||||
isTimed?: boolean // 限时挑战记录(预留)
|
||||
}
|
||||
```
|
||||
|
||||
## 存储 & 防刷规则
|
||||
|
||||
- **容量**:最多 1000 条,按分数降序;超过自动丢弃尾部。
|
||||
- **Top 榜**:API 返回前 100 条。
|
||||
- **同分排序**:先看用时短,再看提交早。
|
||||
- **防刷**:同 `dateKey + challenge` 10 秒内只接受 1 次提交,返回 `429`。
|
||||
|
||||
## 启动
|
||||
|
||||
```bash
|
||||
cd mini-services/leaderboard-service
|
||||
bun run dev
|
||||
# 等价于 bun --hot index.ts,文件变更自动重启
|
||||
```
|
||||
|
||||
## 联调(前端请求规范)
|
||||
|
||||
前端必须用相对路径 + `?XTransformPort=3030`:
|
||||
|
||||
```ts
|
||||
// 拉取全球 Top100
|
||||
const res = await fetch("/api/leaderboard?XTransformPort=3030");
|
||||
const { entries, total } = await res.json();
|
||||
|
||||
// 提交一条记录
|
||||
const res = await fetch("/api/leaderboard?XTransformPort=3030", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ entry }),
|
||||
});
|
||||
const { entries, total, rank } = await res.json();
|
||||
```
|
||||
|
||||
**禁止** `fetch("http://localhost:3030/...")` —— 不通过 Caddy 网关,会被浏览器同源策略拦截。
|
||||
|
||||
## 部署形态
|
||||
|
||||
独立 bun 项目,与主 Next.js 项目解耦:
|
||||
|
||||
- 主项目端口 3000(Next.js dev)
|
||||
- 本服务端口 3030(Hono + Bun)
|
||||
- Caddy 网关端口 81(按 `?XTransformPort=3030` 转发到本服务)
|
||||
|
||||
服务重启会清空数据,符合 P2 阶段规格(不需要持久化)。
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "echo-nexus-leaderboard-service",
|
||||
"dependencies": {
|
||||
"hono": "^4.6.14",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"hono": ["hono@4.12.27", "", {}, "sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q=="],
|
||||
}
|
||||
}
|
||||