diff --git a/src/core/services/app.ts b/src/core/services/app.ts index 95031a923..523418254 100644 --- a/src/core/services/app.ts +++ b/src/core/services/app.ts @@ -144,6 +144,15 @@ export class CoreAppProvider { await this.schemaVersionsTable.insert({ name: schema.name, version: schema.version }); } + /** + * Delete table schema. + * + * @param name Schema name. + */ + async deleteTableSchema(name: string): Promise { + await this.schemaVersionsTable.deleteByPrimaryKey({ name }); + } + /** * Get the application global database. * diff --git a/src/core/services/config.ts b/src/core/services/config.ts index b203690a3..f8d65ee23 100644 --- a/src/core/services/config.ts +++ b/src/core/services/config.ts @@ -120,6 +120,22 @@ export class CoreConfigProvider { } } + /** + * Check whether the given app setting exists. + * + * @param name The config name. + * @returns Whether the app setting exists. + */ + async has(name: string): Promise { + try { + await this.table.getOneByPrimaryKey({ name }); + + return true; + } catch (error) { + return false; + } + } + /** * Set an app setting. * diff --git a/src/core/services/database/sites.ts b/src/core/services/database/sites.ts index 857bdba4b..930955e46 100644 --- a/src/core/services/database/sites.ts +++ b/src/core/services/database/sites.ts @@ -21,7 +21,6 @@ import { CoreSite } from '@classes/site'; * Database variables for CoreSites service. */ export const SITES_TABLE_NAME = 'sites_2'; -export const CURRENT_SITE_TABLE_NAME = 'current_site'; export const SCHEMA_VERSIONS_TABLE_NAME = 'schema_versions'; // Schema to register in App DB. @@ -68,22 +67,6 @@ export const APP_SCHEMA: CoreAppSchema = { }, ], }, - { - name: CURRENT_SITE_TABLE_NAME, - columns: [ - { - name: 'id', - type: 'INTEGER', - primaryKey: true, - }, - { - name: 'siteId', - type: 'TEXT', - notNull: true, - unique: true, - }, - ], - }, ], async migrate(db: SQLiteDB, oldVersion: number): Promise { if (oldVersion < 2) { @@ -184,11 +167,6 @@ export type SiteDBEntry = { oauthId?: number | null; }; -export type CurrentSiteDBEntry = { - id: number; - siteId: string; -}; - export type SchemaVersionsDBEntry = { name: string; version: number; diff --git a/src/core/services/sites.ts b/src/core/services/sites.ts index ee324ae59..1dedfd9cf 100644 --- a/src/core/services/sites.ts +++ b/src/core/services/sites.ts @@ -41,10 +41,8 @@ import { APP_SCHEMA, SCHEMA_VERSIONS_TABLE_SCHEMA, SITES_TABLE_NAME, - CURRENT_SITE_TABLE_NAME, SCHEMA_VERSIONS_TABLE_NAME, SiteDBEntry, - CurrentSiteDBEntry, SchemaVersionsDBEntry, } from '@services/database/sites'; import { CoreArray } from '../singletons/array'; @@ -59,7 +57,13 @@ import { CoreAjaxWSError } from '@classes/errors/ajaxwserror'; import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins'; import { CorePromisedValue } from '@classes/promised-value'; import { CoreDatabaseTable } from '@classes/database/database-table'; -import { CoreDatabaseConfiguration, CoreDatabaseTableProxy } from '@classes/database/database-table-proxy'; +import { + CoreDatabaseCachingStrategy, + CoreDatabaseConfiguration, + CoreDatabaseTableProxy, +} from '@classes/database/database-table-proxy'; +import { asyncInstance, AsyncInstance } from '../utils/async-instance'; +import { CoreConfig } from './config'; export const CORE_SITE_SCHEMAS = new InjectionToken('CORE_SITE_SCHEMAS'); @@ -84,14 +88,11 @@ export class CoreSitesProvider { protected siteSchemasMigration: { [siteId: string]: Promise } = {}; protected siteSchemas: { [name: string]: CoreRegisteredSiteSchema } = {}; protected pluginsSiteSchemas: { [name: string]: CoreRegisteredSiteSchema } = {}; - - // Variables for DB. - protected appDB: Promise; - protected resolveAppDB!: (appDB: SQLiteDB) => void; protected siteTables: Record>> = {}; + protected schemasTables: Record>> = {}; + protected sitesTable = asyncInstance>(); constructor(@Optional() @Inject(CORE_SITE_SCHEMAS) siteSchemas: CoreSiteSchema[][] = []) { - this.appDB = new Promise(resolve => this.resolveAppDB = resolve); this.logger = CoreLogger.getInstance('CoreSitesProvider'); this.siteSchemas = CoreArray.flatten(siteSchemas).reduce( (siteSchemas, schema) => { @@ -132,7 +133,15 @@ export class CoreSitesProvider { // Ignore errors. } - this.resolveAppDB(CoreApp.getDB()); + const sitesTable = new CoreDatabaseTableProxy( + { cachingStrategy: CoreDatabaseCachingStrategy.Eager }, + CoreApp.getDB(), + SITES_TABLE_NAME, + ); + + await sitesTable.initialize(); + + this.sitesTable.setInstance(sitesTable); } /** @@ -743,8 +752,7 @@ export class CoreSitesProvider { config?: CoreSiteConfig, oauthId?: number, ): Promise { - const db = await this.appDB; - const entry: SiteDBEntry = { + await this.sitesTable.insert({ id, siteUrl, token, @@ -753,9 +761,7 @@ export class CoreSitesProvider { config: config ? JSON.stringify(config) : undefined, loggedOut: 0, oauthId, - }; - - await db.insertRecord(SITES_TABLE_NAME, entry); + }); } /** @@ -982,9 +988,7 @@ export class CoreSitesProvider { delete this.sites[siteId]; try { - const db = await this.appDB; - - await db.deleteRecords(SITES_TABLE_NAME, { id: siteId }); + await this.sitesTable.deleteByPrimaryKey({ id: siteId }); } catch (err) { // DB remove shouldn't fail, but we'll go ahead even if it does. } @@ -1001,10 +1005,9 @@ export class CoreSitesProvider { * @return Promise resolved with true if there are sites and false if there aren't. */ async hasSites(): Promise { - const db = await this.appDB; - const count = await db.countRecords(SITES_TABLE_NAME); + const isEmpty = await this.sitesTable.isEmpty(); - return count > 0; + return !isEmpty; } /** @@ -1026,9 +1029,8 @@ export class CoreSitesProvider { return this.sites[siteId]; } else { // Retrieve and create the site. - const db = await this.appDB; try { - const data = await db.getRecord(SITES_TABLE_NAME, { id: siteId }); + const data = await this.sitesTable.getOneByPrimaryKey({ id: siteId }); return this.makeSiteFromSiteListEntry(data); } catch { @@ -1044,8 +1046,7 @@ export class CoreSitesProvider { * @return Promise resolved with the site. */ async getSiteByUrl(siteUrl: string): Promise { - const db = await this.appDB; - const data = await db.getRecord(SITES_TABLE_NAME, { siteUrl }); + const data = await this.sitesTable.getOne({ siteUrl }); if (this.sites[data.id] !== undefined) { return this.sites[data.id]; @@ -1131,8 +1132,7 @@ export class CoreSitesProvider { * @return Promise resolved when the sites are retrieved. */ async getSites(ids?: string[]): Promise { - const db = await this.appDB; - const sites = await db.getAllRecords(SITES_TABLE_NAME); + const sites = await this.sitesTable.getMany(); const formattedSites: CoreSiteBasicInfo[] = []; sites.forEach((site) => { @@ -1197,8 +1197,7 @@ export class CoreSitesProvider { * @return Promise resolved when the sites IDs are retrieved. */ async getLoggedInSitesIds(): Promise { - const db = await this.appDB; - const sites = await db.getRecords(SITES_TABLE_NAME, { loggedOut : 0 }); + const sites = await this.sitesTable.getMany({ loggedOut : 0 }); return sites.map((site) => site.id); } @@ -1209,8 +1208,7 @@ export class CoreSitesProvider { * @return Promise resolved when the sites IDs are retrieved. */ async getSitesIds(): Promise { - const db = await this.appDB; - const sites = await db.getAllRecords(SITES_TABLE_NAME); + const sites = await this.sitesTable.getMany(); return sites.map((site) => site.id); } @@ -1233,13 +1231,7 @@ export class CoreSitesProvider { * @return Promise resolved when current site is stored. */ async login(siteId: string): Promise { - const db = await this.appDB; - const entry = { - id: 1, - siteId, - }; - - await db.insertRecord(CURRENT_SITE_TABLE_NAME, entry); + await CoreConfig.set('current_site_id', siteId); CoreEvents.trigger(CoreEvents.LOGIN, {}, siteId); } @@ -1308,13 +1300,10 @@ export class CoreSitesProvider { return Promise.reject(new CoreError('Session already restored.')); } - const db = await this.appDB; - this.sessionRestored = true; try { - const currentSite = await db.getRecord(CURRENT_SITE_TABLE_NAME, { id: 1 }); - const siteId = currentSite.siteId; + const siteId = await this.getStoredCurrentSiteId(); this.logger.debug(`Restore session in site ${siteId}`); await this.loadSite(siteId); @@ -1330,12 +1319,11 @@ export class CoreSitesProvider { * @return Promise resolved when done. */ protected async setSiteLoggedOut(siteId: string): Promise { - const db = await this.appDB; const site = await this.getSite(siteId); site.setLoggedOut(true); - await db.updateRecords(SITES_TABLE_NAME, { loggedOut: 1 }, { id: siteId }); + await this.sitesTable.update({ loggedOut: 1 }, { id: siteId }); } /** @@ -1371,19 +1359,20 @@ export class CoreSitesProvider { * @return A promise resolved when the site is updated. */ async updateSiteTokenBySiteId(siteId: string, token: string, privateToken: string = ''): Promise { - const db = await this.appDB; const site = await this.getSite(siteId); - const newValues: Partial = { - token, - privateToken, - loggedOut: 0, - }; site.token = token; site.privateToken = privateToken; site.setLoggedOut(false); // Token updated means the user authenticated again, not logged out anymore. - await db.updateRecords(SITES_TABLE_NAME, newValues, { id: siteId }); + await this.sitesTable.update( + { + token, + privateToken, + loggedOut: 0, + }, + { id: siteId }, + ); } /** @@ -1425,9 +1414,7 @@ export class CoreSitesProvider { } try { - const db = await this.appDB; - - await db.updateRecords(SITES_TABLE_NAME, newValues, { id: siteId }); + await this.sitesTable.update(newValues, { id: siteId }); } finally { CoreEvents.trigger(CoreEvents.SITE_UPDATED, info, siteId); } @@ -1484,8 +1471,7 @@ export class CoreSitesProvider { } try { - const db = await this.appDB; - const siteEntries = await db.getAllRecords(SITES_TABLE_NAME); + const siteEntries = await this.sitesTable.getMany(); const ids: string[] = []; const promises: Promise[] = []; @@ -1516,10 +1502,9 @@ export class CoreSitesProvider { * @return Promise resolved with the site ID. */ async getStoredCurrentSiteId(): Promise { - const db = await this.appDB; - const currentSite = await db.getRecord(CURRENT_SITE_TABLE_NAME, { id: 1 }); + await this.migrateCurrentSiteLegacyTable(); - return currentSite.siteId; + return CoreConfig.get('current_site_id'); } /** @@ -1528,9 +1513,7 @@ export class CoreSitesProvider { * @return Promise resolved when done. */ async removeStoredCurrentSite(): Promise { - const db = await this.appDB; - - await db.deleteRecords(CURRENT_SITE_TABLE_NAME, { id: 1 }); + await CoreConfig.delete('current_site_id'); } /** @@ -1645,10 +1628,8 @@ export class CoreSitesProvider { * @return Promise resolved when done. */ protected async applySiteSchemas(site: CoreSite, schemas: {[name: string]: CoreRegisteredSiteSchema}): Promise { - const db = site.getDb(); - // Fetch installed versions of the schema. - const records = await db.getAllRecords(SCHEMA_VERSIONS_TABLE_NAME); + const records = await this.getSiteSchemasTable(site).getMany(); const versions: {[name: string]: number} = {}; records.forEach((record) => { @@ -1695,7 +1676,7 @@ export class CoreSitesProvider { } // Set installed version. - await db.insertRecord(SCHEMA_VERSIONS_TABLE_NAME, { name: schema.name, version: schema.version }); + await this.getSiteSchemasTable(site).insert({ name: schema.name, version: schema.version }); } /** @@ -1794,6 +1775,47 @@ export class CoreSitesProvider { return []; } + /** + * Migrate the legacy current_site table. + */ + protected async migrateCurrentSiteLegacyTable(): Promise { + if (await CoreConfig.has('current_site_migrated')) { + // Already migrated. + return; + } + + try { + const db = CoreApp.getDB(); + + const { siteId } = await db.getRecord<{ siteId: string }>('current_site'); + + await CoreConfig.set('current_site_id', siteId); + await CoreApp.deleteTableSchema('current_site'); + await db.dropTable('current_site'); + } finally { + await CoreConfig.set('current_site_migrated', 1); + } + } + + /** + * Get schemas table for the given site. + * + * @param site Site. + * @returns Scehmas Table. + */ + protected getSiteSchemasTable(site: CoreSite): AsyncInstance> { + this.schemasTables[site.getId()] = this.schemasTables[site.getId()] ?? asyncInstance( + () => this.getSiteTable(SCHEMA_VERSIONS_TABLE_NAME, { + siteId: site.getId(), + database: site.getDb(), + config: { cachingStrategy: CoreDatabaseCachingStrategy.Eager }, + primaryKeyColumns: ['name'], + }), + ); + + return this.schemasTables[site.getId()]; + } + } export const CoreSites = makeSingleton(CoreSitesProvider);