2017-10-14 23:31:44 -04:00
# ! / u s r / b i n / e n v n o d e
2024-07-18 22:58:12 +03:00
import app from "./app.js" ;
import sessionParser from "./routes/session_parser.js" ;
import fs from "fs" ;
import http from "http" ;
import https from "https" ;
import config from "./services/config.js" ;
import log from "./services/log.js" ;
import appInfo from "./services/app_info.js" ;
import ws from "./services/ws.js" ;
import utils from "./services/utils.js" ;
import port from "./services/port.js" ;
import host from "./services/host.js" ;
import semver from "semver" ;
2017-10-14 23:31:44 -04:00
2023-05-07 15:23:46 +02:00
// setup basic error handling even before requiring dependencies, since those can produce errors as well
2024-07-18 22:58:12 +03:00
process . on ( 'unhandledRejection' , ( error : Error ) = > {
2017-10-22 22:56:42 -04:00
// this makes sure that stacktrace of failed promise is printed out
2017-10-26 19:16:36 -04:00
console . log ( error ) ;
2017-10-25 22:39:21 -04:00
// but also try to log it into file
2024-07-18 22:58:12 +03:00
log . info ( error ) ;
2017-10-22 22:56:42 -04:00
} ) ;
2017-10-14 23:31:44 -04:00
2021-04-25 11:05:09 +02:00
function exit() {
console . log ( "Caught interrupt/termination signal. Exiting." ) ;
process . exit ( 0 ) ;
}
process . on ( 'SIGINT' , exit ) ;
process . on ( 'SIGTERM' , exit ) ;
2019-05-28 13:33:03 +03:00
2019-01-26 19:59:51 +01:00
if ( ! semver . satisfies ( process . version , ">=10.5.0" ) ) {
console . error ( "Trilium only supports node.js 10.5 and later" ) ;
process . exit ( 1 ) ;
}
2017-10-14 23:31:44 -04:00
2023-05-07 15:23:46 +02:00
startTrilium ( ) ;
2017-10-14 23:31:44 -04:00
2024-07-18 23:24:36 +03:00
async function startTrilium() {
2022-08-02 22:53:06 +02:00
/ * *
* The intended behavior is to detect when a second instance is running , in that case open the old instance
* instead of the new one . This is complicated by the fact that it is possible to run multiple instances of Trilium
2023-06-30 11:18:34 +02:00
* if port and data dir are configured separately . This complication is the source of the following weird usage .
2022-08-02 22:53:06 +02:00
*
2024-04-02 23:55:02 +03:00
* The line below makes sure that the "second-instance" ( process in window . ts ) is fired . Normally it returns a boolean
2022-08-02 22:53:06 +02:00
* indicating whether another instance is running or not , but we ignore that and kill the app only based on the port conflict .
*
* A bit weird is that "second-instance" is triggered also on the valid usecases ( different port / data dir ) and
* focuses the existing window . But the new process is start as well and will steal the focus too , it will win , because
2023-05-07 15:23:46 +02:00
* its startup is slower than focusing the existing process / window . So in the end , it works out without having
* to do a complex evaluation .
2022-08-02 22:53:06 +02:00
* /
2022-08-09 21:34:18 +02:00
if ( utils . isElectron ( ) ) {
2024-07-18 23:24:36 +03:00
( await import ( 'electron' ) ) . app . requestSingleInstanceLock ( ) ;
2022-08-09 21:34:18 +02:00
}
2022-08-02 22:53:06 +02:00
2023-05-07 15:23:46 +02:00
log . info ( JSON . stringify ( appInfo , null , 2 ) ) ;
2024-07-18 23:24:36 +03:00
const cpuInfos = ( await import ( 'os' ) ) . cpus ( ) ;
2023-12-03 21:37:50 +01:00
if ( cpuInfos && cpuInfos [ 0 ] !== undefined ) { // https://github.com/zadam/trilium/pull/3957
log . info ( ` CPU model: ${ cpuInfos [ 0 ] . model } , logical cores: ${ cpuInfos . length } freq: ${ cpuInfos [ 0 ] . speed } Mhz ` ) ; // for perf. issues it's good to know the rough configuration
}
2023-05-07 15:23:46 +02:00
const httpServer = startHttpServer ( ) ;
2024-04-11 23:14:37 +03:00
ws . init ( httpServer , sessionParser as any ) ; // TODO: Not sure why session parser is incompatible.
2023-05-07 15:23:46 +02:00
if ( utils . isElectron ( ) ) {
2024-07-24 20:33:35 +03:00
const electronRouting = await import ( './routes/electron.js' ) ;
2024-07-18 23:24:36 +03:00
electronRouting . default ( app ) ;
2023-05-07 15:23:46 +02:00
}
}
function startHttpServer() {
2022-08-03 00:19:29 +02:00
app . set ( 'port' , port ) ;
app . set ( 'host' , host ) ;
2017-11-16 23:06:16 -05:00
2022-08-04 00:19:54 +02:00
// Check from config whether to trust reverse proxies to supply user IPs, hostnames and protocols
if ( config [ 'Network' ] [ 'trustedReverseProxy' ] ) {
if ( config [ 'Network' ] [ 'trustedReverseProxy' ] === true || config [ 'Network' ] [ 'trustedReverseProxy' ] . trim ( ) . length ) {
app . set ( 'trust proxy' , config [ 'Network' ] [ 'trustedReverseProxy' ] )
}
}
2023-05-07 15:23:46 +02:00
2022-12-21 15:19:05 +01:00
log . info ( ` Trusted reverse proxy: ${ app . get ( 'trust proxy' ) } ` )
2022-08-04 00:19:54 +02:00
2023-05-07 15:23:46 +02:00
let httpServer ;
2018-07-31 19:50:18 +02:00
if ( config [ 'Network' ] [ 'https' ] ) {
2019-04-08 20:08:29 +02:00
if ( ! config [ 'Network' ] [ 'keyPath' ] || ! config [ 'Network' ] [ 'keyPath' ] . trim ( ) . length ) {
throw new Error ( "keyPath in config.ini is required when https=true, but it's empty" ) ;
}
if ( ! config [ 'Network' ] [ 'certPath' ] || ! config [ 'Network' ] [ 'certPath' ] . trim ( ) . length ) {
throw new Error ( "certPath in config.ini is required when https=true, but it's empty" ) ;
}
2018-07-31 19:50:18 +02:00
const options = {
key : fs.readFileSync ( config [ 'Network' ] [ 'keyPath' ] ) ,
cert : fs.readFileSync ( config [ 'Network' ] [ 'certPath' ] )
} ;
2017-11-21 00:25:53 -05:00
2018-07-31 19:50:18 +02:00
httpServer = https . createServer ( options , app ) ;
2017-10-14 23:31:44 -04:00
2022-12-21 15:19:05 +01:00
log . info ( ` App HTTPS server starting up at port ${ port } ` ) ;
2023-05-07 15:23:46 +02:00
} else {
2018-07-31 19:50:18 +02:00
httpServer = http . createServer ( app ) ;
2017-11-25 17:43:05 -05:00
2022-12-21 15:19:05 +01:00
log . info ( ` App HTTP server starting up at port ${ port } ` ) ;
2018-07-31 19:50:18 +02:00
}
2017-10-14 23:31:44 -04:00
2018-07-31 19:50:18 +02:00
/ * *
* Listen on provided port , on all network interfaces .
* /
2017-10-14 23:31:44 -04:00
2018-07-31 19:50:18 +02:00
httpServer . keepAliveTimeout = 120000 * 5 ;
2023-03-12 21:37:13 +01:00
const listenOnTcp = port !== 0 ;
2023-05-07 15:23:46 +02:00
2023-03-12 21:37:13 +01:00
if ( listenOnTcp ) {
2023-05-07 15:23:46 +02:00
httpServer . listen ( port , host ) ; // TCP socket.
2023-03-11 20:21:32 +11:00
} else {
2023-05-07 15:23:46 +02:00
httpServer . listen ( host ) ; // Unix socket.
2023-03-11 20:21:32 +11:00
}
2023-05-07 15:23:46 +02:00
2019-12-04 22:59:26 +01:00
httpServer . on ( 'error' , error = > {
2024-08-31 23:38:30 +03:00
let message = error . stack || "An unexpected error has occurred." ;
2024-04-02 23:55:02 +03:00
// handle specific listen errors with friendly messages
2024-04-11 23:14:37 +03:00
if ( "code" in error ) {
switch ( error . code ) {
case 'EACCES' :
2024-08-31 23:28:45 +03:00
message = ` Port ${ port } requires elevated privileges. It's recommended to use port above 1024. ` ;
break ;
2024-04-11 23:14:37 +03:00
case 'EADDRINUSE' :
2024-08-31 23:28:45 +03:00
message = ` Port ${ port } is already in use. Most likely, another Trilium process is already running. You might try to find it, kill it, and try again. ` ;
break ;
2024-08-31 23:38:30 +03:00
case 'EADDRNOTAVAIL' :
message = ` Unable to start the server on host ' ${ host } '. Make sure the host (defined in 'config.ini' or via the 'TRILIUM_HOST' environment variable) is an IP address that can be listened on. ` ;
break ;
2024-04-11 23:14:37 +03:00
}
2019-12-04 22:59:26 +01:00
}
2024-04-11 23:14:37 +03:00
2024-08-31 23:28:45 +03:00
if ( utils . isElectron ( ) ) {
2024-09-03 08:23:05 +00:00
import ( "electron" ) . then ( ( { app , dialog } ) = > {
// Not all situations require showing an error dialog. When Trilium is already open,
// clicking the shortcut, the software icon, or the taskbar icon, or when creating a new window,
// should simply focus on the existing window or open a new one, without displaying an error message.
if ( "code" in error && error . code == 'EADDRINUSE' ) {
const isNewWindow = process . argv . includes ( '--new-window' ) ;
if ( isNewWindow || ! app . requestSingleInstanceLock ( ) ) {
console . error ( message ) ;
process . exit ( 1 ) ;
}
}
2024-08-31 23:28:45 +03:00
dialog . showErrorBox ( "Error while initializing the server" , message ) ;
process . exit ( 1 ) ;
} ) ;
} else {
console . error ( message ) ;
process . exit ( 1 ) ;
}
} ) ;
2020-06-20 12:31:38 +02:00
2023-03-11 20:57:16 +11:00
httpServer . on ( 'listening' , ( ) = > {
2023-03-12 21:37:13 +01:00
if ( listenOnTcp ) {
2023-03-11 20:57:16 +11:00
log . info ( ` Listening on port ${ port } ` )
} else {
log . info ( ` Listening on unix socket ${ host } ` )
}
} ) ;
2017-10-14 23:31:44 -04:00
2023-05-07 15:23:46 +02:00
return httpServer ;
2017-10-14 23:31:44 -04:00
}