From 3a188017228b2437820133827c36b03a7b9597f7 Mon Sep 17 00:00:00 2001
From: Justin Tisdale
Date: Wed, 10 Aug 2022 21:46:43 -0400
Subject: [PATCH 01/58] Add Body Encoding field
---
src/languages/en.js | 1 +
src/pages/EditMonitor.vue | 17 +++++++++++++++++
2 files changed, 18 insertions(+)
diff --git a/src/languages/en.js b/src/languages/en.js
index b99516124..4433e2a5f 100644
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -553,4 +553,5 @@ export default {
disableCloudflaredNoAuthMsg: "You are in No Auth mode, password is not require.",
trustProxyDescription: "Trust 'X-Forwarded-*' headers. If you want to get the correct client IP and your Uptime Kuma is behind such as Nginx or Apache, you should enable this.",
wayToGetLineNotifyToken: "You can get an access token from {0}",
+ "Body Encoding": "Body Encoding"
};
diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue
index ac6a3e2e5..7dcc7d64b 100644
--- a/src/pages/EditMonitor.vue
+++ b/src/pages/EditMonitor.vue
@@ -396,6 +396,22 @@
+
+
+ {{ $t("Body Encoding") }}
+
+
+ JSON
+
+
+ x-www-form-urlencoded
+
+
+ XML
+
+
+
+
{{ $t("Body") }}
@@ -644,6 +660,7 @@ export default {
mqttTopic: "",
mqttSuccessMessage: "",
authMethod: null,
+ bodyEncoding: null
};
if (this.$root.proxyList && !this.monitor.proxyId) {
From 2b9bf095a609bbf2f2c517209b13cb57dbd1e1b1 Mon Sep 17 00:00:00 2001
From: Justin Tisdale
Date: Thu, 11 Aug 2022 20:57:03 -0400
Subject: [PATCH 02/58] Add non-json support for http body
---
db/patch-http-body-encoding.sql | 6 ++++++
server/database.js | 1 +
server/model/monitor.js | 20 +++++++++++++++++++-
server/server.js | 1 +
src/pages/EditMonitor.vue | 13 +++++++------
5 files changed, 34 insertions(+), 7 deletions(-)
create mode 100644 db/patch-http-body-encoding.sql
diff --git a/db/patch-http-body-encoding.sql b/db/patch-http-body-encoding.sql
new file mode 100644
index 000000000..de02bede1
--- /dev/null
+++ b/db/patch-http-body-encoding.sql
@@ -0,0 +1,6 @@
+-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
+BEGIN TRANSACTION;
+
+ALTER TABLE [monitor] ADD http_body_encoding TEXT;
+
+COMMIT;
diff --git a/server/database.js b/server/database.js
index b234d67d0..ecf6af728 100644
--- a/server/database.js
+++ b/server/database.js
@@ -62,6 +62,7 @@ class Database {
"patch-add-clickable-status-page-link.sql": true,
"patch-add-sqlserver-monitor.sql": true,
"patch-add-other-auth.sql": { parents: [ "patch-monitor-basic-auth.sql" ] },
+ "patch-http-body-encoding.sql": true
};
/**
diff --git a/server/model/monitor.js b/server/model/monitor.js
index 2feef1356..af3d162a5 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -103,6 +103,7 @@ class Monitor extends BeanModel {
authMethod: this.authMethod,
authWorkstation: this.authWorkstation,
authDomain: this.authDomain,
+ httpBodyEncoding: this.httpBodyEncoding
};
if (includeSensitiveData) {
@@ -241,16 +242,33 @@ class Monitor extends BeanModel {
log.debug("monitor", `[${this.name}] Prepare Options for axios`);
+ // Set content-type header and body values based on the httpBodyEncoding type selected
+ // TODO: Check if this.headers already contains a content-type header set by the user; if so, don't inject one
+ let bodyValue = null;
+ let contentType = null;
+
+ if (this.body && !this.httpBodyEncoding || this.httpBodyEncoding === "json"){
+ bodyValue = JSON.parse(this.body);
+ contentType = "application/json";
+ } else if (this.body && (this.httpBodyEncoding === "xml")) {
+ bodyValue = this.body;
+ contentType = "text/xml";
+ } else if (this.body && (this.httpBodyEncoding === "form")) {
+ bodyValue = this.body;
+ contentType = "application/x-www-form-urlencoded";
+ }
+
const options = {
url: this.url,
method: (this.method || "get").toLowerCase(),
- ...(this.body ? { data: JSON.parse(this.body) } : {}),
+ ...(bodyValue ? { data: bodyValue } : {}),
timeout: this.interval * 1000 * 0.8,
headers: {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"User-Agent": "Uptime-Kuma/" + version,
...(this.headers ? JSON.parse(this.headers) : {}),
...(basicAuthHeader),
+ ...(contentType ? { "Content-Type": contentType } : {})
},
maxRedirects: this.maxredirects,
validateStatus: (status) => {
diff --git a/server/server.js b/server/server.js
index 2a2c4bf61..616a10cda 100644
--- a/server/server.js
+++ b/server/server.js
@@ -693,6 +693,7 @@ let needSetup = false;
bean.authMethod = monitor.authMethod;
bean.authWorkstation = monitor.authWorkstation;
bean.authDomain = monitor.authDomain;
+ bean.httpBodyEncoding = monitor.httpBodyEncoding;
await R.store(bean);
diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue
index 7dcc7d64b..b4aceba7b 100644
--- a/src/pages/EditMonitor.vue
+++ b/src/pages/EditMonitor.vue
@@ -398,8 +398,8 @@
-
{{ $t("Body Encoding") }}
-
+ {{ $t("Body Encoding") }}
+
JSON
@@ -660,7 +660,7 @@ export default {
mqttTopic: "",
mqttSuccessMessage: "",
authMethod: null,
- bodyEncoding: null
+ httpBodyEncoding: "json"
};
if (this.$root.proxyList && !this.monitor.proxyId) {
@@ -698,7 +698,8 @@ export default {
* @returns {boolean} Is the form input valid?
*/
isInputValid() {
- if (this.monitor.body) {
+ //TODO: Handle validation for all 3 possible options + null value
+ if (this.monitor.body && (!this.monitor.httpBodyEncoding || this.monitor.httpBodyEncoding === "json")) {
try {
JSON.parse(this.monitor.body);
} catch (err) {
@@ -729,8 +730,8 @@ export default {
return;
}
- // Beautify the JSON format
- if (this.monitor.body) {
+ // Beautify the JSON format (only if httpBodyEncoding is not set or === json)
+ if (this.monitor.body && (!this.monitor.httpBodyEncoding || this.monitor.httpBodyEncoding === "json")) {
this.monitor.body = JSON.stringify(JSON.parse(this.monitor.body), null, 4);
}
From 31cc328839c9462169070b4b67ba4044fb6716d8 Mon Sep 17 00:00:00 2001
From: Justin Tisdale
Date: Thu, 11 Aug 2022 21:08:13 -0400
Subject: [PATCH 03/58] fix lint
---
server/model/monitor.js | 2 +-
src/languages/en.js | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/server/model/monitor.js b/server/model/monitor.js
index 5d9c4cd42..8f30a0c1d 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -252,7 +252,7 @@ class Monitor extends BeanModel {
let bodyValue = null;
let contentType = null;
- if (this.body && !this.httpBodyEncoding || this.httpBodyEncoding === "json"){
+ if (this.body && !this.httpBodyEncoding || this.httpBodyEncoding === "json") {
bodyValue = JSON.parse(this.body);
contentType = "application/json";
} else if (this.body && (this.httpBodyEncoding === "xml")) {
diff --git a/src/languages/en.js b/src/languages/en.js
index e338d7785..31d926287 100644
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -559,5 +559,5 @@ export default {
disableCloudflaredNoAuthMsg: "You are in No Auth mode, password is not require.",
trustProxyDescription: "Trust 'X-Forwarded-*' headers. If you want to get the correct client IP and your Uptime Kuma is behind such as Nginx or Apache, you should enable this.",
wayToGetLineNotifyToken: "You can get an access token from {0}",
- "Body Encoding": "Body Encoding"
+ "Body Encoding": "Body Encoding",
};
From 5809088f27eb9604b043b782ac375f1feecc2e5a Mon Sep 17 00:00:00 2001
From: Justin Tisdale
Date: Mon, 26 Sep 2022 15:52:43 -0400
Subject: [PATCH 04/58] Don't override a user-defined content-type header
---
server/model/monitor.js | 22 +++++++++++++++-------
1 file changed, 15 insertions(+), 7 deletions(-)
diff --git a/server/model/monitor.js b/server/model/monitor.js
index 509b841c2..c541ecf36 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -249,20 +249,28 @@ class Monitor extends BeanModel {
log.debug("monitor", `[${this.name}] Prepare Options for axios`);
- // Set content-type header and body values based on the httpBodyEncoding type selected
- // TODO: Check if this.headers already contains a content-type header set by the user; if so, don't inject one
- let bodyValue = null;
- let contentType = null;
+
+ // Check if this.headers already contains a content-type header set by the user; if so, don't inject one
+ let contentTypeUserDefinedHeader = this.headers.find(function(header) {
+ return header[0].toLowerCase() == "content-type";
+ });
+
+ let contentType = contentTypeUserDefinedHeader ?
+ contentTypeUserDefinedHeader[1] :
+ null;
+
+ let bodyValue = null;
+
if (this.body && !this.httpBodyEncoding || this.httpBodyEncoding === "json") {
bodyValue = JSON.parse(this.body);
- contentType = "application/json";
+ contentType = contentType ? contentType : "application/json";
} else if (this.body && (this.httpBodyEncoding === "xml")) {
bodyValue = this.body;
- contentType = "text/xml";
+ contentType = contentType ? contentType : "text/xml";
} else if (this.body && (this.httpBodyEncoding === "form")) {
bodyValue = this.body;
- contentType = "application/x-www-form-urlencoded";
+ contentType = contentType ? contentType : "application/x-www-form-urlencoded";
}
const options = {
From 6537f4fe746c157c8a87ff25bbcefeb7a62522f3 Mon Sep 17 00:00:00 2001
From: Justin Tisdale
Date: Mon, 26 Sep 2022 17:09:10 -0400
Subject: [PATCH 05/58] content-type change
---
server/model/monitor.js | 18 ++++--------------
1 file changed, 4 insertions(+), 14 deletions(-)
diff --git a/server/model/monitor.js b/server/model/monitor.js
index c541ecf36..48b0b1d32 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -249,28 +249,18 @@ class Monitor extends BeanModel {
log.debug("monitor", `[${this.name}] Prepare Options for axios`);
-
-
- // Check if this.headers already contains a content-type header set by the user; if so, don't inject one
- let contentTypeUserDefinedHeader = this.headers.find(function(header) {
- return header[0].toLowerCase() == "content-type";
- });
-
- let contentType = contentTypeUserDefinedHeader ?
- contentTypeUserDefinedHeader[1] :
- null;
-
+ let contentType = null;
let bodyValue = null;
if (this.body && !this.httpBodyEncoding || this.httpBodyEncoding === "json") {
bodyValue = JSON.parse(this.body);
- contentType = contentType ? contentType : "application/json";
+ contentType = "application/json";
} else if (this.body && (this.httpBodyEncoding === "xml")) {
bodyValue = this.body;
- contentType = contentType ? contentType : "text/xml";
+ contentType = "text/xml";
} else if (this.body && (this.httpBodyEncoding === "form")) {
bodyValue = this.body;
- contentType = contentType ? contentType : "application/x-www-form-urlencoded";
+ contentType = "application/x-www-form-urlencoded";
}
const options = {
From f6919aef1d3b59dd23229feef6f2815004f34a80 Mon Sep 17 00:00:00 2001
From: Justin Tisdale
Date: Mon, 26 Sep 2022 17:10:56 -0400
Subject: [PATCH 06/58] remove TODO
---
src/pages/EditMonitor.vue | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue
index ba6676703..ea246c600 100644
--- a/src/pages/EditMonitor.vue
+++ b/src/pages/EditMonitor.vue
@@ -741,7 +741,6 @@ export default {
* @returns {boolean} Is the form input valid?
*/
isInputValid() {
- //TODO: Handle validation for all 3 possible options + null value
if (this.monitor.body && (!this.monitor.httpBodyEncoding || this.monitor.httpBodyEncoding === "json")) {
try {
JSON.parse(this.monitor.body);
From 4a7e96f9ea6235d797b3a0f68b8300d82aadfea6 Mon Sep 17 00:00:00 2001
From: Louis Lam
Date: Thu, 6 Oct 2022 12:08:10 +0800
Subject: [PATCH 07/58] Init C# Project for building exe
---
.dockerignore | 3 +-
.gitignore | 3 +
extra/exe-builder/App.config | 10 ++
extra/exe-builder/Program.cs | 59 +++++++++
extra/exe-builder/Properties/AssemblyInfo.cs | 36 ++++++
.../Properties/Resources.Designer.cs | 62 ++++++++++
extra/exe-builder/Properties/Resources.resx | 117 ++++++++++++++++++
.../Properties/Settings.Designer.cs | 23 ++++
.../exe-builder/Properties/Settings.settings | 7 ++
extra/exe-builder/UptimeKuma.csproj | 79 ++++++++++++
extra/exe-builder/UptimeKuma.sln | 16 +++
.../UptimeKuma.sln.DotSettings.user | 3 +
12 files changed, 417 insertions(+), 1 deletion(-)
create mode 100644 extra/exe-builder/App.config
create mode 100644 extra/exe-builder/Program.cs
create mode 100644 extra/exe-builder/Properties/AssemblyInfo.cs
create mode 100644 extra/exe-builder/Properties/Resources.Designer.cs
create mode 100644 extra/exe-builder/Properties/Resources.resx
create mode 100644 extra/exe-builder/Properties/Settings.Designer.cs
create mode 100644 extra/exe-builder/Properties/Settings.settings
create mode 100644 extra/exe-builder/UptimeKuma.csproj
create mode 100644 extra/exe-builder/UptimeKuma.sln
create mode 100644 extra/exe-builder/UptimeKuma.sln.DotSettings.user
diff --git a/.dockerignore b/.dockerignore
index babc429a2..22b71b38c 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -31,6 +31,7 @@ tsconfig.json
/tmp
/babel.config.js
/ecosystem.config.js
+extra/exe-builder
### .gitignore content (commented rules are duplicated)
@@ -45,6 +46,6 @@ dist-ssr
#!/data/.gitkeep
#.vscode
+### End of .gitignore content
-### End of .gitignore content
diff --git a/.gitignore b/.gitignore
index 8eb05867b..8e0edeacb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,3 +16,6 @@ dist-ssr
cypress/videos
cypress/screenshots
+
+extra/exe-builder/bin
+extra/exe-builder/obj
diff --git a/extra/exe-builder/App.config b/extra/exe-builder/App.config
new file mode 100644
index 000000000..09265d8b6
--- /dev/null
+++ b/extra/exe-builder/App.config
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/extra/exe-builder/Program.cs b/extra/exe-builder/Program.cs
new file mode 100644
index 000000000..5516a1ff0
--- /dev/null
+++ b/extra/exe-builder/Program.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Reflection;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using UptimeKuma.Properties;
+
+namespace UptimeKuma {
+ static class Program {
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ static void Main() {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ Application.Run(new UptimeKumaApplicationContext());
+ }
+ }
+
+ public class UptimeKumaApplicationContext : ApplicationContext
+ {
+ private NotifyIcon trayIcon;
+
+ public UptimeKumaApplicationContext()
+ {
+ // Initialize Tray Icon
+ trayIcon = new NotifyIcon();
+
+ trayIcon.Icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);
+ trayIcon.ContextMenu = new ContextMenu(new MenuItem[] {
+ new MenuItem("Check for Update", CheckForUpdate),
+ new MenuItem("About", About),
+ new MenuItem("Exit", Exit),
+ });
+
+ trayIcon.Visible = true;
+ }
+
+ void Exit(object sender, EventArgs e)
+ {
+ // Hide tray icon, otherwise it will remain shown until user mouses over it
+ trayIcon.Visible = false;
+ Application.Exit();
+ }
+
+ void About(object sender, EventArgs e)
+ {
+ MessageBox.Show("Uptime Kuma v1.0.0" + Environment.NewLine + "© 2022 Louis Lam", "Info");
+ }
+
+ void CheckForUpdate(object sneder, EventArgs e) {
+
+ }
+ }
+}
+
diff --git a/extra/exe-builder/Properties/AssemblyInfo.cs b/extra/exe-builder/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..2552870b3
--- /dev/null
+++ b/extra/exe-builder/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Uptime Kuma")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Uptime Kuma")]
+[assembly: AssemblyCopyright("Copyright © 2022 Louis Lam")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("2DB53988-1D93-4AC0-90C4-96ADEAAC5C04")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/extra/exe-builder/Properties/Resources.Designer.cs b/extra/exe-builder/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..8c8e559c5
--- /dev/null
+++ b/extra/exe-builder/Properties/Resources.Designer.cs
@@ -0,0 +1,62 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace UptimeKuma.Properties {
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder",
+ "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance",
+ "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState
+ .Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if ((resourceMan == null)) {
+ global::System.Resources.ResourceManager temp =
+ new global::System.Resources.ResourceManager("UptimeKuma.Properties.Resources",
+ typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState
+ .Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get { return resourceCulture; }
+ set { resourceCulture = value; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/extra/exe-builder/Properties/Resources.resx b/extra/exe-builder/Properties/Resources.resx
new file mode 100644
index 000000000..ffecec851
--- /dev/null
+++ b/extra/exe-builder/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/extra/exe-builder/Properties/Settings.Designer.cs b/extra/exe-builder/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..6c63b395b
--- /dev/null
+++ b/extra/exe-builder/Properties/Settings.Designer.cs
@@ -0,0 +1,23 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace UptimeKuma.Properties {
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute(
+ "Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+ private static Settings defaultInstance =
+ ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get { return defaultInstance; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/extra/exe-builder/Properties/Settings.settings b/extra/exe-builder/Properties/Settings.settings
new file mode 100644
index 000000000..abf36c5d3
--- /dev/null
+++ b/extra/exe-builder/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/extra/exe-builder/UptimeKuma.csproj b/extra/exe-builder/UptimeKuma.csproj
new file mode 100644
index 000000000..d62166e41
--- /dev/null
+++ b/extra/exe-builder/UptimeKuma.csproj
@@ -0,0 +1,79 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {2DB53988-1D93-4AC0-90C4-96ADEAAC5C04}
+ WinExe
+ UptimeKuma
+ uptime-kuma
+ v4.7.2
+ 512
+ true
+ true
+ ..\..\public\favicon.ico
+ 10
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ True
+ Resources.resx
+
+
+ favicon.ico
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+ True
+ Settings.settings
+ True
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/extra/exe-builder/UptimeKuma.sln b/extra/exe-builder/UptimeKuma.sln
new file mode 100644
index 000000000..201d7e234
--- /dev/null
+++ b/extra/exe-builder/UptimeKuma.sln
@@ -0,0 +1,16 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UptimeKuma", "UptimeKuma.csproj", "{2DB53988-1D93-4AC0-90C4-96ADEAAC5C04}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {2DB53988-1D93-4AC0-90C4-96ADEAAC5C04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2DB53988-1D93-4AC0-90C4-96ADEAAC5C04}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2DB53988-1D93-4AC0-90C4-96ADEAAC5C04}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2DB53988-1D93-4AC0-90C4-96ADEAAC5C04}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/extra/exe-builder/UptimeKuma.sln.DotSettings.user b/extra/exe-builder/UptimeKuma.sln.DotSettings.user
new file mode 100644
index 000000000..b4ca9dadf
--- /dev/null
+++ b/extra/exe-builder/UptimeKuma.sln.DotSettings.user
@@ -0,0 +1,3 @@
+
+ True
+ True
\ No newline at end of file
From da778f05ac30a681eeb71735dd21fc86eaef4a23 Mon Sep 17 00:00:00 2001
From: Louis Lam
Date: Fri, 7 Oct 2022 00:16:07 +0800
Subject: [PATCH 08/58] Update
---
extra/exe-builder/Program.cs | 42 ++++++++++++++++++++++++++++++------
1 file changed, 35 insertions(+), 7 deletions(-)
diff --git a/extra/exe-builder/Program.cs b/extra/exe-builder/Program.cs
index 5516a1ff0..840bc873c 100644
--- a/extra/exe-builder/Program.cs
+++ b/extra/exe-builder/Program.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Reflection;
@@ -23,6 +24,7 @@ namespace UptimeKuma {
public class UptimeKumaApplicationContext : ApplicationContext
{
private NotifyIcon trayIcon;
+ private Process process;
public UptimeKumaApplicationContext()
{
@@ -31,19 +33,41 @@ namespace UptimeKuma {
trayIcon.Icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);
trayIcon.ContextMenu = new ContextMenu(new MenuItem[] {
+ new MenuItem("Open", Open),
new MenuItem("Check for Update", CheckForUpdate),
new MenuItem("About", About),
new MenuItem("Exit", Exit),
});
trayIcon.Visible = true;
+
+ var startInfo = new ProcessStartInfo();
+ startInfo.FileName = "node/node.exe";
+ startInfo.Arguments = "server/server.js";
+ startInfo.RedirectStandardOutput = true;
+ startInfo.RedirectStandardError = true;
+ startInfo.UseShellExecute = false;
+ startInfo.CreateNoWindow = true;
+ startInfo.WorkingDirectory = "core";
+
+ process = new Process();
+ process.StartInfo = startInfo;
+ process.EnableRaisingEvents = true;
+ try {
+ process.Start();
+ Open(null, null);
+ } catch (Exception e) {
+ MessageBox.Show("Startup failed: " + e.Message, "Uptime Kuma Error");
+ throw;
+ }
}
- void Exit(object sender, EventArgs e)
- {
- // Hide tray icon, otherwise it will remain shown until user mouses over it
- trayIcon.Visible = false;
- Application.Exit();
+ void Open(object sender, EventArgs e) {
+ Process.Start("http://localhost:3001");
+ }
+
+ void CheckForUpdate(object sender, EventArgs e) {
+
}
void About(object sender, EventArgs e)
@@ -51,8 +75,12 @@ namespace UptimeKuma {
MessageBox.Show("Uptime Kuma v1.0.0" + Environment.NewLine + "© 2022 Louis Lam", "Info");
}
- void CheckForUpdate(object sneder, EventArgs e) {
-
+ void Exit(object sender, EventArgs e)
+ {
+ // Hide tray icon, otherwise it will remain shown until user mouses over it
+ trayIcon.Visible = false;
+ process.Close();
+ Application.Exit();
}
}
}
From 4c456547807dc3fb4d6de0a4da741ef252cb5c7e Mon Sep 17 00:00:00 2001
From: Louis Lam
Date: Fri, 7 Oct 2022 18:38:14 +0800
Subject: [PATCH 09/58] WIP
---
extra/exe-builder/Program.cs | 67 +++++++++++++++++++++--------
extra/exe-builder/UptimeKuma.csproj | 5 ++-
2 files changed, 54 insertions(+), 18 deletions(-)
diff --git a/extra/exe-builder/Program.cs b/extra/exe-builder/Program.cs
index 840bc873c..84ecda317 100644
--- a/extra/exe-builder/Program.cs
+++ b/extra/exe-builder/Program.cs
@@ -4,6 +4,7 @@ using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Reflection;
+using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;
using UptimeKuma.Properties;
@@ -14,7 +15,7 @@ namespace UptimeKuma {
/// The main entry point for the application.
///
[STAThread]
- static void Main() {
+ static void Main(string[] args) {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new UptimeKumaApplicationContext());
@@ -28,37 +29,44 @@ namespace UptimeKuma {
public UptimeKumaApplicationContext()
{
- // Initialize Tray Icon
trayIcon = new NotifyIcon();
trayIcon.Icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);
trayIcon.ContextMenu = new ContextMenu(new MenuItem[] {
- new MenuItem("Open", Open),
- new MenuItem("Check for Update", CheckForUpdate),
- new MenuItem("About", About),
- new MenuItem("Exit", Exit),
+ new("Open", Open),
+ //new("Debug Console", DebugConsole),
+ new("Check for Update...", CheckForUpdate),
+ new("Visit GitHub...", VisitGitHub),
+ new("About", About),
+ new("Exit", Exit),
});
+ trayIcon.MouseDoubleClick += new MouseEventHandler(Open);
+
trayIcon.Visible = true;
- var startInfo = new ProcessStartInfo();
- startInfo.FileName = "node/node.exe";
- startInfo.Arguments = "server/server.js";
- startInfo.RedirectStandardOutput = true;
- startInfo.RedirectStandardError = true;
- startInfo.UseShellExecute = false;
- startInfo.CreateNoWindow = true;
- startInfo.WorkingDirectory = "core";
+ 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"
+ };
process = new Process();
process.StartInfo = startInfo;
process.EnableRaisingEvents = true;
+ process.Exited += new EventHandler(ProcessExited);
+
+
try {
process.Start();
- Open(null, null);
+ //Open(null, null);
+
} catch (Exception e) {
MessageBox.Show("Startup failed: " + e.Message, "Uptime Kuma Error");
- throw;
}
}
@@ -66,8 +74,17 @@ namespace UptimeKuma {
Process.Start("http://localhost:3001");
}
+ void DebugConsole(object sender, EventArgs e) {
+
+ }
+
void CheckForUpdate(object sender, EventArgs e) {
+ Process.Start("https://github.com/louislam/uptime-kuma/releases");
+ }
+ void VisitGitHub(object sender, EventArgs e)
+ {
+ Process.Start("https://github.com/louislam/uptime-kuma");
}
void About(object sender, EventArgs e)
@@ -79,9 +96,25 @@ namespace UptimeKuma {
{
// Hide tray icon, otherwise it will remain shown until user mouses over it
trayIcon.Visible = false;
- process.Close();
+ process.Kill();
+ }
+
+ 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;
Application.Exit();
}
+
}
}
diff --git a/extra/exe-builder/UptimeKuma.csproj b/extra/exe-builder/UptimeKuma.csproj
index d62166e41..c3c6aad27 100644
--- a/extra/exe-builder/UptimeKuma.csproj
+++ b/extra/exe-builder/UptimeKuma.csproj
@@ -13,7 +13,7 @@
true
true
..\..\public\favicon.ico
- 10
+ 9
AnyCPU
@@ -34,6 +34,9 @@
prompt
4
+
+ COPY "$(SolutionDir)bin\Debug\uptime-kuma.exe" "C:\Users\LouisLam\Desktop\uptime-kuma-win64\"
+
From 3eaccb560ed1a0590a6bb3bf7f73765bbe06db00 Mon Sep 17 00:00:00 2001
From: Louis Lam
Date: Sat, 8 Oct 2022 16:23:11 +0800
Subject: [PATCH 10/58] Implement Download Logic
---
extra/exe-builder/DownloadForm.Designer.cs | 84 +++++
extra/exe-builder/DownloadForm.cs | 65 ++++
extra/exe-builder/DownloadForm.resx | 377 +++++++++++++++++++++
extra/exe-builder/Program.cs | 23 +-
extra/exe-builder/UptimeKuma.csproj | 9 +
5 files changed, 554 insertions(+), 4 deletions(-)
create mode 100644 extra/exe-builder/DownloadForm.Designer.cs
create mode 100644 extra/exe-builder/DownloadForm.cs
create mode 100644 extra/exe-builder/DownloadForm.resx
diff --git a/extra/exe-builder/DownloadForm.Designer.cs b/extra/exe-builder/DownloadForm.Designer.cs
new file mode 100644
index 000000000..26a474e9c
--- /dev/null
+++ b/extra/exe-builder/DownloadForm.Designer.cs
@@ -0,0 +1,84 @@
+using System.ComponentModel;
+
+namespace UptimeKuma {
+ partial class DownloadForm {
+ ///
+ /// Required designer variable.
+ ///
+ private IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing) {
+ if (disposing && (components != null)) {
+ components.Dispose();
+ }
+
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent() {
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DownloadForm));
+ this.progressBar = new System.Windows.Forms.ProgressBar();
+ this.label = new System.Windows.Forms.Label();
+ this.labelData = new System.Windows.Forms.Label();
+ this.SuspendLayout();
+ //
+ // progressBar
+ //
+ this.progressBar.Location = new System.Drawing.Point(12, 12);
+ this.progressBar.Name = "progressBar";
+ this.progressBar.Size = new System.Drawing.Size(472, 41);
+ this.progressBar.TabIndex = 0;
+ //
+ // label
+ //
+ this.label.Location = new System.Drawing.Point(12, 59);
+ this.label.Name = "label";
+ this.label.Size = new System.Drawing.Size(472, 23);
+ this.label.TabIndex = 1;
+ this.label.Text = "Preparing...";
+ //
+ // labelData
+ //
+ this.labelData.Location = new System.Drawing.Point(12, 82);
+ this.labelData.Name = "labelData";
+ this.labelData.Size = new System.Drawing.Size(472, 23);
+ this.labelData.TabIndex = 2;
+ //
+ // DownloadForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(496, 117);
+ this.Controls.Add(this.labelData);
+ this.Controls.Add(this.label);
+ this.Controls.Add(this.progressBar);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
+ this.MaximizeBox = false;
+ this.Name = "DownloadForm";
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
+ this.Text = "Uptime Kuma";
+ this.Load += new System.EventHandler(this.DownloadForm_Load);
+ this.ResumeLayout(false);
+ }
+
+ private System.Windows.Forms.Label labelData;
+
+ private System.Windows.Forms.Label label;
+
+ private System.Windows.Forms.ProgressBar progressBar;
+
+ #endregion
+ }
+}
+
diff --git a/extra/exe-builder/DownloadForm.cs b/extra/exe-builder/DownloadForm.cs
new file mode 100644
index 000000000..9c740e312
--- /dev/null
+++ b/extra/exe-builder/DownloadForm.cs
@@ -0,0 +1,65 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Net;
+using System.Windows.Forms;
+
+namespace UptimeKuma {
+ public partial class DownloadForm : Form {
+ private readonly Queue downloadQueue = new();
+ private readonly WebClient webClient = new();
+
+ public DownloadForm() {
+ InitializeComponent();
+ }
+
+ private void DownloadForm_Load(object sender, EventArgs e) {
+ webClient.DownloadProgressChanged += DownloadProgressChanged;
+ webClient.DownloadFileCompleted += DownloadFileCompleted;
+
+ if (!File.Exists("node")) {
+ downloadQueue.Enqueue(new DownloadItem {
+ URL = "https://nodejs.org/dist/v16.17.1/node-v16.17.1-win-x64.zip",
+ Filename = "node.zip"
+ });
+ }
+
+ if (!File.Exists("node")) {
+ downloadQueue.Enqueue(new DownloadItem {
+ URL = "https://github.com/louislam/uptime-kuma/archive/refs/tags/1.18.3.zip",
+ Filename = "core.zip"
+ });
+ }
+
+ DownloadNextFile();
+ }
+
+ void DownloadNextFile() {
+ if (downloadQueue.Count > 0) {
+ var item = downloadQueue.Dequeue();
+ label.Text = item.URL;
+ webClient.DownloadFileAsync(new Uri(item.URL), item.Filename);
+ } else {
+ // TODO: Finished, extract?
+ }
+ }
+
+ void DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) {
+ progressBar.Value = e.ProgressPercentage;
+ var total = e.TotalBytesToReceive / 1024;
+ var current = e.BytesReceived / 1024;
+ labelData.Text = $"{current}KB/{total}KB";
+ }
+
+ void DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) {
+ DownloadNextFile();
+ }
+ }
+
+ public class DownloadItem {
+ public string URL { get; set; }
+ public string Filename { get; set; }
+ }
+}
+
diff --git a/extra/exe-builder/DownloadForm.resx b/extra/exe-builder/DownloadForm.resx
new file mode 100644
index 000000000..e87e0c0d4
--- /dev/null
+++ b/extra/exe-builder/DownloadForm.resx
@@ -0,0 +1,377 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+
+ AAABAAMAMDAAAAEAIACoJQAANgAAACAgAAABACAAqBAAAN4lAAAQEAAAAQAgAGgEAACGNgAAKAAAADAA
+ AABgAAAAAQAgAAAAAAAAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAA////BPT09Bfu7u4e8fHxJPPz8yv19fUy9fX1M/Pz8yvx8fEk9vb2HPPz8xXMzMwFAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//
+ /wHv7+8f7u7uPPPz81Tx8fFs8fHxgPHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGB8fHxcfHx8V3x8fFI9PT0MOvr6w0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AADy8vIU8fHxS/Dw8Hbx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fFr9PT0R/Dw8CIAAAABAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAA8vLyFPHx8Vnx8fGB8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fFs9fX1Mb+/vwQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAICAgALy8vI88fHxfvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvLy8nby8vI8gICAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAzMzMBfHx8Vrx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8vLyYf///wwAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMzMwF8vLyYPHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8W/z8/MWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADv7+9R8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLw8PB26urqDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLy8ijx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgu7w7Ifj79ud2u7PtNLrw83P677dzeu85c3r
+ u+rM67rwzOu68c7rverQ68Dj0uvD3NbuyM3b7c+64u7apujv5ZPx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxXgAAAAEAAAAAAAAAAAAAAAAAAAAA4+PjCfDw
+ 8Hfx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLd7tSmzeu92MbqsvvG6bH/xumy/8fq
+ s//H6rP/yOq0/8jqtf/J6rb/yeq2/8rrt//K67j/y+u4/8vruf/M67r/zOu7/83ru//Q7MDx1u7Kz9/t
+ 163s8OuJ8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgu/v7y8AAAAAAAAAAAAA
+ AAAAAAAA7u7uPfHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC5PDdl8jqtuTE6a7/xOmv/8Xp
+ sP/G6bH/xumx/8bpsv/H6rP/x+qz/8jqtP/I6rX/yeq2/8nqtv/K67f/yuu4/8vruP/L67n/zOu6/8zr
+ u//N67v/zey8/87svf/P67742e3Mx+jv5ZLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvDw
+ 8HWAgIACAAAAAAAAAACqqqoD8vLyc/Hx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLf7degxOiu+cPo
+ rf/D6a7/xOmu/8Xpr//F6bD/xumx/8bpsf/G6bL/x+qz/8fqs//I6rT/yOq1/8nqtv/J6rb/yuu3/8rr
+ uP/L67j/y+u5/8zruv/M67v/zeu7/83svP/O7L3/zuy9/87svfzc7tK28fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fEkAAAAAAAAAADz8/Mq8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgunv
+ 5o3D6a/0wuis/8Lorf/D6K3/xOmu/8Tprv/F6a//xemw/8bpsf/G6bH/xumy/8fqs//H6rP/yOq0/8jq
+ tf/J6rb/yeq2/8rrt//K67j/y+u4/8vruf/M67r/zOu7/83ru//N7Lz/zuy9/87svf/O7L3/3e/TtPHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLy8vJNAAAAAAAAAADy8vJM8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgszqutDB6Kv/weir/8LorP/D6K3/w+it/8Tprv/E6a7/xemv/8XpsP/G6bH/xumx/8bp
+ sv/H6rP/x+qz/8jqtP/I6rX/yeq2/8nqtv/K67f/yuu4/8vruP/L67n/zOu6/8zru//N67v/zey8/87s
+ vf/O7L3/zuy++u3w6Yzx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLy8vJ1AAAAAAAAAADx8fFr8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC6O/kjsDoqvzA6Kr/weir/8Loq//C6Kz/w+it/8Porf/E6a7/xOmu/8Xp
+ r//F6bD/xumx/8bpsf/G6bL/x+qz/8fqtP/I6rT/yOq1/8nqtv/J6rb/yuu3/8rruP/L67n/y+u5/8zr
+ uv/M67v/zeu7/83svP/O7L3/zuy9/93u07Xx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC////Bv//
+ /wfx8fGB8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC1ezJsr/nqf/A56n/weiq/8Hoq//C6Kv/wuis/8Po
+ rf/D6K3/xOmu/8Pprv+856T/uOed/7bmmv+05Zf/teWZ/7jnnf+86KP/wOio/8fqs//J6rb/yeq2/8rr
+ t//K67j/y+u5/8vruf/M67r/zOu7/83ru//N7Lz/zuy9/9buyNLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8vLyE/Ly8hPx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGCy+q6zr/nqP/A56n/wOep/8Ho
+ qv/B6Kv/wuir/8LorP+u5Y//neF2/5bgav+V4Gr/luBr/5fhbP+Y4W7/meFv/5rhcf+b4nL/nOJ0/53i
+ dv+j5H//reaM/7nnnf/E6q//y+y4/8vruf/L67n/zOu6/8zru//N67v/zey8/9Lsxd/x8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC7+/vIPb29hzx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGCx+m03L/n
+ qP+/56j/wOep/8Dnqf/B6Kr/weir/7nmn/+R32T/kt9l/5PfZ/+U4Gj/leBq/5bga/+X4W3/mOFu/5nh
+ b/+a4XH/m+Jy/5zidP+d4nX/nuN3/5/jeP+f4nn/weqq/8rruP/L67n/y+u5/8zruv/M67v/zeu7/9Ls
+ w+Lx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8PDwI/Hx8SXx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGCxeix5L/nqP+/56j/v+eo/8Dnqf/A56n/weiq/7Pllv+Q3mP/kd9k/5LfZf+T32f/lOBo/5Xg
+ av+W4Gv/l+Ft/5jhbv+Z4W//muFx/5vicv+c4nT/neJ1/57jd/+f43j/xOmu/8rrt//K67j/y+u5/8vr
+ uf/M67r/zOu7/9Tsxtfx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC9PT0GO/v7yDx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGCx+m037/nqP+/56j/v+eo/7/nqP/A56n/wOip/7TmmP+P3mH/kN5j/5Hf
+ ZP+S32b/k99n/5TgaP+V4Gr/luBr/5fhbf+Y4W7/meFw/5rhcf+b4nL/nOJ0/53idf+h5Hz/yuu2/8nq
+ t//K67f/yuu4/8vruf/L67n/zOu6/9ftysrx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC7e3tDvT0
+ 9Bfx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGCyOq117/nqP+/56j/v+eo/7/nqP+/56j/wOep/7vn
+ of+O3mD/j95h/5DeY/+R32T/kt9m/5PfZ/+U4Gj/leBq/5bga/+X4W3/mOFu/5nhcP+a4nH/m+Jy/5zi
+ dP+r5Yr/yOq1/8nqtv/J6rf/yuu3/8rruP/L67n/y+u5/9zu1LHx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLz8/OA////A+7u7g/x8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGCz+q+xb/nqP+/56j/v+eo/7/n
+ qP+/56j/v+eo/8Dnqf+S4Gb/jt5g/4/eYf+Q3mP/kd9k/5LfZv+T32f/lOBo/5Xgav+W4Gv/l+Ft/5jh
+ bv+Z4XD/muJx/5vic/+4553/yOq0/8jqtf/J6rb/yeq3/8rrt//K67j/y+u5/+bw4Zfx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fFrAAAAAP///wHz8/N88fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC1+zMrr/n
+ qP+/56j/v+eo/7/nqP+/56j/v+eo/7/nqP+f4Xn/jd5f/47eYP+P3mH/kN5j/5HfZP+S32b/k99n/5Tg
+ af+V4Gr/luBr/5fhbf+Y4W7/meFw/5vic//F6rD/x+q0/8jqtP/I6rX/yeq2/8nqt//K67f/zOu88u/x
+ 74Px8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLv7+9QAAAAAAAAAADw8PBm8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC5e7gk7/nqP+/56j/v+eo/7/nqP+/56j/v+eo/7/nqP+u5I//jN1d/43eX/+O3mD/j95h/5De
+ Y/+R32T/kt9m/5PfZ/+U4Gn/leBq/5bga/+X4W3/mOFu/6rliP/G6rL/x+qz/8fqtP/I6rT/yOq1/8nq
+ tv/J6rf/1OzGy/Hx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YL19fUzAAAAAAAAAADy8vJO8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgsPoru2/56j/v+eo/7/nqP+/56j/v+eo/7/nqP++6Kf/j95i/4zd
+ Xf+N3l//jt5g/4/eYv+Q3mP/kd9k/5LfZv+T32f/lOBp/5Xgav+W4Gz/l+Ft/7voov/G6bL/xuqy/8fq
+ s//H6rT/yOq1/8jqtf/J6rb/4e/Zo/Hx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLw8PARAAAAAAAA
+ AADu7u4u8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgszpvMm/56j/v+eo/7/nqP+/56j/v+eo/7/n
+ qP+/56j/q+SL/4vdXP+M3V3/jd5f/47eYP+P3mL/kN9j/5HfZP+S32b/k99n/5Tgaf+V4Gr/qOOH/8Xp
+ sP/G6bH/xumy/8bqsv/H6rP/x+q0/8jqtf/K67jy8PHwhPHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8WoAAAAAAAAAAAAAAADo6OgL8fHxgfHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxguDv2J2/56j/v+eo/7/n
+ qP+/56j/v+eo/7/nqP+/56j/v+eo/6Xjgv+L3Vz/jN1d/43eX/+O3mD/j95i/5DfY/+R32T/kt9m/5Pf
+ Z/+k44D/xOmu/8XpsP/F6bD/xumx/8bpsv/G6rL/x+qz/8fqtP/W7cnB8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvPz80AAAAAAAAAAAAAAAAAAAAAA8PDwZ/Hx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLD6K/rv+eo/7/nqP+/56j/v+eo/7/nqP+/56j/v+eo/7/nqP+u5I//kt5n/4zdXf+N3l//jt5g/4/e
+ Yv+Q32P/luFs/67kj//D6K3/xOmu/8Tpr//F6bD/xemw/8bpsf/G6bL/xuqy/8fqtP7o7+WR8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvPz8xYAAAAAAAAAAAAAAAAAAAAA8vLyPPHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLV7ci0v+eo/7/nqP+/56j/v+eo/7/nqP+/56j/v+eo/7/nqP+/56j/wOio/7Xl
+ mv+u5I7/rOSM/67kj/+35pz/wumr/8Lorf/D6K3/w+it/8Tprv/E6a//xemw/8XpsP/G6bH/xumy/9Ds
+ wNPx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8vLyZQAAAAAAAAAAAAAAAAAAAAAAAAAA////DPHx
+ 8YDx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGCx+m03L/nqP+/56j/v+eo/7/nqP+/56j/v+eo/7/n
+ qP+/56j/v+eo/7/nqP+/56j/wOep/8Doqv/B6Kr/weir/8LorP/C6K3/w+it/8Porv/E6a7/xOmv/8Xp
+ sP/F6bD/yOq18uvw6Yvx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC7+/vMQAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAPHx8Vzx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC6O/ij8LorPG/56j/v+eo/7/n
+ qP+/56j/v+eo/7/nqP+/56j/v+eo/7/nqP+/56j/v+eo/8Dnqf/A6Kr/weiq/8Hoq//C6Kz/wuit/8Po
+ rf/D6K7/xOmu/8Tpr//F6bH74u/anvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLw8PB6////BQAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAPPz8yrx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxguHu
+ 2pnB56v2v+eo/7/nqP+/56j/v+eo/7/nqP+/56j/v+eo/7/nqP+/56j/v+eo/7/nqP/A56n/wOiq/8Ho
+ q//B6Kv/wuis/8Lorf/D6K3/w+mu/8Tprv3b7dKq8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fFJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHy8vJf8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLi7tyXwumt8L/nqP+/56j/v+eo/7/nqP+/56j/v+eo/7/nqP+/56j/v+eo/7/n
+ qP+/56j/wOep/8Doqv/B6Kv/weir/8LorP/C6K3/xOiv+d7u1aTx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvLy8nb///8KAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADv7+8Q8/Pze/Hx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC6/Dpiszqu82/56j/v+eo/7/nqP+/56j/v+eo/7/n
+ qP+/56j/v+eo/7/nqP+/56j/v+eo/8Dnqf/A6Kr/weir/8Hoq//H6bTj5e7elfHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvPz8yoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAA9fX1MvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLe7tShx+mz3r/n
+ qP+/56j/v+eo/7/nqP+/56j/v+eo/7/nqP+/56j/v+eo/7/nqP/A56n/xumy5drtz6rv8e+D8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8vLyTgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAPHx8Unx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgubv45DU68e2y+q6z8XoseTD6a7uweir9MPpru7F6bHly+q50tLsxLrl796U8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLy8vJh////AwAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wHx8fFZ8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8Wzf398IAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8D8/PzVfHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8PDwZujo
+ 6AsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAA////AfHx8Ujx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fFa////BQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADz8/Mp8vLydvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8/PzfPHx8TcAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////CvLy8lDz8/N/8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvPz84Hx8fFa8PDwEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AADw8PAR8vLyTvHx8X3x8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fF/8/PzVvT09BgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wXz8/Mq8/PzU/Hx8XDx8fGB8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLy8vJz8fHxWO/v7y////8IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8G7e3tHfLy
+ 8ifu7u4u8PDwNPT09C/y8vIo7+/vH+Pj4wkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAP///////wAA////////AAD///////8AAP//gAf//wAA//gAAD//AAD/wAAAB/8AAP+A
+ AAAB/wAA/gAAAAB/AAD8AAAAAD8AAPgAAAAAHwAA8AAAAAAPAADwAAAAAAcAAOAAAAAABwAA4AAAAAAD
+ AADAAAAAAAMAAMAAAAAAAwAAwAAAAAABAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAIAA
+ AAAAAQAAgAAAAAABAACAAAAAAAMAAMAAAAAAAwAAwAAAAAADAADAAAAAAAMAAMAAAAAABwAAwAAAAAAH
+ AADgAAAAAAcAAOAAAAAADwAA4AAAAAAPAADwAAAAAB8AAPAAAAAAHwAA+AAAAAA/AAD8AAAAAD8AAPwA
+ AAAAfwAA/gAAAAD/AAD/AAAAAf8AAP+AAAAD/wAA/8AAAAf/AAD/8AAAH/8AAP/8AAA//wAA//8AAf//
+ AAD//+AP//8AAP///////wAA////////AAD///////8AACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAgICAAu/v7xD09PQX7u7uHvDw8CP29vYb8vLyFOrq6gwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICA
+ gALy8vIm7+/vT/Pz82fz8/N98fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvDw8Hrw8PBm7+/vUPT0
+ 9C3o6OgLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOPj
+ 4wnz8/NC8vLydPHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YHy8vJj8/PzKoCAgAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AADx8fEl8vLydfHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxcfHx8SUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAA9PT0LfHx8YDx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8/PzgPLy8j0AAAABAAAAAAAA
+ AAAAAAAAAAAAAO3t7Rzx8fGA8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLr8OmM5O7emeTv
+ 3Z7h79mj5fDem+nv45Tu8u6H8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvLy
+ 8joAAAAAAAAAAAAAAAD///8E8fHxbvHx8YLx8fGC8fHxgvHx8YLx8fGC7vDshtns0K7N67zayeq288fq
+ s//I6rT/yOq1/8nqtv/K67f/y+u4/8vruf/P7L7w0+zF29vv0Lrn8OKX8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8/PzfvPz8xUAAAAAAAAAAPX19TLx8fGC8fHxgvHx8YLx8fGC8fHxgt3u1KXF6rHzxOmv/8Xp
+ sP/G6bH/xumy/8fqs//I6rT/yOq1/8nqtv/K67f/y+u4/8vruf/M67v/zey8/87svf/S7MPj4u7Zp/Hx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8/PzVQAAAAAAAAAA8fHxavHx8YLx8fGC8fHxgvHx8YLf7defwuis/cPo
+ rf/E6a7/xOmv/8XpsP/G6bH/xumy/8fqs//I6rT/yOq1/8nqtv/K67f/y+u4/8vruv/M67v/zey8/87s
+ vf/N67z/3e7SufHx8YLx8fGC8fHxgvHx8YLz8/N8////Bf///w3x8fGC8fHxgvHx8YLx8fGC8fHxgsXp
+ sOnB6Kv/wuis/8Porf/E6a7/xOmv/8XpsP/G6bH/xumy/8fqs//I6rT/yOq1/8nqtv/K67f/y+u4/8vr
+ uv/M67v/zey8/87svf/O67z96/Hoj/Hx8YLx8fGC8fHxgvHx8YLy8vIm8/PzK/Hx8YLx8fGC8fHxgvHx
+ 8YLg79icwOep/8Hoqv/B6Kv/wuis/8Porf/E6a7/wuit/73opP+76KL/u+eh/77opv/D6a3/yeu1/8nq
+ tv/K67f/y+u5/8zruv/M67v/zey8/87svf/d7tSz8fHxgvHx8YLx8fGC8fHxgvHx8Tby8vI68fHxgvHx
+ 8YLx8fGC8fHxgtTrxre/56j/wOep/8Hoqv/B6Kv/uOad/53idv+V4Gn/leBq/5fhbP+Y4W//muFx/5vi
+ c/+e4Xb/puWD/7PmlP/D6a3/y+u5/8zruv/M67v/zey8/9rtzsHx8fGC8fHxgvHx8YLx8fGC8/PzQfPz
+ 80Lx8fGC8fHxgvHx8YLx8fGC0OvAwr/nqP+/56j/wOep/8Hoqv+o44b/kd9k/5LfZv+U4Gj/leBq/5fh
+ bf+Y4W//muFx/5vic/+d4nX/n+N3/7fnm//K67j/y+u5/8zruv/M67v/2u3QvPHx8YLx8fGC8fHxgvHx
+ 8YLy8vI98/PzP/Hx8YLx8fGC8fHxgvHx8YLQ6sK/v+eo/7/nqP+/56j/wOep/6jjhv+P3mL/kd9k/5Lf
+ Zv+U4Gj/leBr/5fhbf+Y4W//muFx/5zic/+d4nX/v+mm/8nqt//K67j/y+u5/8zruv/f79au8fHxgvHx
+ 8YLx8fGC8fHxgvX19TLx8fE38fHxgvHx8YLx8fGC8fHxgtTrybO/56j/v+eo/7/nqP+/56j/sOSS/47e
+ YP+P3mL/kd9k/5LfZv+U4Gj/leBr/5fhbf+Z4W//muJx/5/jd//H6bP/yeq2/8nqt//K67j/y+u5/+nv
+ 45Tx8fGC8fHxgvHx8YLx8fGC7+/vIPHx8SXx8fGC8fHxgvHx8YLx8fGC4e/Zm7/nqP+/56j/v+eo/7/n
+ qP+956X/jt5h/47eYP+P3mL/kd9k/5LfZv+U4Gn/luBr/5fhbf+Z4W//q+aK/8fqs//I6rT/yeq2/8nq
+ t//N7Lvw8fHxgvHx8YLx8fGC8fHxgvPz84D///8G6+vrDfHx8YLx8fGC8fHxgvHx8YLv8e+Dweis87/n
+ qP+/56j/v+eo/7/nqP+d4XX/jN1e/47eYP+P3mL/kd9k/5PfZ/+U4Gn/luBr/5fhbf+86KP/xuqy/8fq
+ s//I6rX/yeq2/9Tsx8nx8fGC8fHxgvHx8YLx8fGC8PDwaAAAAAAAAAAA8fHxbPHx8YLx8fGC8fHxgvHx
+ 8YLM6rrMv+eo/7/nqP+/56j/v+eo/7blmv+N3V//jN1e/47eYP+Q3mL/kd9k/5PfZ/+U4Gn/qeSH/8Xp
+ sP/G6bH/xuqy/8fqs//I6rX/5fDem/Hx8YLx8fGC8fHxgvHx8YLz8/M/AAAAAAAAAADz8/NB8fHxgvHx
+ 8YLx8fGC8fHxgt3s06O/56j/v+eo/7/nqP+/56j/v+eo/7Xmmf+U32n/jN1e/47eYP+Q3mL/k99o/6zk
+ i//D6a7/xemv/8XpsP/G6bH/xuqy/8vqu+jx8fGC8fHxgvHx8YLx8fGC8fHxgvPz8xUAAAAAAAAAAPT0
+ 9Bfx8fGC8fHxgvHx8YLx8fGC8fHvg8Tpsee/56j/v+eo/7/nqP+/56j/v+eo/7/nqP+35pz/suWV/7Xm
+ mf/A6Kj/wuit/8Porf/E6a7/xemv/8XpsP/G6bH/3e3UqvHx8YLx8fGC8fHxgvHx8YLw8PBmAAAAAAAA
+ AAAAAAAAAAAAAPHx8W7x8fGC8fHxgvHx8YLx8fGC4u7cmMHnqvm/56j/v+eo/7/nqP+/56j/v+eo/7/n
+ qP+/56j/wOep/8Hoqv/C6Kz/wuit/8Porf/E6a7/xemv/9Hrwszx8fGC8fHxgvHx8YLx8fGC8fHxgvX1
+ 9TEAAAAAAAAAAAAAAAAAAAAA7u7uO/Hx8YLx8fGC8fHxgvHx8YLx8fGC3e7SpMHoqfq/56j/v+eo/7/n
+ qP+/56j/v+eo/7/nqP+/56j/wOip/8Hoq//C6Kz/wuit/8Porf/O67zV8PHwhPHx8YLx8fGC8fHxgvHx
+ 8YLy8vJ2////BQAAAAAAAAAAAAAAAAAAAACqqqoD8PDwafHx8YLx8fGC8fHxgvHx8YLx8fGC4O/YnMTo
+ ruy/56j/v+eo/7/nqP+/56j/v+eo/7/nqP+/56j/wOip/8Hoq//C6Kz90uvEwe/x74Px8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvPz8ykAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADz8/MW8fHxfPHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8PLuhdXtyLXF6bHlv+eo/7/nqP+/56j/v+eo/7/nqP/B6Kv0zeq8zOXv4JTx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLy8vJNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADy8vIm8fHxgPHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLs8OmJ4e/Zm93u06Pf7def5+/hkvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxXf///wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AADy8vIo8/PzffHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8VnMzMwFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAD29vYb8fHxbvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvPz83/v7+9BgICAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMzMwF8/PzQPLy8nnx8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgvPz84Hx8fFc9PT0GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////B/X19TLx8fFc8PDwevHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8fHxgPHx8Wv09PRE9PT0FwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAA7+/vEPb29hvw8PAj7+/vH/T09Be/v78EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////8B///wAA//wAAD/wAAAP4AAAB+AA
+ AAfAAAADwAAAA4AAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAADwAAAA8AAAAPAAAAH4AAAB+AA
+ AA/wAAAP+AAAH/gAAD/+AAB//wAB///AA///+B////////////8oAAAAEAAAACAAAAABACAAAAAAAAAE
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////CfDw8BH///8GAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgICAAu7u7i7x8fFe8PDwevHx8YLx8fGC8fHxgvDw
+ 8Hvx8fFs7+/vT/Dw8CMAAAABAAAAAAAAAAAAAAAA5ubmCvLy8l/x8fGC8fHxgvHx8YLx8fGC8fHxgvHx
+ 8YLx8fGC8fHxgvHx8YLx8fGC8/PzZu7u7g8AAAAAAAAAAPHx8V3x8fGC8fHxgunv5o7Z7c200+vFytTs
+ xc7W7cnH2+7QueLu2qbu8OyH8fHxgvHx8YLx8fFu////BfHx8STx8fGC8fHxgtrtzq3D6a/8xemw/8bp
+ sv/I6rT/yeq2/8vruP/M67v/z+u++Nzu0bjx8fGC8fHxgu/v7zDx8fFI8fHxguzw6ojC56z3wuis/8Tp
+ rv/E6q3/weiq/8fqsv/J6rb/y+u5/8zru//N67z/6/HpjfHx8YLy8vJN8fHxXPHx8YLg79icv+eo/8Ho
+ qv+k4n//lOBo/5fhbf+a4XH/n+J5/7Pmlv/L67n/zOu7/+Xw353x8fGC8fHxXvHx8Vrx8fGC4O3Zm7/n
+ qP+/56j/nuF3/5HfZP+U4Gj/l+Ft/5ricf+x5pL/yeq3/8vruf/r8emN8fHxgu/v70/x8fFK8fHxguzw
+ 6ojA6Kn8v+eo/6njiP+O3mD/kd9k/5Tgaf+X4W3/vuim/8jqtP/N67zr8fHxgvHx8YLy8vI68/PzK/Hx
+ 8YLx8fGCx+m03L/nqP++6Kb/meBw/47eYP+S32X/q+SL/8XpsP/G6rL/1+zLvvHx8YLz8/OB8PDwEdXV
+ 1Qbx8fF98fHxgt/t1Z/A56j9v+eo/7/nqP+656H/vuim/8Lorf/E6a7/yOq18Ovw6Yvx8fGC8vLyYwAA
+ AAAAAAAA8fHxR/Hx8YLx8fGC2O3NrMDnqfq/56j/v+eo/7/nqP/B6Kv/xumy7OTu3Zfx8fGC8/PzgfLy
+ 8icAAAAAAAAAAP///wPz8/Nm8fHxgvHx8YLo7+SO0+zFuczquszM6bzJ1+zMru7w7Ibx8fGC8fHxgvHx
+ 8UcAAAAAAAAAAAAAAAAAAAAA4+PjCfHx8Vzx8fGC8fHxgvHx8YLx8fGC8fHxgvHx8YLx8fGC8fHxgfPz
+ 80D///8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8/PzK/Ly8mDz8/N+8fHxgvHx8YLy8vJ68vLyUezs
+ 7BsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAevr6w3j4+MJAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AAD8fwAA4AcAAMADAACAAQAAgAEAAIABAACAAQAAgAEAAIAB
+ AADAAwAAwAMAAOAHAADwDwAA/n8AAP//AAA=
+
+
+
\ No newline at end of file
diff --git a/extra/exe-builder/Program.cs b/extra/exe-builder/Program.cs
index 84ecda317..84aa6e456 100644
--- a/extra/exe-builder/Program.cs
+++ b/extra/exe-builder/Program.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
+using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
@@ -42,9 +43,23 @@ namespace UptimeKuma {
});
trayIcon.MouseDoubleClick += new MouseEventHandler(Open);
-
trayIcon.Visible = true;
+ if (File.Exists("core") && File.Exists("node")) {
+ // Go go go
+ StartProcess();
+ } else {
+ DownloadFiles();
+ }
+ }
+
+ void DownloadFiles() {
+ var form = new DownloadForm();
+ form.Closed += Exit;
+ form.Show();
+ }
+
+ void StartProcess() {
var startInfo = new ProcessStartInfo {
FileName = "node/node.exe",
Arguments = "server/server.js --data-dir=\"../data/\"",
@@ -58,8 +73,7 @@ namespace UptimeKuma {
process = new Process();
process.StartInfo = startInfo;
process.EnableRaisingEvents = true;
- process.Exited += new EventHandler(ProcessExited);
-
+ process.Exited += ProcessExited;
try {
process.Start();
@@ -96,7 +110,8 @@ namespace UptimeKuma {
{
// Hide tray icon, otherwise it will remain shown until user mouses over it
trayIcon.Visible = false;
- process.Kill();
+ process?.Kill();
+ Application.Exit();
}
void ProcessExited(object sender, EventArgs e) {
diff --git a/extra/exe-builder/UptimeKuma.csproj b/extra/exe-builder/UptimeKuma.csproj
index c3c6aad27..aa0a8bf85 100644
--- a/extra/exe-builder/UptimeKuma.csproj
+++ b/extra/exe-builder/UptimeKuma.csproj
@@ -51,8 +51,17 @@
+
+ Form
+
+
+ DownloadForm.cs
+
+
+ DownloadForm.cs
+
ResXFileCodeGenerator
Resources.Designer.cs
From 655ba015a07c87e7b1a24ce8cafbe171103acad1 Mon Sep 17 00:00:00 2001
From: Louis Lam
Date: Sat, 8 Oct 2022 23:47:27 +0800
Subject: [PATCH 11/58] WIP
---
extra/exe-builder/DownloadForm.cs | 127 ++++++++++++++++++++++++++--
extra/exe-builder/Program.cs | 2 +-
extra/exe-builder/UptimeKuma.csproj | 3 +-
3 files changed, 121 insertions(+), 11 deletions(-)
diff --git a/extra/exe-builder/DownloadForm.cs b/extra/exe-builder/DownloadForm.cs
index 9c740e312..5bb88b6d3 100644
--- a/extra/exe-builder/DownloadForm.cs
+++ b/extra/exe-builder/DownloadForm.cs
@@ -1,14 +1,19 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
+using System.Diagnostics;
using System.IO;
+using System.IO.Compression;
using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
using System.Windows.Forms;
namespace UptimeKuma {
public partial class DownloadForm : Form {
private readonly Queue downloadQueue = new();
private readonly WebClient webClient = new();
+ private DownloadItem currentDownloadItem;
public DownloadForm() {
InitializeComponent();
@@ -18,17 +23,19 @@ namespace UptimeKuma {
webClient.DownloadProgressChanged += DownloadProgressChanged;
webClient.DownloadFileCompleted += DownloadFileCompleted;
- if (!File.Exists("node")) {
+ if (!Directory.Exists("node")) {
downloadQueue.Enqueue(new DownloadItem {
URL = "https://nodejs.org/dist/v16.17.1/node-v16.17.1-win-x64.zip",
- Filename = "node.zip"
+ Filename = "node.zip",
+ TargetFolder = "node"
});
}
- if (!File.Exists("node")) {
+ if (!Directory.Exists("node")) {
downloadQueue.Enqueue(new DownloadItem {
URL = "https://github.com/louislam/uptime-kuma/archive/refs/tags/1.18.3.zip",
- Filename = "core.zip"
+ Filename = "core.zip",
+ TargetFolder = "core"
});
}
@@ -38,28 +45,130 @@ namespace UptimeKuma {
void DownloadNextFile() {
if (downloadQueue.Count > 0) {
var item = downloadQueue.Dequeue();
- label.Text = item.URL;
- webClient.DownloadFileAsync(new Uri(item.URL), item.Filename);
+
+ currentDownloadItem = item;
+
+ // Download if the zip file is not existing
+ if (!File.Exists(item.Filename)) {
+ label.Text = item.URL;
+ webClient.DownloadFileAsync(new Uri(item.URL), item.Filename);
+ } else {
+ progressBar.Value = 100;
+ label.Text = "Use local " + item.Filename;
+ DownloadFileCompleted(null, null);
+ }
} else {
- // TODO: Finished, extract?
+ npmSetup();
+ }
+ }
+
+ void npmSetup() {
+ if (Directory.Exists("core/node_modules")) {
+ // Application.Restart();
}
+
+ label.Text = "npm run setup";
+ progressBar.Value = 50;
+ labelData.Text = "";
+
+ var startInfo = new ProcessStartInfo {
+ FileName = "cmd.exe",
+ Arguments = "run setup",
+ RedirectStandardOutput = false,
+ RedirectStandardError = false,
+ RedirectStandardInput = true,
+ UseShellExecute = false,
+ CreateNoWindow = false,
+ WorkingDirectory = "core"
+ };
+
+ var process = new Process();
+ process.StartInfo = startInfo;
+ process.EnableRaisingEvents = true;
+ process.Exited += (object _, EventArgs e) => {
+ // Application.Restart();
+ progressBar.Value = 100;
+
+ if (process.ExitCode == 0) {
+ label.Text = "Done";
+ } else {
+ label.Text = "Failed, exit code: " + process.ExitCode;
+ }
+
+ };
+ process.Start();
+ process.StandardInput.WriteLine("\"../node/npm\" run setup");
}
void DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) {
progressBar.Value = e.ProgressPercentage;
var total = e.TotalBytesToReceive / 1024;
var current = e.BytesReceived / 1024;
- labelData.Text = $"{current}KB/{total}KB";
+
+ if (total > 0) {
+ labelData.Text = $"{current}KB/{total}KB";
+ }
}
- void DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) {
+ async void DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) {
+ Extract(currentDownloadItem);
DownloadNextFile();
}
+
+ void Extract(DownloadItem item) {
+ if (Directory.Exists(item.TargetFolder)) {
+ var dir = new DirectoryInfo(item.TargetFolder);
+ dir.Delete(true);
+ }
+
+ if (Directory.Exists("temp")) {
+ var dir = new DirectoryInfo("temp");
+ dir.Delete(true);
+ }
+
+ labelData.Text = $"Extracting {item.Filename}...";
+
+ ZipFile.ExtractToDirectory(item.Filename, "temp");
+
+ string[] dirList;
+
+ // Move to the correct level
+ dirList = Directory.GetDirectories("temp");
+
+
+
+ if (dirList.Length > 0) {
+ var dir = dirList[0];
+
+ // As sometime ExtractToDirectory is still locking the directory, loop until ok
+ while (true) {
+ try {
+ Directory.Move(dir, item.TargetFolder);
+ break;
+ } catch (Exception exception) {
+ Thread.Sleep(1000);
+ }
+ }
+
+ } else {
+ MessageBox.Show("Unexcepted Error: Cannot move extracted files, folder not found.");
+ }
+
+ labelData.Text = $"Extracted";
+
+ if (Directory.Exists("temp")) {
+ var dir = new DirectoryInfo("temp");
+ dir.Delete(true);
+ }
+
+ File.Delete(item.Filename);
+ }
}
public class DownloadItem {
public string URL { get; set; }
public string Filename { get; set; }
+ public string TargetFolder { get; set; }
}
}
diff --git a/extra/exe-builder/Program.cs b/extra/exe-builder/Program.cs
index 84aa6e456..1b78f0385 100644
--- a/extra/exe-builder/Program.cs
+++ b/extra/exe-builder/Program.cs
@@ -45,7 +45,7 @@ namespace UptimeKuma {
trayIcon.MouseDoubleClick += new MouseEventHandler(Open);
trayIcon.Visible = true;
- if (File.Exists("core") && File.Exists("node")) {
+ if (Directory.Exists("core") && Directory.Exists("node") && Directory.Exists("core/node_modules")) {
// Go go go
StartProcess();
} else {
diff --git a/extra/exe-builder/UptimeKuma.csproj b/extra/exe-builder/UptimeKuma.csproj
index aa0a8bf85..2ad857b2c 100644
--- a/extra/exe-builder/UptimeKuma.csproj
+++ b/extra/exe-builder/UptimeKuma.csproj
@@ -35,11 +35,12 @@
4
- COPY "$(SolutionDir)bin\Debug\uptime-kuma.exe" "C:\Users\LouisLam\Desktop\uptime-kuma-win64\"
+ COPY "$(SolutionDir)bin\Debug\uptime-kuma.exe" "%UserProfile%\Desktop\uptime-kuma-win64\"
+
From a487347b3316cc3192b312a195ac44a46160d310 Mon Sep 17 00:00:00 2001
From: Louis Lam
Date: Sun, 9 Oct 2022 03:47:06 +0800
Subject: [PATCH 12/58] [exe] install dependencies and download dist
---
extra/exe-builder/DownloadForm.cs | 21 +++++++++++----------
extra/exe-builder/Program.cs | 2 +-
2 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/extra/exe-builder/DownloadForm.cs b/extra/exe-builder/DownloadForm.cs
index 5bb88b6d3..f16af4222 100644
--- a/extra/exe-builder/DownloadForm.cs
+++ b/extra/exe-builder/DownloadForm.cs
@@ -63,12 +63,6 @@ namespace UptimeKuma {
}
void npmSetup() {
- if (Directory.Exists("core/node_modules")) {
- // Application.Restart();
- }
-
- label.Text = "npm run setup";
- progressBar.Value = 50;
labelData.Text = "";
var startInfo = new ProcessStartInfo {
@@ -86,10 +80,12 @@ namespace UptimeKuma {
process.StartInfo = startInfo;
process.EnableRaisingEvents = true;
process.Exited += (object _, EventArgs e) => {
- // Application.Restart();
- progressBar.Value = 100;
+ progressBar.Value = 100;
if (process.ExitCode == 0) {
+ Task.Delay(2000).ContinueWith((task) => {
+ Application.Restart();
+ });
label.Text = "Done";
} else {
label.Text = "Failed, exit code: " + process.ExitCode;
@@ -97,7 +93,12 @@ namespace UptimeKuma {
};
process.Start();
- process.StandardInput.WriteLine("\"../node/npm\" run setup");
+ label.Text = "Installing dependencies and download dist files";
+ progressBar.Value = 50;
+
+ process.StandardInput.WriteLine("\"../node/npm\" ci --production");
+ process.StandardInput.WriteLine("\"../node/npm\" run download-dist");
+ process.StandardInput.WriteLine("exit");
}
void DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) {
@@ -110,7 +111,7 @@ namespace UptimeKuma {
}
}
- async void DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) {
+ void DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) {
Extract(currentDownloadItem);
DownloadNextFile();
}
diff --git a/extra/exe-builder/Program.cs b/extra/exe-builder/Program.cs
index 1b78f0385..82c76b05d 100644
--- a/extra/exe-builder/Program.cs
+++ b/extra/exe-builder/Program.cs
@@ -45,7 +45,7 @@ namespace UptimeKuma {
trayIcon.MouseDoubleClick += new MouseEventHandler(Open);
trayIcon.Visible = true;
- if (Directory.Exists("core") && Directory.Exists("node") && Directory.Exists("core/node_modules")) {
+ if (Directory.Exists("core") && Directory.Exists("node") && Directory.Exists("core/node_modules") && Directory.Exists("core/dist")) {
// Go go go
StartProcess();
} else {
From da16796ec45a3ecb12f16a1855bca0813beb2571 Mon Sep 17 00:00:00 2001
From: Nikita Lutsenko
Date: Sat, 19 Nov 2022 16:10:30 -0800
Subject: [PATCH 13/58] Add ability to send Telegram notifications silently.
---
server/notification-providers/telegram.js | 1 +
src/components/notifications/Telegram.vue | 9 +++++++++
src/languages/en.js | 2 ++
3 files changed, 12 insertions(+)
diff --git a/server/notification-providers/telegram.js b/server/notification-providers/telegram.js
index 2b0576224..88923e665 100644
--- a/server/notification-providers/telegram.js
+++ b/server/notification-providers/telegram.js
@@ -13,6 +13,7 @@ class Telegram extends NotificationProvider {
params: {
chat_id: notification.telegramChatID,
text: msg,
+ disable_notification: notification.telegramSendSilently,
},
});
return okMsg;
diff --git a/src/components/notifications/Telegram.vue b/src/components/notifications/Telegram.vue
index 9daf31ac6..4eb014ff7 100644
--- a/src/components/notifications/Telegram.vue
+++ b/src/components/notifications/Telegram.vue
@@ -28,6 +28,15 @@
{{ telegramGetUpdatesURL("masked") }}
+
+
+
+ {{ $t("Send Silently") }}
+
+
+
+ {{ $t("telegramSendSilentlyDescription") }}
+
diff --git a/src/languages/en.js b/src/languages/en.js
index 86abb7912..492689e56 100644
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -214,6 +214,8 @@ export default {
"Chat ID": "Chat ID",
supportTelegramChatID: "Support Direct Chat / Group / Channel's Chat ID",
wayToGetTelegramChatID: "You can get your chat ID by sending a message to the bot and going to this URL to view the chat_id:",
+ "Send Silently": "Send Silently",
+ telegramSendSilentlyDescription: "Sends the message silently. Users will receive a notification with no sound.",
"YOUR BOT TOKEN HERE": "YOUR BOT TOKEN HERE",
chatIDNotFound: "Chat ID is not found; please send a message to this bot first",
webhook: "Webhook",
From b91fe9d96d1b8be9f6abbb241432d53fd9b61247 Mon Sep 17 00:00:00 2001
From: shyneko
Date: Thu, 12 Jan 2023 15:09:05 +0200
Subject: [PATCH 14/58] Added a more telegram options such as thread id, silent
notifications and forward protect
---
server/notification-providers/telegram.js | 17 +++++++++---
src/components/notifications/Telegram.vue | 34 +++++++++++++++++++++++
src/languages/en.js | 6 ++++
3 files changed, 53 insertions(+), 4 deletions(-)
diff --git a/server/notification-providers/telegram.js b/server/notification-providers/telegram.js
index 2b0576224..9c8f57501 100644
--- a/server/notification-providers/telegram.js
+++ b/server/notification-providers/telegram.js
@@ -9,11 +9,20 @@ class Telegram extends NotificationProvider {
let okMsg = "Sent Successfully.";
try {
+ const paramsObj =
+ {
+ chat_id: notification.telegramChatID,
+ text: msg,
+ disable_notification: notification.telegramSilentNotification ?? false,
+ protect_content: notification.telegramProtectContent ?? false,
+
+ };
+ // if telegramChatThread specified, then add it to paramsObj
+ if (notification.telegramChatThread && notification.telegramChatThread.length > 0) {
+ paramsObj.message_thread_id = notification.telegramChatThread;
+ }
await axios.get(`https://api.telegram.org/bot${notification.telegramBotToken}/sendMessage`, {
- params: {
- chat_id: notification.telegramChatID,
- text: msg,
- },
+ params: paramsObj
});
return okMsg;
diff --git a/src/components/notifications/Telegram.vue b/src/components/notifications/Telegram.vue
index 9daf31ac6..9b373b997 100644
--- a/src/components/notifications/Telegram.vue
+++ b/src/components/notifications/Telegram.vue
@@ -29,6 +29,40 @@
+
+
+
{{ $t("Thread ID") }}
+
+
+
+
+
+
+ {{ $t("Thread ID Description") }}
+
+
+
+
+
+
+ {{ $t("Silent Notification") }}
+
+
+
+ {{ $t("Silent Notification Description") }}
+
+
+
+
+
+
+ {{ $t("Protect Forwarding") }}
+
+
+
+ {{ $t("Protect Forwarding Description") }}
+
+