mirror of
https://github.com/HeyPuter/puter.git
synced 2025-02-03 07:48:46 +08:00
dev: lay the groundwork for client FS relay
Adds XDIncomingService to manage messages from another window. IPC now registers with XDIncoming and reports whether it was handled, that way messages intended to reach GUI can be reliably ignored. On GUI-side, XDIncomingService will be used by FSRelayService (not yet implemented) to handle requests for filesystem operations from another window. On App-side, XDIncomingService might be used by the PostMessageFS client-side filesystem implementation terminal to listen for filesystem events that were relayed by GUI (after a permission check) from their original websocket source. Either that, or we'll open a filesystem socket for each app using that app's token. NoPuterYetService was added because IPC.js is loaded before globalThis.puter exists, and was the easiest way to still allow IPC's listener to be registered with XDIncomingService. APIAccessService was added. This service currently holds auth_token and api_token and does nothing else. FilesystemService listens to this to maintain a websocket connection. APIAccessService will help to manage the complexity of all further code dependent on being informed about changes to the auth token or origin. Currently in puter.js these are passed around to several modules which manage the same piece of state information independantly.
This commit is contained in:
parent
bc5d09fe31
commit
c2a475f3c0
@ -44,12 +44,12 @@ window.ipc_handlers = {};
|
||||
*
|
||||
* Precautions are taken to ensure proper usage of appInstanceIDs and other sensitive information.
|
||||
*/
|
||||
window.addEventListener('message', async (event) => {
|
||||
const ipc_listener = async (event, handled) => {
|
||||
const app_env = event.data?.env ?? 'app';
|
||||
|
||||
// Only process messages from apps
|
||||
if(app_env !== 'app')
|
||||
return;
|
||||
return handled.resolve(false);
|
||||
|
||||
// --------------------------------------------------------
|
||||
// A response to a GUI message received from the app.
|
||||
@ -61,7 +61,7 @@ window.addEventListener('message', async (event) => {
|
||||
delete window.appCallbackFunctions[event.data.original_msg_id];
|
||||
|
||||
// Done
|
||||
return;
|
||||
return handled.resolve(false);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
@ -70,18 +70,20 @@ window.addEventListener('message', async (event) => {
|
||||
|
||||
// `data` and `msg` are required
|
||||
if(!event.data || !event.data.msg){
|
||||
return;
|
||||
return handled.resolve(false);
|
||||
}
|
||||
|
||||
// `appInstanceID` is required
|
||||
if(!event.data.appInstanceID){
|
||||
console.error(`appInstanceID is needed`);
|
||||
return;
|
||||
return handled.resolve(false);
|
||||
}else if(!window.app_instance_ids.has(event.data.appInstanceID)){
|
||||
console.error(`appInstanceID is invalid`);
|
||||
return;
|
||||
return handled.resolve(false);
|
||||
}
|
||||
|
||||
handled.resolve(true);
|
||||
|
||||
const $el_parent_window = $(window.window_for_app_instance(event.data.appInstanceID));
|
||||
const parent_window_id = $el_parent_window.attr('data-id');
|
||||
const $el_parent_disable_mask = $el_parent_window.find('.window-disable-mask');
|
||||
@ -1522,4 +1524,11 @@ window.addEventListener('message', async (event) => {
|
||||
status_code,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if ( ! window.when_puter_happens ) window.when_puter_happens = [];
|
||||
window.when_puter_happens.push(async () => {
|
||||
await puter.services.wait_for_init(['xd-incoming']);
|
||||
const svc_xdIncoming = puter.services.get('xd-incoming');
|
||||
svc_xdIncoming.register_filter_listener(ipc_listener);
|
||||
});
|
||||
|
@ -12,6 +12,12 @@ import * as utils from './lib/utils.js';
|
||||
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 { FilesystemService } from './services/Filesystem.js';
|
||||
import { APIAccessService } from './services/APIAccess.js';
|
||||
import { XDIncomingService } from './services/XDIncoming.js';
|
||||
import { NoPuterYetService } from './services/NoPuterYet.js';
|
||||
|
||||
window.puter = (function() {
|
||||
'use strict';
|
||||
@ -60,7 +66,12 @@ window.puter = (function() {
|
||||
constructor(options) {
|
||||
options = options ?? {};
|
||||
|
||||
// "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();
|
||||
this.services = new putility.system.ServiceManager({ context });
|
||||
context.services = this.services;
|
||||
|
||||
// Holds the query parameters found in the current URL
|
||||
let URLParams = new URLSearchParams(window.location.search);
|
||||
@ -137,6 +148,35 @@ window.puter = (function() {
|
||||
this.APIOrigin = 'https://api.' + URLParams.get('puter.domain');
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// When api-access is initialized, bind `.authToken` and
|
||||
// `.APIOrigin` as a 1-1 mapping with the `puter` global
|
||||
(async () => {
|
||||
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, {
|
||||
get () {
|
||||
return svc_apiAccess[key];
|
||||
},
|
||||
set (v) {
|
||||
svc_apiAccess[key] = v;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
// The SDK is running in the Puter GUI (i.e. 'gui')
|
||||
if(this.env === 'gui'){
|
||||
this.authToken = window.auth_token;
|
||||
|
24
src/puter-js/src/services/APIAccess.js
Normal file
24
src/puter-js/src/services/APIAccess.js
Normal file
@ -0,0 +1,24 @@
|
||||
import putility from "@heyputer/putility";
|
||||
|
||||
const { TTopics } = putility.traits;
|
||||
|
||||
/**
|
||||
* Manages the auth token and origin used to communicate with
|
||||
* Puter's API
|
||||
*/
|
||||
export class APIAccessService extends putility.concepts.Service {
|
||||
static TOPICS = ['update'];
|
||||
|
||||
static PROPERTIES = {
|
||||
auth_token: {
|
||||
post_set () {
|
||||
this.as(TTopics).pub('update');
|
||||
}
|
||||
},
|
||||
api_origin: {
|
||||
post_set () {
|
||||
this.as(TTopics).pub('update');
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
7
src/puter-js/src/services/FSHost.js
Normal file
7
src/puter-js/src/services/FSHost.js
Normal file
@ -0,0 +1,7 @@
|
||||
import putility from '@heyputer/putility';
|
||||
|
||||
export class FSHostService extends putility.concepts.Service {
|
||||
async _init () {
|
||||
//
|
||||
}
|
||||
}
|
80
src/puter-js/src/services/Filesystem.js
Normal file
80
src/puter-js/src/services/Filesystem.js
Normal file
@ -0,0 +1,80 @@
|
||||
import putility from "@heyputer/putility";
|
||||
|
||||
export class FilesystemService extends putility.concepts.Service {
|
||||
static PROPERTIES = {
|
||||
// filesystem:
|
||||
};
|
||||
|
||||
static DEPENDS = ['api-access'];
|
||||
static HOOKS = [
|
||||
{
|
||||
service: 'api-access',
|
||||
event: 'update',
|
||||
description: `
|
||||
re-initialize the socket connection whenever the
|
||||
authentication token or API origin is changed.
|
||||
`,
|
||||
async do () {
|
||||
console.log('do() was called');
|
||||
this.initializeSocket();
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
_init () {
|
||||
console.log('does this init get called');
|
||||
this.initializeSocket();
|
||||
}
|
||||
|
||||
initializeSocket () {
|
||||
console.log('THIS IS RUNNING');
|
||||
if (this.socket) {
|
||||
this.socket.disconnect();
|
||||
}
|
||||
|
||||
this.socket = io(this.APIOrigin, {
|
||||
auth: {
|
||||
auth_token: this.authToken,
|
||||
}
|
||||
});
|
||||
|
||||
this.bindSocketEvents();
|
||||
}
|
||||
|
||||
bindSocketEvents() {
|
||||
this.socket.on('connect', () => {
|
||||
if(puter.debugMode)
|
||||
console.log('FileSystem Socket: Connected', this.socket.id);
|
||||
});
|
||||
|
||||
this.socket.on('disconnect', () => {
|
||||
if(puter.debugMode)
|
||||
console.log('FileSystem Socket: Disconnected');
|
||||
});
|
||||
|
||||
this.socket.on('reconnect', (attempt) => {
|
||||
if(puter.debugMode)
|
||||
console.log('FileSystem Socket: Reconnected', this.socket.id);
|
||||
});
|
||||
|
||||
this.socket.on('reconnect_attempt', (attempt) => {
|
||||
if(puter.debugMode)
|
||||
console.log('FileSystem Socket: Reconnection Attemps', attempt);
|
||||
});
|
||||
|
||||
this.socket.on('reconnect_error', (error) => {
|
||||
if(puter.debugMode)
|
||||
console.log('FileSystem Socket: Reconnection Error', error);
|
||||
});
|
||||
|
||||
this.socket.on('reconnect_failed', () => {
|
||||
if(puter.debugMode)
|
||||
console.log('FileSystem Socket: Reconnection Failed');
|
||||
});
|
||||
|
||||
this.socket.on('error', (error) => {
|
||||
if(puter.debugMode)
|
||||
console.error('FileSystem Socket Error:', error);
|
||||
});
|
||||
}
|
||||
}
|
20
src/puter-js/src/services/NoPuterYet.js
Normal file
20
src/puter-js/src/services/NoPuterYet.js
Normal file
@ -0,0 +1,20 @@
|
||||
import putility from "@heyputer/putility";
|
||||
|
||||
/**
|
||||
* Runs commands on the special `window.when_puter_happens` global, for
|
||||
* situations where the `puter` global doesn't exist soon enough.
|
||||
*/
|
||||
export class NoPuterYetService extends putility.concepts.Service {
|
||||
_init () {
|
||||
if ( ! window.when_puter_happens ) return;
|
||||
if ( puter && puter.env !== 'gui' ) return;
|
||||
|
||||
if ( ! Array.isArray(window.when_puter_happens) ) {
|
||||
window.when_puter_happens = [window.when_puter_happens];
|
||||
}
|
||||
|
||||
for ( const fn of window.when_puter_happens ) {
|
||||
fn({ context: this._.context });
|
||||
}
|
||||
}
|
||||
}
|
27
src/puter-js/src/services/XDIncoming.js
Normal file
27
src/puter-js/src/services/XDIncoming.js
Normal file
@ -0,0 +1,27 @@
|
||||
import putility from "@heyputer/putility";
|
||||
|
||||
const TeePromise = putility.libs.promise.TeePromise;
|
||||
|
||||
/**
|
||||
* Manages message events from the window object.
|
||||
*/
|
||||
export class XDIncomingService extends putility.concepts.Service {
|
||||
_construct () {
|
||||
this.filter_listeners_ = [];
|
||||
this.tagged_listeners_ = {};
|
||||
}
|
||||
|
||||
_init () {
|
||||
window.addEventListener('message', async event => {
|
||||
for ( const fn of this.filter_listeners_ ) {
|
||||
const tp = new TeePromise();
|
||||
fn(event, tp);
|
||||
if ( await tp ) return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
register_filter_listener (fn) {
|
||||
this.filter_listeners_.push(fn);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user