Browse Source

Refactor gui loading

KernelDeimos 1 year ago
parent
commit
c89b50bf31

+ 3 - 0
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 }) => {

+ 1 - 0
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',
     };
 }
 

+ 8 - 42
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/...

+ 229 - 0
packages/backend/src/services/PuterHomepageService.js

@@ -0,0 +1,229 @@
+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'),
+    }
+
+    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];
+    }
+
+    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 `<!DOCTYPE html>
+    <html lang="en">
+
+    <head>
+        <title>${e(title)}</title>
+        <meta name="author" content="${e(company)}">
+        <meta name="description" content="${e((description).replace(/\n/g, " "))}">
+        <meta name="facebook-domain-verification" content="e29w3hjbnnnypf4kzk2cewcdaxym1y" />
+        <link rel="canonical" href="${e(canonical_url)}">
+
+        <!-- Meta meta tags -->
+        <meta property="og:url" content="${app_origin}">
+        <meta property="og:type" content="website">
+        <meta property="og:title" content="${e(title)}">
+        <meta property="og:description" content="${e((short_description).replace(/\n/g, " "))}">
+        <meta property="og:image" content="${asset_dir}/images/screenshot.png">
+
+        <!-- Twitter meta tags -->
+        <meta name="twitter:card" content="summary_large_image">
+        <meta property="twitter:domain" content="puter.com">
+        <meta property="twitter:url" content="${app_origin}">
+        <meta name="twitter:title" content="${e(title)}">
+        <meta name="twitter:description" content="${e((short_description).replace(/\n/g, " "))}">
+        <meta name="twitter:image" content="${asset_dir}/images/screenshot.png">
+
+        <!-- favicons -->
+        <link rel="apple-touch-icon" sizes="57x57" href="${asset_dir}/favicons/apple-icon-57x57.png">
+        <link rel="apple-touch-icon" sizes="60x60" href="${asset_dir}/favicons/apple-icon-60x60.png">
+        <link rel="apple-touch-icon" sizes="72x72" href="${asset_dir}/favicons/apple-icon-72x72.png">
+        <link rel="apple-touch-icon" sizes="76x76" href="${asset_dir}/favicons/apple-icon-76x76.png">
+        <link rel="apple-touch-icon" sizes="114x114" href="${asset_dir}/favicons/apple-icon-114x114.png">
+        <link rel="apple-touch-icon" sizes="120x120" href="${asset_dir}/favicons/apple-icon-120x120.png">
+        <link rel="apple-touch-icon" sizes="144x144" href="${asset_dir}/favicons/apple-icon-144x144.png">
+        <link rel="apple-touch-icon" sizes="152x152" href="${asset_dir}/favicons/apple-icon-152x152.png">
+        <link rel="apple-touch-icon" sizes="180x180" href="${asset_dir}/favicons/apple-icon-180x180.png">
+        <link rel="icon" type="image/png" sizes="192x192"  href="${asset_dir}/favicons/android-icon-192x192.png">
+        <link rel="icon" type="image/png" sizes="32x32" href="${asset_dir}/favicons/favicon-32x32.png">
+        <link rel="icon" type="image/png" sizes="96x96" href="${asset_dir}/favicons/favicon-96x96.png">
+        <link rel="icon" type="image/png" sizes="16x16" href="${asset_dir}/favicons/favicon-16x16.png">
+        <link rel="manifest" href="${asset_dir}/manifest.json">
+        <meta name="msapplication-TileColor" content="#ffffff">
+        <meta name="msapplication-TileImage" content="${asset_dir}/favicons/ms-icon-144x144.png">
+        <meta name="theme-color" content="#ffffff">
+
+        <!-- Preload images when applicable -->
+        <link rel="preload" as="image" href="${asset_dir}/images/wallpaper.webp">
+
+        <!-- Files from JSON (may be empty) -->
+        ${
+            ((!bundled && manifest?.css_paths)
+                ? manifest.css_paths.map(path => `<link rel="stylesheet" href="${path}">\n`)
+                : []).join('')
+        }
+        <!-- END Files from JSON -->
+    </head>
+
+    <body>
+        <script>window.puter_gui_enabled = true;</script>
+        ${
+            use_bundled_gui
+                ? `<script>window.gui_env = 'prod';</script>`
+                : ''
+        }
+        ${
+            ((!bundled && manifest?.lib_paths)
+                ? manifest.lib_paths.map(path => `<script type="text/javascript" src="${path}"></script>\n`)
+                : []).join('')
+        }
+
+        <script>
+        window.icons = {};
+
+        ${(() => {
+            if ( !(!bundled && manifest) ) return '';
+            const html = [];
+            fs_.readdirSync(path_.join(gui_path, 'src/icons')).forEach(file => {
+                // skip dotfiles
+                if(file.startsWith('.'))
+                    return;
+                // load image
+                let buff = new Buffer.from(fs_.readFileSync(path_.join(gui_path, 'src/icons') + '/' + file));
+                // convert to base64
+                let base64data = buff.toString('base64');
+                // add to `window.icons`
+                if(file.endsWith('.png'))
+                    html.push(`window.icons['${file}'] = "data:image/png;base64,${base64data}";\n`);
+                else if(file.endsWith('.svg'))
+                    html.push(`window.icons['${file}'] = "data:image/svg+xml;base64,${base64data}";\n`);
+            })
+            return html.join('');
+        })()}
+        </script>
+
+        ${
+            ((!bundled && manifest?.js_paths)
+                ? manifest.js_paths.map(path => `<script type="module" src="${path}"></script>\n`)
+                : []).join('')
+        }
+        <!-- Load the GUI script -->
+        <script ${ !bundled ? ' type="module"' : ''} src="${(!bundled && manifest?.index) || '/dist/gui.js'}"></script>
+        <!-- Initialize GUI when document is loaded -->
+        <script>
+        window.addEventListener('load', function() {
+            gui(${
+                // TODO: override JSON.stringify to ALWAYS to this...
+                //       this should be an opt-OUT, not an opt-IN!
+                JSON.stringify(gui_params).replace(/</g, '\\u003c')
+            });
+        });
+        </script>
+        <div id="templates" style="display: none;"></div>
+    </body>
+
+    </html>`;
+    };
+}
+
+module.exports = {
+    PuterHomepageService
+};

