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,55 @@
import { Controller, Get, Post, Delete, Body, Param, UseGuards } from '@nestjs/common';
import { JwtAuthGuard, CurrentUser } from '@manacore/shared-nestjs-auth';
import type { CurrentUserData } from '@manacore/shared-nestjs-auth';
import { ShareService } from './share.service';
@Controller('api/v1/shares')
export class ShareController {
constructor(private readonly shareService: ShareService) {}
@Get()
@UseGuards(JwtAuthGuard)
async findAll(@CurrentUser() user: CurrentUserData) {
return this.shareService.findAll(user.userId);
}
@Get(':token')
async findByToken(@Param('token') token: string) {
return this.shareService.findByToken(token);
}
@Post()
@UseGuards(JwtAuthGuard)
async create(
@CurrentUser() user: CurrentUserData,
@Body()
dto: {
fileId?: string;
folderId?: string;
accessLevel?: 'view' | 'edit' | 'download';
password?: string;
maxDownloads?: number;
expiresInDays?: number;
}
) {
const expiresAt = dto.expiresInDays
? new Date(Date.now() + dto.expiresInDays * 24 * 60 * 60 * 1000)
: undefined;
return this.shareService.create(user.userId, {
fileId: dto.fileId,
folderId: dto.folderId,
accessLevel: dto.accessLevel,
password: dto.password,
maxDownloads: dto.maxDownloads,
expiresAt,
});
}
@Delete(':id')
@UseGuards(JwtAuthGuard)
async delete(@CurrentUser() user: CurrentUserData, @Param('id') id: string) {
await this.shareService.delete(user.userId, id);
return { success: true };
}
}

View file

@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { ShareController } from './share.controller';
import { ShareService } from './share.service';
@Module({
controllers: [ShareController],
providers: [ShareService],
exports: [ShareService],
})
export class ShareModule {}

View file

@ -0,0 +1,94 @@
import { Injectable, Inject, NotFoundException } from '@nestjs/common';
import { eq, and } from 'drizzle-orm';
import { randomBytes } from 'crypto';
import { DATABASE_CONNECTION } from '../db/database.module';
import { Database } from '../db/connection';
import { shares } from '../db/schema';
import type { Share, NewShare } from '../db/schema';
@Injectable()
export class ShareService {
constructor(@Inject(DATABASE_CONNECTION) private db: Database) {}
async findAll(userId: string): Promise<Share[]> {
return this.db
.select()
.from(shares)
.where(and(eq(shares.userId, userId), eq(shares.isActive, true)));
}
async findByToken(token: string): Promise<Share> {
const result = await this.db
.select()
.from(shares)
.where(and(eq(shares.shareToken, token), eq(shares.isActive, true)));
if (result.length === 0) {
throw new NotFoundException('Share not found');
}
const share = result[0];
// Check expiration
if (share.expiresAt && new Date() > share.expiresAt) {
throw new NotFoundException('Share link has expired');
}
// Check download limit
if (share.maxDownloads && share.downloadCount >= share.maxDownloads) {
throw new NotFoundException('Share link download limit reached');
}
return share;
}
async create(
userId: string,
data: {
fileId?: string;
folderId?: string;
accessLevel?: 'view' | 'edit' | 'download';
password?: string;
maxDownloads?: number;
expiresAt?: Date;
}
): Promise<Share> {
const shareToken = randomBytes(32).toString('hex');
const shareType = data.fileId ? 'file' : 'folder';
const newShare: NewShare = {
userId,
fileId: data.fileId,
folderId: data.folderId,
shareType,
shareToken,
accessLevel: data.accessLevel || 'view',
password: data.password, // Should be hashed in production
maxDownloads: data.maxDownloads,
expiresAt: data.expiresAt,
};
const result = await this.db.insert(shares).values(newShare).returning();
return result[0];
}
async delete(userId: string, id: string): Promise<void> {
await this.db
.update(shares)
.set({ isActive: false })
.where(and(eq(shares.id, id), eq(shares.userId, userId)));
}
async incrementDownloadCount(id: string): Promise<void> {
const share = await this.db.select().from(shares).where(eq(shares.id, id));
if (share.length > 0) {
await this.db
.update(shares)
.set({
downloadCount: share[0].downloadCount + 1,
lastAccessedAt: new Date(),
})
.where(eq(shares.id, id));
}
}
}