Spaces:
Sleeping
Sleeping
| /* | |
| * Copyright (C) 2024-present 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/>. | |
| */ | |
| // surrounding_box function | |
| // | |
| // It's really hard to see an error message without using | |
| // the surrounding_box function to highlight its location. | |
| // The implementation of this in packages/backend might not | |
| // work in older versions of node, so we instead re-implement | |
| // it here. | |
| import console from 'node:console'; | |
| import process from 'node:process'; | |
| try { | |
| await import('dotenv/config'); | |
| } catch (e) { | |
| // dotenv is optional | |
| } | |
| const surrounding_box = (col, lines) => { | |
| const lengths = lines.map(line => line.length); | |
| const max_length = Math.max(...lengths); | |
| const c = str => `\x1b[${col}m${str}\x1b[0m`; | |
| const bar = c(Array(max_length + 4).fill('━').join('')); | |
| for ( let i = 0 ; i < lines.length ; i++ ) { | |
| while ( lines[i].length < max_length ) { | |
| lines[i] += ' '; | |
| } | |
| lines[i] = `${c('┃ ')} ${lines[i]} ${c(' ┃')}`; | |
| } | |
| lines.unshift(`${c('┏')}${bar}${c('┓')}`); | |
| lines.push(`${c('┗')}${bar}${c('┛')}`); | |
| }; | |
| // node version check | |
| { | |
| // Keeping track of WHY certain versions don't work | |
| const ver_info = [ | |
| { under: 14, reasons: ['optional chaining is not available'] }, | |
| { under: 16, reasons: ['disk usage package ABI mismatch'] }, | |
| ]; | |
| const lowest_allowed = Math.max(...ver_info.map(r => r.under)); | |
| // ACTUAL VERSION CHECK | |
| const [major, minor] = process.versions.node.split('.').map(Number); | |
| if ( major < lowest_allowed ) { | |
| const lines = []; | |
| lines.push(`Please use a version of Node.js ${lowest_allowed} or newer.`); | |
| lines.push(`Issues with node ${process.versions.node}:`); | |
| // We also show the user the reasons in case they want to know | |
| for ( const { under, reasons } of ver_info ) { | |
| if ( major < under ) { | |
| lines.push(` - ${reasons.join(', ')}`); | |
| } | |
| } | |
| surrounding_box('31;1', lines); | |
| console.error(lines.join('\n')); | |
| process.exit(1); | |
| } | |
| } | |
| // Annoying polyfill for inconsistency in different node versions | |
| if ( ! import.meta.filename ) { | |
| Object.defineProperty(import.meta, 'filename', { | |
| get: () => import.meta.url.slice('file://'.length), | |
| }) | |
| } | |
| const main = async () => { | |
| const { | |
| Kernel, | |
| EssentialModules, | |
| DatabaseModule, | |
| LocalDiskStorageModule, | |
| MemoryStorageModule, | |
| SelfHostedModule, | |
| BroadcastModule, | |
| TestDriversModule, | |
| TestConfigModule, | |
| PuterAIModule, | |
| InternetModule, | |
| DevelopmentModule, | |
| DNSModule, | |
| PerfMonModule, | |
| } = (await import('@heyputer/backend')).default; | |
| const k = new Kernel({ | |
| entry_path: import.meta.filename | |
| }); | |
| for ( const mod of EssentialModules ) { | |
| k.add_module(new mod()); | |
| } | |
| k.add_module(new DatabaseModule()); | |
| k.add_module(new LocalDiskStorageModule()); | |
| k.add_module(new MemoryStorageModule()); | |
| k.add_module(new SelfHostedModule()); | |
| k.add_module(new BroadcastModule()); | |
| k.add_module(new TestDriversModule()); | |
| k.add_module(new TestConfigModule()); | |
| k.add_module(new PuterAIModule()); | |
| k.add_module(new InternetModule()); | |
| k.add_module(new DNSModule()); | |
| if ( process.env.PERFMON ) { | |
| k.add_module(new PerfMonModule()); | |
| } | |
| if ( process.env.UNSAFE_PUTER_DEV ) { | |
| k.add_module(new DevelopmentModule()); | |
| } | |
| k.boot(); | |
| }; | |
| const early_init_errors = [ | |
| { | |
| text: `Cannot find package '@heyputer/backend'`, | |
| notes: [ | |
| 'this usually happens if you forget `npm install`' | |
| ], | |
| suggestions: [ | |
| 'try running `npm install`' | |
| ], | |
| technical_notes: [ | |
| '@heyputer/backend is in an npm workspace' | |
| ] | |
| }, | |
| { | |
| text: `Cannot find package`, | |
| notes: [ | |
| 'this usually happens if you forget `npm install`' | |
| ], | |
| suggestions: [ | |
| 'try running `npm install`' | |
| ], | |
| }, | |
| { | |
| text: 'Cannot write to path', | |
| notes: [ | |
| 'this usually happens when /var/puter isn\'t chown\'d to the right UID' | |
| ], | |
| suggestions: [ | |
| 'check issue #645 on our github' | |
| ] | |
| } | |
| ]; | |
| // null coalescing operator | |
| const nco = (...args) => { | |
| for ( const arg of args ) { | |
| if ( arg !== undefined && arg !== null ) { | |
| return arg; | |
| } | |
| } | |
| return undefined; | |
| } | |
| const _print_error_help = (error_help) => { | |
| const lines = []; | |
| lines.push(nco(error_help.title, error_help.text)); | |
| for ( const note of (nco(error_help.notes, [])) ) { | |
| lines.push(`📝 ${note}`) | |
| } | |
| if ( error_help.suggestions ) { | |
| lines.push('Suggestions:'); | |
| for ( const suggestion of error_help.suggestions ) { | |
| lines.push(`- ${suggestion}`); | |
| } | |
| } | |
| if ( error_help.technical_notes ) { | |
| lines.push('Technical Notes:'); | |
| for ( const note of error_help.technical_notes ) { | |
| lines.push(`- ${note}`); | |
| } | |
| } | |
| surrounding_box('31;1', lines); | |
| console.error(lines.join('\n')); | |
| } | |
| (async () => { | |
| try { | |
| await main(); | |
| } catch (e) { | |
| for ( const error_help of early_init_errors ) { | |
| const message = e && e.message; | |
| if ( e.message && e.message.includes(error_help.text) ) { | |
| _print_error_help(error_help); | |
| break; | |
| } | |
| } | |
| throw e; | |
| } | |
| })(); | |