fix(admin): remove manual reasons and repair code creation
This commit is contained in:
@@ -27,7 +27,6 @@ const mutationSchema = z.object({
|
||||
email: z.string().trim().email().optional(),
|
||||
userId: z.string().uuid().optional(),
|
||||
roleCode: z.enum(["owner", "model_admin", "billing_admin", "operations", "support", "auditor"]),
|
||||
reason: z.string().trim().min(1).max(500),
|
||||
}).refine((value) => Boolean(value.email || value.userId), {
|
||||
message: "email_or_user_id_required",
|
||||
});
|
||||
@@ -90,7 +89,7 @@ async function mutate(request: Request, assign: boolean) {
|
||||
if (!targetUserId) return NextResponse.json({ error: "用户不存在" }, { status: 404 });
|
||||
const rows = await queryAdminRows<{ user_id: string; role_code: string; assigned: boolean }>(
|
||||
"select * from public.admin_manage_role($1, $2, $3, $4, $5, $6)",
|
||||
[session.user.id, targetUserId, parsed.data.roleCode, assign, parsed.data.reason, requestId(request)],
|
||||
[session.user.id, targetUserId, parsed.data.roleCode, assign, "admin_console_manage_role", requestId(request)],
|
||||
);
|
||||
return NextResponse.json({ data: rows[0] });
|
||||
} catch (error) {
|
||||
|
||||
@@ -12,14 +12,10 @@ import {
|
||||
export const runtime = "nodejs";
|
||||
|
||||
const paramsSchema = z.object({ id: z.string().uuid() });
|
||||
const revokeCodeSchema = z.object({
|
||||
reason: z.string().trim().min(1).max(500),
|
||||
});
|
||||
const updateCodeSchema = z
|
||||
.object({
|
||||
note: z.string().trim().max(500).nullable().optional(),
|
||||
expiresAt: z.string().datetime({ offset: true }).nullable().optional(),
|
||||
reason: z.string().trim().min(1).max(500),
|
||||
})
|
||||
.refine((value) => "note" in value || "expiresAt" in value, {
|
||||
message: "至少提供一个可修改字段",
|
||||
@@ -52,7 +48,7 @@ export async function PATCH(
|
||||
p_note: body.note ?? null,
|
||||
p_set_expires_at: "expiresAt" in body,
|
||||
p_expires_at: body.expiresAt ?? null,
|
||||
p_reason: body.reason,
|
||||
p_reason: "admin_console_update_redemption_code",
|
||||
},
|
||||
);
|
||||
return NextResponse.json({ data: rows[0] });
|
||||
@@ -71,15 +67,12 @@ export async function DELETE(
|
||||
"billing.adjustments.write",
|
||||
);
|
||||
const parsed = paramsSchema.safeParse(await context.params);
|
||||
const parsedBody = revokeCodeSchema.safeParse(
|
||||
await request.json().catch(() => null),
|
||||
);
|
||||
if (!parsed.success || !parsedBody.success) return invalidQueryResponse();
|
||||
if (!parsed.success) return invalidQueryResponse();
|
||||
const rows = await runCodeRpc(
|
||||
"admin_revoke_redemption_code",
|
||||
session,
|
||||
requestId(request),
|
||||
{ p_code_id: parsed.data.id, p_reason: parsedBody.data.reason },
|
||||
{ p_code_id: parsed.data.id, p_reason: "admin_console_revoke_redemption_code" },
|
||||
);
|
||||
return NextResponse.json({ data: rows[0] });
|
||||
} catch (error) {
|
||||
|
||||
@@ -28,7 +28,6 @@ const createCodesSchema = z.object({
|
||||
count: z.number().int().min(1).max(100),
|
||||
expiresAt: z.string().datetime({ offset: true }).nullable().optional(),
|
||||
note: z.string().trim().max(500).nullable().optional(),
|
||||
reason: z.string().trim().min(1).max(500),
|
||||
});
|
||||
|
||||
type CodeRow = {
|
||||
@@ -147,7 +146,7 @@ export async function POST(request: Request) {
|
||||
"admin_create_redemption_codes",
|
||||
session,
|
||||
operationRequestId,
|
||||
{ p_codes: records, p_reason: parsed.data.reason },
|
||||
{ p_codes: records, p_reason: "admin_console_create_redemption_codes" },
|
||||
);
|
||||
const byMask = new Map<string, RedemptionCodeRecord>(
|
||||
stored.map((record) => [record.mask, record]),
|
||||
|
||||
@@ -14,7 +14,6 @@ export const runtime = "nodejs";
|
||||
const resetSchema = z.object({
|
||||
userId: z.string().uuid(),
|
||||
confirmation: z.literal("RESET"),
|
||||
reason: z.string().trim().min(1).max(500),
|
||||
});
|
||||
|
||||
type ResetRow = {
|
||||
@@ -40,7 +39,7 @@ export async function POST(request: Request) {
|
||||
[
|
||||
session.user.id,
|
||||
parsed.data.userId,
|
||||
parsed.data.reason,
|
||||
"admin_console_reset_customer",
|
||||
requestId(request),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -5,9 +5,9 @@ import { pageOffset, queryAdminRows } from "@/lib/admin/database";
|
||||
import { adminErrorResponse, invalidQueryResponse, parseListQuery, requestId, requireAdminMutation } from "@/lib/admin/http";
|
||||
export const runtime="nodejs";
|
||||
const schema=z.discriminatedUnion("action",[
|
||||
z.object({action:z.literal("save"),id:z.string().uuid().nullable().optional(),flagKey:z.string().regex(/^[a-z][a-z0-9._-]{1,99}$/),enabled:z.boolean(),rolloutPercentage:z.number().int().min(0).max(100),config:z.record(z.string(),z.unknown()).default({}),expectedVersion:z.number().int().positive().nullable().optional(),reason:z.string().trim().min(1).max(500)}).strict(),
|
||||
z.object({action:z.literal("publish"),id:z.string().uuid(),expectedVersion:z.number().int().positive(),reason:z.string().trim().min(1).max(500)}).strict(),
|
||||
z.object({action:z.literal("save"),id:z.string().uuid().nullable().optional(),flagKey:z.string().regex(/^[a-z][a-z0-9._-]{1,99}$/),enabled:z.boolean(),rolloutPercentage:z.number().int().min(0).max(100),config:z.record(z.string(),z.unknown()).default({}),expectedVersion:z.number().int().positive().nullable().optional()}).strict(),
|
||||
z.object({action:z.literal("publish"),id:z.string().uuid(),expectedVersion:z.number().int().positive()}).strict(),
|
||||
]);
|
||||
type Row={id:string;flag_key:string;version:number;enabled:boolean;rollout_percentage:number;config:Record<string,unknown>;status:string;created_at:Date;published_at:Date|null;total_count:string};
|
||||
export async function GET(request:Request){try{await requirePermission("admin.access");const p=parseListQuery(request);if(!p.success)return invalidQueryResponse(p.error.flatten());const q=p.data.q?`%${p.data.q}%`:null;const rows=await queryAdminRows<Row>(`select f.*,count(*) over()::text total_count from public.feature_flags f where ($1::text is null or f.flag_key ilike $1) and ($2::text is null or f.status=$2) order by f.created_at desc limit $3 offset $4`,[q,p.data.status??null,p.data.pageSize,pageOffset(p.data.page,p.data.pageSize)]);return NextResponse.json({data:rows.map(r=>({id:r.id,flagKey:r.flag_key,version:r.version,enabled:r.enabled,rolloutPercentage:r.rollout_percentage,config:r.config,status:r.status,createdAt:r.created_at.toISOString(),publishedAt:r.published_at?.toISOString()??null})),total:Number(rows[0]?.total_count??0)});}catch(e){return adminErrorResponse(e)}}
|
||||
export async function POST(request:Request){try{const b=schema.safeParse(await request.json().catch(()=>null));if(!b.success)return invalidQueryResponse(b.error.flatten());const session=await requireAdminMutation(request,"ops.flags.write");const rid=requestId(request);if(b.data.action==="publish"){const rows=await queryAdminRows<{id:string}>("select public.admin_publish_feature_flag($1,$2,$3,$4,$5) id",[session.user.id,b.data.id,b.data.expectedVersion,b.data.reason,rid]);return NextResponse.json({data:{id:rows[0]!.id,requestId:rid}})}const v=b.data;const rows=await queryAdminRows<{id:string}>("select public.admin_save_feature_flag($1,$2,$3,$4,$5,$6::jsonb,$7,$8,$9) id",[session.user.id,v.id??null,v.flagKey,v.enabled,v.rolloutPercentage,JSON.stringify(v.config),v.expectedVersion??null,v.reason,rid]);return NextResponse.json({data:{id:rows[0]!.id,requestId:rid}})}catch(e){return adminErrorResponse(e)}}
|
||||
export async function POST(request:Request){try{const b=schema.safeParse(await request.json().catch(()=>null));if(!b.success)return invalidQueryResponse(b.error.flatten());const session=await requireAdminMutation(request,"ops.flags.write");const rid=requestId(request);if(b.data.action==="publish"){const rows=await queryAdminRows<{id:string}>("select public.admin_publish_feature_flag($1,$2,$3,$4,$5) id",[session.user.id,b.data.id,b.data.expectedVersion,"admin_console_publish_feature_flag",rid]);return NextResponse.json({data:{id:rows[0]!.id,requestId:rid}})}const v=b.data;const rows=await queryAdminRows<{id:string}>("select public.admin_save_feature_flag($1,$2,$3,$4,$5,$6::jsonb,$7,$8,$9) id",[session.user.id,v.id??null,v.flagKey,v.enabled,v.rolloutPercentage,JSON.stringify(v.config),v.expectedVersion??null,"admin_console_save_feature_flag",rid]);return NextResponse.json({data:{id:rows[0]!.id,requestId:rid}})}catch(e){return adminErrorResponse(e)}}
|
||||
|
||||
@@ -17,7 +17,6 @@ const adjustmentSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
action: z.enum(["retry_grant", "compensate", "record_refund"]),
|
||||
expectedVersion: z.number().int().min(0),
|
||||
reason: z.string().trim().min(1).max(500),
|
||||
});
|
||||
|
||||
type Row = {
|
||||
@@ -125,7 +124,7 @@ export async function POST(request: Request) {
|
||||
parsed.data.id,
|
||||
parsed.data.action,
|
||||
parsed.data.expectedVersion,
|
||||
parsed.data.reason,
|
||||
"admin_console_adjust_order",
|
||||
requestId(request),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -32,12 +32,10 @@ const saveSchema = z.object({
|
||||
sortOrder: z.number().int(),
|
||||
oneTimePerUser: z.boolean(),
|
||||
entitlements: z.array(entitlementSchema).min(1),
|
||||
reason: z.string().trim().min(1).max(500),
|
||||
}).strict();
|
||||
const publishSchema = z.object({
|
||||
action: z.literal("publish"),
|
||||
id: z.string().uuid(),
|
||||
reason: z.string().trim().min(1).max(500),
|
||||
}).strict();
|
||||
const mutationSchema = z.discriminatedUnion("action", [saveSchema, publishSchema]);
|
||||
|
||||
@@ -91,7 +89,7 @@ export async function POST(request: Request) {
|
||||
const session = await requireAdminMutation(request, permission);
|
||||
const rid = requestId(request);
|
||||
if (body.data.action === "publish") {
|
||||
const rows = await queryAdminRows<{ id: string }>("select public.admin_publish_product($1,$2,$3,$4) id", [session.user.id, body.data.id, body.data.reason, rid]);
|
||||
const rows = await queryAdminRows<{ id: string }>("select public.admin_publish_product($1,$2,$3,$4) id", [session.user.id, body.data.id, "admin_console_publish_product", rid]);
|
||||
return NextResponse.json({ data: { id: rows[0]!.id, requestId: rid } });
|
||||
}
|
||||
const value = body.data;
|
||||
@@ -99,7 +97,7 @@ export async function POST(request: Request) {
|
||||
select public.admin_save_product_draft($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14::jsonb,$15,$16) id
|
||||
`, [session.user.id, value.id ?? null, value.code, value.name, value.description, value.productType,
|
||||
value.billingPeriod, value.intervalCount, value.priceCents, value.currency, value.enabled, value.sortOrder,
|
||||
value.oneTimePerUser, JSON.stringify(value.entitlements), value.reason, rid]);
|
||||
value.oneTimePerUser, JSON.stringify(value.entitlements), "admin_console_save_product", rid]);
|
||||
return NextResponse.json({ data: { id: rows[0]!.id, requestId: rid } });
|
||||
} catch (error) { return adminErrorResponse(error); }
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ const mutationSchema = z.object({
|
||||
action: z.enum(["extend", "revoke"]),
|
||||
days: z.number().int().min(1).max(3660).optional(),
|
||||
expectedEndsAt: z.string().datetime(),
|
||||
reason: z.string().trim().min(1).max(500),
|
||||
}).strict();
|
||||
type Row = { id:string; user_id:string; email:string|null; product_code:string; product_version:number; status:string; starts_at:Date; ends_at:Date; created_at:Date; total_count:string };
|
||||
export async function GET(request: Request) {
|
||||
@@ -29,7 +28,7 @@ export async function POST(request: Request){
|
||||
try{
|
||||
const body=mutationSchema.safeParse(await request.json().catch(()=>null)); if(!body.success)return invalidQueryResponse(body.error.flatten());
|
||||
const session=await requireAdminMutation(request,"billing.adjustments.write"); const rid=requestId(request);
|
||||
const rows=await queryAdminRows<{id:string}>("select public.admin_adjust_subscription($1,$2,$3,$4,$5,$6,$7) id",[session.user.id,body.data.id,body.data.action,body.data.days??null,body.data.expectedEndsAt,body.data.reason,rid]);
|
||||
const rows=await queryAdminRows<{id:string}>("select public.admin_adjust_subscription($1,$2,$3,$4,$5,$6,$7) id",[session.user.id,body.data.id,body.data.action,body.data.days??null,body.data.expectedEndsAt,"admin_console_adjust_subscription",rid]);
|
||||
return NextResponse.json({data:{id:rows[0]!.id,requestId:rid}});
|
||||
}catch(error){return adminErrorResponse(error);}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user