dev: proof-of-concept for fs postMessage relay

Adds a filesystem relay service to GUI-side which currently reports an
exmaple fsentry for /stat and /readdir. Added filesystem services for
both sides. Moved filesystem implementations from src/modules to src/lib
since they're being called by services now rather than modules.

This functionality is effectively disabled unless
src/puter-js/src/services/Filesystem.js is updated to use init_app_fs_()
when the environment is 'app'.
This commit is contained in:
KernelDeimos 2024-10-19 03:46:47 -04:00
parent 6e5d5c2021
commit 480d996279
12 changed files with 174 additions and 47 deletions

View File

@ -13,7 +13,7 @@ import path from './lib/path.js';
import Util from './modules/Util.js';
import Drivers from './modules/Drivers.js';
import putility from '@heyputer/putility';
import { FSHostService } from './services/FSHost.js';
import { FSRelayService } from './services/FSRelay.js';
import { FilesystemService } from './services/Filesystem.js';
import { APIAccessService } from './services/APIAccess.js';
import { XDIncomingService } from './services/XDIncoming.js';
@ -69,8 +69,10 @@ window.puter = (function() {
// "modules" in puter.js are external interfaces for the developer
this.modules_ = [];
// "services" in puter.js are used by modules and may interact with each other
const context = new putility.libs.context.Context();
const context = new putility.libs.context.Context()
.follow(this, ['env', 'util']);
this.services = new putility.system.ServiceManager({ context });
this.context = context;
context.services = this.services;
// Holds the query parameters found in the current URL
@ -148,12 +150,12 @@ window.puter = (function() {
this.APIOrigin = 'https://api.' + URLParams.get('puter.domain');
}
this.services.register('no-puter-yet', NoPuterYetService);
this.services.register('filesystem', FilesystemService);
this.services.register('api-access', APIAccessService);
this.services.register('xd-incoming', XDIncomingService);
if ( this.env !== 'app' ) {
this.services.register('no-puter-yet', NoPuterYetService);
this.services.register('filesystem', FilesystemService);
this.services.register('api-access', APIAccessService);
this.services.register('xd-incoming', XDIncomingService);
// this.services.register('fs-host', FSHostService);
this.services.register('fs-relay', FSRelayService);
}
// When api-access is initialized, bind `.authToken` and
@ -162,15 +164,18 @@ window.puter = (function() {
await this.services.wait_for_init(['api-access']);
const svc_apiAccess = this.services.get('api-access');
svc_apiAccess.authToken = this.authToken;
svc_apiAccess.APIOrigin = this.APIOrigin;
['authToken', 'APIOrigin'].forEach(key => {
Object.defineProperty(this, key, {
svc_apiAccess.auth_token = this.authToken;
svc_apiAccess.api_origin = this.APIOrigin;
[
['authToken','auth_token'],
['APIOrigin','api_origin'],
].forEach(([k1,k2]) => {
Object.defineProperty(this, k1, {
get () {
return svc_apiAccess[key];
return svc_apiAccess[k2];
},
set (v) {
svc_apiAccess[key] = v;
svc_apiAccess[k2] = v;
return true;
}
});
@ -243,7 +248,7 @@ window.puter = (function() {
new OS(this.authToken, this.APIOrigin, this.appID, this.env));
// FileSystem
this.registerModule('fs',
new PuterJSFileSystemModule(this.authToken, this.APIOrigin, this.appID, this.env));
new PuterJSFileSystemModule(this.authToken, this.APIOrigin, this.appID, this.context));
// UI
this.registerModule('ui',
new UI(this.appInstanceID, this.parentInstanceID, this.appID, this.env, this.util));

View File

@ -1,7 +1,7 @@
import * as utils from '../../lib/utils.js';
import * as utils from '../utils.js';
import putility from "@heyputer/putility";
import { TeePromise } from "@heyputer/putility/src/libs/promise";
import getAbsolutePathForApp from './utils/getAbsolutePathForApp.js';
import getAbsolutePathForApp from '../../modules/FileSystem/utils/getAbsolutePathForApp.js';
import { TFilesystem } from './definitions.js';
export class PuterAPIFilesystem extends putility.AdvancedBase {

View File

@ -1,7 +1,7 @@
import putility from "@heyputer/putility";
import { RWLock } from "@heyputer/putility/src/libs/promise";
import { ProxyFilesystem, TFilesystem } from "./definitions";
import { uuidv4 } from "../../lib/utils";
import { uuidv4 } from "../utils";
export const ROOT_UUID = '00000000-0000-0000-0000-000000000000';
const TTL = 5 * 1000;

View File

@ -0,0 +1,40 @@
import putility from "@heyputer/putility";
import { TFilesystem } from "./definitions";
const example = {
"id": "f485f1ba-de07-422c-8c4b-c2da057d4a44",
"uid": "f485f1ba-de07-422c-8c4b-c2da057d4a44",
"is_dir": true,
"immutable": true,
"name": "Test",
};
export class PostMessageFilesystem extends putility.AdvancedBase {
constructor ({ rpc, messageTarget }) {
super();
this.rpc = rpc;
this.messageTarget = messageTarget;
}
static IMPLEMENTS = {
[TFilesystem]: {
stat: async function (o) {
return example;
},
readdir: async function (o) {
const tp = new putility.libs.promise.TeePromise();
const $callback = this.rpc.registerCallback((result) => {
tp.resolve(result);
});
// return [example];
this.messageTarget.postMessage({
$: 'puter-fs',
$callback,
op: 'readdir',
args: o,
}, '*');
return await tp;
}
}
}
}

View File

@ -13,10 +13,10 @@ import sign from "./operations/sign.js";
// Why is this called deleteFSEntry instead of just delete? because delete is
// a reserved keyword in javascript
import deleteFSEntry from "./operations/deleteFSEntry.js";
import { ProxyFilesystem, TFilesystem } from './definitions.js';
import { ProxyFilesystem, TFilesystem } from '../../lib/filesystem/definitions.js';
import { AdvancedBase } from '../../../../putility/index.js';
import { CachedFilesystem } from './CacheFS.js';
import { PuterAPIFilesystem } from './APIFS.js';
import { CachedFilesystem } from '../../lib/filesystem/CacheFS.js';
import { PuterAPIFilesystem } from '../../lib/filesystem/APIFS.js';
export class PuterJSFileSystemModule extends AdvancedBase {
@ -38,14 +38,16 @@ export class PuterJSFileSystemModule extends AdvancedBase {
positional: ['path'],
firstarg_options: true,
async fn (parameters) {
return this.filesystem.stat(parameters);
const svc_fs = await this.context.services.aget('filesystem');
return svc_fs.filesystem.stat(parameters);
}
},
readdir: {
positional: ['path'],
firstarg_options: true,
fn (parameters) {
return this.filesystem.readdir(parameters);
async fn (parameters) {
const svc_fs = await this.context.services.aget('filesystem');
return svc_fs.filesystem.readdir(parameters);
}
},
}
@ -59,11 +61,12 @@ export class PuterJSFileSystemModule extends AdvancedBase {
* @param {string} APIOrigin - Origin of the API server. Used to build the API endpoint URLs.
* @param {string} appID - ID of the app to use.
*/
constructor (authToken, APIOrigin, appID) {
constructor (authToken, APIOrigin, appID, context) {
super();
this.authToken = authToken;
this.APIOrigin = APIOrigin;
this.appID = appID;
this.context = context;
// Connect socket.
this.initializeSocket();
@ -76,21 +79,6 @@ export class PuterJSFileSystemModule extends AdvancedBase {
Object.defineProperty(api_info, 'APIOrigin', {
get: () => this.APIOrigin,
});
// Construct the decorator chain for the client-side filesystem.
this.fs_nocache_ = new PuterAPIFilesystem({ api_info }).as(TFilesystem);
this.fs_cache_ = new CachedFilesystem({ delegate: this.fs_nocache_ }).as(TFilesystem);
// this.filesystem = this.fs_nocache;
this.fs_proxy_ = new ProxyFilesystem({ delegate: this.fs_nocache_ });
this.filesystem = this.fs_proxy_.as(TFilesystem);
// this.fs_proxy_.delegate = this.fs_cache_;
}
cache_on () {
this.fs_proxy_.delegate = this.fs_cache_;
}
cache_off () {
this.fs_proxy_.delegate = this.fs_nocache_;
}

View File

@ -11,7 +11,7 @@ export class APIAccessService extends putility.concepts.Service {
static PROPERTIES = {
auth_token: {
post_set () {
post_set (v) {
this.as(TTopics).pub('update');
}
},
@ -21,4 +21,24 @@ export class APIAccessService extends putility.concepts.Service {
}
},
};
// TODO: inconsistent! Update all dependents.
get_api_info () {
const self = this;
const o = {};
[
['authToken','auth_token'],
['APIOrigin','api_origin'],
].forEach(([k1,k2]) => {
Object.defineProperty(o, k1, {
get () {
return self[k2];
},
set (v) {
return self;
}
});
});
return o;
}
}

View File

@ -1,7 +0,0 @@
import putility from '@heyputer/putility';
export class FSHostService extends putility.concepts.Service {
async _init () {
//
}
}

View File

@ -0,0 +1,20 @@
import putility from '@heyputer/putility';
const example = {
"id": "f485f1ba-de07-422c-8c4b-c2da057d4a44",
"uid": "f485f1ba-de07-422c-8c4b-c2da057d4a44",
"is_dir": true,
"immutable": true,
"name": "FromParentWindow",
};
export class FSRelayService extends putility.concepts.Service {
async _init () {
const services = this._.context.services;
const util = this._.context.util;
const svc_xdIncoming = services.get('xd-incoming');
svc_xdIncoming.register_tagged_listener('puter-fs', event => {
util.rpc.send(event.source, event.data.$callback, [example]);
});
}
}

View File

@ -1,4 +1,9 @@
import putility from "@heyputer/putility";
import { PuterAPIFilesystem } from "../lib/filesystem/APIFS";
import { CachedFilesystem } from "../lib/filesystem/CacheFS";
import { ProxyFilesystem, TFilesystem } from "../lib/filesystem/definitions";
import io from '../lib/socket.io/socket.io.esm.min.js';
import { PostMessageFilesystem } from "../lib/filesystem/PostMessageFS.js";
export class FilesystemService extends putility.concepts.Service {
static PROPERTIES = {
@ -23,9 +28,44 @@ export class FilesystemService extends putility.concepts.Service {
_init () {
console.log('does this init get called');
const env = this._.context.env;
if ( env === 'app' ) {
// TODO: uncomment when relay is ready
// this.init_app_fs_();
this.init_top_fs_();
} else {
this.init_top_fs_();
}
this.initializeSocket();
}
init_app_fs_ () {
this.fs_nocache_ = new PostMessageFilesystem({
messageTarget: window.parent,
rpc: this._.context.util.rpc,
}).as(TFilesystem);
this.filesystem = this.fs_nocache_;
}
init_top_fs_ () {
const api_info = this._.context.services.get('api-access').get_api_info();
this.fs_nocache_ = new PuterAPIFilesystem({ api_info }).as(TFilesystem);
this.fs_cache_ = new CachedFilesystem({ delegate: this.fs_nocache_ }).as(TFilesystem);
// this.filesystem = this.fs_nocache;
this.fs_proxy_ = new ProxyFilesystem({ delegate: this.fs_nocache_ });
this.filesystem = this.fs_proxy_.as(TFilesystem);
}
cache_on () {
this.fs_proxy_.delegate = this.fs_cache_;
}
cache_off () {
this.fs_proxy_.delegate = this.fs_nocache_;
}
initializeSocket () {
console.log('THIS IS RUNNING');
if (this.socket) {

View File

@ -18,10 +18,27 @@ export class XDIncomingService extends putility.concepts.Service {
fn(event, tp);
if ( await tp ) return;
}
const data = event.data;
const tag = data.$;
if ( ! tag ) return;
if ( ! this.tagged_listeners_[tag] ) return;
for ( const fn of this.tagged_listeners_[tag] ) {
fn({ data, source: event.source });
}
});
}
register_filter_listener (fn) {
this.filter_listeners_.push(fn);
}
register_tagged_listener (tag, fn) {
if ( ! this.tagged_listeners_[tag] ) {
this.tagged_listeners_[tag] = [];
}
this.tagged_listeners_[tag].push(fn);
}
}

View File

@ -77,6 +77,10 @@ class ServiceManager extends AdvancedBase {
}
return info.instance;
}
async aget (name) {
await this.wait_for_init([name]);
return this.get(name);
}
/**
* Wait for the specified list of services to be initialized.