Files
2022-01-31 17:53:56 +11:00

322 lines
10 KiB
JavaScript

"use strict";
const DB_VERSION = 3;
const FILE_PATH = "file_path";
const FILE_CONTENT = "file_content";
function DataFromIndexedDB() {
this.db = null;
this.FILE_PATH = FILE_PATH;
this.FILE_CONTENT = FILE_CONTENT;
return this._init();
}
function DataFromMemory() {
this.data = {};
this.FILE_PATH = FILE_PATH;
this.FILE_CONTENT = FILE_CONTENT;
return this._init();
}
DataFromIndexedDB.prototype._init = function() {
const request = indexedDB.open("filestash", DB_VERSION);
request.onupgradeneeded = function(event) {
let store;
const db = event.target.result;
if (event.oldVersion == 1) {
// we've change the schema on v2 adding an index, let's flush
// to make sure everything will be fine
db.deleteObjectStore(FILE_PATH);
db.deleteObjectStore(FILE_CONTENT);
} else if (event.oldVersion == 2) {
// we've change the primary key to be a (path,share)
db.deleteObjectStore(FILE_PATH);
db.deleteObjectStore(FILE_CONTENT);
}
store = db.createObjectStore(FILE_PATH, { keyPath: ["share", "path"] });
store.createIndex("idx_path", ["share", "path"], { unique: true });
store = db.createObjectStore(FILE_CONTENT, { keyPath: ["share", "path"] });
store.createIndex("idx_path", ["share", "path"], { unique: true });
};
this.db = new Promise((done, err) => {
request.onsuccess = (e) => {
done(e.target.result);
};
request.onerror = (e) => err("INDEXEDDB_NOT_SUPPORTED");
});
};
DataFromMemory.prototype._init = function() {
};
/*
* Fetch a record using its path, can be either a file path or content
*/
DataFromIndexedDB.prototype.get = function(type, key) {
if (type !== FILE_PATH && type !== FILE_CONTENT) return Promise.reject();
return this.db.then((db) => {
const tx = db.transaction(type, "readonly");
const store = tx.objectStore(type);
const query = store.get(key);
return new Promise((done, error) => {
query.onsuccess = (e) => {
done(query.result || null);
};
query.onerror = () => done();
});
});
};
DataFromMemory.prototype.get = function(type, key) {
if (type !== FILE_PATH && type !== FILE_CONTENT) return Promise.reject();
const data = this.data[type] || null;
if (data === null) {
return Promise.resolve(null);
}
const value = data[key.join("_")] || null;
if (value === null) {
return Promise.resolve(null);
}
return new Promise((done) => {
requestAnimationFrame(() => done(value));
});
};
DataFromIndexedDB.prototype.update = function(type, key, fn, exact = true) {
if (type !== FILE_PATH && type !== FILE_CONTENT) return Promise.reject();
return this.db.then((db) => {
const tx = db.transaction(type, "readwrite");
const store = tx.objectStore(type);
const range = exact === true? IDBKeyRange.only(key) : IDBKeyRange.bound(
[key[0], key[1]],
[key[0], key[1]+"\uFFFF"],
false, true,
);
const request = store.openCursor(range);
let new_data = null;
return new Promise((done, err) => {
request.onsuccess = function(event) {
const cursor = event.target.result;
if (!cursor) return done(new_data);
new_data = fn(cursor.value || null);
cursor.delete([key[0], cursor.value.path]);
store.put(new_data);
cursor.continue();
};
});
}).catch(() => Promise.resolve());
};
DataFromMemory.prototype.update = function(type, key, fn, exact = true) {
if (type !== FILE_PATH && type !== FILE_CONTENT) return Promise.reject();
const data = this.data[type];
if (data === undefined) {
return Promise.resolve(null);
}
const k = key.join("_");
if (exact === true) {
if (this.data[type][k] !== undefined) this.data[type][k] = fn(data[k]);
} else {
for (const _k in data) {
if (_k.indexOf(k) === 0) {
this.data[type][_k] = fn(data[_k]);
}
}
}
return Promise.resolve();
};
DataFromIndexedDB.prototype.upsert = function(type, key, fn) {
if (type !== FILE_PATH && type !== FILE_CONTENT) return Promise.reject();
return this.db.then((db) => {
const tx = db.transaction(type, "readwrite");
const store = tx.objectStore(type);
const query = store.get(key);
return new Promise((done, error) => {
query.onsuccess = (e) => {
const new_data = fn(query.result || null);
if (!new_data) return done(query.result || null);
const request = store.put(new_data);
request.onsuccess = () => done(new_data);
request.onerror = (e) => error(e);
};
query.onerror = error;
});
});
};
DataFromMemory.prototype.upsert = function(type, key, fn) {
if (type !== FILE_PATH && type !== FILE_CONTENT) return Promise.reject();
const db = this.data[type] || null;
if (db === null) {
this.data[type] = {};
}
const k = key.join("_");
const new_data = fn(this.data[type][k]);
this.data[type][k] = fn(new_data);
return Promise.resolve(new_data);
};
DataFromIndexedDB.prototype.add = function(type, key, data) {
if (type !== FILE_PATH && type !== FILE_CONTENT) return Promise.reject();
return this.db.then((db) => {
return new Promise((done, error) => {
const tx = db.transaction(type, "readwrite");
const store = tx.objectStore(type);
const request = store.put(data);
request.onsuccess = () => done(data);
request.onerror = (e) => error(e);
});
}).catch(() => Promise.resolve());
};
DataFromMemory.prototype.add = function(type, key, data) {
if (type !== FILE_PATH && type !== FILE_CONTENT) return Promise.reject();
if (this.data[type] === undefined) {
this.data[type] = {};
}
this.data[type][key.join("_")] = data;
return Promise.resolve(data);
};
DataFromIndexedDB.prototype.remove = function(type, key, exact = true) {
if (type !== FILE_PATH && type !== FILE_CONTENT) return Promise.reject();
return this.db.then((db) => {
const tx = db.transaction(type, "readwrite");
const store = tx.objectStore(type);
if (exact === true) {
const req = store.delete(key);
return new Promise((done, err) => {
req.onsuccess = () => done();
req.onerror = err;
});
} else {
const request = store.openCursor(IDBKeyRange.bound(
[key[0], key[1]],
[key[0], key[1]+"\uFFFF"],
true, true,
));
return new Promise((done, err) => {
request.onsuccess = function(event) {
const cursor = event.target.result;
if (cursor) {
cursor.delete([key[0], cursor.value.path]);
cursor.continue();
} else {
done();
}
};
});
}
}).catch(() => Promise.resolve());
};
DataFromMemory.prototype.remove = function(type, key, exact = true) {
if (type !== FILE_PATH && type !== FILE_CONTENT) return Promise.reject();
const data = this.data[type] || null;
if (data === null) {
return Promise.resolve();
}
const k = key.join("_");
if (exact === true) {
delete data[k];
}
for (const _k in data) {
if (_k.indexOf(k) === 0) {
delete data[_k];
}
}
return Promise.resolve();
};
DataFromIndexedDB.prototype.fetchAll = function(fn, type = FILE_PATH, key) {
if (type !== FILE_PATH && type !== FILE_CONTENT) return Promise.reject();
return this.db.then((db) => {
const tx = db.transaction([type], "readonly");
const store = tx.objectStore(type);
const index = store.index("idx_path");
const request = index.openCursor(IDBKeyRange.bound(
[key[0], key[1]],
[key[0], key[1]+("z".repeat(5000))],
));
return new Promise((done, error) => {
request.onsuccess = function(event) {
const cursor = event.target.result;
if (!cursor) {
return done();
}
const ret = fn(cursor.value);
if (ret === false) {
return done();
}
cursor.continue();
};
request.onerror = () => {
done();
};
});
}).catch(() => Promise.resolve());
};
DataFromMemory.prototype.fetchAll = function(fn, type = FILE_PATH, key) {
if (type !== FILE_PATH && type !== FILE_CONTENT) return Promise.reject();
const data = this.data[type] || null;
if (data === null) {
return Promise.resolve();
}
const k = key.join("_");
for (const _k in data) {
if (_k.indexOf(k) === 0) {
const ret = fn(data[_k]);
if (ret === false) {
return Promise.resolve();
}
}
}
return Promise.resolve();
};
DataFromIndexedDB.prototype.destroy = function() {
return new Promise((done, err) => {
this.db.then((db) => {
purgeAll(db, FILE_PATH);
purgeAll(db, FILE_CONTENT);
});
done();
function purgeAll(db, type) {
const tx = db.transaction(type, "readwrite");
const store = tx.objectStore(type);
store.clear();
}
});
};
DataFromMemory.prototype.destroy = function() {
this.data = {};
return Promise.resolve();
};
export let cache = new DataFromMemory();
if ("indexedDB" in window && window.indexedDB !== null) {
const request = indexedDB.open("_indexedDB", 1);
request.onsuccess = (e) => {
cache = new DataFromIndexedDB();
indexedDB.deleteDatabase("_indexedDB");
};
}