소스 검색

Fixes #33 delete file button impl

isundil 1 년 전
부모
커밋
c0306d1f0d

+ 9 - 0
model/mediaService.js

@@ -2,6 +2,8 @@
 const path = require('path');
 const fs = require('fs');
 const MediaFileModel = require('./mediaItem.js').MediaFileModel;
+const { MediaFileTagModel } = require('./mediaItemTag.js');
+const { MediaFileMetaModel } = require('./mediaItemMeta.js');
 const { AccessModel, ACCESS_TYPE, ACCESS_TO, ACCESS_GRANT } = require('./access.js');
 
 function Media()
@@ -214,6 +216,13 @@ function fetchMediasBuildSubQuery(startTs, count, access, maxVersion) {
     };
 }
 
+module.exports.removeMedia = async function(app, media) {
+    await app.databaseHelper.remove(MediaFileTagModel, { md5sum: media.fixedSum });
+    await app.databaseHelper.remove(MediaFileMetaModel, { md5sum: media.fixedSum });
+    await app.databaseHelper.remove(MediaFileModel, { fixedSum: media.fixedSum });
+    await app.libraryManager.removeMedia(media.path);
+}
+
 module.exports.fetchMedias = async function(app, startTs, count, access, maxVersion) {
     let subQuery = fetchMediasBuildSubQuery(startTs, count, access, maxVersion);
     let result = ((await app.databaseHelper.runSql(`

+ 8 - 0
router/api.js

@@ -260,6 +260,14 @@ module.exports = { register: app => {
                 req.sessionObj?.accessList)
         });
     });
+    app.router.del("/api/media/:md5sum", async (req, res) => {
+        app.routerUtils.onApiRequest(req, res);
+        let data = MediaToJson(await MediaService.fetchOne(app, req.params.md5sum, req.sessionObj?.accessList, 0));
+        if (!data)
+            return app.routerUtils.onPageNotFound(res);
+        await MediaService.removeMedia(app, data);
+        app.routerUtils.jsonResponse(res, {});
+    });
     app.router.get("/api/media/:md5sum", async (req, res) => {
         app.routerUtils.onApiRequest(req, res);
         let data = MediaToJson(await MediaService.fetchOne(app, req.params.md5sum, req.sessionObj?.accessList, 0));

+ 1 - 1
src/filetype/imagemagick.js

@@ -115,7 +115,7 @@ module.exports.parse = async (fileObj, data) => {
         result.tags = readTags(imdata);
     }
     catch (err) {
-        result.imException = err;
+        result.imException = err.toString();
     }
     for (let i of Object.keys(result))
         if (result[i] === undefined || result[i].length === 0)

+ 8 - 0
src/library.js

@@ -180,5 +180,13 @@ Library.prototype.findMedia = async function(p) {
     return result;
 }
 
+Library.prototype.removeMedia = async function(path) {
+    if (!path.startsWith(this.path))
+        return false;
+    console.log("removing ", path);
+    fs.unlinkSync(path);
+    return true;
+}
+
 module.exports.Library = Library;
 

+ 9 - 0
src/libraryManager.js

@@ -26,6 +26,15 @@ LibraryManager.prototype.updateLibraries = async function(dbHelper) {
 LibraryManager.prototype.isUpdating = function()
 { return this.updating; }
 
+LibraryManager.prototype.removeMedia = async function(path) {
+    path = Path.normalize(path);
+    for (let i of this.libraries) {
+        if (await i.removeMedia(path))
+            return true;
+    }
+    return false;
+}
+
 LibraryManager.prototype.findMedia = async function(path) {
     path = Path.normalize(path);
     for (let i of this.libraries) {

+ 11 - 4
static/public/js/indexedCache.js

@@ -121,19 +121,26 @@
             });
         }
 
-        async removeObsolete() {
+        async removeItems(md5Sums) {
             await this.#openReq;
             if (!this.#db)
                 return;
-
-            let obsoleteIndexes = await this.#getObsoleteKeys();
-            for (let i of obsoleteIndexes)
+            for (let i of md5Sums)
                 await new Promise((ok, ko) => {
                     let transaction = this.#db.transaction([INDEXEDCACHE_DATABASE_NAME], IDBTransaction.READ_WRITE);
                     transaction.oncomplete = ok;
                     transaction.onerror = ko;
                     transaction.objectStore(INDEXEDCACHE_DATABASE_NAME).delete(i);
                 });
+        }
+
+        async removeObsolete() {
+            await this.#openReq;
+            if (!this.#db)
+                return;
+
+            let obsoleteIndexes = await this.#getObsoleteKeys();
+            await this.removeItems(obsoleteIndexes);
             return obsoleteIndexes;
         }
 

+ 15 - 0
static/public/js/medias.js

@@ -296,6 +296,21 @@ class MediaStorage extends EventTarget
         return this.medias.find(x => x.fixedSum === md5sum);
     }
 
+    async remoteRemove(md5Sums) {
+        for (let md5sum of md5Sums) {
+            await new Promise(ok => {
+                $.ajax({
+                    url: `/api/media/${encodeURIComponent(md5sum)}`,
+                    type: "DELETE",
+                    success: ok,
+                    error: err => { console.error(err); ok() },
+                });
+            });
+        }
+        await indexedData.removeItems(md5Sums);
+        this.removeMedia(md5Sums);
+    }
+
     async getMedia(md5sum) {
         if (!md5sum)
             return null;

+ 4 - 4
static/public/js/uiCommon.js

@@ -18,10 +18,10 @@ $(() => {
             triggerClosePopinsRequestHandlers();
     });
 
-    document.getElementById("pch-navbar-reload").addEventListener("click", () => reloadServerDb());
-    document.getElementById("pch-navbar-share").addEventListener("click", () => showShareUi());
-    document.getElementById("pch-navbar-autotags").addEventListener("click", () => showAutoTagsUi());
-    document.getElementById("fullScreenOverlay").addEventListener("click", triggerClosePopinsRequestHandlers);
+    document.getElementById("pch-navbar-reload").addEventListener("click", e => { e.preventDefault(); reloadServerDb(); });
+    document.getElementById("pch-navbar-share").addEventListener("click", e => { e.preventDefault(); showShareUi(); });
+    document.getElementById("pch-navbar-autotags").addEventListener("click", e => { e.preventDefault(); showAutoTagsUi(); });
+    document.getElementById("fullScreenOverlay").addEventListener("click", e => { e.preventDefault(); triggerClosePopinsRequestHandlers(); });
     document.onClosePopinRequested = (hndl) => closePopinsRequestedHandlers.push(hndl);
 });
 

+ 20 - 4
static/public/js/uiMediaFullpage.js

@@ -209,7 +209,21 @@ $(() => {
         return bt;
     }
 
-    function _displayMediaFullPage(fileName, imgUrl, metaData, downloadLink, writeAccess) {
+    function displayRemoveButton(ids) {
+        let bt = document.createElement("button");
+        bt.type = "button";
+        bt.className = "btn btn-danger";
+        bt.textContent = ids.length > 1 ? "Remove files" : "Remove file";
+        bt.addEventListener("click", async evt => {
+            if (window.confirm("File will be removed from the server. Are you sure ?")) {
+                await MediaStorage.Instance.remoteRemove(ids);
+                CloseFullpageMedia();
+            }
+        });
+        return bt;
+    }
+
+    function _displayMediaFullPage(fileName, imgUrl, metaData, downloadLink, fileIds, writeAccess) {
         return new Promise(ok => {
             document.getElementById("pch-fullPagePreviewContainer").classList.add("loading");
             document.getElementById("pch-fullPageMedia-title").innerText = fileName;
@@ -219,6 +233,8 @@ $(() => {
             document.getElementById("pch-fullPageDetail").innerText = "";
             if (downloadLink)
                 document.getElementById("pch-fullPageDetail").appendChild(displayDownloadBt(downloadLink));
+            if (fileIds && writeAccess)
+                document.getElementById("pch-fullPageDetail").appendChild(displayRemoveButton(fileIds));
             document.getElementById("pch-fullPageDetail").appendChild(displayTags(metaData?.fixedTags || [], metaData?.tags || [], writeAccess));
             document.getElementById("pch-fullPageDetail").appendChild(displayMetas(Object.assign({}, metaData || {}), !writeAccess));
         });
@@ -265,7 +281,7 @@ $(() => {
         fullPageMediaDisplayed = mediaItem ?? null;
         document.body.classList.add("overlay-visible");
         if (!mediaItem)
-            return _displayMediaFullPage("Error", null, {}, null, false);
+            return _displayMediaFullPage("Error", null, {}, null, null, false);
         let containerSize = document.getElementById("pch-fullPageMedia").getBoundingClientRect();
         let requestSize = mediaItem.resize(containerSize.width, containerSize.height);
         document.getElementById("pch-fullPagePreview").parentNode.style.maxWidth = "100%";
@@ -280,7 +296,7 @@ $(() => {
         if (document.location.hash != `#${mediaItem.fixedSum}`)
             history.pushState({}, '', `#${mediaItem.fixedSum}`);
         const requestSizeQuery = requestSize ? `&w=${requestSize.width}&h=${requestSize.height}` : "";
-        return _displayMediaFullPage(mediaItem.fileName, `${mediaItem.thumbnail}?q=6${requestSizeQuery}`, meta, `${mediaItem.original}?trim`, mediaItem.writeAccess);
+        return _displayMediaFullPage(mediaItem.fileName, `${mediaItem.thumbnail}?q=6${requestSizeQuery}`, meta, `${mediaItem.original}?trim`, [mediaItem.fixedSum], mediaItem.writeAccess);
     }
 
     function aggregateMetas(medias) {
@@ -313,7 +329,7 @@ $(() => {
             fixedTags: medias.reduce((acc, x) => { x.fixedTags.forEach(tag => acc.add(tag)) ; return acc; }, new Set()),
             tags: medias.reduce((acc, x) => { x.tags.forEach(tag => acc.add(tag)) ; return acc; }, new Set()),
         };
-        return _displayMediaFullPage(title, "", meta, null, true);
+        return _displayMediaFullPage(title, "", meta, null, medias.map(x => x.fixedSum), true);
     }
 
     document.getElementById("pch-fullPageMedia-closeBt")