Skip to content

Commit

Permalink
Add remote database integration
Browse files Browse the repository at this point in the history
Fixes frappe#907

Add support for remote database connections when importing an existing database.

* **backend/database/manager.ts**
  - Add `connectToRemoteDatabase` method to support remote database connections.
  - Update `connectToDatabase` method to handle both local and remote database connections based on the provided configuration.
  - Add `_connectRemote` method to handle remote database connections.
* **backend/database/core.ts**
  - Add `connectToRemoteDatabase` method to support remote database connections.
* **README.md**
  - Add instructions for configuring and using the remote database connection feature.
* **src/pages/DatabaseConnections.vue**
  - Add a new user interface to configure remote database details.
  - Include fields for database type, authentication method, connection string, connection timeout, and request timeout.
  - Add buttons for testing the connection, saving, and canceling.
  • Loading branch information
nirzaf committed Aug 13, 2024
1 parent 13414c5 commit c54c834
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 0 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
8 changes: 8 additions & 0 deletions backend/database/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
19 changes: 19 additions & 0 deletions backend/database/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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?.(
Expand Down
113 changes: 113 additions & 0 deletions src/pages/DatabaseConnections.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<template>
<div class="database-connections">
<h1>{{ t`Database Connections` }}</h1>
<div class="new-connection">
<h2>{{ t`New Database Connection` }}</h2>
<form @submit.prevent="saveConnection">
<div class="form-group">
<label for="id">{{ t`ID` }}</label>
<input type="text" id="id" v-model="connection.id" required />
</div>
<div class="form-group">
<label for="databaseType">{{ t`Database type` }}</label>
<select id="databaseType" v-model="connection.databaseType" required>
<option value="SQL Server">{{ t`SQL Server` }}</option>
<!-- Add more database types as needed -->
</select>
</div>
<div class="form-group">
<label for="authenticationMethod">{{ t`Authentication method` }}</label>
<select id="authenticationMethod" v-model="connection.authenticationMethod" required>
<option value="">{{ t`Select authentication method` }}</option>
<!-- Add more authentication methods as needed -->
</select>
</div>
<div class="form-group">
<label for="connectionString">{{ t`Connection String` }}</label>
<input type="text" id="connectionString" v-model="connection.connectionString" required />
</div>
<div class="form-group">
<label for="connectionTimeout">{{ t`Connection timeout (ms)` }}</label>
<input type="number" id="connectionTimeout" v-model="connection.connectionTimeout" />
</div>
<div class="form-group">
<label for="requestTimeout">{{ t`Request timeout (ms)` }}</label>
<input type="number" id="requestTimeout" v-model="connection.requestTimeout" />
</div>
<div class="form-actions">
<button type="button" @click="testConnection">{{ t`Test Connection` }}</button>
<button type="button" @click="cancel">{{ t`Cancel` }}</button>
<button type="submit">{{ t`Save` }}</button>
</div>
</form>
</div>
</div>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import { t } from 'fyo';
export default defineComponent({
name: 'DatabaseConnections',
data() {
return {
connection: {
id: '',
databaseType: '',
authenticationMethod: '',
connectionString: '',
connectionTimeout: null,
requestTimeout: null,
},
};
},
methods: {
async testConnection() {
// Implement the logic to test the database connection
},
cancel() {
// Implement the logic to cancel the connection creation
},
async saveConnection() {
// Implement the logic to save the database connection
},
},
});
</script>

<style scoped>
.database-connections {
padding: 20px;
}
.new-connection {
margin-top: 20px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
}
.form-group input,
.form-group select {
width: 100%;
padding: 8px;
box-sizing: border-box;
}
.form-actions {
display: flex;
justify-content: space-between;
margin-top: 20px;
}
.form-actions button {
padding: 10px 20px;
}
</style>

0 comments on commit c54c834

Please sign in to comment.