diff --git a/backend/src/collections/image-db/image-db.service.ts b/backend/src/collections/image-db/image-db.service.ts index 27745db..bc1547f 100644 --- a/backend/src/collections/image-db/image-db.service.ts +++ b/backend/src/collections/image-db/image-db.service.ts @@ -25,8 +25,8 @@ export class ImageDBService { try { imageEntity = await this.imageRepository.save(imageEntity); - } catch (e: any) { - return Fail(e?.message); + } catch (e) { + return Fail(e); } return imageEntity; @@ -48,8 +48,8 @@ export class ImageDBService { return found as B extends undefined ? EImageBackend : Required; - } catch (e: any) { - return Fail(e?.message); + } catch (e) { + return Fail(e); } } @@ -68,8 +68,8 @@ export class ImageDBService { if (found === undefined) return Fail('Images not found'); return found; - } catch (e: any) { - return Fail(e?.message); + } catch (e) { + return Fail(e); } } @@ -77,8 +77,8 @@ export class ImageDBService { try { const result = await this.imageRepository.delete({ id }); if (result.affected === 0) return Fail('Image not found'); - } catch (e: any) { - return Fail(e?.message); + } catch (e) { + return Fail(e); } return true; } @@ -89,8 +89,8 @@ export class ImageDBService { try { await this.imageRepository.delete({}); - } catch (e: any) { - return Fail(e?.message); + } catch (e) { + return Fail(e); } return true; } diff --git a/backend/src/collections/preference-db/preference-defaults.service.ts b/backend/src/collections/preference-db/preference-defaults.service.ts index 93ead74..4b2a57f 100644 --- a/backend/src/collections/preference-db/preference-defaults.service.ts +++ b/backend/src/collections/preference-db/preference-defaults.service.ts @@ -28,7 +28,7 @@ export class PreferenceDefaultsService { if (envSecret) { return envSecret; } else { - this.logger.warn( + this.logger.log( 'Since no JWT secret was provided, a random one will be generated and saved', ); return generateRandomString(64); diff --git a/backend/src/collections/preference-db/sys-preference-db.service.ts b/backend/src/collections/preference-db/sys-preference-db.service.ts index b9743fd..6bb8be7 100644 --- a/backend/src/collections/preference-db/sys-preference-db.service.ts +++ b/backend/src/collections/preference-db/sys-preference-db.service.ts @@ -45,9 +45,8 @@ export class SysPreferenceService { await this.sysPreferenceRepository.upsert(sysPreference, { conflictPaths: ['key'], }); - } catch (e: any) { - this.logger.warn(e); - return Fail('Could not save preference'); + } catch (e) { + return Fail(e); } // Return @@ -75,16 +74,14 @@ export class SysPreferenceService { }), () => this.saveDefault(validatedKey as SysPreference), ); - } catch (e: any) { - this.logger.warn(e); - return Fail('Could not get preference'); + } catch (e) { + return Fail(e); } // Validate const result = ESysPreferenceSchema.safeParse(foundSysPreference); if (!result.success) { - this.logger.warn(result.error); - return Fail('Invalid preference'); + return Fail(result.error); } // Return @@ -157,8 +154,7 @@ export class SysPreferenceService { // It should already be valid, but these two validators might go out of sync const result = ESysPreferenceSchema.safeParse(verifySysPreference); if (!result.success) { - this.logger.warn(result.error); - return Fail('Invalid preference'); + return Fail(result.error); } return result.data; diff --git a/backend/src/collections/preference-db/usr-preference-db.service.ts b/backend/src/collections/preference-db/usr-preference-db.service.ts index 0f7d11c..f8b0f16 100644 --- a/backend/src/collections/preference-db/usr-preference-db.service.ts +++ b/backend/src/collections/preference-db/usr-preference-db.service.ts @@ -46,9 +46,8 @@ export class UsrPreferenceService { await this.usrPreferenceRepository.upsert(usrPreference, { conflictPaths: ['key', 'userId'], }); - } catch (e: any) { - this.logger.warn(e); - return Fail('Could not save preference'); + } catch (e) { + return Fail(e); } // Return @@ -80,16 +79,14 @@ export class UsrPreferenceService { }), () => this.saveDefault(userid, validatedKey as UsrPreference), ); - } catch (e: any) { - this.logger.warn(e); - return Fail('Could not get preference'); + } catch (e) { + return Fail(e); } // Validate const result = EUsrPreferenceSchema.safeParse(foundUsrPreference); if (!result.success) { - this.logger.warn(result.error); - return Fail('Invalid preference'); + return Fail(result.error); } // Return @@ -198,8 +195,7 @@ export class UsrPreferenceService { // It should already be valid, but these two validators might go out of sync const result = EUsrPreferenceSchema.safeParse(verifySysPreference); if (!result.success) { - this.logger.warn(result.error); - return Fail('Invalid preference'); + return Fail(result.error); } return result.data; diff --git a/backend/src/collections/role-db/role-db.module.ts b/backend/src/collections/role-db/role-db.module.ts index c6c903f..1daaff2 100644 --- a/backend/src/collections/role-db/role-db.module.ts +++ b/backend/src/collections/role-db/role-db.module.ts @@ -3,7 +3,11 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { HasFailed } from 'picsur-shared/dist/types'; import { EarlyConfigModule } from '../../config/early/early-config.module'; import { HostConfigService } from '../../config/early/host.config.service'; -import { ImmutableRolesList, SystemRoleDefaults, UndeletableRolesList } from '../../models/constants/roles.const'; +import { + ImmutableRolesList, + SystemRoleDefaults, + UndeletableRolesList +} from '../../models/constants/roles.const'; import { ERoleBackend } from '../../models/entities/role.entity'; import { RolesService } from './role-db.service'; @@ -42,7 +46,7 @@ export class RolesModule implements OnModuleInit { private async ensureSystemRolesExist() { // The UndeletableRolesList is also the list of systemroles for (const systemRole of UndeletableRolesList) { - this.logger.debug(`Ensuring system role "${systemRole}" exists`); + this.logger.verbose(`Ensuring system role "${systemRole}" exists`); const exists = await this.rolesService.exists(systemRole); if (exists) continue; @@ -63,9 +67,9 @@ export class RolesModule implements OnModuleInit { private async updateImmutableRoles() { // Immutable roles can not be updated via the gui // They therefore do have to be kept up to date from the backend - + for (const immutableRole of ImmutableRolesList) { - this.logger.debug( + this.logger.verbose( `Updating permissions for immutable role "${immutableRole}"`, ); diff --git a/backend/src/collections/role-db/role-db.service.ts b/backend/src/collections/role-db/role-db.service.ts index 90593bb..b0d82d8 100644 --- a/backend/src/collections/role-db/role-db.service.ts +++ b/backend/src/collections/role-db/role-db.service.ts @@ -37,8 +37,8 @@ export class RolesService { try { return await this.rolesRepository.save(role, { reload: true }); - } catch (e: any) { - return Fail(e?.message); + } catch (e) { + return Fail(e); } } @@ -52,8 +52,8 @@ export class RolesService { try { return await this.rolesRepository.remove(roleToModify); - } catch (e: any) { - return Fail(e?.message); + } catch (e) { + return Fail(e); } } @@ -118,8 +118,8 @@ export class RolesService { try { return await this.rolesRepository.save(roleToModify); - } catch (e: any) { - return Fail(e?.message); + } catch (e) { + return Fail(e); } } @@ -131,8 +131,8 @@ export class RolesService { if (!found) return Fail('Role not found'); return found; - } catch (e: any) { - return Fail(e?.message); + } catch (e) { + return Fail(e); } } @@ -141,8 +141,8 @@ export class RolesService { const found = await this.rolesRepository.find(); if (!found) return Fail('No roles found'); return found; - } catch (e: any) { - return Fail(e?.message); + } catch (e) { + return Fail(e); } } @@ -158,8 +158,8 @@ export class RolesService { await this.rolesRepository.delete({ name: In(UndeletableRolesList), }); - } catch (e: any) { - return Fail(e?.message); + } catch (e) { + return Fail(e); } return true; } @@ -172,8 +172,7 @@ export class RolesService { } else { const result = ERoleSchema.safeParse(role); if (!result.success) { - this.logger.warn(result.error); - return Fail('Invalid role'); + return Fail(result.error); } // This is safe return result.data as ERoleBackend; diff --git a/backend/src/collections/user-db/user-db.module.ts b/backend/src/collections/user-db/user-db.module.ts index 715d980..043e09f 100644 --- a/backend/src/collections/user-db/user-db.module.ts +++ b/backend/src/collections/user-db/user-db.module.ts @@ -48,7 +48,7 @@ export class UsersModule implements OnModuleInit { password: string, roles: string[], ) { - this.logger.debug(`Ensuring user "${username}" exists`); + this.logger.verbose(`Ensuring user "${username}" exists`); const exists = await this.usersService.exists(username); if (exists) return; diff --git a/backend/src/collections/user-db/user-db.service.ts b/backend/src/collections/user-db/user-db.service.ts index 787bc45..8c97124 100644 --- a/backend/src/collections/user-db/user-db.service.ts +++ b/backend/src/collections/user-db/user-db.service.ts @@ -64,8 +64,8 @@ export class UsersService { try { return await this.usersRepository.save(user); - } catch (e: any) { - return Fail(e?.message); + } catch (e) { + return Fail(e); } } @@ -79,8 +79,8 @@ export class UsersService { try { return await this.usersRepository.remove(userToModify); - } catch (e: any) { - return Fail(e?.message); + } catch (e) { + return Fail(e); } } @@ -95,7 +95,7 @@ export class UsersService { if (ImmutableUsersList.includes(userToModify.username)) { // Just fail silently - this.logger.log("Can't modify system user"); + this.logger.verbose("User tried to modify system user, failed silently"); return userToModify; } @@ -108,8 +108,8 @@ export class UsersService { try { return await this.usersRepository.save(userToModify); - } catch (e: any) { - return Fail(e?.message); + } catch (e) { + return Fail(e); } } @@ -124,8 +124,7 @@ export class UsersService { .where('roles @> ARRAY[:role]', { role }) .execute(); } catch (e) { - this.logger.error(e); - return Fail("Couldn't remove role from everyone"); + return Fail(e); } return true; @@ -150,8 +149,8 @@ export class UsersService { try { userToModify = await this.usersRepository.save(userToModify); - } catch (e: any) { - return Fail(e?.message); + } catch (e) { + return Fail(e); } return userToModify; @@ -193,8 +192,8 @@ export class UsersService { if (!found) return Fail('User not found'); return found; - } catch (e: any) { - return Fail(e?.message); + } catch (e) { + return Fail(e); } } @@ -206,8 +205,8 @@ export class UsersService { if (!found) return Fail('User not found'); return found as EUserBackend; - } catch (e: any) { - return Fail(e?.message); + } catch (e) { + return Fail(e); } } @@ -223,8 +222,8 @@ export class UsersService { take: count, skip: count * page, }); - } catch (e: any) { - return Fail(e?.message); + } catch (e) { + return Fail(e); } } diff --git a/backend/src/collections/userdb/user-db.service.ts b/backend/src/collections/userdb/user-db.service.ts deleted file mode 100644 index 787bc45..0000000 --- a/backend/src/collections/userdb/user-db.service.ts +++ /dev/null @@ -1,254 +0,0 @@ -import { Injectable, Logger } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import * as bcrypt from 'bcrypt'; -import { SysPreference } from 'picsur-shared/dist/dto/sys-preferences.dto'; -import { - AsyncFailable, - Fail, - HasFailed, - HasSuccess -} from 'picsur-shared/dist/types'; -import { makeUnique } from 'picsur-shared/dist/util/unique'; -import { Repository } from 'typeorm'; -import { Permissions } from '../../models/constants/permissions.const'; -import { - DefaultRolesList, - SoulBoundRolesList -} from '../../models/constants/roles.const'; -import { - ImmutableUsersList, - LockedLoginUsersList, - UndeletableUsersList -} from '../../models/constants/special-users.const'; -import { EUserBackend } from '../../models/entities/user.entity'; -import { GetCols } from '../../models/util/collection'; -import { SysPreferenceService } from '../preference-db/sys-preference-db.service'; -import { RolesService } from '../role-db/role-db.service'; - -@Injectable() -export class UsersService { - private readonly logger = new Logger('UsersService'); - - constructor( - @InjectRepository(EUserBackend) - private usersRepository: Repository, - private rolesService: RolesService, - private prefService: SysPreferenceService, - ) {} - - // Creation and deletion - - public async create( - username: string, - password: string, - roles?: string[], - // Add option to create "invalid" users, should only be used by system - byPassRoleCheck?: boolean, - ): AsyncFailable { - if (await this.exists(username)) return Fail('User already exists'); - - const strength = await this.getBCryptStrength(); - const hashedPassword = await bcrypt.hash(password, strength); - - let user = new EUserBackend(); - user.username = username; - user.hashedPassword = hashedPassword; - if (byPassRoleCheck) { - const rolesToAdd = roles ?? []; - user.roles = makeUnique(rolesToAdd); - } else { - // Strip soulbound roles and add default roles - const rolesToAdd = this.filterAddedRoles(roles ?? []); - user.roles = makeUnique([...DefaultRolesList, ...rolesToAdd]); - } - - try { - return await this.usersRepository.save(user); - } catch (e: any) { - return Fail(e?.message); - } - } - - public async delete(uuid: string): AsyncFailable { - const userToModify = await this.findOne(uuid); - if (HasFailed(userToModify)) return userToModify; - - if (UndeletableUsersList.includes(userToModify.username)) { - return Fail('Cannot delete system user'); - } - - try { - return await this.usersRepository.remove(userToModify); - } catch (e: any) { - return Fail(e?.message); - } - } - - // Updating - - public async setRoles( - uuid: string, - roles: string[], - ): AsyncFailable { - const userToModify = await this.findOne(uuid); - if (HasFailed(userToModify)) return userToModify; - - if (ImmutableUsersList.includes(userToModify.username)) { - // Just fail silently - this.logger.log("Can't modify system user"); - return userToModify; - } - - const rolesToKeep = userToModify.roles.filter((role) => - SoulBoundRolesList.includes(role), - ); - const rolesToAdd = this.filterAddedRoles(roles); - const newRoles = makeUnique([...rolesToKeep, ...rolesToAdd]); - userToModify.roles = newRoles; - - try { - return await this.usersRepository.save(userToModify); - } catch (e: any) { - return Fail(e?.message); - } - } - - public async removeRoleEveryone(role: string): AsyncFailable { - try { - await this.usersRepository - .createQueryBuilder('user') - .update() - .set({ - roles: () => 'ARRAY_REMOVE(roles, :role)', - }) - .where('roles @> ARRAY[:role]', { role }) - .execute(); - } catch (e) { - this.logger.error(e); - return Fail("Couldn't remove role from everyone"); - } - - return true; - } - - public async getPermissions(uuid: string): AsyncFailable { - const userToModify = await this.findOne(uuid); - if (HasFailed(userToModify)) return userToModify; - - return await this.rolesService.getPermissions(userToModify.roles); - } - - public async updatePassword( - uuid: string, - password: string, - ): AsyncFailable { - let userToModify = await this.findOne(uuid); - if (HasFailed(userToModify)) return userToModify; - - const strength = await this.getBCryptStrength(); - userToModify.hashedPassword = await bcrypt.hash(password, strength); - - try { - userToModify = await this.usersRepository.save(userToModify); - } catch (e: any) { - return Fail(e?.message); - } - - return userToModify; - } - - // Authentication - - async authenticate( - username: string, - password: string, - ): AsyncFailable { - const user = await this.findByUsername(username, true); - if (HasFailed(user)) return user; - - if (LockedLoginUsersList.includes(user.username)) { - // Error should be kept in backend - return Fail('Wrong username'); - } - - if (!(await bcrypt.compare(password, user.hashedPassword ?? ''))) - return Fail('Wrong password'); - - return await this.findOne(user.id ?? ''); - } - - // Listing - - public async findByUsername( - username: string, - // Also fetch fields that aren't normally sent to the client - // (e.g. hashed password) - getPrivate: boolean = false, - ): AsyncFailable { - try { - const found = await this.usersRepository.findOne({ - where: { username }, - select: getPrivate ? GetCols(this.usersRepository) : undefined, - }); - - if (!found) return Fail('User not found'); - return found; - } catch (e: any) { - return Fail(e?.message); - } - } - - public async findOne(uuid: string): AsyncFailable { - try { - const found = await this.usersRepository.findOne({ - where: { id: uuid }, - }); - - if (!found) return Fail('User not found'); - return found as EUserBackend; - } catch (e: any) { - return Fail(e?.message); - } - } - - public async findMany( - count: number, - page: number, - ): AsyncFailable { - if (count < 1 || page < 0) return Fail('Invalid page'); - if (count > 100) return Fail('Too many results'); - - try { - return await this.usersRepository.find({ - take: count, - skip: count * page, - }); - } catch (e: any) { - return Fail(e?.message); - } - } - - public async exists(username: string): Promise { - return HasSuccess(await this.findByUsername(username)); - } - - // Internal - - private filterAddedRoles(roles: string[]): string[] { - const filteredRoles = roles.filter( - (role) => !SoulBoundRolesList.includes(role), - ); - - return filteredRoles; - } - - private async getBCryptStrength(): Promise { - const result = await this.prefService.getNumberPreference( - SysPreference.BCryptStrength, - ); - if (HasFailed(result)) { - return 12; - } - return result; - } -} diff --git a/backend/src/config/early/host.config.service.ts b/backend/src/config/early/host.config.service.ts index cb5e553..b0441ee 100644 --- a/backend/src/config/early/host.config.service.ts +++ b/backend/src/config/early/host.config.service.ts @@ -7,10 +7,10 @@ export class HostConfigService { private readonly logger = new Logger('HostConfigService'); constructor(private configService: ConfigService) { - this.logger.debug('Host: ' + this.getHost()); - this.logger.debug('Port: ' + this.getPort()); - this.logger.debug('Demo: ' + this.isDemo()); - this.logger.debug('Demo Interval: ' + this.getDemoInterval() / 1000 + 's'); + this.logger.log('Host: ' + this.getHost()); + this.logger.log('Port: ' + this.getPort()); + this.logger.log('Demo: ' + this.isDemo()); + this.logger.log('Demo Interval: ' + this.getDemoInterval() / 1000 + 's'); } public getHost(): string { diff --git a/backend/src/config/early/multipart.config.service.ts b/backend/src/config/early/multipart.config.service.ts index b6da858..4697f22 100644 --- a/backend/src/config/early/multipart.config.service.ts +++ b/backend/src/config/early/multipart.config.service.ts @@ -7,7 +7,7 @@ export class MultipartConfigService { private readonly logger = new Logger('MultipartConfigService'); constructor(private configService: ConfigService) { - this.logger.debug('Max file size: ' + this.getMaxFileSize()); + this.logger.log('Max file size: ' + this.getMaxFileSize()); } public getMaxFileSize(): number { diff --git a/backend/src/config/early/serve-static.config.service.ts b/backend/src/config/early/serve-static.config.service.ts index 9574984..9ab0dfd 100644 --- a/backend/src/config/early/serve-static.config.service.ts +++ b/backend/src/config/early/serve-static.config.service.ts @@ -16,7 +16,7 @@ export class ServeStaticConfigService private defaultLocation = join(PackageRoot, '../frontend/dist'); constructor(private configService: ConfigService) { - this.logger.debug('Static directory: ' + this.getStaticDirectory()); + this.logger.log('Static directory: ' + this.getStaticDirectory()); } public getStaticDirectory(): string { diff --git a/backend/src/config/early/type-orm.config.service.ts b/backend/src/config/early/type-orm.config.service.ts index e0d1674..b4c7a46 100644 --- a/backend/src/config/early/type-orm.config.service.ts +++ b/backend/src/config/early/type-orm.config.service.ts @@ -15,11 +15,12 @@ export class TypeOrmConfigService implements TypeOrmOptionsFactory { ) { const varOptions = this.getTypeOrmServerOptions(); - this.logger.debug('DB host: ' + varOptions.host); - this.logger.debug('DB port: ' + varOptions.port); - this.logger.debug('DB username: ' + varOptions.username); - this.logger.debug('DB password: ' + varOptions.password); - this.logger.debug('DB database: ' + varOptions.database); + this.logger.log('DB host: ' + varOptions.host); + this.logger.log('DB port: ' + varOptions.port); + this.logger.log('DB database: ' + varOptions.database); + + this.logger.verbose('DB username: ' + varOptions.username); + this.logger.verbose('DB password: ' + varOptions.password); } public getTypeOrmServerOptions() { diff --git a/backend/src/config/late/jwt.config.service.ts b/backend/src/config/late/jwt.config.service.ts index 390894a..12e6fb8 100644 --- a/backend/src/config/late/jwt.config.service.ts +++ b/backend/src/config/late/jwt.config.service.ts @@ -14,8 +14,8 @@ export class JwtConfigService implements JwtOptionsFactory { private async printDebug() { const secret = await this.getJwtSecret(); const expiresIn = await this.getJwtExpiresIn(); - this.logger.debug('JWT secret: ' + secret); - this.logger.debug('JWT expiresIn: ' + expiresIn); + this.logger.verbose('JWT secret: ' + secret); + this.logger.verbose('JWT expiresIn: ' + expiresIn); } public async getJwtSecret(): Promise { diff --git a/backend/src/decorators/multipart/multipart.pipe.ts b/backend/src/decorators/multipart/multipart.pipe.ts index 8317c8f..283ae0d 100644 --- a/backend/src/decorators/multipart/multipart.pipe.ts +++ b/backend/src/decorators/multipart/multipart.pipe.ts @@ -28,7 +28,7 @@ export class MultiPartPipe implements PipeTransform { ) { let zodSchema = (metadata?.metatype as ZodDtoStatic)?.zodSchema; if (!zodSchema) { - this.logger.warn('Invalid scheme on multipart body'); + this.logger.error('Invalid scheme on multipart body'); throw new InternalServerErrorException('Invalid scheme on backend'); } diff --git a/backend/src/managers/auth/guards/main.guard.ts b/backend/src/managers/auth/guards/main.guard.ts index da022c3..50f019f 100644 --- a/backend/src/managers/auth/guards/main.guard.ts +++ b/backend/src/managers/auth/guards/main.guard.ts @@ -47,14 +47,14 @@ export class MainAuthGuard extends AuthGuard(['jwt', 'guest']) { // These are the permissions required to access the route const permissions = this.extractPermissions(context); if (HasFailed(permissions)) { - this.logger.warn('Route Permissions: ' + permissions.getReason()); + this.logger.error('Fetching route permission failed: ' + permissions.getReason()); throw new InternalServerErrorException(); } // These are the permissions the user has const userPermissions = await this.usersService.getPermissions(user.id); if (HasFailed(userPermissions)) { - this.logger.warn('User Permissions: ' + userPermissions.getReason()); + this.logger.warn('Fetching user permissions failed: ' + userPermissions.getReason()); throw new InternalServerErrorException(); } diff --git a/backend/src/managers/demo/demo.module.ts b/backend/src/managers/demo/demo.module.ts index b07de27..18ac3cd 100644 --- a/backend/src/managers/demo/demo.module.ts +++ b/backend/src/managers/demo/demo.module.ts @@ -20,7 +20,7 @@ export class DemoManagerModule implements OnModuleInit, OnModuleDestroy { async onModuleInit() { if (this.hostConfigService.isDemo()) { - this.logger.log('Demo mode enabled'); + this.logger.warn('Demo mode enabled, images are ephimeral'); await this.setupDemoMode(); } } diff --git a/backend/src/managers/demo/demo.service.ts b/backend/src/managers/demo/demo.service.ts index c25770a..dd7d588 100644 --- a/backend/src/managers/demo/demo.service.ts +++ b/backend/src/managers/demo/demo.service.ts @@ -14,7 +14,7 @@ export class DemoManagerService { public async setupRoles() { this.logger.warn( - 'Modifying roles for demo mode, this will not be reverted automatically', + 'Modifying roles for demo mode, this will have to be reverted manually', ); // Could be done manually, but this makes settup up a demo instance quicker this.rolesService.addPermissions('guest', [Permission.ImageUpload]); @@ -25,7 +25,7 @@ export class DemoManagerService { } private async executeAsync() { - this.logger.debug('Executing demo cleanup'); + this.logger.verbose('Executing demo cleanup'); await this.imagesService.deleteAll(true); } } diff --git a/backend/src/routes/api/roles/roles.controller.ts b/backend/src/routes/api/roles/roles.controller.ts index 99582a8..794ea34 100644 --- a/backend/src/routes/api/roles/roles.controller.ts +++ b/backend/src/routes/api/roles/roles.controller.ts @@ -123,6 +123,7 @@ export class RolesController { const success = await this.usersService.removeRoleEveryone(role.name); if (HasFailed(success)) { + this.logger.warn(success.getReason()); throw new InternalServerErrorException( 'Could not remove role from users', ); diff --git a/frontend/src/app/components/picsur-img/picsur-img.component.ts b/frontend/src/app/components/picsur-img/picsur-img.component.ts index bc2f78f..9879da6 100644 --- a/frontend/src/app/components/picsur-img/picsur-img.component.ts +++ b/frontend/src/app/components/picsur-img/picsur-img.component.ts @@ -40,7 +40,7 @@ export class PicsurImgComponent implements OnChanges { ngOnChanges(changes: SimpleChanges): void { let url = this.imageURL ?? ''; if (URLRegex.test(url)) { - this.update(url).catch((err) => this.logger.error(err)); + this.update(url).catch(this.logger.error); } else { this.state = PicsurImgState.Loading; } diff --git a/frontend/src/app/routes/user/login/login.component.ts b/frontend/src/app/routes/user/login/login.component.ts index 0c82460..c4794c1 100644 --- a/frontend/src/app/routes/user/login/login.component.ts +++ b/frontend/src/app/routes/user/login/login.component.ts @@ -54,7 +54,7 @@ export class LoginComponent implements OnInit { const user = await this.userService.login(data.username, data.password); if (HasFailed(user)) { - this.logger.warn(user.getReason()); + this.logger.error(user.getReason()); this.utilService.showSnackBar( 'Login failed, please try again', SnackBarType.Error diff --git a/frontend/src/app/routes/user/register/register.component.ts b/frontend/src/app/routes/user/register/register.component.ts index 533f139..faea3c6 100644 --- a/frontend/src/app/routes/user/register/register.component.ts +++ b/frontend/src/app/routes/user/register/register.component.ts @@ -54,7 +54,7 @@ export class RegisterComponent implements OnInit { const user = await this.userService.register(data.username, data.password); if (HasFailed(user)) { - this.logger.warn(user.getReason()); + this.logger.error(user.getReason()); this.utilService.showSnackBar( 'Register failed, please try again', SnackBarType.Error @@ -65,7 +65,7 @@ export class RegisterComponent implements OnInit { if (!this.userService.isLoggedIn) { const loginResult = await this.userService.login(data.username, data.password); if (HasFailed(loginResult)) { - this.logger.warn(loginResult.getReason()); + this.logger.error(loginResult.getReason()); this.utilService.showSnackBar( 'Failed to login after register', SnackBarType.Error diff --git a/frontend/src/app/services/api/api.service.ts b/frontend/src/app/services/api/api.service.ts index 48355bd..f977d70 100644 --- a/frontend/src/app/services/api/api.service.ts +++ b/frontend/src/app/services/api/api.service.ts @@ -44,7 +44,7 @@ export class ApiService { const validateResult = sendSchema.safeParse(data); if (!validateResult.success) { - this.logger.warn(validateResult.error); + this.logger.error(validateResult.error); return Fail('Something went wrong'); } @@ -78,7 +78,7 @@ export class ApiService { const validateResult = resultSchema.safeParse(result); if (!validateResult.success) { - this.logger.warn('result', validateResult.error); + this.logger.error(validateResult.error); return Fail('Something went wrong'); } @@ -98,7 +98,7 @@ export class ApiService { try { return await response.json(); } catch (e) { - this.logger.warn(e); + this.logger.error(e); return Fail('Something went wrong'); } } @@ -113,7 +113,7 @@ export class ApiService { try { return await response.arrayBuffer(); } catch (e) { - this.logger.warn(e); + this.logger.error(e); return Fail('Something went wrong'); } } @@ -133,9 +133,9 @@ export class ApiService { options.headers = headers; return await window.fetch(url, options); - } catch (error: any) { + } catch (e) { this.errorSubject.next({ - error, + error: e, url, }); return Fail('Network Error'); diff --git a/frontend/src/app/services/api/permission.service.ts b/frontend/src/app/services/api/permission.service.ts index 2ded198..5849127 100644 --- a/frontend/src/app/services/api/permission.service.ts +++ b/frontend/src/app/services/api/permission.service.ts @@ -59,7 +59,7 @@ export class PermissionService { return this.userService.live.pipe(Throttle(300)).subscribe(async (user) => { const permissions = await this.updatePermissions(); if (HasFailed(permissions)) { - this.logger.warn(permissions.getReason()); + this.logger.error(permissions.getReason()); return; } }); diff --git a/frontend/src/app/services/api/user.service.ts b/frontend/src/app/services/api/user.service.ts index 708ad74..095e1fb 100644 --- a/frontend/src/app/services/api/user.service.ts +++ b/frontend/src/app/services/api/user.service.ts @@ -44,7 +44,7 @@ export class UserService { const user = await this.extractUser(apikey); if (HasFailed(user)) { - this.logger.warn(user.getReason()); + this.logger.error(user.getReason()); await this.logout(); return; } @@ -53,7 +53,7 @@ export class UserService { const fetchedUser = await this.fetchUser(); if (HasFailed(fetchedUser)) { - this.logger.warn(fetchedUser.getReason()); + this.logger.error(fetchedUser.getReason()); await this.logout(); return; } @@ -122,7 +122,7 @@ export class UserService { const result = JwtDataSchema.safeParse(decoded); if (!result.success) { - this.logger.warn(result.error); + this.logger.error(result.error); return Fail('Invalid token data'); } diff --git a/frontend/src/app/services/logger/global-logger.service.ts b/frontend/src/app/services/logger/global-logger.service.ts index 30de825..d25f5fe 100644 --- a/frontend/src/app/services/logger/global-logger.service.ts +++ b/frontend/src/app/services/logger/global-logger.service.ts @@ -66,8 +66,18 @@ export class GlobalLogger { return; } + const processedArgs = args.map(a => { + if (typeof a === 'string') { + return a; + } else if (a instanceof Error) { + return a.message; + } else { + return a.toString(); + } + }) + const styles = LoggerStyles[level]; - const message = ['%c' + context.source, styles, ...args]; + const message = ['%c' + context.source, styles, ...processedArgs]; console[LoggerFunctions[level]](...message); } } diff --git a/frontend/src/app/util/util-module/api-error.service.ts b/frontend/src/app/util/util-module/api-error.service.ts index 50af63c..98c519f 100644 --- a/frontend/src/app/util/util-module/api-error.service.ts +++ b/frontend/src/app/util/util-module/api-error.service.ts @@ -29,7 +29,7 @@ export class ApiErrorService { this.utilService.showSnackBar('Network Error', SnackBarType.Error); } - this.logger.warn(error.error); + this.logger.error(error.error); }); } } diff --git a/shared/src/types/failable.ts b/shared/src/types/failable.ts index 22785e5..78fd335 100644 --- a/shared/src/types/failable.ts +++ b/shared/src/types/failable.ts @@ -4,15 +4,27 @@ // -> Side effects go brrr export class Failure { - constructor(private readonly reason?: string) {} + constructor(private readonly reason?: string, private readonly stack?: string) {} getReason(): string { return this.reason ?? 'Unknown'; } + + getStack(): string { + return this.stack ?? 'None'; + } } -export function Fail(reason?: string): Failure { - return new Failure(reason); +export function Fail(reason?: any): Failure { + if (typeof reason === 'string') { + return new Failure(reason); + } else if(reason instanceof Error) { + return new Failure(reason.message, reason.stack); + } else if(reason instanceof Failure) { + return reason; + } else { + return new Failure('Converted(' + reason + ')'); + } } export type Failable = T | Failure; diff --git a/tsconfig.base.json b/tsconfig.base.json index 03e55bf..638cb76 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -16,6 +16,7 @@ "strictNullChecks": true, "noImplicitThis": true, "noImplicitReturns": true, + "useUnknownInCatchVariables": true, "skipLibCheck": true, "removeComments": true,