src/client-portal/client-portal.controller.ts
client-portal
Methods |
|
| Async createToken | ||||||||||||
createToken(orgId: string, userId: string, dto: CreatePortalTokenDto)
|
||||||||||||
Decorators :
@Post('tokens')
|
||||||||||||
|
Parameters :
Returns :
unknown
|
| Async listTokens | ||||||
listTokens(clientId: string)
|
||||||
Decorators :
@Get('tokens')
|
||||||
|
Parameters :
Returns :
unknown
|
| Async resolve | ||||||
resolve(token: string)
|
||||||
Decorators :
@Get('resolve/:token')
|
||||||
|
Parameters :
Returns :
unknown
|
| Async revokeToken | ||||||
revokeToken(id: string)
|
||||||
Decorators :
@Delete('tokens/:id')
|
||||||
|
Parameters :
Returns :
unknown
|
| Async submit | ||||||||||||
submit(token: string, dto: SubmitPortalOrderDto, req: any)
|
||||||||||||
Decorators :
@Post('submit/:token')
|
||||||||||||
|
Parameters :
Returns :
unknown
|
| Async submitFile |
submitFile(token: string, file: Express.Multer.File, req: any)
|
Decorators :
@Post('submit/:token/file')
|
|
Returns :
unknown
|
| Async submitViaApiKey | ||||||||||||
submitViaApiKey(apiKey: string, dto: SubmitPortalOrderDto, req: any)
|
||||||||||||
Decorators :
@Post('api/orders')
|
||||||||||||
|
Parameters :
Returns :
unknown
|
import {
Controller,
Get,
Post,
Delete,
Body,
Param,
Query,
Req,
UseGuards,
UseInterceptors,
UploadedFile,
Headers,
UnauthorizedException,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { Throttle } from '@nestjs/throttler';
import { ApiTags, ApiBearerAuth, ApiOperation, ApiConsumes } from '@nestjs/swagger';
import { ClientPortalService } from './client-portal.service';
import { CreatePortalTokenDto, SubmitPortalOrderDto } from './dto/create-portal-token.dto';
import { CombinedAuthGuard } from '../auth/guards/combined-auth.guard';
import { RolesGuard } from '../auth/guards/roles.guard';
import { Roles } from '../auth/decorators/roles.decorator';
import { CurrentUser } from '../auth/decorators/current-user.decorator';
@ApiTags('Client Portal')
@Controller('client-portal')
export class ClientPortalController {
constructor(private readonly service: ClientPortalService) {}
// ── DISPATCHER ENDPOINTS (authenticated) ──────────────────────────────
@Post('tokens')
@ApiBearerAuth()
@UseGuards(CombinedAuthGuard, RolesGuard)
@Roles('DISPATCHER', 'OPERATIONS_MANAGER', 'ADMIN', 'SUPER_ADMIN', 'PLANNER', 'CUSTOMER_SERVICE')
@ApiOperation({ summary: 'Generate a new portal link + API key for a client' })
async createToken(
@CurrentUser('organizationId') orgId: string,
@CurrentUser('id') userId: string,
@Body() dto: CreatePortalTokenDto,
) {
return this.service.createToken(orgId, userId, dto);
}
@Get('tokens')
@ApiBearerAuth()
@UseGuards(CombinedAuthGuard, RolesGuard)
@Roles('DISPATCHER', 'OPERATIONS_MANAGER', 'ADMIN', 'SUPER_ADMIN', 'PLANNER', 'CUSTOMER_SERVICE')
@ApiOperation({ summary: 'List portal tokens for a client' })
async listTokens(@Query('clientId') clientId: string) {
return this.service.listTokens(clientId);
}
@Delete('tokens/:id')
@ApiBearerAuth()
@UseGuards(CombinedAuthGuard, RolesGuard)
@Roles('DISPATCHER', 'OPERATIONS_MANAGER', 'ADMIN', 'SUPER_ADMIN')
@ApiOperation({ summary: 'Revoke a portal token + API key' })
async revokeToken(@Param('id') id: string) {
return this.service.revokeToken(id);
}
// ── PUBLIC ENDPOINTS (no auth — token IS the credential) ─────────────
@Get('resolve/:token')
@Throttle({ short: { ttl: 60000, limit: 30 } })
@ApiOperation({ summary: 'Resolve a portal token → returns client info for the form' })
async resolve(@Param('token') token: string) {
return this.service.resolveToken(token);
}
@Post('submit/:token')
@Throttle({ short: { ttl: 60000, limit: 10 }, medium: { ttl: 3600000, limit: 100 } })
@ApiOperation({ summary: 'Submit an order via portal link (no auth)' })
async submit(
@Param('token') token: string,
@Body() dto: SubmitPortalOrderDto,
@Req() req: any,
) {
const ip = req.ip || req.headers['x-forwarded-for'] || 'unknown';
const userAgent = req.headers['user-agent'];
return this.service.submitOrderByToken(token, dto, ip, userAgent);
}
@Post('submit/:token/file')
@Throttle({ short: { ttl: 60000, limit: 5 }, medium: { ttl: 3600000, limit: 30 } })
@UseInterceptors(
FileInterceptor('file', {
limits: { fileSize: 15 * 1024 * 1024 }, // 15 MB
}),
)
@ApiConsumes('multipart/form-data')
@ApiOperation({
summary: 'Upload an Excel/CSV file via portal link — parses all rows into orders',
})
async submitFile(
@Param('token') token: string,
@UploadedFile() file: Express.Multer.File,
@Req() req: any,
) {
const ip = req.ip || req.headers['x-forwarded-for'] || 'unknown';
const userAgent = req.headers['user-agent'];
return this.service.submitFileByToken(token, file, ip, userAgent);
}
// ── PUBLIC API-KEY endpoint (for client ERPs) ────────────────────────
@Post('api/orders')
@Throttle({ short: { ttl: 60000, limit: 60 }, medium: { ttl: 3600000, limit: 1000 } })
@ApiOperation({ summary: 'Submit an order via API key (X-Client-Key header)' })
async submitViaApiKey(
@Headers('x-client-key') apiKey: string,
@Body() dto: SubmitPortalOrderDto,
@Req() req: any,
) {
if (!apiKey) throw new UnauthorizedException('X-Client-Key header required');
const ip = req.ip || req.headers['x-forwarded-for'] || 'unknown';
const userAgent = req.headers['user-agent'];
return this.service.submitOrderByApiKey(apiKey, dto, ip, userAgent);
}
}