Commit 1: Backend Route & Temporary Storage

This commit is contained in:
Josh Roskos 2026-05-16 21:10:11 -05:00
parent c010f0e1eb
commit 0fb87e1c96
2 changed files with 52 additions and 0 deletions

View file

@ -99,6 +99,57 @@ class MiscController {
res.sendStatus(200)
}
/**
* POST: /api/upload/chunk
* Handle chunked upload
*
* @param {RequestWithUser} req
* @param {Response} res
*/
async handleChunkUpload(req, res) {
if (!req.user.canUpload) {
Logger.warn(`User "${req.user.username}" attempted to upload without permission`)
return res.sendStatus(403)
}
if (!req.files || !Object.values(req.files).length) {
Logger.error('Invalid request, no files')
return res.sendStatus(400)
}
const { fileId, chunkIndex, totalChunks, filename, library: libraryId, folder: folderId, title, author, series } = req.body
if (!fileId || chunkIndex === undefined || !totalChunks || !filename || !libraryId || !folderId || !title) {
return res.status(400).send('Invalid request body for chunk upload')
}
const library = await Database.libraryModel.findByIdWithFolders(libraryId)
if (!library) {
return res.status(404).send('Library not found')
}
if (!req.user.checkCanAccessLibrary(library.id)) {
Logger.error(`[MiscController] User "${req.user.username}" attempting to upload to library "${library.id}" without access`)
return res.sendStatus(403)
}
const tmpDir = Path.join(global.MetadataPath, 'tmp', 'uploads', fileId)
await fs.ensureDir(tmpDir)
const file = Object.values(req.files)[0]
const chunkPath = Path.join(tmpDir, `${fileId}_${chunkIndex}`)
try {
await file.mv(chunkPath)
// If this is the last chunk, we trigger reassembly.
// To keep the commit scoped to "Temporary Storage", we will return success here for now
// and implement the reassembly in Commit 2.
res.sendStatus(200)
} catch (error) {
Logger.error(`Failed to move chunk ${chunkIndex} for file ${fileId}`, error)
return res.status(500).send('Failed to save chunk')
}
}
/**
* GET: /api/tasks
* Get tasks for task manager

View file

@ -339,6 +339,7 @@ class ApiRouter {
// Misc Routes
//
this.router.post('/upload', MiscController.handleUpload.bind(this))
this.router.post('/upload/chunk', MiscController.handleChunkUpload.bind(this))
this.router.get('/tasks', MiscController.getTasks.bind(this))
this.router.patch('/settings', MiscController.updateServerSettings.bind(this))
this.router.patch('/sorting-prefixes', MiscController.updateSortingPrefixes.bind(this))