reconsider logging levels

This commit is contained in:
rubikscraft 2022-04-18 16:43:27 +02:00
parent 5ca8516560
commit 4612a9ea6f
No known key found for this signature in database
GPG key ID: 1463EBE9200A5CD4
29 changed files with 124 additions and 359 deletions

View file

@ -25,8 +25,8 @@ export class ImageDBService {
try { try {
imageEntity = await this.imageRepository.save(imageEntity); imageEntity = await this.imageRepository.save(imageEntity);
} catch (e: any) { } catch (e) {
return Fail(e?.message); return Fail(e);
} }
return imageEntity; return imageEntity;
@ -48,8 +48,8 @@ export class ImageDBService {
return found as B extends undefined return found as B extends undefined
? EImageBackend ? EImageBackend
: Required<EImageBackend>; : Required<EImageBackend>;
} catch (e: any) { } catch (e) {
return Fail(e?.message); return Fail(e);
} }
} }
@ -68,8 +68,8 @@ export class ImageDBService {
if (found === undefined) return Fail('Images not found'); if (found === undefined) return Fail('Images not found');
return found; return found;
} catch (e: any) { } catch (e) {
return Fail(e?.message); return Fail(e);
} }
} }
@ -77,8 +77,8 @@ export class ImageDBService {
try { try {
const result = await this.imageRepository.delete({ id }); const result = await this.imageRepository.delete({ id });
if (result.affected === 0) return Fail('Image not found'); if (result.affected === 0) return Fail('Image not found');
} catch (e: any) { } catch (e) {
return Fail(e?.message); return Fail(e);
} }
return true; return true;
} }
@ -89,8 +89,8 @@ export class ImageDBService {
try { try {
await this.imageRepository.delete({}); await this.imageRepository.delete({});
} catch (e: any) { } catch (e) {
return Fail(e?.message); return Fail(e);
} }
return true; return true;
} }

View file

