@@ -0,0 +1,226 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user