puter_page_loader.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. /*
  2. * Copyright (C) 2024 Puter Technologies Inc.
  3. *
  4. * This file is part of Puter.
  5. *
  6. * Puter is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License as published
  8. * by the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Affero General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public License
  17. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  18. */
  19. const {encode} = require('html-entities');
  20. const path_ = require('path');
  21. const fs_ = require('fs');
  22. const generate_puter_page_html = ({
  23. env,
  24. manifest,
  25. gui_path,
  26. use_bundled_gui,
  27. app_origin,
  28. api_origin,
  29. meta,
  30. gui_params,
  31. }) => {
  32. const e = encode;
  33. const {
  34. title,
  35. description,
  36. short_description,
  37. company,
  38. canonical_url,
  39. } = meta;
  40. gui_params = {
  41. ...meta,
  42. ...gui_params,
  43. app_origin,
  44. api_origin,
  45. gui_origin: app_origin,
  46. };
  47. const asset_dir = env === 'dev'
  48. ? '/src' : '/dist' ;
  49. // const asset_dir = '/dist';
  50. const bundled = env != 'dev' || use_bundled_gui;
  51. return `<!DOCTYPE html>
  52. <html lang="en">
  53. <head>
  54. <title>${e(title)}</title>
  55. <meta name="author" content="${e(company)}">
  56. <meta name="description" content="${e((description).replace(/\n/g, " "))}">
  57. <meta name="facebook-domain-verification" content="e29w3hjbnnnypf4kzk2cewcdaxym1y" />
  58. <link rel="canonical" href="${e(canonical_url)}">
  59. <!-- Meta meta tags -->
  60. <meta property="og:url" content="${app_origin}">
  61. <meta property="og:type" content="website">
  62. <meta property="og:title" content="${e(title)}">
  63. <meta property="og:description" content="${e((short_description).replace(/\n/g, " "))}">
  64. <meta property="og:image" content="${asset_dir}/images/screenshot.png">
  65. <!-- Twitter meta tags -->
  66. <meta name="twitter:card" content="summary_large_image">
  67. <meta property="twitter:domain" content="puter.com">
  68. <meta property="twitter:url" content="${app_origin}">
  69. <meta name="twitter:title" content="${e(title)}">
  70. <meta name="twitter:description" content="${e((short_description).replace(/\n/g, " "))}">
  71. <meta name="twitter:image" content="${asset_dir}/images/screenshot.png">
  72. <!-- favicons -->
  73. <link rel="apple-touch-icon" sizes="57x57" href="${asset_dir}/favicons/apple-icon-57x57.png">
  74. <link rel="apple-touch-icon" sizes="60x60" href="${asset_dir}/favicons/apple-icon-60x60.png">
  75. <link rel="apple-touch-icon" sizes="72x72" href="${asset_dir}/favicons/apple-icon-72x72.png">
  76. <link rel="apple-touch-icon" sizes="76x76" href="${asset_dir}/favicons/apple-icon-76x76.png">
  77. <link rel="apple-touch-icon" sizes="114x114" href="${asset_dir}/favicons/apple-icon-114x114.png">
  78. <link rel="apple-touch-icon" sizes="120x120" href="${asset_dir}/favicons/apple-icon-120x120.png">
  79. <link rel="apple-touch-icon" sizes="144x144" href="${asset_dir}/favicons/apple-icon-144x144.png">
  80. <link rel="apple-touch-icon" sizes="152x152" href="${asset_dir}/favicons/apple-icon-152x152.png">
  81. <link rel="apple-touch-icon" sizes="180x180" href="${asset_dir}/favicons/apple-icon-180x180.png">
  82. <link rel="icon" type="image/png" sizes="192x192" href="${asset_dir}/favicons/android-icon-192x192.png">
  83. <link rel="icon" type="image/png" sizes="32x32" href="${asset_dir}/favicons/favicon-32x32.png">
  84. <link rel="icon" type="image/png" sizes="96x96" href="${asset_dir}/favicons/favicon-96x96.png">
  85. <link rel="icon" type="image/png" sizes="16x16" href="${asset_dir}/favicons/favicon-16x16.png">
  86. <link rel="manifest" href="${asset_dir}/manifest.json">
  87. <meta name="msapplication-TileColor" content="#ffffff">
  88. <meta name="msapplication-TileImage" content="${asset_dir}/favicons/ms-icon-144x144.png">
  89. <meta name="theme-color" content="#ffffff">
  90. <!-- Preload images when applicable -->
  91. <link rel="preload" as="image" href="${asset_dir}/images/wallpaper.webp">
  92. <!-- Files from JSON (may be empty) -->
  93. ${
  94. ((!bundled && manifest?.css_paths)
  95. ? manifest.css_paths.map(path => `<link rel="stylesheet" href="${path}">\n`)
  96. : []).join('')
  97. }
  98. <!-- END Files from JSON -->
  99. </head>
  100. <body>
  101. <script>window.puter_gui_enabled = true;</script>
  102. ${
  103. use_bundled_gui
  104. ? `<script>window.gui_env = 'prod';</script>`
  105. : ''
  106. }
  107. ${
  108. ((!bundled && manifest?.lib_paths)
  109. ? manifest.lib_paths.map(path => `<script type="text/javascript" src="${path}"></script>\n`)
  110. : []).join('')
  111. }
  112. <script>
  113. window.icons = {};
  114. ${(() => {
  115. if ( !(!bundled && manifest) ) return '';
  116. const html = [];
  117. fs_.readdirSync(path_.join(gui_path, 'src/icons')).forEach(file => {
  118. // skip dotfiles
  119. if(file.startsWith('.'))
  120. return;
  121. // load image
  122. let buff = new Buffer.from(fs_.readFileSync(path_.join(gui_path, 'src/icons') + '/' + file));
  123. // convert to base64
  124. let base64data = buff.toString('base64');
  125. // add to `window.icons`
  126. if(file.endsWith('.png'))
  127. html.push(`window.icons['${file}'] = "data:image/png;base64,${base64data}";\n`);
  128. else if(file.endsWith('.svg'))
  129. html.push(`window.icons['${file}'] = "data:image/svg+xml;base64,${base64data}";\n`);
  130. })
  131. return html.join('');
  132. })()}
  133. </script>
  134. ${
  135. ((!bundled && manifest?.js_paths)
  136. ? manifest.js_paths.map(path => `<script type="module" src="${path}"></script>\n`)
  137. : []).join('')
  138. }
  139. <!-- Load the GUI script -->
  140. <script ${ !bundled ? ' type="module"' : ''} src="${(!bundled && manifest?.index) || '/dist/gui.js'}"></script>
  141. <!-- Initialize GUI when document is loaded -->
  142. <script>
  143. window.addEventListener('load', function() {
  144. gui(${
  145. // TODO: override JSON.stringify to ALWAYS to this...
  146. // this should be an opt-OUT, not an opt-IN!
  147. JSON.stringify(gui_params).replace(/</g, '\\u003c')
  148. });
  149. });
  150. </script>
  151. <div id="templates" style="display: none;"></div>
  152. </body>
  153. </html>`;
  154. };
  155. module.exports = {
  156. generate_puter_page_html,
  157. };