From 25afbf493cbee0d6138be3d9e8e69f6a94c3707f Mon Sep 17 00:00:00 2001 From: stringsplit <77242831+nbitzz@users.noreply.github.com> Date: Sun, 3 Mar 2024 19:03:18 -0800 Subject: [PATCH] Server now runs --- src/server/index.ts | 9 ++-- src/server/lib/DiscordAPI/index.ts | 2 +- src/server/lib/files.ts | 2 +- src/server/lib/mail.ts | 7 ++- src/server/routes/api.ts | 28 +++++++---- src/server/routes/api/v0/adminRoutes.ts | 4 +- src/server/routes/api/v0/authRoutes.ts | 5 +- src/server/routes/api/v0/fileApiRoutes.ts | 3 +- src/server/routes/api/v0/primaryApi.ts | 58 +++++++---------------- src/server/routes/api/v1/account.ts | 4 +- src/server/routes/api/v1/admin.ts | 4 +- src/server/routes/api/v1/customization.ts | 5 +- src/server/routes/api/v1/file.ts | 2 +- src/server/routes/api/v1/public.ts | 2 +- src/server/routes/preview.ts | 3 +- 15 files changed, 59 insertions(+), 79 deletions(-) diff --git a/src/server/index.ts b/src/server/index.ts index 040f580..35c5faa 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -7,10 +7,12 @@ import Files from "./lib/files.js" import { getAccount } from "./lib/middleware.js" import APIRouter from "./routes/api.js" import preview from "./routes/preview.js" +import {fileURLToPath} from "url" +import {dirname} from "path" +import pkg from "../../package.json" assert {type:"json"} +import config from "../../config.json" assert {type:"json"} -const pkg = require(`${process.cwd()}/package.json`) const app = new Hono() -let config = require(`${process.cwd()}/config.json`) app.get( "/static/assets/*", @@ -72,6 +74,7 @@ app.get("/server", (ctx) => // init data +const __dirname = dirname(fileURLToPath(import.meta.url)) if (!fs.existsSync(__dirname + "/../.data/")) fs.mkdirSync(__dirname + "/../.data/") @@ -117,4 +120,4 @@ serve( } ) -export default app +export default app \ No newline at end of file diff --git a/src/server/lib/DiscordAPI/index.ts b/src/server/lib/DiscordAPI/index.ts index 000bef2..5dd29e5 100644 --- a/src/server/lib/DiscordAPI/index.ts +++ b/src/server/lib/DiscordAPI/index.ts @@ -92,7 +92,7 @@ export class Client { } let capture = Math.min( - (this.config.maxDiscordFileSize - (bytes_sent % this.config.maxDiscordFileSize)) + 1, + (this.config.maxDiscordFileSize - (bytes_sent % this.config.maxDiscordFileSize)), chunk.byteLength-position ) console.log(`Capturing ${capture} bytes, ${chunk.subarray(position, position+capture).byteLength}`) diff --git a/src/server/lib/files.ts b/src/server/lib/files.ts index 56ed8c2..50a2721 100644 --- a/src/server/lib/files.ts +++ b/src/server/lib/files.ts @@ -137,7 +137,7 @@ export class UploadStream extends Writable { while (position < data.byteLength) { let capture = Math.min( - ((this.files.config.maxDiscordFileSize*10) - (this.filled % (this.files.config.maxDiscordFileSize*10))) + 1, + ((this.files.config.maxDiscordFileSize*10) - (this.filled % (this.files.config.maxDiscordFileSize*10))), data.byteLength-position ) console.log(`Capturing ${capture} bytes for megachunk, ${data.subarray(position, position + capture).byteLength}`) diff --git a/src/server/lib/mail.ts b/src/server/lib/mail.ts index 7d7bf29..647982b 100644 --- a/src/server/lib/mail.ts +++ b/src/server/lib/mail.ts @@ -1,9 +1,8 @@ import { createTransport } from "nodemailer" +import "dotenv/config" +import config from "../../../config.json" assert {type:"json"} -// required i guess -require("dotenv").config() - -let mailConfig = require(process.cwd() + "/config.json").mail, +let mailConfig = config.mail, transport = createTransport({ ...mailConfig.transport, auth: { diff --git a/src/server/routes/api.ts b/src/server/routes/api.ts index 31cd0a7..0d5ebe1 100644 --- a/src/server/routes/api.ts +++ b/src/server/routes/api.ts @@ -1,8 +1,10 @@ import { Hono } from "hono" import { readFile, readdir } from "fs/promises" import Files from "../lib/files.js" +import {fileURLToPath} from "url" +import {dirname} from "path" -const APIDirectory = __dirname + "/api" +const APIDirectory = dirname(fileURLToPath(import.meta.url)) + "/api" interface APIMount { file: string @@ -25,18 +27,21 @@ class APIVersion { readonly definition: APIDefinition readonly apiPath: string readonly root: Hono = new Hono() + readonly files: Files constructor(definition: APIDefinition, files: Files) { this.definition = definition this.apiPath = APIDirectory + "/" + definition.name + this.files = files + } - for (let _mount of definition.mount) { - let mount = resolveMount(_mount) + async load() { + for (let _mount of this.definition.mount) { + let mount = resolveMount(_mount); // no idea if there's a better way to do this but this is all i can think of - let route = require(`${this.apiPath}/${mount.file}.js`) as ( - files: Files - ) => Hono - this.root.route(mount.to, route(files)) + let { default: route } = await import(`${this.apiPath}/${mount.file}.js`) as { default: (files: Files) => Hono } + + this.root.route(mount.to, route(this.files)) } } } @@ -54,12 +59,15 @@ export default class APIRouter { * @param definition Definition to mount. */ - private mount(definition: APIDefinition) { + private async mount(definition: APIDefinition) { console.log(`mounting APIDefinition ${definition.name}`) + let def = new APIVersion(definition, this.files) + await def.load() + this.root.route( definition.baseURL, - new APIVersion(definition, this.files).root + def.root ) } @@ -74,7 +82,7 @@ export default class APIRouter { ) ).toString() ) as APIDefinition - this.mount(def) + await this.mount(def) } } } diff --git a/src/server/routes/api/v0/adminRoutes.ts b/src/server/routes/api/v0/adminRoutes.ts index d66d389..4e561d7 100644 --- a/src/server/routes/api/v0/adminRoutes.ts +++ b/src/server/routes/api/v0/adminRoutes.ts @@ -22,9 +22,7 @@ adminRoutes .use(requiresAdmin) .use(requiresPermissions("admin")) -let config = require(`${process.cwd()}/config.json`) - -module.exports = function (files: Files) { +export default function (files: Files) { adminRoutes.post("/reset", async (ctx) => { let acc = ctx.get("account") as Accounts.Account const body = await ctx.req.json() diff --git a/src/server/routes/api/v0/authRoutes.ts b/src/server/routes/api/v0/authRoutes.ts index 8c3ccf5..6aa1237 100644 --- a/src/server/routes/api/v0/authRoutes.ts +++ b/src/server/routes/api/v0/authRoutes.ts @@ -26,12 +26,11 @@ export let authRoutes = new Hono<{ } }>() -let config = require(`${process.cwd()}/config.json`) +import config from "../../../../../config.json" assert {type:"json"} authRoutes.all("*", getAccount) -module.exports = function (files: Files) { +export default function (files: Files) { authRoutes.post("/login", async (ctx) => { - console.log(ctx) const body = await ctx.req.json() if ( typeof body.username != "string" || diff --git a/src/server/routes/api/v0/fileApiRoutes.ts b/src/server/routes/api/v0/fileApiRoutes.ts index 59e5207..e3f1ccb 100644 --- a/src/server/routes/api/v0/fileApiRoutes.ts +++ b/src/server/routes/api/v0/fileApiRoutes.ts @@ -14,7 +14,6 @@ export let fileApiRoutes = new Hono<{ } }>() -let config = require(`${process.cwd()}/config.json`) fileApiRoutes.use("*", getAccount) // :warning: /list somehow crashes Hono with an internal error! /* @@ -27,7 +26,7 @@ TypeError: Cannot read properties of undefined (reading 'get') at process.processTicksAndRejections (node:internal/process/task_queues:95:5) */ -module.exports = function (files: Files) { +export default function (files: Files) { fileApiRoutes.get( "/list", requiresAccount, diff --git a/src/server/routes/api/v0/primaryApi.ts b/src/server/routes/api/v0/primaryApi.ts index 29ccfc2..41aa51b 100644 --- a/src/server/routes/api/v0/primaryApi.ts +++ b/src/server/routes/api/v0/primaryApi.ts @@ -3,36 +3,24 @@ import { Hono } from "hono" import * as Accounts from "../../../lib/accounts.js" import * as auth from "../../../lib/auth.js" -import axios, { AxiosResponse } from "axios" -import { type Range } from "range-parser" -import multer, { memoryStorage } from "multer" -import { Readable } from "stream" +import RangeParser, { type Range } from "range-parser" import ServeError from "../../../lib/errors.js" import Files from "../../../lib/files.js" -import { getAccount, requiresPermissions } from "../../../lib/middleware.js" - -let parser = bodyParser.json({ - type: ["text/plain", "application/json"], -}) - +import { getAccount } from "../../../lib/middleware.js" +import {Readable} from "node:stream" export let primaryApi = new Hono<{ Variables: { account: Accounts.Account } }>() -const multerSetup = multer({ storage: memoryStorage() }) - -let config = require(`${process.cwd()}/config.json`) - primaryApi.use(getAccount) -module.exports = function (files: Files) {/* +export default function (files: Files) { primaryApi.get( - ["/file/:fileId", "/cpt/:fileId/*", "/:fileId"], - async (ctx) => { + "/file/:fileId", + async (ctx): Promise => { const fileId = (ctx.req.param() as {fileId: string}).fileId - const reqRange let acc = ctx.get("account") as Accounts.Account @@ -54,8 +42,7 @@ module.exports = function (files: Files) {/* .getPermissions(auth.tokenFor(ctx)!) ?.includes("private") ) { - ServeError(ctx, 403, "insufficient permissions") - return + return ServeError(ctx, 403, "insufficient permissions") } } @@ -65,29 +52,18 @@ module.exports = function (files: Files) {/* if (file.sizeInBytes) { ctx.header("Content-Length", file.sizeInBytes.toString()) - if (file.chunkSize) { - let range = ctx.range(file.sizeInBytes) - if (range) { - // error handling - if (typeof range == "number") { - return ctx.status(range == -1 ? 416 : 400) - } - if (range.type != "bytes") { - return ctx.status(400) - } + if (file.chunkSize && ctx.req.header("Range")) { + let ranges = RangeParser(file.sizeInBytes, ctx.req.header("Range") || "") - // set ranges var - let rngs = Array.from(range) - if (rngs.length != 1) { - return ctx.status(400) - } - range = rngs[0] + if (ranges) { + if (typeof ranges == "number") + return ServeError(ctx, ranges == -1 ? 416 : 400, ranges == -1 ? "unsatisfiable ranges" : "invalid ranges") + if (ranges.length > 1) return ServeError(ctx, 400, "multiple ranges not supported") + range = ranges[0] } } } - // supports ranges - return files .readFileStream(fileId, range) .then(async (stream) => { @@ -103,8 +79,8 @@ module.exports = function (files: Files) {/* ) } - return ctx.stream((stre) => { - // Somehow return a stream? + return ctx.req.method == "HEAD" ? ctx.body(null) : ctx.stream(async (webStream) => { + webStream.pipe(Readable.toWeb(stream) as ReadableStream) }) }) .catch((err) => { @@ -114,7 +90,7 @@ module.exports = function (files: Files) {/* return ServeError(ctx, 404, "file not found") } } - )*/ + ) // primaryApi.head( // ["/file/:fileId", "/cpt/:fileId/*", "/:fileId"], diff --git a/src/server/routes/api/v1/account.ts b/src/server/routes/api/v1/account.ts index 0e78f5b..eccd94e 100644 --- a/src/server/routes/api/v1/account.ts +++ b/src/server/routes/api/v1/account.ts @@ -19,7 +19,7 @@ import { import ServeError from "../../../lib/errors.js" import { sendMail } from "../../../lib/mail.js" -const Configuration = require(`${process.cwd()}/config.json`) +import Configuration from "../../../../../config.json" assert {type:"json"} const router = new Hono<{ Variables: { @@ -29,7 +29,7 @@ const router = new Hono<{ router.use(getAccount) -module.exports = function (files: Files) { +export default function (files: Files) { router.post("/login", async (ctx, res) => { const body = await ctx.req.json() if ( diff --git a/src/server/routes/api/v1/admin.ts b/src/server/routes/api/v1/admin.ts index 608dcd9..6c3f1e7 100644 --- a/src/server/routes/api/v1/admin.ts +++ b/src/server/routes/api/v1/admin.ts @@ -17,8 +17,6 @@ import { import ServeError from "../../../lib/errors.js" import { sendMail } from "../../../lib/mail.js" -const Configuration = require(`${process.cwd()}/config.json`) - const router = new Hono<{ Variables: { account?: Accounts.Account @@ -27,7 +25,7 @@ const router = new Hono<{ router.use(getAccount, requiresAccount, requiresAdmin) -module.exports = function (files: Files) { +export default function (files: Files) { router.patch("/account/:username/password", async (ctx) => { const Account = ctx.get("account") as Accounts.Account const body = await ctx.req.json() diff --git a/src/server/routes/api/v1/customization.ts b/src/server/routes/api/v1/customization.ts index a578f4c..430d530 100644 --- a/src/server/routes/api/v1/customization.ts +++ b/src/server/routes/api/v1/customization.ts @@ -7,8 +7,7 @@ import { requiresPermissions, } from "../../../lib/middleware.js" import ServeError from "../../../lib/errors.js" - -const Configuration = require(`${process.cwd()}/config.json`) +import Configuration from "../../../../../config.json" assert {type:"json"} const router = new Hono<{ Variables: { @@ -18,7 +17,7 @@ const router = new Hono<{ router.use(getAccount) -module.exports = function (files: Files) { +export default function (files: Files) { router.put( "/css", requiresAccount, diff --git a/src/server/routes/api/v1/file.ts b/src/server/routes/api/v1/file.ts index d83d3b7..88f54b4 100644 --- a/src/server/routes/api/v1/file.ts +++ b/src/server/routes/api/v1/file.ts @@ -3,6 +3,6 @@ import Files from "../../../lib/files.js"; const router = new Hono() -module.exports = function(files: Files) { +export default function(files: Files) { return router } diff --git a/src/server/routes/api/v1/public.ts b/src/server/routes/api/v1/public.ts index ef7f85c..192861a 100644 --- a/src/server/routes/api/v1/public.ts +++ b/src/server/routes/api/v1/public.ts @@ -3,6 +3,6 @@ import Files from "../../../lib/files.js" const router = new Hono() -module.exports = function (files: Files) { +export default function (files: Files) { return router } diff --git a/src/server/routes/preview.ts b/src/server/routes/preview.ts index 3d9c9e3..dfdd763 100644 --- a/src/server/routes/preview.ts +++ b/src/server/routes/preview.ts @@ -4,7 +4,8 @@ import ServeError from "../lib/errors.js" import * as Accounts from "../lib/accounts.js" import type { Handler } from "hono" import type Files from "../lib/files.js" -const pkg = require(`${process.cwd()}/package.json`) +import pkg from "../../../package.json" assert {type:"json"} + export default (files: Files): Handler => async (ctx) => { let acc = ctx.get("account") as Accounts.Account