227 lines
7.9 KiB
TypeScript
227 lines
7.9 KiB
TypeScript
import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Patch, Post, Query, Req, UseGuards } from '@nestjs/common';
|
|
import { ApiBearerAuth, ApiOperation, ApiQuery, ApiTags } from '@nestjs/swagger';
|
|
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
|
import { RolesGuard } from '../common/guards/roles.guard';
|
|
import { Roles } from '../common/decorators/roles.decorator';
|
|
import { AdminService } from './admin.service';
|
|
import {
|
|
CopyCatalogDto,
|
|
DryRunDto,
|
|
PurgeDto,
|
|
ResetPasswordDto,
|
|
ResetVenueDto,
|
|
TestEmailDto,
|
|
UpdateRoleDto,
|
|
VenueNameDto,
|
|
} from './dto/admin.dto';
|
|
|
|
/** The superadmin page: every venue, terminal and user of KoolTap. Destructive actions take a dryRun to preview. */
|
|
@ApiTags('Admin')
|
|
@ApiBearerAuth()
|
|
@UseGuards(JwtAuthGuard, RolesGuard)
|
|
@Roles('superadmin')
|
|
@Controller('admin')
|
|
export class AdminController {
|
|
constructor(private admin: AdminService) {}
|
|
|
|
// ---- Venues
|
|
|
|
@Get('venues')
|
|
@ApiOperation({ summary: 'Every venue with what it holds' })
|
|
listVenues() {
|
|
return this.admin.listVenues();
|
|
}
|
|
|
|
@Post('venues')
|
|
@ApiOperation({ summary: 'Create a venue' })
|
|
createVenue(@Body() dto: VenueNameDto, @Req() req: any) {
|
|
return this.admin.createVenue(dto.name, req.user);
|
|
}
|
|
|
|
@Patch('venues/:venueId')
|
|
@ApiOperation({ summary: 'Rename a venue' })
|
|
renameVenue(@Param('venueId') venueId: string, @Body() dto: VenueNameDto, @Req() req: any) {
|
|
return this.admin.renameVenue(venueId, dto.name, req.user);
|
|
}
|
|
|
|
@Delete('venues/:venueId')
|
|
@ApiOperation({ summary: 'Delete a venue with everything in it, users and terminals included' })
|
|
@ApiQuery({ name: 'confirmName', description: "The venue's name" })
|
|
deleteVenue(@Param('venueId') venueId: string, @Query('confirmName') confirmName: string | undefined, @Req() req: any) {
|
|
return this.admin.deleteVenue(venueId, confirmName, req.user);
|
|
}
|
|
|
|
@Post('venues/:venueId/view')
|
|
@HttpCode(HttpStatus.OK)
|
|
@ApiOperation({ summary: "Start viewing the dashboard as the venue; recorded in the venue's audit log" })
|
|
viewVenue(@Param('venueId') venueId: string, @Req() req: any) {
|
|
return this.admin.viewVenue(venueId, req.user);
|
|
}
|
|
|
|
@Post('venues/:venueId/copy-catalog')
|
|
@HttpCode(HttpStatus.OK)
|
|
@ApiOperation({ summary: "Copy another venue's products that this venue doesn't have yet" })
|
|
copyCatalog(@Param('venueId') venueId: string, @Body() dto: CopyCatalogDto, @Req() req: any) {
|
|
return this.admin.copyCatalog(venueId, dto.fromVenueId, req.user);
|
|
}
|
|
|
|
@Post('venues/:venueId/reset')
|
|
@HttpCode(HttpStatus.OK)
|
|
@ApiOperation({ summary: "Wipe parts of a venue's data; users, terminals and products stay" })
|
|
resetVenue(@Param('venueId') venueId: string, @Body() dto: ResetVenueDto, @Req() req: any) {
|
|
return this.admin.resetVenue(venueId, dto.scopes, { stockValue: dto.stockValue, dryRun: dto.dryRun }, req.user);
|
|
}
|
|
|
|
@Get('venues/:venueId/consistency')
|
|
@ApiOperation({ summary: "Look for data in the venue that doesn't add up" })
|
|
consistency(@Param('venueId') venueId: string) {
|
|
return this.admin.consistency(venueId);
|
|
}
|
|
|
|
@Get('venues/:venueId/export')
|
|
@ApiOperation({ summary: 'Everything of the venue as JSON, without passwords and pictures' })
|
|
exportVenue(@Param('venueId') venueId: string, @Req() req: any) {
|
|
return this.admin.exportVenue(venueId, req.user);
|
|
}
|
|
|
|
@Post('venues/:venueId/registration-codes')
|
|
@ApiOperation({ summary: 'A pairing code for a terminal of the venue' })
|
|
createRegistrationCode(@Param('venueId') venueId: string, @Req() req: any) {
|
|
return this.admin.createRegistrationCode(venueId, req.user);
|
|
}
|
|
|
|
// ---- Terminals
|
|
|
|
@Get('devices')
|
|
@ApiOperation({ summary: 'Every terminal, unpaired ones included' })
|
|
listDevices() {
|
|
return this.admin.listDevices();
|
|
}
|
|
|
|
@Post('devices/:deviceId/revoke')
|
|
@HttpCode(HttpStatus.OK)
|
|
@ApiOperation({ summary: 'Unpair a terminal' })
|
|
revokeDevice(@Param('deviceId') deviceId: string, @Req() req: any) {
|
|
return this.admin.revokeDevice(deviceId, req.user);
|
|
}
|
|
|
|
@Get('registration-codes')
|
|
@ApiOperation({ summary: 'Pairing codes that can still be used' })
|
|
listRegistrationCodes() {
|
|
return this.admin.listRegistrationCodes();
|
|
}
|
|
|
|
@Post('registration-codes/:id/revoke')
|
|
@HttpCode(HttpStatus.OK)
|
|
@ApiOperation({ summary: 'Make a pairing code unusable' })
|
|
revokeRegistrationCode(@Param('id') id: string) {
|
|
return this.admin.revokeRegistrationCode(id);
|
|
}
|
|
|
|
@Post('registration-codes/cleanup')
|
|
@HttpCode(HttpStatus.OK)
|
|
@ApiOperation({ summary: 'Delete used and expired pairing codes' })
|
|
cleanupRegistrationCodes() {
|
|
return this.admin.cleanupRegistrationCodes();
|
|
}
|
|
|
|
@Post('commands/expire-stuck')
|
|
@HttpCode(HttpStatus.OK)
|
|
@ApiOperation({ summary: 'Expire every pending command' })
|
|
expireStuckCommands(@Body() dto: DryRunDto, @Req() req: any) {
|
|
return this.admin.expireStuckCommands(dto.dryRun ?? false, req.user);
|
|
}
|
|
|
|
// ---- Users
|
|
|
|
@Get('users')
|
|
@ApiOperation({ summary: 'Every dashboard user' })
|
|
listUsers() {
|
|
return this.admin.listUsers();
|
|
}
|
|
|
|
@Patch('users/:userId/role')
|
|
@ApiOperation({ summary: "Change a user's role (not your own)" })
|
|
updateRole(@Param('userId') userId: string, @Body() dto: UpdateRoleDto, @Req() req: any) {
|
|
return this.admin.updateRole(userId, dto.role, req.user);
|
|
}
|
|
|
|
@Post('users/:userId/password')
|
|
@HttpCode(HttpStatus.OK)
|
|
@ApiOperation({ summary: "Set a user's password; signs them out everywhere" })
|
|
resetPassword(@Param('userId') userId: string, @Body() dto: ResetPasswordDto, @Req() req: any) {
|
|
return this.admin.resetPassword(userId, dto.password, req.user);
|
|
}
|
|
|
|
@Post('users/:userId/logout')
|
|
@HttpCode(HttpStatus.OK)
|
|
@ApiOperation({ summary: 'Sign a user out everywhere, at once' })
|
|
logoutUser(@Param('userId') userId: string, @Req() req: any) {
|
|
return this.admin.logoutUser(userId, req.user);
|
|
}
|
|
|
|
@Delete('users/:userId')
|
|
@ApiOperation({ summary: 'Delete a user (not yourself)' })
|
|
deleteUser(@Param('userId') userId: string, @Req() req: any) {
|
|
return this.admin.deleteUser(userId, req.user);
|
|
}
|
|
|
|
@Post('sessions/revoke-all')
|
|
@HttpCode(HttpStatus.OK)
|
|
@ApiOperation({ summary: 'Sign out every user but yourself' })
|
|
logoutEveryone(@Req() req: any) {
|
|
return this.admin.logoutEveryone(req.user);
|
|
}
|
|
|
|
// ---- Maintenance
|
|
|
|
@Post('purge')
|
|
@HttpCode(HttpStatus.OK)
|
|
@ApiOperation({ summary: 'Delete old terminal events, audit entries or finished commands' })
|
|
purge(@Body() dto: PurgeDto, @Req() req: any) {
|
|
return this.admin.purge(dto.targets, dto.olderThanDays, dto.venueId, dto.dryRun ?? false, req.user);
|
|
}
|
|
|
|
@Post('images/cleanup')
|
|
@HttpCode(HttpStatus.OK)
|
|
@ApiOperation({ summary: 'Delete pictures no product uses (older than a week)' })
|
|
cleanupImages(@Body() dto: DryRunDto, @Req() req: any) {
|
|
return this.admin.cleanupImages(dto.dryRun ?? false, req.user);
|
|
}
|
|
|
|
@Post('email/test')
|
|
@HttpCode(HttpStatus.OK)
|
|
@ApiOperation({ summary: 'Send the test email to an address' })
|
|
sendTestEmail(@Body() dto: TestEmailDto, @Req() req: any) {
|
|
return this.admin.sendTestEmail(dto.to, req.user);
|
|
}
|
|
|
|
// ---- Insight
|
|
|
|
@Get('audit')
|
|
@ApiOperation({ summary: 'The audit log of every venue' })
|
|
@ApiQuery({ name: 'venueId', required: false })
|
|
@ApiQuery({ name: 'action', required: false, description: 'Part of the action name' })
|
|
@ApiQuery({ name: 'limit', required: false })
|
|
@ApiQuery({ name: 'offset', required: false })
|
|
auditLog(
|
|
@Query('venueId') venueId?: string,
|
|
@Query('action') action?: string,
|
|
@Query('limit') limit?: string,
|
|
@Query('offset') offset?: string,
|
|
) {
|
|
return this.admin.auditLog({
|
|
venueId: venueId || undefined,
|
|
action: action || undefined,
|
|
limit: Math.min(Math.max(parseInt(limit ?? '', 10) || 50, 1), 200),
|
|
offset: Math.max(parseInt(offset ?? '', 10) || 0, 0),
|
|
});
|
|
}
|
|
|
|
@Get('health')
|
|
@ApiOperation({ summary: 'Database, migrations, live connections and background jobs' })
|
|
health() {
|
|
return this.admin.health();
|
|
}
|
|
}
|