diff --git a/app/src/main/kotlin/net/h34t/filemure/Types.kt b/app/src/main/kotlin/net/h34t/filemure/Types.kt index cb38101..bd271f2 100644 --- a/app/src/main/kotlin/net/h34t/filemure/Types.kt +++ b/app/src/main/kotlin/net/h34t/filemure/Types.kt @@ -95,3 +95,8 @@ enum class State(val code: Int) { } } } + +/** + * Tuple for internal and external ID. + */ +data class IdPair(val id: Long, val extId: ExtId) \ No newline at end of file diff --git a/app/src/main/kotlin/net/h34t/filemure/controller/LimboController.kt b/app/src/main/kotlin/net/h34t/filemure/controller/LimboController.kt index 7704bd4..d7d2b98 100644 --- a/app/src/main/kotlin/net/h34t/filemure/controller/LimboController.kt +++ b/app/src/main/kotlin/net/h34t/filemure/controller/LimboController.kt @@ -10,8 +10,6 @@ import java.time.format.FormatStyle class LimboController(val modifiers: TemplateModifiers, val repository: SqliteRepository) { - private val dtf = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT) - fun formLimbo(ctx: Context) { val session = ctx.requireSession() diff --git a/app/src/main/kotlin/net/h34t/filemure/controller/UploadController.kt b/app/src/main/kotlin/net/h34t/filemure/controller/UploadController.kt index 15ee45d..0e59e47 100644 --- a/app/src/main/kotlin/net/h34t/filemure/controller/UploadController.kt +++ b/app/src/main/kotlin/net/h34t/filemure/controller/UploadController.kt @@ -12,24 +12,45 @@ class UploadController(val modifiers: TemplateModifiers, val repository: SqliteR val accountid = session.id + val target = ctx.queryParam("target") + val files = ctx.uploadedFiles() - files.forEach { - println("filename: " + it.filename()) - println("ext: " + it.extension()) - println("contentType: " + it.contentType()) // mime - println("size: " + it.size()) + when (target) { + "document" -> { + val extIds = files.map { + it.contentAndClose { content -> + repository.addFileToLimbo(accountid, it.filename(), it.contentType(), it.size(), content).extId + } + } + ctx.status(200) + ctx.json( + Result( + files = files.size, + redirect = "/document/new?${extIds.joinToString("&") { "file_id=$it" }}" + ) + ) + } - it.contentAndClose { content -> - repository.addFileToLimbo(accountid, it.filename(), it.contentType(), it.size(), content) + "limbo" -> { + files.forEach { + it.contentAndClose { content -> + repository.addFileToLimbo(accountid, it.filename(), it.contentType(), it.size(), content) + } + } + ctx.status(200) + ctx.json( + Result( + files = files.size, + redirect = "/limbo" + ) + ) } } - - ctx.status(200) - ctx.json(Result(files = files.size)) } data class Result( - val files: Int + val files: Int, + val redirect: String ) } diff --git a/app/src/main/kotlin/net/h34t/filemure/repository/SqliteRepository.kt b/app/src/main/kotlin/net/h34t/filemure/repository/SqliteRepository.kt index ec89a71..79509e2 100644 --- a/app/src/main/kotlin/net/h34t/filemure/repository/SqliteRepository.kt +++ b/app/src/main/kotlin/net/h34t/filemure/repository/SqliteRepository.kt @@ -69,16 +69,52 @@ class SqliteRepository(url: String) { ) } - fun addFileToLimbo(accountId: Long, filename: String, contentType: String?, size: Long, content: InputStream) { - database.databaseQueries.insertFileIntoLimbo( - account_id = accountId, - ext_id = ExtId.generate(), - filename = filename, - content_type = contentType, - file_size = size, - content = content.readAllBytes() - ) - } + fun addFileToLimbo( + accountId: Long, + filename: String, + contentType: String?, + size: Long, + content: InputStream + ): IdPair = + database.databaseQueries.transactionWithResult { + val extId = ExtId.generate() + database.databaseQueries.insertFileIntoLimbo( + account_id = accountId, + ext_id = extId, + filename = filename, + content_type = contentType, + file_size = size, + content = content.readAllBytes() + ) + IdPair( + id = lastInsertedId(), + extId = extId + ) + } + + + fun addNewFileToDocument( + accountId: Long, + documentId: Long, + filename: String, + contentType: String?, + size: Long, + content: InputStream + ) = + database.databaseQueries.transactionWithResult { + ExtId.generate().let { extId -> + database.databaseQueries.insertFileForDocument( + account_id = accountId, + document_id = documentId, + ext_id = extId, + filename = filename, + content_type = contentType, + file_size = size, + content = content.readAllBytes() + ) + IdPair(id = lastInsertedId(), extId = extId) + } + } fun getLimboFileCount(accountId: Long, state: State = State.ACTIVE): Long { return database.databaseQueries.getLimboFileCount(account_id = accountId, state = state) @@ -273,4 +309,6 @@ class SqliteRepository(url: String) { fun setFileState(accountId: Long, extId: ExtId, state: State) { database.databaseQueries.setFileState(account_id = accountId, ext_id = extId, state = state) } + + private fun lastInsertedId() = database.databaseQueries.getLastInsertRowId().executeAsOne() } \ No newline at end of file diff --git a/app/src/main/sqldelight/net/h34t/filemure/db/Database.sq b/app/src/main/sqldelight/net/h34t/filemure/db/Database.sq index 8a1ecb4..0078be9 100644 --- a/app/src/main/sqldelight/net/h34t/filemure/db/Database.sq +++ b/app/src/main/sqldelight/net/h34t/filemure/db/Database.sq @@ -60,6 +60,9 @@ CREATE INDEX file_state_IDX ON file (state); CREATE UNIQUE INDEX file_ext_id_IDX ON file (ext_id); --- +insertFileForDocument: +INSERT INTO file (account_id, document_id, ext_id, filename, content_type, file_size, content) VALUES (?, ?,?,?,?,?,?); + insertFileIntoLimbo: INSERT INTO file (account_id, ext_id, filename, content_type, file_size, content) VALUES (?,?,?,?,?,?); diff --git a/public/filemure.js b/public/filemure.js index 45a1cde..b0e0ff9 100644 --- a/public/filemure.js +++ b/public/filemure.js @@ -1,73 +1,66 @@ -document.addEventListener("DOMContentLoaded", function() { - console.log("filemure ready") +document.addEventListener("DOMContentLoaded", function () { const dropzone = document.querySelector('html'); let progressDialog = document.querySelector('#upload_progress_dialog') let progressBar = document.querySelector('#upload_progress') - // window.addEventListener - - // Prevent default behavior for drag-over and drop dropzone.addEventListener('dragover', (e) => { - console.log("over") e.preventDefault(); dropzone.classList.add('dragactive'); }); dropzone.addEventListener('dragleave', (e) => { - console.log("out") dropzone.classList.remove('dragactive'); }); dropzone.addEventListener('drop', (e) => { - console.log("drop") - e.preventDefault(); - dropzone.classList.remove('dragactive'); + e.preventDefault(); + dropzone.classList.remove('dragactive'); - // Handle dropped files - const files = e.dataTransfer.files; - if (files.length > 0) { - console.log('Files dropped:', files); + // Handle dropped files + const files = e.dataTransfer.files; + if (files.length > 0) { + console.log('Files dropped:', files); - // Process the files - for (const file of files) { - progressDialog.showModal(); - console.log('File name:', file.name); - console.log('File size:', file.size, 'bytes'); - console.log('File type:', file.type); + for (const file of files) { + // show progress dialog + // warning: this way the progress dialog is shown for each file individually + progressDialog.showModal(); + const formData = new FormData(); + formData.append('file', file); - const formData = new FormData(); - formData.append('file', file); + const xhr = new XMLHttpRequest(); + xhr.upload.addEventListener("progress", (event) => { + if (event.lengthComputable) { + // console.log("upload progress:", event.loaded / event.total); + progressBar.value = 100.0 * event.loaded / event.total; + } + }); + xhr.addEventListener("progress", (event) => { + if (event.lengthComputable) { + // console.log("download progress:", event.loaded / event.total); + progressBar.value = event.loaded / event.total; + } + }); + xhr.addEventListener("load", (event) => { + console.log(xhr.readyState, xhr.status) + progressDialog.close(); + let res = JSON.parse(xhr.response); + window.location = res.redirect; + }); - const xhr = new XMLHttpRequest(); - xhr.upload.addEventListener("progress", (event) => { - if (event.lengthComputable) { - console.log("upload progress:", event.loaded / event.total); - progressBar.value = 100.0 * event.loaded / event.total; - } - }); - xhr.addEventListener("progress", (event) => { - if (event.lengthComputable) { - console.log("download progress:", event.loaded / event.total); - progressBar.value = event.loaded / event.total; - } - }); - xhr.addEventListener("loadend", () => { - console.log(xhr.readyState, xhr.status) - progressDialog.close(); - }); + xhr.addEventListener("error", event => { + progressDialog.close(); + alert("An error occurred.") + }); + xhr.addEventListener("abort", event => { + progressDialog.close(); + alert("Upload has been cancelled.") + }); - xhr.open('POST', '/upload', false); - xhr.send(formData); - -// fetch('/upload', { -// method: 'POST', -// body: formData, -// }) -// .then(response => response.json()) -// .then(data => console.log('Upload successful:', data)) -// .catch(error => console.error('Upload failed:', error)); + xhr.open('POST', '/upload?target=document', false); + xhr.send(formData); + } } - } }); }); \ No newline at end of file