Skip to content
Merged
Show file tree
Hide file tree
Changes from 66 commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
747ea5c
feat: adapt mission update command to receive metadata json without s…
lbarberi1927 Sep 12, 2025
7951eec
feat: adapt mission update command to receive metadata json without s…
lbarberi1927 Sep 12, 2025
75a1bab
feat: update mission info to display the correct info with metadata t…
lbarberi1927 Sep 12, 2025
57d7858
feat: add check to ensure update metadata types are coherent
lbarberi1927 Sep 17, 2025
9e22a56
fix: tiny bugfix for imports
lbarberi1927 Sep 17, 2025
56bda59
feat: project get returns required tags too
lbarberi1927 Sep 23, 2025
89f779d
feat: create mission validates metadata against required metadata
lbarberi1927 Sep 23, 2025
0c2377c
chore: validate mission after creation
lbarberi1927 Sep 24, 2025
2fa1ba7
fix: address ESLint errors
lbarberi1927 Sep 24, 2025
1a64144
fix: address prettier/prettier errors
lbarberi1927 Oct 1, 2025
8db2f52
bump to version 0.51.0
wp99cp Oct 1, 2025
26b09b9
add remarks to getting started guide that Safari might cause issues (…
wp99cp Oct 1, 2025
da2d70e
fix: mission name cannot end with whitespace error message
lbarberi1927 Oct 1, 2025
7e27ec7
fix: verbose mission query error for mission delete command
lbarberi1927 Oct 1, 2025
3074d95
fix: klein mission delete abort message displays mission name if proj…
lbarberi1927 Oct 1, 2025
63304a9
fix: kupdating metadata with non-existent tag throws verbose message
lbarberi1927 Oct 1, 2025
5435bc1
feat: add experimental support for additional file types (#1758)
wp99cp Oct 2, 2025
12cbc6e
fix file formating
wp99cp Oct 2, 2025
09372fd
fix file formating
wp99cp Oct 2, 2025
131f8fa
fix: disambiguate klein mission delete error message
lbarberi1927 Oct 3, 2025
1d96109
Merge pull request #1770 from leggedrobotics/feat/1712_CRUD_mission_ops
lbarberi1927 Oct 3, 2025
6705f0a
fix: black reformat new files
lbarberi1927 Oct 3, 2025
39f74dd
fix: project get finds exact matches instead of LIKE matches
lbarberi1927 Oct 6, 2025
ec7c32c
fix: add parameter to specify exact matching query for deleting proje…
lbarberi1927 Oct 6, 2025
2df5b44
fix: address no inferrable types lint error
lbarberi1927 Oct 6, 2025
c0adcb0
chore(deps-dev): bump typescript from 5.9.2 to 5.9.3 in /common
dependabot[bot] Oct 6, 2025
a7c3015
chore(deps-dev): bump @types/node from 24.5.2 to 24.7.0 in /frontend
dependabot[bot] Oct 6, 2025
7a4574d
chore(deps-dev): bump jiti from 2.5.1 to 2.6.1 in /frontend
dependabot[bot] Oct 6, 2025
3ec55a5
chore(deps): bump @tanstack/vue-query from 5.87.1 to 5.90.2 in /frontend
dependabot[bot] Oct 6, 2025
fdbef3f
chore(deps): bump quasar from 2.18.2 to 2.18.5 in /frontend
dependabot[bot] Oct 6, 2025
8871678
chore(deps): bump the open-telemetry group
dependabot[bot] Oct 6, 2025
e1eae5c
chore(deps-dev): bump @types/node in /queueConsumer
dependabot[bot] Oct 6, 2025
95ee2ee
chore(deps): bump the open-telemetry group in /backend with 9 updates
dependabot[bot] Oct 6, 2025
e50181c
chore(deps-dev): bump @types/express from 5.0.1 to 5.0.3 in /backend
dependabot[bot] Oct 6, 2025
9c73622
chore(deps): bump winston from 3.17.0 to 3.18.3 in /queueConsumer
dependabot[bot] Oct 6, 2025
406fc15
chore(deps): bump minio from 8.0.5 to 8.0.6 in /backend
dependabot[bot] Oct 6, 2025
32f457e
chore(deps): bump winston from 3.17.0 to 3.18.3 in /backend
dependabot[bot] Oct 6, 2025
8c37409
chore(deps-dev): bump typescript from 5.9.2 to 5.9.3 in /common
dependabot[bot] Oct 6, 2025
aab55e3
chore(deps): bump winston from 3.17.0 to 3.18.3 in /backend
dependabot[bot] Oct 6, 2025
2e4e809
chore(deps): bump minio from 8.0.5 to 8.0.6 in /backend
dependabot[bot] Oct 6, 2025
54dd8bb
chore(deps): bump winston from 3.17.0 to 3.18.3 in /queueConsumer
dependabot[bot] Oct 6, 2025
f3e5b9d
chore(deps-dev): bump @types/express from 5.0.1 to 5.0.3 in /backend
dependabot[bot] Oct 6, 2025
72a86e9
chore(deps): bump the open-telemetry group in /backend with 9 updates
dependabot[bot] Oct 6, 2025
05896e6
chore(deps-dev): bump @types/node in /queueConsumer
dependabot[bot] Oct 6, 2025
13538e7
chore(deps): bump the open-telemetry group
dependabot[bot] Oct 6, 2025
827a8db
chore(deps): bump quasar from 2.18.2 to 2.18.5 in /frontend
dependabot[bot] Oct 6, 2025
b9b1e1c
chore(deps-dev): bump jiti from 2.5.1 to 2.6.1 in /frontend
dependabot[bot] Oct 6, 2025
e4fdf36
Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/common/s…
wp99cp Oct 10, 2025
a74eeb5
Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/frontend…
wp99cp Oct 10, 2025
79c49e1
Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/frontend…
wp99cp Oct 10, 2025
517b2a3
Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/frontend…
wp99cp Oct 10, 2025
bbd0494
Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/frontend…
wp99cp Oct 10, 2025
9734915
Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/queueCon…
wp99cp Oct 10, 2025
c3db8f2
Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/queueCon…
wp99cp Oct 10, 2025
d47723c
Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/backend/…
wp99cp Oct 10, 2025
b5a793b
Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/backend/…
wp99cp Oct 10, 2025
524ffcc
Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/queueCon…
wp99cp Oct 10, 2025
44db70d
Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/backend/…
wp99cp Oct 10, 2025
2df2b99
Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/backend/…
wp99cp Oct 10, 2025
88a6990
chore(deps-dev): bump @types/node from 24.6.0 to 24.7.0 in /common
dependabot[bot] Oct 10, 2025
5cc04e8
chore(deps-dev): bump typescript from 5.9.2 to 5.9.3 in /queueConsumer
dependabot[bot] Oct 10, 2025
00641c2
chore(deps-dev): bump @types/dockerode in /queueConsumer
dependabot[bot] Oct 10, 2025
400f8e7
Merge remote-tracking branch 'origin/fix/1788_project_deletion' into dev
wp99cp Oct 10, 2025
b74b02a
Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/common/s…
wp99cp Oct 10, 2025
125c159
Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/queueCon…
wp99cp Oct 10, 2025
14fc736
Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/queueCon…
wp99cp Oct 10, 2025
95ad76d
introduce DatatypeNotSupported instea of iFileNameNotSupported
wp99cp Oct 10, 2025
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
4 changes: 4 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
# ignore bag and mcap files
**/*.mcap
**/*.bag
**/*.db3
**/*.tum
**/*.svo2
**/*.yaml

# ignore build info file
frontend/src/build.ts
26 changes: 13 additions & 13 deletions backend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "kleinkram-backend",
"version": "0.50.2",
"version": "0.51.0",
"description": "",
"author": "",
"private": true,
Expand Down Expand Up @@ -31,15 +31,15 @@
"@nestjs/swagger": "^8.0.1",
"@nestjs/typeorm": "^10.0.2",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/exporter-prometheus": "^0.205.0",
"@opentelemetry/exporter-trace-otlp-http": "^0.205.0",
"@opentelemetry/instrumentation-express": "^0.54.3",
"@opentelemetry/instrumentation-fetch": "^0.205.0",
"@opentelemetry/instrumentation-http": "^0.205.0",
"@opentelemetry/instrumentation-nestjs-core": "^0.52.2",
"@opentelemetry/instrumentation-pg": "^0.58.3",
"@opentelemetry/instrumentation-winston": "^0.50.2",
"@opentelemetry/sdk-node": "^0.205.0",
"@opentelemetry/exporter-prometheus": "^0.206.0",
"@opentelemetry/exporter-trace-otlp-http": "^0.206.0",
"@opentelemetry/instrumentation-express": "^0.55.0",
"@opentelemetry/instrumentation-fetch": "^0.206.0",
"@opentelemetry/instrumentation-http": "^0.206.0",
"@opentelemetry/instrumentation-nestjs-core": "^0.53.0",
"@opentelemetry/instrumentation-pg": "^0.59.0",
"@opentelemetry/instrumentation-winston": "^0.51.0",
"@opentelemetry/sdk-node": "^0.206.0",
"@opentelemetry/sdk-trace-base": "^2.0.0",
"@swc/core": "^1.13.3",
"@swc/jest": "^0.2.39",
Expand All @@ -54,7 +54,7 @@
"form-data": "^4.0.4",
"googleapis": "^148.0.0",
"jsonwebtoken": "^9.0.2",
"minio": "8.0.5",
"minio": "8.0.6",
"multer": "^2.0.2",
"passport": "^0.7.0",
"passport-github2": "^0.1.12",
Expand All @@ -70,7 +70,7 @@
"vue": "^3.5.21",
"vue-json-pretty": "^2.4.0",
"webpack": "^5.99.9",
"winston": "^3.17.0",
"winston": "^3.18.3",
"winston-loki": "^6.0.7"
},
"devDependencies": {
Expand All @@ -80,7 +80,7 @@
"@nestjs/schematics": "^11.0.4",
"@nestjs/testing": "^10.0.0",
"@types/cookie-parser": "^1.4.9",
"@types/express": "^5.0.0",
"@types/express": "^5.0.3",
"@types/jest": "^29.5.2",
"@types/node": "^24.0.7",
"@types/supertest": "^6.0.3",
Expand Down
10 changes: 7 additions & 3 deletions backend/src/endpoints/project/project.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { CreateProject } from '@common/api/types/create-project.dto';
import { ProjectDto } from '@common/api/types/project/base-project.dto';
import { DeleteProjectResponseDto } from '@common/api/types/project/delete-project-response.dto';
import { ProjectQueryDto } from '@common/api/types/project/project-query.dto';
import { ProjectWithRequiredTags } from '@common/api/types/project/project-with-required-tags';
import { ProjectWithRequiredTagsDto } from '@common/api/types/project/project-with-required-tags.dto';
import { ProjectsDto } from '@common/api/types/project/projects.dto';
import { ResentProjectsDto } from '@common/api/types/project/recent-projects.dto';
import { RemoveTagTypeDto } from '@common/api/types/remove-tag-type.dto';
Expand Down Expand Up @@ -64,11 +64,11 @@ export class ProjectController {
@CanReadProject()
@ApiOkResponse({
description: 'Returns the project',
type: ProjectWithRequiredTags,
type: ProjectWithRequiredTagsDto,
})
async getProjectById(
@ParameterUID('uuid') uuid: string,
): Promise<ProjectWithRequiredTags> {
): Promise<ProjectWithRequiredTagsDto> {
return this.projectService.findOne(uuid);
}

Expand Down Expand Up @@ -107,6 +107,9 @@ export class ProjectController {
@Query() query: ProjectQueryDto,
@AddUser() user: AuthHeader,
): Promise<ProjectsDto> {
// Convert string 'true'/'false' to boolean
const exactMatch = query.exactMatch === 'true';

return await this.projectService.findMany(
query.projectUuids ?? [],
query.projectPatterns ?? [],
Expand All @@ -116,6 +119,7 @@ export class ProjectController {
query.take,
query.creatorUuid,
user.user.uuid,
exactMatch,
);
}

Expand Down
16 changes: 16 additions & 0 deletions backend/src/serialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
import { ProjectDto } from '@common/api/types/project/base-project.dto';
import { ProjectWithMissionCountDto } from '@common/api/types/project/project-with-mission-count.dto';
import { ProjectWithMissionsDto } from '@common/api/types/project/project-with-missions.dto';
import { ProjectWithRequiredTagsDto } from '@common/api/types/project/project-with-required-tags.dto';
import { TagDto, TagTypeDto } from '@common/api/types/tags/tags.dto';
import { TopicDto } from '@common/api/types/topic.dto';
import { GroupMembershipDto, UserDto } from '@common/api/types/user.dto';
Expand Down Expand Up @@ -282,6 +283,21 @@ export const projectEntityToDtoWithRequiredTags = (
};
};

export const projectEntityToDtoWithMissionCountAndTags = (
project: Project,
): ProjectWithRequiredTagsDto => {
if (project.creator === undefined) {
throw new Error('Creator can never be undefined');
}

return {
...(projectEntityToDto(project) as ProjectWithRequiredTagsDto),
creator: userEntityToDto(project.creator),
missionCount: project.missionCount ?? 0,
requiredTags: project.requiredTags?.map(tagTypeEntityToDto) ?? [],
};
};

export const projectEntityToDtoWithMissions = (
project: Project,
): ProjectWithMissionsDto => {
Expand Down
42 changes: 34 additions & 8 deletions backend/src/services/file.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
Injectable,
NotFoundException,
OnModuleInit,
UnsupportedMediaTypeException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import jwt from 'jsonwebtoken';
Expand Down Expand Up @@ -519,9 +520,9 @@ export class FileService implements OnModuleInit {

return await externalMinio.presignedUrl(
'GET',
file.type === FileType.MCAP
? env.MINIO_MCAP_BUCKET_NAME
: env.MINIO_BAG_BUCKET_NAME,
file.type === FileType.BAG
? env.MINIO_BAG_BUCKET_NAME
: env.MINIO_MCAP_BUCKET_NAME,
file.uuid, // we use the uuid as the filename in Minio
expires ? 4 * 60 * 60 : 604_800, // 604800 seconds = 1 week
{
Expand Down Expand Up @@ -792,15 +793,40 @@ export class FileService implements OnModuleInit {

logger.debug(`Creating temporary access for file: ${filename}`);

// verify that file has ending .bag or .mcap
if (!filename.endsWith('.bag') && !filename.endsWith('.mcap')) {
const fileExtensionToFileTypeMap: ReadonlyMap<
string,
FileType
> = new Map([
['.bag', FileType.BAG],
['.mcap', FileType.MCAP],
['.yaml', FileType.YAML],
['.svo2', FileType.SVO2],
['.tum', FileType.TUM],
['.db3', FileType.DB3],
]);

const supported_file_endings = [
...fileExtensionToFileTypeMap.keys(),
];

if (
!supported_file_endings.some((ending) =>
filename.endsWith(ending),
)
) {
emptyCredentials.error = 'Invalid file ending';
return emptyCredentials;
}

const fileType: FileType = filename.endsWith('.bag')
? FileType.BAG
: FileType.MCAP;
const matchingFileType = supported_file_endings.find((ending) =>
filename.endsWith(ending),
);
if (matchingFileType === undefined)
throw new UnsupportedMediaTypeException();
const fileType: FileType | undefined =
fileExtensionToFileTypeMap.get(matchingFileType);
if (fileType === undefined)
throw new UnsupportedMediaTypeException();

// check if file already exists
const existingFile = await this.fileRepository.exists({
Expand Down
4 changes: 3 additions & 1 deletion backend/src/services/mission.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,9 @@ export class MissionService {
let query = this.missionRepository
.createQueryBuilder('mission')
.leftJoinAndSelect('mission.project', 'project')
.leftJoinAndSelect('mission.creator', 'creator');
.leftJoinAndSelect('mission.creator', 'creator')
.leftJoinAndSelect('mission.tags', 'tag')
.leftJoinAndSelect('tag.tagType', 'tagType');

query = addAccessConstraintsToMissionQuery(query, userUuid);

Expand Down
13 changes: 8 additions & 5 deletions backend/src/services/project.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { DefaultRightDto } from '@common/api/types/access-control/default-right.
import { DefaultRights } from '@common/api/types/access-control/default-rights';
import { SortOrder } from '@common/api/types/pagination';
import { ProjectDto } from '@common/api/types/project/base-project.dto';
import { ProjectWithRequiredTags } from '@common/api/types/project/project-with-required-tags';
import { ProjectWithRequiredTagsDto } from '@common/api/types/project/project-with-required-tags.dto';
import { ProjectsDto } from '@common/api/types/project/projects.dto';
import { ResentProjectDto } from '@common/api/types/project/recent-projects.dto';
import AccessGroup from '@common/entities/auth/accessgroup.entity';
Expand All @@ -32,7 +32,7 @@ import {
import { ConfigService } from '@nestjs/config';
import { AuthHeader } from '../endpoints/auth/parameter-decorator';
import {
projectEntityToDtoWithMissionCount,
projectEntityToDtoWithMissionCountAndTags,
projectEntityToDtoWithRequiredTags,
} from '../serialization';
import { AccessGroupConfig } from '../types/access-group-config';
Expand Down Expand Up @@ -77,10 +77,12 @@ export class ProjectService {
take: number,
creatorUuid: string | undefined,
userUuid: string,
exactMatch = false,
): Promise<ProjectsDto> {
let query = this.projectRepository
.createQueryBuilder('project')
.leftJoinAndSelect('project.creator', 'creator');
.leftJoinAndSelect('project.creator', 'creator')
.leftJoinAndSelect('project.requiredTags', 'requiredTags');

query = addAccessConstraintsToProjectQuery(query, userUuid);

Expand All @@ -89,6 +91,7 @@ export class ProjectService {
this.projectRepository,
projectUuids,
projectPatterns,
exactMatch,
);

if (sortBy !== undefined) {
Expand All @@ -103,15 +106,15 @@ export class ProjectService {

return {
data: projects.map((element) =>
projectEntityToDtoWithMissionCount(element),
projectEntityToDtoWithMissionCountAndTags(element),
),
count,
skip,
take,
};
}

async findOne(uuid: string): Promise<ProjectWithRequiredTags> {
async findOne(uuid: string): Promise<ProjectWithRequiredTagsDto> {
const missionPromise = this.projectRepository
.createQueryBuilder('project')
.where('project.uuid = :uuid', { uuid })
Expand Down
6 changes: 5 additions & 1 deletion backend/src/services/tag.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,11 @@ export class TagService {
if (name !== '') {
where.name = ILike(`%${name}%`);
}
if (type !== undefined && type !== DataType.ANY) {
if (
type !== undefined &&
type !== DataType.ANY &&
(type as any) !== ''
) {
where.datatype = type;
}
const [tags, count] = await this.tagTypeRepository.findAndCount({
Expand Down
20 changes: 12 additions & 8 deletions backend/src/services/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export const getFilteredProjectIdSubQuery = (
projectRepository: Repository<Project>,
projectIds: string[],
projectPatterns: string[],
exactMatch: boolean,
): SelectQueryBuilder<{ uuid: string }> => {
const query = projectRepository
.createQueryBuilder('project')
Expand All @@ -66,14 +67,15 @@ export const getFilteredProjectIdSubQuery = (
}

if (projectPatterns.length > 0) {
query.orWhere(
'LOWER(project.name) LIKE ANY(ARRAY[:...projectPatterns])',
{
projectPatterns: projectPatterns.map(
(pattern) => `%${pattern.toLowerCase()}%`,
),
},
);
const whereStatement = exactMatch
? 'LOWER(project.name) = ANY(ARRAY[:...projectPatterns])'
: 'LOWER(project.name) LIKE ANY(ARRAY[:...projectPatterns])';

query.orWhere(whereStatement, {
projectPatterns: projectPatterns.map(
(pattern) => `%${pattern.toLowerCase()}%`,
),
});
}

return query;
Expand Down Expand Up @@ -220,6 +222,7 @@ export const addProjectFilters = (
projectRepository: Repository<Project>,
projectIds: string[],
projectPatterns: string[],
exactMatch = false,
): SelectQueryBuilder<any> => {
if (projectIds.length > 0 || projectPatterns.length > 0) {
const projectLikePatterns = projectPatterns.map((element) =>
Expand All @@ -230,6 +233,7 @@ export const addProjectFilters = (
projectRepository,
projectIds,
projectLikePatterns,
exactMatch,
);

query
Expand Down
2 changes: 1 addition & 1 deletion backend/src/validation/validation-logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export const MISSION_NAME_REGEX = /^[\w\-_]{3,50}$/;

export const PROJECT_NAME_REGEX = /^[\w\-_]{3,50}$/;

export const FILE_NAME_REGEX = /^[\w\-.()]{3,50}.(bag|mcap)$/;
export const FILE_NAME_REGEX = /^[\w\-.()]{3,50}.(bag|mcap|db3|yaml|svo2|tum)$/;

export const NON_UUID_REGEX =
/^(?![0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$)/;
Loading
Loading