AI Implementation feature(820): Project Scaffold and Platform Foundations (#1)
This commit was merged in pull request #1.
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Req,
|
||||
Res,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiResponse, ApiBody } from '@nestjs/swagger';
|
||||
import { Request, Response } from 'express';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { ZodValidationPipe } from '../../common/pipes/zod-validation.pipe';
|
||||
import { Public } from '../../common/decorators/public.decorator';
|
||||
import { LoginDtoSchema, RefreshDtoSchema } from './dto/auth.dto';
|
||||
import { AuthService } from './auth.service';
|
||||
import { CsrfMiddleware } from '../../common/middleware/csrf.middleware';
|
||||
import { setRefreshCookie, clearRefreshCookie } from './cookie';
|
||||
|
||||
@ApiTags('auth')
|
||||
@Controller('api/v1/auth')
|
||||
export class AuthController {
|
||||
constructor(
|
||||
private readonly auth: AuthService,
|
||||
private readonly csrf: CsrfMiddleware,
|
||||
private readonly config: ConfigService,
|
||||
) {}
|
||||
|
||||
@Public()
|
||||
@Post('login')
|
||||
@ApiOperation({ summary: 'Login with username and password' })
|
||||
@ApiBody({ schema: { example: { username: 'admin', password: 'SuperSecret123!' } } })
|
||||
@ApiResponse({ status: 200, description: 'Login successful' })
|
||||
async login(
|
||||
@Body(new ZodValidationPipe(LoginDtoSchema)) body: { username: string; password: string },
|
||||
@Req() req: Request,
|
||||
@Res({ passthrough: true }) res: Response,
|
||||
) {
|
||||
const ip = (req.ip || req.socket.remoteAddress || 'unknown') as string;
|
||||
const session = await this.auth.login(body, ip);
|
||||
setRefreshCookie(res, session.refreshToken, this.config);
|
||||
return {
|
||||
accessToken: session.accessToken,
|
||||
expiresIn: session.expiresIn,
|
||||
user: session.user,
|
||||
};
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Post('refresh')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({ summary: 'Rotate the refresh token' })
|
||||
async refresh(
|
||||
@Req() req: Request,
|
||||
@Res({ passthrough: true }) res: Response,
|
||||
@Body(new ZodValidationPipe(RefreshDtoSchema)) body: { refreshToken?: string },
|
||||
) {
|
||||
const fromCookie = (req.cookies?.['rt'] as string | undefined) ?? '';
|
||||
const token = body.refreshToken ?? fromCookie;
|
||||
if (!token) return { message: 'No refresh token' };
|
||||
const session = await this.auth.refresh(token);
|
||||
setRefreshCookie(res, session.refreshToken, this.config);
|
||||
return {
|
||||
accessToken: session.accessToken,
|
||||
expiresIn: session.expiresIn,
|
||||
user: session.user,
|
||||
};
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Post('logout')
|
||||
@HttpCode(HttpStatus.NO_CONTENT)
|
||||
@ApiOperation({ summary: 'Invalidate the current refresh token' })
|
||||
async logout(@Req() req: Request, @Res({ passthrough: true }) res: Response): Promise<void> {
|
||||
const token = (req.cookies?.['rt'] as string | undefined) ?? '';
|
||||
await this.auth.logout(token || undefined);
|
||||
clearRefreshCookie(res);
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Get('csrf')
|
||||
@ApiOperation({ summary: 'Issue a CSRF token (sets csrf cookie, returns token)' })
|
||||
@ApiResponse({ status: 200, schema: { example: { csrfToken: '...' } } })
|
||||
csrfEndpoint(@Req() req: Request, @Res({ passthrough: true }) res: Response): { csrfToken: string } {
|
||||
const token = this.csrf.setOrGetCsrfToken(req, res);
|
||||
return { csrfToken: token };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user