Expand seller store functionality

This commit is contained in:
R524809
2026-09-07 17:51:11 +08:00
parent 4c0f3a4a79
commit ceb644a3f9
35 changed files with 32883 additions and 364 deletions
+2 -20
View File
@@ -1,4 +1,4 @@
import { Body, Controller, Get, Headers, Inject, Post, Query, Req, Res, UseGuards } from '@nestjs/common';
import { Body, Controller, Get, Headers, Inject, Post, Req, Res, UseGuards } from '@nestjs/common';
import { z } from 'zod';
import { collectedProductPayloadSchema } from '@atlas/contracts';
import { ApiError } from '../common/api-error.js';
@@ -6,28 +6,11 @@ import { Public } from '../common/public.decorator.js';
import { ExtensionAccessGuard, type ExtensionAuthenticatedRequest } from './extension-auth.guard.js';
import { ExtensionService } from './extension.service.js';
type RequestLike = { header: (name: string) => string | undefined };
type ResponseLike = { redirect: (status: number, url: string) => void; status: (code: number) => ResponseLike };
const authorizeSchema = z.object({ client_id: z.string().min(1).max(128), redirect_uri: z.url(), state: z.string().min(16).max(1_000), code_challenge: z.string().regex(/^[A-Za-z0-9_-]{43,128}$/), code_challenge_method: z.literal('S256') }).strict();
const exchangeSchema = z.object({ client_id: z.string().min(1).max(128), code: z.string().min(32).max(1_000), redirect_uri: z.url(), code_verifier: z.string().min(43).max(128) }).strict();
const refreshSchema = z.object({ refresh_token: z.string().min(32).max(1_000) }).strict();
type ResponseLike = { status: (code: number) => ResponseLike };
@Controller('extension')
export class ExtensionController {
public constructor(@Inject(ExtensionService) private readonly extension: ExtensionService) {}
@Get('authorize') @Public()
public async authorize(@Query() query: unknown, @Req() request: RequestLike, @Res() response: ResponseLike) {
const result = await this.extension.authorize(parse(authorizeSchema, query) as z.infer<typeof authorizeSchema>, cookie(request.header('cookie'), 'atlas_refresh'));
const target = 'redirect_url' in result ? result.redirect_url : result.login_url;
if (!target) throw new ApiError(500, 'EXTENSION_AUTHORIZATION_REDIRECT_MISSING', '扩展授权跳转地址缺失');
response.redirect(302, target);
}
@Post('auth/exchange') @Public()
public async exchange(@Body() body: unknown, @Res({ passthrough: true }) response: ResponseLike) { const result = await this.extension.exchange(parse(exchangeSchema, body) as z.infer<typeof exchangeSchema>); response.status(result.status); return result.body; }
@Post('auth/refresh') @Public()
public async refresh(@Body() body: unknown, @Res({ passthrough: true }) response: ResponseLike) { const input = parse(refreshSchema, body) as z.infer<typeof refreshSchema>; const result = await this.extension.refresh(input.refresh_token); response.status(result.status); return result.body; }
@Post('auth/revoke') @Public() @UseGuards(ExtensionAccessGuard)
public async revoke(@Req() request: ExtensionAuthenticatedRequest, @Res({ passthrough: true }) response: ResponseLike) { await this.extension.revoke(request.extensionAuth.workspaceId, request.extensionAuth.sessionId); response.status(204); }
@Get('session') @Public() @UseGuards(ExtensionAccessGuard)
public session(@Req() request: ExtensionAuthenticatedRequest) { return { data: { authenticated: true, workspace: { id: request.extensionAuth.workspaceId, available: true }, can_collect: true } }; }
@Post('materials') @Public() @UseGuards(ExtensionAccessGuard)
@@ -37,4 +20,3 @@ export class ExtensionController {
}
}
function parse<T extends { safeParse: (value: unknown) => { success: boolean; data?: unknown; error?: { issues: Array<{ path: PropertyKey[]; message: string }> } } }>(schema: T, value: unknown) { const result = schema.safeParse(value); if (result.success) return result.data as never; throw new ApiError(422, 'VALIDATION_FAILED', '请求参数不符合要求', result.error?.issues.map((item) => ({ field: item.path.join('.'), code: 'INVALID_FIELD', message: item.message }))); }
function cookie(header: string | undefined, name: string) { return header?.split(';').map((part) => part.trim()).find((part) => part.startsWith(`${name}=`))?.slice(name.length + 1); }