hyapp-server/docs/api-test-harness.html
2026-05-11 20:01:55 +08:00

2795 lines
91 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>HY API Flow Harness</title>
<style>
:root {
--bg: #f3eadc;
--panel: #fffaf1;
--ink: #20251f;
--muted: #687064;
--line: #d8cbb5;
--accent: #0b6b56;
--accent-2: #c95f2b;
--danger: #a83232;
--code: #12231f;
--ok: #1f7a48;
}
* { box-sizing: border-box; }
body {
margin: 0;
color: var(--ink);
background:
radial-gradient(circle at 10% 0%, rgba(201, 95, 43, .18), transparent 26rem),
radial-gradient(circle at 92% 4%, rgba(11, 107, 86, .16), transparent 28rem),
linear-gradient(180deg, #f8f0e4 0%, var(--bg) 100%);
font-family: ui-serif, Georgia, "Times New Roman", serif;
}
header { padding: 26px clamp(18px, 4vw, 56px) 18px; border-bottom: 1px solid var(--line); }
h1 { margin: 0 0 8px; font-size: clamp(30px, 4vw, 50px); line-height: .96; letter-spacing: -.04em; }
h2 { margin: 0 0 14px; font-size: 20px; }
h3 { margin: 0 0 8px; font-size: 16px; }
p { margin: 0; color: var(--muted); }
main { padding: 18px clamp(14px, 3vw, 36px) 40px; display: grid; gap: 18px; }
section, .card { background: color-mix(in srgb, var(--panel) 94%, white 6%); border: 1px solid var(--line); border-radius: 18px; box-shadow: 0 16px 50px rgba(71, 50, 27, .08); }
section { padding: 18px; }
.grid { display: grid; gap: 12px; }
.grid.cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
label { display: grid; gap: 6px; font: 650 12px/1.2 ui-sans-serif, system-ui, sans-serif; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; }
input, select, textarea { width: 100%; border: 1px solid var(--line); border-radius: 11px; padding: 10px 11px; background: #fffdf8; color: var(--ink); font: 13px/1.45 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
textarea { min-height: 112px; resize: vertical; }
button { border: 0; border-radius: 999px; padding: 10px 14px; cursor: pointer; background: var(--accent); color: white; font: 750 13px/1 ui-sans-serif, system-ui, sans-serif; }
button.secondary { background: #2f3935; }
button.warn { background: var(--accent-2); }
button.danger { background: var(--danger); }
button:disabled { opacity: .55; cursor: not-allowed; }
code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
.row { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.toolbar { display: flex; gap: 10px; flex-wrap: wrap; align-items: end; }
.pill { display: inline-flex; align-items: center; border-radius: 999px; padding: 5px 9px; background: #ebe1cf; font: 750 12px/1 ui-sans-serif, system-ui, sans-serif; color: #3e4a43; }
.pill.get { color: #0c5d8f; } .pill.post { color: #8b4d00; } .pill.put { color: #6c4aa4; } .pill.patch { color: #866200; } .pill.delete { color: #9d2626; } .pill.client { color: #0b6b56; } .pill.manual { color: #9a4c19; }
.note { border-left: 4px solid var(--accent-2); padding: 10px 12px; background: #fff4e6; border-radius: 12px; color: #60412b; }
.flow-steps, .endpoint-list { display: grid; gap: 10px; }
.step, .endpoint { padding: 12px; }
.step-head, .endpoint-head { display: grid; grid-template-columns: auto auto 1fr auto; gap: 10px; align-items: center; }
.path { font: 750 13px/1.35 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; word-break: break-all; }
.summary { margin-top: 6px; font-size: 13px; color: var(--muted); }
.details { display: none; margin-top: 12px; border-top: 1px dashed var(--line); padding-top: 12px; }
.open .details { display: grid; gap: 10px; }
pre#log { margin: 0; min-height: 260px; max-height: 520px; overflow: auto; white-space: pre-wrap; background: var(--code); color: #eaf7ee; border-radius: 14px; padding: 14px; font: 12px/1.55 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
@media (max-width: 900px) { .grid.cols-2, .grid.cols-3 { grid-template-columns: 1fr; } .step-head, .endpoint-head { grid-template-columns: auto 1fr; } }
</style>
</head>
<body>
<header>
<h1>HY API Flow Harness</h1>
<p>页面只保留接口调用器形态:流程链路把 API、客户端 SDK 动作、人工校验按顺序列清楚;可逐步执行,也可执行当前链路里的 API 步骤。</p>
</header>
<main>
<section>
<h2>调用配置</h2>
<div class="note">建议在仓库根目录执行 <code>python3 -m http.server 19090</code>,然后访问 <code>http://localhost:19090/docs/api-test-harness.html</code>。所有业务接口仍以 gateway/admin 实际 CORS 配置为准。</div>
<div class="grid cols-3" style="margin-top:12px">
<label>Gateway Base URL <input id="gatewayBase" value="http://localhost:13000" /></label>
<label>Admin Base URL <input id="adminBase" value="http://localhost:13100" /></label>
<label>App Code <input id="appCode" value="lalu" /></label>
<label>App Access Token <input id="accessToken" placeholder="app bearer token without prefix" /></label>
<label>Refresh Token <input id="refreshToken" /></label>
<label>Admin Token <input id="adminToken" placeholder="admin bearer token without prefix" /></label>
<label>Account / Display ID <input id="account" value="123456" /></label>
<label>Password <input id="password" value="1234567" /></label>
<label>Device ID <input id="deviceId" value="web-smoke-device" /></label>
<label>Room ID <input id="roomId" value="" placeholder="CreateRoom 成功后自动回填" /></label>
<label>Room Version <input id="roomVersion" value="0" /></label>
<label>Mic Session ID <input id="micSessionId" value="" /></label>
<label>Seat No <input id="seatNo" value="1" /></label>
<label>Target User ID <input id="targetUserId" value="0" /></label>
<label>Display User ID <input id="displayUserId" value="123456" /></label>
<label>Resource ID <input id="resourceId" value="1" /></label>
<label>Resource Group ID <input id="groupId" value="1" /></label>
<label>Message ID <input id="messageId" value="1" /></label>
<label>Country <input id="country" value="US" /></label>
</div>
<div class="row" style="margin-top:12px">
<button id="saveConfig">保存配置</button>
<button id="clearTokens" class="secondary">清空 token</button>
<button id="clearLog" class="secondary">清空日志</button>
</div>
</section>
<section>
<h2>流程化接口调用器</h2>
<div class="toolbar">
<label>链路 <select id="flowSelect"></select></label>
<button id="renderFlow" class="secondary">刷新链路</button>
<button id="runFlowApi" class="warn">顺序执行 API 步骤</button>
</div>
<div id="flowDescription" class="summary" style="margin:10px 0"></div>
<div id="flowSteps" class="flow-steps"></div>
</section>
<section>
<h2>全部接口调用器</h2>
<div class="toolbar">
<label>来源 <select id="sourceFilter"><option value="all">全部 HTTP</option><option value="gateway">gateway</option><option value="admin">admin</option><option value="internal">内部 gRPC 引用</option></select></label>
<label>方法 <select id="methodFilter"><option value="all">全部</option><option>GET</option><option>POST</option><option>PUT</option><option>PATCH</option><option>DELETE</option></select></label>
<label>关键字 <input id="search" placeholder="path / summary / operationId" /></label>
<button id="renderEndpoints" class="secondary">刷新列表</button>
</div>
<div id="endpointCount" class="summary" style="margin:10px 0"></div>
<div id="endpoints" class="endpoint-list"></div>
</section>
<section>
<h2>日志</h2>
<pre id="log"></pre>
</section>
</main>
<script>
const GATEWAY_ENDPOINTS = [
{
"source": "gateway",
"executable": true,
"method": "GET",
"path": "/healthz/live",
"operationId": "gatewayLive",
"summary": "进程存活探测",
"tag": "health"
},
{
"source": "gateway",
"executable": true,
"method": "GET",
"path": "/healthz/ready",
"operationId": "gatewayReady",
"summary": "接流量就绪探测",
"tag": "health"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/auth/account/login",
"operationId": "loginAccountPassword",
"summary": "账号密码登录",
"tag": "auth"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/auth/third-party/login",
"operationId": "loginThirdParty",
"summary": "三方登录或注册",
"tag": "auth"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/auth/password/set",
"operationId": "setPassword",
"summary": "已登录用户首次设置密码",
"tag": "auth"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/auth/token/refresh",
"operationId": "refreshToken",
"summary": "刷新 token",
"tag": "auth"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/auth/logout",
"operationId": "logout",
"summary": "登出并失效 refresh session",
"tag": "auth"
},
{
"source": "gateway",
"executable": true,
"method": "GET",
"path": "/api/v1/countries",
"operationId": "listRegistrationCountries",
"summary": "获取 App 注册页可选国家",
"tag": "countries"
},
{
"source": "gateway",
"executable": true,
"method": "GET",
"path": "/api/v1/app/bootstrap",
"operationId": "getAppBootstrap",
"summary": "获取 App 启动配置摘要",
"tag": "app-config"
},
{
"source": "gateway",
"executable": true,
"method": "GET",
"path": "/api/v1/app/h5-links",
"operationId": "listAppH5Links",
"summary": "获取 App H5 入口地址",
"tag": "app-config"
},
{
"source": "gateway",
"executable": true,
"method": "GET",
"path": "/api/v1/im/usersig",
"operationId": "issueTencentIMUserSig",
"summary": "获取腾讯云 IM UserSig",
"tag": "tencent-im"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/rtc/token",
"operationId": "issueTencentRTCToken",
"summary": "获取腾讯 RTC 进房 UserSig",
"tag": "tencent-rtc"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/tencent-rtc/callback",
"operationId": "handleTencentRTCCallback",
"summary": "腾讯 RTC 服务端事件回调",
"tag": "tencent-rtc"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/files/upload",
"operationId": "uploadFile",
"summary": "上传 App 通用文件",
"tag": "files"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/files/avatar/upload",
"operationId": "uploadAvatar",
"summary": "上传用户头像",
"tag": "files"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/devices/push-token",
"operationId": "bindPushToken",
"summary": "绑定设备推送 token",
"tag": "devices"
},
{
"source": "gateway",
"executable": true,
"method": "DELETE",
"path": "/api/v1/devices/push-token",
"operationId": "deletePushToken",
"summary": "删除设备推送 token",
"tag": "devices"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/tencent-im/callback",
"operationId": "handleTencentIMCallback",
"summary": "腾讯云 IM 服务端回调",
"tag": "tencent-im"
},
{
"source": "gateway",
"executable": true,
"method": "GET",
"path": "/api/v1/users/by-display-user-id/{display_user_id}",
"operationId": "resolveDisplayUserID",
"summary": "通过当前展示短号解析用户",
"tag": "users"
},
{
"source": "gateway",
"executable": true,
"method": "GET",
"path": "/api/v1/users/profiles:batch",
"operationId": "batchUserProfiles",
"summary": "批量查询会话展示资料",
"tag": "users"
},
{
"source": "gateway",
"executable": true,
"method": "GET",
"path": "/api/v1/users/me/identity",
"operationId": "getMyIdentity",
"summary": "查询当前用户展示短号状态",
"tag": "users"
},
{
"source": "gateway",
"executable": true,
"method": "GET",
"path": "/api/v1/users/me/host-identity",
"operationId": "getMyHostIdentity",
"summary": "查询当前用户 Host/Agency/BD 身份",
"tag": "users"
},
{
"source": "gateway",
"executable": true,
"method": "GET",
"path": "/api/v1/users/me/overview",
"operationId": "getMyOverview",
"summary": "我的页首屏聚合摘要",
"tag": "users"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/users/me/onboarding/complete",
"operationId": "completeMyOnboarding",
"summary": "完成注册页必填资料",
"tag": "users"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/users/me/profile/update",
"operationId": "updateMyProfile",
"summary": "修改当前用户基础资料",
"tag": "users"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/users/me/country/change",
"operationId": "changeMyCountry",
"summary": "修改当前用户国家",
"tag": "users"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/users/me/display-id/change",
"operationId": "changeMyDisplayUserID",
"summary": "修改当前用户默认短号",
"tag": "users"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/users/me/display-id/pretty/apply",
"operationId": "applyMyPrettyDisplayUserID",
"summary": "申请临时靓号",
"tag": "users"
},
{
"source": "gateway",
"executable": true,
"method": "GET",
"path": "/api/v1/rooms/current",
"operationId": "getCurrentRoom",
"summary": "查询当前可恢复房间",
"tag": "rooms"
},
{
"source": "gateway",
"executable": true,
"method": "GET",
"path": "/api/v1/rooms/snapshot",
"operationId": "getRoomSnapshot",
"summary": "查询房间完整快照",
"tag": "rooms"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/rooms/create",
"operationId": "createRoom",
"summary": "创建房间",
"tag": "rooms"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/rooms/join",
"operationId": "joinRoom",
"summary": "加入房间",
"tag": "rooms"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/rooms/heartbeat",
"operationId": "roomHeartbeat",
"summary": "刷新房间业务 presence",
"tag": "rooms"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/rooms/leave",
"operationId": "leaveRoom",
"summary": "离开房间",
"tag": "rooms"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/rooms/mic/up",
"operationId": "micUp",
"summary": "上麦",
"tag": "rooms"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/rooms/mic/down",
"operationId": "micDown",
"summary": "下麦",
"tag": "rooms"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/rooms/mic/change",
"operationId": "changeMicSeat",
"summary": "调整麦位",
"tag": "rooms"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/rooms/mic/publishing/confirm",
"operationId": "confirmMicPublishing",
"summary": "确认 RTC 音频发布成功",
"tag": "rooms"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/rooms/mic/lock",
"operationId": "setMicSeatLock",
"summary": "锁定或解锁麦位",
"tag": "rooms"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/rooms/chat/enabled",
"operationId": "setChatEnabled",
"summary": "开启或关闭房间公屏",
"tag": "rooms"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/rooms/admin/set",
"operationId": "setRoomAdmin",
"summary": "添加或移除房间管理员",
"tag": "rooms"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/rooms/host/transfer",
"operationId": "transferRoomHost",
"summary": "转移房间主持人",
"tag": "rooms"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/rooms/user/mute",
"operationId": "muteUser",
"summary": "设置房间内禁言",
"tag": "rooms"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/rooms/user/kick",
"operationId": "kickUser",
"summary": "踢出房间用户",
"tag": "rooms"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/rooms/user/unban",
"operationId": "unbanUser",
"summary": "解除房间 ban",
"tag": "rooms"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/rooms/gift/send",
"operationId": "sendGift",
"summary": "发送礼物",
"tag": "rooms"
},
{
"source": "gateway",
"executable": true,
"method": "GET",
"path": "/api/v1/resources",
"operationId": "listResources",
"summary": "App 资源列表",
"tag": "resources"
},
{
"source": "gateway",
"executable": true,
"method": "GET",
"path": "/api/v1/resource-groups/{group_id}",
"operationId": "getResourceGroup",
"summary": "App 资源组详情",
"tag": "resources"
},
{
"source": "gateway",
"executable": true,
"method": "GET",
"path": "/api/v1/gifts",
"operationId": "listGifts",
"summary": "App 礼物列表",
"tag": "resources"
},
{
"source": "gateway",
"executable": true,
"method": "GET",
"path": "/api/v1/users/me/resources",
"operationId": "listMyResources",
"summary": "我的资源权益",
"tag": "resources"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/users/me/resources/{resource_id}/equip",
"operationId": "equipMyResource",
"summary": "佩戴我的资源",
"tag": "resources"
},
{
"source": "gateway",
"executable": true,
"method": "GET",
"path": "/api/v1/messages/tabs",
"operationId": "listMessageTabs",
"summary": "消息 tab 分区摘要",
"tag": "messages"
},
{
"source": "gateway",
"executable": true,
"method": "GET",
"path": "/api/v1/messages",
"operationId": "listInboxMessages",
"summary": "system/activity 消息列表",
"tag": "messages"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/messages/read-all",
"operationId": "markInboxSectionRead",
"summary": "标记分区全部已读",
"tag": "messages"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/messages/{message_id}/read",
"operationId": "markInboxMessageRead",
"summary": "标记单条消息已读",
"tag": "messages"
},
{
"source": "gateway",
"executable": true,
"method": "DELETE",
"path": "/api/v1/messages/{message_id}",
"operationId": "deleteInboxMessage",
"summary": "删除单条消息",
"tag": "messages"
},
{
"source": "gateway",
"executable": true,
"method": "POST",
"path": "/api/v1/wallet/coin-seller/transfer",
"operationId": "transferCoinFromSeller",
"summary": "币商给玩家转金币",
"tag": "wallet"
}
];
const ADMIN_ENDPOINTS = [
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/admin/agencies",
"operationId": "listAgencies",
"summary": "listAgencies",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/agencies",
"operationId": "createAgency",
"summary": "createAgency",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/agencies/{agency_id}/close",
"operationId": "closeAgency",
"summary": "closeAgency",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/agencies/{agency_id}/join-enabled",
"operationId": "setAgencyJoinEnabled",
"summary": "setAgencyJoinEnabled",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/admin/app-config/banners",
"operationId": "listBanners",
"summary": "listBanners",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/app-config/banners",
"operationId": "createBanner",
"summary": "createBanner",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "DELETE",
"path": "/api/v1/admin/app-config/banners/{banner_id}",
"operationId": "deleteBanner",
"summary": "deleteBanner",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "PUT",
"path": "/api/v1/admin/app-config/banners/{banner_id}",
"operationId": "updateBanner",
"summary": "updateBanner",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/admin/app-config/h5-links",
"operationId": "listH5Links",
"summary": "listH5Links",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "PUT",
"path": "/api/v1/admin/app-config/h5-links",
"operationId": "updateH5Links",
"summary": "updateH5Links",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/admin/apps",
"operationId": "listApps",
"summary": "listApps",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/admin/bd-leaders",
"operationId": "listBDLeaders",
"summary": "listBDLeaders",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/bd-leaders",
"operationId": "createBDLeader",
"summary": "createBDLeader",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "PATCH",
"path": "/api/v1/admin/bd-leaders/{user_id}/status",
"operationId": "setBDLeaderStatus",
"summary": "setBDLeaderStatus",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/admin/bds",
"operationId": "listBDs",
"summary": "listBDs",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/bds",
"operationId": "createBD",
"summary": "createBD",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "PATCH",
"path": "/api/v1/admin/bds/{user_id}/status",
"operationId": "setBDStatus",
"summary": "setBDStatus",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/admin/coin-sellers",
"operationId": "listCoinSellers",
"summary": "listCoinSellers",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/coin-sellers",
"operationId": "createCoinSeller",
"summary": "createCoinSeller",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "PATCH",
"path": "/api/v1/admin/coin-sellers/{user_id}/status",
"operationId": "setCoinSellerStatus",
"summary": "setCoinSellerStatus",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/coin-sellers/{user_id}/stock-credits",
"operationId": "creditCoinSellerStock",
"summary": "creditCoinSellerStock",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/admin/countries",
"operationId": "listCountries",
"summary": "listCountries",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/countries",
"operationId": "createCountry",
"summary": "createCountry",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "DELETE",
"path": "/api/v1/admin/countries/{country_id}",
"operationId": "deleteCountry",
"summary": "deleteCountry",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/admin/countries/{country_id}",
"operationId": "getCountry",
"summary": "getCountry",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "PATCH",
"path": "/api/v1/admin/countries/{country_id}",
"operationId": "updateCountry",
"summary": "updateCountry",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/countries/{country_id}/disable",
"operationId": "disableCountry",
"summary": "disableCountry",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/countries/{country_id}/enable",
"operationId": "enableCountry",
"summary": "enableCountry",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/files/image/upload",
"operationId": "uploadImage",
"summary": "uploadImage",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/files/upload",
"operationId": "uploadFile",
"summary": "uploadFile",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/admin/gifts",
"operationId": "listGifts",
"summary": "listGifts",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/gifts",
"operationId": "createGift",
"summary": "createGift",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "PUT",
"path": "/api/v1/admin/gifts/{gift_id}",
"operationId": "updateGift",
"summary": "updateGift",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/gifts/{gift_id}/disable",
"operationId": "disableGift",
"summary": "disableGift",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/gifts/{gift_id}/enable",
"operationId": "enableGift",
"summary": "enableGift",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/admin/hosts",
"operationId": "listHosts",
"summary": "listHosts",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/admin/payment/recharge-bills",
"operationId": "listRechargeBills",
"summary": "listRechargeBills",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/admin/regions",
"operationId": "listRegions",
"summary": "listRegions",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/regions",
"operationId": "createRegion",
"summary": "createRegion",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "DELETE",
"path": "/api/v1/admin/regions/{region_id}",
"operationId": "disableRegion",
"summary": "disableRegion",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/admin/regions/{region_id}",
"operationId": "getRegion",
"summary": "getRegion",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "PATCH",
"path": "/api/v1/admin/regions/{region_id}",
"operationId": "updateRegion",
"summary": "updateRegion",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "PUT",
"path": "/api/v1/admin/regions/{region_id}/countries",
"operationId": "replaceRegionCountries",
"summary": "replaceRegionCountries",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/regions/{region_id}/enable",
"operationId": "enableRegion",
"summary": "enableRegion",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/admin/resource-grants",
"operationId": "listResourceGrants",
"summary": "listResourceGrants",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/resource-grants/group",
"operationId": "grantResourceGroup",
"summary": "grantResourceGroup",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/resource-grants/resource",
"operationId": "grantResource",
"summary": "grantResource",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/admin/resource-groups",
"operationId": "listResourceGroups",
"summary": "listResourceGroups",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/resource-groups",
"operationId": "createResourceGroup",
"summary": "createResourceGroup",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/admin/resource-groups/{group_id}",
"operationId": "getResourceGroup",
"summary": "getResourceGroup",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "PUT",
"path": "/api/v1/admin/resource-groups/{group_id}",
"operationId": "updateResourceGroup",
"summary": "updateResourceGroup",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/resource-groups/{group_id}/disable",
"operationId": "disableResourceGroup",
"summary": "disableResourceGroup",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/resource-groups/{group_id}/enable",
"operationId": "enableResourceGroup",
"summary": "enableResourceGroup",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "PUT",
"path": "/api/v1/admin/resource-groups/{group_id}/items",
"operationId": "updateResourceGroupItems",
"summary": "updateResourceGroupItems",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/admin/resources",
"operationId": "listResources",
"summary": "listResources",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/resources",
"operationId": "createResource",
"summary": "createResource",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/admin/resources/{resource_id}",
"operationId": "getResource",
"summary": "getResource",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "PUT",
"path": "/api/v1/admin/resources/{resource_id}",
"operationId": "updateResource",
"summary": "updateResource",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/resources/{resource_id}/disable",
"operationId": "disableResource",
"summary": "disableResource",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/admin/resources/{resource_id}/enable",
"operationId": "enableResource",
"summary": "enableResource",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/admin/rooms",
"operationId": "listRooms",
"summary": "listRooms",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "DELETE",
"path": "/api/v1/admin/rooms/{room_id}",
"operationId": "deleteRoom",
"summary": "deleteRoom",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "PATCH",
"path": "/api/v1/admin/rooms/{room_id}",
"operationId": "updateRoom",
"summary": "updateRoom",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/app/users",
"operationId": "appListUsers",
"summary": "appListUsers",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/app/users/{id}",
"operationId": "appGetUser",
"summary": "appGetUser",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "PATCH",
"path": "/api/v1/app/users/{id}",
"operationId": "appUpdateUser",
"summary": "appUpdateUser",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/app/users/{id}/ban",
"operationId": "appBanUser",
"summary": "appBanUser",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/app/users/{id}/password",
"operationId": "appSetPassword",
"summary": "appSetPassword",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/app/users/{id}/unban",
"operationId": "appUnbanUser",
"summary": "appUnbanUser",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/auth/change-password",
"operationId": "changePassword",
"summary": "changePassword",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/auth/login",
"operationId": "login",
"summary": "login",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/auth/logout",
"operationId": "logout",
"summary": "logout",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/auth/me",
"operationId": "me",
"summary": "me",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/auth/refresh",
"operationId": "refresh",
"summary": "refresh",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/dashboard/overview",
"operationId": "dashboardOverview",
"summary": "dashboardOverview",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/exports/users",
"operationId": "createUserExportJob",
"summary": "createUserExportJob",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/jobs",
"operationId": "listJobs",
"summary": "listJobs",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/jobs/{id}/artifact",
"operationId": "downloadJobArtifact",
"summary": "downloadJobArtifact",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/jobs/{id}/cancel",
"operationId": "cancelJob",
"summary": "cancelJob",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/logs/login",
"operationId": "listLoginLogs",
"summary": "listLoginLogs",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/logs/login/export",
"operationId": "exportLoginLogs",
"summary": "exportLoginLogs",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/logs/operations",
"operationId": "listOperationLogs",
"summary": "listOperationLogs",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/logs/operations/export",
"operationId": "exportOperationLogs",
"summary": "exportOperationLogs",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/navigation/menus",
"operationId": "navigationMenus",
"summary": "navigationMenus",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/notifications",
"operationId": "listNotifications",
"summary": "listNotifications",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "DELETE",
"path": "/api/v1/notifications/{id}",
"operationId": "deleteNotification",
"summary": "deleteNotification",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "PATCH",
"path": "/api/v1/notifications/{id}/read",
"operationId": "markNotificationRead",
"summary": "markNotificationRead",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "PATCH",
"path": "/api/v1/notifications/read-all",
"operationId": "markAllNotificationsRead",
"summary": "markAllNotificationsRead",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/permissions",
"operationId": "listPermissions",
"summary": "listPermissions",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/permissions",
"operationId": "createPermission",
"summary": "createPermission",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "DELETE",
"path": "/api/v1/permissions/{id}",
"operationId": "deletePermission",
"summary": "deletePermission",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "PATCH",
"path": "/api/v1/permissions/{id}",
"operationId": "updatePermission",
"summary": "updatePermission",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/permissions/sync",
"operationId": "syncPermissions",
"summary": "syncPermissions",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/roles",
"operationId": "listRoles",
"summary": "listRoles",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/roles",
"operationId": "createRole",
"summary": "createRole",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "DELETE",
"path": "/api/v1/roles/{id}",
"operationId": "deleteRole",
"summary": "deleteRole",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "PATCH",
"path": "/api/v1/roles/{id}",
"operationId": "updateRole",
"summary": "updateRole",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/roles/{id}/data-scopes",
"operationId": "getRoleDataScopes",
"summary": "getRoleDataScopes",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "PUT",
"path": "/api/v1/roles/{id}/data-scopes",
"operationId": "replaceRoleDataScopes",
"summary": "replaceRoleDataScopes",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "PUT",
"path": "/api/v1/roles/{id}/permissions",
"operationId": "replaceRolePermissions",
"summary": "replaceRolePermissions",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/search",
"operationId": "search",
"summary": "search",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/system/menus",
"operationId": "listSystemMenus",
"summary": "listSystemMenus",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/system/menus",
"operationId": "createMenu",
"summary": "createMenu",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "DELETE",
"path": "/api/v1/system/menus/{id}",
"operationId": "deleteMenu",
"summary": "deleteMenu",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "PATCH",
"path": "/api/v1/system/menus/{id}",
"operationId": "updateMenu",
"summary": "updateMenu",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "PATCH",
"path": "/api/v1/system/menus/{id}/visible",
"operationId": "updateMenuVisible",
"summary": "updateMenuVisible",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "PUT",
"path": "/api/v1/system/menus/sort",
"operationId": "sortMenus",
"summary": "sortMenus",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/users",
"operationId": "listUsers",
"summary": "listUsers",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/users",
"operationId": "createUser",
"summary": "createUser",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/users/{id}",
"operationId": "getUser",
"summary": "getUser",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "PATCH",
"path": "/api/v1/users/{id}",
"operationId": "updateUser",
"summary": "updateUser",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/users/{id}/reset-password",
"operationId": "resetUserPassword",
"summary": "resetUserPassword",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "PATCH",
"path": "/api/v1/users/{id}/status",
"operationId": "updateUserStatus",
"summary": "updateUserStatus",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "POST",
"path": "/api/v1/users/batch/status",
"operationId": "batchUpdateUserStatus",
"summary": "batchUpdateUserStatus",
"tag": "admin"
},
{
"source": "admin",
"executable": true,
"method": "GET",
"path": "/api/v1/users/export",
"operationId": "exportUsers",
"summary": "exportUsers",
"tag": "admin"
}
];
const INTERNAL_ENDPOINTS = [
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.AuthService/LoginPassword",
"operationId": "userAuthLoginPassword",
"summary": "短号密码登录",
"tag": "auth"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.AuthService/LoginThirdParty",
"operationId": "userAuthLoginThirdParty",
"summary": "三方登录或注册",
"tag": "auth"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.AuthService/SetPassword",
"operationId": "userAuthSetPassword",
"summary": "首次设置密码",
"tag": "auth"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.AuthService/RefreshToken",
"operationId": "userAuthRefreshToken",
"summary": "刷新 token",
"tag": "auth"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.AuthService/Logout",
"operationId": "userAuthLogout",
"summary": "登出并失效 refresh session",
"tag": "auth"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.UserService/GetUser",
"operationId": "userGetUser",
"summary": "查询单个用户主状态",
"tag": "users"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.UserService/BatchGetUsers",
"operationId": "userBatchGetUsers",
"summary": "批量查询用户主状态",
"tag": "users"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.UserService/ListUserIDs",
"operationId": "userListUserIDs",
"summary": "后台任务按 user_id 游标查询目标用户",
"tag": "users"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.UserService/UpdateUserProfile",
"operationId": "userUpdateUserProfile",
"summary": "修改用户基础资料",
"tag": "users"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.UserService/ChangeUserCountry",
"operationId": "userChangeUserCountry",
"summary": "修改用户国家",
"tag": "users"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.UserService/CompleteOnboarding",
"operationId": "userCompleteOnboarding",
"summary": "完成注册页必填资料",
"tag": "users"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.UserDeviceService/BindPushToken",
"operationId": "userBindPushToken",
"summary": "绑定当前登录用户的系统推送 token",
"tag": "user-devices"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.UserDeviceService/DeletePushToken",
"operationId": "userDeletePushToken",
"summary": "失效当前登录用户的系统推送 token",
"tag": "user-devices"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.CountryAdminService/CreateCountry",
"operationId": "userCreateCountry",
"summary": "创建国家主数据",
"tag": "country-admin"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.CountryAdminService/ListCountries",
"operationId": "userListCountries",
"summary": "查询国家主数据",
"tag": "country-admin"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.CountryAdminService/UpdateCountry",
"operationId": "userUpdateCountry",
"summary": "修改国家展示字段",
"tag": "country-admin"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.CountryAdminService/EnableCountry",
"operationId": "userEnableCountry",
"summary": "开放国家给 App 选择",
"tag": "country-admin"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.CountryAdminService/DisableCountry",
"operationId": "userDisableCountry",
"summary": "关闭国家 App 可选状态",
"tag": "country-admin"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.CountryQueryService/ListRegistrationCountries",
"operationId": "userListRegistrationCountries",
"summary": "查询 App 注册页开放国家列表",
"tag": "country-query"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.RegionAdminService/CreateRegion",
"operationId": "userCreateRegion",
"summary": "创建区域并配置国家列表",
"tag": "region-admin"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.RegionAdminService/ListRegions",
"operationId": "userListRegions",
"summary": "查询区域列表",
"tag": "region-admin"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.RegionAdminService/GetRegion",
"operationId": "userGetRegion",
"summary": "查询单个区域",
"tag": "region-admin"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.RegionAdminService/UpdateRegion",
"operationId": "userUpdateRegion",
"summary": "修改区域展示字段",
"tag": "region-admin"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.RegionAdminService/ReplaceRegionCountries",
"operationId": "userReplaceRegionCountries",
"summary": "替换区域国家列表",
"tag": "region-admin"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.RegionAdminService/DisableRegion",
"operationId": "userDisableRegion",
"summary": "停用区域",
"tag": "region-admin"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.UserIdentityService/GetUserIdentity",
"operationId": "userGetUserIdentity",
"summary": "按 user_id 查询当前展示短号",
"tag": "user-identity"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.UserIdentityService/ResolveDisplayUserID",
"operationId": "userResolveDisplayUserID",
"summary": "通过当前展示短号解析用户",
"tag": "user-identity"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.UserIdentityService/ChangeDisplayUserID",
"operationId": "userChangeDisplayUserID",
"summary": "修改默认展示短号",
"tag": "user-identity"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.UserIdentityService/ApplyPrettyDisplayUserID",
"operationId": "userApplyPrettyDisplayUserID",
"summary": "申请临时靓号",
"tag": "user-identity"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.user.v1.UserIdentityService/ExpirePrettyDisplayUserID",
"operationId": "userExpirePrettyDisplayUserID",
"summary": "主动触发靓号过期恢复",
"tag": "user-identity"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/grpc.health.v1.Health/Check",
"operationId": "userHealthCheck",
"summary": "标准 gRPC health check",
"tag": "health"
},
{
"source": "user-grpc",
"executable": false,
"method": "POST",
"path": "/grpc.health.v1.Health/Watch",
"operationId": "userHealthWatch",
"summary": "标准 gRPC health watch",
"tag": "health"
},
{
"source": "room-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.room.v1.RoomCommandService/CreateRoom",
"operationId": "roomCreateRoom",
"summary": "创建房间",
"tag": "room-command"
},
{
"source": "room-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.room.v1.RoomCommandService/JoinRoom",
"operationId": "roomJoinRoom",
"summary": "加入房间业务 presence",
"tag": "room-command"
},
{
"source": "room-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.room.v1.RoomCommandService/LeaveRoom",
"operationId": "roomLeaveRoom",
"summary": "离开房间业务 presence",
"tag": "room-command"
},
{
"source": "room-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.room.v1.RoomCommandService/MicUp",
"operationId": "roomMicUp",
"summary": "上麦",
"tag": "room-command"
},
{
"source": "room-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.room.v1.RoomCommandService/MicDown",
"operationId": "roomMicDown",
"summary": "下麦",
"tag": "room-command"
},
{
"source": "room-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.room.v1.RoomCommandService/ChangeMicSeat",
"operationId": "roomChangeMicSeat",
"summary": "调整麦位",
"tag": "room-command"
},
{
"source": "room-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.room.v1.RoomCommandService/ConfirmMicPublishing",
"operationId": "roomConfirmMicPublishing",
"summary": "确认 RTC 音频发布成功",
"tag": "room-command"
},
{
"source": "room-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.room.v1.RoomCommandService/SetMicSeatLock",
"operationId": "roomSetMicSeatLock",
"summary": "锁定或解锁麦位",
"tag": "room-command"
},
{
"source": "room-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.room.v1.RoomCommandService/SetChatEnabled",
"operationId": "roomSetChatEnabled",
"summary": "开启或关闭公屏",
"tag": "room-command"
},
{
"source": "room-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.room.v1.RoomCommandService/SetRoomAdmin",
"operationId": "roomSetRoomAdmin",
"summary": "添加或移除房间管理员",
"tag": "room-command"
},
{
"source": "room-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.room.v1.RoomCommandService/TransferRoomHost",
"operationId": "roomTransferRoomHost",
"summary": "转移主持人",
"tag": "room-command"
},
{
"source": "room-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.room.v1.RoomCommandService/MuteUser",
"operationId": "roomMuteUser",
"summary": "设置房间内禁言",
"tag": "room-command"
},
{
"source": "room-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.room.v1.RoomCommandService/KickUser",
"operationId": "roomKickUser",
"summary": "踢出房间用户",
"tag": "room-command"
},
{
"source": "room-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.room.v1.RoomCommandService/UnbanUser",
"operationId": "roomUnbanUser",
"summary": "解除房间 ban",
"tag": "room-command"
},
{
"source": "room-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.room.v1.RoomCommandService/SendGift",
"operationId": "roomSendGift",
"summary": "发送礼物",
"tag": "room-command"
},
{
"source": "room-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.room.v1.RoomGuardService/CheckSpeakPermission",
"operationId": "roomCheckSpeakPermission",
"summary": "发言前守卫",
"tag": "room-guard"
},
{
"source": "room-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.room.v1.RoomGuardService/VerifyRoomPresence",
"operationId": "roomVerifyRoomPresence",
"summary": "入群前 presence 守卫",
"tag": "room-guard"
},
{
"source": "room-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.room.v1.RoomQueryService/GetCurrentRoom",
"operationId": "roomGetCurrentRoom",
"summary": "查询用户当前可恢复房间",
"tag": "room-query"
},
{
"source": "room-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.room.v1.RoomQueryService/GetRoomSnapshot",
"operationId": "roomGetRoomSnapshot",
"summary": "查询房间完整快照",
"tag": "room-query"
},
{
"source": "room-grpc",
"executable": false,
"method": "POST",
"path": "/grpc.health.v1.Health/Check",
"operationId": "roomHealthCheck",
"summary": "标准 gRPC health check",
"tag": "health"
},
{
"source": "room-grpc",
"executable": false,
"method": "POST",
"path": "/grpc.health.v1.Health/Watch",
"operationId": "roomHealthWatch",
"summary": "标准 gRPC health watch",
"tag": "health"
},
{
"source": "wallet-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.wallet.v1.WalletService/DebitGift",
"operationId": "walletDebitGift",
"summary": "送礼扣费",
"tag": "wallet"
},
{
"source": "wallet-grpc",
"executable": false,
"method": "POST",
"path": "/grpc.health.v1.Health/Check",
"operationId": "walletHealthCheck",
"summary": "标准 gRPC health check",
"tag": "health"
},
{
"source": "wallet-grpc",
"executable": false,
"method": "POST",
"path": "/grpc.health.v1.Health/Watch",
"operationId": "walletHealthWatch",
"summary": "标准 gRPC health watch",
"tag": "health"
},
{
"source": "activity-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.activity.v1.ActivityService/PingActivity",
"operationId": "activityPingActivity",
"summary": "活动服务连通性探测",
"tag": "activity"
},
{
"source": "activity-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.activity.v1.ActivityService/GetActivityStatus",
"operationId": "activityGetActivityStatus",
"summary": "查询活动状态",
"tag": "activity"
},
{
"source": "activity-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.activity.v1.MessageInboxService/ListMessageTabs",
"operationId": "activityListMessageTabs",
"summary": "查询 App 消息 tab 摘要",
"tag": "messages"
},
{
"source": "activity-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.activity.v1.MessageInboxService/ListInboxMessages",
"operationId": "activityListInboxMessages",
"summary": "分页查询 system/activity inbox",
"tag": "messages"
},
{
"source": "activity-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.activity.v1.MessageInboxService/MarkInboxMessageRead",
"operationId": "activityMarkInboxMessageRead",
"summary": "标记单条 inbox 消息已读",
"tag": "messages"
},
{
"source": "activity-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.activity.v1.MessageInboxService/MarkInboxSectionRead",
"operationId": "activityMarkInboxSectionRead",
"summary": "标记一个消息分区全部已读",
"tag": "messages"
},
{
"source": "activity-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.activity.v1.MessageInboxService/DeleteInboxMessage",
"operationId": "activityDeleteInboxMessage",
"summary": "删除当前用户可见 inbox 消息",
"tag": "messages"
},
{
"source": "activity-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.activity.v1.MessageInboxService/CreateInboxMessage",
"operationId": "activityCreateInboxMessage",
"summary": "生产者创建单用户 inbox 消息",
"tag": "messages"
},
{
"source": "activity-grpc",
"executable": false,
"method": "POST",
"path": "/hyapp.activity.v1.MessageInboxService/CreateFanoutJob",
"operationId": "activityCreateFanoutJob",
"summary": "创建后台 fanout 任务",
"tag": "messages"
},
{
"source": "activity-grpc",
"executable": false,
"method": "POST",
"path": "/grpc.health.v1.Health/Check",
"operationId": "activityHealthCheck",
"summary": "标准 gRPC health check",
"tag": "health"
},
{
"source": "activity-grpc",
"executable": false,
"method": "POST",
"path": "/grpc.health.v1.Health/Watch",
"operationId": "activityHealthWatch",
"summary": "标准 gRPC health watch",
"tag": "health"
}
];
const STORAGE_KEY = 'hy-api-flow-harness:v2';
const runtime = { imSig: null, rtcToken: null };
const $ = (id) => document.getElementById(id);
const cfgIds = ['gatewayBase','adminBase','appCode','accessToken','refreshToken','adminToken','account','password','deviceId','roomId','roomVersion','micSessionId','seatNo','targetUserId','displayUserId','resourceId','groupId','messageId','country'];
const commandId = (prefix) => `${prefix}_${Date.now()}_${Math.random().toString(16).slice(2, 8)}`;
const numberValue = (id, fallback = 0) => Number($(id).value || fallback);
function cfg() {
return Object.fromEntries(cfgIds.map((id) => [id, $(id).value.trim()]));
}
function loadConfig() {
try {
const saved = JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}');
for (const id of cfgIds) if (saved[id] !== undefined && $(id)) $(id).value = saved[id];
} catch (err) {
log('配置读取失败:' + err.message, 'bad');
}
}
function saveConfig() {
localStorage.setItem(STORAGE_KEY, JSON.stringify(cfg()));
log('配置已保存。');
}
function log(value, status = '') {
const line = typeof value === 'string' ? value : JSON.stringify(value, null, 2);
const ts = new Date().toISOString();
$('log').textContent += `[${ts}] ${line}\n`;
$('log').scrollTop = $('log').scrollHeight;
if (status === 'bad') console.error(value); else console.log(value);
}
function parseJSON(text, fallback = {}) {
const trimmed = (text || '').trim();
if (!trimmed || trimmed === 'undefined') return fallback;
return JSON.parse(trimmed);
}
function unwrap(payload) {
return payload && typeof payload === 'object' && Object.prototype.hasOwnProperty.call(payload, 'data') ? payload.data : payload;
}
function firstValue(...values) {
return values.find((value) => value !== undefined && value !== null && value !== '');
}
function findDeep(obj, keys) {
if (!obj || typeof obj !== 'object') return undefined;
for (const key of keys) if (obj[key] !== undefined && obj[key] !== null && obj[key] !== '') return obj[key];
for (const value of Object.values(obj)) {
if (value && typeof value === 'object') {
const nested = findDeep(value, keys);
if (nested !== undefined && nested !== null && nested !== '') return nested;
}
}
return undefined;
}
function headersFor(source, withJson = true) {
const c = cfg();
const headers = { 'X-Request-ID': commandId('req') };
if (withJson) headers['Content-Type'] = 'application/json';
if (c.appCode) headers['X-App-Code'] = c.appCode;
const token = source === 'admin' ? c.adminToken : c.accessToken;
if (token) headers.Authorization = token.startsWith('Bearer ') ? token : `Bearer ${token}`;
return headers;
}
function updateFromResponse(source, ep, payload) {
const data = unwrap(payload) || {};
const tokenValue = firstValue(data.access_token, data.accessToken, data.token, data.jwt);
if (tokenValue && source === 'admin') $('adminToken').value = tokenValue;
if (tokenValue && source !== 'admin') $('accessToken').value = tokenValue;
const refresh = firstValue(data.refresh_token, data.refreshToken);
if (refresh) $('refreshToken').value = refresh;
const displayID = findDeep(data, ['display_user_id', 'displayUserId', 'target_display_user_id']);
if (displayID) $('displayUserId').value = String(displayID);
const userID = findDeep(data, ['user_id', 'userId', 'target_user_id']);
if (userID && numberValue('targetUserId', 0) <= 0) $('targetUserId').value = String(userID);
const roomID = findDeep(data, ['room_id', 'roomId']);
if (roomID) $('roomId').value = String(roomID);
const roomVersion = findDeep(data, ['room_version', 'roomVersion']);
if (roomVersion !== undefined) $('roomVersion').value = String(roomVersion);
const micSessionID = findDeep(data, ['mic_session_id', 'micSessionId']);
if (micSessionID) $('micSessionId').value = String(micSessionID);
if (ep && ep.path.endsWith('/im/usersig')) runtime.imSig = data;
if (ep && ep.path.endsWith('/rtc/token')) runtime.rtcToken = data;
if (ep && ep.path.endsWith('/rooms/join') && data.rtc && data.rtc.token) runtime.rtcToken = data.rtc.token;
localStorage.setItem(STORAGE_KEY, JSON.stringify(cfg()));
}
function fillPath(path, localValues = {}) {
const c = cfg();
const values = {
display_user_id: c.displayUserId,
group_id: c.groupId,
resource_id: c.resourceId,
message_id: c.messageId,
room_id: c.roomId,
user_id: c.targetUserId,
country_id: c.country,
region_id: '1',
gift_id: 'gift_1',
banner_id: '1',
agency_id: '1',
id: '1',
...localValues,
};
return path.replace(/\{([^}]+)\}/g, (_, key) => encodeURIComponent(values[key] || '1'));
}
function appendQuery(url, query) {
for (const [key, value] of Object.entries(query || {})) {
if (value === undefined || value === null || value === '') continue;
if (Array.isArray(value)) value.forEach((item) => url.searchParams.append(key, item));
else url.searchParams.set(key, value);
}
}
async function request(source, method, path, body, query = {}) {
const c = cfg();
const base = source === 'admin' ? c.adminBase : c.gatewayBase;
const url = new URL(fillPath(path), base.endsWith('/') ? base : base + '/');
appendQuery(url, query);
const hasBody = method !== 'GET' && body !== undefined;
const init = { method, headers: headersFor(source, hasBody), credentials: 'include' };
if (hasBody) init.body = JSON.stringify(body);
const started = performance.now();
const res = await fetch(url.toString(), init);
const text = await res.text();
let payload = text;
try { payload = text ? JSON.parse(text) : null; } catch (_) {}
const ep = { source, method, path };
updateFromResponse(source, ep, payload);
log({ request: { source, method, url: url.toString(), body, query }, status: res.status, ms: Math.round(performance.now() - started), response: payload }, res.ok ? 'ok' : 'bad');
if (!res.ok) throw new Error(`${method} ${path} failed: HTTP ${res.status}`);
return payload;
}
async function upload(source, path, file) {
if (!file) throw new Error('请选择文件。');
const base = source === 'admin' ? cfg().adminBase : cfg().gatewayBase;
const url = new URL(fillPath(path), base.endsWith('/') ? base : base + '/');
const form = new FormData();
form.append('file', file);
const res = await fetch(url.toString(), { method: 'POST', headers: headersFor(source, false), body: form, credentials: 'include' });
const text = await res.text();
let payload = text;
try { payload = text ? JSON.parse(text) : null; } catch (_) {}
updateFromResponse(source, { source, method: 'POST', path }, payload);
log({ request: { source, method: 'POST', url: url.toString(), file: file.name }, status: res.status, response: payload }, res.ok ? 'ok' : 'bad');
if (!res.ok) throw new Error(`upload failed: HTTP ${res.status}`);
return payload;
}
function exampleBody(ep) {
const c = cfg();
const p = ep.path;
if (p.endsWith('/auth/account/login')) return { account: c.account, password: c.password, device_id: c.deviceId };
if (p.endsWith('/auth/third-party/login')) return { provider: 'firebase', credential: 'firebase_id_token', username: 'WebSmoke', avatar: 'https://example.com/avatar.png', gender: 'male', birth: '1998-07-16', country: c.country || 'US', invite_code: '', device_id: c.deviceId, device: 'Browser', os_version: navigator.platform || 'web', platform: 'ios', app_version: '1.2.3', build_number: '123', language: navigator.language || 'en-US', timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC', source: 'smoke', install_channel: 'web', campaign: 'api_harness' };
if (p.endsWith('/auth/password/set')) return { password: c.password };
if (p.endsWith('/auth/token/refresh')) return { refresh_token: c.refreshToken, device_id: c.deviceId };
if (p.endsWith('/auth/logout')) return { refresh_token: c.refreshToken, session_id: '' };
if (p.endsWith('/rtc/token')) return { room_id: c.roomId };
if (p.endsWith('/devices/push-token')) return { device_id: c.deviceId, platform: 'ios', push_token: 'push-token-smoke', app_version: '1.2.3' };
if (p.endsWith('/users/me/profile/update')) return { username: 'WebSmoke', avatar: 'https://example.com/avatar.png', birth: '1998-07-16' };
if (p.endsWith('/users/me/country/change')) return { country: c.country || 'US' };
if (p.endsWith('/users/me/display-id/change')) return { new_display_user_id: c.displayUserId };
if (p.endsWith('/users/me/display-id/pretty/apply')) return { pretty_display_user_id: c.displayUserId };
if (p.endsWith('/rooms/create')) return { mode: 'voice', room_name: 'Smoke Room', room_avatar: '', room_description: 'local smoke room' };
if (p.endsWith('/rooms/join')) return { command_id: commandId('cmd_join'), room_id: c.roomId, role: 'audience' };
if (p.endsWith('/rooms/heartbeat')) return { command_id: commandId('cmd_heartbeat'), room_id: c.roomId };
if (p.endsWith('/rooms/leave')) return { command_id: commandId('cmd_leave'), room_id: c.roomId };
if (p.endsWith('/rooms/mic/up')) return { command_id: commandId('cmd_mic_up'), room_id: c.roomId, seat_no: numberValue('seatNo', 1) };
if (p.endsWith('/rooms/mic/down')) return { command_id: commandId('cmd_mic_down'), room_id: c.roomId, target_user_id: numberValue('targetUserId', 0), reason: 'manual' };
if (p.endsWith('/rooms/mic/change')) return { command_id: commandId('cmd_mic_change'), room_id: c.roomId, target_user_id: numberValue('targetUserId', 0), seat_no: numberValue('seatNo', 1) };
if (p.endsWith('/rooms/mic/publishing/confirm')) return { command_id: commandId('cmd_confirm'), room_id: c.roomId, target_user_id: numberValue('targetUserId', 0), mic_session_id: c.micSessionId, room_version: numberValue('roomVersion', 0), event_time_ms: Date.now(), source: 'client' };
if (p.endsWith('/rooms/mic/lock')) return { command_id: commandId('cmd_mic_lock'), room_id: c.roomId, seat_no: numberValue('seatNo', 1), locked: true };
if (p.endsWith('/rooms/chat/enabled')) return { command_id: commandId('cmd_chat'), room_id: c.roomId, enabled: true };
if (p.endsWith('/rooms/admin/set')) return { command_id: commandId('cmd_admin'), room_id: c.roomId, target_user_id: numberValue('targetUserId', 0), is_admin: true };
if (p.endsWith('/rooms/host/transfer')) return { command_id: commandId('cmd_host'), room_id: c.roomId, target_user_id: numberValue('targetUserId', 0) };
if (p.endsWith('/rooms/user/mute')) return { command_id: commandId('cmd_mute'), room_id: c.roomId, target_user_id: numberValue('targetUserId', 0), duration_ms: 60000, reason: 'smoke' };
if (p.endsWith('/rooms/user/kick')) return { command_id: commandId('cmd_kick'), room_id: c.roomId, target_user_id: numberValue('targetUserId', 0), duration_ms: 60000, reason: 'smoke' };
if (p.endsWith('/rooms/user/unban')) return { command_id: commandId('cmd_unban'), room_id: c.roomId, target_user_id: numberValue('targetUserId', 0) };
if (p.endsWith('/rooms/gift/send')) return { command_id: commandId('cmd_gift'), room_id: c.roomId, target_user_id: numberValue('targetUserId', 0), gift_id: 'gift_1', gift_count: 1 };
if (p.includes('/resources/{resource_id}/equip')) return { entitlement_id: '' };
if (p.endsWith('/messages/read-all')) return { tab: 'system' };
if (p.includes('/messages/{message_id}/read')) return { section: 'system' };
if (p.endsWith('/wallet/coin-seller/transfer')) return { command_id: commandId('cmd_seller_transfer'), target_display_user_id: c.displayUserId, amount: 1, reason: 'smoke' };
if (p.endsWith('/api/v1/auth/login')) return { username: 'admin', password: 'admin123' };
if (p.endsWith('/api/v1/auth/refresh')) return { refresh_token: c.refreshToken };
if (p.endsWith('/api/v1/auth/change-password')) return { old_password: 'admin123', new_password: 'admin1234' };
if (p.includes('/stock-credits')) return { amount: 100, reason: 'smoke stock credit', idempotency_key: commandId('stock') };
if (p.endsWith('/api/v1/admin/countries')) return { country_code: c.country || 'US', name: 'United States', enabled: true };
if (p.endsWith('/api/v1/admin/regions')) return { region_code: `region_${Date.now()}`, name: 'Smoke Region', enabled: true };
if (p.includes('/countries') && ep.method === 'PATCH') return { name: 'Smoke Country', enabled: true };
if (p.includes('/regions') && ep.method === 'PATCH') return { name: 'Smoke Region', enabled: true };
if (p.includes('/regions/{region_id}/countries')) return { country_codes: [c.country || 'US'] };
if (p.includes('/app/users/{id}/password')) return { password: c.password };
if (p.includes('/app/users/{id}/ban')) return { reason: 'smoke' };
if (p.includes('/admin/resources') && ep.method === 'POST') return { resource_code: `res_${Date.now()}`, name: 'Smoke Resource', resource_type: 'avatar_frame', enabled: true };
if (p.includes('/admin/gifts') && ep.method === 'POST') return { gift_code: `gift_${Date.now()}`, name: 'Smoke Gift', coin_price: 1, enabled: true };
if (p.includes('/admin/resource-grants/resource')) return { user_id: numberValue('targetUserId', 0), resource_id: Number(c.resourceId || 1), reason: 'smoke' };
if (p.includes('/admin/resource-grants/group')) return { user_id: numberValue('targetUserId', 0), group_id: Number(c.groupId || 1), reason: 'smoke' };
if (ep.method === 'GET') return undefined;
return {};
}
function exampleQuery(ep) {
const c = cfg();
const p = ep.path;
if (p.endsWith('/rooms/snapshot')) return { room_id: c.roomId };
if (p.endsWith('/resources') || p.endsWith('/gifts') || p.endsWith('/messages')) return { page_size: 20 };
if (p.endsWith('/users/profiles:batch')) return { user_ids: c.targetUserId || c.displayUserId };
if (p.includes('/tencent-im/callback')) return { SdkAppid: 0, CallbackCommand: 'Group.CallbackBeforeSendMsg', CallbackAuthToken: '' };
if (p.startsWith('/api/v1/admin/payment/recharge-bills')) return { page: 1, page_size: 20 };
if (p.startsWith('/api/v1/admin') || p.startsWith('/api/v1/users') || p.startsWith('/api/v1/app/users')) return { page: 1, page_size: 20 };
return {};
}
function apiStep(label, source, method, path, note = '') {
return { kind: 'api', label, source, method, path, note };
}
function clientStep(label, note) {
return { kind: 'client', label, note };
}
function manualStep(label, note) {
return { kind: 'manual', label, note };
}
const FLOW_DEFINITIONS = [
{
id: 'auth-third-party-profile',
title: '01 三方注册/登录 -> 资料 -> 国家 -> token 生命周期',
description: '验证注册字段入库边界、当前用户资料修改、国家修改冷却、set password、refresh 和 logout。credential 必须换成真实 Firebase/Google ID token。',
destructive: true,
steps: [
apiStep('gateway 就绪检查', 'gateway', 'GET', '/healthz/ready'),
apiStep('读取注册页国家', 'gateway', 'GET', '/api/v1/countries'),
apiStep('三方注册或登录', 'gateway', 'POST', '/api/v1/auth/third-party/login', '首次注册会写 username/avatar/gender/birth/country/device/source 等字段。'),
apiStep('查询我的短号身份', 'gateway', 'GET', '/api/v1/users/me/identity'),
apiStep('查询我的首页聚合信息', 'gateway', 'GET', '/api/v1/users/me/overview'),
apiStep('修改用户名/头像/生日', 'gateway', 'POST', '/api/v1/users/me/profile/update'),
apiStep('单独修改国家', 'gateway', 'POST', '/api/v1/users/me/country/change', '同一用户一个月只能成功修改一次国家;重复跑应观察 409。'),
apiStep('首次设置密码', 'gateway', 'POST', '/api/v1/auth/password/set'),
apiStep('刷新 access token', 'gateway', 'POST', '/api/v1/auth/token/refresh'),
apiStep('登出 refresh session', 'gateway', 'POST', '/api/v1/auth/logout'),
],
},
{
id: 'password-session',
title: '02 密码登录 -> refresh -> logout',
description: '验证账号密码入口、refresh token 轮换和 logout 失效;账号是当前 display_user_id。',
destructive: false,
steps: [
apiStep('账号密码登录', 'gateway', 'POST', '/api/v1/auth/account/login'),
apiStep('查询我的身份', 'gateway', 'GET', '/api/v1/users/me/identity'),
apiStep('Refresh token', 'gateway', 'POST', '/api/v1/auth/token/refresh'),
apiStep('Logout', 'gateway', 'POST', '/api/v1/auth/logout'),
],
},
{
id: 'room-im-rtc-publish',
title: '03 房间 -> IM -> RTC -> MicUp -> 发流确认 -> 下麦离房',
description: '核心语音房正向链路。后端只签发 IM/RTC 票据和维护房间状态IM 登录、TRTC 进房、切主播、发本地音频必须由客户端 SDK 完成。',
destructive: true,
steps: [
apiStep('创建房间', 'gateway', 'POST', '/api/v1/rooms/create', 'CreateRoom 成功后 room_id 会自动回填。'),
apiStep('加入房间业务 presence', 'gateway', 'POST', '/api/v1/rooms/join'),
apiStep('查询当前可恢复房间', 'gateway', 'GET', '/api/v1/rooms/current'),
apiStep('获取 IM UserSig', 'gateway', 'GET', '/api/v1/im/usersig'),
clientStep('客户端 IM SDK 登录并加入房间群', '使用 IM UserSig 登录;房间群 ID 使用 JoinRoom 响应 data.im.group_id。'),
clientStep('客户端 TRTC 进房', '优先使用 JoinRoom 响应 data.rtc.token 的 sdk_app_id / rtc_user_id / user_sig / str_room_id 进入 TRTC string roomavailable=false 时再调用 /api/v1/rtc/token 补偿。'),
apiStep('MicUp 进入 pending_publish', 'gateway', 'POST', '/api/v1/rooms/mic/up', '成功后回填 mic_session_id / room_version。'),
clientStep('客户端切主播并启动本地音频', 'App 调 TRTC switchRole(anchor) + startLocalAudio发布成功后立刻执行下一步 ConfirmMicPublishing。'),
apiStep('确认 RTC 音频发布成功', 'gateway', 'POST', '/api/v1/rooms/mic/publishing/confirm', '必须带 mic_session_id / room_version / event_time_ms避免旧事件污染新麦位会话。'),
apiStep('拉房间快照验证 publishing', 'gateway', 'GET', '/api/v1/rooms/snapshot'),
apiStep('业务 presence heartbeat', 'gateway', 'POST', '/api/v1/rooms/heartbeat'),
apiStep('下麦', 'gateway', 'POST', '/api/v1/rooms/mic/down'),
apiStep('离开房间', 'gateway', 'POST', '/api/v1/rooms/leave'),
],
},
{
id: 'mic-publish-timeout',
title: '04 MicUp 未确认 -> publish_timeout 自动释放验证',
description: '验证 MicUp 只代表业务占麦成功,不代表 RTC 已发布;不调用 ConfirmMicPublishing等待 deadline 后观察状态被自动清理。',
destructive: true,
steps: [
apiStep('创建房间', 'gateway', 'POST', '/api/v1/rooms/create'),
apiStep('加入房间', 'gateway', 'POST', '/api/v1/rooms/join'),
apiStep('获取 RTC token', 'gateway', 'POST', '/api/v1/rtc/token'),
apiStep('MicUp 进入 pending_publish', 'gateway', 'POST', '/api/v1/rooms/mic/up'),
manualStep('不要调用 ConfirmMicPublishing', '等待返回里的 publish_deadline_ms 或服务配置 deadline 之后,再执行后续查询。'),
apiStep('查询当前房间状态', 'gateway', 'GET', '/api/v1/rooms/current'),
apiStep('拉快照确认麦位释放', 'gateway', 'GET', '/api/v1/rooms/snapshot'),
],
},
{
id: 'message-inbox',
title: '05 App 消息 tab/inbox 读写链路',
description: '验证 App 消息页后端负责的 system/activity inbox用户私聊仍走腾讯云 IM SDK不走本仓库 HTTP。',
destructive: true,
steps: [
apiStep('查询消息 tab', 'gateway', 'GET', '/api/v1/messages/tabs'),
apiStep('分页查询消息', 'gateway', 'GET', '/api/v1/messages'),
apiStep('标记单条已读', 'gateway', 'POST', '/api/v1/messages/{message_id}/read'),
apiStep('标记分区全部已读', 'gateway', 'POST', '/api/v1/messages/read-all'),
apiStep('删除当前用户可见消息', 'gateway', 'DELETE', '/api/v1/messages/{message_id}'),
],
},
{
id: 'resource-gift-wallet',
title: '06 资源/礼物/送礼/币商转账链路',
description: '验证 App 资源读、我的资源佩戴、礼物配置读取、SendGift 扣费表现、币商给玩家转金币。送礼和币商转账都依赖真实余额和身份。',
destructive: true,
steps: [
apiStep('读取资源列表', 'gateway', 'GET', '/api/v1/resources'),
apiStep('读取资源组', 'gateway', 'GET', '/api/v1/resource-groups/{group_id}'),
apiStep('读取礼物列表', 'gateway', 'GET', '/api/v1/gifts'),
apiStep('读取我的资源', 'gateway', 'GET', '/api/v1/users/me/resources'),
apiStep('佩戴资源', 'gateway', 'POST', '/api/v1/users/me/resources/{resource_id}/equip'),
apiStep('房间送礼', 'gateway', 'POST', '/api/v1/rooms/gift/send'),
apiStep('币商给玩家转金币', 'gateway', 'POST', '/api/v1/wallet/coin-seller/transfer'),
],
},
{
id: 'admin-region-country',
title: '07 后台国家/区域配置链路',
description: '验证后台登录、国家主数据、区域配置、区域国家替换App 新注册和改国家应读取最新映射,历史 rebuild 由服务端任务处理。',
destructive: true,
steps: [
apiStep('后台登录', 'admin', 'POST', '/api/v1/auth/login'),
apiStep('后台当前用户', 'admin', 'GET', '/api/v1/auth/me'),
apiStep('查询国家列表', 'admin', 'GET', '/api/v1/admin/countries'),
apiStep('创建国家', 'admin', 'POST', '/api/v1/admin/countries'),
apiStep('启用国家', 'admin', 'POST', '/api/v1/admin/countries/{country_id}/enable'),
apiStep('查询区域列表', 'admin', 'GET', '/api/v1/admin/regions'),
apiStep('创建区域', 'admin', 'POST', '/api/v1/admin/regions'),
apiStep('替换区域国家列表', 'admin', 'PUT', '/api/v1/admin/regions/{region_id}/countries'),
apiStep('启用区域', 'admin', 'POST', '/api/v1/admin/regions/{region_id}/enable'),
],
},
{
id: 'admin-wallet-stock',
title: '08 后台币商库存入账 -> App 币商转账',
description: '验证后台给币商 STOCK_CREDIT 入账,再用该币商 app token 给玩家转金币;两边需要分别使用 admin token 和 app token。',
destructive: true,
steps: [
apiStep('后台登录', 'admin', 'POST', '/api/v1/auth/login'),
apiStep('查询币商列表', 'admin', 'GET', '/api/v1/admin/coin-sellers'),
apiStep('币商库存入账', 'admin', 'POST', '/api/v1/admin/coin-sellers/{user_id}/stock-credits'),
apiStep('App 币商转账给玩家', 'gateway', 'POST', '/api/v1/wallet/coin-seller/transfer'),
apiStep('后台查询充值账单', 'admin', 'GET', '/api/v1/admin/payment/recharge-bills'),
],
},
];
function allEndpointsForFilter() {
const source = $('sourceFilter').value;
if (source === 'gateway') return GATEWAY_ENDPOINTS;
if (source === 'admin') return ADMIN_ENDPOINTS;
if (source === 'internal') return INTERNAL_ENDPOINTS;
return [...GATEWAY_ENDPOINTS, ...ADMIN_ENDPOINTS];
}
function endpointByStep(step) {
return { source: step.source, executable: step.kind === 'api', method: step.method, path: step.path, operationId: '', summary: step.label, tag: step.source };
}
function isUploadEndpoint(ep) {
return ep.executable && ep.method === 'POST' && /\/files\/.*upload$/.test(ep.path);
}
function renderApiCard(container, ep, index, label, note) {
const el = document.createElement('div');
el.className = `${container === $('flowSteps') ? 'step' : 'endpoint'} card`;
const upload = isUploadEndpoint(ep);
const disabled = ep.executable ? '' : 'disabled';
el.innerHTML = `
<div class="${container === $('flowSteps') ? 'step-head' : 'endpoint-head'}">
<span class="pill">${index}</span>
<span class="pill ${ep.method.toLowerCase()}">${ep.method}</span>
<div><div class="path">${ep.path}</div><div class="summary">${label || ep.summary || '-'}${note ? ' / ' + note : ''}</div></div>
<button class="secondary" type="button">展开</button>
</div>
<div class="details">
<label>Query JSON <textarea class="query-json">${JSON.stringify(exampleQuery(ep), null, 2)}</textarea></label>
<label>Body JSON <textarea class="body-json">${JSON.stringify(exampleBody(ep), null, 2)}</textarea></label>
${upload ? '<label>Multipart File <input class="file-input" type="file" /></label>' : ''}
<div class="row"><button class="send-endpoint" ${disabled}>执行这一步</button>${ep.executable ? '' : '<span class="pill">内部 gRPC 文档,浏览器不可直接调用</span>'}</div>
</div>`;
el.querySelector('.secondary').addEventListener('click', () => el.classList.toggle('open'));
el.querySelector('.send-endpoint')?.addEventListener('click', async () => {
try {
await executeApiElement(ep, el);
} catch (err) {
log(err.stack || err.message, 'bad');
}
});
// 保存步骤元数据,链路顺序执行时直接 await避免 click 后异步请求并发打乱状态。
el.__endpoint = ep;
container.appendChild(el);
return el;
}
async function executeApiElement(ep, el) {
const source = ep.source === 'admin' ? 'admin' : 'gateway';
if (isUploadEndpoint(ep)) {
const file = el.querySelector('.file-input')?.files?.[0];
return upload(source, ep.path, file);
}
const query = parseJSON(el.querySelector('.query-json').value, {});
const bodyRaw = el.querySelector('.body-json').value.trim();
const body = bodyRaw && bodyRaw !== 'undefined' ? parseJSON(bodyRaw, undefined) : undefined;
return request(source, ep.method, ep.path, body, query);
}
function renderFlowOptions() {
$('flowSelect').innerHTML = FLOW_DEFINITIONS.map((flow) => `<option value="${flow.id}">${flow.title}</option>`).join('');
}
function currentFlow() {
return FLOW_DEFINITIONS.find((flow) => flow.id === $('flowSelect').value) || FLOW_DEFINITIONS[0];
}
function renderFlow() {
const flow = currentFlow();
$('flowDescription').textContent = flow.description;
$('flowSteps').innerHTML = '';
flow.steps.forEach((step, index) => {
if (step.kind === 'api') {
renderApiCard($('flowSteps'), endpointByStep(step), index + 1, step.label, step.note);
return;
}
const el = document.createElement('div');
el.className = 'step card';
el.innerHTML = `
<div class="step-head">
<span class="pill">${index + 1}</span>
<span class="pill ${step.kind}">${step.kind === 'client' ? 'CLIENT SDK' : 'MANUAL'}</span>
<div><div class="path">${step.label}</div><div class="summary">${step.note}</div></div>
<button class="secondary" type="button">记录完成</button>
</div>`;
el.querySelector('button').addEventListener('click', () => log({ step: index + 1, kind: step.kind, label: step.label, note: step.note }));
$('flowSteps').appendChild(el);
});
}
async function runCurrentFlowApiSteps() {
const flow = currentFlow();
if (flow.destructive && !confirm(`链路「${flow.title}」包含写操作或状态变更,确认按当前页面参数顺序执行 API 步骤?`)) return;
const cards = [...$('flowSteps').querySelectorAll('.step.card')];
for (const card of cards) {
const ep = card.__endpoint;
if (!ep) {
log('跳过非 API 步骤:' + card.querySelector('.path')?.textContent);
continue;
}
try {
await executeApiElement(ep, card);
} catch (err) {
log(err.stack || err.message, 'bad');
break;
}
}
}
function renderEndpoints() {
const method = $('methodFilter').value;
const needle = $('search').value.trim().toLowerCase();
const endpoints = allEndpointsForFilter().filter((ep) => {
if (method !== 'all' && ep.method !== method) return false;
const text = `${ep.source} ${ep.method} ${ep.path} ${ep.operationId} ${ep.summary} ${ep.tag}`.toLowerCase();
return !needle || text.includes(needle);
});
$('endpointCount').textContent = `显示 ${endpoints.length} 个接口gateway=${GATEWAY_ENDPOINTS.length}admin=${ADMIN_ENDPOINTS.length},内部 gRPC 引用=${INTERNAL_ENDPOINTS.length}`;
$('endpoints').innerHTML = '';
endpoints.forEach((ep, index) => renderApiCard($('endpoints'), ep, index + 1));
}
document.addEventListener('DOMContentLoaded', () => {
loadConfig();
renderFlowOptions();
renderFlow();
renderEndpoints();
$('saveConfig').addEventListener('click', saveConfig);
$('clearTokens').addEventListener('click', () => { $('accessToken').value = ''; $('refreshToken').value = ''; $('adminToken').value = ''; saveConfig(); });
$('clearLog').addEventListener('click', () => { $('log').textContent = ''; });
$('renderFlow').addEventListener('click', renderFlow);
$('runFlowApi').addEventListener('click', runCurrentFlowApiSteps);
$('flowSelect').addEventListener('change', renderFlow);
$('renderEndpoints').addEventListener('click', renderEndpoints);
$('sourceFilter').addEventListener('change', renderEndpoints);
$('methodFilter').addEventListener('change', renderEndpoints);
$('search').addEventListener('input', renderEndpoints);
log(`接口元数据已加载gateway=${GATEWAY_ENDPOINTS.length}, admin=${ADMIN_ENDPOINTS.length}, internal=${INTERNAL_ENDPOINTS.length}`);
});
</script>
</body>
</html>