mirror of
https://github.com/tiddly-gittly/TidGi-Desktop.git
synced 2025-12-06 02:30:47 -08:00
fix: remove better-sqlite3 that is not working anymore
This commit is contained in:
parent
4e3985040f
commit
c79143fd32
15 changed files with 10 additions and 416 deletions
|
|
@ -82,8 +82,6 @@ Some library doesn't fit electron usage, we move their code to this repo and mod
|
|||
- [app-path](https://github.com/sindresorhus/app-path): Need to be installed, so we can copy its binary to the resource folder. This lib is used by `externalApp` below.
|
||||
- When not installed in package.json, when make release, forge will throw error `An unhandled rejection has occurred inside Forge: Error: ENOENT: no such file or directory, stat '/Users/linonetwo/Desktop/repo/TiddlyGit-Desktop/node_modules/app-path/main'`
|
||||
- [externalApp](https://github.com/desktop/desktop/blob/742b4c44c39d64d01048f1e85364d395432e3413/app/src/lib/editors/lookup.ts): This was used by [Github Desktop](https://github.com/desktop/desktop) to lookup the location of editors like VSCode, we use it in context menu to "open in default text editor"
|
||||
- [sqlite-vss](https://github.com/asg017/sqlite-vss): The path from its method `loadablePathResolver` maybe incorrect after electron app packaged. (It will be in `.webpack/main/index.js` in the dist folder instead of in `node_modules/sqlite-vss` folder.)
|
||||
- Still need to install its `optionalDependencies` like `sqlite-vss-darwin-x64` in package.json
|
||||
|
||||
## Code Tour
|
||||
|
||||
|
|
|
|||
|
|
@ -88,7 +88,6 @@ exports.default = async (
|
|||
}),
|
||||
);
|
||||
const packagePathsToCopyDereferenced = [
|
||||
[`sqlite-vss-${process.platform}-${process.arch}`],
|
||||
['@tiddlygit', 'tiddlywiki', 'package.json'],
|
||||
['@tiddlygit', 'tiddlywiki', 'boot'],
|
||||
['@tiddlygit', 'tiddlywiki', 'core'],
|
||||
|
|
@ -106,7 +105,6 @@ exports.default = async (
|
|||
['@llama-node', 'rwkv-cpp', 'package.json'],
|
||||
['@llama-node', 'llama-cpp', 'index.js'],
|
||||
['@llama-node', 'llama-cpp', 'package.json'],
|
||||
['better-sqlite3', 'build', 'Release', 'better_sqlite3.node'],
|
||||
// we only need its `main` binary, no need its dependency and code, because we already copy it to src/services/native/externalApp
|
||||
['app-path', 'main'],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { app } from 'electron';
|
|||
import path from 'path';
|
||||
import { __TEST__ as v8CompileCacheLibrary } from 'v8-compile-cache-lib';
|
||||
import { isDevelopmentOrTest } from './environment';
|
||||
import { cacheDatabaseFolderName, httpsCertKeyFolderName, languageModelFolderName, settingFolderName } from './fileNames';
|
||||
import { httpsCertKeyFolderName, languageModelFolderName, settingFolderName } from './fileNames';
|
||||
import { sourcePath } from './paths';
|
||||
|
||||
// in dev mode, set userData to a different folder, so gotTheLock will be true, we can run dev instance and normal instance.
|
||||
|
|
@ -12,7 +12,6 @@ if (isDevelopmentOrTest) {
|
|||
export const USER_DATA_FOLDER = app.getPath('userData');
|
||||
export const SETTINGS_FOLDER = path.resolve(USER_DATA_FOLDER, settingFolderName);
|
||||
export const HTTPS_CERT_KEY_FOLDER = path.resolve(USER_DATA_FOLDER, httpsCertKeyFolderName);
|
||||
export const CACHE_DATABASE_FOLDER = path.resolve(USER_DATA_FOLDER, cacheDatabaseFolderName);
|
||||
/** During dev, we don't want to clean up the language model folder */
|
||||
export const LANGUAGE_MODEL_FOLDER = isDevelopmentOrTest
|
||||
? path.resolve(sourcePath, '..', `${languageModelFolderName}-dev`)
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ export const PACKAGE_PATH_BASE = isDevelopmentOrTest
|
|||
: path.resolve(process.resourcesPath, 'node_modules');
|
||||
export const ZX_FOLDER = path.resolve(PACKAGE_PATH_BASE, 'zx', 'build', 'cli.js');
|
||||
export const TIDDLYWIKI_PACKAGE_FOLDER = path.resolve(PACKAGE_PATH_BASE, '@tiddlygit', 'tiddlywiki', 'boot');
|
||||
export const SQLITE_BINARY_PATH = path.resolve(PACKAGE_PATH_BASE, 'better-sqlite3', 'build', 'Release', 'better_sqlite3.node');
|
||||
export const RWKV_CPP_TOKENIZER_PATH = path.resolve(PACKAGE_PATH_BASE, 'llama-node', '20B_tokenizer.json');
|
||||
|
||||
export const LOCALIZATION_FOLDER = isDevelopmentOrTest
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
# Database Service
|
||||
|
||||
We have workspace level db, work as cache for tiddlers, and change logs (to record deletion, for sync deletion with mobile clients).
|
||||
We used to have workspace level db, work as cache for tiddlers, and change logs (to record deletion, for sync deletion with mobile clients).
|
||||
|
||||
And we have app level db, to store things for pages like workflow pages. They are also regarded as temporary cache, and user can toggle a switch to store something inside cache to a wiki.
|
||||
|
||||
## App level DB
|
||||
|
||||
`src/services/database/entity` and `src/services/database/migration` are for app level db.
|
||||
But better-sqlite build failed for latest electron, and sqlite-vss is always not useable, so we drop support for sqlite.
|
||||
|
|
|
|||
|
|
@ -1,111 +1,19 @@
|
|||
/* eslint-disable @typescript-eslint/require-await */
|
||||
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
||||
import Sqlite3Database from 'better-sqlite3';
|
||||
import settings from 'electron-settings';
|
||||
import fs from 'fs-extra';
|
||||
import { injectable } from 'inversify';
|
||||
import { debounce } from 'lodash';
|
||||
import path from 'path';
|
||||
import * as rotateFs from 'rotating-file-stream';
|
||||
import { DataSource } from 'typeorm';
|
||||
|
||||
import { CACHE_DATABASE_FOLDER } from '@/constants/appPaths';
|
||||
import { DEBOUNCE_SAVE_SETTING_BACKUP_FILE, DEBOUNCE_SAVE_SETTING_FILE } from '@/constants/parameters';
|
||||
import { PACKAGE_PATH_BASE, SQLITE_BINARY_PATH } from '@/constants/paths';
|
||||
import { logger } from '@services/libs/log';
|
||||
import { fixSettingFileWhenError } from './configSetting';
|
||||
import { IDatabaseService, ISettingFile } from './interface';
|
||||
import { loadSqliteVss } from './sqlite-vss';
|
||||
|
||||
@injectable()
|
||||
export class DatabaseService implements IDatabaseService {
|
||||
// tiddlywiki require methods to be sync, so direct run them in the main process. But later we can use worker_thread to run heavier search queries, as a readonly slave db, and do some data sync between them.
|
||||
// many operations has to be done in wikiWorker, so can be accessed by nodejs wiki in a sync way.
|
||||
// private readonly dbWorker?: ModuleThread<GitWorker>;
|
||||
|
||||
async initializeForWorkspace(workspaceID: string): Promise<void> {
|
||||
const destinationFilePath = this.getWorkspaceDataBasePath(workspaceID);
|
||||
// only create db file for this workspace's wiki if it doesn't exist
|
||||
if (await fs.exists(this.getWorkspaceDataBasePath(workspaceID))) {
|
||||
logger.debug(`DatabaseService.initializeForWorkspace skip, there already has sqlite database for workspace ${workspaceID} in ${destinationFilePath}`);
|
||||
return;
|
||||
}
|
||||
await fs.ensureDir(CACHE_DATABASE_FOLDER);
|
||||
try {
|
||||
// create a database and table that adapts tiddlywiki usage
|
||||
logger.debug(`DatabaseService.initializeForWorkspace create a sqlite database for workspace`, { SQLITE_BINARY_PATH, workspaceID });
|
||||
let database: Sqlite3Database.Database;
|
||||
try {
|
||||
database = new Sqlite3Database(':memory:', { verbose: logger.debug, nativeBinding: SQLITE_BINARY_PATH });
|
||||
} catch (error) {
|
||||
logger.error(`error when loading sqlite3 for workspace ${workspaceID}, skip because of error to prevent crash: ${(error as Error).message}`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
logger.debug(`DatabaseService.initializeForWorkspace load vss for sqlite database`, { PACKAGE_PATH_BASE, workspaceID });
|
||||
loadSqliteVss(database, PACKAGE_PATH_BASE);
|
||||
const vssVersion = database.prepare('select vss_version()').pluck().get() as string;
|
||||
logger.debug(`DatabaseService.initializeForWorkspace successfully using sqlite-vss version: ${vssVersion} for workspace ${workspaceID}`);
|
||||
} catch (error) {
|
||||
logger.error(`error when loading sqlite-vss for workspace ${workspaceID}: ${(error as Error).message}`);
|
||||
}
|
||||
logger.debug(`DatabaseService.initializeForWorkspace create a table that adapts tiddlywiki usage for workspace`, { workspaceID });
|
||||
/**
|
||||
* Create table storing most commonly used tiddler fields, other fields are stored in `fields` column as a JSON string.
|
||||
*/
|
||||
const createTiddlywikiTable = database.prepare(`
|
||||
CREATE TABLE IF NOT EXISTS tiddlers (
|
||||
title TEXT PRIMARY KEY,
|
||||
text TEXT,
|
||||
type TEXT,
|
||||
created INTEGER,
|
||||
modified INTEGER,
|
||||
tags TEXT,
|
||||
fields TEXT,
|
||||
creator TEXT,
|
||||
modifier TEXT
|
||||
);
|
||||
`);
|
||||
logger.debug(`DatabaseService.initializeForWorkspace table is created, start backup and close`, { workspaceID });
|
||||
createTiddlywikiTable.run();
|
||||
await database.backup(destinationFilePath);
|
||||
database.close();
|
||||
} catch (error) {
|
||||
logger.error(`DatabaseService.initializeForWorkspace error when creating sqlite cache database for workspace: ${(error as Error).message}`, { workspaceID });
|
||||
}
|
||||
}
|
||||
|
||||
async initializeForApp(): Promise<void> {
|
||||
const destinationFilePath = this.getAppDataBasePath();
|
||||
// only create db file for app if it doesn't exist
|
||||
if (await fs.exists(destinationFilePath)) {
|
||||
logger.debug(`DatabaseService.initializeForApp skip, there already has sqlite database for app in ${destinationFilePath}`);
|
||||
return;
|
||||
}
|
||||
await fs.ensureDir(CACHE_DATABASE_FOLDER);
|
||||
|
||||
try {
|
||||
logger.debug(`DatabaseService.initializeForApp create a sqlite database for app`, { SQLITE_BINARY_PATH });
|
||||
|
||||
// Initialize TypeORM Connection using DataSource
|
||||
const appDataSource = new DataSource({
|
||||
type: 'better-sqlite3',
|
||||
nativeBinding: SQLITE_BINARY_PATH,
|
||||
database: destinationFilePath,
|
||||
// entities,
|
||||
synchronize: false,
|
||||
migrationsRun: true,
|
||||
logging: true,
|
||||
// migrations,
|
||||
});
|
||||
|
||||
await appDataSource.initialize();
|
||||
await appDataSource.runMigrations();
|
||||
await appDataSource.destroy();
|
||||
logger.info(`DatabaseService.initializeForApp TypeORM connection initialized and migrations ran for app`);
|
||||
} catch (error) {
|
||||
logger.error(`DatabaseService.initializeForApp error when initializing TypeORM connection and running migrations for app: ${(error as Error).message}`);
|
||||
}
|
||||
|
||||
// init config
|
||||
try {
|
||||
this.settingBackupStream = rotateFs.createStream(`${settings.file()}.bak`, {
|
||||
|
|
@ -118,100 +26,6 @@ export class DatabaseService implements IDatabaseService {
|
|||
}
|
||||
}
|
||||
|
||||
private readonly dataSources = new Map<string, DataSource>();
|
||||
|
||||
async getAppDatabase(isRetry = false): Promise<DataSource> {
|
||||
const name = 'app-tidgi';
|
||||
if (!this.dataSources.has(name)) {
|
||||
try {
|
||||
const dataSource = new DataSource({
|
||||
type: 'sqlite',
|
||||
database: this.getAppDataBasePath(),
|
||||
// entities,
|
||||
synchronize: false,
|
||||
migrationsRun: false,
|
||||
logging: true,
|
||||
// migrations,
|
||||
});
|
||||
/**
|
||||
* Error `TypeError: Cannot read property 'transaction' of undefined` will show if run any query without initialize.
|
||||
*/
|
||||
await dataSource.initialize();
|
||||
|
||||
this.dataSources.set(name, dataSource);
|
||||
return dataSource;
|
||||
} catch (error) {
|
||||
console.error(`Failed to getDatabase ${name}: ${(error as Error).message} ${(error as Error).stack ?? ''}`);
|
||||
if (!isRetry) {
|
||||
try {
|
||||
await this.#fixAppDbLock();
|
||||
return await this.getAppDatabase(true);
|
||||
} catch (error) {
|
||||
console.error(`Failed to retry getDatabase ${name}: ${(error as Error).message} ${(error as Error).stack ?? ''}`);
|
||||
}
|
||||
}
|
||||
try {
|
||||
await this.dataSources.get(name)?.destroy();
|
||||
} catch (error) {
|
||||
console.error(`Failed to destroy in getDatabase ${name}: ${(error as Error).message} ${(error as Error).stack ?? ''}`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
return this.dataSources.get(name)!;
|
||||
}
|
||||
|
||||
async closeAppDatabase(drop?: boolean) {
|
||||
const name = 'app-tidgi';
|
||||
if (this.dataSources.has(name)) {
|
||||
try {
|
||||
const dataSource = this.dataSources.get(name)!;
|
||||
this.dataSources.delete(name);
|
||||
if (drop === true) {
|
||||
await dataSource.dropDatabase();
|
||||
// need to delete the file. May encounter SQLITE_BUSY error if not deleted.
|
||||
await fs.unlink(this.getAppDataBasePath());
|
||||
} else {
|
||||
await dataSource.destroy();
|
||||
console.log(`closeDatabase ${name}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to closeDatabase ${name}: ${(error as Error).message} ${(error as Error).stack ?? ''}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix SQLite busy by move the file.
|
||||
* @url https://stackoverflow.com/a/1226850
|
||||
*
|
||||
* Fixes this:
|
||||
*
|
||||
* ```error
|
||||
* [Error: Error getting skinny tiddlers list from SQLite: Call to function 'ExpoSQLite.exec' has been rejected.
|
||||
* → Caused by: android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5 SQLITE_BUSY): , while compiling: PRAGMA journal_mode] Error: Error getting skinny tiddlers list from SQLite: Call to function 'ExpoSQLite.exec' has been rejected.
|
||||
* → Caused by: android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5 SQLITE_BUSY): , while compiling: PRAGMA journal_mode
|
||||
* ```
|
||||
*/
|
||||
async #fixAppDbLock() {
|
||||
const oldSqlitePath = this.getAppDataBasePath();
|
||||
const temporarySqlitePath = `${oldSqlitePath}.temp`;
|
||||
await fs.copy(oldSqlitePath, temporarySqlitePath);
|
||||
await fs.unlink(oldSqlitePath);
|
||||
await fs.copy(temporarySqlitePath, oldSqlitePath);
|
||||
await fs.unlink(temporarySqlitePath);
|
||||
}
|
||||
|
||||
getWorkspaceDataBasePath(workspaceID: string): string {
|
||||
return path.resolve(CACHE_DATABASE_FOLDER, `${workspaceID}-sqlite3-cache.db`);
|
||||
}
|
||||
|
||||
getAppDataBasePath(): string {
|
||||
return path.resolve(CACHE_DATABASE_FOLDER, `app-tidgi-sqlite3-cache.db`);
|
||||
}
|
||||
|
||||
private settingFileContent: ISettingFile = settings.getSync() as unknown as ISettingFile || {};
|
||||
private settingBackupStream: rotateFs.RotatingFileStream | undefined;
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import { IPage } from '@services/pages/interface';
|
|||
import { IPreferences } from '@services/preferences/interface';
|
||||
import { IWorkspace } from '@services/workspaces/interface';
|
||||
import { ProxyPropertyType } from 'electron-ipc-cat/common';
|
||||
import { DataSource } from 'typeorm';
|
||||
|
||||
export interface ISettingFile {
|
||||
pages: Record<string, IPage>;
|
||||
|
|
@ -14,31 +13,19 @@ export interface ISettingFile {
|
|||
}
|
||||
|
||||
/**
|
||||
* Allow wiki or external app to save/search tiddlers cache from database like sqlite+sqlite-vss (vector storage)
|
||||
* Allow wiki or external app to save/search external non-tiddlywiki store like sqlite (removed) or config file.
|
||||
*/
|
||||
export interface IDatabaseService {
|
||||
closeAppDatabase(drop?: boolean): void;
|
||||
getAppDataBasePath(): string;
|
||||
/**
|
||||
* Get a database connection for the app db, which is a sqlite manages by TypeORM for all app level data
|
||||
*/
|
||||
getAppDatabase(isRetry?: boolean): Promise<DataSource>;
|
||||
/**
|
||||
* Get setting that used by services
|
||||
* @param key setting file top level key like `userInfos`
|
||||
*/
|
||||
getSetting<K extends keyof ISettingFile>(key: K): ISettingFile[K] | undefined;
|
||||
getWorkspaceDataBasePath(workspaceID: string): string;
|
||||
/**
|
||||
* Save settings to FS. Due to bugs of electron-settings, you should mostly use `setSetting` instead.
|
||||
*/
|
||||
immediatelyStoreSettingsToFile(): Promise<void>;
|
||||
initializeForApp(): Promise<void>;
|
||||
/**
|
||||
* Create a database file for a workspace, store it in the appData folder, and load it in a worker_thread to execute SQL. *
|
||||
* (not store `.db` file in the workspace wiki's folder, because this cache file shouldn't not by Database committed)
|
||||
*/
|
||||
initializeForWorkspace(workspaceID: string): Promise<void>;
|
||||
/**
|
||||
* Save setting that used by services to same file, will handle data race.
|
||||
* Normally you should use methods on other services instead of this, and they will can this method instead.
|
||||
|
|
@ -50,9 +37,7 @@ export interface IDatabaseService {
|
|||
export const DatabaseServiceIPCDescriptor = {
|
||||
channel: DatabaseChannel.name,
|
||||
properties: {
|
||||
getAppDataBasePath: ProxyPropertyType.Function,
|
||||
getDataBasePath: ProxyPropertyType.Function,
|
||||
initializeForApp: ProxyPropertyType.Function,
|
||||
initializeForWorkspace: ProxyPropertyType.Function,
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
import type Sqlite3Database from 'better-sqlite3';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import { arch, platform } from 'process';
|
||||
|
||||
const supportedPlatforms: Array<[string, string]> = [
|
||||
['darwin', 'x64'],
|
||||
['darwin', 'arm64'],
|
||||
['linux', 'x64'],
|
||||
];
|
||||
|
||||
function validPlatform(platform: string, arch: string): boolean {
|
||||
return supportedPlatforms.find(([p, a]) => platform === p && arch === a) !== null;
|
||||
}
|
||||
|
||||
function extensionSuffix(platform: string): string {
|
||||
if (platform === 'win32') return 'dll';
|
||||
if (platform === 'darwin') return 'dylib';
|
||||
return 'so';
|
||||
}
|
||||
|
||||
function platformPackageName(platform: string, arch: string): string {
|
||||
const os = platform === 'win32' ? 'windows' : platform;
|
||||
return `sqlite-vss-${os}-${arch}`;
|
||||
}
|
||||
|
||||
function loadablePathResolver(name: string, PACKAGE_PATH_BASE: string): string {
|
||||
if (!validPlatform(platform, arch)) {
|
||||
throw new Error(
|
||||
`Unsupported platform for sqlite-vss, on a ${platform}-${arch} machine, but not in supported platforms (${
|
||||
supportedPlatforms
|
||||
.map(([p, a]) => `${p}-${a}`)
|
||||
.join(',')
|
||||
}). Consult the sqlite-vss NPM package README for details. `,
|
||||
);
|
||||
}
|
||||
const packageName = platformPackageName(platform, arch);
|
||||
const loadablePath = path.join(
|
||||
PACKAGE_PATH_BASE,
|
||||
packageName,
|
||||
'lib',
|
||||
`${name}.${extensionSuffix(platform)}`,
|
||||
);
|
||||
if (fs.statSync(loadablePath, { throwIfNoEntry: false }) === undefined) {
|
||||
throw new Error(
|
||||
`Loadble extension for sqlite-vss not found in ${loadablePath}. Was the ${packageName} package installed? Avoid using the --no-optional flag, as the optional dependencies for sqlite-vss are required.`,
|
||||
);
|
||||
}
|
||||
|
||||
return loadablePath;
|
||||
}
|
||||
|
||||
export function getVectorLoadablePath(PACKAGE_PATH_BASE: string): string {
|
||||
return loadablePathResolver('vector0', PACKAGE_PATH_BASE);
|
||||
}
|
||||
|
||||
export function getVssLoadablePath(PACKAGE_PATH_BASE: string): string {
|
||||
return loadablePathResolver('vss0', PACKAGE_PATH_BASE);
|
||||
}
|
||||
|
||||
export function loadVector(database: Sqlite3Database.Database, PACKAGE_PATH_BASE: string): void {
|
||||
database.loadExtension(getVectorLoadablePath(PACKAGE_PATH_BASE));
|
||||
}
|
||||
|
||||
export function loadVss(database: Sqlite3Database.Database, PACKAGE_PATH_BASE: string): void {
|
||||
database.loadExtension(getVssLoadablePath(PACKAGE_PATH_BASE));
|
||||
}
|
||||
|
||||
export function loadSqliteVss(database: Sqlite3Database.Database, PACKAGE_PATH_BASE: string): void {
|
||||
loadVector(database, PACKAGE_PATH_BASE);
|
||||
loadVss(database, PACKAGE_PATH_BASE);
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
import type { ITiddlerFields } from '@tiddlygit/tiddlywiki';
|
||||
import Sqlite3Database from 'better-sqlite3';
|
||||
import fs from 'fs-extra';
|
||||
import { loadSqliteVss } from './sqlite-vss';
|
||||
|
||||
export interface ISqliteDatabasePaths {
|
||||
databaseFile: string;
|
||||
packagePathBase: string;
|
||||
sqliteBinary: string;
|
||||
}
|
||||
export class WikiWorkerDatabaseOperations {
|
||||
public database: Sqlite3Database.Database;
|
||||
constructor(paths: ISqliteDatabasePaths) {
|
||||
if (!fs.existsSync(paths.databaseFile)) {
|
||||
throw new SqliteDatabaseNotInitializedError(paths.databaseFile);
|
||||
}
|
||||
const database = new Sqlite3Database(paths.databaseFile, { verbose: console.log, fileMustExist: true, nativeBinding: paths.sqliteBinary });
|
||||
this.database = database;
|
||||
this.prepareMethods();
|
||||
this.loadVSS(database, paths);
|
||||
}
|
||||
|
||||
loadVSS(database: Sqlite3Database.Database, paths: ISqliteDatabasePaths) {
|
||||
try {
|
||||
loadSqliteVss(database, paths.packagePathBase);
|
||||
} catch {
|
||||
// ignore, error already logged in src/services/database/index.ts 's `initializeForWorkspace`
|
||||
}
|
||||
}
|
||||
|
||||
insertTiddlers!: Sqlite3Database.Transaction<(tiddlers: ITiddlerFields[]) => void>;
|
||||
putTiddlers(tiddlers: ITiddlerFields[]) {
|
||||
this.insertTiddlers(tiddlers);
|
||||
}
|
||||
|
||||
private prepareMethods() {
|
||||
const insertTiddler = this.database.prepare(`
|
||||
INSERT INTO tiddlers (title, text, type, created, modified, tags, fields, creator, modifier)
|
||||
VALUES (@title, @text, @type, @created, @modified, @tags, @fields, @creator, @modifier)
|
||||
ON CONFLICT(title) DO UPDATE SET
|
||||
text = excluded.text,
|
||||
type = excluded.type,
|
||||
created = excluded.created,
|
||||
modified = excluded.modified,
|
||||
tags = excluded.tags,
|
||||
fields = excluded.fields,
|
||||
creator = excluded.creator,
|
||||
modifier = excluded.modifier
|
||||
`);
|
||||
this.insertTiddlers = this.database.transaction((tiddlers: ITiddlerFields[]) => {
|
||||
for (const tiddler of tiddlers) {
|
||||
insertTiddler.run(tiddler);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class SqliteDatabaseNotInitializedError extends Error {
|
||||
constructor(databaseFile: string) {
|
||||
super();
|
||||
this.message = `database file not found (This is OK for first init of workspace, until initializeWorkspaceView call initializeForWorkspace): ${databaseFile}`;
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,6 @@ const logger = (
|
|||
close: () => {},
|
||||
})
|
||||
: winston.createLogger({
|
||||
levels,
|
||||
transports: [
|
||||
new winston.transports.Console(),
|
||||
new winston.transports.DailyRotateFile({
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { ModuleThread, spawn, Thread, Worker } from 'threads';
|
|||
import type { WorkerEvent } from 'threads/dist/types/master';
|
||||
|
||||
import { WikiChannel } from '@/constants/channels';
|
||||
import { PACKAGE_PATH_BASE, SQLITE_BINARY_PATH, TIDDLERS_PATH, TIDDLYWIKI_PACKAGE_FOLDER, TIDDLYWIKI_TEMPLATE_FOLDER_PATH } from '@/constants/paths';
|
||||
import { TIDDLERS_PATH, TIDDLYWIKI_PACKAGE_FOLDER, TIDDLYWIKI_TEMPLATE_FOLDER_PATH } from '@/constants/paths';
|
||||
import type { IAuthenticationService } from '@services/auth/interface';
|
||||
import { lazyInject } from '@services/container';
|
||||
import type { IGitService, IGitUserInfos } from '@services/git/interface';
|
||||
|
|
@ -179,17 +179,6 @@ export class Wiki implements IWikiService {
|
|||
}
|
||||
});
|
||||
|
||||
logger.debug('startWiki calling initCacheDatabase in the main process', { function: 'wikiWorker.initCacheDatabase' });
|
||||
worker.initCacheDatabase({
|
||||
databaseFile: this.databaseService.getWorkspaceDataBasePath(workspaceID),
|
||||
sqliteBinary: SQLITE_BINARY_PATH,
|
||||
packagePathBase: PACKAGE_PATH_BASE,
|
||||
}).subscribe(async (message) => {
|
||||
if (message.type === 'stderr' || message.type === 'stdout') {
|
||||
logger.info(message.message, { function: 'wikiWorker.initCacheDatabase' });
|
||||
}
|
||||
});
|
||||
|
||||
// subscribe to the Observable that startNodeJSWiki returns, handle messages send by our code
|
||||
logger.debug('startWiki calling startNodeJSWiki in the main process', { function: 'wikiWorker.startNodeJSWiki' });
|
||||
worker.startNodeJSWiki(workerData).subscribe(async (message) => {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,8 @@
|
|||
import { WikiWorkerDatabaseOperations } from '@services/database/wikiWorkerOperations';
|
||||
import type { ITiddlyWiki } from 'tiddlywiki';
|
||||
|
||||
let wikiInstance: ITiddlyWiki | undefined;
|
||||
let cacheDatabase: WikiWorkerDatabaseOperations | undefined;
|
||||
|
||||
export const getWikiInstance = () => wikiInstance;
|
||||
export const getCacheDatabase = () => cacheDatabase;
|
||||
export const setWikiInstance = (instance: ITiddlyWiki) => {
|
||||
wikiInstance = instance;
|
||||
};
|
||||
export const setCacheDatabase = (database: WikiWorkerDatabaseOperations) => {
|
||||
cacheDatabase = database;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,19 +9,16 @@
|
|||
import { uninstall } from '@/helpers/installV8Cache';
|
||||
import './preload';
|
||||
import 'source-map-support/register';
|
||||
import { type IUtils } from '@tiddlygit/tiddlywiki';
|
||||
import Sqlite3Database from 'better-sqlite3';
|
||||
import { mkdtemp } from 'fs-extra';
|
||||
import { tmpdir } from 'os';
|
||||
import path from 'path';
|
||||
import { Observable } from 'rxjs';
|
||||
import { expose } from 'threads/worker';
|
||||
|
||||
import { ISqliteDatabasePaths, SqliteDatabaseNotInitializedError, WikiWorkerDatabaseOperations } from '@services/database/wikiWorkerOperations';
|
||||
import { IWikiLogMessage, IZxWorkerMessage, ZxWorkerControlActions } from '../interface';
|
||||
import { IZxWorkerMessage, ZxWorkerControlActions } from '../interface';
|
||||
import { executeScriptInTWContext, executeScriptInZxScriptContext, extractTWContextScripts, type IVariableContextList } from '../plugin/zxPlugin';
|
||||
import { wikiOperationsInWikiWorker } from '../wikiOperations/executor/wikiOperationInServer';
|
||||
import { getWikiInstance, setCacheDatabase } from './globals';
|
||||
import { getWikiInstance } from './globals';
|
||||
import { extractWikiHTML, packetHTMLFromWikiFolder } from './htmlWiki';
|
||||
import { ipcServerRoutesMethods } from './ipcServerRoutes';
|
||||
import { startNodeJSWiki } from './startNodeJSWiki';
|
||||
|
|
@ -47,30 +44,6 @@ export interface IStartNodeJSWikiConfigs {
|
|||
userName: string;
|
||||
}
|
||||
|
||||
export interface IUtilsWithSqlite extends IUtils {
|
||||
Sqlite: Sqlite3Database.Database;
|
||||
TidgiCacheDB: WikiWorkerDatabaseOperations;
|
||||
}
|
||||
|
||||
function initCacheDatabase(cacheDatabaseConfig: ISqliteDatabasePaths) {
|
||||
return new Observable<IWikiLogMessage>((observer) => {
|
||||
try {
|
||||
observer.next({ type: 'stdout', message: 'Will new WikiWorkerDatabaseOperations' });
|
||||
const cacheDatabase = new WikiWorkerDatabaseOperations(cacheDatabaseConfig);
|
||||
observer.next({ type: 'stdout', message: 'WikiWorkerDatabaseOperations instance created.' });
|
||||
setCacheDatabase(cacheDatabase);
|
||||
} catch (error) {
|
||||
if (error instanceof SqliteDatabaseNotInitializedError) {
|
||||
// this is usual for first time
|
||||
observer.next({ type: 'stdout', message: error.message });
|
||||
} else {
|
||||
// unexpected error
|
||||
observer.next({ type: 'stderr', message: (error as Error)?.message });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export type IZxFileInput = { fileContent: string; fileName: string } | { filePath: string };
|
||||
function executeZxScript(file: IZxFileInput, zxPath: string): Observable<IZxWorkerMessage> {
|
||||
/** this will be observed in src/services/native/index.ts */
|
||||
|
|
@ -131,7 +104,6 @@ const wikiWorker = {
|
|||
extractWikiHTML,
|
||||
packetHTMLFromWikiFolder,
|
||||
beforeExit,
|
||||
initCacheDatabase,
|
||||
wikiOperation: wikiOperationsInWikiWorker.wikiOperation.bind(wikiOperationsInWikiWorker),
|
||||
...ipcServerRoutesMethods,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import path from 'path';
|
|||
import { Observable } from 'rxjs';
|
||||
import { IWikiMessage, WikiControlActions } from '../interface';
|
||||
import { wikiOperationsInWikiWorker } from '../wikiOperations/executor/wikiOperationInServer';
|
||||
import { IStartNodeJSWikiConfigs, IUtilsWithSqlite } from '.';
|
||||
import { getCacheDatabase, setWikiInstance } from './globals';
|
||||
import { IStartNodeJSWikiConfigs } from '.';
|
||||
import { setWikiInstance } from './globals';
|
||||
import { ipcServerRoutes } from './ipcServerRoutes';
|
||||
import { authTokenIsProvided } from './wikiWorkerUtils';
|
||||
|
||||
|
|
@ -50,12 +50,6 @@ export function startNodeJSWiki({
|
|||
try {
|
||||
const wikiInstance = TiddlyWiki();
|
||||
setWikiInstance(wikiInstance);
|
||||
const cacheDatabase = getCacheDatabase();
|
||||
// mount database to $tw
|
||||
if (wikiInstance !== undefined && cacheDatabase !== undefined) {
|
||||
(wikiInstance.utils as IUtilsWithSqlite).TidgiCacheDB = cacheDatabase;
|
||||
(wikiInstance.utils as IUtilsWithSqlite).Sqlite = cacheDatabase.database;
|
||||
}
|
||||
process.env.TIDDLYWIKI_PLUGIN_PATH = path.resolve(homePath, 'plugins');
|
||||
process.env.TIDDLYWIKI_THEME_PATH = path.resolve(homePath, 'themes');
|
||||
// don't add `+` prefix to plugin name here. `+` only used in args[0], but we are not prepend this list to the args list.
|
||||
|
|
|
|||
|
|
@ -183,19 +183,11 @@ export class WorkspaceView implements IWorkspaceViewService {
|
|||
}
|
||||
}
|
||||
};
|
||||
const initDatabaseWhenInitializeWorkspaceView = async (): Promise<void> => {
|
||||
if (workspace.isSubWiki) {
|
||||
return;
|
||||
}
|
||||
// after all init finished, create cache database if there is no one
|
||||
await this.databaseService.initializeForWorkspace(workspace.id);
|
||||
};
|
||||
|
||||
logger.debug(`initializeWorkspaceView() calling wikiStartup()`);
|
||||
await Promise.all([
|
||||
this.wikiService.wikiStartup(workspace),
|
||||
addViewWhenInitializeWorkspaceView(),
|
||||
initDatabaseWhenInitializeWorkspaceView(),
|
||||
]);
|
||||
void syncGitWhenInitializeWorkspaceView();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue