refactor(backend): de-couple driver registration from DriverService

This commit is contained in:
KernelDeimos 2024-06-01 18:10:12 -04:00
parent 2d8e6240c6
commit 665b2d4e4e
12 changed files with 71 additions and 121 deletions

View File

@ -225,6 +225,9 @@ const install = async ({ services, app }) => {
const { DevTODService } = require('./services/DevTODService'); const { DevTODService } = require('./services/DevTODService');
services.registerService('__dev-tod', DevTODService); services.registerService('__dev-tod', DevTODService);
const { DriverService } = require("./services/drivers/DriverService");
services.registerService('driver', DriverService);
} }
const install_legacy = async ({ services }) => { const install_legacy = async ({ services }) => {

View File

@ -5,6 +5,9 @@ class SelfHostedModule extends AdvancedBase {
async install (context) { async install (context) {
const services = context.get('services'); const services = context.get('services');
const { SelfhostedService } = require('./services/SelfhostedService');
services.registerService('__selfhosted', SelfhostedService);
const DefaultUserService = require('./services/DefaultUserService'); const DefaultUserService = require('./services/DefaultUserService');
services.registerService('__default-user', DefaultUserService); services.registerService('__default-user', DefaultUserService);

View File

@ -17,16 +17,16 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
const { AdvancedBase } = require("@heyputer/puter-js-common"); const { AdvancedBase } = require("@heyputer/puter-js-common");
const { Context } = require("../../../util/context"); const { Context } = require('../util/context')
const APIError = require("../../../api/APIError"); const APIError = require("../api/APIError");
const { AppUnderUserActorType, Actor, UserActorType } = require("../../auth/Actor"); const { AppUnderUserActorType, UserActorType } = require("../services/auth/Actor");
const { BaseOperation } = require("../../OperationTraceService"); const { BaseOperation } = require("../services/OperationTraceService");
const { CodeUtil } = require("../../../codex/CodeUtil"); const { CodeUtil } = require("../codex/CodeUtil");
/** /**
* Base class for all driver implementations. * Base class for all driver implementations.
*/ */
class BaseImplementation extends AdvancedBase { class Driver extends AdvancedBase {
constructor (...a) { constructor (...a) {
super(...a); super(...a);
const methods = this._get_merged_static_object('METHODS'); const methods = this._get_merged_static_object('METHODS');
@ -184,5 +184,5 @@ class BaseImplementation extends AdvancedBase {
} }
module.exports = { module.exports = {
BaseImplementation, Driver,
}; };

View File

@ -16,14 +16,12 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
const { AppUnderUserActorType } = require("../../auth/Actor"); const config = require("../config");
const { BaseImplementation } = require("./BaseImplementation"); const APIError = require("../api/APIError");
const { DB_READ, DB_WRITE } = require("../services/database/consts");
const { Driver } = require("../definitions/Driver");
const config = require("../../../config"); class DBKVStore extends Driver {
const APIError = require("../../../api/APIError");
const { DB_READ, DB_WRITE } = require("../../database/consts");
class DBKVStore extends BaseImplementation {
static ID = 'public-db-kvstore'; static ID = 'public-db-kvstore';
static VERSION = '0.0.0'; static VERSION = '0.0.0';
static INTERFACE = 'puter-kvstore'; static INTERFACE = 'puter-kvstore';
@ -32,6 +30,7 @@ class DBKVStore extends BaseImplementation {
} }
static METHODS = { static METHODS = {
get: async function ({ key }) { get: async function ({ key }) {
console.log('THIS WAS CALLED', { key });
const actor = this.context.get('actor'); const actor = this.context.get('actor');
// If the actor is an app then it gets its own KV store. // If the actor is an app then it gets its own KV store.
@ -58,6 +57,7 @@ class DBKVStore extends BaseImplementation {
return kv[0]?.value ?? null; return kv[0]?.value ?? null;
}, },
set: async function ({ key, value }) { set: async function ({ key, value }) {
console.log('THIS WAS CALLED (SET)', { key, value })
const actor = this.context.get('actor'); const actor = this.context.get('actor');
// Validate the key // Validate the key
@ -98,12 +98,15 @@ class DBKVStore extends BaseImplementation {
] ]
); );
} catch (e) { } catch (e) {
// if ( e.code !== 'SQLITE_ERROR' && e.code !== 'SQLITE_CONSTRAINT_PRIMARYKEY' ) throw e; // I discovered that my .sqlite file was corrupted and the update
// The "ON CONFLICT" clause isn't currently working. // above didn't work. The current database initialization does not
await db.write( // cause this issue so I'm adding this log as a safeguard.
`UPDATE kv SET value = ? WHERE user_id=? AND app=? AND kkey_hash=?`, // - KernelDeimos / ED
[ value, user.id, app?.uid ?? 'global', key_hash ] const svc_error = this.services.get('error-service');
); svc_error.report('kvstore:sqlite_error', {
message: 'Broken database version - please contact maintainers',
source: e,
});
} }
return true; return true;

View File

@ -16,10 +16,10 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
const APIError = require("../../../api/APIError"); const APIError = require("../api/APIError");
const { Entity } = require("../../../om/entitystorage/Entity"); const { Driver } = require("../definitions/Driver");
const { Or, And, Eq } = require("../../../om/query/query"); const { Entity } = require("../om/entitystorage/Entity");
const { BaseImplementation } = require("./BaseImplementation"); const { Or, And, Eq } = require("../om/query/query");
const _fetch_based_on_complex_id = async (self, id) => { const _fetch_based_on_complex_id = async (self, id) => {
// Ensure `id` is an object and get its keys // Ensure `id` is an object and get its keys
@ -87,7 +87,7 @@ const _fetch_based_on_either_id = async (self, uid, id) => {
return await _fetch_based_on_complex_id(self, id); return await _fetch_based_on_complex_id(self, id);
} }
class EntityStoreImplementation extends BaseImplementation { class EntityStoreImplementation extends Driver {
constructor ({ service }) { constructor ({ service }) {
super(); super();
this.service = service; this.service = service;

View File

@ -16,10 +16,9 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
const { Context } = require("../../../util/context"); const { Driver } = require("../definitions/Driver");
const { BaseImplementation } = require("./BaseImplementation");
class HelloWorld extends BaseImplementation { class HelloWorld extends Driver {
static ID = 'public-helloworld'; static ID = 'public-helloworld';
static VERSION = '0.0.0'; static VERSION = '0.0.0';
static INTERFACE = 'helloworld'; static INTERFACE = 'helloworld';

View File

@ -128,8 +128,6 @@ class PropType extends AdvancedBase {
async is_set (value) { async is_set (value) {
const is_setters = this.chains.is_set || []; const is_setters = this.chains.is_set || [];
console.log('IS SETTERS', is_setters)
for ( const is_setter of is_setters ) { for ( const is_setter of is_setters ) {
const result = await is_setter(value); const result = await is_setter(value);
if ( ! result ) { if ( ! result ) {

View File

@ -0,0 +1,21 @@
const { DBKVStore } = require("../drivers/DBKVStore");
const { EntityStoreImplementation } = require("../drivers/EntityStoreImplementation");
const { HelloWorld } = require("../drivers/HelloWorld");
const BaseService = require("./BaseService");
class SelfhostedService extends BaseService {
static description = `
Registers drivers for self-hosted Puter instances.
`
async _init () {
const svc_driver = this.services.get('driver');
svc_driver.register_driver('helloworld', new HelloWorld());
svc_driver.register_driver('puter-kvstore', new DBKVStore());
svc_driver.register_driver('puter-apps', new EntityStoreImplementation({ service: 'es:app' }));
svc_driver.register_driver('puter-subdomains', new EntityStoreImplementation({ service: 'es:subdomain' }));
}
}
module.exports = { SelfhostedService };

View File

@ -168,22 +168,13 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService {
query = this.sqlite_transform_query_(query); query = this.sqlite_transform_query_(query);
params = this.sqlite_transform_params_(params); params = this.sqlite_transform_params_(params);
try { const stmt = this.db.prepare(query);
const stmt = this.db.prepare(query); const info = stmt.run(...params);
const info = stmt.run(...params);
return { return {
insertId: info.lastInsertRowid, insertId: info.lastInsertRowid,
anyRowsAffected: info.changes > 0, anyRowsAffected: info.changes > 0,
}; };
} catch ( e ) {
console.error(e);
console.log('everything', {
query, params,
})
console.log(params.map(p => typeof p));
// throw e;
}
} }
async _batch_write (entries) { async _batch_write (entries) {

View File

@ -16,37 +16,27 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
const { AdvancedBase } = require("@heyputer/puter-js-common");
const { Context } = require("../../util/context"); const { Context } = require("../../util/context");
const APIError = require("../../api/APIError"); const APIError = require("../../api/APIError");
const { DriverError } = require("./DriverError"); const { DriverError } = require("./DriverError");
const { Value, TypedValue } = require("./meta/Runtime"); const { TypedValue } = require("./meta/Runtime");
const { ServiceImplementation, EntityStoreImplementation } = require("./implementations/EntityStoreImplementation"); const BaseService = require("../BaseService");
/** /**
* DriverService provides the functionality of Puter drivers. * DriverService provides the functionality of Puter drivers.
*/ */
class DriverService extends AdvancedBase { class DriverService extends BaseService {
static MODULES = { static MODULES = {
types: require('./types'), types: require('./types'),
} }
constructor ({ services }) { _construct () {
super();
this.services = services;
// TODO: move this to an init method
this.log = services.get('log-service').create(this.constructor.name);
this.errors = services.get('error-service').create(this.log);
this.interfaces = require('./interfaces'); this.interfaces = require('./interfaces');
this.interface_to_implementation = {};
}
this.interface_to_implementation = { register_driver (interface_name, implementation) {
'helloworld': new (require('./implementations/HelloWorld').HelloWorld)(), this.interface_to_implementation[interface_name] = implementation;
'puter-kvstore': new (require('./implementations/DBKVStore').DBKVStore)(),
'puter-apps': new EntityStoreImplementation({ service: 'es:app' }),
'puter-subdomains': new EntityStoreImplementation({ service: 'es:subdomain' }),
};
} }
get_interface (interface_name) { get_interface (interface_name) {

View File

@ -1,56 +0,0 @@
/*
* Copyright (C) 2024 Puter Technologies Inc.
*
* This file is part of Puter.
*
* Puter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
const { AdvancedBase } = require("@heyputer/puter-js-common");
const ENDPOINT = 'https://api.puter.com/drivers/call';
/*
Fetch example:
await fetch("https://api.puter.local/drivers/call", {
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer <actor token>",
},
"body": JSON.stringify({ interface: '...', method: '...', args: { ... } }),
"method": "POST",
});
*/
class PuterDriverProxy extends AdvancedBase {
static MODULES = {
axios: require('axios'),
}
constructor ({ target }) {
super();
this.target = target;
}
async call (method, args) {
const require = this.require;
const axios = require('axios');
// TODO: We need the BYOK feature before we can implement this
}
}
module.exports = PuterDriverProxy;

View File

@ -57,7 +57,6 @@ const main = async () => {
Kernel, Kernel,
CoreModule, CoreModule,
DatabaseModule, DatabaseModule,
PuterDriversModule,
LocalDiskStorageModule, LocalDiskStorageModule,
SelfHostedModule SelfHostedModule
} = (await import('@heyputer/backend')).default; } = (await import('@heyputer/backend')).default;
@ -66,7 +65,6 @@ const main = async () => {
const k = new Kernel(); const k = new Kernel();
k.add_module(new CoreModule()); k.add_module(new CoreModule());
k.add_module(new DatabaseModule()); k.add_module(new DatabaseModule());
k.add_module(new PuterDriversModule());
k.add_module(new LocalDiskStorageModule()); k.add_module(new LocalDiskStorageModule());
k.add_module(new SelfHostedModule()); k.add_module(new SelfHostedModule());
k.boot(); k.boot();