diff --git a/server/.eslintrc.js b/server/.eslintrc.js index 259de13..288aa6c 100644 --- a/server/.eslintrc.js +++ b/server/.eslintrc.js @@ -17,6 +17,7 @@ module.exports = { }, ignorePatterns: ['.eslintrc.js'], rules: { + 'no-console': 'error', '@typescript-eslint/interface-name-prefix': 'off', '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/explicit-module-boundary-types': 'off', diff --git a/server/src/app.module.ts b/server/src/app.module.ts index aea6ad3..4415e8a 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -3,9 +3,10 @@ import { UsersModule } from './users/users.module'; import { VideosModule } from './videos/videos.module'; import { PrismaService } from './prisma.service'; import { SystemService } from './system.service'; +import { LoggerModule } from './core/logging/logger.module'; @Module({ - imports: [UsersModule, VideosModule], + imports: [UsersModule, VideosModule, LoggerModule], providers: [SystemService, PrismaService], // Do I need PrismaService here? }) export class AppModule {} diff --git a/server/src/core/logging/app-logger.service.ts b/server/src/core/logging/app-logger.service.ts new file mode 100644 index 0000000..be6d360 --- /dev/null +++ b/server/src/core/logging/app-logger.service.ts @@ -0,0 +1,19 @@ +import { Injectable, Scope, ConsoleLogger } from '@nestjs/common'; + +type LogMeta = Record; + +@Injectable({ scope: Scope.TRANSIENT }) +export class AppLogger extends ConsoleLogger { + customLog(message: string, meta?: LogMeta): string { + if (!meta || Object.keys(meta).length === 0) return message; + return `${message} | meta=${this.safeStringify(meta)}`; + } + + private safeStringify(value: unknown): string { + try { + return JSON.stringify(value); + } catch { + return '[unserializable-meta]'; + } + } +} diff --git a/server/src/core/logging/logger.module.ts b/server/src/core/logging/logger.module.ts new file mode 100644 index 0000000..cef74ba --- /dev/null +++ b/server/src/core/logging/logger.module.ts @@ -0,0 +1,8 @@ +import { Module } from '@nestjs/common'; +import { AppLogger } from './app-logger.service'; + +@Module({ + providers: [AppLogger], + exports: [AppLogger], +}) +export class LoggerModule {} diff --git a/server/src/system.service.ts b/server/src/system.service.ts index eed9625..de5f348 100644 --- a/server/src/system.service.ts +++ b/server/src/system.service.ts @@ -6,14 +6,21 @@ import { import { promises as fs } from 'fs'; import { join, resolve } from 'path'; import { existsSync } from 'fs'; +import { AppLogger } from 'src/core/logging/app-logger.service'; @Injectable() export class SystemService { private readonly uploadsDir = join(process.cwd(), 'uploads'); + constructor(private readonly logger: AppLogger) { + this.logger.setContext(SystemService.name); - constructor() { this.findOrCreateUserDirectory(this.uploadsDir); - console.log('uploadsDir', this.uploadsDir); + + this.logger.debug( + this.logger.customLog('uploadsDir', { + uploadsDir: this.uploadsDir, + }), + ); } /** diff --git a/server/src/users/users.module.ts b/server/src/users/users.module.ts index 93c1e62..c8a6428 100644 --- a/server/src/users/users.module.ts +++ b/server/src/users/users.module.ts @@ -2,9 +2,11 @@ import { Module } from '@nestjs/common'; import { UsersService } from './users.service'; import { UsersController } from './users.controller'; import { PrismaService } from 'src/prisma.service'; +import { LoggerModule } from 'src/core/logging/logger.module'; @Module({ controllers: [UsersController], + imports: [LoggerModule], providers: [UsersService, PrismaService], }) export class UsersModule {} diff --git a/server/src/users/users.service.ts b/server/src/users/users.service.ts index 77ae2e7..4fb2c89 100644 --- a/server/src/users/users.service.ts +++ b/server/src/users/users.service.ts @@ -2,20 +2,61 @@ import { Injectable } from '@nestjs/common'; import { User } from 'generated/prisma'; import { PrismaService } from 'src/prisma.service'; import { CreateUserDto } from './users.dto'; +import { AppLogger } from 'src/core/logging/app-logger.service'; @Injectable() export class UsersService { - constructor(private readonly prisma: PrismaService) {} + constructor( + private readonly prisma: PrismaService, + private readonly logger: AppLogger, + ) { + this.logger.setContext(UsersService.name); + } async get(): Promise { - return this.prisma.user.findMany(); + this.logger.log( + this.logger.customLog('Listing users', { + feature: 'users', + action: 'list', + }), + ); + const users = await this.prisma.user.findMany(); + this.logger.log( + this.logger.customLog('Users listed', { + count: users.length, + }), + ); + return users; } async upsert(user: CreateUserDto): Promise { - return this.prisma.user.upsert({ - where: { email: user.email }, - update: user, - create: user, + this.logger.customLog('Upserting user', { + feature: 'users', + action: 'upsert', + email: user.email, }); + + try { + const saved = await this.prisma.user.upsert({ + where: { email: user.email }, + update: user, + create: user, + }); + this.logger.log( + this.logger.customLog('User upserted', { + id: saved.id, + email: saved.email, + }), + ); + return saved; + } catch (err) { + const message = err instanceof Error ? err.message : 'Unknown error'; + this.logger.error( + this.logger.customLog('Upsert failed', { + email: user.email, + error: message, + }), + ); + } } }