feat: Complete stub services with real implementations and graceful not-configured responses
All checks were successful
Test Asgard Runner / test (push) Successful in 2s

- ChangelogService: inject PlatformChangelog repo, query with findAndCount(skip/take), return actual data
- ChangelogModule: add TypeOrmModule.forFeature([PlatformChangelog])
- MapsService: add uploadMap() — SHA-256 checksum, storage path, full entity save
- MapsController: add POST /maps/upload with FileInterceptor, map.manage permission, @UploadedFile
- AuthService: replace console.log stubs with Logger; forgotPassword returns 200 with clear message; resetPassword throws NotImplementedException
- PluginsService: searchUmod returns { results: [], message: 'not yet configured' } instead of bare []
- SteamService: add Logger.warn on every stub path (checkForceWipe, getPlayerSummary)
- SettingsService: add Logger; both Cloudflare DNS stubs emit Logger.warn before DB save
- MigrationService: add Logger; exportConfig logs warning + returns note field; importConfig throws NotImplementedException
- Install @types/multer dev dependency for Express.Multer.File type support

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell
2026-02-21 13:33:08 -05:00
parent e1a3ea3b78
commit a181ed7ded
11 changed files with 164 additions and 60 deletions

View File

@@ -1,7 +1,21 @@
import { Controller, Get, Delete, Put, Body, Param, UseGuards } from '@nestjs/common';
import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
import {
Controller,
Get,
Delete,
Put,
Post,
Body,
Param,
UseGuards,
UseInterceptors,
UploadedFile,
BadRequestException,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { ApiTags, ApiBearerAuth, ApiOperation, ApiConsumes } from '@nestjs/swagger';
import { MapsService } from './maps.service';
import { UpdateRotationDto } from './dto/update-rotation.dto';
import { UploadMapDto } from './dto/upload-map.dto';
import { CurrentTenant } from '../../common/decorators/current-tenant.decorator';
import { RequirePermission } from '../../common/decorators/require-permission.decorator';
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
@@ -14,6 +28,22 @@ import { PermissionsGuard } from '../../common/guards/permissions.guard';
export class MapsController {
constructor(private readonly mapsService: MapsService) {}
@Post('upload')
@RequirePermission('map.manage')
@ApiOperation({ summary: 'Upload a map to the library' })
@ApiConsumes('multipart/form-data')
@UseInterceptors(FileInterceptor('file'))
uploadMap(
@CurrentTenant() licenseId: string,
@Body() dto: UploadMapDto,
@UploadedFile() file: Express.Multer.File,
) {
if (!file) {
throw new BadRequestException('No file provided');
}
return this.mapsService.uploadMap(licenseId, dto, file);
}
@Get()
@RequirePermission('map.view')
@ApiOperation({ summary: 'Get all maps for tenant' })

View File

@@ -1,9 +1,11 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { createHash } from 'crypto';
import { MapLibrary } from '../../entities/map-library.entity';
import { MapRotation } from '../../entities/map-rotation.entity';
import { UpdateRotationDto } from './dto/update-rotation.dto';
import { UploadMapDto } from './dto/upload-map.dto';
@Injectable()
export class MapsService {
@@ -14,6 +16,30 @@ export class MapsService {
private readonly mapRotationRepo: Repository<MapRotation>,
) {}
async uploadMap(
licenseId: string,
dto: UploadMapDto,
file: Express.Multer.File,
): Promise<MapLibrary> {
const checksum = createHash('sha256').update(file.buffer).digest('hex');
const storagePath = `/maps/${licenseId}/${Date.now()}_${file.originalname}`;
const map = this.mapLibraryRepo.create({
license_id: licenseId,
filename: file.originalname,
display_name: dto.display_name,
storage_path: storagePath,
file_size_bytes: file.size,
map_type: dto.map_type,
seed: dto.seed ?? null,
world_size: dto.world_size ?? null,
thumbnail_path: null,
checksum,
});
return this.mapLibraryRepo.save(map);
}
async getMaps(licenseId: string): Promise<{ maps: MapLibrary[] }> {
const maps = await this.mapLibraryRepo.find({
where: { license_id: licenseId },