mirror of
https://github.com/CaramelFur/Picsur.git
synced 2025-11-02 10:15:47 +01:00
update packages and fix build
This commit is contained in:
@@ -21,65 +21,65 @@
|
|||||||
"purge": "rm -rf dist && rm -rf node_modules"
|
"purge": "rm -rf dist && rm -rf node_modules"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fastify/helmet": "^10.1.1",
|
"@fastify/helmet": "^11.1.1",
|
||||||
"@fastify/multipart": "^7.6.1",
|
"@fastify/multipart": "^8.0.0",
|
||||||
"@fastify/reply-from": "^9.3.0",
|
"@fastify/reply-from": "^9.4.0",
|
||||||
"@fastify/static": "^6.10.2",
|
"@fastify/static": "^6.12.0",
|
||||||
"@nestjs/common": "^10.0.0",
|
"@nestjs/common": "^10.2.10",
|
||||||
"@nestjs/config": "^2.3.4",
|
"@nestjs/config": "^3.1.1",
|
||||||
"@nestjs/core": "^10.0.0",
|
"@nestjs/core": "^10.2.10",
|
||||||
"@nestjs/jwt": "^10.1.0",
|
"@nestjs/jwt": "^10.2.0",
|
||||||
"@nestjs/passport": "^9.0.3",
|
"@nestjs/passport": "^10.0.2",
|
||||||
"@nestjs/platform-fastify": "^10.0.0",
|
"@nestjs/platform-fastify": "^10.2.10",
|
||||||
"@nestjs/schedule": "^3.0.0",
|
"@nestjs/schedule": "^4.0.0",
|
||||||
"@nestjs/serve-static": "^4.0.0",
|
"@nestjs/serve-static": "^4.0.0",
|
||||||
"@nestjs/throttler": "^4.0.0",
|
"@nestjs/throttler": "^5.0.1",
|
||||||
"@nestjs/typeorm": "^9.0.1",
|
"@nestjs/typeorm": "^10.0.1",
|
||||||
"bcrypt": "^5.1.0",
|
"bcrypt": "^5.1.1",
|
||||||
"bmp-img": "^1.2.1",
|
"bmp-img": "^1.2.1",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"extensionless": "^1.4.5",
|
"extensionless": "^1.7.3",
|
||||||
"file-type": "^18.5.0",
|
"file-type": "^18.7.0",
|
||||||
"is-docker": "^3.0.0",
|
"is-docker": "^3.0.0",
|
||||||
"ms": "2.1.3",
|
"ms": "2.1.3",
|
||||||
"node-fetch": "^3.3.1",
|
"node-fetch": "^3.3.2",
|
||||||
"p-timeout": "^6.1.2",
|
"p-timeout": "^6.1.2",
|
||||||
"passport": "^0.6.0",
|
"passport": "^0.7.0",
|
||||||
"passport-headerapikey": "^1.2.2",
|
"passport-headerapikey": "^1.2.2",
|
||||||
"passport-jwt": "^4.0.1",
|
"passport-jwt": "^4.0.1",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
"passport-strategy": "^1.0.0",
|
"passport-strategy": "^1.0.0",
|
||||||
"pg": "^8.11.0",
|
"pg": "^8.11.3",
|
||||||
"picsur-shared": "*",
|
"picsur-shared": "*",
|
||||||
"posix.js": "^0.1.1",
|
"posix.js": "^0.1.1",
|
||||||
"qoi-img": "^2.1.1",
|
"qoi-img": "^2.1.1",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"rimraf": "^5.0.1",
|
"rimraf": "^5.0.5",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
"sharp": "^0.32.1",
|
"sharp": "^0.32.6",
|
||||||
"stream-parser": "^0.3.1",
|
"stream-parser": "^0.3.1",
|
||||||
"thunks": "^4.9.6",
|
"thunks": "^4.9.6",
|
||||||
"typeorm": "0.3.16",
|
"typeorm": "0.3.17",
|
||||||
"zod": "^3.21.4"
|
"zod": "^3.22.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nestjs/cli": "^10.0.1",
|
"@nestjs/cli": "^10.2.1",
|
||||||
"@nestjs/schematics": "^10.0.1",
|
"@nestjs/schematics": "^10.0.3",
|
||||||
"@nestjs/testing": "^10.0.0",
|
"@nestjs/testing": "^10.2.10",
|
||||||
"@types/bcrypt": "^5.0.0",
|
"@types/bcrypt": "^5.0.2",
|
||||||
"@types/cors": "^2.8.13",
|
"@types/cors": "^2.8.17",
|
||||||
"@types/multer": "^1.4.7",
|
"@types/multer": "^1.4.11",
|
||||||
"@types/node": "^20.3.1",
|
"@types/node": "^20.10.0",
|
||||||
"@types/passport-jwt": "^3.0.8",
|
"@types/passport-jwt": "^3.0.13",
|
||||||
"@types/passport-local": "^1.0.35",
|
"@types/passport-local": "^1.0.38",
|
||||||
"@types/passport-strategy": "^0.2.35",
|
"@types/passport-strategy": "^0.2.38",
|
||||||
"@types/sharp": "^0.32.0",
|
"@types/sharp": "^0.32.0",
|
||||||
"@types/supertest": "^2.0.12",
|
"@types/supertest": "^2.0.16",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^3.1.0",
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "^0.5.21",
|
||||||
"ts-loader": "^9.4.3",
|
"ts-loader": "^9.5.1",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"tsconfig-paths": "^4.2.0",
|
"tsconfig-paths": "^4.2.0",
|
||||||
"typescript": "^5.1.3"
|
"typescript": "^5.3.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export class JwtConfigService implements JwtOptionsFactory {
|
|||||||
await this.prefService.getStringPreference('jwt_expires_in'),
|
await this.prefService.getStringPreference('jwt_expires_in'),
|
||||||
);
|
);
|
||||||
|
|
||||||
let milliseconds = ms(expiresIn as any);
|
let milliseconds = ms(expiresIn as string);
|
||||||
if (isNaN(milliseconds)) {
|
if (isNaN(milliseconds)) {
|
||||||
milliseconds = 1000 * 60 * 60 * 24; // 1 day
|
milliseconds = 1000 * 60 * 60 * 24; // 1 day
|
||||||
}
|
}
|
||||||
|
|||||||
12
backend/src/decorators/easy-throttle.decorator.ts
Normal file
12
backend/src/decorators/easy-throttle.decorator.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { Throttle } from '@nestjs/throttler';
|
||||||
|
|
||||||
|
export const EasyThrottle = (
|
||||||
|
limit: number,
|
||||||
|
ttl?: number,
|
||||||
|
): MethodDecorator & ClassDecorator =>
|
||||||
|
Throttle({
|
||||||
|
default: {
|
||||||
|
limit,
|
||||||
|
ttl: ttl ?? 60,
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -8,8 +8,12 @@ import { ZodValidationPipe } from './validate/zod-validator.pipe';
|
|||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
ThrottlerModule.forRoot({
|
ThrottlerModule.forRoot({
|
||||||
ttl: 60,
|
throttlers: [
|
||||||
limit: 60,
|
{
|
||||||
|
limit: 60,
|
||||||
|
ttl: 60,
|
||||||
|
},
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { FT, Fail } from 'picsur-shared/dist/types/failable';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PicsurThrottlerGuard extends ThrottlerGuard {
|
export class PicsurThrottlerGuard extends ThrottlerGuard {
|
||||||
protected override throwThrottlingException(): void {
|
protected override throwThrottlingException(): Promise<void> {
|
||||||
throw Fail(FT.RateLimit);
|
throw Fail(FT.RateLimit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export class ImageConverterService {
|
|||||||
if (HasFailed(memLimit) || HasFailed(timeLimit)) {
|
if (HasFailed(memLimit) || HasFailed(timeLimit)) {
|
||||||
return Fail(FT.Internal, 'Failed to get conversion limits');
|
return Fail(FT.Internal, 'Failed to get conversion limits');
|
||||||
}
|
}
|
||||||
let timeLimitMS = ms(timeLimit as any);
|
let timeLimitMS = ms(timeLimit as string);
|
||||||
if (isNaN(timeLimitMS) || timeLimitMS === 0) timeLimitMS = 15 * 1000; // 15 seconds
|
if (isNaN(timeLimitMS) || timeLimitMS === 0) timeLimitMS = 15 * 1000; // 15 seconds
|
||||||
|
|
||||||
const sharpWrapper = new SharpWrapper(timeLimitMS, memLimit);
|
const sharpWrapper = new SharpWrapper(timeLimitMS, memLimit);
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export class ImageManagerModule implements OnModuleInit {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let after_ms = ms(remove_derivatives_after as any);
|
let after_ms = ms(remove_derivatives_after as string);
|
||||||
if (isNaN(after_ms) || after_ms === 0) {
|
if (isNaN(after_ms) || after_ms === 0) {
|
||||||
this.logger.log('remove_derivatives_after is 0, skipping cron');
|
this.logger.log('remove_derivatives_after is 0, skipping cron');
|
||||||
return;
|
return;
|
||||||
@@ -59,6 +59,7 @@ export class ImageManagerModule implements OnModuleInit {
|
|||||||
const result = await this.imageFileDB.cleanupDerivatives(after_ms / 1000);
|
const result = await this.imageFileDB.cleanupDerivatives(after_ms / 1000);
|
||||||
if (HasFailed(result)) {
|
if (HasFailed(result)) {
|
||||||
result.print(this.logger);
|
result.print(this.logger);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result > 0) this.logger.log(`Cleaned up ${result} derivatives`);
|
if (result > 0) this.logger.log(`Cleaned up ${result} derivatives`);
|
||||||
|
|||||||
@@ -242,9 +242,8 @@ export class ImageManagerService {
|
|||||||
// Util stuff ==================================================================
|
// Util stuff ==================================================================
|
||||||
|
|
||||||
private async getFileTypeFromBuffer(image: Buffer): AsyncFailable<FileType> {
|
private async getFileTypeFromBuffer(image: Buffer): AsyncFailable<FileType> {
|
||||||
const filetypeResult: FileTypeResult | undefined = await fileTypeFromBuffer(
|
const filetypeResult: FileTypeResult | undefined =
|
||||||
image,
|
await fileTypeFromBuffer(image);
|
||||||
);
|
|
||||||
|
|
||||||
let mime: string | undefined;
|
let mime: string | undefined;
|
||||||
if (filetypeResult === undefined) {
|
if (filetypeResult === undefined) {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Body, Controller, Post } from '@nestjs/common';
|
import { Body, Controller, Post } from '@nestjs/common';
|
||||||
import { Throttle } from '@nestjs/throttler';
|
|
||||||
import {
|
import {
|
||||||
ApiKeyCreateResponse,
|
ApiKeyCreateResponse,
|
||||||
ApiKeyDeleteRequest,
|
ApiKeyDeleteRequest,
|
||||||
@@ -14,6 +13,7 @@ import {
|
|||||||
import { Permission } from 'picsur-shared/dist/dto/permissions.enum';
|
import { Permission } from 'picsur-shared/dist/dto/permissions.enum';
|
||||||
import { ThrowIfFailed } from 'picsur-shared/dist/types/failable';
|
import { ThrowIfFailed } from 'picsur-shared/dist/types/failable';
|
||||||
import { ApiKeyDbService } from '../../../collections/apikey-db/apikey-db.service';
|
import { ApiKeyDbService } from '../../../collections/apikey-db/apikey-db.service';
|
||||||
|
import { EasyThrottle } from '../../../decorators/easy-throttle.decorator';
|
||||||
import {
|
import {
|
||||||
HasPermission,
|
HasPermission,
|
||||||
RequiredPermissions,
|
RequiredPermissions,
|
||||||
@@ -54,7 +54,7 @@ export class ApiKeysController {
|
|||||||
|
|
||||||
@Post('create')
|
@Post('create')
|
||||||
@Returns(ApiKeyCreateResponse)
|
@Returns(ApiKeyCreateResponse)
|
||||||
@Throttle(10)
|
@EasyThrottle(10)
|
||||||
async createApiKey(
|
async createApiKey(
|
||||||
@ReqUserID() userID: string,
|
@ReqUserID() userID: string,
|
||||||
): Promise<ApiKeyCreateResponse> {
|
): Promise<ApiKeyCreateResponse> {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Body, Controller, Get, Logger, Param, Post } from '@nestjs/common';
|
import { Body, Controller, Get, Logger, Param, Post } from '@nestjs/common';
|
||||||
import { Throttle } from '@nestjs/throttler';
|
|
||||||
import {
|
import {
|
||||||
GetPreferenceResponse,
|
GetPreferenceResponse,
|
||||||
MultiplePreferencesResponse,
|
MultiplePreferencesResponse,
|
||||||
@@ -8,6 +7,7 @@ import {
|
|||||||
} from 'picsur-shared/dist/dto/api/pref.dto';
|
} from 'picsur-shared/dist/dto/api/pref.dto';
|
||||||
import { ThrowIfFailed } from 'picsur-shared/dist/types/failable';
|
import { ThrowIfFailed } from 'picsur-shared/dist/types/failable';
|
||||||
import { SysPreferenceDbService } from '../../../collections/preference-db/sys-preference-db.service';
|
import { SysPreferenceDbService } from '../../../collections/preference-db/sys-preference-db.service';
|
||||||
|
import { EasyThrottle } from '../../../decorators/easy-throttle.decorator';
|
||||||
import { RequiredPermissions } from '../../../decorators/permissions.decorator';
|
import { RequiredPermissions } from '../../../decorators/permissions.decorator';
|
||||||
import { Returns } from '../../../decorators/returns.decorator';
|
import { Returns } from '../../../decorators/returns.decorator';
|
||||||
import { Permission } from '../../../models/constants/permissions.const';
|
import { Permission } from '../../../models/constants/permissions.const';
|
||||||
@@ -21,7 +21,7 @@ export class SysPrefController {
|
|||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@Returns(MultiplePreferencesResponse)
|
@Returns(MultiplePreferencesResponse)
|
||||||
@Throttle(20)
|
@EasyThrottle(20)
|
||||||
async getAllSysPrefs(): Promise<MultiplePreferencesResponse> {
|
async getAllSysPrefs(): Promise<MultiplePreferencesResponse> {
|
||||||
const prefs = ThrowIfFailed(await this.prefService.getAllPreferences());
|
const prefs = ThrowIfFailed(await this.prefService.getAllPreferences());
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ export class SysPrefController {
|
|||||||
|
|
||||||
@Post(':key')
|
@Post(':key')
|
||||||
@Returns(UpdatePreferenceResponse)
|
@Returns(UpdatePreferenceResponse)
|
||||||
@Throttle(30)
|
@EasyThrottle(30)
|
||||||
async setSysPref(
|
async setSysPref(
|
||||||
@Param('key') key: string,
|
@Param('key') key: string,
|
||||||
@Body() body: UpdatePreferenceRequest,
|
@Body() body: UpdatePreferenceRequest,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Body, Controller, Get, Logger, Param, Post } from '@nestjs/common';
|
import { Body, Controller, Get, Logger, Param, Post } from '@nestjs/common';
|
||||||
import { Throttle } from '@nestjs/throttler';
|
|
||||||
import {
|
import {
|
||||||
GetPreferenceResponse,
|
GetPreferenceResponse,
|
||||||
MultiplePreferencesResponse,
|
MultiplePreferencesResponse,
|
||||||
@@ -8,6 +7,7 @@ import {
|
|||||||
} from 'picsur-shared/dist/dto/api/pref.dto';
|
} from 'picsur-shared/dist/dto/api/pref.dto';
|
||||||
import { ThrowIfFailed } from 'picsur-shared/dist/types/failable';
|
import { ThrowIfFailed } from 'picsur-shared/dist/types/failable';
|
||||||
import { UsrPreferenceDbService } from '../../../collections/preference-db/usr-preference-db.service';
|
import { UsrPreferenceDbService } from '../../../collections/preference-db/usr-preference-db.service';
|
||||||
|
import { EasyThrottle } from '../../../decorators/easy-throttle.decorator';
|
||||||
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';
|
||||||
import { Returns } from '../../../decorators/returns.decorator';
|
import { Returns } from '../../../decorators/returns.decorator';
|
||||||
@@ -22,7 +22,7 @@ export class UsrPrefController {
|
|||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@Returns(MultiplePreferencesResponse)
|
@Returns(MultiplePreferencesResponse)
|
||||||
@Throttle(20)
|
@EasyThrottle(20)
|
||||||
async getAllUsrPrefs(
|
async getAllUsrPrefs(
|
||||||
@ReqUserID() userid: string,
|
@ReqUserID() userid: string,
|
||||||
): Promise<MultiplePreferencesResponse> {
|
): Promise<MultiplePreferencesResponse> {
|
||||||
@@ -51,7 +51,7 @@ export class UsrPrefController {
|
|||||||
|
|
||||||
@Post(':key')
|
@Post(':key')
|
||||||
@Returns(UpdatePreferenceResponse)
|
@Returns(UpdatePreferenceResponse)
|
||||||
@Throttle(30)
|
@EasyThrottle(30)
|
||||||
async setUsrPref(
|
async setUsrPref(
|
||||||
@Param('key') key: string,
|
@Param('key') key: string,
|
||||||
@ReqUserID() userid: string,
|
@ReqUserID() userid: string,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Body, Controller, Get, Logger, Post } from '@nestjs/common';
|
import { Body, Controller, Get, Logger, Post } from '@nestjs/common';
|
||||||
import { Throttle } from '@nestjs/throttler';
|
|
||||||
import {
|
import {
|
||||||
RoleCreateRequest,
|
RoleCreateRequest,
|
||||||
RoleCreateResponse,
|
RoleCreateResponse,
|
||||||
@@ -12,9 +11,10 @@ import {
|
|||||||
RoleUpdateResponse,
|
RoleUpdateResponse,
|
||||||
SpecialRolesResponse,
|
SpecialRolesResponse,
|
||||||
} from 'picsur-shared/dist/dto/api/roles.dto';
|
} from 'picsur-shared/dist/dto/api/roles.dto';
|
||||||
import { Fail, FT, ThrowIfFailed } from 'picsur-shared/dist/types/failable';
|
import { FT, Fail, ThrowIfFailed } from 'picsur-shared/dist/types/failable';
|
||||||
import { RoleDbService } from '../../../collections/role-db/role-db.service';
|
import { RoleDbService } from '../../../collections/role-db/role-db.service';
|
||||||
import { UserDbService } from '../../../collections/user-db/user-db.service';
|
import { UserDbService } from '../../../collections/user-db/user-db.service';
|
||||||
|
import { EasyThrottle } from '../../../decorators/easy-throttle.decorator';
|
||||||
import { RequiredPermissions } from '../../../decorators/permissions.decorator';
|
import { RequiredPermissions } from '../../../decorators/permissions.decorator';
|
||||||
import { Returns } from '../../../decorators/returns.decorator';
|
import { Returns } from '../../../decorators/returns.decorator';
|
||||||
import { Permission } from '../../../models/constants/permissions.const';
|
import { Permission } from '../../../models/constants/permissions.const';
|
||||||
@@ -57,7 +57,7 @@ export class RolesController {
|
|||||||
|
|
||||||
@Post('update')
|
@Post('update')
|
||||||
@Returns(RoleUpdateResponse)
|
@Returns(RoleUpdateResponse)
|
||||||
@Throttle(20)
|
@EasyThrottle(20)
|
||||||
async updateRole(
|
async updateRole(
|
||||||
@Body() body: RoleUpdateRequest,
|
@Body() body: RoleUpdateRequest,
|
||||||
): Promise<RoleUpdateResponse> {
|
): Promise<RoleUpdateResponse> {
|
||||||
@@ -75,7 +75,7 @@ export class RolesController {
|
|||||||
|
|
||||||
@Post('create')
|
@Post('create')
|
||||||
@Returns(RoleCreateResponse)
|
@Returns(RoleCreateResponse)
|
||||||
@Throttle(10)
|
@EasyThrottle(10)
|
||||||
async createRole(
|
async createRole(
|
||||||
@Body() role: RoleCreateRequest,
|
@Body() role: RoleCreateRequest,
|
||||||
): Promise<RoleCreateResponse> {
|
): Promise<RoleCreateResponse> {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Controller, Logger, Post, Req, Res } from '@nestjs/common';
|
import { Controller, Logger, Post, Req, Res } from '@nestjs/common';
|
||||||
import { Throttle } from '@nestjs/throttler';
|
|
||||||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||||
import { Fail, FT, ThrowIfFailed } from 'picsur-shared/dist/types/failable';
|
import { FT, Fail, ThrowIfFailed } from 'picsur-shared/dist/types/failable';
|
||||||
import { UsageConfigService } from '../../../config/late/usage.config.service';
|
import { UsageConfigService } from '../../../config/late/usage.config.service';
|
||||||
|
import { EasyThrottle } from '../../../decorators/easy-throttle.decorator';
|
||||||
import { NoPermissions } from '../../../decorators/permissions.decorator';
|
import { NoPermissions } from '../../../decorators/permissions.decorator';
|
||||||
import { ReturnsAnything } from '../../../decorators/returns.decorator';
|
import { ReturnsAnything } from '../../../decorators/returns.decorator';
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ export class UsageController {
|
|||||||
|
|
||||||
@Post(['report', 'report/*'])
|
@Post(['report', 'report/*'])
|
||||||
@ReturnsAnything()
|
@ReturnsAnything()
|
||||||
@Throttle(120)
|
@EasyThrottle(120)
|
||||||
async deleteRole(
|
async deleteRole(
|
||||||
@Req() req: FastifyRequest,
|
@Req() req: FastifyRequest,
|
||||||
@Res({
|
@Res({
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Body, Controller, Get, Logger, Post } from '@nestjs/common';
|
import { Body, Controller, Get, Logger, Post } from '@nestjs/common';
|
||||||
import { Throttle } from '@nestjs/throttler';
|
|
||||||
import {
|
import {
|
||||||
GetSpecialUsersResponse,
|
GetSpecialUsersResponse,
|
||||||
UserCreateRequest,
|
UserCreateRequest,
|
||||||
@@ -15,6 +14,7 @@ import {
|
|||||||
} from 'picsur-shared/dist/dto/api/user-manage.dto';
|
} from 'picsur-shared/dist/dto/api/user-manage.dto';
|
||||||
import { ThrowIfFailed } from 'picsur-shared/dist/types/failable';
|
import { ThrowIfFailed } from 'picsur-shared/dist/types/failable';
|
||||||
import { UserDbService } from '../../../collections/user-db/user-db.service';
|
import { UserDbService } from '../../../collections/user-db/user-db.service';
|
||||||
|
import { EasyThrottle } from '../../../decorators/easy-throttle.decorator';
|
||||||
import { RequiredPermissions } from '../../../decorators/permissions.decorator';
|
import { RequiredPermissions } from '../../../decorators/permissions.decorator';
|
||||||
import { Returns } from '../../../decorators/returns.decorator';
|
import { Returns } from '../../../decorators/returns.decorator';
|
||||||
import { Permission } from '../../../models/constants/permissions.const';
|
import { Permission } from '../../../models/constants/permissions.const';
|
||||||
@@ -47,7 +47,7 @@ export class UserAdminController {
|
|||||||
|
|
||||||
@Post('create')
|
@Post('create')
|
||||||
@Returns(UserCreateResponse)
|
@Returns(UserCreateResponse)
|
||||||
@Throttle(10)
|
@EasyThrottle(10)
|
||||||
async register(
|
async register(
|
||||||
@Body() create: UserCreateRequest,
|
@Body() create: UserCreateRequest,
|
||||||
): Promise<UserCreateResponse> {
|
): Promise<UserCreateResponse> {
|
||||||
@@ -80,7 +80,7 @@ export class UserAdminController {
|
|||||||
|
|
||||||
@Post('update')
|
@Post('update')
|
||||||
@Returns(UserUpdateResponse)
|
@Returns(UserUpdateResponse)
|
||||||
@Throttle(20)
|
@EasyThrottle(20)
|
||||||
async setPermissions(
|
async setPermissions(
|
||||||
@Body() body: UserUpdateRequest,
|
@Body() body: UserUpdateRequest,
|
||||||
): Promise<UserUpdateResponse> {
|
): Promise<UserUpdateResponse> {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Body, Controller, Get, Logger, Post } from '@nestjs/common';
|
import { Body, Controller, Get, Logger, Post } from '@nestjs/common';
|
||||||
import { Throttle } from '@nestjs/throttler';
|
|
||||||
import {
|
import {
|
||||||
UserCheckNameRequest,
|
UserCheckNameRequest,
|
||||||
UserCheckNameResponse,
|
UserCheckNameResponse,
|
||||||
@@ -12,6 +11,7 @@ import {
|
|||||||
import type { EUser } from 'picsur-shared/dist/entities/user.entity';
|
import type { EUser } from 'picsur-shared/dist/entities/user.entity';
|
||||||
import { ThrowIfFailed } from 'picsur-shared/dist/types/failable';
|
import { ThrowIfFailed } from 'picsur-shared/dist/types/failable';
|
||||||
import { UserDbService } from '../../../collections/user-db/user-db.service';
|
import { UserDbService } from '../../../collections/user-db/user-db.service';
|
||||||
|
import { EasyThrottle } from '../../../decorators/easy-throttle.decorator';
|
||||||
import {
|
import {
|
||||||
NoPermissions,
|
NoPermissions,
|
||||||
RequiredPermissions,
|
RequiredPermissions,
|
||||||
@@ -35,7 +35,7 @@ export class UserController {
|
|||||||
@Post('login')
|
@Post('login')
|
||||||
@Returns(UserLoginResponse)
|
@Returns(UserLoginResponse)
|
||||||
@UseLocalAuth(Permission.UserLogin)
|
@UseLocalAuth(Permission.UserLogin)
|
||||||
@Throttle(30, 300)
|
@EasyThrottle(30, 300)
|
||||||
async login(@ReqUser() user: EUser): Promise<UserLoginResponse> {
|
async login(@ReqUser() user: EUser): Promise<UserLoginResponse> {
|
||||||
const jwt_token = ThrowIfFailed(await this.authService.createToken(user));
|
const jwt_token = ThrowIfFailed(await this.authService.createToken(user));
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ export class UserController {
|
|||||||
@Post('register')
|
@Post('register')
|
||||||
@Returns(UserRegisterResponse)
|
@Returns(UserRegisterResponse)
|
||||||
@RequiredPermissions(Permission.UserRegister)
|
@RequiredPermissions(Permission.UserRegister)
|
||||||
@Throttle(5, 300)
|
@EasyThrottle(5, 300)
|
||||||
async register(
|
async register(
|
||||||
@Body() register: UserRegisterRequest,
|
@Body() register: UserRegisterRequest,
|
||||||
): Promise<UserRegisterResponse> {
|
): Promise<UserRegisterResponse> {
|
||||||
@@ -59,7 +59,7 @@ export class UserController {
|
|||||||
@Post('checkname')
|
@Post('checkname')
|
||||||
@Returns(UserCheckNameResponse)
|
@Returns(UserCheckNameResponse)
|
||||||
@RequiredPermissions(Permission.UserRegister)
|
@RequiredPermissions(Permission.UserRegister)
|
||||||
@Throttle(20)
|
@EasyThrottle(20)
|
||||||
async checkName(
|
async checkName(
|
||||||
@Body() checkName: UserCheckNameRequest,
|
@Body() checkName: UserCheckNameRequest,
|
||||||
): Promise<UserCheckNameResponse> {
|
): Promise<UserCheckNameResponse> {
|
||||||
@@ -71,7 +71,7 @@ export class UserController {
|
|||||||
@Get('me')
|
@Get('me')
|
||||||
@Returns(UserMeResponse)
|
@Returns(UserMeResponse)
|
||||||
@RequiredPermissions(Permission.UserKeepLogin)
|
@RequiredPermissions(Permission.UserKeepLogin)
|
||||||
@Throttle(10)
|
@EasyThrottle(10)
|
||||||
async me(@ReqUserID() userid: string): Promise<UserMeResponse> {
|
async me(@ReqUserID() userid: string): Promise<UserMeResponse> {
|
||||||
const backenduser = ThrowIfFailed(await this.usersService.findOne(userid));
|
const backenduser = ThrowIfFailed(await this.usersService.findOne(userid));
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ export class UserController {
|
|||||||
@Get('me/permissions')
|
@Get('me/permissions')
|
||||||
@Returns(UserMePermissionsResponse)
|
@Returns(UserMePermissionsResponse)
|
||||||
@NoPermissions()
|
@NoPermissions()
|
||||||
@Throttle(20)
|
@EasyThrottle(20)
|
||||||
async refresh(
|
async refresh(
|
||||||
@ReqUserID() userid: string,
|
@ReqUserID() userid: string,
|
||||||
): Promise<UserMePermissionsResponse> {
|
): Promise<UserMePermissionsResponse> {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
Post,
|
Post,
|
||||||
Res,
|
Res,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { Throttle } from '@nestjs/throttler';
|
|
||||||
import type { FastifyReply } from 'fastify';
|
import type { FastifyReply } from 'fastify';
|
||||||
import {
|
import {
|
||||||
ImageDeleteRequest,
|
ImageDeleteRequest,
|
||||||
@@ -22,11 +21,12 @@ import {
|
|||||||
} 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 {
|
import {
|
||||||
Fail,
|
|
||||||
FT,
|
FT,
|
||||||
|
Fail,
|
||||||
HasFailed,
|
HasFailed,
|
||||||
ThrowIfFailed,
|
ThrowIfFailed,
|
||||||
} from 'picsur-shared/dist/types/failable';
|
} from 'picsur-shared/dist/types/failable';
|
||||||
|
import { EasyThrottle } from '../../decorators/easy-throttle.decorator';
|
||||||
import { PostFiles } from '../../decorators/multipart/multipart.decorator';
|
import { PostFiles } from '../../decorators/multipart/multipart.decorator';
|
||||||
import type { FileIterator } from '../../decorators/multipart/postfiles.pipe';
|
import type { FileIterator } from '../../decorators/multipart/postfiles.pipe';
|
||||||
import {
|
import {
|
||||||
@@ -37,6 +37,7 @@ import { ReqUserID } from '../../decorators/request-user.decorator';
|
|||||||
import { Returns } from '../../decorators/returns.decorator';
|
import { Returns } from '../../decorators/returns.decorator';
|
||||||
import { ImageManagerService } from '../../managers/image/image.service';
|
import { ImageManagerService } from '../../managers/image/image.service';
|
||||||
import { GetNextAsync } from '../../util/iterator';
|
import { GetNextAsync } from '../../util/iterator';
|
||||||
|
|
||||||
@Controller('api/image')
|
@Controller('api/image')
|
||||||
@RequiredPermissions(Permission.ImageUpload)
|
@RequiredPermissions(Permission.ImageUpload)
|
||||||
export class ImageManageController {
|
export class ImageManageController {
|
||||||
@@ -46,7 +47,7 @@ export class ImageManageController {
|
|||||||
|
|
||||||
@Post('upload')
|
@Post('upload')
|
||||||
@Returns(ImageUploadResponse)
|
@Returns(ImageUploadResponse)
|
||||||
@Throttle(20)
|
@EasyThrottle(20)
|
||||||
async uploadImage(
|
async uploadImage(
|
||||||
@PostFiles(1) multipart: FileIterator,
|
@PostFiles(1) multipart: FileIterator,
|
||||||
@ReqUserID() userid: string,
|
@ReqUserID() userid: string,
|
||||||
|
|||||||
@@ -21,9 +21,6 @@ export const HelmetOptions: FastifyHelmetOptions = {
|
|||||||
// Destroy reference to global object on new page
|
// Destroy reference to global object on new page
|
||||||
crossOriginOpenerPolicy: true,
|
crossOriginOpenerPolicy: true,
|
||||||
crossOriginResourcePolicy: true,
|
crossOriginResourcePolicy: true,
|
||||||
// Dont fully understand the purpose of this
|
|
||||||
// But pretty sure we dont need it
|
|
||||||
expectCt: false,
|
|
||||||
// Do not send referrer header
|
// Do not send referrer header
|
||||||
referrerPolicy: true,
|
referrerPolicy: true,
|
||||||
// Ensure browser doesnt connect with HTTP
|
// Ensure browser doesnt connect with HTTP
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ async function bmpSharpOut(sharpImage: Sharp): Promise<SharpResult> {
|
|||||||
const encoded = BMPencode(raw.data, {
|
const encoded = BMPencode(raw.data, {
|
||||||
width: raw.info.width,
|
width: raw.info.width,
|
||||||
height: raw.info.height,
|
height: raw.info.height,
|
||||||
channels: raw.info.channels,
|
channels: raw.info.channels as 3 | 4,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -141,7 +141,7 @@ async function qoiSharpOut(sharpImage: Sharp): Promise<SharpResult> {
|
|||||||
const encoded = QOIencode(raw.data, {
|
const encoded = QOIencode(raw.data, {
|
||||||
width: raw.info.width,
|
width: raw.info.width,
|
||||||
height: raw.info.height,
|
height: raw.info.height,
|
||||||
channels: raw.info.channels,
|
channels: raw.info.channels as 3 | 4,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -43,7 +43,8 @@
|
|||||||
"ngx-auto-unsubscribe-decorator",
|
"ngx-auto-unsubscribe-decorator",
|
||||||
"moment",
|
"moment",
|
||||||
"platform",
|
"platform",
|
||||||
"form-data"
|
"form-data",
|
||||||
|
"ms"
|
||||||
],
|
],
|
||||||
"optimization": true,
|
"optimization": true,
|
||||||
"webWorkerTsConfig": "tsconfig.worker.json",
|
"webWorkerTsConfig": "tsconfig.worker.json",
|
||||||
|
|||||||
@@ -14,40 +14,40 @@
|
|||||||
"purge": "rm -rf dist && rm -rf node_modules && rm -rf .angular"
|
"purge": "rm -rf dist && rm -rf node_modules && rm -rf .angular"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-builders/custom-webpack": "^16.0.0",
|
"@angular-builders/custom-webpack": "^17.0.0",
|
||||||
"@angular-devkit/build-angular": "^16.1.0",
|
"@angular-devkit/build-angular": "^17.0.3",
|
||||||
"@angular/animations": "^16.1.1",
|
"@angular/animations": "^17.0.4",
|
||||||
"@angular/cdk": "^16.1.1",
|
"@angular/cdk": "^17.0.1",
|
||||||
"@angular/cli": "^16.1.0",
|
"@angular/cli": "^17.0.3",
|
||||||
"@angular/common": "^16.1.1",
|
"@angular/common": "^17.0.4",
|
||||||
"@angular/compiler": "^16.1.1",
|
"@angular/compiler": "^17.0.4",
|
||||||
"@angular/compiler-cli": "^16.1.1",
|
"@angular/compiler-cli": "^17.0.4",
|
||||||
"@angular/core": "^16.1.1",
|
"@angular/core": "^17.0.4",
|
||||||
"@angular/forms": "^16.1.1",
|
"@angular/forms": "^17.0.4",
|
||||||
"@angular/material": "^16.1.1",
|
"@angular/material": "^17.0.1",
|
||||||
"@angular/platform-browser": "^16.1.1",
|
"@angular/platform-browser": "^17.0.4",
|
||||||
"@angular/platform-browser-dynamic": "^16.1.1",
|
"@angular/platform-browser-dynamic": "^17.0.4",
|
||||||
"@angular/router": "^16.1.1",
|
"@angular/router": "^17.0.4",
|
||||||
"@babel/cli": "^7.22.5",
|
"@babel/cli": "^7.23.4",
|
||||||
"@babel/core": "^7.22.5",
|
"@babel/core": "^7.23.3",
|
||||||
"@babel/preset-env": "^7.22.5",
|
"@babel/preset-env": "^7.23.3",
|
||||||
"@fontsource/roboto": "^5.0.3",
|
"@fontsource/roboto": "^5.0.8",
|
||||||
"@ng-web-apis/common": "^2.1.0",
|
"@ng-web-apis/common": "^3.0.6",
|
||||||
"@ng-web-apis/resize-observer": "^2.0.0",
|
"@ng-web-apis/resize-observer": "^3.0.6",
|
||||||
"@ngui/common": "^1.0.0",
|
"@ngui/common": "^1.0.0",
|
||||||
"@popperjs/core": "^2.11.8",
|
"@popperjs/core": "^2.11.8",
|
||||||
"@types/ackee-tracker": "^5.0.2",
|
"@types/ackee-tracker": "^5.0.4",
|
||||||
"@types/node": "^20.3.1",
|
"@types/node": "^20.10.0",
|
||||||
"@types/resize-observer-browser": "^0.1.7",
|
"@types/resize-observer-browser": "^0.1.11",
|
||||||
"@types/validator": "^13.7.17",
|
"@types/validator": "^13.11.7",
|
||||||
"ackee-tracker": "^5.1.0",
|
"ackee-tracker": "^5.1.0",
|
||||||
"axios": "^1.4.0",
|
"axios": "^1.6.2",
|
||||||
"babel-loader": "^9.1.2",
|
"babel-loader": "^9.1.3",
|
||||||
"bootstrap": "^5.3.0",
|
"bootstrap": "^5.3.2",
|
||||||
"browserslist": "^4.21.8",
|
"browserslist": "^4.22.1",
|
||||||
"caniuse-lite": "^1.0.30001503",
|
"caniuse-lite": "^1.0.30001565",
|
||||||
"fuse.js": "^6.6.2",
|
"fuse.js": "^7.0.0",
|
||||||
"material-icons": "^1.13.8",
|
"material-icons": "^1.13.12",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"ng-mat-select-infinite-scroll": "^4.0.0",
|
"ng-mat-select-infinite-scroll": "^4.0.0",
|
||||||
"ngx-auto-unsubscribe-decorator": "^1.1.0",
|
"ngx-auto-unsubscribe-decorator": "^1.1.0",
|
||||||
@@ -56,11 +56,11 @@
|
|||||||
"picsur-shared": "*",
|
"picsur-shared": "*",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
"tslib": "^2.5.3",
|
"tslib": "^2.6.2",
|
||||||
"typescript": "^5.1.3",
|
"typescript": "^5.3.2",
|
||||||
"webpack-bundle-analyzer": "^4.9.0",
|
"webpack-bundle-analyzer": "^4.10.1",
|
||||||
"zod": "^3.21.4",
|
"zod": "^3.22.4",
|
||||||
"zone.js": "^0.13.1"
|
"zone.js": "^0.14.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@leteu/jwt-decoder": "^1.0.4"
|
"@leteu/jwt-decoder": "^1.0.4"
|
||||||
|
|||||||
@@ -52,8 +52,10 @@ mat-toolbar {
|
|||||||
/* Make sure the toolbar will stay on top of the content as it scrolls past. */
|
/* Make sure the toolbar will stay on top of the content as it scrolls past. */
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
|
||||||
box-shadow: 0px 2px 5px -3px rgba(0, 0, 0, 0.2),
|
box-shadow:
|
||||||
0px 5px 8px 0px rgba(0, 0, 0, 0.14), 0px 1px 14px 0px rgba(0, 0, 0, 0.12);
|
0px 2px 5px -3px rgba(0, 0, 0, 0.2),
|
||||||
|
0px 5px 8px 0px rgba(0, 0, 0, 0.14),
|
||||||
|
0px 1px 14px 0px rgba(0, 0, 0, 0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-bar {
|
.loading-bar {
|
||||||
|
|||||||
@@ -80,12 +80,12 @@ export class PrefOptionComponent implements OnInit {
|
|||||||
this.pref.type === 'string'
|
this.pref.type === 'string'
|
||||||
? `Updated ${this.name}`
|
? `Updated ${this.name}`
|
||||||
: this.pref.type === 'number'
|
: this.pref.type === 'number'
|
||||||
? `Updated ${this.name}`
|
? `Updated ${this.name}`
|
||||||
: this.pref.type === 'boolean'
|
: this.pref.type === 'boolean'
|
||||||
? value
|
? value
|
||||||
? `Enabled ${this.name}`
|
? `Enabled ${this.name}`
|
||||||
: `Disabled ${this.name}`
|
: `Disabled ${this.name}`
|
||||||
: '';
|
: '';
|
||||||
this.errorService.success(message);
|
this.errorService.success(message);
|
||||||
} else {
|
} else {
|
||||||
this.errorService.showFailure(result, this.logger);
|
this.errorService.showFailure(result, this.logger);
|
||||||
|
|||||||
@@ -29,18 +29,18 @@ export class InfoService {
|
|||||||
return this.infoSubject.value;
|
return this.infoSubject.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private infoSubject: BehaviorSubject<ServerInfo>;
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(LOCATION) private readonly location: Location,
|
@Inject(LOCATION) private readonly location: Location,
|
||||||
private readonly api: ApiService,
|
private readonly api: ApiService,
|
||||||
private readonly infoStorage: InfoStorageService,
|
private readonly infoStorage: InfoStorageService,
|
||||||
) {
|
) {
|
||||||
this.updateInfo().catch((e) => this.logger.warn(e));
|
this.updateInfo().catch((e) => this.logger.warn(e));
|
||||||
|
this.infoSubject = new BehaviorSubject<ServerInfo>(
|
||||||
|
this.infoStorage?.get() ?? new ServerInfo(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private infoSubject = new BehaviorSubject<ServerInfo>(
|
|
||||||
this.infoStorage?.get() ?? new ServerInfo(),
|
|
||||||
);
|
|
||||||
|
|
||||||
public async getLoadedSnapshot(): Promise<ServerInfo> {
|
public async getLoadedSnapshot(): Promise<ServerInfo> {
|
||||||
if (this.isLoaded()) {
|
if (this.isLoaded()) {
|
||||||
return this.snapshot;
|
return this.snapshot;
|
||||||
|
|||||||
@@ -60,16 +60,16 @@ export class BootstrapService {
|
|||||||
const size = xxxl
|
const size = xxxl
|
||||||
? BSScreenSize.xxxl
|
? BSScreenSize.xxxl
|
||||||
: xxl
|
: xxl
|
||||||
? BSScreenSize.xxl
|
? BSScreenSize.xxl
|
||||||
: xl
|
: xl
|
||||||
? BSScreenSize.xl
|
? BSScreenSize.xl
|
||||||
: lg
|
: lg
|
||||||
? BSScreenSize.lg
|
? BSScreenSize.lg
|
||||||
: md
|
: md
|
||||||
? BSScreenSize.md
|
? BSScreenSize.md
|
||||||
: sm
|
: sm
|
||||||
? BSScreenSize.sm
|
? BSScreenSize.sm
|
||||||
: BSScreenSize.xs;
|
: BSScreenSize.xs;
|
||||||
|
|
||||||
this.screenSizeSubject.next(size);
|
this.screenSizeSubject.next(size);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
|
|||||||
18
package.json
18
package.json
@@ -23,22 +23,22 @@
|
|||||||
"terser": ">=5.14.2",
|
"terser": ">=5.14.2",
|
||||||
"typeorm": ">=0.3.9",
|
"typeorm": ">=0.3.9",
|
||||||
"jsonwebtoken": ">=9.0.0",
|
"jsonwebtoken": ">=9.0.0",
|
||||||
"typescript": "~4.9.5",
|
"typescript": "~5.2.0",
|
||||||
"webpack": ">=5.76.0",
|
"webpack": ">=5.76.0",
|
||||||
"fastify-static": "npm:@fastify/static@*",
|
"fastify-static": "npm:@fastify/static@*",
|
||||||
"fastify-formbody": "npm:@fastify/formbody@*",
|
"fastify-formbody": "npm:@fastify/formbody@*",
|
||||||
"minimist": "npm:minimist-lite@*"
|
"minimist": "npm:minimist-lite@*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "^5.59.11",
|
"@typescript-eslint/eslint-plugin": "^6.13.1",
|
||||||
"@typescript-eslint/parser": "^5.59.11",
|
"@typescript-eslint/parser": "^6.13.1",
|
||||||
"eslint": "^8.42.0",
|
"eslint": "^8.54.0",
|
||||||
"eslint-config-prettier": "^8.8.0",
|
"eslint-config-prettier": "^9.0.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^5.0.1",
|
||||||
"eslint-plugin-require-extensions": "^0.1.3",
|
"eslint-plugin-require-extensions": "^0.1.3",
|
||||||
"ms": "3.0.0-canary.1",
|
"ms": "2.1.3",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^3.1.0",
|
||||||
"prettier-plugin-sh": "^0.12.8"
|
"prettier-plugin-sh": "^0.13.1"
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@3.2.2"
|
"packageManager": "yarn@3.2.2"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,12 +14,13 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": "2.1.3",
|
"ms": "2.1.3",
|
||||||
"zod": "^3.21.4"
|
"zod": "^3.22.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.3.1",
|
"@types/ms": "^0.7.34",
|
||||||
|
"@types/node": "^20.10.0",
|
||||||
"tsc-watch": "^6.0.4",
|
"tsc-watch": "^6.0.4",
|
||||||
"typescript": "^5.1.3"
|
"typescript": "^5.3.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"clean": "rm -rf ./dist",
|
"clean": "rm -rf ./dist",
|
||||||
|
|||||||
@@ -2,24 +2,24 @@ import { z } from 'zod';
|
|||||||
import { EImageSchema } from '../../entities/image.entity';
|
import { EImageSchema } from '../../entities/image.entity';
|
||||||
import { EUserSchema } from '../../entities/user.entity';
|
import { EUserSchema } from '../../entities/user.entity';
|
||||||
import { createZodDto } from '../../util/create-zod-dto';
|
import { createZodDto } from '../../util/create-zod-dto';
|
||||||
import { ParseBool, ParseInt } from '../../util/parse-simple';
|
import { ParseBoolZ, ParseIntZ } from '../../util/parse-simple';
|
||||||
import { ImageEntryVariant } from '../image-entry-variant.enum';
|
import { ImageEntryVariant } from '../image-entry-variant.enum';
|
||||||
|
|
||||||
export const ImageRequestParamsSchema = z
|
export const ImageRequestParamsSchema = z
|
||||||
.object({
|
.object({
|
||||||
height: z.preprocess(ParseInt, z.number().int().min(1).max(32767)),
|
height: z.preprocess(ParseIntZ, z.number().int().min(1).max(32767)),
|
||||||
width: z.preprocess(ParseInt, z.number().int().min(1).max(32767)),
|
width: z.preprocess(ParseIntZ, z.number().int().min(1).max(32767)),
|
||||||
rotate: z.preprocess(
|
rotate: z.preprocess(
|
||||||
ParseInt,
|
ParseIntZ,
|
||||||
z.number().int().multipleOf(90).min(0).max(360),
|
z.number().int().multipleOf(90).min(0).max(360),
|
||||||
),
|
),
|
||||||
flipx: z.preprocess(ParseBool, z.boolean()),
|
flipx: z.preprocess(ParseBoolZ, z.boolean()),
|
||||||
flipy: z.preprocess(ParseBool, z.boolean()),
|
flipy: z.preprocess(ParseBoolZ, z.boolean()),
|
||||||
greyscale: z.preprocess(ParseBool, z.boolean()),
|
greyscale: z.preprocess(ParseBoolZ, z.boolean()),
|
||||||
noalpha: z.preprocess(ParseBool, z.boolean()),
|
noalpha: z.preprocess(ParseBoolZ, z.boolean()),
|
||||||
negative: z.preprocess(ParseBool, z.boolean()),
|
negative: z.preprocess(ParseBoolZ, z.boolean()),
|
||||||
shrinkonly: z.preprocess(ParseBool, z.boolean()),
|
shrinkonly: z.preprocess(ParseBoolZ, z.boolean()),
|
||||||
quality: z.preprocess(ParseInt, z.number().int().min(1).max(100)),
|
quality: z.preprocess(ParseIntZ, z.number().int().min(1).max(100)),
|
||||||
})
|
})
|
||||||
.partial();
|
.partial();
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ export const ParseBool = <T extends boolean | null = null>(
|
|||||||
return fallback === undefined ? (null as T) : fallback;
|
return fallback === undefined ? (null as T) : fallback;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ParseBoolZ = (value: unknown): boolean | null => {
|
||||||
|
return ParseBool(value, null);
|
||||||
|
};
|
||||||
|
|
||||||
export const ParseInt = <T extends number | null = null>(
|
export const ParseInt = <T extends number | null = null>(
|
||||||
value: unknown,
|
value: unknown,
|
||||||
fallback?: T,
|
fallback?: T,
|
||||||
@@ -23,8 +27,12 @@ export const ParseInt = <T extends number | null = null>(
|
|||||||
return fallback === undefined
|
return fallback === undefined
|
||||||
? (null as T)
|
? (null as T)
|
||||||
: fallback === null
|
: fallback === null
|
||||||
? fallback
|
? fallback
|
||||||
: Math.round(fallback);
|
: Math.round(fallback);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ParseIntZ = (value: unknown): number | null => {
|
||||||
|
return ParseInt(value, null);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ParseString = <T extends string | null = null>(
|
export const ParseString = <T extends string | null = null>(
|
||||||
@@ -36,3 +44,7 @@ export const ParseString = <T extends string | null = null>(
|
|||||||
if (typeof value === 'number') return value.toString();
|
if (typeof value === 'number') return value.toString();
|
||||||
return fallback === undefined ? (null as T) : fallback;
|
return fallback === undefined ? (null as T) : fallback;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ParseStringZ = (value: unknown): string | null => {
|
||||||
|
return ParseString(value, null);
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user