Add device-certificate mTLS auth, live position tracking, and API docs
Introduces a CA/PKI module so field devices can authenticate to Mosquitto over TLS (8883) with per-device client certificates (CN = serial number) instead of a shared password, with matching Devices/MQTT-Certs UI. Adds live transmitter position tracking alongside logged points, an MQTTS transport option in the simulator for exercising the real cert-auth path, and Swagger API docs at /api/docs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import { OrgRole } from '@prisma/client';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsEmail, IsEnum, IsNotEmpty, IsString, MaxLength } from 'class-validator';
|
||||
|
||||
export class UpdateOrgDto {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@MaxLength(120)
|
||||
@@ -9,14 +11,17 @@ export class UpdateOrgDto {
|
||||
}
|
||||
|
||||
export class AddMemberDto {
|
||||
@ApiProperty({ description: 'Must belong to an existing user (they must have registered already)' })
|
||||
@IsEmail()
|
||||
email: string;
|
||||
|
||||
@ApiProperty({ enum: OrgRole })
|
||||
@IsEnum(OrgRole)
|
||||
role: OrgRole;
|
||||
}
|
||||
|
||||
export class UpdateMemberDto {
|
||||
@ApiProperty({ enum: OrgRole })
|
||||
@IsEnum(OrgRole)
|
||||
role: OrgRole;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post, UseGuards } from '@nestjs/common';
|
||||
import { ApiBearerAuth, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
import { CurrentPrincipal } from '../auth/decorators/current-user.decorator';
|
||||
import { Roles } from '../auth/decorators/roles.decorator';
|
||||
import { JwtAuthGuard, UserOrApiKeyGuard } from '../auth/guards/auth.guard';
|
||||
@@ -8,12 +9,14 @@ import { UserPrincipal } from '../auth/principal';
|
||||
import { AddMemberDto, UpdateMemberDto, UpdateOrgDto } from './dto/orgs.dto';
|
||||
import { OrgsService } from './orgs.service';
|
||||
|
||||
@ApiTags('orgs')
|
||||
@Controller('orgs')
|
||||
export class OrgsController {
|
||||
constructor(private readonly orgsService: OrgsService) {}
|
||||
|
||||
@Get()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ApiBearerAuth('jwt')
|
||||
listMine(@CurrentPrincipal() principal: UserPrincipal) {
|
||||
return this.orgsService.listForUser(principal.userId);
|
||||
}
|
||||
@@ -21,12 +24,15 @@ export class OrgsController {
|
||||
@Patch(':orgId')
|
||||
@UseGuards(JwtAuthGuard, OrgRolesGuard)
|
||||
@Roles('ORG_ADMIN')
|
||||
@ApiBearerAuth('jwt')
|
||||
rename(@Param('orgId') orgId: string, @Body() dto: UpdateOrgDto) {
|
||||
return this.orgsService.rename(orgId, dto.name);
|
||||
}
|
||||
|
||||
@Get(':orgId/members')
|
||||
@UseGuards(UserOrApiKeyGuard, OrgRolesGuard, ScopesGuard)
|
||||
@ApiBearerAuth('jwt')
|
||||
@ApiSecurity('apiKey')
|
||||
listMembers(@Param('orgId') orgId: string) {
|
||||
return this.orgsService.listMembers(orgId);
|
||||
}
|
||||
@@ -34,6 +40,7 @@ export class OrgsController {
|
||||
@Post(':orgId/members')
|
||||
@UseGuards(JwtAuthGuard, OrgRolesGuard)
|
||||
@Roles('ORG_ADMIN')
|
||||
@ApiBearerAuth('jwt')
|
||||
addMember(@Param('orgId') orgId: string, @Body() dto: AddMemberDto) {
|
||||
return this.orgsService.addMember(orgId, dto.email, dto.role);
|
||||
}
|
||||
@@ -41,6 +48,7 @@ export class OrgsController {
|
||||
@Patch(':orgId/members/:userId')
|
||||
@UseGuards(JwtAuthGuard, OrgRolesGuard)
|
||||
@Roles('ORG_ADMIN')
|
||||
@ApiBearerAuth('jwt')
|
||||
updateMember(
|
||||
@Param('orgId') orgId: string,
|
||||
@Param('userId') userId: string,
|
||||
@@ -52,6 +60,7 @@ export class OrgsController {
|
||||
@Delete(':orgId/members/:userId')
|
||||
@UseGuards(JwtAuthGuard, OrgRolesGuard)
|
||||
@Roles('ORG_ADMIN')
|
||||
@ApiBearerAuth('jwt')
|
||||
removeMember(@Param('orgId') orgId: string, @Param('userId') userId: string) {
|
||||
return this.orgsService.removeMember(orgId, userId);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user