ソースを参照

Fix access issues
Refs #28 Add inaccessible flag on missing files

isundil 1 年間 前
コミット
af30a11796

+ 2 - 1
main.js

@@ -30,7 +30,8 @@ App.prototype.init = async function() {
         require('./model/mediaItemMeta.js').MediaFileMetaModel,
         require('./model/mediaItemTag.js').MediaFileTagModel,
         require('./model/access.js').AccessModel,
-        require('./model/autotag.js').AutotagModel
+        require('./model/autotag.js').AutotagModel,
+        require('./model/configModel.js').ConfigModel
     ]);
 }
 

+ 68 - 0
model/configModel.js

@@ -0,0 +1,68 @@
+
+const DatabaseModel = require("./DatabaseModel.js").DatabaseModel;
+
+function ConfigModel() {
+    DatabaseModel.call(this);
+    this.key = "";
+    this.value = "";
+}
+
+ConfigModel.prototype = Object.create(DatabaseModel.prototype);
+
+ConfigModel.prototype.getTableName = function() {
+    return "config";
+}
+
+ConfigModel.prototype.createOrUpdateBase = async function(dbHelper) {
+    await dbHelper.runSql(`CREATE TABLE IF NOT EXISTS 'config' (
+        key STRING NOT NULL PRIMARY KEY,
+        value STRING
+        )`);
+}
+
+ConfigModel.prototype.describe = function() {
+    return {
+        "key": this.key,
+        "value": this.value
+    };
+}
+
+ConfigModel.prototype.versionColumn = function() { return ""; }
+
+ConfigModel.prototype.fromDb = function(dbObj) {
+    this.key = dbObj["key"];
+    this.value = dbObj["value"];
+}
+
+class ConfigLoader
+{
+    #loading = null;
+    #configEntries = {};
+
+    constructor(app) {
+        this.#loading = this.#init(app);
+    }
+
+    async #init(app) {
+        // FIXME
+    }
+
+    async getValue(key) {
+        await this.#loading;
+        return this.#configEntries[key] || null;
+    }
+
+    async setValue(key, value) {
+        await this.#loading;
+        return this.#configEntries[key] = value;
+    }
+
+    async flush(app) {
+        await this.#loading;
+        // FIXME
+    }
+}
+
+module.exports.ConfigModel = ConfigModel;
+module.exports.ConfigLoader = (app) => new ConfigModel(app);
+

+ 4 - 0
model/mediaItem.js

@@ -8,6 +8,7 @@ function MediaFileModel() {
     this.fixedSum = "";
     this.date = null;
     this.version = 0;
+    this.inaccessible = 0;
     this.updateVersion();
 }
 
