2022-02-27 03:07:39 +01:00
import * as fs from 'node:fs' ;
import { fileURLToPath } from 'node:url' ;
import { dirname } from 'node:path' ;
import * as os from 'node:os' ;
import cluster from 'node:cluster' ;
import chalk from 'chalk' ;
import chalkTemplate from 'chalk-template' ;
2022-03-19 17:34:45 +01:00
import semver from 'semver' ;
2019-04-07 14:50:36 +02:00
2022-02-27 03:07:39 +01:00
import Logger from '@/services/logger.js' ;
import loadConfig from '@/config/load.js' ;
import { Config } from '@/config/types.js' ;
import { lessThan } from '@/prelude/array.js' ;
import { envOption } from '../env.js' ;
import { showMachineInfo } from '@/misc/show-machine-info.js' ;
2022-03-26 07:34:00 +01:00
import { db , initDb } from '../db/postgre.js' ;
2021-08-20 14:34:56 +02:00
2022-02-27 03:07:39 +01:00
const _filename = fileURLToPath ( import . meta . url ) ;
2021-08-20 14:34:56 +02:00
const _dirname = dirname ( _filename ) ;
2021-11-11 18:02:25 +01:00
const meta = JSON . parse ( fs . readFileSync ( ` ${ _dirname } /../../../../built/meta.json ` , 'utf-8' ) ) ;
2019-04-07 14:50:36 +02:00
const logger = new Logger ( 'core' , 'cyan' ) ;
const bootLogger = logger . createSubLogger ( 'boot' , 'magenta' , false ) ;
2022-07-19 08:07:18 +02:00
const themeColor = chalk . hex ( '#31748f' ) ;
2022-02-27 03:07:39 +01:00
2019-11-24 09:11:53 +01:00
function greet() {
2021-10-08 14:24:05 +02:00
if ( ! envOption . quiet ) {
2022-07-19 19:13:17 +02:00
//#region Calckey logo
2019-11-24 09:11:53 +01:00
const v = ` v ${ meta . version } ` ;
2022-07-19 08:07:18 +02:00
console . log ( themeColor ( ' ___ _ _ ' ) ) ;
console . log ( themeColor ( ' / __\\__ _| | ___| | _____ _ _ ' ) ) ;
console . log ( themeColor ( ' / / / _` | |/ __| |/ / _ \ | | |' ) ) ;
console . log ( themeColor ( '/ /__| (_| | | (__| < __/ |_| |' ) ) ;
console . log ( themeColor ( '\\____/\\__,_|_|\\___|_|\\_\\___|\\__, |' ) ) ;
2022-07-19 20:18:44 +02:00
console . log ( themeColor ( ' (___/ ' ) ) ;
2019-04-07 14:50:36 +02:00
//#endregion
2022-07-19 19:18:05 +02:00
console . log ( ' Calckey is an open-source decentralized microblogging platform.' ) ;
2022-07-19 08:07:18 +02:00
console . log ( chalk . rgb ( 255 , 136 , 0 ) ( ' If you like Calckey, please consider starring or contributing to the repo. https://codeberg.org/thatonecalculator/calckey' ) ) ;
2019-04-07 14:50:36 +02:00
console . log ( '' ) ;
2022-02-27 03:07:39 +01:00
console . log ( chalkTemplate ` --- ${ os . hostname ( ) } {gray (PID: ${ process . pid . toString ( ) } )} --- ` ) ;
2019-04-07 14:50:36 +02:00
}
2022-07-19 19:18:05 +02:00
bootLogger . info ( 'Welcome to Calckey!' ) ;
bootLogger . info ( ` Calckey v ${ meta . version } ` , null , true ) ;
2019-04-07 14:50:36 +02:00
}
/ * *
* Init master process
* /
export async function masterMain() {
2019-04-12 18:43:22 +02:00
let config ! : Config ;
2019-04-07 14:50:36 +02:00
2021-04-24 15:55:18 +02:00
// initialize app
2019-04-07 14:50:36 +02:00
try {
2019-11-24 09:11:53 +01:00
greet ( ) ;
2021-04-24 15:55:18 +02:00
showEnvironment ( ) ;
await showMachineInfo ( bootLogger ) ;
showNodejsVersion ( ) ;
config = loadConfigBoot ( ) ;
await connectDb ( ) ;
2019-04-07 14:50:36 +02:00
} catch ( e ) {
bootLogger . error ( 'Fatal error occurred during initialization' , null , true ) ;
process . exit ( 1 ) ;
}
2022-07-19 19:18:05 +02:00
bootLogger . succ ( 'Calckey initialized' ) ;
2019-04-07 14:50:36 +02:00
2021-10-08 14:24:05 +02:00
if ( ! envOption . disableClustering ) {
2019-04-07 14:50:36 +02:00
await spawnWorkers ( config . clusterLimit ) ;
}
2020-04-26 04:39:15 +02:00
bootLogger . succ ( ` Now listening on port ${ config . port } on ${ config . url } ` , null , true ) ;
2021-10-08 14:24:05 +02:00
if ( ! envOption . noDaemons ) {
2022-02-27 03:07:39 +01:00
import ( '../daemons/server-stats.js' ) . then ( x = > x . default ( ) ) ;
import ( '../daemons/queue-stats.js' ) . then ( x = > x . default ( ) ) ;
import ( '../daemons/janitor.js' ) . then ( x = > x . default ( ) ) ;
2019-04-07 14:50:36 +02:00
}
}
function showEnvironment ( ) : void {
const env = process . env . NODE_ENV ;
const logger = bootLogger . createSubLogger ( 'env' ) ;
2020-04-04 01:46:54 +02:00
logger . info ( typeof env === 'undefined' ? 'NODE_ENV is not set' : ` NODE_ENV: ${ env } ` ) ;
2019-04-07 14:50:36 +02:00
if ( env !== 'production' ) {
logger . warn ( 'The environment is not in production mode.' ) ;
logger . warn ( 'DO NOT USE FOR PRODUCTION PURPOSE!' , null , true ) ;
}
}
2021-04-24 15:55:18 +02:00
function showNodejsVersion ( ) : void {
2019-04-07 14:50:36 +02:00
const nodejsLogger = bootLogger . createSubLogger ( 'nodejs' ) ;
2022-03-19 17:34:45 +01:00
nodejsLogger . info ( ` Version ${ process . version } detected. ` ) ;
2019-04-07 14:50:36 +02:00
2022-03-19 17:34:45 +01:00
const minVersion = fs . readFileSync ( ` ${ _dirname } /../../../../.node-version ` , 'utf-8' ) . trim ( ) ;
if ( semver . lt ( process . version , minVersion ) ) {
nodejsLogger . error ( ` At least Node.js ${ minVersion } required! ` ) ;
2019-04-07 14:50:36 +02:00
process . exit ( 1 ) ;
}
2021-04-24 15:55:18 +02:00
}
2019-04-07 14:50:36 +02:00
2021-04-24 15:55:18 +02:00
function loadConfigBoot ( ) : Config {
2019-04-07 14:50:36 +02:00
const configLogger = bootLogger . createSubLogger ( 'config' ) ;
let config ;
try {
config = loadConfig ( ) ;
} catch ( exception ) {
if ( exception . code === 'ENOENT' ) {
configLogger . error ( 'Configuration file not found' , null , true ) ;
process . exit ( 1 ) ;
2022-08-04 11:00:02 +02:00
} else if ( e instanceof Error ) {
configLogger . error ( e . message ) ;
process . exit ( 1 ) ;
2019-04-07 14:50:36 +02:00
}
throw exception ;
}
configLogger . succ ( 'Loaded' ) ;
2021-04-24 15:55:18 +02:00
return config ;
}
async function connectDb ( ) : Promise < void > {
2020-04-26 04:39:15 +02:00
const dbLogger = bootLogger . createSubLogger ( 'db' ) ;
2019-04-07 14:50:36 +02:00
// Try to connect to DB
try {
2020-04-26 04:39:15 +02:00
dbLogger . info ( 'Connecting...' ) ;
2019-04-07 14:50:36 +02:00
await initDb ( ) ;
2022-03-26 07:34:00 +01:00
const v = await db . query ( 'SHOW server_version' ) . then ( x = > x [ 0 ] . server_version ) ;
2020-04-26 04:39:15 +02:00
dbLogger . succ ( ` Connected: v ${ v } ` ) ;
2019-04-07 14:50:36 +02:00
} catch ( e ) {
2020-04-26 04:39:15 +02:00
dbLogger . error ( 'Cannot connect' , null , true ) ;
dbLogger . error ( e ) ;
2019-04-07 14:50:36 +02:00
process . exit ( 1 ) ;
}
2021-04-24 15:55:18 +02:00
}
2019-04-07 14:50:36 +02:00
2019-08-26 22:33:24 +02:00
async function spawnWorkers ( limit : number = 1 ) {
2019-04-07 14:50:36 +02:00
const workers = Math . min ( limit , os . cpus ( ) . length ) ;
bootLogger . info ( ` Starting ${ workers } worker ${ workers === 1 ? '' : 's' } ... ` ) ;
await Promise . all ( [ . . . Array ( workers ) ] . map ( spawnWorker ) ) ;
bootLogger . succ ( 'All workers started' ) ;
}
function spawnWorker ( ) : Promise < void > {
return new Promise ( res = > {
const worker = cluster . fork ( ) ;
worker . on ( 'message' , message = > {
2022-05-19 04:49:07 +02:00
if ( message === 'listenFailed' ) {
bootLogger . error ( ` The server Listen failed due to the previous error. ` ) ;
process . exit ( 1 ) ;
}
2019-04-07 14:50:36 +02:00
if ( message !== 'ready' ) return ;
res ( ) ;
} ) ;
} ) ;
}