implement database cleanup on logout
This commit is contained in:
parent
8e81926076
commit
0e229bedff
2
routes/_utils/database/cache.js
Normal file
2
routes/_utils/database/cache.js
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export const openReqs = {}
|
||||||
|
export const databaseCache = {}
|
|
@ -1,9 +1,5 @@
|
||||||
import worker from 'workerize-loader!./databaseInsideWorker'
|
import worker from 'workerize-loader!./databaseCore'
|
||||||
|
const database = process.browser && worker()
|
||||||
import * as databaseInsideWorker from './databaseInsideWorker'
|
|
||||||
|
|
||||||
// workerize-loader causes weirdness during development
|
|
||||||
let database = process.browser && process.env.NODE_ENV === 'production' ? worker() : databaseInsideWorker
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
database
|
database
|
||||||
|
|
62
routes/_utils/database/databaseCore.js
Normal file
62
routes/_utils/database/databaseCore.js
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
import { META_STORE, getMetaDatabase } from './meta'
|
||||||
|
import { cleanupOldStatuses } from './cleanupTimelines'
|
||||||
|
import { TIMELINE_STORE, getTimelineDatabase } from './timelines'
|
||||||
|
import { toReversePaddedBigInt, transformStatusForStorage, dbPromise, deleteDbPromise } from './utils'
|
||||||
|
import { getKnownDbsForInstance, deleteInstanceFromKnownDbs } from './knownDbs'
|
||||||
|
|
||||||
|
export async function getTimeline(instanceName, timeline, maxId = null, limit = 20) {
|
||||||
|
const db = await getTimelineDatabase(instanceName, timeline)
|
||||||
|
return await dbPromise(db, TIMELINE_STORE, 'readonly', (store, callback) => {
|
||||||
|
const index = store.index('pinafore_id_as_negative_big_int')
|
||||||
|
let sinceAsNegativeBigInt = maxId ? toReversePaddedBigInt(maxId) : null
|
||||||
|
let query = sinceAsNegativeBigInt ? IDBKeyRange.lowerBound(sinceAsNegativeBigInt, false) : null
|
||||||
|
|
||||||
|
index.getAll(query, limit).onsuccess = (e) => {
|
||||||
|
callback(e.target.result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function insertStatuses(instanceName, timeline, statuses) {
|
||||||
|
const db = await getTimelineDatabase(instanceName, timeline)
|
||||||
|
await dbPromise(db, TIMELINE_STORE, 'readwrite', (store) => {
|
||||||
|
for (let status of statuses) {
|
||||||
|
store.put(transformStatusForStorage(status))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
/* no await */ cleanupOldStatuses()
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getInstanceVerifyCredentials(instanceName) {
|
||||||
|
const db = await getMetaDatabase(instanceName)
|
||||||
|
return await dbPromise(db, META_STORE, 'readonly', (store, callback) => {
|
||||||
|
store.get('verifyCredentials').onsuccess = (e) => {
|
||||||
|
callback(e.target.result && e.target.result.value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setInstanceVerifyCredentials(instanceName, verifyCredentials) {
|
||||||
|
const db = await getMetaDatabase(instanceName)
|
||||||
|
return await dbPromise(db, META_STORE, 'readwrite', (store) => {
|
||||||
|
store.put({
|
||||||
|
key: 'verifyCredentials',
|
||||||
|
value: verifyCredentials
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function clearDatabasesForInstance(instanceName) {
|
||||||
|
console.log('clearDatabasesForInstance', instanceName)
|
||||||
|
const knownDbsForInstance = await getKnownDbsForInstance(instanceName)
|
||||||
|
for (let knownDb of knownDbsForInstance) {
|
||||||
|
let { dbName } = knownDb
|
||||||
|
try {
|
||||||
|
await deleteDbPromise(dbName)
|
||||||
|
console.error(`deleted database ${dbName}`)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`failed to delete database ${dbName}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await deleteInstanceFromKnownDbs(instanceName)
|
||||||
|
}
|
|
@ -1,65 +0,0 @@
|
||||||
import { META_STORE, getMetaDatabase } from './meta'
|
|
||||||
import { cleanupOldStatuses } from './cleanupTimelines'
|
|
||||||
import { TIMELINE_STORE, getTimelineDatabase } from './timelines'
|
|
||||||
import { toReversePaddedBigInt, transformStatusForStorage } from './utils'
|
|
||||||
|
|
||||||
export async function getTimeline(instanceName, timeline, maxId = null, limit = 20) {
|
|
||||||
const db = await getTimelineDatabase(instanceName, timeline)
|
|
||||||
return await new Promise((resolve, reject) => {
|
|
||||||
const tx = db.transaction(TIMELINE_STORE, 'readonly')
|
|
||||||
const store = tx.objectStore(TIMELINE_STORE)
|
|
||||||
const index = store.index('pinafore_id_as_negative_big_int')
|
|
||||||
let sinceAsNegativeBigInt = maxId ? toReversePaddedBigInt(maxId) : null
|
|
||||||
let query = sinceAsNegativeBigInt ? IDBKeyRange.lowerBound(sinceAsNegativeBigInt, false) : null
|
|
||||||
|
|
||||||
let res
|
|
||||||
index.getAll(query, limit).onsuccess = (e) => {
|
|
||||||
res = e.target.result
|
|
||||||
}
|
|
||||||
|
|
||||||
tx.oncomplete = () => resolve(res)
|
|
||||||
tx.onerror = () => reject(tx.error.name + ' ' + tx.error.message)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function insertStatuses(instanceName, timeline, statuses) {
|
|
||||||
const db = await getTimelineDatabase(instanceName, timeline)
|
|
||||||
await new Promise((resolve, reject) => {
|
|
||||||
const tx = db.transaction(TIMELINE_STORE, 'readwrite')
|
|
||||||
const store = tx.objectStore(TIMELINE_STORE)
|
|
||||||
for (let status of statuses) {
|
|
||||||
store.put(transformStatusForStorage(status))
|
|
||||||
}
|
|
||||||
tx.oncomplete = () => resolve()
|
|
||||||
tx.onerror = () => reject(tx.error.name + ' ' + tx.error.message)
|
|
||||||
})
|
|
||||||
/* no await */ cleanupOldStatuses()
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function setInstanceVerifyCredentials(instanceName, verifyCredentials) {
|
|
||||||
const db = await getMetaDatabase(instanceName)
|
|
||||||
return await new Promise((resolve, reject) => {
|
|
||||||
const tx = db.transaction(META_STORE, 'readwrite')
|
|
||||||
const store = tx.objectStore(META_STORE)
|
|
||||||
store.put({
|
|
||||||
key: 'verifyCredentials',
|
|
||||||
value: verifyCredentials
|
|
||||||
})
|
|
||||||
tx.oncomplete = () => resolve()
|
|
||||||
tx.onerror = () => reject(tx.error.name + ' ' + tx.error.message)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getInstanceVerifyCredentials(instanceName, verifyCredentials) {
|
|
||||||
const db = await getMetaDatabase(instanceName)
|
|
||||||
return await new Promise((resolve, reject) => {
|
|
||||||
const tx = db.transaction(META_STORE, 'readwrite')
|
|
||||||
const store = tx.objectStore(META_STORE)
|
|
||||||
let res
|
|
||||||
store.get('verifyCredentials').onsuccess = (e) => {
|
|
||||||
res = e.target.result && e.target.result.value
|
|
||||||
}
|
|
||||||
tx.oncomplete = () => resolve(res)
|
|
||||||
tx.onerror = () => reject(tx.error.name + ' ' + tx.error.message)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
import keyval from "idb-keyval"
|
import keyval from "idb-keyval"
|
||||||
|
|
||||||
export async function addKnownDb(instanceName, type, dbName) {
|
export async function addKnownDb(instanceName, type, dbName) {
|
||||||
let knownDbs = (await keyval.get('known_dbs')) || {}
|
let knownDbs = (await getKnownDbs())
|
||||||
if (!knownDbs[instanceName]) {
|
if (!knownDbs[instanceName]) {
|
||||||
knownDbs[instanceName] = []
|
knownDbs[instanceName] = []
|
||||||
}
|
}
|
||||||
|
@ -18,4 +18,10 @@ export async function getKnownDbs() {
|
||||||
export async function getKnownDbsForInstance(instanceName) {
|
export async function getKnownDbsForInstance(instanceName) {
|
||||||
let knownDbs = await getKnownDbs()
|
let knownDbs = await getKnownDbs()
|
||||||
return knownDbs[instanceName] || []
|
return knownDbs[instanceName] || []
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteInstanceFromKnownDbs(instanceName) {
|
||||||
|
let knownDbs = await getKnownDbs()
|
||||||
|
delete knownDbs[instanceName]
|
||||||
|
await keyval.set('known_dbs', knownDbs)
|
||||||
}
|
}
|
|
@ -1,20 +1,19 @@
|
||||||
import { addKnownDb } from './knownDbs'
|
import { addKnownDb } from './knownDbs'
|
||||||
|
import { openReqs, databaseCache } from './cache'
|
||||||
|
|
||||||
const databaseCache = {}
|
|
||||||
export const META_STORE = 'meta'
|
export const META_STORE = 'meta'
|
||||||
|
|
||||||
export function getMetaDatabase(instanceName) {
|
export function getMetaDatabase(instanceName) {
|
||||||
const key = `${instanceName}_${META_STORE}`
|
const dbName = `${instanceName}_${META_STORE}`
|
||||||
if (databaseCache[key]) {
|
if (databaseCache[dbName]) {
|
||||||
return Promise.resolve(databaseCache[key])
|
return Promise.resolve(databaseCache[dbName])
|
||||||
}
|
}
|
||||||
|
|
||||||
let dbName = key
|
|
||||||
|
|
||||||
addKnownDb(instanceName, 'meta', dbName)
|
addKnownDb(instanceName, 'meta', dbName)
|
||||||
|
|
||||||
databaseCache[key] = new Promise((resolve, reject) => {
|
databaseCache[dbName] = new Promise((resolve, reject) => {
|
||||||
let req = indexedDB.open(dbName, 1)
|
let req = indexedDB.open(dbName, 1)
|
||||||
|
openReqs[dbName] = req
|
||||||
req.onerror = reject
|
req.onerror = reject
|
||||||
req.onblocked = () => {
|
req.onblocked = () => {
|
||||||
console.log('idb blocked')
|
console.log('idb blocked')
|
||||||
|
@ -25,5 +24,5 @@ export function getMetaDatabase(instanceName) {
|
||||||
}
|
}
|
||||||
req.onsuccess = () => resolve(req.result)
|
req.onsuccess = () => resolve(req.result)
|
||||||
})
|
})
|
||||||
return databaseCache[key]
|
return databaseCache[dbName]
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import { addKnownDb } from './knownDbs'
|
import { addKnownDb } from './knownDbs'
|
||||||
|
import { openReqs, databaseCache } from './cache'
|
||||||
|
|
||||||
const databaseCache = {}
|
|
||||||
export const TIMELINE_STORE = 'statuses'
|
export const TIMELINE_STORE = 'statuses'
|
||||||
|
|
||||||
export function createTimelineDbName(instanceName, timeline) {
|
export function createTimelineDbName(instanceName, timeline) {
|
||||||
|
@ -8,17 +8,17 @@ export function createTimelineDbName(instanceName, timeline) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTimelineDatabase(instanceName, timeline) {
|
export function getTimelineDatabase(instanceName, timeline) {
|
||||||
const key = `${instanceName}_${timeline}`
|
|
||||||
if (databaseCache[key]) {
|
|
||||||
return Promise.resolve(databaseCache[key])
|
|
||||||
}
|
|
||||||
|
|
||||||
let dbName = createTimelineDbName(instanceName, timeline)
|
let dbName = createTimelineDbName(instanceName, timeline)
|
||||||
|
|
||||||
|
if (databaseCache[dbName]) {
|
||||||
|
return Promise.resolve(databaseCache[dbName])
|
||||||
|
}
|
||||||
|
|
||||||
addKnownDb(instanceName, 'timeline', dbName)
|
addKnownDb(instanceName, 'timeline', dbName)
|
||||||
|
|
||||||
databaseCache[key] = new Promise((resolve, reject) => {
|
databaseCache[dbName] = new Promise((resolve, reject) => {
|
||||||
let req = indexedDB.open(dbName, 1)
|
let req = indexedDB.open(dbName, 1)
|
||||||
|
openReqs[dbName] = req
|
||||||
req.onerror = reject
|
req.onerror = reject
|
||||||
req.onblocked = () => {
|
req.onblocked = () => {
|
||||||
console.log('idb blocked')
|
console.log('idb blocked')
|
||||||
|
@ -32,5 +32,5 @@ export function getTimelineDatabase(instanceName, timeline) {
|
||||||
}
|
}
|
||||||
req.onsuccess = () => resolve(req.result)
|
req.onsuccess = () => resolve(req.result)
|
||||||
})
|
})
|
||||||
return databaseCache[key]
|
return databaseCache[dbName]
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
import cloneDeep from 'lodash/cloneDeep'
|
import cloneDeep from 'lodash/cloneDeep'
|
||||||
import padStart from 'lodash/padStart'
|
import padStart from 'lodash/padStart'
|
||||||
|
import { databaseCache, openReqs } from './cache'
|
||||||
|
|
||||||
export function toPaddedBigInt (id) {
|
export function toPaddedBigInt (id) {
|
||||||
return padStart(id, 30, '0')
|
return padStart(id, 30, '0')
|
||||||
|
@ -19,4 +20,33 @@ export function transformStatusForStorage (status) {
|
||||||
status.pinafore_id_as_negative_big_int = toReversePaddedBigInt(status.id)
|
status.pinafore_id_as_negative_big_int = toReversePaddedBigInt(status.id)
|
||||||
status.pinafore_stale = true
|
status.pinafore_stale = true
|
||||||
return status
|
return status
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function dbPromise(db, storeName, readOnlyOrReadWrite, cb) {
|
||||||
|
return await new Promise((resolve, reject) => {
|
||||||
|
const tx = db.transaction(storeName, readOnlyOrReadWrite)
|
||||||
|
const store = tx.objectStore(storeName)
|
||||||
|
let res
|
||||||
|
cb(store, (result) => {
|
||||||
|
res = result
|
||||||
|
})
|
||||||
|
|
||||||
|
tx.oncomplete = () => resolve(res)
|
||||||
|
tx.onerror = () => reject(tx.error.name + ' ' + tx.error.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteDbPromise(dbName) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// close any open requests
|
||||||
|
let openReq = openReqs[dbName];
|
||||||
|
if (openReq && openReq.result) {
|
||||||
|
openReq.result.close()
|
||||||
|
}
|
||||||
|
delete openReqs[dbName]
|
||||||
|
delete databaseCache[dbName]
|
||||||
|
let req = indexedDB.deleteDatabase(dbName)
|
||||||
|
req.onsuccess = () => resolve()
|
||||||
|
req.onerror = () => reject(req.error.name + ' ' + req.error.message)
|
||||||
|
})
|
||||||
}
|
}
|
|
@ -30,11 +30,11 @@
|
||||||
<form class="instance-actions" aria-label="Switch to or log out of this instance">
|
<form class="instance-actions" aria-label="Switch to or log out of this instance">
|
||||||
{{#if $loggedInInstancesInOrder.length > 1}}
|
{{#if $loggedInInstancesInOrder.length > 1}}
|
||||||
<button class="primary" disabled="{{$currentInstance === params.instanceName}}"
|
<button class="primary" disabled="{{$currentInstance === params.instanceName}}"
|
||||||
on:click="onSwitchToThisInstance()">
|
on:click="onSwitchToThisInstance(event)">
|
||||||
{{$currentInstance === params.instanceName ? 'This is your current instance' : 'Switch to this instance'}}
|
{{$currentInstance === params.instanceName ? 'This is your current instance' : 'Switch to this instance'}}
|
||||||
</button>
|
</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<button on:click="onLogOut()">Log out</button>
|
<button on:click="onLogOut(event)">Log out</button>
|
||||||
</form>
|
</form>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</SettingsLayout>
|
</SettingsLayout>
|
||||||
|
@ -145,7 +145,8 @@
|
||||||
switchToTheme(newTheme)
|
switchToTheme(newTheme)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSwitchToThisInstance() {
|
onSwitchToThisInstance(e) {
|
||||||
|
e.preventDefault()
|
||||||
let instanceName = this.get('params').instanceName
|
let instanceName = this.get('params').instanceName
|
||||||
let instanceThemes = this.store.get('instanceThemes')
|
let instanceThemes = this.store.get('instanceThemes')
|
||||||
this.store.set({
|
this.store.set({
|
||||||
|
@ -154,7 +155,8 @@
|
||||||
this.store.save()
|
this.store.save()
|
||||||
switchToTheme(instanceThemes[instanceName])
|
switchToTheme(instanceThemes[instanceName])
|
||||||
},
|
},
|
||||||
onLogOut() {
|
onLogOut(e) {
|
||||||
|
e.preventDefault()
|
||||||
let loggedInInstances = this.store.get('loggedInInstances')
|
let loggedInInstances = this.store.get('loggedInInstances')
|
||||||
let instanceThemes = this.store.get('instanceThemes')
|
let instanceThemes = this.store.get('instanceThemes')
|
||||||
let loggedInInstancesInOrder = this.store.get('loggedInInstancesInOrder')
|
let loggedInInstancesInOrder = this.store.get('loggedInInstancesInOrder')
|
||||||
|
@ -173,6 +175,7 @@
|
||||||
currentInstance: newInstance
|
currentInstance: newInstance
|
||||||
})
|
})
|
||||||
this.store.save()
|
this.store.save()
|
||||||
|
database.clearDatabasesForInstance(instanceName)
|
||||||
switchToTheme(instanceThemes[newInstance] || 'default')
|
switchToTheme(instanceThemes[newInstance] || 'default')
|
||||||
toast.say(`Logged out of ${instanceName}`)
|
toast.say(`Logged out of ${instanceName}`)
|
||||||
goto('/settings/instances')
|
goto('/settings/instances')
|
||||||
|
|
|
@ -47,7 +47,8 @@ body.offline,body.theme-hotpants.offline,body.theme-majesty.offline,body.theme-o
|
||||||
offline: "#999999"
|
offline: "#999999"
|
||||||
}
|
}
|
||||||
if (localStorage.store_currentInstance && localStorage.store_instanceThemes) {
|
if (localStorage.store_currentInstance && localStorage.store_instanceThemes) {
|
||||||
let theme = JSON.parse(localStorage.store_instanceThemes)[JSON.parse(localStorage.store_currentInstance)]
|
let safeParse = (str) => str === 'undefined' ? undefined : JSON.parse(str)
|
||||||
|
let theme = safeParse(localStorage.store_instanceThemes)[safeParse(localStorage.store_currentInstance)]
|
||||||
if (theme !== 'default') {
|
if (theme !== 'default') {
|
||||||
document.body.classList.add(`theme-${theme}`)
|
document.body.classList.add(`theme-${theme}`)
|
||||||
let link = document.createElement('link')
|
let link = document.createElement('link')
|
||||||
|
|
Loading…
Reference in a new issue