2023-09-01 09:20:00 +01:00
using System ;
2022-10-06 05:08:10 +01:00
using System.Collections.Generic ;
2022-10-06 17:16:07 +01:00
using System.Diagnostics ;
2022-10-06 05:08:10 +01:00
using System.Drawing ;
2022-10-08 09:23:11 +01:00
using System.IO ;
2022-10-06 05:08:10 +01:00
using System.Linq ;
2023-02-20 18:50:25 +00:00
using System.Net ;
2023-02-26 09:56:01 +00:00
using System.Net.Sockets ;
2022-10-06 05:08:10 +01:00
using System.Reflection ;
2022-10-07 11:38:14 +01:00
using System.Runtime.InteropServices ;
2023-02-27 10:52:19 +00:00
using System.Threading ;
2022-10-06 05:08:10 +01:00
using System.Threading.Tasks ;
using System.Windows.Forms ;
2023-02-20 12:26:53 +00:00
using Microsoft.Win32 ;
2023-02-20 18:50:25 +00:00
using Newtonsoft.Json ;
2022-10-06 05:08:10 +01:00
using UptimeKuma.Properties ;
namespace UptimeKuma {
static class Program {
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
2022-10-07 11:38:14 +01:00
static void Main ( string [ ] args ) {
2023-02-27 10:45:54 +00:00
var cwd = Path . GetDirectoryName ( Application . ExecutablePath ) ;
if ( cwd ! = null ) {
Environment . CurrentDirectory = cwd ;
}
2023-02-27 10:48:11 +00:00
2023-09-01 09:20:00 +01:00
bool isIntranet = args . Contains ( "--intranet" ) ;
if ( isIntranet ) {
Console . WriteLine ( "The --intranet argument was provided, so we will not try to access the internet. The first time this application runs you'll need to run it without the --intranet param or copy the result from another machine to the intranet server." ) ;
}
2022-10-06 05:08:10 +01:00
Application . EnableVisualStyles ( ) ;
Application . SetCompatibleTextRenderingDefault ( false ) ;
2023-09-01 09:20:00 +01:00
Application . Run ( new UptimeKumaApplicationContext ( isIntranet ) ) ;
2022-10-06 05:08:10 +01:00
}
}
public class UptimeKumaApplicationContext : ApplicationContext
{
2023-02-27 10:52:19 +00:00
private static Mutex mutex = null ;
2023-02-20 12:26:53 +00:00
const string appName = "Uptime Kuma" ;
2022-10-06 05:08:10 +01:00
private NotifyIcon trayIcon ;
2022-10-06 17:16:07 +01:00
private Process process ;
2022-10-06 05:08:10 +01:00
2023-02-26 09:56:01 +00:00
private MenuItem statusMenuItem ;
2023-02-20 12:26:53 +00:00
private MenuItem runWhenStarts ;
2023-02-27 10:48:11 +00:00
private MenuItem openMenuItem ;
2023-02-20 12:26:53 +00:00
private RegistryKey registryKey = Registry . CurrentUser . OpenSubKey ( "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run" , true ) ;
2023-09-01 09:20:00 +01:00
private readonly bool intranetOnly ;
2023-02-26 09:56:01 +00:00
2023-09-01 09:20:00 +01:00
public UptimeKumaApplicationContext ( bool intranetOnly ) {
2023-02-27 10:52:19 +00:00
// Single instance only
bool createdNew ;
mutex = new Mutex ( true , appName , out createdNew ) ;
if ( ! createdNew ) {
return ;
}
2023-09-01 09:20:00 +01:00
this . intranetOnly = intranetOnly ;
2023-02-26 09:56:01 +00:00
var startingText = "Starting server..." ;
2022-10-06 05:08:10 +01:00
trayIcon = new NotifyIcon ( ) ;
2023-02-26 09:56:01 +00:00
trayIcon . Text = startingText ;
2022-10-06 05:08:10 +01:00
2023-02-20 12:26:53 +00:00
runWhenStarts = new MenuItem ( "Run when system starts" , RunWhenStarts ) ;
runWhenStarts . Checked = registryKey . GetValue ( appName ) ! = null ;
2023-02-26 09:56:01 +00:00
statusMenuItem = new MenuItem ( startingText ) ;
statusMenuItem . Enabled = false ;
2023-02-27 10:48:11 +00:00
openMenuItem = new MenuItem ( "Open" , Open ) ;
openMenuItem . Enabled = false ;
2022-10-06 05:08:10 +01:00
trayIcon . Icon = Icon . ExtractAssociatedIcon ( Assembly . GetExecutingAssembly ( ) . Location ) ;
trayIcon . ContextMenu = new ContextMenu ( new MenuItem [ ] {
2023-02-26 09:56:01 +00:00
statusMenuItem ,
2023-02-27 10:48:11 +00:00
openMenuItem ,
2022-10-07 11:38:14 +01:00
//new("Debug Console", DebugConsole),
2023-02-20 12:26:53 +00:00
runWhenStarts ,
2022-10-07 11:38:14 +01:00
new ( "Check for Update..." , CheckForUpdate ) ,
new ( "Visit GitHub..." , VisitGitHub ) ,
new ( "About" , About ) ,
new ( "Exit" , Exit ) ,
2022-10-06 05:08:10 +01:00
} ) ;
2022-10-07 11:38:14 +01:00
trayIcon . MouseDoubleClick + = new MouseEventHandler ( Open ) ;
2022-10-06 05:08:10 +01:00
trayIcon . Visible = true ;
2022-10-06 17:16:07 +01:00
2023-02-20 18:50:25 +00:00
var hasUpdateFile = File . Exists ( "update" ) ;
if ( ! hasUpdateFile & & Directory . Exists ( "core" ) & & Directory . Exists ( "node" ) & & Directory . Exists ( "core/node_modules" ) & & Directory . Exists ( "core/dist" ) ) {
2022-10-08 09:23:11 +01:00
// Go go go
StartProcess ( ) ;
} else {
DownloadFiles ( ) ;
}
}
void DownloadFiles ( ) {
2023-09-01 09:20:00 +01:00
if ( intranetOnly ) {
return ;
}
2022-10-08 09:23:11 +01:00
var form = new DownloadForm ( ) ;
form . Closed + = Exit ;
form . Show ( ) ;
}
2023-02-20 12:26:53 +00:00
private void RunWhenStarts ( object sender , EventArgs e ) {
if ( registryKey = = null ) {
MessageBox . Show ( "Error: Unable to set startup registry key." ) ;
return ;
}
if ( runWhenStarts . Checked ) {
registryKey . DeleteValue ( appName , false ) ;
runWhenStarts . Checked = false ;
} else {
registryKey . SetValue ( appName , Application . ExecutablePath ) ;
runWhenStarts . Checked = true ;
}
}
2022-10-08 09:23:11 +01:00
void StartProcess ( ) {
2022-10-07 11:38:14 +01:00
var startInfo = new ProcessStartInfo {
FileName = "node/node.exe" ,
Arguments = "server/server.js --data-dir=\"../data/\"" ,
RedirectStandardOutput = false ,
RedirectStandardError = false ,
UseShellExecute = false ,
CreateNoWindow = true ,
WorkingDirectory = "core"
} ;
2022-10-06 17:16:07 +01:00
process = new Process ( ) ;
process . StartInfo = startInfo ;
process . EnableRaisingEvents = true ;
2022-10-08 09:23:11 +01:00
process . Exited + = ProcessExited ;
2022-10-07 11:38:14 +01:00
2022-10-06 17:16:07 +01:00
try {
process . Start ( ) ;
2022-10-07 11:38:14 +01:00
//Open(null, null);
2023-02-26 09:56:01 +00:00
// Async task to check if the server is ready
Task . Run ( ( ) = > {
var runningText = "Server is running" ;
using TcpClient tcpClient = new TcpClient ( ) ;
while ( true ) {
try {
tcpClient . Connect ( "127.0.0.1" , 3001 ) ;
statusMenuItem . Text = runningText ;
2023-02-27 10:48:11 +00:00
openMenuItem . Enabled = true ;
2023-02-26 09:56:01 +00:00
trayIcon . Text = runningText ;
break ;
} catch ( Exception ) {
System . Threading . Thread . Sleep ( 2000 ) ;
}
}
} ) ;
2022-10-06 17:16:07 +01:00
} catch ( Exception e ) {
MessageBox . Show ( "Startup failed: " + e . Message , "Uptime Kuma Error" ) ;
}
2022-10-06 05:08:10 +01:00
}
2023-02-20 18:50:25 +00:00
void StopProcess ( ) {
process ? . Kill ( ) ;
}
2022-10-06 17:16:07 +01:00
void Open ( object sender , EventArgs e ) {
Process . Start ( "http://localhost:3001" ) ;
}
2022-10-07 11:38:14 +01:00
void DebugConsole ( object sender , EventArgs e ) {
}
2022-10-06 17:16:07 +01:00
void CheckForUpdate ( object sender , EventArgs e ) {
2023-09-01 09:20:00 +01:00
if ( intranetOnly ) {
return ;
}
2023-02-20 18:50:25 +00:00
// Check version.json exists
if ( File . Exists ( "version.json" ) ) {
// Load version.json and compare with the latest version from GitHub
var currentVersionObj = JsonConvert . DeserializeObject < Version > ( File . ReadAllText ( "version.json" ) ) ;
var versionJson = new WebClient ( ) . DownloadString ( "https://uptime.kuma.pet/version" ) ;
var latestVersionObj = JsonConvert . DeserializeObject < Version > ( versionJson ) ;
// Compare version, if the latest version is newer, then update
if ( new System . Version ( latestVersionObj . latest ) . CompareTo ( new System . Version ( currentVersionObj . latest ) ) > 0 ) {
var result = MessageBox . Show ( "A new version is available. Do you want to update?" , "Update" , MessageBoxButtons . YesNo ) ;
if ( result = = DialogResult . Yes ) {
// Create a empty file `update`, so the app will download the core files again at startup
File . Create ( "update" ) . Close ( ) ;
trayIcon . Visible = false ;
process ? . Kill ( ) ;
// Restart the app, it will download the core files again at startup
Application . Restart ( ) ;
}
} else {
MessageBox . Show ( "You are using the latest version." ) ;
}
}
2022-10-07 11:38:14 +01:00
}
2022-10-06 17:16:07 +01:00
2023-09-01 09:20:00 +01:00
void VisitGitHub ( object sender , EventArgs e ) {
if ( intranetOnly ) {
MessageBox . Show ( "You have parsed in --intranet so we will not try to access the internet or visit github.com, please go to https://github.com/louislam/uptime-kuma if you want to visit github." ) ;
return ;
}
2022-10-07 11:38:14 +01:00
Process . Start ( "https://github.com/louislam/uptime-kuma" ) ;
2022-10-06 05:08:10 +01:00
}
void About ( object sender , EventArgs e )
{
2023-02-20 12:26:53 +00:00
MessageBox . Show ( "Uptime Kuma Windows Runtime v1.0.0" + Environment . NewLine + "© 2023 Louis Lam" , "Info" ) ;
2022-10-06 05:08:10 +01:00
}
2022-10-06 17:16:07 +01:00
void Exit ( object sender , EventArgs e )
{
// Hide tray icon, otherwise it will remain shown until user mouses over it
trayIcon . Visible = false ;
2022-10-08 09:23:11 +01:00
process ? . Kill ( ) ;
Application . Exit ( ) ;
2022-10-07 11:38:14 +01:00
}
void ProcessExited ( object sender , EventArgs e ) {
if ( process . ExitCode ! = 0 ) {
var line = "" ;
while ( ! process . StandardOutput . EndOfStream )
{
line + = process . StandardOutput . ReadLine ( ) ;
}
MessageBox . Show ( "Uptime Kuma exited unexpectedly. Exit code: " + process . ExitCode + " " + line ) ;
}
trayIcon . Visible = false ;
2022-10-06 17:16:07 +01:00
Application . Exit ( ) ;
2022-10-06 05:08:10 +01:00
}
2022-10-07 11:38:14 +01:00
2022-10-06 05:08:10 +01:00
}
}