feat(api): add admin bulk import endpoints (MS21-MIG-004) (#567)
All checks were successful
ci/woodpecker/push/api Pipeline was successful

Co-authored-by: Jason Woltje <jason@diversecanvas.com>
Co-committed-by: Jason Woltje <jason@diversecanvas.com>
This commit was merged in pull request #567.
This commit is contained in:
2026-02-28 19:55:01 +00:00
committed by jason.woltje
parent 5b782bafc9
commit f99107fbfc
9 changed files with 968 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
import { IsNumber, IsOptional, IsString, MaxLength, MinLength } from "class-validator";
/**
* DTO for a single jarvis-brain project record.
* This matches the project object shape consumed by scripts/migrate-brain.ts.
*/
export class ImportProjectDto {
@IsString({ message: "id must be a string" })
@MinLength(1, { message: "id must not be empty" })
@MaxLength(255, { message: "id must not exceed 255 characters" })
id!: string;
@IsString({ message: "name must be a string" })
@MinLength(1, { message: "name must not be empty" })
@MaxLength(255, { message: "name must not exceed 255 characters" })
name!: string;
@IsOptional()
@IsString({ message: "description must be a string" })
description?: string | null;
@IsOptional()
@IsString({ message: "domain must be a string" })
domain?: string | null;
@IsOptional()
@IsString({ message: "status must be a string" })
status?: string | null;
// jarvis-brain project priority can be a number, string, or null
@IsOptional()
priority?: number | string | null;
@IsOptional()
@IsNumber({}, { message: "progress must be a number" })
progress?: number | null;
@IsOptional()
@IsString({ message: "repo must be a string" })
repo?: string | null;
@IsOptional()
@IsString({ message: "branch must be a string" })
branch?: string | null;
@IsOptional()
@IsString({ message: "current_milestone must be a string" })
current_milestone?: string | null;
@IsOptional()
@IsString({ message: "next_milestone must be a string" })
next_milestone?: string | null;
@IsOptional()
@IsString({ message: "blocker must be a string" })
blocker?: string | null;
@IsOptional()
@IsString({ message: "owner must be a string" })
owner?: string | null;
@IsOptional()
@IsString({ message: "docs_path must be a string" })
docs_path?: string | null;
@IsOptional()
@IsString({ message: "created must be a string" })
created?: string | null;
@IsOptional()
@IsString({ message: "updated must be a string" })
updated?: string | null;
@IsOptional()
@IsString({ message: "target_date must be a string" })
target_date?: string | null;
@IsOptional()
@IsString({ message: "notes must be a string" })
notes?: string | null;
@IsOptional()
@IsString({ message: "notes_nontechnical must be a string" })
notes_nontechnical?: string | null;
@IsOptional()
@IsString({ message: "parent must be a string" })
parent?: string | null;
}

View File

@@ -0,0 +1,5 @@
export interface ImportResponseDto {
imported: number;
skipped: number;
errors: string[];
}

View File

@@ -0,0 +1,76 @@
import { IsArray, IsNumber, IsOptional, IsString, MaxLength, MinLength } from "class-validator";
/**
* DTO for a single jarvis-brain task record.
* This matches the task object shape consumed by scripts/migrate-brain.ts.
*/
export class ImportTaskDto {
@IsString({ message: "id must be a string" })
@MinLength(1, { message: "id must not be empty" })
@MaxLength(255, { message: "id must not exceed 255 characters" })
id!: string;
@IsString({ message: "title must be a string" })
@MinLength(1, { message: "title must not be empty" })
@MaxLength(255, { message: "title must not exceed 255 characters" })
title!: string;
@IsOptional()
@IsString({ message: "domain must be a string" })
domain?: string | null;
@IsOptional()
@IsString({ message: "project must be a string" })
project?: string | null;
@IsOptional()
@IsArray({ message: "related must be an array" })
@IsString({ each: true, message: "related items must be strings" })
related?: string[];
@IsOptional()
@IsString({ message: "priority must be a string" })
priority?: string | null;
@IsOptional()
@IsString({ message: "status must be a string" })
status?: string | null;
@IsOptional()
@IsNumber({}, { message: "progress must be a number" })
progress?: number | null;
@IsOptional()
@IsString({ message: "due must be a string" })
due?: string | null;
@IsOptional()
@IsArray({ message: "blocks must be an array" })
@IsString({ each: true, message: "blocks items must be strings" })
blocks?: string[];
@IsOptional()
@IsArray({ message: "blocked_by must be an array" })
@IsString({ each: true, message: "blocked_by items must be strings" })
blocked_by?: string[];
@IsOptional()
@IsString({ message: "assignee must be a string" })
assignee?: string | null;
@IsOptional()
@IsString({ message: "created must be a string" })
created?: string | null;
@IsOptional()
@IsString({ message: "updated must be a string" })
updated?: string | null;
@IsOptional()
@IsString({ message: "notes must be a string" })
notes?: string | null;
@IsOptional()
@IsString({ message: "notes_nontechnical must be a string" })
notes_nontechnical?: string | null;
}

View File

@@ -0,0 +1,3 @@
export { ImportTaskDto } from "./import-task.dto";
export { ImportProjectDto } from "./import-project.dto";
export type { ImportResponseDto } from "./import-response.dto";