@@ -23,6 +24,7 @@ MediaFileModel.prototype.createOrUpdateBase = async function(dbHelper) {
         md5sum varchar(32) NOT NULL,
         fixedSum varchar(32) NOT NULL,
         date DateTime NOT NULL,
+        inaccessible DateTime NOT NULL,
         version integer NOT NULL,
         PRIMARY KEY (path, md5sum))`);
 }
@@ -33,6 +35,7 @@ MediaFileModel.prototype.describe = function() {
         "md5sum": this.md5sum,
         "fixedSum": this.fixedSum,
         "date": this.date?.getTime(),
+        "inaccessible": this.inaccessible,
         "version": this.version
     };
 }
@@ -44,6 +47,7 @@ MediaFileModel.prototype.fromDb = function(dbObj) {
     this.md5sum = dbObj["md5sum"];
     this.fixedSum = dbObj["fixedSum"];
     this.date = new Date(dbObj["date"]);
+    this.inaccessible = dbObj["inaccessible"] || 0;
     this.version = dbObj["fixedSum"];
 }
 

+ 8 - 4
model/mediaService.js

@@ -140,7 +140,7 @@ module.exports.fetchMultiple = async function(app, md5sums, accessList, maxVersi
         from mediaFile
         left join mediaMeta on mediaMeta.md5sum=mediaFile.fixedSum
         left join mediaTag on mediaTag.md5sum=mediaFile.fixedSum
-        where mediaFile.version>? and mediaFile.fixedSum in (` +md5sums.map(x => '?').join(',')+`)`, [maxVersion].concat(md5sums))) || []).reduce(reduceReqToMediaStruct, {}) || null;
+        where mediaFile.inaccessible=0 and mediaFile.version>? and mediaFile.fixedSum in (` +md5sums.map(x => '?').join(',')+`)`, [maxVersion].concat(md5sums))) || []).reduce(reduceReqToMediaStruct, {}) || null;
     accessList = await buildAccessList(app, accessList);
     for (let key in result)
         if (!result[key].HaveAccess(accessList))
@@ -177,7 +177,7 @@ function fetchAccessSubQuery(args, access) {
 
 function fetchMediasBuildSubQuery(startTs, count, access, maxVersion) {
     let query = "select distinct fixedSum from mediaFile";
-    let whereClause = ` where version>?`;
+    let whereClause = ` where inaccessible=0 and version>?`;
     let accessWhere = null;
     let args = [maxVersion];
 
@@ -248,7 +248,11 @@ module.exports.updateVersionInDb = function(app, fixedSum) {
 }
 
 module.exports.getMediaRange = async function(app) {
-    let result = await app.databaseHelper.runSql("select min(date) as _min, max(date) as _max from mediaFile");
-    return [ result?.[0]?._min || 0, result?.[0]?._max || 0 ];
+    let result = await app.databaseHelper.runSql("select min(date) as _min, max(date) as _max, max(version) as _version from mediaFile");
+    return {
+        min: result?.[0]?._min || 0,
+        max: result?.[0]?._max || 0,
+        maxVersion: result?.[0]?._version || 0
+    };
 }
 

+ 9 - 6
router/api.js

@@ -64,7 +64,7 @@ module.exports = { register: app => {
 
         let data = await MediaService.fetchMultiple(app, checksum, req.sessionObj?.accessList, 0);
         data = Object.keys(data).map(x => data[x]).filter(x => x.ACCESS_TYPE != ACCESS_GRANT.write);
-        await Promise.all(data.map(x => x.updateVersionInDb(app.databaseHelper)));
+        await Promise.all(data.map(x => MediaService.updateVersionInDb(app, x.fixedSum)));
 
         await app.databaseHelper.remove(MediaFileTagModel, { md5sum: data.map(x => x.fixedSum), tag: decodeURIComponent(req.params.tag), fromMeta: 0 });
         const allMedias = await MediaService.fetchMultiple(app, checksum, req.sessionObj?.accessList, 0);
@@ -95,7 +95,7 @@ module.exports = { register: app => {
                     }
                 }
             });
-        await Promise.all(data.map(x => x.updateVersionInDb(app.databaseHelper)));
+        await Promise.all(data.map(x => MediaService.updateVersionInDb(app, x.fixedSum)));
 
         let tag = data.map(x => new MediaFileTagModel(x.fixedSum, requestedTag, false));
         await app.databaseHelper.insertMultipleSameTable(tag);
@@ -133,11 +133,13 @@ module.exports = { register: app => {
     app.router.get("/api/media/list", async (req, res) => {
         app.routerUtils.onApiRequest(req, res);
         let first = undefined,
-            last = undefined;
+            last = undefined,
+            maxVersion = undefined;
         if (req.body?.chronology !== undefined) {
             let range = await MediaService.getMediaRange(app);
-            first = range[0];
-            last = range[1];
+            first = range.min;
+            last = range.max;
+            maxVersion = range.maxVersion;
         }
         let fromDate = parseInt(req.body?.from);
         let count = parseInt(req.body?.count);
@@ -149,7 +151,8 @@ module.exports = { register: app => {
                 req.sessionObj?.accessList,
                 req.body?.version || 0)).map(MediaToJson),
             first: first,
-            last: last
+            last: last,
+            maxVersion: maxVersion
         });
     });
     app.router.get("/api/media/:md5sum", async (req, res) => {

+ 26 - 2
src/library.js

@@ -6,6 +6,7 @@ const md5String = require('craftlabhttpserver/src/md5sum.js').string;
 const FileTypeManager = require('./fileTypeManager.js');
 
 const { ACCESS_TO, AccessModel } = require("../model/access.js");
+const CreateConfigLoader = require('../model/configModel.js').ConfigLoader;
 const MediaFileModel = require("../model/mediaItem.js").MediaFileModel;
 const MediaFileMetaModel = require("../model/mediaItemMeta.js").MediaFileMetaModel;
 const MediaFileTagModel = require("../model/mediaItemTag.js").MediaFileTagModel;
@@ -92,9 +93,9 @@ async function Library_doUpdate(dbHelper, lib) {
     if (lib.buff.length === 0)
         return;
     lib.buff = lib.buff.filter(i => !!i.checksum);
-    const dbItems = (await dbHelper.fetchRaw(["path", "md5sum", "fixedSum"], MediaFileModel.prototype.getTableName.call(null), { "path": lib.buff.map(i => i.path) }));
+    const dbItems = (await dbHelper.fetchRaw(["path", "md5sum", "fixedSum", "inaccessible"], MediaFileModel.prototype.getTableName.call(null), { "path": lib.buff.map(i => i.path) }));
     lib.buff.forEach(i => i.dbItem = dbItems.find(x => x.path == i.path));
-    lib.buff = lib.buff.filter(i => !i.dbItem || i.dbItem.md5sum != i.checksum);
+    lib.buff = lib.buff.filter(i => !i.dbItem || i.dbItem.md5sum != i.checksum || i.dbItem.inaccessible);
     await enrichAll(lib);
     (await Promise.allSettled(lib.buff.map(i => i.saveDb(dbHelper, lib.dbHash)))).forEach(x => { if (x.status === 'rejected') { console.log(`Cannot update item: `, x.reason); }});
     lib.foundMedias = lib.foundMedias.concat(lib.buff);
@@ -130,6 +131,28 @@ function Library(p) {
     this.dbHash = md5String(this.path);
 }
 
+function notReadable(path) {
+    return !fs.existsSync(path);
+}
+
+Library.prototype.findInaccessibleItems = async function(dbHelper) {
+    let i =0;
+    const pager = 500;
+    const currentVersion = Date.now();
+    let data = [];
+    do {
+        data = (await dbHelper.runSql(`select mediaFile.fixedSum, mediaFile.path from mediaFile
+                inner join mediaMeta on mediaMeta.md5sum=mediaFile.fixedSum
+                where mediaMeta.key=? and mediaMeta.value=? and mediaFile.inaccessible=0 limit ? offset ?`, ["libraryPath", this.dbHash, pager, pager*i]));
+        let missing = data.filter(x => notReadable(x.path));
+        if (missing.length) {
+            console.log(missing);
+            await dbHelper.runSql("update mediaFile set version=?, inaccessible=? where fixedSum in ("+missing.map(x => '?').join(',')+")", [ currentVersion, currentVersion].concat(missing.map(x => x.fixedSum)));
+        }
+        ++i;
+    } while (data.length == pager);
+}
+
 Library.prototype.updateLibrary = async function(dbHelper) {
     const startTime = Date.now();
     console.log(`Starting update of library ${this.path}`);
@@ -140,6 +163,7 @@ Library.prototype.updateLibrary = async function(dbHelper) {
         if (Library_isValid(this))
             await Library_depthupdate(dbHelper, this, this.path);
         await Library_doUpdate(dbHelper, this)
+        await this.findInaccessibleItems(dbHelper);
     } catch (err) {
         console.err(`Cannot update Library ${this.path}:`, err);
     }

+ 4 - 2
static/public/js/access.js

@@ -38,8 +38,10 @@ $(() => {
                     $.ajax({
                         url: `/api/access/${code}`,
                         type: "DELETE",
-                        success: data => {
+                        success: async data => {
                             window.ReloadAccessList(data);
+                            await window.indexedData.removeAll();
+                            await MediaStorage.Instance.rebuildMetaList();
                             ok();
                         },
                         error: err => ok(false),
@@ -58,7 +60,7 @@ $(() => {
                     url: "/api/access/link",
                     type: "POST",
                     data: { linkIds: JSON.stringify(Array.from(linkList)) },
-                    success: data => {
+                    success: async data => {
                         for (let i in data)
                             data[i].linkId && this.#linkStoredAccesses.add({key: i, value: data[i].linkId });
                         this.#isAdmin = data.isAdmin;

+ 13 - 0
static/public/js/indexedCache.js

@@ -103,6 +103,19 @@
             console.log("Done updating db");
         }
 
+        async removeAll() {
+            await this.#openReq;
+            if (!this.#db)
+                return;
+            await new Promise(ok => {
+                let transaction = this.#db.transaction([INDEXEDCACHE_DATABASE_NAME], IDBTransaction.READ_WRITE);
+                transaction.oncomplete = ok;
+                transaction.onerror = ok;
+                let storage = transaction.objectStore(INDEXEDCACHE_DATABASE_NAME);
+                storage.clear();
+            });
+        }
+
         async clean() {
             await this.#openReq;
             if (!this.#db)

+ 4 - 3
static/public/js/medias.js

@@ -117,7 +117,6 @@ class MediaStorage extends EventTarget
 
     async rebuildMetaList() {
         this.#reset();
-        await window.indexedData.clean();
         this.dispatchEvent(new CustomEvent("rebuildMedia"));
         window.chronology.reset();
         this.downloadMetaList();
@@ -160,14 +159,16 @@ class MediaStorage extends EventTarget
                     this.pushAll(data.data.map(i => new Media(i)));
                     if (data.first || data.last)
                         window.chronology.rebuildRange(data.first, data.last);
+                    if (data.maxVersion)
+                        this.loadingVersion = parseInt(data.maxVersion);
                     if ((data.data?.length || 0) < requestCount) {
                         this.#isLoading = false;
-                        document.getElementById("pch-infiniteScrollLoading").classList.add("hidden");
                         this.#updateDbVersion(this.loadingVersion);
                         window.ReloadFilters(MediaStorage.Instance);
                         this.dispatchEvent(new CustomEvent("doneLoading"));
+                        document.getElementById("pch-infiniteScrollLoading").classList.add("hidden");
                     } else {
-                        this.#doDownloadMetaList(isUpdate);
+                        this.#doDownloadMetaList(false);
                     }
                     ok();
                 });

+ 8 - 1
static/public/js/uiAccess.js

@@ -52,10 +52,17 @@ async function logout(accessId, linkId) {
 }
 
 window.ReloadAccessList = function(accessList) {
-    console.log(accessList);
     document.getElementById("pch-navbar-reload");
     document.getElementById("pch-navbar-share");
 
+    if (accessList.isAdmin) {
+        document.getElementById("pch-navbar-reload").classList.remove("hidden");
+        document.getElementById("pch-navbar-share").classList.remove("hidden");
+    } else {
+        document.getElementById("pch-navbar-reload").classList.add("hidden");
+        document.getElementById("pch-navbar-share").classList.add("hidden");
+    }
+
     let getIconForType = type => {
         if (!!type.linkId) return "bi-link-45deg";
         if (!!type.userName) return "bi-person";

+ 2 - 2
templates/_menu.js

@@ -6,10 +6,10 @@ module.exports = `
         <div class="container-fluid" id="navbarSupportedContent">
             <ul class="navbar-nav col justify-content-end">
                 <li class="nav-item">
-                    <a class="nav-link" href="#" role="button" id="pch-navbar-reload"><i class="bi bi-database-gear">&nbsp;</i></a>
+                    <a class="nav-link hidden" href="#" role="button" id="pch-navbar-reload"><i class="bi bi-database-gear">&nbsp;</i></a>
                 </li>
                 <li class="nav-item">
-                    <a class="nav-link" href="#" role="button" id="pch-navbar-share"><i class="bi bi-share">&nbsp;</i></a>
+                    <a class="nav-link hidden" href="#" role="button" id="pch-navbar-share"><i class="bi bi-share">&nbsp;</i></a>
                 </li>
                 <li class="nav-item">
                     <a class="nav-link" id="pch-navbar-filterToggle" href="#" role="button">