@ -28,7 +28,7 @@ export class PreferenceDefaultsService {
if (envSecret) { if (envSecret) {
return envSecret; return envSecret;
} else { } else {
this.logger.warn( this.logger.log(
'Since no JWT secret was provided, a random one will be generated and saved', 'Since no JWT secret was provided, a random one will be generated and saved',
); );
return generateRandomString(64); return generateRandomString(64);

View file

@ -45,9 +45,8 @@ export class SysPreferenceService {
await this.sysPreferenceRepository.upsert(sysPreference, { await this.sysPreferenceRepository.upsert(sysPreference, {
conflictPaths: ['key'], conflictPaths: ['key'],
}); });
} catch (e: any) { } catch (e) {
this.logger.warn(e); return Fail(e);
return Fail('Could not save preference');
} }
// Return // Return
@ -75,16 +74,14 @@ export class SysPreferenceService {
}), }),
() => this.saveDefault(validatedKey as SysPreference), () => this.saveDefault(validatedKey as SysPreference),
); );
} catch (e: any) { } catch (e) {
this.logger.warn(e); return Fail(e);
return Fail('Could not get preference');
} }
// Validate // Validate
const result = ESysPreferenceSchema.safeParse(foundSysPreference); const result = ESysPreferenceSchema.safeParse(foundSysPreference);
if (!result.success) { if (!result.success) {
this.logger.warn(result.error); return Fail(result.error);
return Fail('Invalid preference');
} }
// Return // Return
@ -157,8 +154,7 @@ export class SysPreferenceService {
// It should already be valid, but these two validators might go out of sync // It should already be valid, but these two validators might go out of sync
const result = ESysPreferenceSchema.safeParse(verifySysPreference); const result = ESysPreferenceSchema.safeParse(verifySysPreference);
if (!result.success) { if (!result.success) {
this.logger.warn(result.error); return Fail(result.error);
return Fail('Invalid preference');
} }
return result.data; return result.data;

View file

@ -46,9 +46,8 @@ export class UsrPreferenceService {
await this.usrPreferenceRepository.upsert(usrPreference, { await this.usrPreferenceRepository.upsert(usrPreference, {
conflictPaths: ['key', 'userId'], conflictPaths: ['key', 'userId'],
}); });
} catch (e: any) { } catch (e) {
this.logger.warn(e); return Fail(e);
return Fail('Could not save preference');
} }
// Return // Return
@ -80,16 +79,14 @@ export class UsrPreferenceService {
}), }),
() => this.saveDefault(userid, validatedKey as UsrPreference), () => this.saveDefault(userid, validatedKey as UsrPreference),
); );
} catch (e: any) { } catch (e) {
this.logger.warn(e); return Fail(e);
return Fail('Could not get preference');
} }
// Validate // Validate
const result = EUsrPreferenceSchema.safeParse(foundUsrPreference); const result = EUsrPreferenceSchema.safeParse(foundUsrPreference);
if (!result.success) { if (!result.success) {
this.logger.warn(result.error); return Fail(result.error);
return Fail('Invalid preference');
} }
// Return // Return
@ -198,8 +195,7 @@ export class UsrPreferenceService {
// It should already be valid, but these two validators might go out of sync // It should already be valid, but these two validators might go out of sync
const result = EUsrPreferenceSchema.safeParse(verifySysPreference); const result = EUsrPreferenceSchema.safeParse(verifySysPreference);
if (!result.success) { if (!result.success) {
this.logger.warn(result.error); return Fail(result.error);
return Fail('Invalid preference');
} }
return result.data; return result.data;

View file

@ -3,7 +3,11 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { HasFailed } from 'picsur-shared/dist/types'; import { HasFailed } from 'picsur-shared/dist/types';
import { EarlyConfigModule } from '../../config/early/early-config.module'; import { EarlyConfigModule } from '../../config/early/early-config.module';
import { HostConfigService } from '../../config/early/host.config.service'; 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 { ERoleBackend } from '../../models/entities/role.entity';
import { RolesService } from './role-db.service'; import { RolesService } from './role-db.service';
@ -42,7 +46,7 @@ export class RolesModule implements OnModuleInit {
private async ensureSystemRolesExist() { private async ensureSystemRolesExist() {
// The UndeletableRolesList is also the list of systemroles // The UndeletableRolesList is also the list of systemroles
for (const systemRole of UndeletableRolesList) { 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); const exists = await this.rolesService.exists(systemRole);
if (exists) continue; if (exists) continue;
@ -65,7 +69,7 @@ export class RolesModule implements OnModuleInit {
// They therefore do have to be kept up to date from the backend // They therefore do have to be kept up to date from the backend
for (const immutableRole of ImmutableRolesList) { for (const immutableRole of ImmutableRolesList) {
this.logger.debug( this.logger.verbose(
`Updating permissions for immutable role "${immutableRole}"`, `Updating permissions for immutable role "${immutableRole}"`,
); );

View file

@ -37,8 +37,8 @@ export class RolesService {
try { try {
return await this.rolesRepository.save(role, { reload: true }); return await this.rolesRepository.save(role, { reload: true });
} catch (e: any) { } catch (e) {
return Fail(e?.message); return Fail(e);
} }
} }
@ -52,8 +52,8 @@ export class RolesService {
try { try {
return await this.rolesRepository.remove(roleToModify); return await this.rolesRepository.remove(roleToModify);
} catch (e: any) { } catch (e) {
return Fail(e?.message); return Fail(e);
} }
} }
@ -118,8 +118,8 @@ export class RolesService {
try { try {
return await this.rolesRepository.save(roleToModify); return await this.rolesRepository.save(roleToModify);
} catch (e: any) { } catch (e) {
return Fail(e?.message); return Fail(e);
} }
} }
@ -131,8 +131,8 @@ export class RolesService {
if (!found) return Fail('Role not found'); if (!found) return Fail('Role not found');
return found; return found;
} catch (e: any) { } catch (e) {
return Fail(e?.message); return Fail(e);
} }
} }
@ -141,8 +141,8 @@ export class RolesService {
const found = await this.rolesRepository.find(); const found = await this.rolesRepository.find();
if (!found) return Fail('No roles found'); if (!found) return Fail('No roles found');
return found; return found;
} catch (e: any) { } catch (e) {
return Fail(e?.message); return Fail(e);
} }
} }
@ -158,8 +158,8 @@ export class RolesService {
await this.rolesRepository.delete({ await this.rolesRepository.delete({
name: In(UndeletableRolesList), name: In(UndeletableRolesList),
}); });
} catch (e: any) { } catch (e) {
return Fail(e?.message); return Fail(e);
} }
return true; return true;
} }
@ -172,8 +172,7 @@ export class RolesService {
} else { } else {
const result = ERoleSchema.safeParse(role); const result = ERoleSchema.safeParse(role);
if (!result.success) { if (!result.success) {
this.logger.warn(result.error); return Fail(result.error);
return Fail('Invalid role');
} }
// This is safe // This is safe
return result.data as ERoleBackend; return result.data as ERoleBackend;

View file

@ -48,7 +48,7 @@ export class UsersModule implements OnModuleInit {
password: string, password: string,
roles: string[], roles: string[],
) { ) {
this.logger.debug(`Ensuring user "${username}" exists`); this.logger.verbose(`Ensuring user "${username}" exists`);
const exists = await this.usersService.exists(username); const exists = await this.usersService.exists(username);
if (exists) return; if (exists) return;

View file

@ -64,8 +64,8 @@ export class UsersService {
try { try {
return await this.usersRepository.save(user); return await this.usersRepository.save(user);
} catch (e: any) { } catch (e) {
return Fail(e?.message); return Fail(e);
} }
} }
@ -79,8 +79,8 @@ export class UsersService {
try { try {
return await this.usersRepository.remove(userToModify); return await this.usersRepository.remove(userToModify);
} catch (e: any) { } catch (e) {
return Fail(e?.message); return Fail(e);
} }
} }
@ -95,7 +95,7 @@ export class UsersService {
if (ImmutableUsersList.includes(userToModify.username)) { if (ImmutableUsersList.includes(userToModify.username)) {
// Just fail silently // Just fail silently
this.logger.log("Can't modify system user"); this.logger.verbose("User tried to modify system user, failed silently");
return userToModify; return userToModify;
} }
@ -108,8 +108,8 @@ export class UsersService {
try { try {
return await this.usersRepository.save(userToModify); return await this.usersRepository.save(userToModify);
} catch (e: any) { } catch (e) {
return Fail(e?.message); return Fail(e);
} }
} }
@ -124,8 +124,7 @@ export class UsersService {
.where('roles @> ARRAY[:role]', { role }) .where('roles @> ARRAY[:role]', { role })
.execute(); .execute();
} catch (e) { } catch (e) {
this.logger.error(e); return Fail(e);
return Fail("Couldn't remove role from everyone");
} }
return true; return true;
@ -150,8 +149,8 @@ export class UsersService {
try { try {
userToModify = await this.usersRepository.save(userToModify); userToModify = await this.usersRepository.save(userToModify);
} catch (e: any) { } catch (e) {
return Fail(e?.message); return Fail(e);
} }
return userToModify; return userToModify;
@ -193,8 +192,8 @@ export class UsersService {
if (!found) return Fail('User not found'); if (!found) return Fail('User not found');
return found; return found;
} catch (e: any) { } catch (e) {
return Fail(e?.message); return Fail(e);
} }
} }
@ -206,8 +205,8 @@ export class UsersService {
if (!found) return Fail('User not found'); if (!found) return Fail('User not found');
return found as EUserBackend; return found as EUserBackend;
} catch (e: any) { } catch (e) {
return Fail(e?.message); return Fail(e);
} }
} }
@ -223,8 +222,8 @@ export class UsersService {
take: count, take: count,
skip: count * page, skip: count * page,
}); });
} catch (e: any) { } catch (e) {
return Fail(e?.message); return Fail(e);
} }
} }

