diff --git a/packages/backend/src/CoreModule.js b/packages/backend/src/CoreModule.js index eece92094..6ae1c420a 100644 --- a/packages/backend/src/CoreModule.js +++ b/packages/backend/src/CoreModule.js @@ -213,6 +213,9 @@ const install = async ({ services, app }) => { const { LockService } = require('./services/LockService'); services.registerService('lock', LockService); + + const { PuterHomepageService } = require('./services/PuterHomepageService'); + services.registerService('puter-homepage', PuterHomepageService); } const install_legacy = async ({ services }) => { diff --git a/packages/backend/src/config.js b/packages/backend/src/config.js index bb0a0cf9f..0ba65cceb 100644 --- a/packages/backend/src/config.js +++ b/packages/backend/src/config.js @@ -79,6 +79,7 @@ config.puter_hosted_data = { const path_ = require('path'); config.assets = { gui: path_.join(__dirname, '../../..'), + gui_profile: 'development', }; } @@ -174,9 +175,25 @@ const config_pointer = {}; // We'd like to store values changed at runtime separately // for easier runtime debugging { - const config_runtime_values = {}; + const config_runtime_values = { + $: 'runtime-values' + }; config_runtime_values.__proto__ = config_to_export; config_to_export = config_runtime_values + + // These can be difficult to find and cause painful + // confusing issues, so we log any time this happens + config_to_export = new Proxy(config_to_export, { + set: (target, prop, value, receiver) => { + console.log( + '\x1B[36;1mCONFIGURATION MUTATED AT RUNTIME\x1B[0m', + prop, 'to', value + ); + // console.log(new Error('stack trace to find configuration mutation')); + target[prop] = value; + return true; + } + }) } -module.exports = config_to_export; \ No newline at end of file +module.exports = config_to_export; diff --git a/packages/backend/src/routers/_default.js b/packages/backend/src/routers/_default.js index 3579e533e..4e645b72d 100644 --- a/packages/backend/src/routers/_default.js +++ b/packages/backend/src/routers/_default.js @@ -23,7 +23,6 @@ const router = express.Router(); const _path = require('path'); const _fs = require('fs'); const auth = require('../middleware/auth.js'); -const { generate_puter_page_html } = require('../temp/puter_page_loader'); const { Context } = require('../util/context'); const { DB_READ } = require('../services/database/consts'); const { PathBuilder } = require('../util/pathutil.js'); @@ -302,47 +301,14 @@ router.all('*', async function(req, res, next) { // index.js if(path === '/'){ - const APP_ORIGIN = config.origin; - const API_ORIGIN = config.api_base_url; - return res.send(generate_puter_page_html({ - env: config.env, - - app_origin: APP_ORIGIN, - api_origin: API_ORIGIN, - use_bundled_gui: config.use_bundled_gui, - - manifest, - gui_path: config.assets.gui, - - // page meta - meta: { - title: app_title, - description: description || config.short_description, - short_description: config.short_description, - company: 'Puter Technologies Inc.', - canonical_url: canonical_url, - }, - - // gui parameters - gui_params: { - app_name_regex: config.app_name_regex, - app_name_max_length: config.app_name_max_length, - app_title_max_length: config.app_title_max_length, - subdomain_regex: config.subdomain_regex, - subdomain_max_length: config.subdomain_max_length, - domain: config.domain, - protocol: config.protocol, - env: config.env, - api_base_url: config.api_base_url, - thumb_width: config.thumb_width, - thumb_height: config.thumb_height, - contact_email: config.contact_email, - max_fsentry_name_length: config.max_fsentry_name_length, - require_email_verification_to_publish_website: config.require_email_verification_to_publish_website, - short_description: config.short_description, - long_description: config.long_description, - }, - })); + const svc_puterHomepage = Context.get('services').get('puter-homepage'); + return svc_puterHomepage.send(res, { + title: app_title, + description: description || config.short_description, + short_description: config.short_description, + company: 'Puter Technologies Inc.', + canonical_url: canonical_url, + }); } // /dist/... diff --git a/packages/backend/src/services/PuterHomepageService.js b/packages/backend/src/services/PuterHomepageService.js new file mode 100644 index 000000000..003f11cf9 --- /dev/null +++ b/packages/backend/src/services/PuterHomepageService.js @@ -0,0 +1,241 @@ +const { PathBuilder } = require("../util/pathutil"); +const BaseService = require("./BaseService"); + +/** + * PuterHomepageService serves the initial HTML page that loads the Puter GUI + * and all of its assets. + */ +class PuterHomepageService extends BaseService { + static MODULES = { + fs: require('node:fs'), + } + + _construct () { + this.service_scripts = []; + } + + async _init () { + // Load manifest + const config = this.global_config; + const manifest_raw = this.modules.fs.readFileSync( + PathBuilder + .add(config.assets.gui, { allow_traversal: true }) + .add('puter-gui.json') + .build(), + 'utf8' + ); + const manifest_data = JSON.parse(manifest_raw); + this.manifest = manifest_data[config.assets.gui_profile]; + } + + register_script (url) { + this.service_scripts.push(url); + } + + async send (res, meta) { + const config = this.global_config; + return res.send(this.generate_puter_page_html({ + env: config.env, + + app_origin: config.origin, + api_origin: config.api_base_url, + use_bundled_gui: config.use_bundled_gui, + + manifest: this.manifest, + gui_path: config.assets.gui, + + // page meta + meta, + + // gui parameters + gui_params: { + app_name_regex: config.app_name_regex, + app_name_max_length: config.app_name_max_length, + app_title_max_length: config.app_title_max_length, + subdomain_regex: config.subdomain_regex, + subdomain_max_length: config.subdomain_max_length, + domain: config.domain, + protocol: config.protocol, + env: config.env, + api_base_url: config.api_base_url, + thumb_width: config.thumb_width, + thumb_height: config.thumb_height, + contact_email: config.contact_email, + max_fsentry_name_length: config.max_fsentry_name_length, + require_email_verification_to_publish_website: config.require_email_verification_to_publish_website, + short_description: config.short_description, + long_description: config.long_description, + }, + })); + } + + generate_puter_page_html ({ + env, + + manifest, + gui_path, + use_bundled_gui, + + app_origin, + api_origin, + + meta, + + gui_params, + }) { + const require = this.require; + const {encode} = require('html-entities'); + const path_ = require('path'); + const fs_ = require('fs'); + + const e = encode; + + const { + title, + description, + short_description, + company, + canonical_url, + } = meta; + + gui_params = { + ...meta, + ...gui_params, + app_origin, + api_origin, + gui_origin: app_origin, + }; + + const asset_dir = env === 'dev' + ? '/src' : '/dist' ; + // const asset_dir = '/dist'; + + gui_params.asset_dir = asset_dir; + + const bundled = env != 'dev' || use_bundled_gui; + + return ` + + +
+