feat: 支付登记从平台选择订阅——登录拉取预填,密码不落库

This commit is contained in:
2026-10-08 22:32:33 +08:00
parent 29e0317155
commit ed2ff0f950
8 changed files with 551 additions and 4 deletions
+143
View File
@@ -0,0 +1,143 @@
import { OpenAPIHono, createRoute } from "@hono/zod-openapi";
import { z } from "zod";
import {
ERR,
fetchPlatformSubscriptions,
platformLogin,
} from "@new-api-key-usage/core";
import { jsonError, requireAdminSession, type AppVars } from "../middleware.ts";
import { err401, err422, err502 } from "../api-schemas.ts";
const platformSubsSchema = z.object({
subscriptions: z.array(
z.object({
id: z.number().int(),
plan_id: z.number().int(),
start_time: z.number().int(),
end_time: z.number().int(),
status: z.string(),
amount_total: z.number(),
amount_used: z.number(),
}),
),
});
const loginBody = z.object({
username: z.string().min(1),
password: z.string().min(1),
});
const listBody = z.object({
access_token: z.string().min(1),
});
/**
* 登记辅助:控制台账号登录 + 拉订阅。密码不落库;access_token 由前端短暂持有。
*/
export function adminPlatformRoutes() {
const r = new OpenAPIHono<{ Variables: AppVars }>();
r.use("*", requireAdminSession);
r.openapi(
createRoute({
method: "post",
path: "/platform/login",
summary: "登录 NewAPI 控制台(登记辅助)",
tags: ["支付"],
security: [{ adminSession: [] }],
request: {
body: {
content: { "application/json": { schema: loginBody } },
required: true,
},
},
responses: {
200: {
description: "access_token",
content: {
"application/json": {
schema: z.object({ access_token: z.string() }),
},
},
},
401: err401,
422: err422,
502: err502,
},
}),
async (c) => {
const body = await c.req.json().catch(() => null);
const username = String(body?.username ?? "");
const password = String(body?.password ?? "");
if (!username || !password) {
return jsonError(
c,
422,
ERR.validation_failed({
username: username ? "" : "请填写用户名",
password: password ? "" : "请填写密码",
}),
);
}
const login = await platformLogin(c.get("newapiBase"), username, password);
if (!login.ok) {
if (login.kind === "rejected") return jsonError(c, 422, ERR.invalid_credentials());
if (login.kind === "unreachable") return jsonError(c, 502, ERR.upstream_unreachable());
return jsonError(
c,
422,
ERR.validation_failed({
password: "登录未成功——可能需验证码/加密登录,或账号密码不正确",
}),
);
}
return c.json({ access_token: login.data.access_token }, 200);
},
);
r.openapi(
createRoute({
method: "post",
path: "/platform/subscriptions",
summary: "拉取订阅列表(登记辅助)",
tags: ["支付"],
security: [{ adminSession: [] }],
request: {
body: {
content: { "application/json": { schema: listBody } },
required: true,
},
},
responses: {
200: {
description: "subscriptions",
content: { "application/json": { schema: platformSubsSchema } },
},
401: err401,
422: err422,
502: err502,
},
}),
async (c) => {
const body = await c.req.json().catch(() => null);
const token = String(body?.access_token ?? "");
if (!token) {
return jsonError(
c,
422,
ERR.validation_failed({ access_token: "缺少 access_token" }),
);
}
const subs = await fetchPlatformSubscriptions(c.get("newapiBase"), token);
if (!subs.ok) {
if (subs.kind === "rejected") {
return jsonError(c, 401, ERR.session_expired());
}
return jsonError(c, 502, ERR.upstream_unreachable());
}
return c.json({ subscriptions: subs.data }, 200);
},
);
return r;
}