Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a1ac96d
Add server-side mission and project migration with CLI support
Idate96 Mar 3, 2026
fcbbd69
Align migration storage wiring with dev and tighten tests
Idate96 Mar 3, 2026
0b0ba94
Format CLI migration commands with black
Idate96 Mar 3, 2026
4972d6f
Fix MigrateMissionByBodyGuard constructor for backend build
Idate96 Mar 3, 2026
b1a7209
Fix migration auth/rollback edge cases and tighten tests
Idate96 Mar 3, 2026
968e2cc
Fix CI regressions in migration PR
Idate96 Mar 3, 2026
42dc87e
Address review feedback on migration guards and docs
Idate96 Mar 3, 2026
7fb32d0
Unify mission migration into bulk move across backend CLI and UI
Idate96 Mar 3, 2026
01b3610
Fix import ordering in migration route tests
Idate96 Mar 3, 2026
99374ac
Fix frontend lint errors in mission move dialog
Idate96 Mar 3, 2026
26d1967
Format mission guard file to satisfy prettier check
Idate96 Mar 3, 2026
1d99bf6
Fix mission move update path for integration stability
Idate96 Mar 3, 2026
9fbc0fb
Use query-builder collision checks for mission moves
Idate96 Mar 3, 2026
b86af82
Add explicit query-builder mock typing in mission tests
Idate96 Mar 3, 2026
bd488a7
Use repository exists check for mission-name collisions
Idate96 Mar 3, 2026
06238ae
Harden mission move against missing relation payloads
Idate96 Mar 3, 2026
7dec044
Normalize moved mission relation fields for lint compliance
Idate96 Mar 3, 2026
2cd2dc6
Replace class-instance spread in mission move mapping
Idate96 Mar 3, 2026
4f4f741
Log mission move response body in failing auth tests
Idate96 Mar 3, 2026
7aa5b2b
Validate mission move response DTO fields
Idate96 Mar 3, 2026
a056685
Remove unrelated Loki change and clarify move vs migrate intent
Idate96 Mar 4, 2026
bb2c3f1
Drop unrelated action-dispatcher diff from migration PR
Idate96 Mar 4, 2026
3529d9e
Fix move and migrate collision handling
Idate96 Mar 17, 2026
fd2a843
Merge origin/dev and address remaining PR feedback
Idate96 Mar 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions backend/src/endpoints/auth/guards/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export {
CreateGuard,
CreateInProjectByBodyGuard,
DeleteProjectGuard,
MigrateProjectByBodyGuard,
ReadProjectByNameGuard,
ReadProjectGuard,
WriteProjectGuard,
Expand All @@ -24,6 +25,7 @@ export {
CanReadManyMissionsGuard,
CreateInMissionByBodyGuard,
DeleteTagGuard,
MigrateMissionByBodyGuard,
MoveMissionToProjectGuard,
ReadMissionByNameGuard,
ReadMissionGuard,
Expand Down
69 changes: 57 additions & 12 deletions backend/src/endpoints/auth/guards/mission.guards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import {
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { isUUID } from 'class-validator';
import { BaseGuard } from './base.guards';

interface MissionBody {
missionUUID?: string;
missionUuid?: string;
uuid?: string;
mission?: string;
targetProjectUUID?: string;
}

interface TagParameters {
Expand Down Expand Up @@ -120,10 +122,6 @@ export class CreateInMissionByBodyGuard extends BaseGuard {
const body = request.body as MissionBody;
const missionUUID = body.missionUUID ?? body.missionUuid;

if (user.role === UserRole.ADMIN) {
return true;
}

if (!missionUUID) {
return false; // Deny access if UUID not provided
}
Expand All @@ -135,6 +133,9 @@ export class CreateInMissionByBodyGuard extends BaseGuard {
AccessGroupRights.CREATE,
);
}
if (user.role === UserRole.ADMIN) {
return true;
}
return this.missionGuardService.canAccessMission(
user,
missionUUID,
Expand All @@ -155,10 +156,6 @@ export class WriteMissionByBodyGuard extends BaseGuard {
const body = request.body as MissionBody;
const missionUUID = body.missionUUID ?? body.missionUuid;

if (user.role === UserRole.ADMIN) {
return true;
}

if (!missionUUID) {
return false; // Deny access if UUID not provided
}
Expand All @@ -170,6 +167,9 @@ export class WriteMissionByBodyGuard extends BaseGuard {
AccessGroupRights.WRITE,
);
}
if (user.role === UserRole.ADMIN) {
return true;
}
return this.missionGuardService.canAccessMission(
user,
missionUUID,
Expand All @@ -195,10 +195,6 @@ export class CanDeleteMissionGuard extends BaseGuard {
missionUUID = body.uuid ?? body.missionUUID ?? body.missionUuid;
}

if (user.role === UserRole.ADMIN) {
return true;
}

if (!missionUUID && params) {
missionUUID = params.uuid;
}
Expand All @@ -216,6 +212,9 @@ export class CanDeleteMissionGuard extends BaseGuard {
AccessGroupRights.DELETE,
);
}
if (user.role === UserRole.ADMIN) {
return true;
}
return this.missionGuardService.canAccessMission(
user,
missionUUID,
Expand Down Expand Up @@ -323,3 +322,49 @@ export class MoveMissionToProjectGuard extends BaseGuard {
);
}
}

@Injectable()
export class MigrateMissionByBodyGuard extends BaseGuard {
constructor(
private projectGuardService: ProjectGuardService,
private missionGuardService: MissionGuardService,
) {
super();
}

async canActivate(context: ExecutionContext): Promise<boolean> {
const { user, apiKey, request } = await this.getUser(context);

const body = request.body as MissionBody | undefined;
const missionUUID = body?.missionUUID;
const targetProjectUUID = body?.targetProjectUUID;

if (!missionUUID || !targetProjectUUID) {
throw new BadRequestException(
'missionUUID and targetProjectUUID are required',
);
}
if (!isUUID(missionUUID) || !isUUID(targetProjectUUID)) {
throw new BadRequestException(
'missionUUID and targetProjectUUID must be valid UUIDs',
);
}

if (apiKey) {
throw new UnauthorizedException('CLI Keys cannot move missions');
}

return (
(await this.projectGuardService.canAccessProject(
user,
targetProjectUUID,
AccessGroupRights.CREATE,
)) &&
(await this.missionGuardService.canAccessMission(
user,
missionUUID,
AccessGroupRights.DELETE,
))
);
}
}
47 changes: 47 additions & 0 deletions backend/src/endpoints/auth/guards/project.guards.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import { ProjectGuardService } from '@/services/project-guard.service';
import { AccessGroupRights, KeyTypes } from '@kleinkram/shared';
import {
BadRequestException,
ExecutionContext,
ForbiddenException,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { isUUID } from 'class-validator';
import { BaseGuard } from './base.guards';

interface ProjectBody {
projectUUID?: string;
uuid?: string;
sourceProjectUUID?: string;
targetProjectUUID?: string;
}

interface ProjectParameters {
Expand Down Expand Up @@ -191,3 +195,46 @@ export class CreateGuard extends BaseGuard {
return this.projectGuardService.canCreate(user);
}
}

@Injectable()
export class MigrateProjectByBodyGuard extends BaseGuard {
constructor(private projectGuardService: ProjectGuardService) {
super();
}

async canActivate(context: ExecutionContext): Promise<boolean> {
const { user, apiKey, request } = await this.getUser(context);

if (apiKey) {
throw new UnauthorizedException('CLI Keys cannot migrate projects');
}

const body = request.body as ProjectBody | undefined;
const sourceProjectUUID = body?.sourceProjectUUID;
const targetProjectUUID = body?.targetProjectUUID;

if (!sourceProjectUUID || !targetProjectUUID) {
throw new BadRequestException(
'sourceProjectUUID and targetProjectUUID are required',
);
}
if (!isUUID(sourceProjectUUID) || !isUUID(targetProjectUUID)) {
throw new BadRequestException(
'sourceProjectUUID and targetProjectUUID must be valid UUIDs',
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent validation order between project and mission migration guards

MigrateProjectByBodyGuard checks API key authorization before validating the request body UUIDs:

if (apiKey) {
    throw new UnauthorizedException('CLI Keys cannot migrate projects');
}
// ... then body validation at lines 216-225

But MigrateMissionByBodyGuard (same PR) validates body first, then checks API key:

if (!missionUUID || !targetProjectUUID) {
    throw new BadRequestException('...');
}
if (!isUUID(missionUUID) || !isUUID(targetProjectUUID)) {
    throw new BadRequestException('...');
}
if (apiKey) {
    throw new UnauthorizedException('CLI Keys cannot move missions');
}

This causes an API-key caller with invalid/missing UUIDs to receive:

  • 400 Bad Request for mission migration (body validated first)
  • 401 Unauthorized for project migration (auth checked first)

For consistency and better security posture (avoid leaking "this endpoint blocks API keys" before validation), validate request body fields before auth shortcuts. Recommend reordering to match MigrateMissionByBodyGuard pattern: body validation → apiKey check → permission checks.

Prompt To Fix With AI
This is a comment left during a code review.
Path: backend/src/endpoints/auth/guards/project.guards.ts
Line: 208-225

Comment:
**Inconsistent validation order between project and mission migration guards**

`MigrateProjectByBodyGuard` checks API key authorization before validating the request body UUIDs:
```ts
if (apiKey) {
    throw new UnauthorizedException('CLI Keys cannot migrate projects');
}
// ... then body validation at lines 216-225
```

But `MigrateMissionByBodyGuard` (same PR) validates body first, then checks API key:
```ts
if (!missionUUID || !targetProjectUUID) {
    throw new BadRequestException('...');
}
if (!isUUID(missionUUID) || !isUUID(targetProjectUUID)) {
    throw new BadRequestException('...');
}
if (apiKey) {
    throw new UnauthorizedException('CLI Keys cannot move missions');
}
```

This causes an API-key caller with invalid/missing UUIDs to receive:
- **400 Bad Request** for mission migration (body validated first)
- **401 Unauthorized** for project migration (auth checked first)

For consistency and better security posture (avoid leaking "this endpoint blocks API keys" before validation), validate request body fields before auth shortcuts. Recommend reordering to match `MigrateMissionByBodyGuard` pattern: body validation → apiKey check → permission checks.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — good catch on consistency. I aligned MigrateProjectByBodyGuard with the mission migration guard in 42dc87eb: body UUID presence/format is validated first, then API-key rejection is applied.


return (
(await this.projectGuardService.canAccessProject(
user,
sourceProjectUUID,
AccessGroupRights.DELETE,
)) &&
(await this.projectGuardService.canAccessProject(
user,
targetProjectUUID,
AccessGroupRights.CREATE,
))
);
}
}
28 changes: 28 additions & 0 deletions backend/src/endpoints/auth/roles.decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import {
DeleteProjectGuard,
DeleteTagGuard,
LoggedInUserGuard,
MigrateMissionByBodyGuard,
MigrateProjectByBodyGuard,
MoveFilesGuard,
MoveMissionToProjectGuard,
ReadActionGuard,
Expand Down Expand Up @@ -143,6 +145,19 @@ export function CanDeleteProject() {
);
}

export function CanMigrateProjectByBody() {
return applyDecorators(
SetMetadata('CanMigrateProjectByBody', true),
UseGuards(MigrateProjectByBodyGuard),
ApiResponse({
status: 401,
type: UnauthorizedExceptionDto,
description:
'User does not have permissions to migrate between the specified projects.',
}),
);
}
Comment on lines +147 to +169
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CanMigrateProjectByBody decorator is missing HTTP 403 documentation.

MigrateProjectByBodyGuard.canActivate can return false when canAccessProject evaluates to false for either the source or target project, which NestJS translates to a 403 Forbidden response. However, the decorator only documents the 401 case:

ApiResponse({ status: 401, ... })
// 403 is never declared

The parallel decorator CanMigrateMissionByBody (lines 213-229) correctly documents both 401 and 403. Clients and generated SDK consumers relying on the Swagger spec won't know to handle a 403 response on POST /projects/migrate.

Suggested change
export function CanMigrateProjectByBody() {
return applyDecorators(
SetMetadata('CanMigrateProjectByBody', true),
UseGuards(MigrateProjectByBodyGuard),
ApiResponse({
status: 401,
type: UnauthorizedExceptionDto,
description:
'User does not have permissions to migrate between the specified projects.',
}),
);
}
export function CanMigrateProjectByBody() {
return applyDecorators(
SetMetadata('CanMigrateProjectByBody', true),
UseGuards(MigrateProjectByBodyGuard),
ApiResponse({
status: 401,
type: UnauthorizedExceptionDto,
description:
'User does not have permissions to migrate between the specified projects.',
}),
ApiResponse({
status: 403,
type: ForbiddenException,
description:
'User does not have sufficient access rights on the source or target project.',
}),
);
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: backend/src/endpoints/auth/roles.decorator.ts
Line: 148-159

Comment:
`CanMigrateProjectByBody` decorator is missing HTTP 403 documentation. 

`MigrateProjectByBodyGuard.canActivate` can return `false` when `canAccessProject` evaluates to `false` for either the source or target project, which NestJS translates to a **403 Forbidden** response. However, the decorator only documents the 401 case:

```ts
ApiResponse({ status: 401, ... })
// 403 is never declared
```

The parallel decorator `CanMigrateMissionByBody` (lines 213-229) correctly documents both 401 and 403. Clients and generated SDK consumers relying on the Swagger spec won't know to handle a 403 response on `POST /projects/migrate`.

```suggestion
export function CanMigrateProjectByBody() {
    return applyDecorators(
        SetMetadata('CanMigrateProjectByBody', true),
        UseGuards(MigrateProjectByBodyGuard),
        ApiResponse({
            status: 401,
            type: UnauthorizedExceptionDto,
            description:
                'User does not have permissions to migrate between the specified projects.',
        }),
        ApiResponse({
            status: 403,
            type: ForbiddenException,
            description:
                'User does not have sufficient access rights on the source or target project.',
        }),
    );
}
```

How can I resolve this? If you propose a fix, please make it concise.


export function CanCreate() {
return applyDecorators(
SetMetadata('CanCreate', true),
Expand Down Expand Up @@ -195,6 +210,19 @@ export function CanMoveMission() {
);
}

export function CanMigrateMissionByBody() {
return applyDecorators(
SetMetadata('CanMigrateMissionByBody', true),
UseGuards(MigrateMissionByBodyGuard),
ApiResponse({
status: 403,
type: ForbiddenException,
description:
'User does not have permissions to migrate the specified mission.',
}),
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CanMigrateMissionByBody missing HTTP 401 documentation

MigrateMissionByBodyGuard.canActivate throws UnauthorizedException (HTTP 401) when an API key is detected:

if (apiKey) {
    throw new UnauthorizedException('CLI Keys cannot move missions');
}

But CanMigrateMissionByBody only documents status 403, not 401. This is inconsistent with the parallel CanMigrateProjectByBody decorator (line 148), which correctly documents 401. Clients or generated SDK consumers relying on the Swagger spec won't know to handle a 401 response.

Suggested change
export function CanMigrateMissionByBody() {
return applyDecorators(
SetMetadata('CanMigrateMissionByBody', true),
UseGuards(MigrateMissionByBodyGuard),
ApiResponse({
status: 403,
type: ForbiddenException,
description:
'User does not have permissions to migrate the specified mission.',
}),
);
}
export function CanMigrateMissionByBody() {
return applyDecorators(
SetMetadata('CanMigrateMissionByBody', true),
UseGuards(MigrateMissionByBodyGuard),
ApiResponse({
status: 401,
type: UnauthorizedExceptionDto,
description:
'User does not have permissions to migrate the specified mission.',
}),
);
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: backend/src/endpoints/auth/roles.decorator.ts
Line: 213-224

Comment:
`CanMigrateMissionByBody` missing HTTP 401 documentation

`MigrateMissionByBodyGuard.canActivate` throws `UnauthorizedException` (HTTP 401) when an API key is detected:

```ts
if (apiKey) {
    throw new UnauthorizedException('CLI Keys cannot move missions');
}
```

But `CanMigrateMissionByBody` only documents status 403, not 401. This is inconsistent with the parallel `CanMigrateProjectByBody` decorator (line 148), which correctly documents 401. Clients or generated SDK consumers relying on the Swagger spec won't know to handle a 401 response.

```suggestion
export function CanMigrateMissionByBody() {
    return applyDecorators(
        SetMetadata('CanMigrateMissionByBody', true),
        UseGuards(MigrateMissionByBodyGuard),
        ApiResponse({
            status: 401,
            type: UnauthorizedExceptionDto,
            description:
                'User does not have permissions to migrate the specified mission.',
        }),
    );
}
```

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 42dc87eb. CanMigrateMissionByBody now documents 401 (API key not allowed) in addition to the existing 403 response.


export function CanReadFile() {
return applyDecorators(
SetMetadata('CanReadFile', true),
Expand Down
24 changes: 24 additions & 0 deletions backend/src/endpoints/mission/mission.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
import {
CreateMission,
FlatMissionDto,
MigrateMissionDto,
MigrateMissionResponseDto,
MinimumMissionsDto,
MissionsDto,
MissionWithFilesDto,
Expand All @@ -21,6 +23,7 @@ import { ParameterUuid as ParameterUID } from '../../validation/parameter-decora
import {
CanCreateInProjectByBody,
CanDeleteMission,
CanMigrateMissionByBody,
CanMoveMission,
CanReadMission,
CanWriteMissionByBody,
Expand Down Expand Up @@ -172,6 +175,27 @@ export class MissionController {
return this.missionService.moveMission(missionUUID, projectUUID);
}

@Post('migrate')
@CanMigrateMissionByBody()
@ApiOkResponse({
description: 'Mission migrated to another project',
type: MigrateMissionResponseDto,
})
async migrateMission(
@Body() dto: MigrateMissionDto,
): Promise<MigrateMissionResponseDto> {
await this.missionService.migrateMission(
dto.missionUUID,
dto.targetProjectUUID,
dto.newName,
);
return {
success: true,
missionUUID: dto.missionUUID,
targetProjectUUID: dto.targetProjectUUID,
};
Comment on lines +177 to +187
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

movedMissionUUIDs silently dropped from response

The service returns both movedMissionCount and movedMissionUUIDs, but the controller discards the UUIDs:

return {
    success: true,
    movedMissionCount: result.movedMissionCount,
    targetProjectUUID: dto.targetProjectUUID,
    // movedMissionUUIDs: result.movedMissionUUIDs  ← never forwarded
};

MoveMissionsResponseDto doesn't declare the field either, so callers (CLI, frontend) cannot programmatically know which UUIDs were actually moved in a bulk operation. The parallel MigrateProjectResponseDto does expose movedMissionUUIDs, making this an API inconsistency.

Add the field to both the DTO and the controller response:

// move-missions.dto.ts
export class MoveMissionsResponseDto {
    @ApiProperty()
    @IsBoolean()
    success!: boolean;

    @ApiProperty()
    movedMissionCount!: number;

    @ApiProperty({ type: [String] })
    @IsArray()
    @IsUUID('4', { each: true })
    movedMissionUUIDs!: string[];

    @ApiProperty()
    targetProjectUUID!: string;
}
// controller
return {
    success: true,
    movedMissionCount: result.movedMissionCount,
    movedMissionUUIDs: result.movedMissionUUIDs,
    targetProjectUUID: dto.targetProjectUUID,
};
Prompt To Fix With AI
This is a comment left during a code review.
Path: backend/src/endpoints/mission/mission.controller.ts
Line: 176-185

Comment:
**`movedMissionUUIDs` silently dropped from response**

The service returns both `movedMissionCount` and `movedMissionUUIDs`, but the controller discards the UUIDs:

```ts
return {
    success: true,
    movedMissionCount: result.movedMissionCount,
    targetProjectUUID: dto.targetProjectUUID,
    // movedMissionUUIDs: result.movedMissionUUIDs  ← never forwarded
};
```

`MoveMissionsResponseDto` doesn't declare the field either, so callers (CLI, frontend) cannot programmatically know which UUIDs were actually moved in a bulk operation. The parallel `MigrateProjectResponseDto` **does** expose `movedMissionUUIDs`, making this an API inconsistency.

Add the field to both the DTO and the controller response:

```ts
// move-missions.dto.ts
export class MoveMissionsResponseDto {
    @ApiProperty()
    @IsBoolean()
    success!: boolean;

    @ApiProperty()
    movedMissionCount!: number;

    @ApiProperty({ type: [String] })
    @IsArray()
    @IsUUID('4', { each: true })
    movedMissionUUIDs!: string[];

    @ApiProperty()
    targetProjectUUID!: string;
}
```

```ts
// controller
return {
    success: true,
    movedMissionCount: result.movedMissionCount,
    movedMissionUUIDs: result.movedMissionUUIDs,
    targetProjectUUID: dto.targetProjectUUID,
};
```

How can I resolve this? If you propose a fix, please make it concise.

}

@Delete(':uuid')
@CanDeleteMission()
@OutputDto(null) // TODO: type API response
Expand Down
15 changes: 15 additions & 0 deletions backend/src/endpoints/project/project.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
CreateProject,
DefaultRights,
DeleteProjectResponseDto,
MigrateProjectDto,
MigrateProjectResponseDto,
ProjectAccessDto,
ProjectAccessListDto,
ProjectDto,
Expand All @@ -34,6 +36,7 @@ import { AddUser, AuthHeader } from '../auth/parameter-decorator';
import {
CanCreate,
CanDeleteProject,
CanMigrateProjectByBody,
CanReadProject,
CanWriteProject,
LoggedIn,
Expand Down Expand Up @@ -195,6 +198,18 @@ export class ProjectController {
): Promise<ProjectAccessListDto> {
return this.accessService.updateProjectAccess(uuid, body, auth);
}

@Post('migrate')
@CanMigrateProjectByBody()
@ApiOkResponse({
description: 'Project missions migrated to another project',
type: MigrateProjectResponseDto,
})
async migrateProject(
@Body() dto: MigrateProjectDto,
): Promise<MigrateProjectResponseDto> {
return this.projectService.migrateProject(dto);
}
}

// TODO: this controller should get removed at some point,
Expand Down
4 changes: 4 additions & 0 deletions backend/src/endpoints/project/project.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { ProjectService } from '@/services/project.service';
import { AccessGroupEntity, ProjectEntity } from '@kleinkram/backend-common';
import { AccountEntity } from '@kleinkram/backend-common/entities/auth/account.entity';
import { ProjectAccessEntity } from '@kleinkram/backend-common/entities/auth/project-access.entity';
import { MissionEntity } from '@kleinkram/backend-common/entities/mission/mission.entity';
import { TagTypeEntity } from '@kleinkram/backend-common/entities/tagType/tag-type.entity';
import { StorageModule } from '@kleinkram/backend-common/modules/storage/storage.module';
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { OldProjectController, ProjectController } from './project.controller';
Expand All @@ -16,7 +18,9 @@ import { OldProjectController, ProjectController } from './project.controller';
AccessGroupEntity,
TagTypeEntity,
ProjectAccessEntity,
MissionEntity,
]),
StorageModule,
],
providers: [ProjectService, AccessService],
exports: [ProjectService],
Expand Down
Loading
Loading