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 ` + + + + ${e(title)} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${ + ((!bundled && manifest?.css_paths) + ? manifest.css_paths.map(path => `\n`) + : []).join('') + } + + + + + + ${ + use_bundled_gui + ? `` + : '' + } + ${ + ((!bundled && manifest?.lib_paths) + ? manifest.lib_paths.map(path => `\n`) + : []).join('') + } + + + + ${ + ((!bundled && manifest?.js_paths) + ? manifest.js_paths.map(path => `\n`) + : []).join('') + } + ${ + this.service_scripts + .map(path => `\n`) + .join('') + } + + + + + + + + `; + }; +} + +module.exports = { + PuterHomepageService +}; diff --git a/packages/backend/src/services/ServeLandingService.js b/packages/backend/src/services/ServeLandingService.js new file mode 100644 index 000000000..f1c9cd9f8 --- /dev/null +++ b/packages/backend/src/services/ServeLandingService.js @@ -0,0 +1,5 @@ + +/** + * ServeLandingService is for "landing" pages, like payment success or failure. + */ +class ServceLandingService {} diff --git a/packages/backend/src/temp/puter_page_loader.js b/packages/backend/src/temp/puter_page_loader.js deleted file mode 100644 index e0ed4b8a3..000000000 --- a/packages/backend/src/temp/puter_page_loader.js +++ /dev/null @@ -1,181 +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 . - */ -const {encode} = require('html-entities'); -const path_ = require('path'); -const fs_ = require('fs'); - -const generate_puter_page_html = ({ - env, - - manifest, - gui_path, - use_bundled_gui, - - app_origin, - api_origin, - - meta, - - gui_params, -}) => { - 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 ` - - - - ${e(title)} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ${ - ((!bundled && manifest?.css_paths) - ? manifest.css_paths.map(path => `\n`) - : []).join('') - } - - - - - - ${ - use_bundled_gui - ? `` - : '' - } - ${ - ((!bundled && manifest?.lib_paths) - ? manifest.lib_paths.map(path => `\n`) - : []).join('') - } - - - - ${ - ((!bundled && manifest?.js_paths) - ? manifest.js_paths.map(path => `\n`) - : []).join('') - } - - - - - - - -`; -}; - -module.exports = { - generate_puter_page_html, -}; diff --git a/puter-gui.json b/puter-gui.json index af7691d9f..5f15a36dc 100644 --- a/puter-gui.json +++ b/puter-gui.json @@ -1,32 +1,37 @@ { - "index": "/src/index.js", - "lib_paths": [ - "/lib/jquery-3.6.1/jquery-3.6.1.min.js", - "/lib/viselect.min.js", - "/lib/FileSaver.min.js", - "/lib/socket.io/socket.io.min.js", - "/lib/qrcode.min.js", - "/lib/jquery-ui-1.13.2/jquery-ui.min.js", - "/lib/lodash@4.17.21.min.js", - "/lib/jquery.dragster.js", - "/lib/jquery.menu-aim.js", - "/lib/html-entities.js", - "/lib/timeago.min.js", - "/lib/iro.min.js", - "/lib/isMobile.min.js", - "/lib/jszip-3.10.1.min.js" - ], - "css_paths": [ - "/css/normalize.css", - "/lib/jquery-ui-1.13.2/jquery-ui.min.css", - "/css/style.css", - "/css/theme.css" - ], - "js_paths": [ - "/src/initgui.js", - "/src/helpers.js", - "/src/IPC.js", - "/src/globals.js", - "/src/i18n/i18n.js" - ] + "development": { + "index": "/src/index.js", + "lib_paths": [ + "/lib/jquery-3.6.1/jquery-3.6.1.min.js", + "/lib/viselect.min.js", + "/lib/FileSaver.min.js", + "/lib/socket.io/socket.io.min.js", + "/lib/qrcode.min.js", + "/lib/jquery-ui-1.13.2/jquery-ui.min.js", + "/lib/lodash@4.17.21.min.js", + "/lib/jquery.dragster.js", + "/lib/jquery.menu-aim.js", + "/lib/html-entities.js", + "/lib/timeago.min.js", + "/lib/iro.min.js", + "/lib/isMobile.min.js", + "/lib/jszip-3.10.1.min.js" + ], + "css_paths": [ + "/css/normalize.css", + "/lib/jquery-ui-1.13.2/jquery-ui.min.css", + "/css/style.css", + "/css/theme.css" + ], + "js_paths": [ + "/src/initgui.js", + "/src/helpers.js", + "/src/IPC.js", + "/src/globals.js", + "/src/i18n/i18n.js" + ] + }, + "bundle": { + "index": ["/dist/gui.js", false] + } }