change error behaviour
This commit is contained in:
parent
659a41cd28
commit
ab600c20b7
|
@ -25,7 +25,7 @@ const imageCorsConfig = cors({
|
||||||
maxAge: 30 * 24 * 60 * 60,
|
maxAge: 30 * 24 * 60 * 60,
|
||||||
});
|
});
|
||||||
|
|
||||||
const imageCorpOverride = (
|
const imageCorsOverride = (
|
||||||
req: IncomingMessage,
|
req: IncomingMessage,
|
||||||
res: ServerResponse,
|
res: ServerResponse,
|
||||||
next: Function,
|
next: Function,
|
||||||
|
@ -54,6 +54,6 @@ const imageCorpOverride = (
|
||||||
export class AppModule implements NestModule {
|
export class AppModule implements NestModule {
|
||||||
configure(consumer: MiddlewareConsumer) {
|
configure(consumer: MiddlewareConsumer) {
|
||||||
consumer.apply(mainCorsConfig).exclude('/i').forRoutes('/');
|
consumer.apply(mainCorsConfig).exclude('/i').forRoutes('/');
|
||||||
consumer.apply(imageCorsConfig, imageCorpOverride).forRoutes('/i');
|
consumer.apply(imageCorsConfig, imageCorsOverride).forRoutes('/i');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ export class PreferenceCommonService {
|
||||||
case 'boolean':
|
case 'boolean':
|
||||||
return {
|
return {
|
||||||
key: preference.key,
|
key: preference.key,
|
||||||
value: preference.value == 'true',
|
value: preference.value === 'true',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ export class PreferenceCommonService {
|
||||||
expectedType: PrefValueTypeStrings,
|
expectedType: PrefValueTypeStrings,
|
||||||
): Failable<string> {
|
): Failable<string> {
|
||||||
const type = typeof value;
|
const type = typeof value;
|
||||||
if (type != expectedType) {
|
if (type !== expectedType) {
|
||||||
return Fail(FT.UsrValidation, 'Invalid preference value');
|
return Fail(FT.UsrValidation, 'Invalid preference value');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { FactoryProvider, Injectable, Logger } from '@nestjs/common';
|
import { FactoryProvider, Injectable, Logger } from '@nestjs/common';
|
||||||
import { JwtModuleOptions, JwtOptionsFactory } from '@nestjs/jwt';
|
import { JwtModuleOptions, JwtOptionsFactory } from '@nestjs/jwt';
|
||||||
import { HasFailed } from 'picsur-shared/dist/types';
|
import { ThrowIfFailed } from 'picsur-shared/dist/types';
|
||||||
import { SysPreferenceService } from '../../collections/preference-db/sys-preference-db.service';
|
import { SysPreferenceService } from '../../collections/preference-db/sys-preference-db.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -19,20 +19,18 @@ export class JwtConfigService implements JwtOptionsFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getJwtSecret(): Promise<string> {
|
public async getJwtSecret(): Promise<string> {
|
||||||
const secret = await this.prefService.getStringPreference('jwt_secret');
|
const secret = ThrowIfFailed(
|
||||||
if (HasFailed(secret)) {
|
await this.prefService.getStringPreference('jwt_secret'),
|
||||||
throw new Error('JWT secret could not be retrieved');
|
);
|
||||||
}
|
|
||||||
return secret;
|
return secret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getJwtExpiresIn(): Promise<string> {
|
public async getJwtExpiresIn(): Promise<string> {
|
||||||
const expiresIn = await this.prefService.getStringPreference(
|
const expiresIn = ThrowIfFailed(
|
||||||
'jwt_expires_in',
|
await this.prefService.getStringPreference('jwt_expires_in'),
|
||||||
);
|
);
|
||||||
if (HasFailed(expiresIn)) {
|
|
||||||
throw new Error('JWT expiresIn could not be retrieved');
|
|
||||||
}
|
|
||||||
return expiresIn;
|
return expiresIn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,9 @@ import {
|
||||||
createParamDecorator,
|
createParamDecorator,
|
||||||
ExecutionContext,
|
ExecutionContext,
|
||||||
SetMetadata,
|
SetMetadata,
|
||||||
UseGuards,
|
UseGuards
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
|
import { Fail, FT } from 'picsur-shared/dist/types';
|
||||||
import { CombineFCDecorators } from 'picsur-shared/dist/util/decorator';
|
import { CombineFCDecorators } from 'picsur-shared/dist/util/decorator';
|
||||||
import { LocalAuthGuard } from '../managers/auth/guards/local-auth.guard';
|
import { LocalAuthGuard } from '../managers/auth/guards/local-auth.guard';
|
||||||
import { Permission, Permissions } from '../models/constants/permissions.const';
|
import { Permission, Permissions } from '../models/constants/permissions.const';
|
||||||
|
@ -28,7 +29,7 @@ export const HasPermission = createParamDecorator(
|
||||||
const req: AuthFasityRequest = ctx.switchToHttp().getRequest();
|
const req: AuthFasityRequest = ctx.switchToHttp().getRequest();
|
||||||
const permissions = req.userPermissions;
|
const permissions = req.userPermissions;
|
||||||
if (!permissions) {
|
if (!permissions) {
|
||||||
throw new Error('Permissions are missing from request');
|
throw Fail(FT.Internal, undefined, 'Permissions are missing from request');
|
||||||
}
|
}
|
||||||
|
|
||||||
return permissions.includes(data);
|
return permissions.includes(data);
|
||||||
|
@ -40,7 +41,7 @@ export const GetPermissions = createParamDecorator(
|
||||||
const req: AuthFasityRequest = ctx.switchToHttp().getRequest();
|
const req: AuthFasityRequest = ctx.switchToHttp().getRequest();
|
||||||
const permissions = req.userPermissions;
|
const permissions = req.userPermissions;
|
||||||
if (!permissions) {
|
if (!permissions) {
|
||||||
throw new Error('Permissions are missing from request');
|
throw Fail(FT.Internal, undefined, 'Permissions are missing from request');
|
||||||
}
|
}
|
||||||
|
|
||||||
return permissions;
|
return permissions;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
||||||
|
import { Fail, FT } from 'picsur-shared/dist/types';
|
||||||
import AuthFasityRequest from '../models/interfaces/authrequest.dto';
|
import AuthFasityRequest from '../models/interfaces/authrequest.dto';
|
||||||
|
|
||||||
export const ReqUser = createParamDecorator(
|
export const ReqUser = createParamDecorator(
|
||||||
|
@ -12,7 +13,7 @@ export const ReqUserID = createParamDecorator(
|
||||||
(input: any, ctx: ExecutionContext) => {
|
(input: any, ctx: ExecutionContext) => {
|
||||||
const request = ctx.switchToHttp().getRequest() as AuthFasityRequest;
|
const request = ctx.switchToHttp().getRequest() as AuthFasityRequest;
|
||||||
const id = request.user.id;
|
const id = request.user.id;
|
||||||
if (!id) throw new Error('User ID is not set');
|
if (!id) throw Fail(FT.Internal, undefined, 'User ID is not set');
|
||||||
return id;
|
return id;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import { ArgumentsHost, Catch, ExceptionFilter, Logger } from '@nestjs/common';
|
import { ArgumentsHost, Catch, ExceptionFilter, Logger } from '@nestjs/common';
|
||||||
import { FastifyReply, FastifyRequest } from 'fastify';
|
import { FastifyReply, FastifyRequest } from 'fastify';
|
||||||
import { ApiErrorResponse } from 'picsur-shared/dist/dto/api/api.dto';
|
import { ApiErrorResponse } from 'picsur-shared/dist/dto/api/api.dto';
|
||||||
import { IsFailure } from 'picsur-shared/dist/types/failable';
|
import {
|
||||||
|
Fail,
|
||||||
|
Failure,
|
||||||
|
FT,
|
||||||
|
IsFailure
|
||||||
|
} from 'picsur-shared/dist/types/failable';
|
||||||
|
|
||||||
// This will catch any exception that is made in any request
|
// This will catch any exception that is made in any request
|
||||||
// (As long as its within nest, the earlier fastify stages are not handled here)
|
// (As long as its within nest, the earlier fastify stages are not handled here)
|
||||||
|
@ -11,7 +16,7 @@ import { IsFailure } from 'picsur-shared/dist/types/failable';
|
||||||
export class MainExceptionFilter implements ExceptionFilter {
|
export class MainExceptionFilter implements ExceptionFilter {
|
||||||
private static readonly logger = new Logger('MainExceptionFilter');
|
private static readonly logger = new Logger('MainExceptionFilter');
|
||||||
|
|
||||||
catch(exception: unknown, host: ArgumentsHost) {
|
catch(exception: Failure, host: ArgumentsHost) {
|
||||||
const ctx = host.switchToHttp();
|
const ctx = host.switchToHttp();
|
||||||
const response = ctx.getResponse<FastifyReply>();
|
const response = ctx.getResponse<FastifyReply>();
|
||||||
const request = ctx.getRequest<FastifyRequest>();
|
const request = ctx.getRequest<FastifyRequest>();
|
||||||
|
@ -22,7 +27,7 @@ export class MainExceptionFilter implements ExceptionFilter {
|
||||||
MainExceptionFilter.logger.error(
|
MainExceptionFilter.logger.error(
|
||||||
traceString + ' Unkown exception: ' + exception,
|
traceString + ' Unkown exception: ' + exception,
|
||||||
);
|
);
|
||||||
return;
|
exception = Fail(FT.Internal, 'Unknown exception', exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
const status = exception.getCode();
|
const status = exception.getCode();
|
||||||
|
@ -50,6 +55,7 @@ export class MainExceptionFilter implements ExceptionFilter {
|
||||||
success: false,
|
success: false,
|
||||||
statusCode: status,
|
statusCode: status,
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
|
timeMs: Math.round(response.getResponseTime()),
|
||||||
|
|
||||||
data: {
|
data: {
|
||||||
type,
|
type,
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import {
|
import {
|
||||||
CallHandler,
|
CallHandler,
|
||||||
ExecutionContext,
|
ExecutionContext,
|
||||||
Injectable, Logger,
|
Injectable,
|
||||||
|
Logger,
|
||||||
NestInterceptor,
|
NestInterceptor,
|
||||||
Optional
|
Optional
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { Reflector } from '@nestjs/core';
|
import { Reflector } from '@nestjs/core';
|
||||||
|
import { FastifyReply } from 'fastify';
|
||||||
import { ApiAnySuccessResponse } from 'picsur-shared/dist/dto/api/api.dto';
|
import { ApiAnySuccessResponse } from 'picsur-shared/dist/dto/api/api.dto';
|
||||||
import { Fail, FT } from 'picsur-shared/dist/types';
|
import { Fail, FT } from 'picsur-shared/dist/types';
|
||||||
import { ZodDtoStatic } from 'picsur-shared/dist/util/create-zod-dto';
|
import { ZodDtoStatic } from 'picsur-shared/dist/util/create-zod-dto';
|
||||||
|
@ -81,15 +83,17 @@ export class SuccessInterceptor<T> implements NestInterceptor {
|
||||||
context: ExecutionContext,
|
context: ExecutionContext,
|
||||||
data: unknown,
|
data: unknown,
|
||||||
): ApiAnySuccessResponse {
|
): ApiAnySuccessResponse {
|
||||||
const status = context.switchToHttp().getResponse().statusCode;
|
const response = context.switchToHttp().getResponse<FastifyReply>();
|
||||||
const response = {
|
|
||||||
|
const newResponse: ApiAnySuccessResponse = {
|
||||||
success: true as true, // really typescript
|
success: true as true, // really typescript
|
||||||
statusCode: status,
|
statusCode: response.statusCode,
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
|
timeMs: Math.round(response.getResponseTime()),
|
||||||
|
|
||||||
data,
|
data,
|
||||||
};
|
};
|
||||||
|
|
||||||
return response;
|
return newResponse;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import {
|
import {
|
||||||
Body,
|
Body,
|
||||||
Controller,
|
Controller,
|
||||||
Get,
|
Get, Logger,
|
||||||
InternalServerErrorException,
|
|
||||||
Logger,
|
|
||||||
Param,
|
Param,
|
||||||
Post
|
Post
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
|
@ -13,7 +11,7 @@ import {
|
||||||
UpdatePreferenceRequest,
|
UpdatePreferenceRequest,
|
||||||
UpdatePreferenceResponse
|
UpdatePreferenceResponse
|
||||||
} from 'picsur-shared/dist/dto/api/pref.dto';
|
} from 'picsur-shared/dist/dto/api/pref.dto';
|
||||||
import { HasFailed } from 'picsur-shared/dist/types';
|
import { ThrowIfFailed } from 'picsur-shared/dist/types';
|
||||||
import { SysPreferenceService } from '../../../collections/preference-db/sys-preference-db.service';
|
import { SysPreferenceService } from '../../../collections/preference-db/sys-preference-db.service';
|
||||||
import { RequiredPermissions } from '../../../decorators/permissions.decorator';
|
import { RequiredPermissions } from '../../../decorators/permissions.decorator';
|
||||||
import { Returns } from '../../../decorators/returns.decorator';
|
import { Returns } from '../../../decorators/returns.decorator';
|
||||||
|
@ -29,11 +27,7 @@ export class SysPrefController {
|
||||||
@Get()
|
@Get()
|
||||||
@Returns(MultiplePreferencesResponse)
|
@Returns(MultiplePreferencesResponse)
|
||||||
async getAllSysPrefs(): Promise<MultiplePreferencesResponse> {
|
async getAllSysPrefs(): Promise<MultiplePreferencesResponse> {
|
||||||
const prefs = await this.prefService.getAllPreferences();
|
const prefs = ThrowIfFailed(await this.prefService.getAllPreferences());
|
||||||
if (HasFailed(prefs)) {
|
|
||||||
this.logger.warn(prefs.getReason());
|
|
||||||
throw new InternalServerErrorException('Could not get preferences');
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
results: prefs,
|
results: prefs,
|
||||||
|
@ -44,11 +38,7 @@ export class SysPrefController {
|
||||||
@Get(':key')
|
@Get(':key')
|
||||||
@Returns(GetPreferenceResponse)
|
@Returns(GetPreferenceResponse)
|
||||||
async getSysPref(@Param('key') key: string): Promise<GetPreferenceResponse> {
|
async getSysPref(@Param('key') key: string): Promise<GetPreferenceResponse> {
|
||||||
const pref = await this.prefService.getPreference(key);
|
const pref = ThrowIfFailed(await this.prefService.getPreference(key));
|
||||||
if (HasFailed(pref)) {
|
|
||||||
this.logger.warn(pref.getReason());
|
|
||||||
throw new InternalServerErrorException('Could not get preference');
|
|
||||||
}
|
|
||||||
|
|
||||||
return pref;
|
return pref;
|
||||||
}
|
}
|
||||||
|
@ -61,11 +51,9 @@ export class SysPrefController {
|
||||||
): Promise<UpdatePreferenceResponse> {
|
): Promise<UpdatePreferenceResponse> {
|
||||||
const value = body.value;
|
const value = body.value;
|
||||||
|
|
||||||
const pref = await this.prefService.setPreference(key, value);
|
const pref = ThrowIfFailed(
|
||||||
if (HasFailed(pref)) {
|
await this.prefService.setPreference(key, value),
|
||||||
this.logger.warn(pref.getReason());
|
);
|
||||||
throw new InternalServerErrorException('Could not set preference');
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
key,
|
key,
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import {
|
import {
|
||||||
Body,
|
Body,
|
||||||
Controller,
|
Controller,
|
||||||
Get,
|
Get, Logger,
|
||||||
InternalServerErrorException,
|
|
||||||
Logger,
|
|
||||||
Param,
|
Param,
|
||||||
Post
|
Post
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
|
@ -13,7 +11,7 @@ import {
|
||||||
UpdatePreferenceRequest,
|
UpdatePreferenceRequest,
|
||||||
UpdatePreferenceResponse
|
UpdatePreferenceResponse
|
||||||
} from 'picsur-shared/dist/dto/api/pref.dto';
|
} from 'picsur-shared/dist/dto/api/pref.dto';
|
||||||
import { HasFailed } from 'picsur-shared/dist/types';
|
import { ThrowIfFailed } from 'picsur-shared/dist/types';
|
||||||
import { UsrPreferenceService } from '../../../collections/preference-db/usr-preference-db.service';
|
import { UsrPreferenceService } from '../../../collections/preference-db/usr-preference-db.service';
|
||||||
import { RequiredPermissions } from '../../../decorators/permissions.decorator';
|
import { RequiredPermissions } from '../../../decorators/permissions.decorator';
|
||||||
import { ReqUserID } from '../../../decorators/request-user.decorator';
|
import { ReqUserID } from '../../../decorators/request-user.decorator';
|
||||||
|
@ -32,11 +30,9 @@ export class UsrPrefController {
|
||||||
async getAllSysPrefs(
|
async getAllSysPrefs(
|
||||||
@ReqUserID() userid: string,
|
@ReqUserID() userid: string,
|
||||||
): Promise<MultiplePreferencesResponse> {
|
): Promise<MultiplePreferencesResponse> {
|
||||||
const prefs = await this.prefService.getAllPreferences(userid);
|
const prefs = ThrowIfFailed(
|
||||||
if (HasFailed(prefs)) {
|
await this.prefService.getAllPreferences(userid),
|
||||||
this.logger.warn(prefs.getReason());
|
);
|
||||||
throw new InternalServerErrorException('Could not get preferences');
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
results: prefs,
|
results: prefs,
|
||||||
|
@ -50,11 +46,9 @@ export class UsrPrefController {
|
||||||
@Param('key') key: string,
|
@Param('key') key: string,
|
||||||
@ReqUserID() userid: string,
|
@ReqUserID() userid: string,
|
||||||
): Promise<GetPreferenceResponse> {
|
): Promise<GetPreferenceResponse> {
|
||||||
const pref = await this.prefService.getPreference(userid, key);
|
const pref = ThrowIfFailed(
|
||||||
if (HasFailed(pref)) {
|
await this.prefService.getPreference(userid, key),
|
||||||
this.logger.warn(pref.getReason());
|
);
|
||||||
throw new InternalServerErrorException('Could not get preference');
|
|
||||||
}
|
|
||||||
|
|
||||||
return pref;
|
return pref;
|
||||||
}
|
}
|
||||||
|
@ -68,11 +62,9 @@ export class UsrPrefController {
|
||||||
): Promise<UpdatePreferenceResponse> {
|
): Promise<UpdatePreferenceResponse> {
|
||||||
const value = body.value;
|
const value = body.value;
|
||||||
|
|
||||||
const pref = await this.prefService.setPreference(userid, key, value);
|
const pref = ThrowIfFailed(
|
||||||
if (HasFailed(pref)) {
|
await this.prefService.setPreference(userid, key, value),
|
||||||
this.logger.warn(pref.getReason());
|
);
|
||||||
throw new InternalServerErrorException('Could not set preference');
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
key,
|
key,
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import {
|
import {
|
||||||
Body,
|
Body,
|
||||||
Controller,
|
Controller,
|
||||||
Get,
|
Get, Logger,
|
||||||
InternalServerErrorException,
|
|
||||||
Logger,
|
|
||||||
Post
|
Post
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
|
@ -18,7 +16,7 @@ import {
|
||||||
RoleUpdateResponse,
|
RoleUpdateResponse,
|
||||||
SpecialRolesResponse
|
SpecialRolesResponse
|
||||||
} from 'picsur-shared/dist/dto/api/roles.dto';
|
} from 'picsur-shared/dist/dto/api/roles.dto';
|
||||||
import { HasFailed } from 'picsur-shared/dist/types';
|
import { Fail, FT, ThrowIfFailed } from 'picsur-shared/dist/types';
|
||||||
import { RolesService } from '../../../collections/role-db/role-db.service';
|
import { RolesService } from '../../../collections/role-db/role-db.service';
|
||||||
import { UsersService } from '../../../collections/user-db/user-db.service';
|
import { UsersService } from '../../../collections/user-db/user-db.service';
|
||||||
import { RequiredPermissions } from '../../../decorators/permissions.decorator';
|
import { RequiredPermissions } from '../../../decorators/permissions.decorator';
|
||||||
|
@ -45,11 +43,7 @@ export class RolesController {
|
||||||
@Get('list')
|
@Get('list')
|
||||||
@Returns(RoleListResponse)
|
@Returns(RoleListResponse)
|
||||||
async getRoles(): Promise<RoleListResponse> {
|
async getRoles(): Promise<RoleListResponse> {
|
||||||
const roles = await this.rolesService.findAll();
|
const roles = ThrowIfFailed(await this.rolesService.findAll());
|
||||||
if (HasFailed(roles)) {
|
|
||||||
this.logger.warn(roles.getReason());
|
|
||||||
throw new InternalServerErrorException('Could not list roles');
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
results: roles,
|
results: roles,
|
||||||
|
@ -60,11 +54,7 @@ export class RolesController {
|
||||||
@Post('info')
|
@Post('info')
|
||||||
@Returns(RoleInfoResponse)
|
@Returns(RoleInfoResponse)
|
||||||
async getRole(@Body() body: RoleInfoRequest): Promise<RoleInfoResponse> {
|
async getRole(@Body() body: RoleInfoRequest): Promise<RoleInfoResponse> {
|
||||||
const role = await this.rolesService.findOne(body.name);
|
const role = ThrowIfFailed(await this.rolesService.findOne(body.name));
|
||||||
if (HasFailed(role)) {
|
|
||||||
this.logger.warn(role.getReason());
|
|
||||||
throw new InternalServerErrorException('Could not find role');
|
|
||||||
}
|
|
||||||
|
|
||||||
return role;
|
return role;
|
||||||
}
|
}
|
||||||
|
@ -76,17 +66,12 @@ export class RolesController {
|
||||||
): Promise<RoleUpdateResponse> {
|
): Promise<RoleUpdateResponse> {
|
||||||
const permissions = body.permissions;
|
const permissions = body.permissions;
|
||||||
if (!isPermissionsArray(permissions)) {
|
if (!isPermissionsArray(permissions)) {
|
||||||
throw new InternalServerErrorException('Invalid permissions');
|
throw Fail(FT.UsrValidation, 'Invalid permissions array');
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedRole = await this.rolesService.setPermissions(
|
const updatedRole = ThrowIfFailed(
|
||||||
body.name,
|
await this.rolesService.setPermissions(body.name, permissions),
|
||||||
permissions,
|
|
||||||
);
|
);
|
||||||
if (HasFailed(updatedRole)) {
|
|
||||||
this.logger.warn(updatedRole.getReason());
|
|
||||||
throw new InternalServerErrorException('Could not set role permissions');
|
|
||||||
}
|
|
||||||
|
|
||||||
return updatedRole;
|
return updatedRole;
|
||||||
}
|
}
|
||||||
|
@ -98,14 +83,12 @@ export class RolesController {
|
||||||
): Promise<RoleCreateResponse> {
|
): Promise<RoleCreateResponse> {
|
||||||
const permissions = role.permissions;
|
const permissions = role.permissions;
|
||||||
if (!isPermissionsArray(permissions)) {
|
if (!isPermissionsArray(permissions)) {
|
||||||
throw new InternalServerErrorException('Invalid permissions array');
|
throw Fail(FT.UsrValidation, 'Invalid permissions array');
|
||||||
}
|
}
|
||||||
|
|
||||||
const newRole = await this.rolesService.create(role.name, permissions);
|
const newRole = ThrowIfFailed(
|
||||||
if (HasFailed(newRole)) {
|
await this.rolesService.create(role.name, permissions),
|
||||||
this.logger.warn(newRole.getReason());
|
);
|
||||||
throw new InternalServerErrorException('Could not create role');
|
|
||||||
}
|
|
||||||
|
|
||||||
return newRole;
|
return newRole;
|
||||||
}
|
}
|
||||||
|
@ -115,19 +98,13 @@ export class RolesController {
|
||||||
async deleteRole(
|
async deleteRole(
|
||||||
@Body() role: RoleDeleteRequest,
|
@Body() role: RoleDeleteRequest,
|
||||||
): Promise<RoleDeleteResponse> {
|
): Promise<RoleDeleteResponse> {
|
||||||
const deletedRole = await this.rolesService.delete(role.name);
|
const deletedRole = ThrowIfFailed(
|
||||||
if (HasFailed(deletedRole)) {
|
await this.rolesService.delete(role.name),
|
||||||
this.logger.warn(deletedRole.getReason());
|
);
|
||||||
throw new InternalServerErrorException('Could not delete role');
|
|
||||||
}
|
ThrowIfFailed(
|
||||||
|
await this.usersService.removeRoleEveryone(role.name),
|
||||||
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',
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
return deletedRole;
|
return deletedRole;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import {
|
import {
|
||||||
Body,
|
Body,
|
||||||
Controller,
|
Controller,
|
||||||
Get,
|
Get, Logger,
|
||||||
InternalServerErrorException,
|
|
||||||
Logger,
|
|
||||||
Post
|
Post
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
|
@ -19,7 +17,7 @@ import {
|
||||||
UserUpdateRequest,
|
UserUpdateRequest,
|
||||||
UserUpdateResponse
|
UserUpdateResponse
|
||||||
} from 'picsur-shared/dist/dto/api/user-manage.dto';
|
} from 'picsur-shared/dist/dto/api/user-manage.dto';
|
||||||
import { HasFailed } from 'picsur-shared/dist/types';
|
import { ThrowIfFailed } from 'picsur-shared/dist/types';
|
||||||
import { UsersService } from '../../../collections/user-db/user-db.service';
|
import { UsersService } from '../../../collections/user-db/user-db.service';
|
||||||
import { RequiredPermissions } from '../../../decorators/permissions.decorator';
|
import { RequiredPermissions } from '../../../decorators/permissions.decorator';
|
||||||
import { Returns } from '../../../decorators/returns.decorator';
|
import { Returns } from '../../../decorators/returns.decorator';
|
||||||
|
@ -43,11 +41,9 @@ export class UserAdminController {
|
||||||
async listUsersPaged(
|
async listUsersPaged(
|
||||||
@Body() body: UserListRequest,
|
@Body() body: UserListRequest,
|
||||||
): Promise<UserListResponse> {
|
): Promise<UserListResponse> {
|
||||||
const found = await this.usersService.findMany(body.count, body.page);
|
const found = ThrowIfFailed(
|
||||||
if (HasFailed(found)) {
|
await this.usersService.findMany(body.count, body.page),
|
||||||
this.logger.warn(found.getReason());
|
);
|
||||||
throw new InternalServerErrorException('Could not list users');
|
|
||||||
}
|
|
||||||
|
|
||||||
found.results = found.results.map(EUserBackend2EUser);
|
found.results = found.results.map(EUserBackend2EUser);
|
||||||
return found;
|
return found;
|
||||||
|
@ -58,15 +54,13 @@ export class UserAdminController {
|
||||||
async register(
|
async register(
|
||||||
@Body() create: UserCreateRequest,
|
@Body() create: UserCreateRequest,
|
||||||
): Promise<UserCreateResponse> {
|
): Promise<UserCreateResponse> {
|
||||||
const user = await this.usersService.create(
|
const user = ThrowIfFailed(
|
||||||
|
await this.usersService.create(
|
||||||
create.username,
|
create.username,
|
||||||
create.password,
|
create.password,
|
||||||
create.roles,
|
create.roles,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
if (HasFailed(user)) {
|
|
||||||
this.logger.warn(user.getReason());
|
|
||||||
throw new InternalServerErrorException('Could not create user');
|
|
||||||
}
|
|
||||||
|
|
||||||
return EUserBackend2EUser(user);
|
return EUserBackend2EUser(user);
|
||||||
}
|
}
|
||||||
|
@ -74,11 +68,7 @@ export class UserAdminController {
|
||||||
@Post('delete')
|
@Post('delete')
|
||||||
@Returns(UserDeleteResponse)
|
@Returns(UserDeleteResponse)
|
||||||
async delete(@Body() body: UserDeleteRequest): Promise<UserDeleteResponse> {
|
async delete(@Body() body: UserDeleteRequest): Promise<UserDeleteResponse> {
|
||||||
const user = await this.usersService.delete(body.id);
|
const user = ThrowIfFailed(await this.usersService.delete(body.id));
|
||||||
if (HasFailed(user)) {
|
|
||||||
this.logger.warn(user.getReason());
|
|
||||||
throw new InternalServerErrorException('Could not delete user');
|
|
||||||
}
|
|
||||||
|
|
||||||
return EUserBackend2EUser(user);
|
return EUserBackend2EUser(user);
|
||||||
}
|
}
|
||||||
|
@ -86,11 +76,7 @@ export class UserAdminController {
|
||||||
@Post('info')
|
@Post('info')
|
||||||
@Returns(UserInfoResponse)
|
@Returns(UserInfoResponse)
|
||||||
async getUser(@Body() body: UserInfoRequest): Promise<UserInfoResponse> {
|
async getUser(@Body() body: UserInfoRequest): Promise<UserInfoResponse> {
|
||||||
const user = await this.usersService.findOne(body.id);
|
const user = ThrowIfFailed(await this.usersService.findOne(body.id));
|
||||||
if (HasFailed(user)) {
|
|
||||||
this.logger.warn(user.getReason());
|
|
||||||
throw new InternalServerErrorException('Could not find user');
|
|
||||||
}
|
|
||||||
|
|
||||||
return EUserBackend2EUser(user);
|
return EUserBackend2EUser(user);
|
||||||
}
|
}
|
||||||
|
@ -100,26 +86,18 @@ export class UserAdminController {
|
||||||
async setPermissions(
|
async setPermissions(
|
||||||
@Body() body: UserUpdateRequest,
|
@Body() body: UserUpdateRequest,
|
||||||
): Promise<UserUpdateResponse> {
|
): Promise<UserUpdateResponse> {
|
||||||
let user = await this.usersService.findOne(body.id);
|
let user = ThrowIfFailed(await this.usersService.findOne(body.id));
|
||||||
if (HasFailed(user)) {
|
|
||||||
this.logger.warn(user.getReason());
|
|
||||||
throw new InternalServerErrorException('Could not find user');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (body.roles) {
|
if (body.roles) {
|
||||||
user = await this.usersService.setRoles(body.id, body.roles);
|
user = ThrowIfFailed(
|
||||||
if (HasFailed(user)) {
|
await this.usersService.setRoles(body.id, body.roles),
|
||||||
this.logger.warn(user.getReason());
|
);
|
||||||
throw new InternalServerErrorException('Could not update user');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (body.password) {
|
if (body.password) {
|
||||||
user = await this.usersService.updatePassword(body.id, body.password);
|
user = ThrowIfFailed(
|
||||||
if (HasFailed(user)) {
|
await this.usersService.updatePassword(body.id, body.password),
|
||||||
this.logger.warn(user.getReason());
|
);
|
||||||
throw new InternalServerErrorException('Could not update user');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return EUserBackend2EUser(user);
|
return EUserBackend2EUser(user);
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import {
|
import {
|
||||||
Body,
|
Body,
|
||||||
Controller,
|
Controller,
|
||||||
Get,
|
Get, Logger,
|
||||||
InternalServerErrorException,
|
|
||||||
Logger,
|
|
||||||
Post
|
Post
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
|
@ -14,7 +12,7 @@ import {
|
||||||
UserRegisterResponse
|
UserRegisterResponse
|
||||||
} from 'picsur-shared/dist/dto/api/user.dto';
|
} from 'picsur-shared/dist/dto/api/user.dto';
|
||||||
import type { EUser } from 'picsur-shared/dist/entities/user.entity';
|
import type { EUser } from 'picsur-shared/dist/entities/user.entity';
|
||||||
import { HasFailed } from 'picsur-shared/dist/types';
|
import { ThrowIfFailed } from 'picsur-shared/dist/types';
|
||||||
import { UsersService } from '../../../collections/user-db/user-db.service';
|
import { UsersService } from '../../../collections/user-db/user-db.service';
|
||||||
import {
|
import {
|
||||||
NoPermissions,
|
NoPermissions,
|
||||||
|
@ -40,11 +38,7 @@ export class UserController {
|
||||||
@Returns(UserLoginResponse)
|
@Returns(UserLoginResponse)
|
||||||
@UseLocalAuth(Permission.UserLogin)
|
@UseLocalAuth(Permission.UserLogin)
|
||||||
async login(@ReqUser() user: EUser): Promise<UserLoginResponse> {
|
async login(@ReqUser() user: EUser): Promise<UserLoginResponse> {
|
||||||
const jwt_token = await this.authService.createToken(user);
|
const jwt_token = ThrowIfFailed(await this.authService.createToken(user));
|
||||||
if (HasFailed(jwt_token)) {
|
|
||||||
this.logger.warn(jwt_token.getReason());
|
|
||||||
throw new InternalServerErrorException('Could not get new token');
|
|
||||||
}
|
|
||||||
|
|
||||||
return { jwt_token };
|
return { jwt_token };
|
||||||
}
|
}
|
||||||
|
@ -55,14 +49,9 @@ export class UserController {
|
||||||
async register(
|
async register(
|
||||||
@Body() register: UserRegisterRequest,
|
@Body() register: UserRegisterRequest,
|
||||||
): Promise<UserRegisterResponse> {
|
): Promise<UserRegisterResponse> {
|
||||||
const user = await this.usersService.create(
|
const user = ThrowIfFailed(
|
||||||
register.username,
|
await this.usersService.create(register.username, register.password),
|
||||||
register.password,
|
|
||||||
);
|
);
|
||||||
if (HasFailed(user)) {
|
|
||||||
this.logger.warn(user.getReason());
|
|
||||||
throw new InternalServerErrorException('Could not register user');
|
|
||||||
}
|
|
||||||
|
|
||||||
return EUserBackend2EUser(user);
|
return EUserBackend2EUser(user);
|
||||||
}
|
}
|
||||||
|
@ -71,20 +60,11 @@ export class UserController {
|
||||||
@Returns(UserMeResponse)
|
@Returns(UserMeResponse)
|
||||||
@RequiredPermissions(Permission.UserKeepLogin)
|
@RequiredPermissions(Permission.UserKeepLogin)
|
||||||
async me(@ReqUserID() userid: string): Promise<UserMeResponse> {
|
async me(@ReqUserID() userid: string): Promise<UserMeResponse> {
|
||||||
const backenduser = await this.usersService.findOne(userid);
|
const backenduser = ThrowIfFailed(await this.usersService.findOne(userid));
|
||||||
|
|
||||||
if (HasFailed(backenduser)) {
|
|
||||||
this.logger.warn(backenduser.getReason());
|
|
||||||
throw new InternalServerErrorException('Could not get user');
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = EUserBackend2EUser(backenduser);
|
const user = EUserBackend2EUser(backenduser);
|
||||||
|
|
||||||
const token = await this.authService.createToken(user);
|
const token = ThrowIfFailed(await this.authService.createToken(user));
|
||||||
if (HasFailed(token)) {
|
|
||||||
this.logger.warn(token.getReason());
|
|
||||||
throw new InternalServerErrorException('Could not get new token');
|
|
||||||
}
|
|
||||||
|
|
||||||
return { user, token };
|
return { user, token };
|
||||||
}
|
}
|
||||||
|
@ -96,11 +76,7 @@ export class UserController {
|
||||||
async refresh(
|
async refresh(
|
||||||
@ReqUserID() userid: string,
|
@ReqUserID() userid: string,
|
||||||
): Promise<UserMePermissionsResponse> {
|
): Promise<UserMePermissionsResponse> {
|
||||||
const permissions = await this.usersService.getPermissions(userid);
|
const permissions = ThrowIfFailed(await this.usersService.getPermissions(userid));
|
||||||
if (HasFailed(permissions)) {
|
|
||||||
this.logger.warn(permissions.getReason());
|
|
||||||
throw new InternalServerErrorException('Could not get permissions');
|
|
||||||
}
|
|
||||||
|
|
||||||
return { permissions };
|
return { permissions };
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import {
|
import {
|
||||||
BadRequestException,
|
|
||||||
Body,
|
Body,
|
||||||
Controller,
|
Controller,
|
||||||
InternalServerErrorException,
|
|
||||||
Logger,
|
Logger,
|
||||||
Post
|
Post
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
|
@ -14,7 +12,7 @@ import {
|
||||||
ImageUploadResponse
|
ImageUploadResponse
|
||||||
} from 'picsur-shared/dist/dto/api/image-manage.dto';
|
} from 'picsur-shared/dist/dto/api/image-manage.dto';
|
||||||
import { Permission } from 'picsur-shared/dist/dto/permissions.enum';
|
import { Permission } from 'picsur-shared/dist/dto/permissions.enum';
|
||||||
import { HasFailed } from 'picsur-shared/dist/types';
|
import { ThrowIfFailed } from 'picsur-shared/dist/types';
|
||||||
import { MultiPart } from '../../decorators/multipart/multipart.decorator';
|
import { MultiPart } from '../../decorators/multipart/multipart.decorator';
|
||||||
import {
|
import {
|
||||||
HasPermission,
|
HasPermission,
|
||||||
|
@ -38,14 +36,9 @@ export class ImageManageController {
|
||||||
@MultiPart() multipart: ImageUploadDto,
|
@MultiPart() multipart: ImageUploadDto,
|
||||||
@ReqUserID() userid: string,
|
@ReqUserID() userid: string,
|
||||||
): Promise<ImageUploadResponse> {
|
): Promise<ImageUploadResponse> {
|
||||||
const image = await this.imagesService.upload(
|
const image = ThrowIfFailed(
|
||||||
multipart.image.buffer,
|
await this.imagesService.upload(multipart.image.buffer, userid),
|
||||||
userid,
|
|
||||||
);
|
);
|
||||||
if (HasFailed(image)) {
|
|
||||||
this.logger.warn(image.getReason(), image.getStack());
|
|
||||||
throw new InternalServerErrorException('Could not upload image');
|
|
||||||
}
|
|
||||||
|
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
@ -61,15 +54,9 @@ export class ImageManageController {
|
||||||
body.user_id = userid;
|
body.user_id = userid;
|
||||||
}
|
}
|
||||||
|
|
||||||
const found = await this.imagesService.findMany(
|
const found = ThrowIfFailed(
|
||||||
body.count,
|
await this.imagesService.findMany(body.count, body.page, body.user_id),
|
||||||
body.page,
|
|
||||||
body.user_id,
|
|
||||||
);
|
);
|
||||||
if (HasFailed(found)) {
|
|
||||||
this.logger.warn(found.getReason());
|
|
||||||
throw new InternalServerErrorException('Could not list images');
|
|
||||||
}
|
|
||||||
|
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
@ -81,14 +68,12 @@ export class ImageManageController {
|
||||||
@ReqUserID() userid: string,
|
@ReqUserID() userid: string,
|
||||||
@HasPermission(Permission.ImageAdmin) isImageAdmin: boolean,
|
@HasPermission(Permission.ImageAdmin) isImageAdmin: boolean,
|
||||||
): Promise<ImageDeleteResponse> {
|
): Promise<ImageDeleteResponse> {
|
||||||
const deletedImages = await this.imagesService.deleteMany(
|
const deletedImages = ThrowIfFailed(
|
||||||
|
await this.imagesService.deleteMany(
|
||||||
body.ids,
|
body.ids,
|
||||||
isImageAdmin ? undefined : userid,
|
isImageAdmin ? undefined : userid,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
if (HasFailed(deletedImages)) {
|
|
||||||
this.logger.warn(deletedImages.getReason());
|
|
||||||
throw new BadRequestException('Could not delete images');
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
images: deletedImages,
|
images: deletedImages,
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
import {
|
import {
|
||||||
Controller,
|
Controller,
|
||||||
Get,
|
Get,
|
||||||
Head,
|
Head, Logger, Query,
|
||||||
InternalServerErrorException,
|
|
||||||
Logger,
|
|
||||||
NotFoundException,
|
|
||||||
Query,
|
|
||||||
Res
|
Res
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import type { FastifyReply } from 'fastify';
|
import type { FastifyReply } from 'fastify';
|
||||||
|
@ -13,7 +9,7 @@ import {
|
||||||
ImageMetaResponse,
|
ImageMetaResponse,
|
||||||
ImageRequestParams
|
ImageRequestParams
|
||||||
} from 'picsur-shared/dist/dto/api/image.dto';
|
} from 'picsur-shared/dist/dto/api/image.dto';
|
||||||
import { HasFailed } from 'picsur-shared/dist/types';
|
import { ThrowIfFailed } from 'picsur-shared/dist/types';
|
||||||
import { UsersService } from '../../collections/user-db/user-db.service';
|
import { UsersService } from '../../collections/user-db/user-db.service';
|
||||||
import { ImageFullIdParam } from '../../decorators/image-id/image-full-id.decorator';
|
import { ImageFullIdParam } from '../../decorators/image-id/image-full-id.decorator';
|
||||||
import { ImageIdParam } from '../../decorators/image-id/image-id.decorator';
|
import { ImageIdParam } from '../../decorators/image-id/image-id.decorator';
|
||||||
|
@ -41,11 +37,9 @@ export class ImageController {
|
||||||
@ImageFullIdParam() fullid: ImageFullId,
|
@ImageFullIdParam() fullid: ImageFullId,
|
||||||
) {
|
) {
|
||||||
if (fullid.type === 'original') {
|
if (fullid.type === 'original') {
|
||||||
const fullmime = await this.imagesService.getOriginalMime(fullid.id);
|
const fullmime = ThrowIfFailed(
|
||||||
if (HasFailed(fullmime)) {
|
await this.imagesService.getOriginalMime(fullid.id),
|
||||||
this.logger.warn(fullmime.getReason());
|
);
|
||||||
throw new NotFoundException('Could not find image');
|
|
||||||
}
|
|
||||||
|
|
||||||
res.type(fullmime.mime);
|
res.type(fullmime.mime);
|
||||||
return;
|
return;
|
||||||
|
@ -63,25 +57,17 @@ export class ImageController {
|
||||||
@Query() params: ImageRequestParams,
|
@Query() params: ImageRequestParams,
|
||||||
): Promise<Buffer> {
|
): Promise<Buffer> {
|
||||||
if (fullid.type === 'original') {
|
if (fullid.type === 'original') {
|
||||||
const image = await this.imagesService.getOriginal(fullid.id);
|
const image = ThrowIfFailed(
|
||||||
if (HasFailed(image)) {
|
await this.imagesService.getOriginal(fullid.id),
|
||||||
this.logger.warn(image.getReason());
|
);
|
||||||
throw new NotFoundException('Could not find image');
|
|
||||||
}
|
|
||||||
|
|
||||||
res.type(image.mime);
|
res.type(image.mime);
|
||||||
return image.data;
|
return image.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
const image = await this.imagesService.getConverted(
|
const image = ThrowIfFailed(
|
||||||
fullid.id,
|
await this.imagesService.getConverted(fullid.id, fullid.mime, params),
|
||||||
fullid.mime,
|
|
||||||
params,
|
|
||||||
);
|
);
|
||||||
if (HasFailed(image)) {
|
|
||||||
this.logger.warn(image.getReason());
|
|
||||||
throw new NotFoundException('Failed to get image');
|
|
||||||
}
|
|
||||||
|
|
||||||
res.type(image.mime);
|
res.type(image.mime);
|
||||||
return image.data;
|
return image.data;
|
||||||
|
@ -90,24 +76,15 @@ export class ImageController {
|
||||||
@Get('meta/:id')
|
@Get('meta/:id')
|
||||||
@Returns(ImageMetaResponse)
|
@Returns(ImageMetaResponse)
|
||||||
async getImageMeta(@ImageIdParam() id: string): Promise<ImageMetaResponse> {
|
async getImageMeta(@ImageIdParam() id: string): Promise<ImageMetaResponse> {
|
||||||
const image = await this.imagesService.findOne(id);
|
const image = ThrowIfFailed(await this.imagesService.findOne(id));
|
||||||
if (HasFailed(image)) {
|
|
||||||
this.logger.warn(image.getReason());
|
|
||||||
throw new NotFoundException('Could not find image');
|
|
||||||
}
|
|
||||||
|
|
||||||
const [fileMimes, imageUser] = await Promise.all([
|
const [fileMimesRes, imageUserRes] = await Promise.all([
|
||||||
this.imagesService.getFileMimes(id),
|
this.imagesService.getFileMimes(id),
|
||||||
this.userService.findOne(image.user_id),
|
this.userService.findOne(image.user_id),
|
||||||
]);
|
])
|
||||||
if (HasFailed(fileMimes)) {
|
|
||||||
this.logger.warn(fileMimes.getReason());
|
const fileMimes = ThrowIfFailed(fileMimesRes);
|
||||||
throw new InternalServerErrorException('Could not get image mime');
|
const imageUser = ThrowIfFailed(imageUserRes);
|
||||||
}
|
|
||||||
if (HasFailed(imageUser)) {
|
|
||||||
this.logger.warn(imageUser.getReason());
|
|
||||||
throw new InternalServerErrorException('Could not get image user');
|
|
||||||
}
|
|
||||||
|
|
||||||
return { image, user: EUserBackend2EUser(imageUser), fileMimes };
|
return { image, user: EUserBackend2EUser(imageUser), fileMimes };
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import z from 'zod';
|
import z from 'zod';
|
||||||
|
|
||||||
const ApiResponseBase = z.object({
|
const ApiResponseBase = z.object({
|
||||||
statusCode: z.number().min(0).max(600),
|
statusCode: z.number().min(0).max(600).int(),
|
||||||
timestamp: z.string(),
|
timestamp: z.string(),
|
||||||
|
timeMs: z.number().min(0).int(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const ApiSuccessResponse = <T extends z.AnyZodObject>(data: T) =>
|
const ApiSuccessResponse = <T extends z.AnyZodObject>(data: T) =>
|
||||||
|
|
|
@ -134,34 +134,51 @@ export class Failure {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Fail(type: FT, reason?: any, dbgReason?: any): Failure {
|
export function Fail(type: FT, reason?: any, dbgReason?: any): Failure {
|
||||||
const strReason = reason.toString();
|
if (IsFailure(reason) || IsFailure(dbgReason)) {
|
||||||
|
throw new Error('Cannot fail with another failure, just return it');
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof dbgReason === 'string') {
|
if (dbgReason === undefined || dbgReason === null) {
|
||||||
return new Failure(type, strReason, undefined, dbgReason);
|
if (reason === undefined || reason === null) {
|
||||||
} else if (dbgReason instanceof Error) {
|
// If both are null, just return a default error message
|
||||||
return new Failure(type, strReason, dbgReason.stack, dbgReason.message);
|
return new Failure(type, FTProps[type].message, undefined, undefined);
|
||||||
} else if (dbgReason instanceof Failure) {
|
} else if (typeof reason === 'string') {
|
||||||
throw new Error('Cannot fail with a failure, just return it');
|
// If it is a string, this was intentionally specified, so pass it through
|
||||||
} else {
|
return new Failure(type, reason, undefined, undefined);
|
||||||
if (typeof reason === 'string') {
|
|
||||||
return new Failure(type, strReason, undefined, undefined);
|
|
||||||
} else if (reason instanceof Error) {
|
} else if (reason instanceof Error) {
|
||||||
|
// In case of an error, we want to keep that hidden, so return the default message
|
||||||
|
// Only send the specifics to debug
|
||||||
return new Failure(
|
return new Failure(
|
||||||
type,
|
type,
|
||||||
FTProps[type].message,
|
FTProps[type].message,
|
||||||
reason.stack,
|
reason.stack,
|
||||||
reason.message,
|
reason.message,
|
||||||
);
|
);
|
||||||
} else if (dbgReason instanceof Failure) {
|
|
||||||
throw new Error('Cannot fail with a failure, just return it');
|
|
||||||
} else {
|
} else {
|
||||||
return new Failure(type, FTProps[type].message, undefined, undefined);
|
// No clue what it is, so just transform it to a string and return the default message
|
||||||
|
return new Failure(
|
||||||
|
type,
|
||||||
|
FTProps[type].message,
|
||||||
|
undefined,
|
||||||
|
String(reason),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// In this case we only accept strings for the reason
|
||||||
|
const strReason = reason?.toString() ?? FTProps[type].message;
|
||||||
|
|
||||||
|
if (typeof dbgReason === 'string') {
|
||||||
|
return new Failure(type, strReason, undefined, dbgReason);
|
||||||
|
} else if (dbgReason instanceof Error) {
|
||||||
|
return new Failure(type, strReason, dbgReason.stack, dbgReason.message);
|
||||||
|
} else {
|
||||||
|
return new Failure(type, strReason, undefined, String(dbgReason));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function IsFailure(value: any): value is Failure {
|
export function IsFailure(value: any): value is Failure {
|
||||||
return value.__68351953531423479708__id_failure === 1148363914;
|
return value?.__68351953531423479708__id_failure === 1148363914;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Failable<T> = T | Failure;
|
export type Failable<T> = T | Failure;
|
||||||
|
@ -178,6 +195,14 @@ export function HasSuccess<T>(failable: Failable<T>): failable is T {
|
||||||
return (failable as any).__68351953531423479708__id_failure !== 1148363914;
|
return (failable as any).__68351953531423479708__id_failure !== 1148363914;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ThrowIfFailed<V>(failable: Failable<V>): V {
|
||||||
|
if (HasFailed(failable)) {
|
||||||
|
throw failable;
|
||||||
|
}
|
||||||
|
|
||||||
|
return failable;
|
||||||
|
}
|
||||||
|
|
||||||
export function Map<T, U>(
|
export function Map<T, U>(
|
||||||
failable: Failable<T>,
|
failable: Failable<T>,
|
||||||
mapper: (value: T) => U,
|
mapper: (value: T) => U,
|
||||||
|
|
Loading…
Reference in a new issue