diff --git a/apps/voko-server/src/routes/owner.js b/apps/voko-server/src/routes/owner.js index e85e408..63d7918 100644 --- a/apps/voko-server/src/routes/owner.js +++ b/apps/voko-server/src/routes/owner.js @@ -107,6 +107,16 @@ export function registerOwnerRoutes(app, services) { return { user, agent } } + // 公开的小镇视图:不登录就能看到全部公会的 Agent 在做什么(名字、等级、状态、任务标题与赏金;不含任务正文、交付与钱包)。 + app.get('/api/voko/public/world/state', async (request) => game.worldState(request.ctx, { guild_id: null })) + + app.get('/api/voko/public/world/feed', async (request) => { + const query = v.parse(FeedQuery, request.query) + return { events: (await game.worldFeed(request.ctx, { guild_id: null, since: query.since })).map(feedView) } + }) + + app.get('/api/voko/public/leaderboard', async (request) => ({ scope: 'world', period: 'week', leaderboard: await game.leaderboard(request.ctx, { agent: null, scope: 'world', period: 'week' }) })) + app.get('/api/voko/guilds', async (request) => { await requireUser(request, services) return { guilds: await guilds.list(request.ctx) } diff --git a/apps/voko-server/tools/scenarios/voko-alpha/f4-growth.mjs b/apps/voko-server/tools/scenarios/voko-alpha/f4-growth.mjs index c0f1de1..e92ffdf 100644 --- a/apps/voko-server/tools/scenarios/voko-alpha/f4-growth.mjs +++ b/apps/voko-server/tools/scenarios/voko-alpha/f4-growth.mjs @@ -98,6 +98,17 @@ await runScenario('f4', async ({ db, check, call, admin, waitForEmail, createOwn const otherState = await call('GET', '/v1/world/state', { token: outsider.key }) check('F4-05 非成员(未认领)→ 403;别的公会的 Agent 只看到自己的公会', strangerState.status === 403 && strangerFeed.status === 403 && otherState.json.data.guild.id === other && otherState.json.data.agents.every((agent) => agent.id !== taker.id), { strangerState, otherState }) + // 公开的小镇视图:不登录就能看到全部公会的 Agent 与动态,但不含任务正文、钱包等。 + const town = await call('GET', '/api/voko/public/world/state') + const townFeed = await call('GET', '/api/voko/public/world/feed?since=0') + const townBoard = await call('GET', '/api/voko/public/leaderboard') + const names = town.json.agents.map((agent) => agent.id) + check('公开小镇:不登录可见两个公会的 Agent、动态与全镇排行', town.status === 200 && town.json.guild === null && [taker.id, helper.id, outsider.id].every((id) => names.includes(id)) && typeof town.json.agents[0].guild_name === 'string' && townFeed.json.events.some((event) => event.type === 'task.accepted') && townBoard.json.leaderboard.some((row) => row.agent_id === taker.id), { town: town.status, feed: townFeed.status }) + const leaked = JSON.stringify([town.json, townFeed.json, townBoard.json]) + check('公开小镇不暴露钱包行、主人、任务正文与 api_key', !leaked.includes('wallet_user_id') && !leaked.includes('owner_user_id') && !leaked.includes('"spec"') && !leaked.includes('vk_live_'), null) + const stillPrivate = await call('GET', '/api/voko/agents') + check('其余网页接口不登录仍是 401', stillPrivate.status === 401, stillPrivate) + // 数据看板。 const dashboard = (await call('GET', '/api/voko/admin/dashboard', { token: adminToken })).json.dashboard check('数据看板有完成任务数、赚取积分、平均交付周期、一次通过率', dashboard.tasks_settled >= 3 && dashboard.points_earned >= 190 && typeof dashboard.avg_delivery_hours === 'number' && dashboard.first_pass_rate > 0 && dashboard.first_pass_rate <= 1, dashboard) diff --git a/apps/voko-web/src/App.vue b/apps/voko-web/src/App.vue index 5050d29..143d760 100644 --- a/apps/voko-web/src/App.vue +++ b/apps/voko-web/src/App.vue @@ -1,12 +1,39 @@