plugins: api event 'uploadStart'

Massimo Melina committed May 16, 2024 at 00:03 UTC be0bc7d06b443c04641adcfcadfb3a6a8e6eded2
1 file changed +32 -34
src/upload.ts
+32 -34
@@ -1,11 +1,6 @@
1 import { getNodeByName, hasPermission, statusCodeForMissingPerm, VfsNode } from './vfs'
2 import Koa from 'koa'
3 -import {
4 - HTTP_CONFLICT, HTTP_FOOL,
5 - HTTP_PAYLOAD_TOO_LARGE,
6 - HTTP_RANGE_NOT_SATISFIABLE,
7 - HTTP_SERVER_ERROR,
8 -} from './const'
3 +import { HTTP_CONFLICT, HTTP_FOOL, HTTP_PAYLOAD_TOO_LARGE, HTTP_RANGE_NOT_SATISFIABLE, HTTP_SERVER_ERROR } from './const'
4 import { basename, dirname, extname, join } from 'path'
5 import fs from 'fs'
6 import { Callback, dirTraversal, escapeHTML, loadFileAttr, storeFileAttr, try_ } from './misc'
@@ -16,6 +11,8 @@ import { disconnect, updateConnection, updateConnectionForCtx } from './connecti
11 import { roundSpeed } from './throttler'
12 import { getCurrentUsername } from './auth'
13 import { setCommentFor } from './comments'
14 +import _ from 'lodash'
15 +import events from './events'
16
17 export const deleteUnfinishedUploadsAfter = defineConfig<undefined|number>('delete_unfinished_uploads_after', 86_400)
18 export const minAvailableMb = defineConfig('min_available_mb', 100)
@@ -95,7 +92,7 @@ export function uploadWriter(base: VfsNode, path: string, ctx: Koa.Context) {
92 const resuming = resume && resumable
93 if (!resuming)
94 resume = 0
98 - const ret = resuming ? fs.createWriteStream(resumable, { flags: 'r+', start: resume })
95 + const writeStream = resuming ? fs.createWriteStream(resumable, { flags: 'r+', start: resume })
96 : fs.createWriteStream(tempName)
97 if (resuming) {
98 fs.rm(tempName, () => {})
@@ -103,33 +100,34 @@ export function uploadWriter(base: VfsNode, path: string, ctx: Koa.Context) {
100 }
101 cancelDeletion(tempName)
102 trackProgress()
106 - ret.once('close', async () => {
107 - if (!ctx.req.aborted) {
108 - let dest = fullPath
109 - if (dontOverwriteUploading.get() && fs.existsSync(dest) && !await overwriteAnyway()) {
110 - const ext = extname(dest)
111 - const base = dest.slice(0, -ext.length)
112 - let i = 1
113 - do dest = `${base} (${i++})${ext}`
114 - while (fs.existsSync(dest))
115 - }
116 - return fs.rename(tempName, dest, err => {
117 - setUploadMeta(err ? tempName : dest, ctx)
118 - if (err)
119 - console.error("couldn't rename temp to", dest, String(err))
120 - else if (ctx.query.comment)
121 - setCommentFor(dest, escapeHTML(String(ctx.query.comment)))
122 - if (resumable)
123 - delayedDelete(resumable, 0)
124 - })
103 + writeStream.once('close', async () => {
104 + if (ctx.req.aborted) {
105 + if (resumable) // we don't want to be left with 2 temp files
106 + return delayedDelete(tempName, 0)
107 + const sec = deleteUnfinishedUploadsAfter.get()
108 + return _.isNumber(sec) && delayedDelete(tempName, sec)
109 + }
110 + let dest = fullPath
111 + if (dontOverwriteUploading.get() && fs.existsSync(dest) && !await overwriteAnyway()) {
112 + const ext = extname(dest)
113 + const base = dest.slice(0, -ext.length)
114 + let i = 1
115 + do dest = `${base} (${i++})${ext}`
116 + while (fs.existsSync(dest))
117 }
126 - if (resumable) // we don't want to be left with 2 temp files
127 - return delayedDelete(tempName, 0)
128 - const sec = deleteUnfinishedUploadsAfter.get()
129 - if (typeof sec !== 'number') return
130 - delayedDelete(tempName, sec)
118 + return fs.rename(tempName, dest, err => {
119 + setUploadMeta(err ? tempName : dest, ctx)
120 + if (err)
121 + console.error("couldn't rename temp to", dest, String(err))
122 + else if (ctx.query.comment)
123 + setCommentFor(dest, escapeHTML(String(ctx.query.comment)))
124 + if (resumable)
125 + delayedDelete(resumable, 0)
126 + })
127 })
132 - return ret
128 + const obj = { ctx, writeStream }
129 + events.emit('uploadStart', obj)
130 + return obj.writeStream
131
132 async function overwriteAnyway() {
133 if (ctx.query.overwrite === undefined // legacy pre-0.52
@@ -147,13 +145,13 @@ export function uploadWriter(base: VfsNode, path: string, ctx: Koa.Context) {
145 if (!conn) return
146 const h = setInterval(() => {
147 const now = Date.now()
150 - const got = ret.bytesWritten
148 + const got = writeStream.bytesWritten
149 const inSpeed = roundSpeed((got - lastGot) / (now - lastGotTime))
150 lastGot = got
151 lastGotTime = now
152 updateConnection(conn, { inSpeed, got }, { opProgress: (resume + got) / opTotal })
153 }, 1000)
156 - ret.once('close', () => clearInterval(h) )
154 + writeStream.once('close', () => clearInterval(h) )
155 }
156
157 function delayedDelete(path: string, secs: number, cb?: Callback) {