admin/config: Don't overwrite uploading https://github.com/rejetto/hfs/issues/110

Massimo Melina committed Feb 21, 2023 at 00:12 UTC ced75d837d04f4c22560ba3cb9055c7692a24d7f
2 files changed +17 -5
admin/src/ConfigPage.ts
+3 -1
@@ -104,7 +104,7 @@ export default function ConfigPage() {
104 ...with_(status?.https.error, e => isKeyError(e) ? { error: true, helperText: e } : null)
105 },
106 { k: 'open_browser_at_start', comp: BoolField },
107 - { k: 'localhost_admin', comp: BoolField, label: "Admin access for localhost connections",
107 + { k: 'localhost_admin', comp: BoolField, sm: 12, md: 6, label: "Admin access for localhost connections",
108 getError: x => !x && admins?.length===0 && "First create at least one admin account",
109 helperText: "To access Admin without entering credentials"
110 },
@@ -126,6 +126,8 @@ export default function ConfigPage() {
126 helperText: "If time is not enough, the browser will not show download percentage" },
127 { k: 'favicon', comp: FileField, sm: 6, md: 6, fileMask: '*.png|*.ico|*.jpg|*.jpeg|*.gif|*.svg',
128 helperText: "The icon associated to your website" },
129 + { k: 'dont_overwrite_uploading', comp: BoolField, md: 4, label: "Don't overwrite uploading",
130 + helperText: "Files will be numbered to avoid overwriting" },
131 { k: 'custom_header', multiline: true, sm: 12, md: 6, sx: { '& textarea': { fontFamily: 'monospace' } },
132 helperText: "Any HTML code here will be displayed on top of the Frontend"
133 },
src/upload.ts
+14 -4
@@ -7,7 +7,7 @@ import {
7 HTTP_SERVER_ERROR,
8 HTTP_UNAUTHORIZED
9 } from './const'
10 -import { basename, dirname, join } from 'path'
10 +import { basename, dirname, extname, join } from 'path'
11 import fs from 'fs'
12 import { Callback, try_ } from './misc'
13 import { notifyClient } from './frontEndApis'
@@ -16,6 +16,7 @@ import { getFreeDiskSync } from './util-os'
16
17 export const deleteUnfinishedUploadsAfter = defineConfig('delete_unfinished_uploads_after')
18 export const minAvailableMb = defineConfig('min_available_mb', 100)
19 +const dontOverwriteUploading = defineConfig('dont_overwrite_uploading', false)
20
21 const waitingToBeDeleted: Record<string, ReturnType<typeof setTimeout>> = {}
22
@@ -64,12 +65,21 @@ export function uploadWriter(base: VfsNode, path: string, ctx: Koa.Context) {
65 }
66 cancelDeletion(tempName)
67 ret.on('close', () => {
67 - if (!ctx.req.aborted)
68 - return fs.rename(tempName, fullPath, err => {
69 - err && console.error("couldn't rename temp to", fullPath, String(err))
68 + if (!ctx.req.aborted) {
69 + let dest = fullPath
70 + if (dontOverwriteUploading.get() && fs.existsSync(dest)) {
71 + const ext = extname(dest)
72 + const base = dest.slice(0, -ext.length)
73 + let i = 1
74 + do dest = `${base} (${i++})${ext}`
75 + while (fs.existsSync(dest))
76 + }
77 + return fs.rename(tempName, dest, err => {
78 + err && console.error("couldn't rename temp to", dest, String(err))
79 if (resumable)
80 delayedDelete(resumable, 0)
81 })
82 + }
83 if (resumable) // we don't want to be left with 2 temp files
84 return delayedDelete(tempName, 0)
85 const sec = deleteUnfinishedUploadsAfter.get()