{"record":{"id":"f9518cc39943efbb","repo":"yikart/AiToEarn","slug":"token-mail-f9518c","errorCode":null,"errorMessage":"token和mail是必须的","messagePattern":"token和mail是必须的","errorType":"validation","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"project/aitoearn-electron/server/src/modules/plat/twitter/twitter.controller.ts","lineNumber":30,"sourceCode":"@Controller('plat/twitter')\nexport class TwitterController {\n  constructor(\n    private readonly twitterAuthService: TwitterAuthService,\n    private readonly twitterService: TwitterService,\n  ) {}\n\n  /**\n   * 获取Twitter授权URL\n   */\n  @Get('auth/url')\n  @ApiOperation({ summary: '获取Twitter授权URL' })\n  @ApiQuery({ name: 'mail', required: false, description: '用户邮箱' })\n  async getAuthUrl(\n    @GetToken() systemToken: TokenInfo,\n    @Query('mail') mail: string,\n  ) {\n    if (!systemToken.id || !mail) {\n      throw new BadRequestException('token和mail是必须的');\n    }\n    return this.twitterAuthService.getAuthorizationUrl(systemToken.id, mail);\n  }\n\n  /**\n   * 处理Twitter OAuth回调\n   */\n  @Get('auth/callback')\n  @ApiOperation({ summary: 'Twitter授权回调' })\n  // @ApiQuery({ name: 'code', required: true, description: '授权码' })\n  // @ApiQuery({ name: 'state', required: true, description: '状态值' })\n  @Public()\n  async handleOAuthCallback(\n    // @GetToken() systemToken: TokenInfo,\n    @Query('code') code: string,\n    @Query('state') state: string,\n    @Res() res: Response,\n  ) {","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-electron/server/src/modules/plat/twitter/twitter.controller.ts#L12-L48","documentation":"The GET /plat/twitter/auth/url endpoint starts Twitter OAuth by requiring a valid system JWT (extracted via @GetToken() into systemToken) and a `mail` query parameter. If the request lacks an authenticatable token (systemToken.id falsy) or omits mail, the controller throws BadRequestException('token和mail是必须的') with HTTP 400 before generating the authorization URL.","triggerScenarios":"Calling GET /plat/twitter/auth/url without an Authorization header / invalid expired JWT; calling with a valid token but no ?mail= query; passing mail as an empty string.","commonSituations":"Frontend forgetting to attach the auth token in an early onboarding step; Swagger 'try it out' without authorizing; mail collected later in the signup flow than the OAuth kickoff; URL-encoded mail lost by a redirect.","solutions":["Attach a valid system JWT (Authorization header) obtained from login before calling this endpoint","Append the user's email as a query param: GET /plat/twitter/auth/url?mail=user@example.com","Check why systemToken.id is empty — expired token or missing auth guard configuration; re-login to refresh the JWT","Trim/validate the mail input client-side before the request"],"exampleFix":"// before\nawait api.get('/plat/twitter/auth/url');\n// after\nawait api.get('/plat/twitter/auth/url', {\n  params: { mail: user.email },\n  headers: { Authorization: `Bearer ${systemToken}` },\n});","handlingStrategy":"validation","validationCode":"if (!systemToken || !userEmail) {\n  throw new Error('Cannot start Twitter OAuth: missing auth token or mail');\n}\nawait api.get('/plat/twitter/auth/url', { params: { mail: userEmail }, headers: { Authorization: `Bearer ${systemToken}` } });","typeGuard":"function canStartOAuth(t: unknown, mail: unknown): t is { id: string } {\n  return !!t && typeof (t as any).id === 'string' && (t as any).id.length > 0\n    && typeof mail === 'string' && mail.includes('@');\n}","tryCatchPattern":"try {\n  return await api.get('/plat/twitter/auth/url', { params: { mail } });\n} catch (e) {\n  if (e.response?.status === 400) {\n    // token missing/expired or mail absent — re-login and collect mail, then retry\n    await relogin();\n  }\n  throw e;\n}","preventionTips":["Attach the system JWT to every request via an axios/fetch interceptor","Collect the user's email before starting OAuth","Redirect to login on 401 so tokens never silently expire mid-flow","Validate query params are non-empty before dispatching requests"],"tags":["validation","bad-request","oauth","auth"],"backgroundTag":"missing-required-parameter","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}