this hurts

This commit is contained in:
May 2024-03-03 22:02:12 -08:00
parent fd4441633e
commit 971ee31d73
5 changed files with 79 additions and 92 deletions

17
package-lock.json generated
View file

@ -9,7 +9,7 @@
"version": "1.4.0-dev",
"license": "Unlicense",
"dependencies": {
"@hono/node-server": "^1.2.0",
"@hono/node-server": "^1.8.2",
"@types/body-parser": "^1.19.2",
"@types/express": "^4.17.14",
"@types/multer": "^1.4.7",
@ -21,7 +21,6 @@
"cookie-parser": "^1.4.6",
"dotenv": "^16.0.2",
"express": "^4.18.1",
"form-data": "^4.0.0",
"hono": "^3.8.3",
"multer": "^1.4.5-lts.1",
"node-fetch": "^3.3.2",
@ -395,11 +394,11 @@
}
},
"node_modules/@hono/node-server": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.2.0.tgz",
"integrity": "sha512-aHT8lDMLpd7ioXJ1/057+h+oE/k7rCOWmjklYDsE0jE4CoNB9XzG4f8dRHvw4s5HJFocaYDiGgYM/V0kYbQ0ww==",
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.8.2.tgz",
"integrity": "sha512-h8l2TBLCPHZBUrrkosZ6L5CpBLj6zdESyF4B+zngiCDF7aZFQJ0alVbLx7jn8PCVi9EyoFf8a4hOZFi1tD95EA==",
"engines": {
"node": ">=18.0.0"
"node": ">=18.14.1"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
@ -2135,9 +2134,9 @@
"optional": true
},
"@hono/node-server": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.2.0.tgz",
"integrity": "sha512-aHT8lDMLpd7ioXJ1/057+h+oE/k7rCOWmjklYDsE0jE4CoNB9XzG4f8dRHvw4s5HJFocaYDiGgYM/V0kYbQ0ww=="
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.8.2.tgz",
"integrity": "sha512-h8l2TBLCPHZBUrrkosZ6L5CpBLj6zdESyF4B+zngiCDF7aZFQJ0alVbLx7jn8PCVi9EyoFf8a4hOZFi1tD95EA=="
},
"@jridgewell/sourcemap-codec": {
"version": "1.4.15",

View file

@ -18,7 +18,7 @@
"node": ">=v16.11"
},
"dependencies": {
"@hono/node-server": "^1.2.0",
"@hono/node-server": "^1.8.2",
"@types/body-parser": "^1.19.2",
"@types/express": "^4.17.14",
"@types/multer": "^1.4.7",
@ -30,7 +30,6 @@
"cookie-parser": "^1.4.6",
"dotenv": "^16.0.2",
"express": "^4.18.1",
"form-data": "^4.0.0",
"hono": "^3.8.3",
"multer": "^1.4.5-lts.1",
"node-fetch": "^3.3.2",

View file

@ -172,9 +172,10 @@ export class UploadStream extends Writable {
_destroy(error: Error | null) {
this.error = error || undefined
this.abort()
/*
if (error instanceof WebError) return // destroyed by self
if (error) this.abort() // destroyed externally...
if (error) return // destroyed externally...*/
}
/**
@ -182,7 +183,11 @@ export class UploadStream extends Writable {
*/
async abort() {
if (!this.destroyed) this.destroy()
if (this.current) this.current.destroy(this.error)
await this.files.api.deleteMessages(this.messages)
if (this.uploadId) {
delete this.files.locks[this.uploadId]
}
}
/**
@ -222,6 +227,7 @@ export class UploadStream extends Writable {
}
await this.files.write()
delete this.files.locks[this.uploadId!]
return this.uploadId
}
@ -257,11 +263,10 @@ export class UploadStream extends Writable {
if (this.files.files[id] && this.files.files[id].owner != this.owner)
return this.destroy( new WebError(403, "you don't own this file") )
if (false /* check if locked here */)
if (this.files.locks[id])
return this.destroy( new WebError(409, "a file with this ID is already being uploaded") )
/* lock the id */
this.files.locks[id] = true
this.uploadId = id
return this
}
@ -325,6 +330,8 @@ export default class Files {
files: { [key: string]: FilePointer } = {}
data_directory: string = `${process.cwd()}/.data`
locks: Record<string, boolean> = {} // I'll, like, do something more proper later
constructor(config: Configuration) {
this.config = config
this.api = new API(process.env.TOKEN!, config)

View file

@ -0,0 +1,46 @@
import { Transform, Duplex } from "node:stream";
import { TransformCallback } from "stream";
let content_disposition_matcher = /\s*([^=;]+)(?:=(?:"((?:\\"|[^"])*)"|([^;]*))?;?|;?)/g // probably a bad regex but IDC
export type Headers = {
["content-disposition"]?: (string|{key: string, value: string})[],
["content-type"]?: string
}
export class Field extends Duplex {
headers: Headers = {}
constructor(unparsedHeaders: string) {
super()
this.headers = Object.fromEntries(
unparsedHeaders.split("\r\n")
.map(e => [e.split(":")[0].trim(), e.split(":").slice(1).join(":").trim()])
)
if (this.headers["content-disposition"])
this.headers["content-disposition"] = Array.from(
(this.headers["content-disposition"] as unknown as string)
.matchAll(content_disposition_matcher)).map(e => e[2] ? {key: e[1], value: e[2]} : e[1])
}
}
export default class FormDataParser extends Transform {
readableObjectMode = true
boundary: string
internalBuffer: Buffer | undefined
constructor(boundary: string) {
super()
this.boundary = boundary
}
_transform(chunk: any, encoding: BufferEncoding, callback: TransformCallback): void {
}
}

View file

@ -5,8 +5,8 @@ import * as Accounts from "../../../lib/accounts.js"
import * as auth from "../../../lib/auth.js"
import RangeParser, { type Range } from "range-parser"
import ServeError from "../../../lib/errors.js"
import Files from "../../../lib/files.js"
import { getAccount } from "../../../lib/middleware.js"
import Files, { WebError } from "../../../lib/files.js"
import { getAccount, requiresPermissions } from "../../../lib/middleware.js"
import {Readable} from "node:stream"
export let primaryApi = new Hono<{
Variables: {
@ -27,8 +27,7 @@ export default function (files: Files) {
let file = files.files[fileId]
ctx.header("Access-Control-Allow-Origin", "*")
ctx.header("Content-Security-Policy", "sandbox allow-scripts")
if (ctx.req.query("attachment") == "1")
ctx.header("Content-Disposition", "attachment")
ctx.header("Content-Disposition", `${ctx.req.query("attachment") == "1" ? "attachment" : "inline"}; filename="${file.filename.replaceAll("\n","\\n")}"`)
if (file) {
if (file.visibility == "private") {
@ -91,89 +90,26 @@ export default function (files: Files) {
}
}
)
// primaryApi.head(
// ["/file/:fileId", "/cpt/:fileId/*", "/:fileId"],
// async (ctx) => {
// let file = files.files[req.params.fileId]
// if (
// file.visibility == "private" &&
// (ctx.get("account")?.id != file.owner ||
// (auth.getType(auth.tokenFor(ctx)!) == "App" &&
// auth
// .getPermissions(auth.tokenFor(ctx)!)
// ?.includes("private")))
// ) {
// return ctx.status(403)
// }
// ctx.header("Content-Security-Policy", "sandbox allow-scripts")
// if (ctx.req.query("attachment") == "1")
// ctx.header("Content-Disposition", "attachment")
// if (!file) {
// res.status(404)
// res.send()
// } else {
// ctx.header("Content-Type", file.mime)
// if (file.sizeInBytes) {
// ctx.header("Content-Length", file.sizeInBytes)
// }
// if (file.chunkSize) {
// ctx.header("Accept-Ranges", "bytes")
// }
// res.send()
// }
// }
// )
// upload handlers
/*
primaryApi.post(
"/upload",
requiresPermissions("upload"),
multerSetup.single("file"),
async (ctx) => {
let acc = ctx.get("account") as Accounts.Account
if (req.file) {
try {
let prm = req.header("monofile-params")
let params: { [key: string]: any } = {}
if (prm) {
params = JSON.parse(prm)
if (!ctx.req.header("Content-Type")?.startsWith("multipart/form-data")) {
ctx.status(400)
return ctx.body("[err] must be multipart/form-data")
}
files
.uploadFile(
{
owner: acc?.id,
uploadId: params.uploadId,
filename: req.file.originalname,
mime: req.file.mimetype,
},
req.file.buffer
)
.then((uID) => res.send(uID))
.catch((stat) => {
res.status(stat.status)
res.send(`[err] ${stat.message}`)
})
} catch {
res.status(400)
res.send("[err] bad request")
}
} else {
res.status(400)
res.send("[err] bad request")
if (!ctx.req.raw.body) {
ctx.status(400)
return ctx.body("[err] body must be supplied")
}
}
)
/*
primaryApi.post(
"/clone",
requiresPermissions("upload"),