mirror of
https://github.com/CaramelFur/Picsur.git
synced 2025-11-12 14:55:39 +01:00
clean up validation, still not happy
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { plainToClass } from 'class-transformer';
|
||||
import Crypto from 'crypto';
|
||||
import {
|
||||
AsyncFailable,
|
||||
@@ -36,8 +35,7 @@ export class ImageDBService {
|
||||
return Fail(e?.message);
|
||||
}
|
||||
|
||||
// Strips unwanted data
|
||||
return plainToClass(EImageBackend, imageEntity);
|
||||
return imageEntity;
|
||||
}
|
||||
|
||||
public async findOne<B extends true | undefined = undefined>(
|
||||
|
||||
@@ -52,10 +52,7 @@ export class RolesService {
|
||||
}
|
||||
|
||||
try {
|
||||
// Makes sure we can return the id
|
||||
const cloned = plainToClass(ERoleBackend, roleToModify);
|
||||
await this.rolesRepository.remove(roleToModify);
|
||||
return cloned;
|
||||
return await this.rolesRepository.remove(roleToModify);
|
||||
} catch (e: any) {
|
||||
return Fail(e?.message);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { PrefValueType, PrefValueTypeStrings } from 'picsur-shared/dist/dto/preferences.dto';
|
||||
import {
|
||||
InternalSysPrefRepresentation,
|
||||
SysPreference
|
||||
} from 'picsur-shared/dist/dto/syspreferences.dto';
|
||||
DecodedSysPref,
|
||||
PrefValueType,
|
||||
PrefValueTypeStrings
|
||||
} from 'picsur-shared/dist/dto/preferences.dto';
|
||||
import { SysPreference } from 'picsur-shared/dist/dto/syspreferences.dto';
|
||||
import {
|
||||
AsyncFailable,
|
||||
Fail,
|
||||
@@ -33,7 +34,7 @@ export class SysPreferenceService {
|
||||
public async setPreference(
|
||||
key: string,
|
||||
value: PrefValueType,
|
||||
): AsyncFailable<InternalSysPrefRepresentation> {
|
||||
): AsyncFailable<DecodedSysPref> {
|
||||
// Validate
|
||||
let sysPreference = await this.validatePref(key, value);
|
||||
if (HasFailed(sysPreference)) return sysPreference;
|
||||
@@ -58,9 +59,7 @@ export class SysPreferenceService {
|
||||
};
|
||||
}
|
||||
|
||||
public async getPreference(
|
||||
key: string,
|
||||
): AsyncFailable<InternalSysPrefRepresentation> {
|
||||
public async getPreference(key: string): AsyncFailable<DecodedSysPref> {
|
||||
// Validate
|
||||
let validatedKey = this.validatePrefKey(key);
|
||||
if (HasFailed(validatedKey)) return validatedKey;
|
||||
@@ -116,9 +115,7 @@ export class SysPreferenceService {
|
||||
return pref.value;
|
||||
}
|
||||
|
||||
public async getAllPreferences(): AsyncFailable<
|
||||
InternalSysPrefRepresentation[]
|
||||
> {
|
||||
public async getAllPreferences(): AsyncFailable<DecodedSysPref[]> {
|
||||
// TODO: We are fetching each value invidually, we should fetch all at once
|
||||
let internalSysPrefs = await Promise.all(
|
||||
SysPreferenceList.map((key) => this.getPreference(key)),
|
||||
@@ -127,21 +124,21 @@ export class SysPreferenceService {
|
||||
return Fail('Could not get all preferences');
|
||||
}
|
||||
|
||||
return internalSysPrefs as InternalSysPrefRepresentation[];
|
||||
return internalSysPrefs as DecodedSysPref[];
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
private async saveDefault(
|
||||
key: SysPreference, // Force enum here because we dont validate
|
||||
): AsyncFailable<InternalSysPrefRepresentation> {
|
||||
): AsyncFailable<DecodedSysPref> {
|
||||
return this.setPreference(key, this.defaultsService.sysDefaults[key]());
|
||||
}
|
||||
|
||||
// This converts the raw string representation of the value to the correct type
|
||||
private retrieveConvertedValue(
|
||||
preference: ESysPreferenceBackend,
|
||||
): Failable<InternalSysPrefRepresentation> {
|
||||
): Failable<DecodedSysPref> {
|
||||
const key = this.validatePrefKey(preference.key);
|
||||
if (HasFailed(key)) return key;
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ export class UsersService {
|
||||
|
||||
let user = new EUserBackend();
|
||||
user.username = username;
|
||||
user.password = hashedPassword;
|
||||
user.hashedPassword = hashedPassword;
|
||||
if (byPassRoleCheck) {
|
||||
const rolesToAdd = roles ?? [];
|
||||
user.roles = makeUnique(rolesToAdd);
|
||||
@@ -64,13 +64,10 @@ export class UsersService {
|
||||
}
|
||||
|
||||
try {
|
||||
user = await this.usersRepository.save(user, { reload: true });
|
||||
return await this.usersRepository.save(user);
|
||||
} catch (e: any) {
|
||||
return Fail(e?.message);
|
||||
}
|
||||
|
||||
// Strips unwanted data
|
||||
return plainToClass(EUserBackend, user);
|
||||
}
|
||||
|
||||
public async delete(uuid: string): AsyncFailable<EUserBackend> {
|
||||
@@ -153,8 +150,7 @@ export class UsersService {
|
||||
if (HasFailed(userToModify)) return userToModify;
|
||||
|
||||
const strength = await this.getBCryptStrength();
|
||||
const hashedPassword = await bcrypt.hash(password, strength);
|
||||
userToModify.password = hashedPassword;
|
||||
userToModify.hashedPassword = await bcrypt.hash(password, strength);
|
||||
|
||||
try {
|
||||
userToModify = await this.usersRepository.save(userToModify);
|
||||
@@ -180,7 +176,7 @@ export class UsersService {
|
||||
return Fail('Wrong username');
|
||||
}
|
||||
|
||||
if (!(await bcrypt.compare(password, user.password)))
|
||||
if (!(await bcrypt.compare(password, user.hashedPassword)))
|
||||
return Fail('Wrong password');
|
||||
|
||||
return await this.findOne(user.id);
|
||||
@@ -199,7 +195,11 @@ export class UsersService {
|
||||
try {
|
||||
const found = await this.usersRepository.findOne({
|
||||
where: { username },
|
||||
select: getPrivate ? GetCols(this.usersRepository) : undefined,
|
||||
...(getPrivate
|
||||
? {
|
||||
select: GetCols(this.usersRepository),
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
|
||||
if (!found) return Fail('User not found');
|
||||
|
||||
@@ -41,6 +41,10 @@ export class MainAuthGuard extends AuthGuard(['jwt', 'guest']) {
|
||||
const user = await this.validateUser(
|
||||
context.switchToHttp().getRequest().user,
|
||||
);
|
||||
if (!user.id) {
|
||||
this.logger.error('User has no id, this should not happen');
|
||||
throw new InternalServerErrorException();
|
||||
}
|
||||
|
||||
// These are the permissions required to access the route
|
||||
const permissions = this.extractPermissions(context);
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
import { IsNotEmpty, IsOptional } from 'class-validator';
|
||||
import { EImage } from 'picsur-shared/dist/entities/image.entity';
|
||||
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
|
||||
|
||||
@Entity()
|
||||
export class EImageBackend extends EImage {
|
||||
@PrimaryGeneratedColumn("uuid")
|
||||
override id: string;
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
override id?: string;
|
||||
|
||||
@Index()
|
||||
@Column({ unique: true, nullable: false })
|
||||
override hash: string;
|
||||
|
||||
// Binary data
|
||||
@Column({ type: 'bytea', nullable: false, select: false })
|
||||
override data?: Buffer;
|
||||
|
||||
@Column({ nullable: false })
|
||||
override mime: string;
|
||||
|
||||
// Binary data
|
||||
@Column({ type: 'bytea', nullable: false, select: false })
|
||||
@IsOptional()
|
||||
@IsNotEmpty()
|
||||
// @ts-ignore
|
||||
override data?: Buffer;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Permissions } from '../dto/permissions.dto';
|
||||
@Entity()
|
||||
export class ERoleBackend extends ERole {
|
||||
@PrimaryGeneratedColumn("uuid")
|
||||
override id: string;
|
||||
override id?: string;
|
||||
|
||||
@Index()
|
||||
@Column({ nullable: false, unique: true })
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { IsOptional, IsString } from 'class-validator';
|
||||
import { EUser } from 'picsur-shared/dist/entities/user.entity';
|
||||
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
|
||||
|
||||
@@ -6,7 +7,7 @@ import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
|
||||
@Entity()
|
||||
export class EUserBackend extends EUser {
|
||||
@PrimaryGeneratedColumn("uuid")
|
||||
override id: string;
|
||||
override id?: string;
|
||||
|
||||
@Index()
|
||||
@Column({ nullable: false, unique: true })
|
||||
@@ -16,5 +17,9 @@ export class EUserBackend extends EUser {
|
||||
override roles: string[];
|
||||
|
||||
@Column({ nullable: false, select: false })
|
||||
override password?: string;
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
// @ts-ignore
|
||||
override hashedPassword?: string;
|
||||
}
|
||||
|
||||
|
||||
14
backend/src/models/transformers/image.transformer.ts
Normal file
14
backend/src/models/transformers/image.transformer.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { EImage } from 'picsur-shared/dist/entities/image.entity';
|
||||
import { EImageBackend } from '../entities/image.entity';
|
||||
|
||||
export function EImageBackend2EImage(
|
||||
eImage: EImageBackend,
|
||||
): EImage {
|
||||
if (eImage.data === undefined)
|
||||
return eImage as EImage;
|
||||
|
||||
return {
|
||||
...eImage,
|
||||
data: undefined,
|
||||
};
|
||||
}
|
||||
14
backend/src/models/transformers/user.transformer.ts
Normal file
14
backend/src/models/transformers/user.transformer.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { EUser } from 'picsur-shared/dist/entities/user.entity';
|
||||
import { EUserBackend } from '../entities/user.entity';
|
||||
|
||||
export function EUserBackend2EUser(
|
||||
eUser: EUserBackend,
|
||||
): EUser {
|
||||
if (eUser.hashedPassword === undefined)
|
||||
return eUser as EUser;
|
||||
|
||||
return {
|
||||
...eUser,
|
||||
hashedPassword: undefined,
|
||||
};
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
import { AuthManagerService } from '../../../managers/auth/auth.service';
|
||||
import { Permission } from '../../../models/dto/permissions.dto';
|
||||
import AuthFasityRequest from '../../../models/requests/authrequest.dto';
|
||||
import { EUserBackend2EUser } from '../../../models/transformers/user.transformer';
|
||||
|
||||
@Controller('api/user')
|
||||
export class UserController {
|
||||
@@ -60,12 +61,14 @@ export class UserController {
|
||||
throw new InternalServerErrorException('Could not register user');
|
||||
}
|
||||
|
||||
return user;
|
||||
return EUserBackend2EUser(user);
|
||||
}
|
||||
|
||||
@Get('me')
|
||||
@RequiredPermissions(Permission.UserKeepLogin)
|
||||
async me(@Request() req: AuthFasityRequest): Promise<UserMeResponse> {
|
||||
if (!req.user.id) throw new InternalServerErrorException('User is corrupt');
|
||||
|
||||
const user = await this.usersService.findOne(req.user.id);
|
||||
|
||||
if (HasFailed(user)) {
|
||||
@@ -79,7 +82,7 @@ export class UserController {
|
||||
throw new InternalServerErrorException('Could not get new token');
|
||||
}
|
||||
|
||||
return { user, token };
|
||||
return { user: EUserBackend2EUser(user), token };
|
||||
}
|
||||
|
||||
// You can always check your permissions
|
||||
@@ -88,6 +91,8 @@ export class UserController {
|
||||
async refresh(
|
||||
@Request() req: AuthFasityRequest,
|
||||
): Promise<UserMePermissionsResponse> {
|
||||
if (!req.user.id) throw new InternalServerErrorException('User is corrupt');
|
||||
|
||||
const permissions = await this.usersService.getPermissions(req.user.id);
|
||||
if (HasFailed(permissions)) {
|
||||
this.logger.warn(permissions.getReason());
|
||||
|
||||
@@ -24,7 +24,12 @@ import { HasFailed } from 'picsur-shared/dist/types';
|
||||
import { UsersService } from '../../../collections/userdb/userdb.service';
|
||||
import { RequiredPermissions } from '../../../decorators/permissions.decorator';
|
||||
import { Permission } from '../../../models/dto/permissions.dto';
|
||||
import { ImmutableUsersList, LockedLoginUsersList, UndeletableUsersList } from '../../../models/dto/specialusers.dto';
|
||||
import {
|
||||
ImmutableUsersList,
|
||||
LockedLoginUsersList,
|
||||
UndeletableUsersList
|
||||
} from '../../../models/dto/specialusers.dto';
|
||||
import { EUserBackend2EUser } from '../../../models/transformers/user.transformer';
|
||||
|
||||
@Controller('api/user')
|
||||
@RequiredPermissions(Permission.UserManage)
|
||||
@@ -53,7 +58,7 @@ export class UserManageController {
|
||||
}
|
||||
|
||||
return {
|
||||
users,
|
||||
users: users.map(EUserBackend2EUser),
|
||||
count: users.length,
|
||||
page: body.page,
|
||||
};
|
||||
@@ -73,20 +78,18 @@ export class UserManageController {
|
||||
throw new InternalServerErrorException('Could not create user');
|
||||
}
|
||||
|
||||
return user;
|
||||
return EUserBackend2EUser(user);
|
||||
}
|
||||
|
||||
@Post('delete')
|
||||
async delete(
|
||||
@Body() body: UserDeleteRequest,
|
||||
): Promise<UserDeleteResponse> {
|
||||
async delete(@Body() body: UserDeleteRequest): Promise<UserDeleteResponse> {
|
||||
const user = await this.usersService.delete(body.id);
|
||||
if (HasFailed(user)) {
|
||||
this.logger.warn(user.getReason());
|
||||
throw new InternalServerErrorException('Could not delete user');
|
||||
}
|
||||
|
||||
return user;
|
||||
return EUserBackend2EUser(user);
|
||||
}
|
||||
|
||||
@Post('info')
|
||||
@@ -97,7 +100,7 @@ export class UserManageController {
|
||||
throw new InternalServerErrorException('Could not find user');
|
||||
}
|
||||
|
||||
return user;
|
||||
return EUserBackend2EUser(user);
|
||||
}
|
||||
|
||||
@Post('update')
|
||||
@@ -111,7 +114,7 @@ export class UserManageController {
|
||||
}
|
||||
|
||||
if (body.roles) {
|
||||
user = await this.usersService.setRoles(user.id, body.roles);
|
||||
user = await this.usersService.setRoles(body.id, body.roles);
|
||||
if (HasFailed(user)) {
|
||||
this.logger.warn(user.getReason());
|
||||
throw new InternalServerErrorException('Could not update user');
|
||||
@@ -119,14 +122,14 @@ export class UserManageController {
|
||||
}
|
||||
|
||||
if (body.password) {
|
||||
user = await this.usersService.updatePassword(user.id, body.password);
|
||||
user = await this.usersService.updatePassword(body.id, body.password);
|
||||
if (HasFailed(user)) {
|
||||
this.logger.warn(user.getReason());
|
||||
throw new InternalServerErrorException('Could not update user');
|
||||
}
|
||||
}
|
||||
|
||||
return user;
|
||||
return EUserBackend2EUser(user);
|
||||
}
|
||||
|
||||
@Get('special')
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
InternalServerErrorException,
|
||||
Logger,
|
||||
NotFoundException,
|
||||
Param,
|
||||
Post, Res
|
||||
Controller,
|
||||
Get,
|
||||
InternalServerErrorException,
|
||||
Logger,
|
||||
NotFoundException,
|
||||
Param,
|
||||
Post,
|
||||
Res
|
||||
} from '@nestjs/common';
|
||||
import { FastifyReply } from 'fastify';
|
||||
import { ImageMetaResponse } from 'picsur-shared/dist/dto/api/image.dto';
|
||||
@@ -15,6 +16,7 @@ import { RequiredPermissions } from '../../decorators/permissions.decorator';
|
||||
import { ImageManagerService } from '../../managers/imagemanager/imagemanager.service';
|
||||
import { Permission } from '../../models/dto/permissions.dto';
|
||||
import { ImageUploadDto } from '../../models/requests/imageroute.dto';
|
||||
import { EImageBackend2EImage } from '../../models/transformers/image.transformer';
|
||||
import { ImageIdValidator } from './imageid.validator';
|
||||
|
||||
// This is the only controller with CORS enabled
|
||||
@@ -52,7 +54,7 @@ export class ImageController {
|
||||
throw new NotFoundException('Could not find image');
|
||||
}
|
||||
|
||||
return image;
|
||||
return EImageBackend2EImage(image);
|
||||
}
|
||||
|
||||
@Post()
|
||||
@@ -67,6 +69,6 @@ export class ImageController {
|
||||
throw new InternalServerErrorException('Could not upload image');
|
||||
}
|
||||
|
||||
return image;
|
||||
return EImageBackend2EImage(image);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { SysPreferenceBaseResponse } from 'picsur-shared/dist/dto/api/syspref.dto';
|
||||
import { DecodedSysPref } from 'picsur-shared/dist/dto/preferences.dto';
|
||||
import { Observable } from 'rxjs';
|
||||
import { SysprefService as SysPrefService } from 'src/app/services/api/syspref.service';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { SysprefService as SysPrefService } from 'src/app/services/api/syspref.s
|
||||
templateUrl: './settings-syspref.component.html',
|
||||
})
|
||||
export class SettingsSysprefComponent {
|
||||
preferences: Observable<SysPreferenceBaseResponse[]>;
|
||||
preferences: Observable<DecodedSysPref[]>;
|
||||
|
||||
constructor(sysprefService: SysPrefService) {
|
||||
this.preferences = sysprefService.live;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
|
||||
import { SysPreferenceBaseResponse } from 'picsur-shared/dist/dto/api/syspref.dto';
|
||||
import { PrefValueType } from 'picsur-shared/dist/dto/preferences.dto';
|
||||
import { DecodedSysPref, PrefValueType } from 'picsur-shared/dist/dto/preferences.dto';
|
||||
import { SysPreference } from 'picsur-shared/dist/dto/syspreferences.dto';
|
||||
import { HasFailed } from 'picsur-shared/dist/types';
|
||||
import { Subject, throttleTime } from 'rxjs';
|
||||
@@ -16,7 +15,7 @@ import { UtilService } from 'src/app/util/util.service';
|
||||
styleUrls: ['./settings-syspref-option.component.scss'],
|
||||
})
|
||||
export class SettingsSysprefOptionComponent implements OnInit {
|
||||
@Input() pref: SysPreferenceBaseResponse;
|
||||
@Input() pref: DecodedSysPref;
|
||||
|
||||
private updateSubject = new Subject<PrefValueType>();
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ export class SettingsUsersComponent implements OnInit {
|
||||
});
|
||||
|
||||
if (pressedButton === 'delete') {
|
||||
const result = await this.userManageService.deleteUser(user.id);
|
||||
const result = await this.userManageService.deleteUser(user.id ?? '');
|
||||
if (HasFailed(result)) {
|
||||
this.utilService.showSnackBar(
|
||||
'Failed to delete user',
|
||||
|
||||
@@ -3,12 +3,11 @@ import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
|
||||
import {
|
||||
GetSyspreferenceResponse,
|
||||
MultipleSysPreferencesResponse,
|
||||
SysPreferenceBaseResponse,
|
||||
UpdateSysPreferenceRequest,
|
||||
UpdateSysPreferenceResponse
|
||||
} from 'picsur-shared/dist/dto/api/syspref.dto';
|
||||
import { Permission } from 'picsur-shared/dist/dto/permissions.dto';
|
||||
import { PrefValueType } from 'picsur-shared/dist/dto/preferences.dto';
|
||||
import { DecodedSysPref, PrefValueType } from 'picsur-shared/dist/dto/preferences.dto';
|
||||
import { AsyncFailable, Fail, HasFailed, Map } from 'picsur-shared/dist/types';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
|
||||
@@ -25,7 +24,7 @@ export class SysprefService {
|
||||
|
||||
private hasPermission = false;
|
||||
|
||||
private sysprefObservable = new BehaviorSubject<SysPreferenceBaseResponse[]>(
|
||||
private sysprefObservable = new BehaviorSubject<DecodedSysPref[]>(
|
||||
[]
|
||||
);
|
||||
|
||||
@@ -57,7 +56,7 @@ export class SysprefService {
|
||||
}
|
||||
}
|
||||
|
||||
public async getPreferences(): AsyncFailable<SysPreferenceBaseResponse[]> {
|
||||
public async getPreferences(): AsyncFailable<DecodedSysPref[]> {
|
||||
if (!this.hasPermission)
|
||||
return Fail('You do not have permission to edit system preferences');
|
||||
|
||||
@@ -105,7 +104,7 @@ export class SysprefService {
|
||||
return response;
|
||||
}
|
||||
|
||||
private updatePrefArray(pref: SysPreferenceBaseResponse) {
|
||||
private updatePrefArray(pref: DecodedSysPref) {
|
||||
const prefArray = this.snapshot;
|
||||
// Replace the old pref with the new one
|
||||
const index = prefArray.findIndex((i) => pref.key === i.key);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import {
|
||||
IsBoolean,
|
||||
IsDefined,
|
||||
IsInt,
|
||||
IsBoolean, IsInt,
|
||||
IsNotEmpty,
|
||||
IsString,
|
||||
Max,
|
||||
@@ -10,21 +8,17 @@ import {
|
||||
|
||||
class BaseApiResponse<T extends Object, W extends boolean> {
|
||||
@IsBoolean()
|
||||
@IsDefined()
|
||||
success: W;
|
||||
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@Max(1000)
|
||||
@IsDefined()
|
||||
statusCode: number;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
timestamp: string;
|
||||
|
||||
//@ValidateNested()
|
||||
@IsDefined()
|
||||
@IsNotEmpty()
|
||||
data: T;
|
||||
}
|
||||
|
||||
@@ -35,7 +29,6 @@ export class ApiSuccessResponse<T extends Object> extends BaseApiResponse<
|
||||
|
||||
export class ApiErrorData {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
message: string;
|
||||
}
|
||||
export class ApiErrorResponse extends BaseApiResponse<ApiErrorData, false> {}
|
||||
|
||||
@@ -1,24 +1,19 @@
|
||||
import { IsBoolean, IsDefined, IsSemVer, IsString } from 'class-validator';
|
||||
import { IsBoolean, IsSemVer } from 'class-validator';
|
||||
import { IsStringList } from '../../validators/string-list.validator';
|
||||
|
||||
export class InfoResponse {
|
||||
@IsBoolean()
|
||||
@IsDefined()
|
||||
production: boolean;
|
||||
|
||||
@IsBoolean()
|
||||
@IsDefined()
|
||||
demo: boolean;
|
||||
|
||||
@IsDefined()
|
||||
@IsString()
|
||||
@IsSemVer()
|
||||
version: string;
|
||||
}
|
||||
|
||||
// AllPermissions
|
||||
export class AllPermissionsResponse {
|
||||
@IsDefined()
|
||||
@IsStringList()
|
||||
permissions: string[];
|
||||
}
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsArray, IsDefined, ValidateNested } from 'class-validator';
|
||||
import {
|
||||
ERole,
|
||||
RoleNameObject,
|
||||
RoleNamePermsObject
|
||||
} from '../../entities/role.entity';
|
||||
import { IsArray } from 'class-validator';
|
||||
import { ERole, SimpleRole } from '../../entities/role.entity';
|
||||
import { IsNested } from '../../validators/nested.validator';
|
||||
import { IsPosInt } from '../../validators/positive-int.validator';
|
||||
import { IsRoleName } from '../../validators/role.validators';
|
||||
import { IsStringList } from '../../validators/string-list.validator';
|
||||
|
||||
// RoleInfo
|
||||
export class RoleInfoRequest extends RoleNameObject {}
|
||||
export class RoleInfoRequest {
|
||||
@IsRoleName()
|
||||
name: string;
|
||||
}
|
||||
export class RoleInfoResponse extends ERole {}
|
||||
|
||||
// RoleList
|
||||
export class RoleListResponse {
|
||||
@IsArray()
|
||||
@IsDefined()
|
||||
@ValidateNested()
|
||||
@Type(() => ERole)
|
||||
@IsNested(ERole)
|
||||
roles: ERole[];
|
||||
|
||||
@IsPosInt()
|
||||
@@ -25,32 +23,31 @@ export class RoleListResponse {
|
||||
}
|
||||
|
||||
// RoleUpdate
|
||||
export class RoleUpdateRequest extends RoleNamePermsObject {}
|
||||
export class RoleUpdateRequest extends SimpleRole {}
|
||||
export class RoleUpdateResponse extends ERole {}
|
||||
|
||||
// RoleCreate
|
||||
export class RoleCreateRequest extends RoleNamePermsObject {}
|
||||
export class RoleCreateRequest extends SimpleRole {}
|
||||
export class RoleCreateResponse extends ERole {}
|
||||
|
||||
// RoleDelete
|
||||
export class RoleDeleteRequest extends RoleNameObject {}
|
||||
export class RoleDeleteRequest {
|
||||
@IsRoleName()
|
||||
name: string;
|
||||
}
|
||||
export class RoleDeleteResponse extends ERole {}
|
||||
|
||||
// SpecialRoles
|
||||
export class SpecialRolesResponse {
|
||||
@IsDefined()
|
||||
@IsStringList()
|
||||
SoulBoundRoles: string[];
|
||||
|
||||
@IsDefined()
|
||||
@IsStringList()
|
||||
ImmutableRoles: string[];
|
||||
|
||||
@IsDefined()
|
||||
@IsStringList()
|
||||
UndeletableRoles: string[];
|
||||
|
||||
@IsDefined()
|
||||
@IsStringList()
|
||||
DefaultRoles: string[];
|
||||
}
|
||||
|
||||
@@ -1,36 +1,21 @@
|
||||
import { Type } from 'class-transformer';
|
||||
import {
|
||||
IsArray, IsEnum, IsNotEmpty, IsString, ValidateNested
|
||||
IsArray
|
||||
} from 'class-validator';
|
||||
import { IsNested } from '../../validators/nested.validator';
|
||||
import { IsPosInt } from '../../validators/positive-int.validator';
|
||||
import { IsSysPrefValue } from '../../validators/syspref.validator';
|
||||
import { PrefValueType, PrefValueTypes, PrefValueTypeStrings } from '../preferences.dto';
|
||||
import { IsPrefValue } from '../../validators/pref-value.validator';
|
||||
import { DecodedSysPref, PrefValueType } from '../preferences.dto';
|
||||
|
||||
export class SysPreferenceBaseResponse {
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
key: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsSysPrefValue()
|
||||
value: PrefValueType;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsEnum(PrefValueTypes)
|
||||
type: PrefValueTypeStrings;
|
||||
}
|
||||
|
||||
// Get Syspreference
|
||||
// Request is done via url parameters
|
||||
export class GetSyspreferenceResponse extends SysPreferenceBaseResponse {}
|
||||
export class GetSyspreferenceResponse extends DecodedSysPref {}
|
||||
|
||||
// Get syspreferences
|
||||
export class MultipleSysPreferencesResponse {
|
||||
@IsArray()
|
||||
@IsNotEmpty()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => SysPreferenceBaseResponse)
|
||||
preferences: SysPreferenceBaseResponse[];
|
||||
@IsNested(DecodedSysPref)
|
||||
preferences: DecodedSysPref[];
|
||||
|
||||
@IsPosInt()
|
||||
total: number;
|
||||
@@ -38,10 +23,9 @@ export class MultipleSysPreferencesResponse {
|
||||
|
||||
// Update Syspreference
|
||||
export class UpdateSysPreferenceRequest {
|
||||
@IsNotEmpty()
|
||||
@IsSysPrefValue()
|
||||
@IsPrefValue()
|
||||
value: PrefValueType;
|
||||
}
|
||||
export class UpdateSysPreferenceResponse extends SysPreferenceBaseResponse {}
|
||||
export class UpdateSysPreferenceResponse extends DecodedSysPref {}
|
||||
|
||||
|
||||
|
||||
@@ -1,45 +1,47 @@
|
||||
import { Type } from 'class-transformer';
|
||||
import {
|
||||
IsDefined, IsJWT,
|
||||
IsString,
|
||||
ValidateNested
|
||||
IsJWT
|
||||
} from 'class-validator';
|
||||
import { EUser, NamePassUser } from '../../entities/user.entity';
|
||||
import { EUser } from '../../entities/user.entity';
|
||||
import { IsNested } from '../../validators/nested.validator';
|
||||
import { IsStringList } from '../../validators/string-list.validator';
|
||||
import { IsPlainTextPwd, IsUsername } from '../../validators/user.validators';
|
||||
|
||||
// Api
|
||||
|
||||
// UserLogin
|
||||
export class UserLoginRequest extends NamePassUser {}
|
||||
export class UserLoginRequest {
|
||||
@IsUsername()
|
||||
username: string;
|
||||
|
||||
@IsPlainTextPwd()
|
||||
password: string;
|
||||
}
|
||||
export class UserLoginResponse {
|
||||
@IsString()
|
||||
@IsDefined()
|
||||
@IsJWT()
|
||||
jwt_token: string;
|
||||
}
|
||||
|
||||
// UserRegister
|
||||
export class UserRegisterRequest extends NamePassUser {}
|
||||
export class UserRegisterRequest {
|
||||
@IsUsername()
|
||||
username: string;
|
||||
|
||||
@IsPlainTextPwd()
|
||||
password: string;
|
||||
}
|
||||
export class UserRegisterResponse extends EUser {}
|
||||
|
||||
// UserMe
|
||||
export class UserMeResponse {
|
||||
@IsDefined()
|
||||
@ValidateNested()
|
||||
@Type(() => EUser)
|
||||
@IsNested(EUser)
|
||||
user: EUser;
|
||||
|
||||
@IsString()
|
||||
@IsDefined()
|
||||
@IsJWT()
|
||||
token: string;
|
||||
}
|
||||
|
||||
// UserMePermissions
|
||||
export class UserMePermissionsResponse {
|
||||
@IsDefined()
|
||||
@IsStringList()
|
||||
permissions: string[];
|
||||
}
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import { Type } from 'class-transformer';
|
||||
import {
|
||||
IsArray,
|
||||
IsDefined,
|
||||
IsOptional,
|
||||
ValidateNested
|
||||
} from 'class-validator';
|
||||
import { EUser, NamePassUser } from '../../entities/user.entity';
|
||||
import { IsArray, IsOptional } from 'class-validator';
|
||||
import { EUser, SimpleUser } from '../../entities/user.entity';
|
||||
import { Newable } from '../../types';
|
||||
import { IsEntityID } from '../../validators/entity-id.validator';
|
||||
import { IsNested } from '../../validators/nested.validator';
|
||||
import { IsPosInt } from '../../validators/positive-int.validator';
|
||||
import { IsStringList } from '../../validators/string-list.validator';
|
||||
import { IsPlainTextPwd, IsUsername } from '../../validators/user.validators';
|
||||
import { EntityIDObject } from '../idobject.dto';
|
||||
|
||||
// UserList
|
||||
@@ -22,9 +18,7 @@ export class UserListRequest {
|
||||
|
||||
export class UserListResponse {
|
||||
@IsArray()
|
||||
@IsDefined()
|
||||
@ValidateNested()
|
||||
@Type(() => EUser)
|
||||
@IsNested(EUser)
|
||||
users: EUser[];
|
||||
|
||||
@IsPosInt()
|
||||
@@ -35,11 +29,7 @@ export class UserListResponse {
|
||||
}
|
||||
|
||||
// UserCreate
|
||||
export class UserCreateRequest extends NamePassUser {
|
||||
@IsOptional()
|
||||
@IsStringList()
|
||||
roles?: string[];
|
||||
}
|
||||
export class UserCreateRequest extends SimpleUser {}
|
||||
export class UserCreateResponse extends EUser {}
|
||||
|
||||
// UserDelete
|
||||
@@ -51,33 +41,31 @@ export class UserInfoRequest extends EntityIDObject {}
|
||||
export class UserInfoResponse extends EUser {}
|
||||
|
||||
// UserUpdate
|
||||
export class UserUpdateRequest extends EntityIDObject {
|
||||
@IsOptional()
|
||||
@IsUsername()
|
||||
username?: string;
|
||||
export class UserUpdateRequest extends (SimpleUser as Newable<
|
||||
Partial<SimpleUser>
|
||||
>) {
|
||||
@IsEntityID()
|
||||
id: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsStringList()
|
||||
roles?: string[];
|
||||
override username?: string;
|
||||
|
||||
@IsPlainTextPwd()
|
||||
@IsOptional()
|
||||
password?: string;
|
||||
override password?: string;
|
||||
|
||||
@IsOptional()
|
||||
override roles?: string[];
|
||||
}
|
||||
|
||||
export class UserUpdateResponse extends EUser {}
|
||||
|
||||
// GetSpecialUsers
|
||||
export class GetSpecialUsersResponse {
|
||||
@IsDefined()
|
||||
@IsStringList()
|
||||
UndeletableUsersList: string[];
|
||||
|
||||
@IsDefined()
|
||||
@IsStringList()
|
||||
ImmutableUsersList: string[];
|
||||
|
||||
@IsDefined()
|
||||
|
||||
@IsStringList()
|
||||
LockedLoginUsersList: string[];
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { EntityID } from '../validators/entity-id.validator';
|
||||
import { IsEntityID } from '../validators/entity-id.validator';
|
||||
|
||||
export class EntityIDObject {
|
||||
@EntityID()
|
||||
@IsEntityID()
|
||||
id: string;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsDefined, IsInt, IsOptional, ValidateNested } from 'class-validator';
|
||||
import { IsInt, IsOptional } from 'class-validator';
|
||||
import { EUser } from '../entities/user.entity';
|
||||
import { IsNested } from '../validators/nested.validator';
|
||||
|
||||
export class JwtDataDto {
|
||||
@IsDefined()
|
||||
@ValidateNested()
|
||||
@Type(() => EUser)
|
||||
@IsNested(EUser)
|
||||
user: EUser;
|
||||
|
||||
@IsOptional()
|
||||
|
||||
@@ -1,4 +1,26 @@
|
||||
import { IsEnum, IsString } from 'class-validator';
|
||||
import { IsEntityID } from '../validators/entity-id.validator';
|
||||
import { IsPrefValue } from '../validators/pref-value.validator';
|
||||
|
||||
// Variable value type
|
||||
export type PrefValueType = string | number | boolean;
|
||||
export type PrefValueTypeStrings = 'string' | 'number' | 'boolean';
|
||||
export const PrefValueTypes = ['string', 'number', 'boolean'];
|
||||
|
||||
// Decoded Representations
|
||||
|
||||
export class DecodedSysPref {
|
||||
@IsString()
|
||||
key: string;
|
||||
|
||||
@IsPrefValue()
|
||||
value: PrefValueType;
|
||||
|
||||
@IsEnum(PrefValueTypes)
|
||||
type: PrefValueTypeStrings;
|
||||
}
|
||||
|
||||
export class DecodedUsrPref extends DecodedSysPref {
|
||||
@IsEntityID()
|
||||
user: string;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { PrefValueType, PrefValueTypeStrings } from './preferences.dto';
|
||||
|
||||
// This enum is only here to make accessing the values easier, and type checking in the backend
|
||||
export enum SysPreference {
|
||||
@@ -9,10 +8,3 @@ export enum SysPreference {
|
||||
TestNumber = 'test_number',
|
||||
TestBoolean = 'test_boolean',
|
||||
}
|
||||
|
||||
// Interfaces
|
||||
export interface InternalSysPrefRepresentation {
|
||||
key: string;
|
||||
value: PrefValueType;
|
||||
type: PrefValueTypeStrings;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { PrefValueType, PrefValueTypeStrings } from './preferences.dto';
|
||||
|
||||
// This enum is only here to make accessing the values easier, and type checking in the backend
|
||||
export enum UsrPreference {
|
||||
@@ -7,10 +6,3 @@ export enum UsrPreference {
|
||||
TestBoolean = 'test_boolean',
|
||||
}
|
||||
|
||||
// Interfaces
|
||||
export interface InternalUsrPrefRepresentation {
|
||||
key: string;
|
||||
value: PrefValueType;
|
||||
type: PrefValueTypeStrings;
|
||||
user: number;
|
||||
}
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
import { Exclude } from 'class-transformer';
|
||||
import { IsHash, IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||
import { EntityID } from '../validators/entity-id.validator';
|
||||
import { IsHash, IsOptional, IsString } from 'class-validator';
|
||||
import { IsEntityID } from '../validators/entity-id.validator';
|
||||
import { IsNotDefined } from '../validators/not-defined.validator';
|
||||
|
||||
export class EImage {
|
||||
@EntityID()
|
||||
id: string;
|
||||
@IsOptional()
|
||||
@IsEntityID()
|
||||
id?: string;
|
||||
|
||||
@IsHash('sha256')
|
||||
hash: string;
|
||||
|
||||
// Binary data
|
||||
@IsOptional()
|
||||
@Exclude() // Dont send this by default
|
||||
data?: object;
|
||||
|
||||
@IsNotEmpty()
|
||||
// Because typescript does not support exact types, we have to do this stupidness
|
||||
@IsNotDefined()
|
||||
data: undefined;
|
||||
|
||||
@IsString()
|
||||
mime: string;
|
||||
}
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
import { IsDefined } from 'class-validator';
|
||||
import { EntityID } from '../validators/entity-id.validator';
|
||||
import { IsOptional } from 'class-validator';
|
||||
import { IsEntityID } from '../validators/entity-id.validator';
|
||||
import { IsRoleName } from '../validators/role.validators';
|
||||
import { IsStringList } from '../validators/string-list.validator';
|
||||
|
||||
// This entity is build from multiple smaller enitities
|
||||
// Theses smaller entities are used in other places
|
||||
|
||||
export class RoleNameObject {
|
||||
export class SimpleRole {
|
||||
@IsRoleName()
|
||||
name: string;
|
||||
}
|
||||
|
||||
export class RoleNamePermsObject extends RoleNameObject {
|
||||
@IsDefined()
|
||||
@IsStringList()
|
||||
permissions: string[];
|
||||
}
|
||||
|
||||
export class ERole extends RoleNamePermsObject {
|
||||
@EntityID()
|
||||
id: string;
|
||||
export class ERole {
|
||||
@IsOptional()
|
||||
@IsEntityID()
|
||||
id?: string;
|
||||
|
||||
@IsRoleName()
|
||||
name: string;
|
||||
|
||||
@IsStringList()
|
||||
permissions: string[];
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
import { EntityIDOptional } from '../validators/entity-id.validator';
|
||||
import { IsOptional, IsString } from 'class-validator';
|
||||
import { IsEntityID } from '../validators/entity-id.validator';
|
||||
|
||||
export class ESysPreference {
|
||||
@EntityIDOptional()
|
||||
@IsOptional()
|
||||
@IsEntityID()
|
||||
id?: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
key: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
value: string;
|
||||
}
|
||||
|
||||
@@ -1,37 +1,32 @@
|
||||
import { Exclude } from 'class-transformer';
|
||||
import { IsDefined, IsOptional, IsString } from 'class-validator';
|
||||
import { EntityID } from '../validators/entity-id.validator';
|
||||
import { IsOptional } from 'class-validator';
|
||||
import { IsEntityID } from '../validators/entity-id.validator';
|
||||
import { IsNotDefined } from '../validators/not-defined.validator';
|
||||
import { IsStringList } from '../validators/string-list.validator';
|
||||
import { IsPlainTextPwd, IsUsername } from '../validators/user.validators';
|
||||
|
||||
// This entity is build from multiple smaller enitities
|
||||
// Theses smaller entities are used in other places
|
||||
|
||||
export class UsernameUser {
|
||||
export class SimpleUser {
|
||||
@IsUsername()
|
||||
username: string;
|
||||
}
|
||||
|
||||
// This is a simple user object with just the username and unhashed password
|
||||
export class NamePassUser extends UsernameUser {
|
||||
@IsPlainTextPwd()
|
||||
password: string;
|
||||
}
|
||||
|
||||
// Add a user object with just the username and roles for jwt
|
||||
export class NameRolesUser extends UsernameUser {
|
||||
@IsDefined()
|
||||
@IsStringList()
|
||||
roles: string[];
|
||||
}
|
||||
|
||||
// Actual entity that goes in the db
|
||||
export class EUser extends NameRolesUser {
|
||||
@EntityID()
|
||||
id: string;
|
||||
|
||||
export class EUser {
|
||||
@IsOptional()
|
||||
@Exclude()
|
||||
@IsString()
|
||||
password?: string;
|
||||
@IsEntityID()
|
||||
id?: string;
|
||||
|
||||
@IsUsername()
|
||||
username: string;
|
||||
|
||||
@IsStringList()
|
||||
roles: string[];
|
||||
|
||||
// Because typescript does not support exact types, we have to do this stupidness
|
||||
@IsNotDefined()
|
||||
hashedPassword: undefined;
|
||||
}
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
import { IsDefined, IsNotEmpty, IsString } from 'class-validator';
|
||||
import { EntityIDOptional } from '../validators/entity-id.validator';
|
||||
import { IsOptional, IsString } from 'class-validator';
|
||||
import { IsEntityID } from '../validators/entity-id.validator';
|
||||
import { IsPosInt } from '../validators/positive-int.validator';
|
||||
|
||||
export class EUsrPreference {
|
||||
@EntityIDOptional()
|
||||
@IsOptional()
|
||||
@IsEntityID()
|
||||
id?: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
key: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
value: string;
|
||||
|
||||
@IsDefined()
|
||||
@IsPosInt()
|
||||
userId: number;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ type FCDecorator = MethodDecorator & ClassDecorator;
|
||||
export function CombineFCDecorators(...decorators: FCDecorator[]) {
|
||||
return (target: any, key: string, descriptor: PropertyDescriptor) => {
|
||||
decorators.forEach(decorator => {
|
||||
decorator(target, key, descriptor);
|
||||
decorator(target, key, descriptor as any);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { validate } from 'class-validator';
|
||||
import { validate, ValidatorOptions } from 'class-validator';
|
||||
|
||||
// For some stupid reason, the class-validator library does not have a way to set global defaults
|
||||
// So now we have to do it this way
|
||||
|
||||
export const ValidateOptions = {
|
||||
disableErrorMessages: true,
|
||||
export const ValidateOptions: ValidatorOptions = {
|
||||
forbidNonWhitelisted: true,
|
||||
forbidUnknownValues: true,
|
||||
stopAtFirstError: true,
|
||||
whitelist: true,
|
||||
strictGroups: true,
|
||||
};
|
||||
|
||||
export const strictValidate = (object: object) =>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { IsNotEmpty, IsOptional, IsUUID } from 'class-validator';
|
||||
import { IsUUID } from 'class-validator';
|
||||
import { CombinePDecorators } from '../util/decorator';
|
||||
|
||||
export const EntityID = CombinePDecorators(IsNotEmpty(), IsUUID('4'));
|
||||
export const EntityIDOptional = CombinePDecorators(IsOptional(), IsUUID('4'));
|
||||
export const IsEntityID = CombinePDecorators(IsUUID('4'));
|
||||
|
||||
15
shared/src/validators/nested.validator.ts
Normal file
15
shared/src/validators/nested.validator.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsNotEmpty, ValidateNested } from 'class-validator';
|
||||
import { Newable } from '../types';
|
||||
|
||||
export const IsNested = (nestedClass: Newable<any>) => {
|
||||
const nestedValidator = ValidateNested();
|
||||
const isNotEmptyValidator = IsNotEmpty();
|
||||
const typeValidator = Type(() => nestedClass);
|
||||
|
||||
return (target: Object, propertyKey: string | symbol): void => {
|
||||
nestedValidator(target, propertyKey);
|
||||
isNotEmptyValidator(target, propertyKey);
|
||||
typeValidator(target, propertyKey);
|
||||
};
|
||||
};
|
||||
26
shared/src/validators/not-defined.validator.ts
Normal file
26
shared/src/validators/not-defined.validator.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import {
|
||||
IsOptional,
|
||||
registerDecorator,
|
||||
ValidationArguments,
|
||||
ValidationOptions
|
||||
} from 'class-validator';
|
||||
|
||||
export function isNotDefined(value: any, args: ValidationArguments) {
|
||||
return value === undefined || value === null;
|
||||
}
|
||||
|
||||
export function IsNotDefined(validationOptions?: ValidationOptions) {
|
||||
const optional = IsOptional();
|
||||
return function (object: Object, propertyName: string) {
|
||||
registerDecorator({
|
||||
name: 'isNotDefined',
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
options: validationOptions ?? {},
|
||||
validator: {
|
||||
validate: isNotDefined,
|
||||
},
|
||||
});
|
||||
optional(object, propertyName);
|
||||
};
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { IsDefined, IsInt, Min } from 'class-validator';
|
||||
import { IsInt, Min } from 'class-validator';
|
||||
import { CombinePDecorators } from '../util/decorator';
|
||||
|
||||
export const IsPosInt = CombinePDecorators(IsInt(), Min(0), IsDefined());
|
||||
export const IsPosInt = CombinePDecorators(IsInt(), Min(0));
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { registerDecorator, ValidationArguments, ValidationOptions } from 'class-validator';
|
||||
import { PrefValueTypes } from '../dto/preferences.dto';
|
||||
|
||||
export function isSysPrefValue(value: any, args: ValidationArguments) {
|
||||
export function isPrefValue(value: any, args: ValidationArguments) {
|
||||
const type = typeof value;
|
||||
return PrefValueTypes.includes(type);
|
||||
}
|
||||
|
||||
export function IsSysPrefValue(validationOptions?: ValidationOptions) {
|
||||
export function IsPrefValue(validationOptions?: ValidationOptions) {
|
||||
return function (object: Object, propertyName: string) {
|
||||
registerDecorator({
|
||||
name: 'isSysPrefValue',
|
||||
name: 'isPrefValue',
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
options: validationOptions,
|
||||
options: validationOptions ?? {},
|
||||
validator: {
|
||||
validate: isSysPrefValue,
|
||||
validate: isPrefValue,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -1,8 +1,7 @@
|
||||
import { IsAlphanumeric, IsNotEmpty, IsString, Length } from 'class-validator';
|
||||
import { IsAlphanumeric, IsString, Length } from 'class-validator';
|
||||
import { CombinePDecorators } from '../util/decorator';
|
||||
|
||||
export const IsRoleName = CombinePDecorators(
|
||||
IsNotEmpty(),
|
||||
IsString(),
|
||||
Length(4, 32),
|
||||
IsAlphanumeric(),
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import {
|
||||
IsArray,
|
||||
IsNotEmpty,
|
||||
IsString
|
||||
IsArray, IsString
|
||||
} from 'class-validator';
|
||||
import { CombinePDecorators } from '../util/decorator';
|
||||
|
||||
export const IsStringList = CombinePDecorators(
|
||||
IsArray(),
|
||||
IsString({ each: true }),
|
||||
IsNotEmpty({ each: true }),
|
||||
);
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import { IsAlphanumeric, IsNotEmpty, IsString, Length } from 'class-validator';
|
||||
import { IsAlphanumeric, IsString, Length } from 'class-validator';
|
||||
import { CombinePDecorators } from '../util/decorator';
|
||||
|
||||
// Match this with user validators in frontend
|
||||
// (Frontend is not security focused, but it tells the user what is wrong)
|
||||
|
||||
export const IsUsername = CombinePDecorators(
|
||||
IsNotEmpty(),
|
||||
IsString(),
|
||||
Length(4, 32),
|
||||
IsAlphanumeric(),
|
||||
);
|
||||
|
||||
export const IsPlainTextPwd = CombinePDecorators(
|
||||
IsNotEmpty(),
|
||||
IsString(),
|
||||
Length(4, 1024),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user