You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
118 lines
3.2 KiB
118 lines
3.2 KiB
import { parseAdminAuth } from "@/lib/api/admin-auth-context";
|
|
import { fail, ok } from "@/lib/response";
|
|
import { writeAudit } from "@/modules/audit/service";
|
|
import { AuthError } from "@/modules/auth/errors";
|
|
import {
|
|
hasCustomerProviderCredential,
|
|
isProviderCredentialProvider,
|
|
listCustomerProviderCredentialsForAdmin,
|
|
upsertCustomerProviderCredential,
|
|
} from "@/modules/customer/provider-credentials";
|
|
|
|
type RouteContext = {
|
|
params: Promise<{ customer_id: string }>;
|
|
};
|
|
|
|
export async function GET(request: Request, context: RouteContext) {
|
|
let auth;
|
|
try {
|
|
auth = parseAdminAuth(request);
|
|
} catch (error) {
|
|
if (error instanceof AuthError) {
|
|
return fail(error.code, error.message, error.httpStatus);
|
|
}
|
|
throw error;
|
|
}
|
|
|
|
const { customer_id: customerId } = await context.params;
|
|
const list = await listCustomerProviderCredentialsForAdmin(customerId);
|
|
await writeAudit(
|
|
"customer:provider_credential_view",
|
|
auth.userId,
|
|
customerId,
|
|
{ providers: list.filter((x) => x.has_password).map((x) => x.provider) },
|
|
);
|
|
return ok({ customer_id: customerId, list });
|
|
}
|
|
|
|
export async function PUT(request: Request, context: RouteContext) {
|
|
let auth;
|
|
try {
|
|
auth = parseAdminAuth(request);
|
|
} catch (error) {
|
|
if (error instanceof AuthError) {
|
|
return fail(error.code, error.message, error.httpStatus);
|
|
}
|
|
throw error;
|
|
}
|
|
|
|
const { customer_id: customerId } = await context.params;
|
|
|
|
let body: unknown;
|
|
try {
|
|
body = await request.json();
|
|
} catch {
|
|
return fail("VALIDATION_FAILED", "请求体格式无效", 400);
|
|
}
|
|
|
|
const payload = body as {
|
|
provider?: unknown;
|
|
email?: unknown;
|
|
password?: unknown;
|
|
clear?: unknown;
|
|
};
|
|
|
|
if (!isProviderCredentialProvider(payload.provider)) {
|
|
return fail("VALIDATION_FAILED", "provider 须为 mothership 或 flock", 400);
|
|
}
|
|
|
|
// 产品约束:查价网站账密仅支持新建客户时首次写入,禁止事后修改/清除
|
|
if (payload.clear === true) {
|
|
return fail(
|
|
"VALIDATION_FAILED",
|
|
"不支持清除查价网站账密;如需更换请新建客户并重新绑定",
|
|
400,
|
|
);
|
|
}
|
|
|
|
if (typeof payload.email !== "string") {
|
|
return fail("VALIDATION_FAILED", "登录邮箱无效", 400);
|
|
}
|
|
const password =
|
|
typeof payload.password === "string" ? payload.password : undefined;
|
|
|
|
try {
|
|
if (await hasCustomerProviderCredential(customerId, payload.provider)) {
|
|
return fail(
|
|
"VALIDATION_FAILED",
|
|
"该承运商账密已绑定,不支持修改;仅可在管理端查看",
|
|
400,
|
|
);
|
|
}
|
|
|
|
const saved = await upsertCustomerProviderCredential(
|
|
customerId,
|
|
payload.provider,
|
|
{
|
|
email: payload.email,
|
|
password,
|
|
updatedBy: auth.userId,
|
|
},
|
|
);
|
|
await writeAudit(
|
|
"customer:provider_credential_upsert",
|
|
auth.userId,
|
|
customerId,
|
|
{
|
|
provider: payload.provider,
|
|
email: saved.email,
|
|
password_updated: Boolean(password?.trim()),
|
|
},
|
|
);
|
|
return ok(saved);
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : "保存失败";
|
|
return fail("VALIDATION_FAILED", message, 400);
|
|
}
|
|
}
|