Merge branch 'dev-1' into dev

This commit is contained in:
Wuesteon 2025-12-05 17:57:26 +01:00
commit d41d060bb3
1770 changed files with 168028 additions and 31031 deletions

View file

@ -0,0 +1,20 @@
import { IsString, IsOptional, IsUUID, MaxLength } from 'class-validator';
export class CreateFolderDto {
@IsString()
@MaxLength(255)
name: string;
@IsOptional()
@IsUUID()
parentFolderId?: string;
@IsOptional()
@IsString()
@MaxLength(20)
color?: string;
@IsOptional()
@IsString()
description?: string;
}

View file

@ -0,0 +1,23 @@
import { IsString, IsOptional, IsUUID, MaxLength } from 'class-validator';
export class UpdateFolderDto {
@IsOptional()
@IsString()
@MaxLength(255)
name?: string;
@IsOptional()
@IsString()
@MaxLength(20)
color?: string;
@IsOptional()
@IsString()
description?: string;
}
export class MoveFolderDto {
@IsOptional()
@IsUUID()
parentFolderId?: string | null;
}

View file

@ -0,0 +1,69 @@
import {
Controller,
Get,
Post,
Patch,
Delete,
Body,
Param,
Query,
UseGuards,
} from '@nestjs/common';
import { JwtAuthGuard, CurrentUser } from '@manacore/shared-nestjs-auth';
import type { CurrentUserData } from '@manacore/shared-nestjs-auth';
import { FolderService } from './folder.service';
import { CreateFolderDto } from './dto/create-folder.dto';
import { UpdateFolderDto, MoveFolderDto } from './dto/update-folder.dto';
@Controller('api/v1/folders')
@UseGuards(JwtAuthGuard)
export class FolderController {
constructor(private readonly folderService: FolderService) {}
@Get()
async findAll(
@CurrentUser() user: CurrentUserData,
@Query('parentFolderId') parentFolderId?: string
) {
return this.folderService.findAll(user.userId, parentFolderId);
}
@Get(':id')
async findOne(@CurrentUser() user: CurrentUserData, @Param('id') id: string) {
return this.folderService.findOne(user.userId, id);
}
@Post()
async create(@CurrentUser() user: CurrentUserData, @Body() dto: CreateFolderDto) {
return this.folderService.create(user.userId, dto);
}
@Patch(':id')
async update(
@CurrentUser() user: CurrentUserData,
@Param('id') id: string,
@Body() dto: UpdateFolderDto
) {
return this.folderService.update(user.userId, id, dto);
}
@Patch(':id/move')
async move(
@CurrentUser() user: CurrentUserData,
@Param('id') id: string,
@Body() dto: MoveFolderDto
) {
return this.folderService.move(user.userId, id, dto);
}
@Delete(':id')
async delete(@CurrentUser() user: CurrentUserData, @Param('id') id: string) {
await this.folderService.delete(user.userId, id);
return { success: true };
}
@Post(':id/favorite')
async toggleFavorite(@CurrentUser() user: CurrentUserData, @Param('id') id: string) {
return this.folderService.toggleFavorite(user.userId, id);
}
}

View file

@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { FolderController } from './folder.controller';
import { FolderService } from './folder.service';
@Module({
controllers: [FolderController],
providers: [FolderService],
exports: [FolderService],
})
export class FolderModule {}

View file

@ -0,0 +1,147 @@
import { Injectable, Inject, NotFoundException } from '@nestjs/common';
import { eq, and, isNull } from 'drizzle-orm';
import { DATABASE_CONNECTION } from '../db/database.module';
import { Database } from '../db/connection';
import { folders } from '../db/schema';
import type { Folder, NewFolder } from '../db/schema';
import { CreateFolderDto } from './dto/create-folder.dto';
import { UpdateFolderDto, MoveFolderDto } from './dto/update-folder.dto';
@Injectable()
export class FolderService {
constructor(@Inject(DATABASE_CONNECTION) private db: Database) {}
async findAll(userId: string, parentFolderId?: string): Promise<Folder[]> {
if (parentFolderId) {
return this.db
.select()
.from(folders)
.where(
and(
eq(folders.userId, userId),
eq(folders.parentFolderId, parentFolderId),
eq(folders.isDeleted, false)
)
);
}
// Root folders (no parent)
return this.db
.select()
.from(folders)
.where(
and(
eq(folders.userId, userId),
isNull(folders.parentFolderId),
eq(folders.isDeleted, false)
)
);
}
async findOne(userId: string, id: string): Promise<Folder> {
const result = await this.db
.select()
.from(folders)
.where(and(eq(folders.id, id), eq(folders.userId, userId), eq(folders.isDeleted, false)));
if (result.length === 0) {
throw new NotFoundException('Folder not found');
}
return result[0];
}
async create(userId: string, dto: CreateFolderDto): Promise<Folder> {
let path = `/${dto.name}`;
let depth = 0;
if (dto.parentFolderId) {
const parent = await this.findOne(userId, dto.parentFolderId);
path = `${parent.path}/${dto.name}`;
depth = parent.depth + 1;
}
const newFolder: NewFolder = {
userId,
name: dto.name,
parentFolderId: dto.parentFolderId || null,
color: dto.color,
description: dto.description,
path,
depth,
};
const result = await this.db.insert(folders).values(newFolder).returning();
return result[0];
}
async update(userId: string, id: string, dto: UpdateFolderDto): Promise<Folder> {
const folder = await this.findOne(userId, id);
const result = await this.db
.update(folders)
.set({
...dto,
updatedAt: new Date(),
})
.where(and(eq(folders.id, id), eq(folders.userId, userId)))
.returning();
return result[0];
}
async move(userId: string, id: string, dto: MoveFolderDto): Promise<Folder> {
const folder = await this.findOne(userId, id);
let newPath = `/${folder.name}`;
let newDepth = 0;
if (dto.parentFolderId) {
const parent = await this.findOne(userId, dto.parentFolderId);
newPath = `${parent.path}/${folder.name}`;
newDepth = parent.depth + 1;
}
const result = await this.db
.update(folders)
.set({
parentFolderId: dto.parentFolderId || null,
path: newPath,
depth: newDepth,
updatedAt: new Date(),
})
.where(and(eq(folders.id, id), eq(folders.userId, userId)))
.returning();
return result[0];
}
async delete(userId: string, id: string): Promise<void> {
await this.findOne(userId, id);
// Soft delete
await this.db
.update(folders)
.set({
isDeleted: true,
deletedAt: new Date(),
updatedAt: new Date(),
})
.where(and(eq(folders.id, id), eq(folders.userId, userId)));
}
async toggleFavorite(userId: string, id: string): Promise<Folder> {
const folder = await this.findOne(userId, id);
const result = await this.db
.update(folders)
.set({
isFavorite: !folder.isFavorite,
updatedAt: new Date(),
})
.where(and(eq(folders.id, id), eq(folders.userId, userId)))
.returning();
return result[0];
}
}