diff --git a/README.md b/README.md index 24396e11d..b839e6ef5 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,23 @@ computer) check the _Building_ section at So to build for linux you could use the `--linux` flag like so: `yarn build --linux`. +## Remote Database Connection + +Frappe Books now supports connecting to a remote database when importing an existing database. Follow the steps below to configure and use the remote database connection feature: + +1. Open Frappe Books and navigate to the "Database Connections" section. +2. Click on "New Database Connection" to add a new remote database connection. +3. Fill in the required details: + - **ID**: A unique identifier for the database connection. + - **Database type**: Select the database type (e.g., SQL Server). + - **Authentication method**: Choose the authentication method. + - **Connection String**: Enter the connection string for the remote database. + - **Connection timeout (ms)**: Specify the connection timeout in milliseconds (optional). + - **Request timeout (ms)**: Specify the request timeout in milliseconds (optional). +4. Click on "Test Connection" to verify the connection details. +5. If the connection is successful, click "Save" to save the remote database connection. +6. You can now use the remote database connection when importing an existing database. + ## Contributions and Community If you want to contribute to Frappe Books, please check our [Contribution Guidelines](https://github.com/frappe/books/blob/master/.github/CONTRIBUTING.md). There are many ways you can contribute even if you don't code: diff --git a/backend/database/core.ts b/backend/database/core.ts index b5f9a9a1f..fd998298e 100644 --- a/backend/database/core.ts +++ b/backend/database/core.ts @@ -97,6 +97,14 @@ export default class DatabaseCore extends DatabaseBase { await this.knex.raw('PRAGMA foreign_keys=ON'); } + async connectToRemoteDatabase(connectionString: string) { + this.knex = knex({ + client: 'pg', + connection: connectionString, + }); + await this.knex.raw('SELECT 1'); + } + async close() { await this.knex!.destroy(); } diff --git a/backend/database/manager.ts b/backend/database/manager.ts index 1c045afbd..d9070f156 100644 --- a/backend/database/manager.ts +++ b/backend/database/manager.ts @@ -35,11 +35,20 @@ export class DatabaseManager extends DatabaseDemuxBase { } async connectToDatabase(dbPath: string, countryCode?: string) { + if (dbPath.startsWith('http://') || dbPath.startsWith('https://')) { + return await this.connectToRemoteDatabase(dbPath, countryCode); + } countryCode = await this._connect(dbPath, countryCode); await this.#migrate(); return countryCode; } + async connectToRemoteDatabase(dbPath: string, countryCode?: string) { + countryCode = await this._connectRemote(dbPath, countryCode); + await this.#migrate(); + return countryCode; + } + async _connect(dbPath: string, countryCode?: string) { countryCode ??= await DatabaseCore.getCountryCode(dbPath); this.db = new DatabaseCore(dbPath); @@ -50,6 +59,16 @@ export class DatabaseManager extends DatabaseDemuxBase { return countryCode; } + async _connectRemote(dbPath: string, countryCode?: string) { + countryCode ??= await DatabaseCore.getCountryCode(dbPath); + this.db = new DatabaseCore(dbPath); + await this.db.connectToRemoteDatabase(dbPath); + await this.setRawCustomFields(); + const schemaMap = getSchemas(countryCode, this.rawCustomFields); + this.db.setSchemaMap(schemaMap); + return countryCode; + } + async setRawCustomFields() { try { this.rawCustomFields = (await this.db?.knex?.( diff --git a/src/pages/DatabaseConnections.vue b/src/pages/DatabaseConnections.vue new file mode 100644 index 000000000..eb321c949 --- /dev/null +++ b/src/pages/DatabaseConnections.vue @@ -0,0 +1,113 @@ + + + + +