View file

@ -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<EUserBackend>,
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<EUserBackend> {
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<EUserBackend> {
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<EUserBackend> {
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<true> {
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<Permissions> {
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<EUserBackend> {
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<EUserBackend> {
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<EUserBackend> {
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<EUserBackend> {
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<EUserBackend[]> {
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<boolean> {
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<number> {
const result = await this.prefService.getNumberPreference(
SysPreference.BCryptStrength,
);
if (HasFailed(result)) {
return 12;
}
return result;
}
}

View file

@ -7,10 +7,10 @@ export class HostConfigService {
private readonly logger = new Logger('HostConfigService'); private readonly logger = new Logger('HostConfigService');
constructor(private configService: ConfigService) { constructor(private configService: ConfigService) {
this.logger.debug('Host: ' + this.getHost()); this.logger.log('Host: ' + this.getHost());
this.logger.debug('Port: ' + this.getPort()); this.logger.log('Port: ' + this.getPort());
this.logger.debug('Demo: ' + this.isDemo()); this.logger.log('Demo: ' + this.isDemo());
this.logger.debug('Demo Interval: ' + this.getDemoInterval() / 1000 + 's'); this.logger.log('Demo Interval: ' + this.getDemoInterval() / 1000 + 's');
} }
public getHost(): string { public getHost(): string {

View file

@ -7,7 +7,7 @@ export class MultipartConfigService {
private readonly logger = new Logger('MultipartConfigService'); private readonly logger = new Logger('MultipartConfigService');
constructor(private configService: ConfigService) { constructor(private configService: ConfigService) {
this.logger.debug('Max file size: ' + this.getMaxFileSize()); this.logger.log('Max file size: ' + this.getMaxFileSize());
} }
public getMaxFileSize(): number { public getMaxFileSize(): number {

View file

@ -16,7 +16,7 @@ export class ServeStaticConfigService
private defaultLocation = join(PackageRoot, '../frontend/dist'); private defaultLocation = join(PackageRoot, '../frontend/dist');
constructor(private configService: ConfigService) { constructor(private configService: ConfigService) {
this.logger.debug('Static directory: ' + this.getStaticDirectory()); this.logger.log('Static directory: ' + this.getStaticDirectory());
} }
public getStaticDirectory(): string { public getStaticDirectory(): string {

View file

@ -15,11 +15,12 @@ export class TypeOrmConfigService implements TypeOrmOptionsFactory {
) { ) {
const varOptions = this.getTypeOrmServerOptions(); const varOptions = this.getTypeOrmServerOptions();
this.logger.debug('DB host: ' + varOptions.host); this.logger.log('DB host: ' + varOptions.host);
this.logger.debug('DB port: ' + varOptions.port); this.logger.log('DB port: ' + varOptions.port);
this.logger.debug('DB username: ' + varOptions.username); this.logger.log('DB database: ' + varOptions.database);
this.logger.debug('DB password: ' + varOptions.password);
this.logger.debug('DB database: ' + varOptions.database); this.logger.verbose('DB username: ' + varOptions.username);
this.logger.verbose('DB password: ' + varOptions.password);
} }
public getTypeOrmServerOptions() { public getTypeOrmServerOptions() {

View file

@ -14,8 +14,8 @@ export class JwtConfigService implements JwtOptionsFactory {
private async printDebug() { private async printDebug() {
const secret = await this.getJwtSecret(); const secret = await this.getJwtSecret();
const expiresIn = await this.getJwtExpiresIn(); const expiresIn = await this.getJwtExpiresIn();
this.logger.debug('JWT secret: ' + secret); this.logger.verbose('JWT secret: ' + secret);
this.logger.debug('JWT expiresIn: ' + expiresIn); this.logger.verbose('JWT expiresIn: ' + expiresIn);
} }
public async getJwtSecret(): Promise<string> { public async getJwtSecret(): Promise<string> {

View file

@ -28,7 +28,7 @@ export class MultiPartPipe implements PipeTransform {
) { ) {
let zodSchema = (metadata?.metatype as ZodDtoStatic)?.zodSchema; let zodSchema = (metadata?.metatype as ZodDtoStatic)?.zodSchema;
if (!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'); throw new InternalServerErrorException('Invalid scheme on backend');
} }

View file

@ -47,14 +47,14 @@ export class MainAuthGuard extends AuthGuard(['jwt', 'guest']) {
// These are the permissions required to access the route // These are the permissions required to access the route
const permissions = this.extractPermissions(context); const permissions = this.extractPermissions(context);
if (HasFailed(permissions)) { if (HasFailed(permissions)) {
this.logger.warn('Route Permissions: ' + permissions.getReason()); this.logger.error('Fetching route permission failed: ' + permissions.getReason());
throw new InternalServerErrorException(); throw new InternalServerErrorException();
} }
// These are the permissions the user has // These are the permissions the user has
const userPermissions = await this.usersService.getPermissions(user.id); const userPermissions = await this.usersService.getPermissions(user.id);
if (HasFailed(userPermissions)) { if (HasFailed(userPermissions)) {
this.logger.warn('User Permissions: ' + userPermissions.getReason()); this.logger.warn('Fetching user permissions failed: ' + userPermissions.getReason());
throw new InternalServerErrorException(); throw new InternalServerErrorException();
} }

View file

@ -20,7 +20,7 @@ export class DemoManagerModule implements OnModuleInit, OnModuleDestroy {
async onModuleInit() { async onModuleInit() {
if (this.hostConfigService.isDemo()) { if (this.hostConfigService.isDemo()) {
this.logger.log('Demo mode enabled'); this.logger.warn('Demo mode enabled, images are ephimeral');
await this.setupDemoMode(); await this.setupDemoMode();
} }
} }

View file

@ -14,7 +14,7 @@ export class DemoManagerService {
public async setupRoles() { public async setupRoles() {
this.logger.warn( 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 // Could be done manually, but this makes settup up a demo instance quicker
this.rolesService.addPermissions('guest', [Permission.ImageUpload]); this.rolesService.addPermissions('guest', [Permission.ImageUpload]);
@ -25,7 +25,7 @@ export class DemoManagerService {
} }
private async executeAsync() { private async executeAsync() {
this.logger.debug('Executing demo cleanup'); this.logger.verbose('Executing demo cleanup');
await this.imagesService.deleteAll(true); await this.imagesService.deleteAll(true);
} }
} }

View file

@ -123,6 +123,7 @@ export class RolesController {
const success = await this.usersService.removeRoleEveryone(role.name); const success = await this.usersService.removeRoleEveryone(role.name);
if (HasFailed(success)) { if (HasFailed(success)) {
this.logger.warn(success.getReason());
throw new InternalServerErrorException( throw new InternalServerErrorException(
'Could not remove role from users', 'Could not remove role from users',
); );

View file

@ -40,7 +40,7 @@ export class PicsurImgComponent implements OnChanges {
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
let url = this.imageURL ?? ''; let url = this.imageURL ?? '';
if (URLRegex.test(url)) { if (URLRegex.test(url)) {
this.update(url).catch((err) => this.logger.error(err)); this.update(url).catch(this.logger.error);
} else { } else {
this.state = PicsurImgState.Loading; this.state = PicsurImgState.Loading;
} }

View file

@ -54,7 +54,7 @@ export class LoginComponent implements OnInit {
const user = await this.userService.login(data.username, data.password); const user = await this.userService.login(data.username, data.password);
if (HasFailed(user)) { if (HasFailed(user)) {
this.logger.warn(user.getReason()); this.logger.error(user.getReason());
this.utilService.showSnackBar( this.utilService.showSnackBar(
'Login failed, please try again', 'Login failed, please try again',
SnackBarType.Error SnackBarType.Error

View file

@ -54,7 +54,7 @@ export class RegisterComponent implements OnInit {
const user = await this.userService.register(data.username, data.password); const user = await this.userService.register(data.username, data.password);
if (HasFailed(user)) { if (HasFailed(user)) {
this.logger.warn(user.getReason()); this.logger.error(user.getReason());
this.utilService.showSnackBar( this.utilService.showSnackBar(
'Register failed, please try again', 'Register failed, please try again',
SnackBarType.Error SnackBarType.Error
@ -65,7 +65,7 @@ export class RegisterComponent implements OnInit {
if (!this.userService.isLoggedIn) { if (!this.userService.isLoggedIn) {
const loginResult = await this.userService.login(data.username, data.password); const loginResult = await this.userService.login(data.username, data.password);
if (HasFailed(loginResult)) { if (HasFailed(loginResult)) {
this.logger.warn(loginResult.getReason()); this.logger.error(loginResult.getReason());
this.utilService.showSnackBar( this.utilService.showSnackBar(
'Failed to login after register', 'Failed to login after register',
SnackBarType.Error SnackBarType.Error

View file

@ -44,7 +44,7 @@ export class ApiService {
const validateResult = sendSchema.safeParse(data); const validateResult = sendSchema.safeParse(data);
if (!validateResult.success) { if (!validateResult.success) {
this.logger.warn(validateResult.error); this.logger.error(validateResult.error);
return Fail('Something went wrong'); return Fail('Something went wrong');
} }
@ -78,7 +78,7 @@ export class ApiService {
const validateResult = resultSchema.safeParse(result); const validateResult = resultSchema.safeParse(result);
if (!validateResult.success) { if (!validateResult.success) {
this.logger.warn('result', validateResult.error); this.logger.error(validateResult.error);
return Fail('Something went wrong'); return Fail('Something went wrong');
} }
@ -98,7 +98,7 @@ export class ApiService {
try { try {
return await response.json(); return await response.json();
} catch (e) { } catch (e) {
this.logger.warn(e); this.logger.error(e);
return Fail('Something went wrong'); return Fail('Something went wrong');
} }
} }
@ -113,7 +113,7 @@ export class ApiService {
try { try {
return await response.arrayBuffer(); return await response.arrayBuffer();
} catch (e) { } catch (e) {
this.logger.warn(e); this.logger.error(e);
return Fail('Something went wrong'); return Fail('Something went wrong');
} }
} }
@ -133,9 +133,9 @@ export class ApiService {
options.headers = headers; options.headers = headers;
return await window.fetch(url, options); return await window.fetch(url, options);
} catch (error: any) { } catch (e) {
this.errorSubject.next({ this.errorSubject.next({
error, error: e,
url, url,
}); });
return Fail('Network Error'); return Fail('Network Error');

View file

@ -59,7 +59,7 @@ export class PermissionService {
return this.userService.live.pipe(Throttle(300)).subscribe(async (user) => { return this.userService.live.pipe(Throttle(300)).subscribe(async (user) => {
const permissions = await this.updatePermissions(); const permissions = await this.updatePermissions();
if (HasFailed(permissions)) { if (HasFailed(permissions)) {
this.logger.warn(permissions.getReason()); this.logger.error(permissions.getReason());
return; return;
} }
}); });

View file

@ -44,7 +44,7 @@ export class UserService {
const user = await this.extractUser(apikey); const user = await this.extractUser(apikey);
if (HasFailed(user)) { if (HasFailed(user)) {
this.logger.warn(user.getReason()); this.logger.error(user.getReason());
await this.logout(); await this.logout();
return; return;
} }
@ -53,7 +53,7 @@ export class UserService {
const fetchedUser = await this.fetchUser(); const fetchedUser = await this.fetchUser();
if (HasFailed(fetchedUser)) { if (HasFailed(fetchedUser)) {
this.logger.warn(fetchedUser.getReason()); this.logger.error(fetchedUser.getReason());
await this.logout(); await this.logout();
return; return;
} }
@ -122,7 +122,7 @@ export class UserService {
const result = JwtDataSchema.safeParse(decoded); const result = JwtDataSchema.safeParse(decoded);
if (!result.success) { if (!result.success) {
this.logger.warn(result.error); this.logger.error(result.error);
return Fail('Invalid token data'); return Fail('Invalid token data');
} }

View file

@ -66,8 +66,18 @@ export class GlobalLogger {
return; 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 styles = LoggerStyles[level];
const message = ['%c' + context.source, styles, ...args]; const message = ['%c' + context.source, styles, ...processedArgs];
console[LoggerFunctions[level]](...message); console[LoggerFunctions[level]](...message);
} }
} }

View file

@ -29,7 +29,7 @@ export class ApiErrorService {
this.utilService.showSnackBar('Network Error', SnackBarType.Error); this.utilService.showSnackBar('Network Error', SnackBarType.Error);
} }
this.logger.warn(error.error); this.logger.error(error.error);
}); });
} }
} }

View file

@ -4,15 +4,27 @@
// -> Side effects go brrr // -> Side effects go brrr
export class Failure { export class Failure {
constructor(private readonly reason?: string) {} constructor(private readonly reason?: string, private readonly stack?: string) {}
getReason(): string { getReason(): string {
return this.reason ?? 'Unknown'; return this.reason ?? 'Unknown';
} }
getStack(): string {
return this.stack ?? 'None';
}
} }
export function Fail(reason?: string): Failure { export function Fail(reason?: any): Failure {
if (typeof reason === 'string') {
return new Failure(reason); 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> = T | Failure; export type Failable<T> = T | Failure;

View file

@ -16,6 +16,7 @@
"strictNullChecks": true, "strictNullChecks": true,
"noImplicitThis": true, "noImplicitThis": true,
"noImplicitReturns": true, "noImplicitReturns": true,
"useUnknownInCatchVariables": true,
"skipLibCheck": true, "skipLibCheck": true,
"removeComments": true, "removeComments": true,