+ 5 - 0
packages/backend/src/services/ServeLandingService.js

@@ -0,0 +1,5 @@
+
+/**
+ * ServeLandingService is for "landing" pages, like payment success or failure.
+ */
+class ServceLandingService {}

+ 0 - 181
packages/backend/src/temp/puter_page_loader.js

@@ -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 <https://www.gnu.org/licenses/>.
- */
-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 `<!DOCTYPE html>
-<html lang="en">
-
-<head>
-    <title>${e(title)}</title>
-    <meta name="author" content="${e(company)}">
-    <meta name="description" content="${e((description).replace(/\n/g, " "))}">
-    <meta name="facebook-domain-verification" content="e29w3hjbnnnypf4kzk2cewcdaxym1y" />
-    <link rel="canonical" href="${e(canonical_url)}">
-
-    <!-- Meta meta tags -->
-    <meta property="og:url" content="${app_origin}">
-    <meta property="og:type" content="website">
-    <meta property="og:title" content="${e(title)}">
-    <meta property="og:description" content="${e((short_description).replace(/\n/g, " "))}">
-    <meta property="og:image" content="${asset_dir}/images/screenshot.png">
-
-    <!-- Twitter meta tags -->
-    <meta name="twitter:card" content="summary_large_image">
-    <meta property="twitter:domain" content="puter.com">
-    <meta property="twitter:url" content="${app_origin}">
-    <meta name="twitter:title" content="${e(title)}">
-    <meta name="twitter:description" content="${e((short_description).replace(/\n/g, " "))}">
-    <meta name="twitter:image" content="${asset_dir}/images/screenshot.png">
-
-    <!-- favicons -->
-    <link rel="apple-touch-icon" sizes="57x57" href="${asset_dir}/favicons/apple-icon-57x57.png">
-    <link rel="apple-touch-icon" sizes="60x60" href="${asset_dir}/favicons/apple-icon-60x60.png">
-    <link rel="apple-touch-icon" sizes="72x72" href="${asset_dir}/favicons/apple-icon-72x72.png">
-    <link rel="apple-touch-icon" sizes="76x76" href="${asset_dir}/favicons/apple-icon-76x76.png">
-    <link rel="apple-touch-icon" sizes="114x114" href="${asset_dir}/favicons/apple-icon-114x114.png">
-    <link rel="apple-touch-icon" sizes="120x120" href="${asset_dir}/favicons/apple-icon-120x120.png">
-    <link rel="apple-touch-icon" sizes="144x144" href="${asset_dir}/favicons/apple-icon-144x144.png">
-    <link rel="apple-touch-icon" sizes="152x152" href="${asset_dir}/favicons/apple-icon-152x152.png">
-    <link rel="apple-touch-icon" sizes="180x180" href="${asset_dir}/favicons/apple-icon-180x180.png">
-    <link rel="icon" type="image/png" sizes="192x192"  href="${asset_dir}/favicons/android-icon-192x192.png">
-    <link rel="icon" type="image/png" sizes="32x32" href="${asset_dir}/favicons/favicon-32x32.png">
-    <link rel="icon" type="image/png" sizes="96x96" href="${asset_dir}/favicons/favicon-96x96.png">
-    <link rel="icon" type="image/png" sizes="16x16" href="${asset_dir}/favicons/favicon-16x16.png">
-    <link rel="manifest" href="${asset_dir}/manifest.json">
-    <meta name="msapplication-TileColor" content="#ffffff">
-    <meta name="msapplication-TileImage" content="${asset_dir}/favicons/ms-icon-144x144.png">
-    <meta name="theme-color" content="#ffffff">
-
-    <!-- Preload images when applicable -->
-    <link rel="preload" as="image" href="${asset_dir}/images/wallpaper.webp">
-
-    <!-- Files from JSON (may be empty) -->
-    ${
-        ((!bundled && manifest?.css_paths)
-            ? manifest.css_paths.map(path => `<link rel="stylesheet" href="${path}">\n`)
-            : []).join('')
-    }
-    <!-- END Files from JSON -->
-</head>
-
-<body>
-    <script>window.puter_gui_enabled = true;</script>
-    ${
-        use_bundled_gui
-            ? `<script>window.gui_env = 'prod';</script>`
-            : ''
-    }
-    ${
-        ((!bundled && manifest?.lib_paths)
-            ? manifest.lib_paths.map(path => `<script type="text/javascript" src="${path}"></script>\n`)
-            : []).join('')
-    }
-
-    <script>
-    window.icons = {};
-
-    ${(() => {
-        if ( !(!bundled && manifest) ) return '';
-        const html = [];
-        fs_.readdirSync(path_.join(gui_path, 'src/icons')).forEach(file => {
-            // skip dotfiles
-            if(file.startsWith('.'))
-                return;
-            // load image
-            let buff = new Buffer.from(fs_.readFileSync(path_.join(gui_path, 'src/icons') + '/' + file));
-            // convert to base64
-            let base64data = buff.toString('base64');
-            // add to `window.icons`
-            if(file.endsWith('.png'))
-                html.push(`window.icons['${file}'] = "data:image/png;base64,${base64data}";\n`);
-            else if(file.endsWith('.svg'))
-                html.push(`window.icons['${file}'] = "data:image/svg+xml;base64,${base64data}";\n`);
-        })
-        return html.join('');
-    })()}
-    </script>
-
-    ${
-        ((!bundled && manifest?.js_paths)
-            ? manifest.js_paths.map(path => `<script type="module" src="${path}"></script>\n`)
-            : []).join('')
-    }
-    <!-- Load the GUI script -->
-    <script ${ !bundled ? ' type="module"' : ''} src="${(!bundled && manifest?.index) || '/dist/gui.js'}"></script>
-    <!-- Initialize GUI when document is loaded -->
-    <script>
-    window.addEventListener('load', function() {
-        gui(${
-            // TODO: override JSON.stringify to ALWAYS to this...
-            //       this should be an opt-OUT, not an opt-IN!
-            JSON.stringify(gui_params).replace(/</g, '\\u003c')
-        });
-    });
-    </script>
-    <div id="templates" style="display: none;"></div>
-</body>
-
-</html>`;
-};
-
-module.exports = {
-    generate_puter_page_html,
-};

+ 35 - 30
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]
+    }
 }