From ea466ee848824d235f5a0e662d405fb9522cd524 Mon Sep 17 00:00:00 2001 From: Ivan Gabaldon Date: Sun, 18 Feb 2024 12:33:19 +0100 Subject: [PATCH] initial rework --- src/classes/AbstractEndpoint.ts | 6 +- src/classes/DocumentHandler.ts | 223 ++++++++++++++------------------ src/classes/JSPError.ts | 2 +- src/classes/Server.ts | 57 ++++---- src/index.ts | 4 +- src/routes/AccessRawV1.ts | 23 ++-- src/routes/AccessRawV2.ts | 22 ++-- src/routes/AccessV1.ts | 17 ++- src/routes/AccessV2.ts | 26 ++-- src/routes/EditV2.ts | 26 ++-- src/routes/ExistsV2.ts | 15 ++- src/routes/IndexV1.ts | 7 +- src/routes/IndexV2.ts | 7 +- src/routes/PublishV1.ts | 15 ++- src/routes/PublishV2.ts | 32 +++-- src/routes/RemoveV1.ts | 26 ++-- src/routes/RemoveV2.ts | 26 ++-- src/types/DocumentHandler.ts | 17 +-- 18 files changed, 267 insertions(+), 284 deletions(-) diff --git a/src/classes/AbstractEndpoint.ts b/src/classes/AbstractEndpoint.ts index 2fe0dd8..3d78b9e 100644 --- a/src/classes/AbstractEndpoint.ts +++ b/src/classes/AbstractEndpoint.ts @@ -1,9 +1,9 @@ -import type { Elysia } from 'elysia'; +import type { Server } from './Server.ts'; export abstract class AbstractEndpoint { - protected readonly server: Elysia; + protected readonly server: Server; - protected constructor(server: Elysia) { + protected constructor(server: Server) { this.server = server; } diff --git a/src/classes/DocumentHandler.ts b/src/classes/DocumentHandler.ts index c23c0cf..e2849f1 100644 --- a/src/classes/DocumentHandler.ts +++ b/src/classes/DocumentHandler.ts @@ -1,158 +1,138 @@ -import { unlink } from 'node:fs/promises'; +import type { Access, Edit, Exists, Publish, Remove } from '../types/DocumentHandler.ts'; +import { ServerVersion } from '../types/Server.ts'; import { ValidatorUtils } from '../utils/ValidatorUtils.ts'; +import { JSPError } from './JSPError.ts'; +import { ErrorCode } from '../types/JSPError.ts'; +import { Server } from './Server.ts'; import { DocumentManager } from './DocumentManager.ts'; +import { unlink } from 'node:fs/promises'; import { StringUtils } from '../utils/StringUtils.ts'; import type { IDocumentDataStruct } from '../structures/Structures'; -import type { - HandleAccess, - HandleEdit, - HandleExists, - HandleGetDocument, - HandlePublish, - HandleRemove -} from '../types/DocumentHandler.ts'; -import { ServerVersion } from '../types/Server.ts'; -import { JSPError } from './JSPError.ts'; -import { Server } from './Server.ts'; import type { Range } from '../types/Range.ts'; -import { ErrorCode, type ErrorType } from '../types/JSPError.ts'; +import type { BunFile } from 'bun'; export class DocumentHandler { - public static async handleAccess( - set: any, - { key, password, raw }: HandleAccess & { raw?: never }, - version: ServerVersion - ): Promise< - | ErrorType - | { - key: string; - data: string; - url?: string; - expirationTimestamp?: number; - } - >; - public static async handleAccess( - set: any, - { key, password, raw }: HandleAccess & { raw: true }, - version: ServerVersion - ): Promise; - public static async handleAccess( - set: any, - { key, password, raw = false }: HandleAccess, - version: ServerVersion - ): Promise< - | ErrorType - | Response - | { - key: string; - data: string; - url?: string; - expirationTimestamp?: number; - } - > { - const res = await DocumentHandler.handleGetDocument(set, { key: key, password }); - if (ValidatorUtils.isJSPError(res)) return res; - - if (raw) return new Response(res.rawFileData); - - const data = new TextDecoder().decode(res.rawFileData); + private readonly server: Server; + private context: any; + + public constructor(server: Server) { + this.server = server; + } + + public set setContext(value: any) { + this.context = value; + } + + public async access(params: Access, version: ServerVersion) { + this.validateKeyValue(params.key); + + const file = await this.validateKeyExistance(params.key); + const document = await DocumentManager.read(file); + + if ( + document.expirationTimestamp && + ValidatorUtils.isLengthBetweenLimits(document.expirationTimestamp, 0, Date.now()) + ) { + await unlink(Server.config.documents.documentPath + params.key); + + throw JSPError.send(this.context, 404, JSPError.message[ErrorCode.documentNotFound]); + } + + if (document.password && document.password !== params.password) { + throw JSPError.send(this.context, 403, JSPError.message[ErrorCode.documentInvalidPassword]); + } + + const data = new TextDecoder().decode(document.rawFileData); switch (version) { case ServerVersion.v1: - return { key, data }; + return { key: params.key, data }; case ServerVersion.v2: return { - key, + key: params.key, data, - url: (Server.config.tls ? 'https://' : 'http://').concat(Server.config.domain + '/') + key, - expirationTimestamp: res.expirationTimestamp ? Number(res.expirationTimestamp) : undefined + url: (Server.config.tls ? 'https://' : 'http://').concat(Server.config.domain + '/') + params.key, + expirationTimestamp: document.expirationTimestamp }; } } - public static async handleEdit(set: any, { key, newBody, secret }: HandleEdit) { - if (!ValidatorUtils.isStringLengthBetweenLimits(key, 1, 255) || !ValidatorUtils.isAlphanumeric(key)) - return JSPError.send(set, 400, JSPError.message[ErrorCode.inputInvalid]); + public async edit(params: Edit) { + this.validateKeyValue(params.key); - const file = Bun.file(Server.config.documents.documentPath + key); - const fileExists = await file.exists(); - - if (!fileExists) return JSPError.send(set, 404, JSPError.message[ErrorCode.documentNotFound]); - - const buffer = Buffer.from(newBody as ArrayBuffer); + const buffer = Buffer.from(params.newBody as ArrayBuffer); if (!ValidatorUtils.isLengthBetweenLimits(buffer, 1, Server.config.documents.maxLength)) - return JSPError.send(set, 400, JSPError.message[ErrorCode.documentInvalidLength]); + throw JSPError.send(this.context, 400, JSPError.message[ErrorCode.documentInvalidLength]); - const doc = await DocumentManager.read(file); + const file = await this.validateKeyExistance(params.key); + const document = await DocumentManager.read(file); - if (doc.secret && doc.secret !== secret) - return JSPError.send(set, 403, JSPError.message[ErrorCode.documentInvalidSecret]); + if (document.secret && document.secret !== params.secret) { + throw JSPError.send(this.context, 403, JSPError.message[ErrorCode.documentInvalidSecret]); + } - doc.rawFileData = buffer; + document.rawFileData = buffer; return { - edited: await DocumentManager.write(Server.config.documents.documentPath + key, doc) + edited: await DocumentManager.write(Server.config.documents.documentPath + params.key, document) .then(() => true) .catch(() => false) }; } - public static async handleExists(set: any, { key }: HandleExists) { - if (!ValidatorUtils.isStringLengthBetweenLimits(key, 1, 255) || !ValidatorUtils.isAlphanumeric(key)) - return JSPError.send(set, 400, JSPError.message[ErrorCode.inputInvalid]); + public async exists(params: Exists): Promise { + this.validateKeyValue(params.key); - return await Bun.file(Server.config.documents.documentPath + key).exists(); + return Bun.file(Server.config.documents.documentPath + params.key).exists(); } - public static async handlePublish( - set: any, - { body, selectedSecret, lifetime, password, selectedKeyLength, selectedKey }: HandlePublish, - version: ServerVersion - ) { - const buffer = Buffer.from(body as ArrayBuffer); + // TODO: Rework publish + public async publish(params: Publish, version: ServerVersion) { + const buffer = Buffer.from(params.body as ArrayBuffer); if (!ValidatorUtils.isLengthBetweenLimits(buffer, 1, Server.config.documents.maxLength)) - return JSPError.send(set, 400, JSPError.message[ErrorCode.documentInvalidLength]); + throw JSPError.send(this.context, 400, JSPError.message[ErrorCode.documentInvalidLength]); - const secret = selectedSecret || StringUtils.createSecret(); + const secret = params.selectedSecret || StringUtils.createSecret(); if (!ValidatorUtils.isStringLengthBetweenLimits(secret || '', 1, 255)) - return JSPError.send(set, 400, JSPError.message[ErrorCode.documentInvalidSecretLength]); + throw JSPError.send(this.context, 400, JSPError.message[ErrorCode.documentInvalidSecretLength]); if ( - selectedKey && - (!ValidatorUtils.isStringLengthBetweenLimits(selectedKey, 2, 32) || - !ValidatorUtils.isAlphanumeric(selectedKey)) + params.selectedKey && + (!ValidatorUtils.isStringLengthBetweenLimits(params.selectedKey, 2, 32) || + !ValidatorUtils.isAlphanumeric(params.selectedKey)) ) - return JSPError.send(set, 400, JSPError.message[ErrorCode.inputInvalid]); + throw JSPError.send(this.context, 400, JSPError.message[ErrorCode.inputInvalid]); - if (selectedKeyLength && (selectedKeyLength > 32 || selectedKeyLength < 2)) - return JSPError.send(set, 400, JSPError.message[ErrorCode.documentInvalidKeyLength]); + if (params.selectedKeyLength && (params.selectedKeyLength > 32 || params.selectedKeyLength < 2)) + throw JSPError.send(this.context, 400, JSPError.message[ErrorCode.documentInvalidKeyLength]); - if (password && !ValidatorUtils.isStringLengthBetweenLimits(password, 0, 255)) - return JSPError.send(set, 400, JSPError.message[ErrorCode.documentInvalidPasswordLength]); + if (params.password && !ValidatorUtils.isStringLengthBetweenLimits(params.password, 0, 255)) + throw JSPError.send(this.context, 400, JSPError.message[ErrorCode.documentInvalidPasswordLength]); - lifetime = lifetime ?? Server.config.documents.maxTime; + params.lifetime = params.lifetime ?? Server.config.documents.maxTime; // Make the document permanent if the value exceeds 5 years - if (lifetime > 157_784_760) lifetime = 0; + if (params.lifetime > 157_784_760) params.lifetime = 0; - const msLifetime = lifetime * 1000; + const msLifetime = params.lifetime * 1000; const expirationTimestamp = msLifetime > 0 ? BigInt(Date.now() + msLifetime) : undefined; const newDoc: IDocumentDataStruct = { rawFileData: buffer, secret, expirationTimestamp, - password + password: params.password }; - const key = selectedKey || (await StringUtils.createKey((selectedKeyLength as Range<2, 32>) || 8)); + const key = + params.selectedKey || (await StringUtils.createKey((params.selectedKeyLength as Range<2, 32>) || 8)); - if (selectedKey && (await StringUtils.keyExists(key))) - return JSPError.send(set, 400, JSPError.message[ErrorCode.documentKeyAlreadyExists]); + if (params.selectedKey && (await StringUtils.keyExists(key))) + throw JSPError.send(this.context, 400, JSPError.message[ErrorCode.documentKeyAlreadyExists]); await DocumentManager.write(Server.config.documents.documentPath + key, newDoc); @@ -170,49 +150,36 @@ export class DocumentHandler { } } - public static async handleRemove(set: any, { key, secret }: HandleRemove) { - if (!ValidatorUtils.isStringLengthBetweenLimits(key, 1, 255) || !ValidatorUtils.isAlphanumeric(key)) - return JSPError.send(set, 400, JSPError.message[ErrorCode.inputInvalid]); + public async remove(params: Remove) { + this.validateKeyValue(params.key); - const file = Bun.file(Server.config.documents.documentPath + key); - const fileExists = await file.exists(); - - if (!fileExists) return JSPError.send(set, 404, JSPError.message[ErrorCode.documentNotFound]); - - const doc = await DocumentManager.read(file); + const file = await this.validateKeyExistance(params.key); + const document = await DocumentManager.read(file); - if (doc.secret && doc.secret !== secret) - return JSPError.send(set, 403, JSPError.message[ErrorCode.documentInvalidSecret]); + if (document.secret && document.secret !== params.secret) { + throw JSPError.send(this.context, 403, JSPError.message[ErrorCode.documentInvalidSecret]); + } return { - // TODO: Use optimized Bun.unlink when available -> https://bun.sh/docs/api/file-io#writing-files-bun-write - removed: await unlink(Server.config.documents.documentPath + key) + removed: await unlink(Server.config.documents.documentPath + params.key) .then(() => true) .catch(() => false) }; } - private static async handleGetDocument(set: any, { key, password }: HandleGetDocument) { - if (!ValidatorUtils.isStringLengthBetweenLimits(key, 1, 255) || !ValidatorUtils.isAlphanumeric(key)) - return JSPError.send(set, 400, JSPError.message[ErrorCode.inputInvalid]); + private validateKeyValue(key: string): void { + if (!ValidatorUtils.isAlphanumeric(key) || !ValidatorUtils.isStringLengthBetweenLimits(key, 2, 32)) { + throw JSPError.send(this.context, 400, JSPError.message[ErrorCode.inputInvalid]); + } + } + private async validateKeyExistance(key: string): Promise { const file = Bun.file(Server.config.documents.documentPath + key); - const fileExists = await file.exists(); - const doc = fileExists && (await DocumentManager.read(file)); - - if (!doc || (doc.expirationTimestamp && doc.expirationTimestamp > 0 && doc.expirationTimestamp < Date.now())) { - // TODO: Use optimized Bun.unlink when available -> https://bun.sh/docs/api/file-io#writing-files-bun-write - if (fileExists) await unlink(Server.config.documents.documentPath + key).catch(() => null); - return JSPError.send(set, 404, JSPError.message[ErrorCode.documentNotFound]); + if (!(await file.exists())) { + throw JSPError.send(this.context, 404, JSPError.message[ErrorCode.documentNotFound]); } - if (doc.password && !password) - return JSPError.send(set, 401, JSPError.message[ErrorCode.documentPasswordNeeded]); - - if (doc.password && doc.password !== password) - return JSPError.send(set, 403, JSPError.message[ErrorCode.documentInvalidPassword]); - - return doc; + return file; } } diff --git a/src/classes/JSPError.ts b/src/classes/JSPError.ts index 055f0d5..a9b32e2 100644 --- a/src/classes/JSPError.ts +++ b/src/classes/JSPError.ts @@ -80,7 +80,7 @@ export class JSPError { } }; - public static readonly errorSchema = t.Object( + public static readonly schema = t.Object( { type: t.String({ description: 'The error type' }), message: t.String({ description: 'The error message' }), diff --git a/src/classes/Server.ts b/src/classes/Server.ts index 2b70f0c..f474b36 100644 --- a/src/classes/Server.ts +++ b/src/classes/Server.ts @@ -16,6 +16,7 @@ import { AccessRawV2 } from '../routes/AccessRawV2.ts'; import { type ServerOptions, ServerVersion } from '../types/Server.ts'; import { JSPError } from './JSPError.ts'; import * as env from 'env-var'; +import { DocumentHandler } from './DocumentHandler.ts'; import { ErrorCode } from '../types/JSPError.ts'; export class Server { @@ -42,30 +43,30 @@ export class Server { level: 6 } }; + private readonly elysia: Elysia = new Elysia(); + private readonly documentHandler: DocumentHandler = new DocumentHandler(this); - private readonly server: Elysia = this.createServer(); + public constructor() { + this.initCORS(); + Server.config.docs.enabled && this.initDocs(); + this.initErrorHandler(); + this.initRoutes(); - public get self(): Elysia { - return this.server; + this.elysia.listen(Server.config.port, ({ port }) => + console.info('Listening on port', port, `-> http://localhost:${port}`) + ); } - private createServer(): Elysia { - const server = new Elysia(); - - this.initCORS(server); - Server.config.docs.enabled && this.initDocs(server); - this.initErrorHandler(server); - this.initRoutes(server); - - server.listen(Server.config.port, (server) => - console.info('Listening on port', server.port, `-> http://localhost:${server.port}`) - ); + public get getElysia(): Elysia { + return this.elysia; + } - return server; + public get getDocumentHandler(): DocumentHandler { + return this.documentHandler; } - private initCORS(server: Elysia): void { - server.use( + private initCORS(): void { + this.elysia.use( cors({ origin: true, methods: ['GET', 'POST', 'DELETE', 'HEAD', 'OPTIONS', 'PATCH'] @@ -73,8 +74,8 @@ export class Server { ); } - private initDocs(server: Elysia): void { - server.use( + private initDocs(): void { + this.elysia.use( swagger({ documentation: { servers: [ @@ -106,30 +107,30 @@ export class Server { ); } - private initErrorHandler(server: Elysia): void { - server.onError(({ set, code, error }) => { + private initErrorHandler(): void { + this.elysia.onError(({ set, code, error }) => { switch (code) { case 'NOT_FOUND': - return 'Not found'; + return ''; case 'VALIDATION': - return JSPError.send(set, 400, JSPError.message[ErrorCode.validation]); + throw JSPError.send(set, 400, JSPError.message[ErrorCode.validation]); case 'INTERNAL_SERVER_ERROR': console.error(error); - return JSPError.send(set, 500, JSPError.message[ErrorCode.internalServerError]); + throw JSPError.send(set, 500, JSPError.message[ErrorCode.internalServerError]); case 'PARSE': - return JSPError.send(set, 400, JSPError.message[ErrorCode.parseFailed]); + throw JSPError.send(set, 400, JSPError.message[ErrorCode.parseFailed]); default: console.error(error); - return JSPError.send(set, 400, JSPError.message[ErrorCode.unknown]); + throw JSPError.send(set, 400, JSPError.message[ErrorCode.unknown]); } }); } - private initRoutes(server: Elysia): void { + private initRoutes(): void { const apiVersions = Server.config.versions.toReversed(); const routes = { [ServerVersion.v1]: { @@ -144,7 +145,7 @@ export class Server { for (const [i, version] of apiVersions.entries()) { routes[version].endpoints.forEach((Endpoint) => { - const endpoint = new Endpoint(server); + const endpoint = new Endpoint(this); routes[version].prefixes.forEach(endpoint.register.bind(endpoint)); }); diff --git a/src/index.ts b/src/index.ts index 5e25a21..2230a37 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,10 +4,10 @@ const server = new Server(); process .on('SIGTERM', () => { - server.self.stop(); + server.getElysia.stop(); process.exit(0); }) .on('SIGINT', () => { - server.self.stop(); + server.getElysia.stop(); process.exit(0); }); diff --git a/src/routes/AccessRawV1.ts b/src/routes/AccessRawV1.ts index 21eb839..5010f44 100644 --- a/src/routes/AccessRawV1.ts +++ b/src/routes/AccessRawV1.ts @@ -1,11 +1,11 @@ import { AbstractEndpoint } from '../classes/AbstractEndpoint.ts'; -import { type Elysia, t } from 'elysia'; -import { DocumentHandler } from '../classes/DocumentHandler.ts'; +import { t } from 'elysia'; import { ServerVersion } from '../types/Server.ts'; import { JSPError } from '../classes/JSPError.ts'; +import type { Server } from '../classes/Server.ts'; export class AccessRawV1 extends AbstractEndpoint { - public constructor(server: Elysia) { + public constructor(server: Server) { super(server); } @@ -28,8 +28,8 @@ export class AccessRawV1 extends AbstractEndpoint { description: 'The raw document', examples: ['Hello world'] }), - 400: JSPError.errorSchema, - 404: JSPError.errorSchema + 400: JSPError.schema, + 404: JSPError.schema }, detail: { summary: 'Get raw document', @@ -37,12 +37,19 @@ export class AccessRawV1 extends AbstractEndpoint { } }; - this.server.get( + this.server.getElysia.get( prefix.concat('/:key/raw'), - async ({ set, params: { key } }) => { + async ({ set, params }) => { set.headers['Content-Type'] = 'text/plain'; - return DocumentHandler.handleAccess(set, { key: key, raw: true }, ServerVersion.v1); + this.server.getDocumentHandler.setContext = set; + return this.server.getDocumentHandler.access( + { + key: params.key, + raw: true + }, + ServerVersion.v1 + ); }, hook ); diff --git a/src/routes/AccessRawV2.ts b/src/routes/AccessRawV2.ts index 657d851..4cd093b 100644 --- a/src/routes/AccessRawV2.ts +++ b/src/routes/AccessRawV2.ts @@ -1,11 +1,11 @@ import { AbstractEndpoint } from '../classes/AbstractEndpoint.ts'; -import { type Elysia, t } from 'elysia'; -import { DocumentHandler } from '../classes/DocumentHandler.ts'; +import { t } from 'elysia'; import { ServerVersion } from '../types/Server.ts'; import { JSPError } from '../classes/JSPError.ts'; +import type { Server } from '../classes/Server.ts'; export class AccessRawV2 extends AbstractEndpoint { - public constructor(server: Elysia) { + public constructor(server: Server) { super(server); } @@ -49,8 +49,8 @@ export class AccessRawV2 extends AbstractEndpoint { description: 'The raw document', examples: ['Hello world'] }), - 400: JSPError.errorSchema, - 404: JSPError.errorSchema + 400: JSPError.schema, + 404: JSPError.schema }, detail: { summary: 'Get raw document', @@ -58,16 +58,16 @@ export class AccessRawV2 extends AbstractEndpoint { } }; - this.server.get( + this.server.getElysia.get( prefix.concat('/:key/raw'), - async ({ set, request, query: { p }, params: { key } }) => { + async ({ set, headers, query, params }) => { set.headers['Content-Type'] = 'text/plain'; - return DocumentHandler.handleAccess( - set, + this.server.getDocumentHandler.setContext = set; + return this.server.getDocumentHandler.access( { - key, - password: request.headers.get('password') || p || '', + key: params.key, + password: headers.password || query.p || '', raw: true }, ServerVersion.v2 diff --git a/src/routes/AccessV1.ts b/src/routes/AccessV1.ts index 8dd87fa..aadcdc8 100644 --- a/src/routes/AccessV1.ts +++ b/src/routes/AccessV1.ts @@ -1,11 +1,11 @@ import { AbstractEndpoint } from '../classes/AbstractEndpoint.ts'; -import { type Elysia, t } from 'elysia'; -import { DocumentHandler } from '../classes/DocumentHandler.ts'; +import { t } from 'elysia'; import { ServerVersion } from '../types/Server.ts'; import { JSPError } from '../classes/JSPError.ts'; +import type { Server } from '../classes/Server.ts'; export class AccessV1 extends AbstractEndpoint { - public constructor(server: Elysia) { + public constructor(server: Server) { super(server); } @@ -31,15 +31,18 @@ export class AccessV1 extends AbstractEndpoint { }, { description: 'The document object' } ), - 400: JSPError.errorSchema, - 404: JSPError.errorSchema + 400: JSPError.schema, + 404: JSPError.schema }, detail: { summary: 'Get document', tags: ['v1'] } }; - this.server.get( + this.server.getElysia.get( prefix.concat('/:key'), - async ({ set, params: { key } }) => DocumentHandler.handleAccess(set, { key }, ServerVersion.v1), + async ({ set, params }) => { + this.server.getDocumentHandler.setContext = set; + return this.server.getDocumentHandler.access({ key: params.key }, ServerVersion.v1); + }, hook ); } diff --git a/src/routes/AccessV2.ts b/src/routes/AccessV2.ts index 97449de..c105cad 100644 --- a/src/routes/AccessV2.ts +++ b/src/routes/AccessV2.ts @@ -1,11 +1,11 @@ import { AbstractEndpoint } from '../classes/AbstractEndpoint.ts'; -import { type Elysia, t } from 'elysia'; -import { DocumentHandler } from '../classes/DocumentHandler.ts'; +import { t } from 'elysia'; import { ServerVersion } from '../types/Server.ts'; import { JSPError } from '../classes/JSPError.ts'; +import type { Server } from '../classes/Server.ts'; export class AccessV2 extends AbstractEndpoint { - public constructor(server: Elysia) { + public constructor(server: Server) { super(server); } @@ -68,23 +68,21 @@ export class AccessV2 extends AbstractEndpoint { 'The document object, including the key, the data, the display URL and an expiration timestamp for the document' } ), - 400: JSPError.errorSchema, - 404: JSPError.errorSchema + 400: JSPError.schema, + 404: JSPError.schema }, detail: { summary: 'Get document', tags: ['v2'] } }; - this.server.get( + this.server.getElysia.get( prefix.concat('/:key'), - async ({ set, request, query: { p }, params: { key } }) => - DocumentHandler.handleAccess( - set, - { - key, - password: request.headers.get('password') || p || '' - }, + async ({ set, query, headers, params }) => { + this.server.getDocumentHandler.setContext = set; + return this.server.getDocumentHandler.access( + { key: params.key, password: headers.password || query.p || '' }, ServerVersion.v2 - ), + ); + }, hook ); } diff --git a/src/routes/EditV2.ts b/src/routes/EditV2.ts index d34a06f..cc545ed 100644 --- a/src/routes/EditV2.ts +++ b/src/routes/EditV2.ts @@ -1,10 +1,10 @@ import { AbstractEndpoint } from '../classes/AbstractEndpoint.ts'; -import { type Elysia, t } from 'elysia'; -import { DocumentHandler } from '../classes/DocumentHandler.ts'; +import { t } from 'elysia'; import { JSPError } from '../classes/JSPError.ts'; +import type { Server } from '../classes/Server.ts'; export class EditV2 extends AbstractEndpoint { - public constructor(server: Elysia) { + public constructor(server: Server) { super(server); } @@ -33,21 +33,23 @@ export class EditV2 extends AbstractEndpoint { }, { description: 'A response object with a boolean' } ), - 400: JSPError.errorSchema, - 403: JSPError.errorSchema, - 404: JSPError.errorSchema + 400: JSPError.schema, + 403: JSPError.schema, + 404: JSPError.schema }, detail: { summary: 'Edit document', tags: ['v2'] } }; - this.server.patch( + this.server.getElysia.patch( prefix.concat('/:key'), - async ({ set, request, body, params: { key } }) => - DocumentHandler.handleEdit(set, { - key, + async ({ set, headers, body, params }) => { + this.server.getDocumentHandler.setContext = set; + return this.server.getDocumentHandler.edit({ + key: params.key, newBody: body, - secret: request.headers.get('secret') || '' - }), + secret: headers.secret + }); + }, hook ); } diff --git a/src/routes/ExistsV2.ts b/src/routes/ExistsV2.ts index bd68d54..9157799 100644 --- a/src/routes/ExistsV2.ts +++ b/src/routes/ExistsV2.ts @@ -1,10 +1,10 @@ import { AbstractEndpoint } from '../classes/AbstractEndpoint.ts'; -import { type Elysia, t } from 'elysia'; -import { DocumentHandler } from '../classes/DocumentHandler.ts'; +import { t } from 'elysia'; import { JSPError } from '../classes/JSPError.ts'; +import type { Server } from '../classes/Server.ts'; export class ExistsV2 extends AbstractEndpoint { - public constructor(server: Elysia) { + public constructor(server: Server) { super(server); } @@ -18,14 +18,17 @@ export class ExistsV2 extends AbstractEndpoint { }), response: { 200: t.Boolean({ description: 'A boolean indicating if the document exists' }), - 400: JSPError.errorSchema + 400: JSPError.schema }, detail: { summary: 'Check document', tags: ['v2'] } }; - this.server.get( + this.server.getElysia.get( prefix.concat('/:key/exists'), - async ({ set, params: { key } }) => DocumentHandler.handleExists(set, { key: key }), + async ({ set, params }) => { + this.server.getDocumentHandler.setContext = set; + return this.server.getDocumentHandler.exists(params); + }, hook ); } diff --git a/src/routes/IndexV1.ts b/src/routes/IndexV1.ts index 607154e..00a17c1 100644 --- a/src/routes/IndexV1.ts +++ b/src/routes/IndexV1.ts @@ -1,8 +1,9 @@ import { AbstractEndpoint } from '../classes/AbstractEndpoint.ts'; -import { type Elysia, t } from 'elysia'; +import { t } from 'elysia'; +import type { Server } from '../classes/Server.ts'; export class IndexV1 extends AbstractEndpoint { - public constructor(server: Elysia) { + public constructor(server: Server) { super(server); } @@ -15,6 +16,6 @@ export class IndexV1 extends AbstractEndpoint { detail: { summary: 'Index', tags: ['v1'] } }; - this.server.get(prefix, () => 'Welcome to JSPaste API v1', hook); + this.server.getElysia.get(prefix, 'Welcome to JSPaste API v1', hook); } } diff --git a/src/routes/IndexV2.ts b/src/routes/IndexV2.ts index 2d116d7..9ae52a4 100644 --- a/src/routes/IndexV2.ts +++ b/src/routes/IndexV2.ts @@ -1,8 +1,9 @@ import { AbstractEndpoint } from '../classes/AbstractEndpoint.ts'; -import { type Elysia, t } from 'elysia'; +import { t } from 'elysia'; +import type { Server } from '../classes/Server.ts'; export class IndexV2 extends AbstractEndpoint { - public constructor(server: Elysia) { + public constructor(server: Server) { super(server); } @@ -15,6 +16,6 @@ export class IndexV2 extends AbstractEndpoint { detail: { summary: 'Index', tags: ['v2'] } }; - this.server.get(prefix, () => 'Welcome to JSPaste API v2', hook); + this.server.getElysia.get(prefix, 'Welcome to JSPaste API v2', hook); } } diff --git a/src/routes/PublishV1.ts b/src/routes/PublishV1.ts index ac90f16..73a4e97 100644 --- a/src/routes/PublishV1.ts +++ b/src/routes/PublishV1.ts @@ -1,11 +1,11 @@ import { AbstractEndpoint } from '../classes/AbstractEndpoint.ts'; -import { type Elysia, t } from 'elysia'; -import { DocumentHandler } from '../classes/DocumentHandler.ts'; +import { t } from 'elysia'; import { ServerVersion } from '../types/Server.ts'; import { JSPError } from '../classes/JSPError.ts'; +import type { Server } from '../classes/Server.ts'; export class PublishV1 extends AbstractEndpoint { - public constructor(server: Elysia) { + public constructor(server: Server) { super(server); } @@ -25,14 +25,17 @@ export class PublishV1 extends AbstractEndpoint { }, { description: 'An object with a key and a secret for the document' } ), - 400: JSPError.errorSchema + 400: JSPError.schema }, detail: { summary: 'Publish document', tags: ['v1'] } }; - this.server.post( + this.server.getElysia.post( prefix, - async ({ set, body }) => DocumentHandler.handlePublish(set, { body }, ServerVersion.v1), + async ({ set, body }) => { + this.server.getDocumentHandler.setContext = set; + return this.server.getDocumentHandler.publish({ body }, ServerVersion.v1); + }, hook ); } diff --git a/src/routes/PublishV2.ts b/src/routes/PublishV2.ts index 5ed6725..0c916a0 100644 --- a/src/routes/PublishV2.ts +++ b/src/routes/PublishV2.ts @@ -1,12 +1,11 @@ import { AbstractEndpoint } from '../classes/AbstractEndpoint.ts'; -import { type Elysia, t } from 'elysia'; -import { DocumentHandler } from '../classes/DocumentHandler.ts'; +import { t } from 'elysia'; import { ServerVersion } from '../types/Server.ts'; import { JSPError } from '../classes/JSPError.ts'; import { Server } from '../classes/Server.ts'; export class PublishV2 extends AbstractEndpoint { - public constructor(server: Elysia) { + public constructor(server: Server) { super(server); } @@ -24,7 +23,7 @@ export class PublishV2 extends AbstractEndpoint { examples: ['abc123'] }) ), - ['key-length']: t.Optional( + keyLength: t.Optional( t.Numeric({ description: 'If a custom key is not set, this will determine the key length of the automatically generated key', @@ -82,28 +81,27 @@ export class PublishV2 extends AbstractEndpoint { 'An object with a key, a secret, the display URL and an expiration timestamp for the document' } ), - 400: JSPError.errorSchema + 400: JSPError.schema }, detail: { summary: 'Publish document', tags: ['v2'] } }; - this.server.post( + this.server.getElysia.post( prefix, - async ({ set, request, query, body }) => - DocumentHandler.handlePublish( - set, + async ({ set, headers, query, body }) => { + this.server.getDocumentHandler.setContext = set; + return this.server.getDocumentHandler.publish( { body, - selectedKey: request.headers.get('key') || '', - selectedKeyLength: parseInt(request.headers.get('key-length') ?? '') || undefined, - selectedSecret: request.headers.get('secret') || '', - lifetime: parseInt( - request.headers.get('lifetime') || Server.config.documents.maxTime.toString() - ), - password: request.headers.get('password') || query['password'] || '' + selectedKey: headers.key || '', + selectedKeyLength: headers.keyLength, + selectedSecret: headers.secret || '', + lifetime: headers.lifetime ?? Server.config.documents.maxTime, + password: headers.password || query['password'] || '' }, ServerVersion.v2 - ), + ); + }, hook ); } diff --git a/src/routes/RemoveV1.ts b/src/routes/RemoveV1.ts index 90a22a1..897e474 100644 --- a/src/routes/RemoveV1.ts +++ b/src/routes/RemoveV1.ts @@ -1,10 +1,10 @@ import { AbstractEndpoint } from '../classes/AbstractEndpoint.ts'; -import { type Elysia, t } from 'elysia'; -import { DocumentHandler } from '../classes/DocumentHandler.ts'; +import { t } from 'elysia'; import { JSPError } from '../classes/JSPError.ts'; +import type { Server } from '../classes/Server.ts'; export class RemoveV1 extends AbstractEndpoint { - public constructor(server: Elysia) { + public constructor(server: Server) { super(server); } @@ -31,20 +31,22 @@ export class RemoveV1 extends AbstractEndpoint { }, { description: 'A response object with a boolean' } ), - 400: JSPError.errorSchema, - 403: JSPError.errorSchema, - 404: JSPError.errorSchema + 400: JSPError.schema, + 403: JSPError.schema, + 404: JSPError.schema }, detail: { summary: 'Remove document', tags: ['v1'] } }; - this.server.delete( + this.server.getElysia.delete( prefix.concat('/:key'), - async ({ set, request, params: { key } }) => - DocumentHandler.handleRemove(set, { - key, - secret: request.headers.get('secret') || '' - }), + async ({ set, headers, params }) => { + this.server.getDocumentHandler.setContext = set; + return this.server.getDocumentHandler.remove({ + key: params.key, + secret: headers.secret + }); + }, hook ); } diff --git a/src/routes/RemoveV2.ts b/src/routes/RemoveV2.ts index 5ce5f50..a99299e 100644 --- a/src/routes/RemoveV2.ts +++ b/src/routes/RemoveV2.ts @@ -1,10 +1,10 @@ import { AbstractEndpoint } from '../classes/AbstractEndpoint.ts'; -import { type Elysia, t } from 'elysia'; -import { DocumentHandler } from '../classes/DocumentHandler.ts'; +import { t } from 'elysia'; import { JSPError } from '../classes/JSPError.ts'; +import type { Server } from '../classes/Server.ts'; export class RemoveV2 extends AbstractEndpoint { - public constructor(server: Elysia) { + public constructor(server: Server) { super(server); } @@ -31,20 +31,22 @@ export class RemoveV2 extends AbstractEndpoint { }, { description: 'A response object with a boolean' } ), - 400: JSPError.errorSchema, - 403: JSPError.errorSchema, - 404: JSPError.errorSchema + 400: JSPError.schema, + 403: JSPError.schema, + 404: JSPError.schema }, detail: { summary: 'Remove document', tags: ['v2'] } }; - this.server.delete( + this.server.getElysia.delete( prefix.concat('/:key'), - async ({ set, request, params: { key } }) => - DocumentHandler.handleRemove(set, { - key, - secret: request.headers.get('secret') || '' - }), + async ({ set, headers, params }) => { + this.server.getDocumentHandler.setContext = set; + return this.server.getDocumentHandler.remove({ + key: params.key, + secret: headers.secret + }); + }, hook ); } diff --git a/src/types/DocumentHandler.ts b/src/types/DocumentHandler.ts index 12ea1c2..9b8f1ac 100644 --- a/src/types/DocumentHandler.ts +++ b/src/types/DocumentHandler.ts @@ -1,20 +1,20 @@ -type HandleAccess = { +type Access = { key: string; password?: string; raw?: boolean; }; -type HandleEdit = { +type Edit = { key: string; newBody: any; secret?: string; }; -type HandleExists = { +type Exists = { key: string; }; -type HandlePublish = { +type Publish = { body: any; selectedSecret?: string; lifetime?: number; @@ -23,14 +23,9 @@ type HandlePublish = { selectedKey?: string; }; -type HandleRemove = { +type Remove = { key: string; secret: string; }; -type HandleGetDocument = { - key: string; - password?: string; -}; - -export type { HandleAccess, HandleEdit, HandleExists, HandlePublish, HandleRemove, HandleGetDocument }; +export type { Access, Edit, Exists, Publish, Remove };