diff --git a/.air.toml b/.air.toml
index 0d1e7033f..cb229bcce 100644
--- a/.air.toml
+++ b/.air.toml
@@ -7,13 +7,13 @@ tmp_dir = "tmp"
[build]
# Just plain old shell command. You could use `make` as well.
-cmd = "go build -ldflags=\"-X 'github.com/0xJacky/Nginx-UI/server/settings.buildTime=$(date +%s)'\" -o ./tmp/main ."
+cmd = "CGO_ENABLED=1 go build -ldflags=\"-X 'github.com/0xJacky/Nginx-UI/server/settings.buildTime=$(date +%s)'\" -o ./tmp/main ."
# Binary file yields from `cmd`.
bin = "tmp/main"
# Customize binary.
full_bin = "APP_ENV=dev APP_USER=air ./tmp/main"
# Watch these filename extensions.
-include_ext = ["go", "tpl", "tmpl", "html", "conf"]
+include_ext = ["go", "tpl", "tmpl", "html", "conf", "ini", "toml"]
# Ignore these filename extensions or directories.
exclude_dir = ["assets", "tmp", "vendor", "frontend/node_modules", "upload"]
# Watch these directories if you specified.
diff --git a/dev.Dockerfile b/dev.Dockerfile
new file mode 100644
index 000000000..ab9d16a1a
--- /dev/null
+++ b/dev.Dockerfile
@@ -0,0 +1,27 @@
+FROM --platform=linux/amd64 ubuntu:latest
+
+WORKDIR /app
+EXPOSE 80 443
+
+COPY resources/development/sources.list /etc/apt/sources.list
+
+RUN set -x \
+# create nginx user/group first, to be consistent throughout docker variants
+ && addgroup --system --gid 101 nginx \
+ && adduser --system --disabled-login --ingroup nginx --no-create-home --home /nonexistent --gecos "nginx user" --shell /bin/false --uid 101 nginx \
+ && apt update && apt install -y wget nginx gcc curl
+
+RUN wget https://go.dev/dl/go1.20.linux-amd64.tar.gz && \
+ rm -rf /usr/local/go && tar -C /usr/local -xzf go1.20.linux-amd64.tar.gz && rm -f go1.20.linux-amd64.tar.gz
+
+ENV PATH="${PATH}:/usr/local/go/bin"
+
+RUN go install github.com/cosmtrek/air@latest
+
+COPY resources/development/entrypoint.sh /entrypoint.sh
+
+RUN chmod a+x /entrypoint.sh \
+ && rm -f /etc/nginx/conf.d/default.conf \
+ && rm -f /usr/etc/nginx/conf.d/default.conf
+
+CMD ["/entrypoint.sh"]
diff --git a/frontend/package.json b/frontend/package.json
index f1b04bdce..542a67459 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,7 +1,7 @@
{
"name": "nginx-ui-frontend-next",
"private": true,
- "version": "1.7.5",
+ "version": "1.7.6",
"type": "commonjs",
"scripts": {
"dev": "vite",
diff --git a/frontend/src/api/domain.ts b/frontend/src/api/domain.ts
index fceab1793..201ea8b9a 100644
--- a/frontend/src/api/domain.ts
+++ b/frontend/src/api/domain.ts
@@ -14,8 +14,8 @@ class Domain extends Curd {
return http.get('template')
}
- add_auto_cert(domain: string) {
- return http.post('auto_cert/' + domain)
+ add_auto_cert(domain: string, data: any) {
+ return http.post('auto_cert/' + domain, data)
}
remove_auto_cert(domain: string) {
diff --git a/frontend/src/components/StdDataDisplay/StdTable.vue b/frontend/src/components/StdDataDisplay/StdTable.vue
index f7611d4ed..1d64e96b5 100644
--- a/frontend/src/components/StdDataDisplay/StdTable.vue
+++ b/frontend/src/components/StdDataDisplay/StdTable.vue
@@ -525,7 +525,7 @@ function initSortable() {
:okText="$gettext('OK')"
:title="$gettext('Are you sure you want to delete?')"
@confirm="destroy(record[rowKey])">
- Delete
+ {{ $gettext('Delete') }}
diff --git a/frontend/src/language/en/app.po b/frontend/src/language/en/app.po
index 1eff1d907..c34f73157 100644
--- a/frontend/src/language/en/app.po
+++ b/frontend/src/language/en/app.po
@@ -17,8 +17,8 @@ msgstr "About"
msgid "Access Logs"
msgstr ""
-#: src/views/cert/Cert.vue:78 src/views/config/config.ts:36
-#: src/views/domain/DomainList.vue:47 src/views/user/User.vue:43
+#: src/views/cert/Cert.vue:74 src/views/config/config.ts:36
+#: src/views/domain/DomainList.vue:48 src/views/user/User.vue:43
msgid "Action"
msgstr "Action"
@@ -85,11 +85,11 @@ msgstr ""
msgid "Auto"
msgstr ""
-#: src/views/cert/Cert.vue:41 src/views/domain/cert/ChangeCert.vue:35
+#: src/views/cert/Cert.vue:37 src/views/domain/cert/ChangeCert.vue:35
msgid "Auto Cert"
msgstr ""
-#: src/views/cert/Cert.vue:8
+#: src/views/cert/Cert.vue:9
msgid "Auto cert is enabled, please do not modify this certification."
msgstr ""
@@ -97,6 +97,10 @@ msgstr ""
msgid "Auto Refresh"
msgstr ""
+#: src/views/cert/Cert.vue:27
+msgid "Auto-Cert Log"
+msgstr ""
+
#: src/views/domain/cert/IssueCert.vue:71
msgid "Auto-renewal disabled for %{name}"
msgstr "Auto-renewal disabled for %{name}"
@@ -152,7 +156,7 @@ msgstr "Certificate has expired"
msgid "Certificate is valid"
msgstr "Certificate is valid"
-#: src/views/cert/Cert.vue:12 src/views/domain/cert/Cert.vue:35
+#: src/views/cert/Cert.vue:34 src/views/domain/cert/Cert.vue:35
msgid "Certificate Status"
msgstr "Certificate Status"
@@ -176,10 +180,15 @@ msgstr ""
#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:34
#: src/views/domain/ngx_conf/LocationEditor.vue:35
#: src/views/domain/ngx_conf/LocationEditor.vue:52
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:180
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:181
msgid "Comments"
msgstr "Comments"
+#: src/views/cert/Cert.vue:32
+#, fuzzy
+msgid "Config Name"
+msgstr "Configuration Name"
+
#: src/views/domain/ngx_conf/ConfigTemplate.vue:61
#, fuzzy
msgid "Config Templates"
@@ -258,7 +267,13 @@ msgstr "Dashboard"
msgid "Database (Optional, default: database)"
msgstr "Database (Optional, default: database)"
-#: src/components/StdDataDisplay/StdTable.vue:528
+#: src/components/StdDataDisplay/StdTable.vue:31
+#: src/components/StdDataDisplay/StdTable.vue:32
+#: src/components/StdDataDisplay/StdTable.vue:37
+#: src/components/StdDataDisplay/StdTable.vue:50
+#: src/components/StdDataDisplay/StdTable.vue:52
+#: src/components/StdDataDisplay/StdTable.vue:53
+#: src/components/StdDataDisplay/StdTable.vue:57
#: src/views/domain/DomainList.vue:19 src/views/domain/DomainList.vue:20
#: src/views/domain/DomainList.vue:21 src/views/domain/DomainList.vue:28
#: src/views/domain/DomainList.vue:32
@@ -269,7 +284,7 @@ msgstr ""
msgid "Delete ID: %{id}"
msgstr ""
-#: src/views/domain/DomainList.vue:81
+#: src/views/domain/DomainList.vue:82
msgid "Delete site: %{site_name}"
msgstr ""
@@ -304,15 +319,15 @@ msgstr "Directives"
msgid "Disable auto-renewal failed for %{name}"
msgstr "Disable auto-renewal failed for %{name}"
-#: src/views/cert/Cert.vue:51 src/views/domain/cert/ChangeCert.vue:45
+#: src/views/cert/Cert.vue:47 src/views/domain/cert/ChangeCert.vue:45
#: src/views/domain/DomainEdit.vue:10 src/views/domain/DomainEdit.vue:9
-#: src/views/domain/DomainList.vue:16 src/views/domain/DomainList.vue:34
+#: src/views/domain/DomainList.vue:16 src/views/domain/DomainList.vue:35
#: src/views/domain/DomainList.vue:7 src/views/domain/DomainList.vue:8
#: src/views/domain/DomainList.vue:9
msgid "Disabled"
msgstr "Disabled"
-#: src/views/domain/DomainEdit.vue:146 src/views/domain/DomainList.vue:69
+#: src/views/domain/DomainEdit.vue:146 src/views/domain/DomainList.vue:70
msgid "Disabled successfully"
msgstr "Disabled successfully"
@@ -320,14 +335,14 @@ msgstr "Disabled successfully"
msgid "Disk IO"
msgstr "Disk IO"
-#: src/views/cert/Cert.vue:32
-msgid "Domain"
-msgstr ""
-
#: src/views/domain/DomainAdd.vue:58
msgid "Domain Config Created Successfully"
msgstr "Domain Config Created Successfully"
+#: src/views/cert/Cert.vue:21
+msgid "Domains list is empty, try to reopen auto-cert for %{config}"
+msgstr ""
+
#: src/language/constants.ts:26
msgid "Download latest release error"
msgstr ""
@@ -338,9 +353,15 @@ msgstr ""
#: src/views/domain/DomainList.vue:14 src/views/domain/DomainList.vue:15
#: src/views/domain/DomainList.vue:16 src/views/domain/DomainList.vue:23
+#: src/views/domain/SiteDuplicate.vue:2
msgid "Duplicate"
msgstr ""
+#: src/views/domain/SiteDuplicate.vue:43
+#, fuzzy
+msgid "Duplicated successfully"
+msgstr "Saved successfully"
+
#: src/views/domain/DomainEdit.vue:4 src/views/domain/DomainEdit.vue:5
msgid "Edit %{n}"
msgstr "Edit %{n}"
@@ -369,16 +390,16 @@ msgstr "Enable failed"
msgid "Enable TLS"
msgstr "Enable TLS"
-#: src/views/cert/Cert.vue:48 src/views/domain/cert/ChangeCert.vue:42
+#: src/views/cert/Cert.vue:44 src/views/domain/cert/ChangeCert.vue:42
#: src/views/domain/DomainEdit.vue:43 src/views/domain/DomainEdit.vue:6
#: src/views/domain/DomainEdit.vue:7 src/views/domain/DomainList.vue:10
#: src/views/domain/DomainList.vue:11 src/views/domain/DomainList.vue:12
-#: src/views/domain/DomainList.vue:19 src/views/domain/DomainList.vue:31
+#: src/views/domain/DomainList.vue:19 src/views/domain/DomainList.vue:32
msgid "Enabled"
msgstr "Enabled"
#: src/views/domain/DomainAdd.vue:47 src/views/domain/DomainEdit.vue:137
-#: src/views/domain/DomainList.vue:59
+#: src/views/domain/DomainList.vue:60
msgid "Enabled successfully"
msgstr "Enabled successfully"
@@ -410,11 +431,11 @@ msgstr "Expiration Date: %{date}"
msgid "Export"
msgstr ""
-#: src/views/domain/DomainEdit.vue:149 src/views/domain/DomainList.vue:73
+#: src/views/domain/DomainEdit.vue:149 src/views/domain/DomainList.vue:74
msgid "Failed to disable %{msg}"
msgstr "Failed to disable %{msg}"
-#: src/views/domain/DomainEdit.vue:140 src/views/domain/DomainList.vue:63
+#: src/views/domain/DomainEdit.vue:140 src/views/domain/DomainList.vue:64
msgid "Failed to enable %{msg}"
msgstr "Failed to enable %{msg}"
@@ -609,7 +630,7 @@ msgstr "Single Directive"
#: src/views/cert/Cert.vue:16 src/views/config/config.ts:9
#: src/views/domain/cert/ChangeCert.vue:19 src/views/domain/DomainEdit.vue:46
-#: src/views/domain/DomainList.vue:15
+#: src/views/domain/DomainList.vue:16 src/views/domain/SiteDuplicate.vue:5
msgid "Name"
msgstr "Name"
@@ -736,6 +757,12 @@ msgstr ""
msgid "Performing core upgrade"
msgstr ""
+#: src/views/domain/SiteDuplicate.vue:28
+msgid ""
+"Please input name, this will be used as the filename of the new "
+"configuration!"
+msgstr ""
+
#: src/views/other/Install.vue:36
msgid "Please input your E-mail!"
msgstr "Please input your E-mail!"
@@ -885,9 +912,8 @@ msgstr "Send"
#: src/components/StdDataDisplay/StdTable.vue:168
#: src/components/StdDataDisplay/StdTable.vue:343
#: src/components/StdDataDisplay/StdTable.vue:463
-#: src/views/config/ConfigEdit.vue:32 src/views/domain/DomainEdit.vue:87
-#: src/views/domain/DomainList.vue:83 src/views/other/Install.vue:71
-#: src/views/preference/Preference.vue:41
+#: src/views/config/ConfigEdit.vue:32 src/views/domain/DomainList.vue:84
+#: src/views/other/Install.vue:71 src/views/preference/Preference.vue:41
msgid "Server error"
msgstr "Server error"
@@ -917,27 +943,27 @@ msgstr "Sites List"
msgid "Sites List"
msgstr "Sites List"
-#: src/views/cert/Cert.vue:65
+#: src/views/cert/Cert.vue:61
#, fuzzy
msgid "SSL Certificate Key Path"
msgstr "Certificate Status"
-#: src/views/cert/Cert.vue:58
+#: src/views/cert/Cert.vue:54
#, fuzzy
msgid "SSL Certificate Path"
msgstr "Certificate Status"
-#: src/views/cert/Cert.vue:19
+#: src/views/cert/Cert.vue:41
#, fuzzy
msgid "SSL Certification Content"
msgstr "Certificate Status"
-#: src/views/cert/Cert.vue:22
+#: src/views/cert/Cert.vue:44
#, fuzzy
msgid "SSL Certification Key Content"
msgstr "Certificate Status"
-#: src/views/domain/DomainList.vue:24
+#: src/views/domain/DomainList.vue:25
msgid "Status"
msgstr "Status"
@@ -1003,12 +1029,16 @@ msgstr ""
msgid "Theme"
msgstr ""
+#: src/views/cert/Cert.vue:15
+msgid "This auto-cert item is invalid, please remove it."
+msgstr ""
+
#: src/views/config/config.ts:14
msgid "Type"
msgstr ""
-#: src/views/cert/Cert.vue:72 src/views/config/config.ts:29
-#: src/views/domain/DomainList.vue:41 src/views/user/User.vue:37
+#: src/views/cert/Cert.vue:68 src/views/config/config.ts:29
+#: src/views/domain/DomainList.vue:42 src/views/user/User.vue:37
msgid "Updated at"
msgstr "Updated at"
diff --git a/frontend/src/language/messages.pot b/frontend/src/language/messages.pot
index 63fecf1bd..7d25bcac7 100644
--- a/frontend/src/language/messages.pot
+++ b/frontend/src/language/messages.pot
@@ -11,9 +11,9 @@ msgstr ""
msgid "Access Logs"
msgstr ""
-#: src/views/cert/Cert.vue:78
+#: src/views/cert/Cert.vue:74
#: src/views/config/config.ts:36
-#: src/views/domain/DomainList.vue:47
+#: src/views/domain/DomainList.vue:48
#: src/views/user/User.vue:43
msgid "Action"
msgstr ""
@@ -83,12 +83,12 @@ msgstr ""
msgid "Auto"
msgstr ""
-#: src/views/cert/Cert.vue:41
+#: src/views/cert/Cert.vue:37
#: src/views/domain/cert/ChangeCert.vue:35
msgid "Auto Cert"
msgstr ""
-#: src/views/cert/Cert.vue:8
+#: src/views/cert/Cert.vue:9
msgid "Auto cert is enabled, please do not modify this certification."
msgstr ""
@@ -96,6 +96,10 @@ msgstr ""
msgid "Auto Refresh"
msgstr ""
+#: src/views/cert/Cert.vue:27
+msgid "Auto-Cert Log"
+msgstr ""
+
#: src/views/domain/cert/IssueCert.vue:71
msgid "Auto-renewal disabled for %{name}"
msgstr ""
@@ -153,7 +157,7 @@ msgstr ""
msgid "Certificate is valid"
msgstr ""
-#: src/views/cert/Cert.vue:12
+#: src/views/cert/Cert.vue:34
#: src/views/domain/cert/Cert.vue:35
msgid "Certificate Status"
msgstr ""
@@ -179,10 +183,14 @@ msgstr ""
#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:34
#: src/views/domain/ngx_conf/LocationEditor.vue:35
#: src/views/domain/ngx_conf/LocationEditor.vue:52
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:180
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:181
msgid "Comments"
msgstr ""
+#: src/views/cert/Cert.vue:32
+msgid "Config Name"
+msgstr ""
+
#: src/views/domain/ngx_conf/ConfigTemplate.vue:61
msgid "Config Templates"
msgstr ""
@@ -263,7 +271,13 @@ msgstr ""
msgid "Database (Optional, default: database)"
msgstr ""
-#: src/components/StdDataDisplay/StdTable.vue:528
+#: src/components/StdDataDisplay/StdTable.vue:31
+#: src/components/StdDataDisplay/StdTable.vue:32
+#: src/components/StdDataDisplay/StdTable.vue:37
+#: src/components/StdDataDisplay/StdTable.vue:50
+#: src/components/StdDataDisplay/StdTable.vue:52
+#: src/components/StdDataDisplay/StdTable.vue:53
+#: src/components/StdDataDisplay/StdTable.vue:57
#: src/views/domain/DomainList.vue:19
#: src/views/domain/DomainList.vue:20
#: src/views/domain/DomainList.vue:21
@@ -276,7 +290,7 @@ msgstr ""
msgid "Delete ID: %{id}"
msgstr ""
-#: src/views/domain/DomainList.vue:81
+#: src/views/domain/DomainList.vue:82
msgid "Delete site: %{site_name}"
msgstr ""
@@ -312,12 +326,12 @@ msgstr ""
msgid "Disable auto-renewal failed for %{name}"
msgstr ""
-#: src/views/cert/Cert.vue:51
+#: src/views/cert/Cert.vue:47
#: src/views/domain/cert/ChangeCert.vue:45
#: src/views/domain/DomainEdit.vue:10
#: src/views/domain/DomainEdit.vue:9
#: src/views/domain/DomainList.vue:16
-#: src/views/domain/DomainList.vue:34
+#: src/views/domain/DomainList.vue:35
#: src/views/domain/DomainList.vue:7
#: src/views/domain/DomainList.vue:8
#: src/views/domain/DomainList.vue:9
@@ -325,7 +339,7 @@ msgid "Disabled"
msgstr ""
#: src/views/domain/DomainEdit.vue:146
-#: src/views/domain/DomainList.vue:69
+#: src/views/domain/DomainList.vue:70
msgid "Disabled successfully"
msgstr ""
@@ -333,14 +347,14 @@ msgstr ""
msgid "Disk IO"
msgstr ""
-#: src/views/cert/Cert.vue:32
-msgid "Domain"
-msgstr ""
-
#: src/views/domain/DomainAdd.vue:58
msgid "Domain Config Created Successfully"
msgstr ""
+#: src/views/cert/Cert.vue:21
+msgid "Domains list is empty, try to reopen auto-cert for %{config}"
+msgstr ""
+
#: src/language/constants.ts:26
msgid "Download latest release error"
msgstr ""
@@ -353,9 +367,14 @@ msgstr ""
#: src/views/domain/DomainList.vue:15
#: src/views/domain/DomainList.vue:16
#: src/views/domain/DomainList.vue:23
+#: src/views/domain/SiteDuplicate.vue:2
msgid "Duplicate"
msgstr ""
+#: src/views/domain/SiteDuplicate.vue:43
+msgid "Duplicated successfully"
+msgstr ""
+
#: src/views/domain/DomainEdit.vue:4
#: src/views/domain/DomainEdit.vue:5
msgid "Edit %{n}"
@@ -386,7 +405,7 @@ msgstr ""
msgid "Enable TLS"
msgstr ""
-#: src/views/cert/Cert.vue:48
+#: src/views/cert/Cert.vue:44
#: src/views/domain/cert/ChangeCert.vue:42
#: src/views/domain/DomainEdit.vue:43
#: src/views/domain/DomainEdit.vue:6
@@ -395,13 +414,13 @@ msgstr ""
#: src/views/domain/DomainList.vue:11
#: src/views/domain/DomainList.vue:12
#: src/views/domain/DomainList.vue:19
-#: src/views/domain/DomainList.vue:31
+#: src/views/domain/DomainList.vue:32
msgid "Enabled"
msgstr ""
#: src/views/domain/DomainAdd.vue:47
#: src/views/domain/DomainEdit.vue:137
-#: src/views/domain/DomainList.vue:59
+#: src/views/domain/DomainList.vue:60
msgid "Enabled successfully"
msgstr ""
@@ -437,12 +456,12 @@ msgid "Export"
msgstr ""
#: src/views/domain/DomainEdit.vue:149
-#: src/views/domain/DomainList.vue:73
+#: src/views/domain/DomainList.vue:74
msgid "Failed to disable %{msg}"
msgstr ""
#: src/views/domain/DomainEdit.vue:140
-#: src/views/domain/DomainList.vue:63
+#: src/views/domain/DomainList.vue:64
msgid "Failed to enable %{msg}"
msgstr ""
@@ -636,7 +655,8 @@ msgstr ""
#: src/views/config/config.ts:9
#: src/views/domain/cert/ChangeCert.vue:19
#: src/views/domain/DomainEdit.vue:46
-#: src/views/domain/DomainList.vue:15
+#: src/views/domain/DomainList.vue:16
+#: src/views/domain/SiteDuplicate.vue:5
msgid "Name"
msgstr ""
@@ -765,6 +785,10 @@ msgstr ""
msgid "Performing core upgrade"
msgstr ""
+#: src/views/domain/SiteDuplicate.vue:28
+msgid "Please input name, this will be used as the filename of the new configuration!"
+msgstr ""
+
#: src/views/other/Install.vue:36
msgid "Please input your E-mail!"
msgstr ""
@@ -924,8 +948,7 @@ msgstr ""
#: src/components/StdDataDisplay/StdTable.vue:343
#: src/components/StdDataDisplay/StdTable.vue:463
#: src/views/config/ConfigEdit.vue:32
-#: src/views/domain/DomainEdit.vue:87
-#: src/views/domain/DomainList.vue:83
+#: src/views/domain/DomainList.vue:84
#: src/views/other/Install.vue:71
#: src/views/preference/Preference.vue:41
msgid "Server error"
@@ -957,23 +980,23 @@ msgstr ""
msgid "Sites List"
msgstr ""
-#: src/views/cert/Cert.vue:65
+#: src/views/cert/Cert.vue:61
msgid "SSL Certificate Key Path"
msgstr ""
-#: src/views/cert/Cert.vue:58
+#: src/views/cert/Cert.vue:54
msgid "SSL Certificate Path"
msgstr ""
-#: src/views/cert/Cert.vue:19
+#: src/views/cert/Cert.vue:41
msgid "SSL Certification Content"
msgstr ""
-#: src/views/cert/Cert.vue:22
+#: src/views/cert/Cert.vue:44
msgid "SSL Certification Key Content"
msgstr ""
-#: src/views/domain/DomainList.vue:24
+#: src/views/domain/DomainList.vue:25
msgid "Status"
msgstr ""
@@ -1030,13 +1053,17 @@ msgstr ""
msgid "Theme"
msgstr ""
+#: src/views/cert/Cert.vue:15
+msgid "This auto-cert item is invalid, please remove it."
+msgstr ""
+
#: src/views/config/config.ts:14
msgid "Type"
msgstr ""
-#: src/views/cert/Cert.vue:72
+#: src/views/cert/Cert.vue:68
#: src/views/config/config.ts:29
-#: src/views/domain/DomainList.vue:41
+#: src/views/domain/DomainList.vue:42
#: src/views/user/User.vue:37
msgid "Updated at"
msgstr ""
diff --git a/frontend/src/language/translations.json b/frontend/src/language/translations.json
index 4bcc3df6e..afca28657 100644
--- a/frontend/src/language/translations.json
+++ b/frontend/src/language/translations.json
@@ -1 +1 @@
-{"en":{"About":"About","Action":"Action","Add Directive Below":"Add Directive Below","Add Location":"Add Location","Add Site":"Add Site","Advance Mode":"Advance Mode","Are you sure you want to remove this directive?":"Are you sure you want to remove this directive?","Auto-renewal disabled for %{name}":"Auto-renewal disabled for %{name}","Auto-renewal enabled for %{name}":"Auto-renewal enabled for %{name}","Back":"Back","Base information":"Base information","Basic Mode":"Basic Mode","Build with":"Build with","Cancel":"Cancel","Certificate has expired":"Certificate has expired","Certificate is valid":"Certificate is valid","Certificate Status":"Certificate Status","Comments":"Comments","Configuration Name":"Configuration Name","Configurations":"Configurations","Configure SSL":"Configure SSL","Content":"Content","CPU Status":"CPU Status","CPU:":"CPU:","Create Another":"Create Another","Created at":"Created at","Dashboard":"Dashboard","Database (Optional, default: database)":"Database (Optional, default: database)","Development Mode":"Development Mode","Directive":"Directive","Directives":"Directives","Disable auto-renewal failed for %{name}":"Disable auto-renewal failed for %{name}","Disabled":"Disabled","Disabled successfully":"Disabled successfully","Disk IO":"Disk IO","Domain Config Created Successfully":"Domain Config Created Successfully","Edit %{n}":"Edit %{n}","Edit Configuration":"Edit Configuration","Edit Site":"Edit Site","Email (*)":"Email (*)","Enable auto-renewal failed for %{name}":"Enable auto-renewal failed for %{name}","Enable failed":"Enable failed","Enable TLS":"Enable TLS","Enabled":"Enabled","Enabled successfully":"Enabled successfully","Encrypt website with Let's Encrypt":"Encrypt website with Let's Encrypt","Expiration Date: %{date}":"Expiration Date: %{date}","Failed to disable %{msg}":"Failed to disable %{msg}","Failed to enable %{msg}":"Failed to enable %{msg}","File Not Found":"File Not Found","Finished":"Finished","Getting the certificate, please wait...":"Getting the certificate, please wait...","Home":"Home","Install":"Install","Intermediate Certification Authorities: %{issuer}":"Intermediate Certification Authorities: %{issuer}","Leave blank for no change":"Leave blank for no change","Load Averages:":"Load Averages:","Location":"Location","Locations":"Locations","Login":"Login","Login successful":"Login successful","Logout successful":"Logout successful","Make sure you have configured a reverse proxy for .well-known directory to HTTPChallengePort (default: 9180) before getting the certificate.":"Make sure you have configured a reverse proxy for .well-known directory to HTTPChallengePort (default: 9180) before getting the certificate.","Manage Configs":"Manage Configs","Manage Sites":"Manage Sites","Manage Users":"Manage Users","Memory":"Memory","Memory and Storage":"Memory and Storage","Modify Config":"Modify Config","Name":"Name","Network":"Network","Network Statistics":"Network Statistics","Network Total Receive":"Network Total Receive","Network Total Send":"Network Total Send","Next":"Next","No":"No","Not Found":"Not Found","Not Valid Before: %{date}":"Not Valid Before: %{date}","OS:":"OS:","Params":"Params","Password":"Password","Password (*)":"Password (*)","Path":"Path","Please input your E-mail!":"Please input your E-mail!","Please input your password!":"Please input your password!","Please input your username!":"Please input your username!","Project Team":"Project Team","Reads":"Reads","Receive":"Receive","Save":"Save","Save Directive":"Save Directive","Save error %{msg}":"Save error %{msg}","Saved successfully":"Saved successfully","Send":"Send","Server error":"Server error","Server Info":"Server Info","server_name not found in directives":"server_name not found in directives","server_name parameter is required":"server_name parameter is required","Single Directive":"Single Directive","Sites List":"Sites List","Status":"Status","Storage":"Storage","Subject Name: %{name}":"Subject Name: %{name}","Swap":"Swap","Terminal":"Terminal","The certificate for the domain will be checked every hour, and will be renewed if it has been more than 1 month since it was last issued.":"The certificate for the domain will be checked every hour, and will be renewed if it has been more than 1 month since it was last issued.","The filename cannot contain the following characters: %{c}":"The filename cannot contain the following characters: %{c}","Updated at":"Updated at","Uptime:":"Uptime:","Username":"Username","Username (*)":"Username (*)","Warning":"Warning","Writes":"Writes","Yes":"Yes","License":{"Project":"License"}},"zh_TW":{"About":"關於","Access Logs":"訪問日誌","Action":"操作","Add":"新增","Add Directive Below":"在下面新增指令","Add Location":"新增 Location","Add Site":"新增站點","Advance Mode":"高階模式","Arch":"架搆","Are you sure you want to delete?":"你確定你要刪除?","Are you sure you want to remove this directive?":"您確定要刪除這條指令?","Are you sure you want to remove this location?":"您確定要刪除此 Location 嗎?","Author":"作者","Auto":"自動","Auto Cert":"自動更新","Auto cert is enabled, please do not modify this certification.":"自動證書已啟用,請不要修改此證書。","Auto Refresh":"自動刷新","Auto-renewal disabled for %{name}":"已關閉 %{name} 自動續簽","Auto-renewal enabled for %{name}":"已啟用 %{name} 自動續簽","Back":"返回","Back Home":"回到首頁","Base information":"基本訊息","Basic Mode":"基本模式","Batch Modify":"批量修改","Build with":"構建基於","Cancel":"取消","Certificate has expired":"此憑證已過期","Certificate is valid":"此憑證有效","Certificate Status":"憑證狀態","Certification":"證照","Change Certificate":"更改證書","Check again":"再次檢查","Comments":"註釋","Config Templates":"配置模板","Configuration file is test successful":"配置文件測試成功","Configuration Name":"配置名稱","Configurations":"配置","Configure SSL":"配置 SSL","Content":"內容","Core Upgrade":"核心升級","CPU Status":"中央處理器狀態","CPU:":"中央處理器:","Create Another":"再創建一個","Created at":"建立時間","Creating client facilitates communication with the CA server":"創建客戶端方便與CA服務器通信","Current Version":"當前版本","Custom":"自訂","Dark":"深色","Dashboard":"儀表盤","Database (Optional, default: database)":"資料庫 (可選,預設: database)","Delete":"刪除","Delete ID: %{id}":"刪除 ID: %{id}","Delete site: %{site_name}":"刪除站點:%{site_name}","Description":"敘述","Development Mode":"開發模式","Dir":"目錄","Directive":"指令","Directives":"指令","Disable auto-renewal failed for %{name}":"關閉 %{name} 自動續簽失敗","Disabled":"禁用","Disabled successfully":"禁用成功","Disk IO":"磁碟 IO","Domain":"網域","Domain Config Created Successfully":"域名配置文件創建成功","Download latest release error":"下載最新版本錯誤","Downloading latest release":"正在下載最新版本","Duplicate":"複製","Edit %{n}":"編輯 %{n}","Edit Configuration":"編輯配置","Edit Site":"編輯站點","Email (*)":"郵箱 (*)","Enable auto-renewal failed for %{name}":"啟用 %{name} 自動續簽失敗","Enable failed":"啟用失敗","Enable TLS":"啟用 TLS","Enabled":"啟用","Enabled successfully":"啟用成功","Encrypt website with Let's Encrypt":"用 Let's Encrypt 對網站進行加密","Error":"錯誤","Error Logs":"錯誤日志","Executable Path":"可執行檔路徑","Expiration Date: %{date}":"過期時間: %{date}","Export":"導出","Failed to disable %{msg}":"禁用失敗 %{msg}","Failed to enable %{msg}":"啟用失敗 %{msg}","Failed to get certificate information":"獲取證書信息失敗","Failed to save, syntax error(s) was detected in the configuration.":"保存失敗,在配置中檢測到語法錯誤。","File":"文件","File exists":"文件已存在","File Not Found":"未找到檔案","Filter":"篩選","Finished":"完成","Format Code":"格式化代碼","Format error %{msg}":"格式錯誤 %{msg}","Format successfully":"格式化成功","Generate":"生成","Generating private key for registering account":"生成註冊賬號私鑰","Getting the certificate, please wait...":"正在獲取憑證,請稍等...","Home":"首頁","HTTP Challenge Port":"HTTP 挑戰端口","HTTP Port":"HTTP 監聽埠","Initial core upgrader error":"初始核心升級程序錯誤","Initialing core upgrader":"正在初始化核心升級程序","Inspect Configurations":"檢查配置","Install":"安裝","Install successfully":"安裝成功","Intermediate Certification Authorities: %{issuer}":"中級憑證頒發機構: %{issuer}","Issued certificate successfully":"頒發證書成功","Jwt Secret":"Jwt Secret","Last checked at":"上次檢查時間","Leave blank for no change":"留空表示不修改","Light":"淺色","Load Averages:":"系統負載:","Location":"Location","Locations":"Locations","Login":"登入","Login successful":"登入成功","Logout successful":"登出成功","Make sure you have configured a reverse proxy for .well-known directory to HTTPChallengePort (default: 9180) before getting the certificate.":"在獲取證書前,請確保配置檔案中已將 .well-known 目錄反向代理到 HTTPChallengePort (預設: 9180)","Manage Configs":"配置管理","Manage Sites":"網站管理","Manage Users":"使用者管理","Memory":"記憶體","Memory and Storage":"記憶體和存儲","Modify":"修改","Modify Config":"修改配置","Multi-line Directive":"多行指令","Name":"名稱","Network":"網路","Network Statistics":"網路統計","Network Total Receive":"下載流量","Network Total Send":"上傳流量","New version released":"新版本發布","Next":"下一步","Nginx Access Log Path":"Nginx 訪問日誌路徑","Nginx Configuration Parse Error":"Nginx 配置解析錯誤","Nginx Control":"Nginx 控件","Nginx Error Log Path":"Nginx 錯誤日誌路徑","Nginx Log":"Nginx 日誌","Nginx reloaded successfully":"Nginx 重載成功","Nginx restarted successfully":"Nginx 重啟成功","No":"取消","Not Found":"找不到頁面","Not Valid Before: %{date}":"此前無效: %{date}","Note":"備註","Obtaining certificate":"正在獲取證書","OK":"確定","OS":"作業系統","OS:":"作業系統:","Params":"參數","Password":"密碼","Password (*)":"密碼 (*)","Path":"路徑","Perform core upgrade error":"執行核心升級錯誤","Performing core upgrade":"正在執行核心升級","Please input your E-mail!":"請輸入您的郵箱!","Please input your password!":"請輸入您的密碼!","Please input your username!":"請輸入您的使用者名稱!","Preference":"設定","Preparing lego configurations":"準備 Lego 配置","Prohibit changing root password in demo":"禁止在demo中修改root密碼","Prohibit deleting the default user":"禁止刪除默認用戶","Project Team":"專案團隊","Reads":"讀","Receive":"下載","Registering user":"註冊用戶","Reinstall":"安裝","Release Note":"發行公告","Reload":"重新載入","Reloading":"换弹中","Reloading nginx":"重载 Nginx","Reset":"重設","Restart":"重新啟動","Restarting":"重啟","Run Mode":"高階模式","Running":"運行中","Save":"儲存","Save Directive":"儲存指令","Save error %{msg}":"儲存錯誤 %{msg}","Save successfully":"保存成功","Save Successfully":"保存成功","Saved successfully":"儲存成功","Selector":"選擇器","Send":"上傳","Server error":"伺服器錯誤","Server Info":"伺服器資訊","server_name not found in directives":"未在指令集合中找到 server_name","server_name parameter is required":"必須為 server_name 指令指明參數","Single Directive":"單行指令","Site Logs":"網站日誌","Sites List":"站點列表","SSL Certificate Key Path":"SSL 證書密鑰路徑","SSL Certificate Path":"SSL證書路徑","SSL Certification Content":"SSL認證內容","SSL Certification Key Content":"SSL 證書密鑰內容","Status":"狀態","Stopped":"已停止","Storage":"儲存","Subject Name: %{name}":"主體名稱: %{name}","Swap":"交換空間","System":"系統","Table":"表格","Terminal":"終端","Terminal Start Command":"終端啟動命令","The certificate for the domain will be checked every hour, and will be renewed if it has been more than 1 month since it was last issued.":"系統將會每小時檢測一次該域名證書,若距離上次簽發已超過1個月,則將自動續簽。
如果您之前沒有證書,請先點選「從 Let's Encrypt 獲取證書」。","The filename cannot contain the following characters: %{c}":"檔名不能包含以下字元: %{c}","The server_name in the current configuration must be the domain name you need to get the certificate.":"注意:當前配置中的 server_name 必須為需要申請證書的域名。","The username or password is incorrect":"用戶名或密碼不正確","Theme":"外觀樣式","Type":"類型","Updated at":"修改時間","Updated successfully":"已成功更新","Upgrade":"升級","Upgraded successfully":"升級成功","Upgrading Nginx UI, please wait...":"正在升級 Nginx UI,請稍候...","Uptime:":"執行時間:","Username":"使用者名稱","Username (*)":"使用者名稱 (*)","Using HTTP01 challenge provider":"使用 HTTP01 挑戰提供者","View":"查看","Warning":"警告","Writes":"寫","Writing certificate private key to disk":"將證書私鑰寫入磁盤","Writing certificate to disk":"將證書寫入磁盤","Yes":"是的","You are using the latest version":"您使用的是最新版本","You can check Nginx UI upgrade at this page.":"您可以在此頁面查看 Nginx UI 升級。","License":{"Project":"開源軟體授權條款"}},"zh_CN":{"About":"关于","Access Logs":"访问日志","Action":"操作","Add":"添加","Add Directive Below":"在下面添加指令","Add Location":"添加 Location","Add Site":"添加站点","Advance Mode":"高级模式","Arch":"架构","Are you sure you want to delete?":"您确定要删除吗?","Are you sure you want to remove this directive?":"您确定要删除这条指令?","Are you sure you want to remove this location?":"您确定要删除这个 Location?","Author":"作者","Auto":"自动","Auto Cert":"自动更新","Auto cert is enabled, please do not modify this certification.":"自动更新已启用,请勿修改此证书配置。","Auto Refresh":"自动刷新","Auto-renewal disabled for %{name}":"成功关闭 %{name} 自动续签","Auto-renewal enabled for %{name}":"成功启用 %{name} 自动续签","Back":"返回","Back Home":"返回首页","Base information":"基本信息","Basic Mode":"基本模式","Batch Modify":"批量修改","Build with":"构建基于","Cancel":"取消","Certificate has expired":"此证书已过期","Certificate is valid":"此证书有效","Certificate Status":"证书状态","Certification":"证书","Change Certificate":"更改证书","Check again":"重新检查","Comments":"注释","Config Templates":"配置","Configuration file is test successful":"配置文件测试成功","Configuration Name":"配置名称","Configurations":"配置","Configure SSL":"配置 SSL","Content":"内容","Core Upgrade":"核心升级","CPU Status":"CPU 状态","CPU:":"CPU:","Create Another":"再创建一个","Created at":"创建时间","Creating client facilitates communication with the CA server":"正在创建客户端用于与 CA 服务器通信","Current Version":"当前版本","Custom":"自定义","Dark":"深色","Dashboard":"仪表盘","Database (Optional, default: database)":"数据库 (可选,默认: database)","Delete":"删除","Delete ID: %{id}":"删除 ID: %{id}","Delete site: %{site_name}":"删除站点: %{site_name}","Description":"描述","Development Mode":"开发模式","Dir":"目录","Directive":"指令","Directives":"指令","Disable auto-renewal failed for %{name}":"关闭 %{name} 自动续签失败","Disabled":"禁用","Disabled successfully":"禁用成功","Disk IO":"磁盘 IO","Domain":"域名","Domain Config Created Successfully":"域名配置文件创建成功","Download latest release error":"下载最新版本错误","Downloading latest release":"下载最新版本","Duplicate":"复制","Edit %{n}":"编辑 %{n}","Edit Configuration":"编辑配置","Edit Site":"编辑站点","Email (*)":"邮箱 (*)","Enable auto-renewal failed for %{name}":"启用 %{name} 自动续签失败","Enable failed":"启用失败","Enable TLS":"启用 TLS","Enabled":"启用","Enabled successfully":"启用成功","Encrypt website with Let's Encrypt":"用 Let's Encrypt 对网站进行加密","Error":"错误","Error Logs":"错误日志","Executable Path":"可执行文件路径","Expiration Date: %{date}":"过期时间: %{date}","Export":"导出","Failed to disable %{msg}":"禁用失败 %{msg}","Failed to enable %{msg}":"启用失败 %{msg}","Failed to get certificate information":"获取证书信息失败","Failed to save, syntax error(s) was detected in the configuration.":"保存失败,在配置中检测到语法错误。","File":"文件","File exists":"文件已存在","File Not Found":"未找到文件","Filter":"过滤","Finished":"完成","Format Code":"代码格式化","Format error %{msg}":"保存错误 %{msg}","Format successfully":"格式化成功","Generate":"生成","Generating private key for registering account":"正在生成私钥用于注册账户","Getting the certificate, please wait...":"正在获取证书,请稍等...","Home":"首页","HTTP Challenge Port":"HTTP Challenge 监听端口","HTTP Port":"HTTP 监听端口","Initial core upgrader error":"初始化核心升级程序错误","Initialing core upgrader":"初始化核心升级器","Inspect Configurations":"检查配置","Install":"安装","Install successfully":"安装成功","Intermediate Certification Authorities: %{issuer}":"中级证书颁发机构: %{issuer}","Issued certificate successfully":"证书申请成功","Jwt Secret":"Jwt 密钥","Last checked at":"最后检查时间","Leave blank for no change":"留空表示不修改","Light":"浅色","Load Averages:":"系统负载:","Location":"Location","Locations":"Locations","Login":"登录","Login successful":"登录成功","Logout successful":"登出成功","Make sure you have configured a reverse proxy for .well-known directory to HTTPChallengePort (default: 9180) before getting the certificate.":"在获取签发证书前,请确保配置文件中已将 .well-known 目录反向代理到 HTTPChallengePort (默认: 9180)","Manage Configs":"配置管理","Manage Sites":"网站管理","Manage Users":"用户管理","Memory":"内存","Memory and Storage":"内存与存储","Modify":"修改","Modify Config":"修改配置文件","Multi-line Directive":"单行指令","Name":"名称","Network":"网络","Network Statistics":"流量统计","Network Total Receive":"下载流量","Network Total Send":"上传流量","New version released":"新版本发布","Next":"下一步","Nginx Access Log Path":"Nginx 访问日志路径","Nginx Configuration Parse Error":"Nginx 配置解析错误","Nginx Control":"控制 Nginx","Nginx Error Log Path":"Nginx 错误日志路径","Nginx Log":"Nginx 日志","Nginx reloaded successfully":"Nginx 重载成功","Nginx restarted successfully":"Nginx 重启成功","No":"取消","Not Found":"找不到页面","Not Valid Before: %{date}":"此前无效: %{date}","Note":"注意","Obtaining certificate":"正在获取证书","OK":"确定","OS":"OS","OS:":"OS:","Params":"参数","Password":"密码","Password (*)":"密码 (*)","Path":"路径","Perform core upgrade error":"执行核心升级错误","Performing core upgrade":"正在进行核心升级","Please input your E-mail!":"请输入您的邮箱!","Please input your password!":"请输入您的密码!","Please input your username!":"请输入您的用户名!","Preference":"偏好设置","Preparing lego configurations":"正在准备 Lego 的配置","Prohibit changing root password in demo":"禁止在演示模式下修改 root 账户的密码","Prohibit deleting the default user":"禁止删除默认用户","Project Team":"项目团队","Reads":"读","Receive":"下载","Registering user":"正在注册用户","Reinstall":"重新安装","Release Note":"发行日志","Reload":"重载","Reloading":"重载中","Reloading nginx":"正在重载 Nginx","Reset":"重置","Restart":"重启","Restarting":"重启中","Run Mode":"运行模式","Running":"运行中","Save":"保存","Save Directive":"保存指令","Save error %{msg}":"保存错误 %{msg}","Save successfully":"保存成功","Save Successfully":"保存成功","Saved successfully":"保存成功","Selector":"选择器","Send":"上传","Server error":"服务器错误","Server Info":"服务器信息","server_name not found in directives":"未在指令集合中找到 server_name","server_name parameter is required":"必须为 server_name 指令指明参数","Single Directive":"单行指令","Site Logs":"站点列表","Sites List":"站点列表","SSL Certificate Key Path":"SSL证书密钥路径","SSL Certificate Path":"SSL证书路径","SSL Certification Content":"SSL证书内容","SSL Certification Key Content":"SSL证书密钥内容","Status":"状态","Stopped":"已停止","Storage":"存储","Subject Name: %{name}":"主体名称: %{name}","Swap":"Swap","System":"系统","Table":"列表","Terminal":"终端","Terminal Start Command":"终端启动命令","The certificate for the domain will be checked every hour, and will be renewed if it has been more than 1 month since it was last issued.":"系统将会每小时检测一次该域名证书,若距离上次签发已超过1个月,则将自动续签。","The filename cannot contain the following characters: %{c}":"文件名不能包含以下字符: %{c}","The server_name in the current configuration must be the domain name you need to get the certificate.":"当前配置中的 server_name 必须为需要申请证书的域名。","The username or password is incorrect":"用户名或密码错误","Theme":"主题","Type":"类型","Updated at":"修改时间","Updated successfully":"更新成功","Upgrade":"升级","Upgraded successfully":"升级成功","Upgrading Nginx UI, please wait...":"正在升级Nginx UI,请等待...","Uptime:":"运行时间:","Username":"用户名","Username (*)":"用户名 (*)","Using HTTP01 challenge provider":"使用 HTTP01 challenge provider","View":"查看","Warning":"警告","Writes":"写","Writing certificate private key to disk":"正在将证书私钥写入磁盘","Writing certificate to disk":"正在将证书写入磁盘","Yes":"是的","You are using the latest version":"您使用的是最新版本","You can check Nginx UI upgrade at this page.":"你可以在这个页面检查Nginx UI的升级。","License":{"Project":"开源许可"}}}
\ No newline at end of file
+{"en":{"About":"About","Action":"Action","Add Directive Below":"Add Directive Below","Add Location":"Add Location","Add Site":"Add Site","Advance Mode":"Advance Mode","Are you sure you want to remove this directive?":"Are you sure you want to remove this directive?","Auto-renewal disabled for %{name}":"Auto-renewal disabled for %{name}","Auto-renewal enabled for %{name}":"Auto-renewal enabled for %{name}","Back":"Back","Base information":"Base information","Basic Mode":"Basic Mode","Build with":"Build with","Cancel":"Cancel","Certificate has expired":"Certificate has expired","Certificate is valid":"Certificate is valid","Certificate Status":"Certificate Status","Comments":"Comments","Configuration Name":"Configuration Name","Configurations":"Configurations","Configure SSL":"Configure SSL","Content":"Content","CPU Status":"CPU Status","CPU:":"CPU:","Create Another":"Create Another","Created at":"Created at","Dashboard":"Dashboard","Database (Optional, default: database)":"Database (Optional, default: database)","Development Mode":"Development Mode","Directive":"Directive","Directives":"Directives","Disable auto-renewal failed for %{name}":"Disable auto-renewal failed for %{name}","Disabled":"Disabled","Disabled successfully":"Disabled successfully","Disk IO":"Disk IO","Domain Config Created Successfully":"Domain Config Created Successfully","Edit %{n}":"Edit %{n}","Edit Configuration":"Edit Configuration","Edit Site":"Edit Site","Email (*)":"Email (*)","Enable auto-renewal failed for %{name}":"Enable auto-renewal failed for %{name}","Enable failed":"Enable failed","Enable TLS":"Enable TLS","Enabled":"Enabled","Enabled successfully":"Enabled successfully","Encrypt website with Let's Encrypt":"Encrypt website with Let's Encrypt","Expiration Date: %{date}":"Expiration Date: %{date}","Failed to disable %{msg}":"Failed to disable %{msg}","Failed to enable %{msg}":"Failed to enable %{msg}","File Not Found":"File Not Found","Finished":"Finished","Getting the certificate, please wait...":"Getting the certificate, please wait...","Home":"Home","Install":"Install","Intermediate Certification Authorities: %{issuer}":"Intermediate Certification Authorities: %{issuer}","Leave blank for no change":"Leave blank for no change","Load Averages:":"Load Averages:","Location":"Location","Locations":"Locations","Login":"Login","Login successful":"Login successful","Logout successful":"Logout successful","Make sure you have configured a reverse proxy for .well-known directory to HTTPChallengePort (default: 9180) before getting the certificate.":"Make sure you have configured a reverse proxy for .well-known directory to HTTPChallengePort (default: 9180) before getting the certificate.","Manage Configs":"Manage Configs","Manage Sites":"Manage Sites","Manage Users":"Manage Users","Memory":"Memory","Memory and Storage":"Memory and Storage","Modify Config":"Modify Config","Name":"Name","Network":"Network","Network Statistics":"Network Statistics","Network Total Receive":"Network Total Receive","Network Total Send":"Network Total Send","Next":"Next","No":"No","Not Found":"Not Found","Not Valid Before: %{date}":"Not Valid Before: %{date}","OS:":"OS:","Params":"Params","Password":"Password","Password (*)":"Password (*)","Path":"Path","Please input your E-mail!":"Please input your E-mail!","Please input your password!":"Please input your password!","Please input your username!":"Please input your username!","Project Team":"Project Team","Reads":"Reads","Receive":"Receive","Save":"Save","Save Directive":"Save Directive","Save error %{msg}":"Save error %{msg}","Saved successfully":"Saved successfully","Send":"Send","Server error":"Server error","Server Info":"Server Info","server_name not found in directives":"server_name not found in directives","server_name parameter is required":"server_name parameter is required","Single Directive":"Single Directive","Sites List":"Sites List","Status":"Status","Storage":"Storage","Subject Name: %{name}":"Subject Name: %{name}","Swap":"Swap","Terminal":"Terminal","The certificate for the domain will be checked every hour, and will be renewed if it has been more than 1 month since it was last issued.":"The certificate for the domain will be checked every hour, and will be renewed if it has been more than 1 month since it was last issued.","The filename cannot contain the following characters: %{c}":"The filename cannot contain the following characters: %{c}","Updated at":"Updated at","Uptime:":"Uptime:","Username":"Username","Username (*)":"Username (*)","Warning":"Warning","Writes":"Writes","Yes":"Yes","License":{"Project":"License"}},"zh_TW":{"About":"關於","Access Logs":"訪問日誌","Action":"操作","Add":"新增","Add Directive Below":"在下面新增指令","Add Location":"新增 Location","Add Site":"新增站點","Advance Mode":"高階模式","Arch":"架搆","Are you sure you want to delete?":"你確定你要刪除?","Are you sure you want to remove this directive?":"您確定要刪除這條指令?","Are you sure you want to remove this location?":"您確定要刪除此 Location 嗎?","Author":"作者","Auto":"自動","Auto Cert":"自動更新","Auto cert is enabled, please do not modify this certification.":"自動證書已啟用,請不要修改此證書。","Auto Refresh":"自動刷新","Auto-Cert Log":"自動證書日誌","Auto-renewal disabled for %{name}":"已關閉 %{name} 自動續簽","Auto-renewal enabled for %{name}":"已啟用 %{name} 自動續簽","Back":"返回","Back Home":"回到首頁","Base information":"基本訊息","Basic Mode":"基本模式","Batch Modify":"批量修改","Build with":"構建基於","Cancel":"取消","Certificate has expired":"此憑證已過期","Certificate is valid":"此憑證有效","Certificate Status":"憑證狀態","Certification":"證照","Change Certificate":"更改證書","Check again":"再次檢查","Comments":"註釋","Config Name":"配置名稱","Config Templates":"配置模板","Configuration file is test successful":"配置文件測試成功","Configuration Name":"配置名稱","Configurations":"配置","Configure SSL":"配置 SSL","Content":"內容","Core Upgrade":"核心升級","CPU Status":"中央處理器狀態","CPU:":"中央處理器:","Create Another":"再創建一個","Created at":"建立時間","Creating client facilitates communication with the CA server":"創建客戶端方便與CA服務器通信","Current Version":"當前版本","Custom":"自訂","Dark":"深色","Dashboard":"儀表盤","Database (Optional, default: database)":"資料庫 (可選,預設: database)","Delete":"刪除","Delete ID: %{id}":"刪除 ID: %{id}","Delete site: %{site_name}":"刪除站點:%{site_name}","Description":"敘述","Development Mode":"開發模式","Dir":"目錄","Directive":"指令","Directives":"指令","Disable auto-renewal failed for %{name}":"關閉 %{name} 自動續簽失敗","Disabled":"禁用","Disabled successfully":"禁用成功","Disk IO":"磁碟 IO","Domain Config Created Successfully":"域名配置文件創建成功","Domains list is empty, try to reopen auto-cert for %{config}":"域列表為空,請嘗試重新打開 %{config} 的自動證書","Download latest release error":"下載最新版本錯誤","Downloading latest release":"正在下載最新版本","Duplicate":"複製","Duplicated successfully":"複製成功","Edit %{n}":"編輯 %{n}","Edit Configuration":"編輯配置","Edit Site":"編輯站點","Email (*)":"郵箱 (*)","Enable auto-renewal failed for %{name}":"啟用 %{name} 自動續簽失敗","Enable failed":"啟用失敗","Enable TLS":"啟用 TLS","Enabled":"啟用","Enabled successfully":"啟用成功","Encrypt website with Let's Encrypt":"用 Let's Encrypt 對網站進行加密","Error":"錯誤","Error Logs":"錯誤日志","Executable Path":"可執行檔路徑","Expiration Date: %{date}":"過期時間: %{date}","Export":"導出","Failed to disable %{msg}":"禁用失敗 %{msg}","Failed to enable %{msg}":"啟用失敗 %{msg}","Failed to get certificate information":"獲取證書信息失敗","Failed to save, syntax error(s) was detected in the configuration.":"保存失敗,在配置中檢測到語法錯誤。","File":"文件","File exists":"文件已存在","File Not Found":"未找到檔案","Filter":"篩選","Finished":"完成","Format Code":"格式化代碼","Format error %{msg}":"格式錯誤 %{msg}","Format successfully":"格式化成功","Generate":"生成","Generating private key for registering account":"生成註冊賬號私鑰","Getting the certificate, please wait...":"正在獲取憑證,請稍等...","Home":"首頁","HTTP Challenge Port":"HTTP 挑戰端口","HTTP Port":"HTTP 監聽埠","Initial core upgrader error":"初始核心升級程序錯誤","Initialing core upgrader":"正在初始化核心升級程序","Inspect Configurations":"檢查配置","Install":"安裝","Install successfully":"安裝成功","Intermediate Certification Authorities: %{issuer}":"中級憑證頒發機構: %{issuer}","Issued certificate successfully":"頒發證書成功","Jwt Secret":"Jwt Secret","Last checked at":"上次檢查時間","Leave blank for no change":"留空表示不修改","Light":"淺色","Load Averages:":"系統負載:","Location":"Location","Locations":"Locations","Login":"登入","Login successful":"登入成功","Logout successful":"登出成功","Make sure you have configured a reverse proxy for .well-known directory to HTTPChallengePort (default: 9180) before getting the certificate.":"在獲取證書前,請確保配置檔案中已將 .well-known 目錄反向代理到 HTTPChallengePort (預設: 9180)","Manage Configs":"配置管理","Manage Sites":"網站管理","Manage Users":"使用者管理","Memory":"記憶體","Memory and Storage":"記憶體和存儲","Modify":"修改","Modify Config":"修改配置","Multi-line Directive":"多行指令","Name":"名稱","Network":"網路","Network Statistics":"網路統計","Network Total Receive":"下載流量","Network Total Send":"上傳流量","New version released":"新版本發布","Next":"下一步","Nginx Access Log Path":"Nginx 訪問日誌路徑","Nginx Configuration Parse Error":"Nginx 配置解析錯誤","Nginx Control":"Nginx 控件","Nginx Error Log Path":"Nginx 錯誤日誌路徑","Nginx Log":"Nginx 日誌","Nginx reloaded successfully":"Nginx 重載成功","Nginx restarted successfully":"Nginx 重啟成功","No":"取消","Not Found":"找不到頁面","Not Valid Before: %{date}":"此前無效: %{date}","Note":"備註","Obtaining certificate":"正在獲取證書","OK":"確定","OS":"作業系統","OS:":"作業系統:","Params":"參數","Password":"密碼","Password (*)":"密碼 (*)","Path":"路徑","Perform core upgrade error":"執行核心升級錯誤","Performing core upgrade":"正在執行核心升級","Please input name, this will be used as the filename of the new configuration!":"請輸入名稱,這將作為新配置的文件名!","Please input your E-mail!":"請輸入您的郵箱!","Please input your password!":"請輸入您的密碼!","Please input your username!":"請輸入您的使用者名稱!","Preference":"設定","Preparing lego configurations":"準備 Lego 配置","Prohibit changing root password in demo":"禁止在demo中修改root密碼","Prohibit deleting the default user":"禁止刪除默認用戶","Project Team":"專案團隊","Reads":"讀","Receive":"下載","Registering user":"註冊用戶","Reinstall":"安裝","Release Note":"發行公告","Reload":"重新載入","Reloading":"换弹中","Reloading nginx":"重载 Nginx","Reset":"重設","Restart":"重新啟動","Restarting":"重啟","Run Mode":"高階模式","Running":"運行中","Save":"儲存","Save Directive":"儲存指令","Save error %{msg}":"儲存錯誤 %{msg}","Save successfully":"保存成功","Save Successfully":"保存成功","Saved successfully":"儲存成功","Selector":"選擇器","Send":"上傳","Server error":"伺服器錯誤","Server Info":"伺服器資訊","server_name not found in directives":"未在指令集合中找到 server_name","server_name parameter is required":"必須為 server_name 指令指明參數","Single Directive":"單行指令","Site Logs":"網站日誌","Sites List":"站點列表","SSL Certificate Key Path":"SSL 證書密鑰路徑","SSL Certificate Path":"SSL證書路徑","SSL Certification Content":"SSL認證內容","SSL Certification Key Content":"SSL 證書密鑰內容","Status":"狀態","Stopped":"已停止","Storage":"儲存","Subject Name: %{name}":"主體名稱: %{name}","Swap":"交換空間","System":"系統","Table":"表格","Terminal":"終端","Terminal Start Command":"終端啟動命令","The certificate for the domain will be checked every hour, and will be renewed if it has been more than 1 month since it was last issued.":"系統將會每小時檢測一次該域名證書,若距離上次簽發已超過1個月,則將自動續簽。
如果您之前沒有證書,請先點選「從 Let's Encrypt 獲取證書」。","The filename cannot contain the following characters: %{c}":"檔名不能包含以下字元: %{c}","The server_name in the current configuration must be the domain name you need to get the certificate.":"注意:當前配置中的 server_name 必須為需要申請證書的域名。","The username or password is incorrect":"用戶名或密碼不正確","Theme":"外觀樣式","This auto-cert item is invalid, please remove it.":"此自動證書項無效,請將其刪除。","Type":"類型","Updated at":"修改時間","Updated successfully":"已成功更新","Upgrade":"升級","Upgraded successfully":"升級成功","Upgrading Nginx UI, please wait...":"正在升級 Nginx UI,請稍候...","Uptime:":"執行時間:","Username":"使用者名稱","Username (*)":"使用者名稱 (*)","Using HTTP01 challenge provider":"使用 HTTP01 挑戰提供者","View":"查看","Warning":"警告","Writes":"寫","Writing certificate private key to disk":"將證書私鑰寫入磁盤","Writing certificate to disk":"將證書寫入磁盤","Yes":"是的","You are using the latest version":"您使用的是最新版本","You can check Nginx UI upgrade at this page.":"您可以在此頁面查看 Nginx UI 升級。","License":{"Project":"開源軟體授權條款"}},"zh_CN":{"About":"关于","Access Logs":"访问日志","Action":"操作","Add":"添加","Add Directive Below":"在下面添加指令","Add Location":"添加 Location","Add Site":"添加站点","Advance Mode":"高级模式","Arch":"架构","Are you sure you want to delete?":"您确定要删除吗?","Are you sure you want to remove this directive?":"您确定要删除这条指令?","Are you sure you want to remove this location?":"您确定要删除这个 Location?","Author":"作者","Auto":"自动","Auto Cert":"自动更新","Auto cert is enabled, please do not modify this certification.":"自动更新已启用,请勿修改此证书配置。","Auto Refresh":"自动刷新","Auto-Cert Log":"证书自动续期日志","Auto-renewal disabled for %{name}":"成功关闭 %{name} 自动续签","Auto-renewal enabled for %{name}":"成功启用 %{name} 自动续签","Back":"返回","Back Home":"返回首页","Base information":"基本信息","Basic Mode":"基本模式","Batch Modify":"批量修改","Build with":"构建基于","Cancel":"取消","Certificate has expired":"此证书已过期","Certificate is valid":"此证书有效","Certificate Status":"证书状态","Certification":"证书","Change Certificate":"更改证书","Check again":"重新检查","Comments":"注释","Config Name":"配置文件名称","Config Templates":"配置","Configuration file is test successful":"配置文件测试成功","Configuration Name":"配置名称","Configurations":"配置","Configure SSL":"配置 SSL","Content":"内容","Core Upgrade":"核心升级","CPU Status":"CPU 状态","CPU:":"CPU:","Create Another":"再创建一个","Created at":"创建时间","Creating client facilitates communication with the CA server":"正在创建客户端用于与 CA 服务器通信","Current Version":"当前版本","Custom":"自定义","Dark":"深色","Dashboard":"仪表盘","Database (Optional, default: database)":"数据库 (可选,默认: database)","Delete":"删除","Delete ID: %{id}":"删除 ID: %{id}","Delete site: %{site_name}":"删除站点: %{site_name}","Description":"描述","Development Mode":"开发模式","Dir":"目录","Directive":"指令","Directives":"指令","Disable auto-renewal failed for %{name}":"关闭 %{name} 自动续签失败","Disabled":"禁用","Disabled successfully":"禁用成功","Disk IO":"磁盘 IO","Domain Config Created Successfully":"域名配置文件创建成功","Domains list is empty, try to reopen auto-cert for %{config}":"域名列表为空,请尝试为%{config}重新打开证书自动续期。","Download latest release error":"下载最新版本错误","Downloading latest release":"下载最新版本","Duplicate":"复制","Duplicated successfully":"复制成功","Edit %{n}":"编辑 %{n}","Edit Configuration":"编辑配置","Edit Site":"编辑站点","Email (*)":"邮箱 (*)","Enable auto-renewal failed for %{name}":"启用 %{name} 自动续签失败","Enable failed":"启用失败","Enable TLS":"启用 TLS","Enabled":"启用","Enabled successfully":"启用成功","Encrypt website with Let's Encrypt":"用 Let's Encrypt 对网站进行加密","Error":"错误","Error Logs":"错误日志","Executable Path":"可执行文件路径","Expiration Date: %{date}":"过期时间: %{date}","Export":"导出","Failed to disable %{msg}":"禁用失败 %{msg}","Failed to enable %{msg}":"启用失败 %{msg}","Failed to get certificate information":"获取证书信息失败","Failed to save, syntax error(s) was detected in the configuration.":"保存失败,在配置中检测到语法错误。","File":"文件","File exists":"文件已存在","File Not Found":"未找到文件","Filter":"过滤","Finished":"完成","Format Code":"代码格式化","Format error %{msg}":"保存错误 %{msg}","Format successfully":"格式化成功","Generate":"生成","Generating private key for registering account":"正在生成私钥用于注册账户","Getting the certificate, please wait...":"正在获取证书,请稍等...","Home":"首页","HTTP Challenge Port":"HTTP Challenge 监听端口","HTTP Port":"HTTP 监听端口","Initial core upgrader error":"初始化核心升级程序错误","Initialing core upgrader":"初始化核心升级器","Inspect Configurations":"检查配置","Install":"安装","Install successfully":"安装成功","Intermediate Certification Authorities: %{issuer}":"中级证书颁发机构: %{issuer}","Issued certificate successfully":"证书申请成功","Jwt Secret":"Jwt 密钥","Last checked at":"最后检查时间","Leave blank for no change":"留空表示不修改","Light":"浅色","Load Averages:":"系统负载:","Location":"Location","Locations":"Locations","Login":"登录","Login successful":"登录成功","Logout successful":"登出成功","Make sure you have configured a reverse proxy for .well-known directory to HTTPChallengePort (default: 9180) before getting the certificate.":"在获取签发证书前,请确保配置文件中已将 .well-known 目录反向代理到 HTTPChallengePort (默认: 9180)","Manage Configs":"配置管理","Manage Sites":"网站管理","Manage Users":"用户管理","Memory":"内存","Memory and Storage":"内存与存储","Modify":"修改","Modify Config":"修改配置文件","Multi-line Directive":"单行指令","Name":"名称","Network":"网络","Network Statistics":"流量统计","Network Total Receive":"下载流量","Network Total Send":"上传流量","New version released":"新版本发布","Next":"下一步","Nginx Access Log Path":"Nginx 访问日志路径","Nginx Configuration Parse Error":"Nginx 配置解析错误","Nginx Control":"控制 Nginx","Nginx Error Log Path":"Nginx 错误日志路径","Nginx Log":"Nginx 日志","Nginx reloaded successfully":"Nginx 重载成功","Nginx restarted successfully":"Nginx 重启成功","No":"取消","Not Found":"找不到页面","Not Valid Before: %{date}":"此前无效: %{date}","Note":"注意","Obtaining certificate":"正在获取证书","OK":"确定","OS":"OS","OS:":"OS:","Params":"参数","Password":"密码","Password (*)":"密码 (*)","Path":"路径","Perform core upgrade error":"执行核心升级错误","Performing core upgrade":"正在进行核心升级","Please input name, this will be used as the filename of the new configuration!":"请输入名称,这将被用作新配置的文件名。","Please input your E-mail!":"请输入您的邮箱!","Please input your password!":"请输入您的密码!","Please input your username!":"请输入您的用户名!","Preference":"偏好设置","Preparing lego configurations":"正在准备 Lego 的配置","Prohibit changing root password in demo":"禁止在演示模式下修改 root 账户的密码","Prohibit deleting the default user":"禁止删除默认用户","Project Team":"项目团队","Reads":"读","Receive":"下载","Registering user":"正在注册用户","Reinstall":"重新安装","Release Note":"发行日志","Reload":"重载","Reloading":"重载中","Reloading nginx":"正在重载 Nginx","Reset":"重置","Restart":"重启","Restarting":"重启中","Run Mode":"运行模式","Running":"运行中","Save":"保存","Save Directive":"保存指令","Save error %{msg}":"保存错误 %{msg}","Save successfully":"保存成功","Save Successfully":"保存成功","Saved successfully":"保存成功","Selector":"选择器","Send":"上传","Server error":"服务器错误","Server Info":"服务器信息","server_name not found in directives":"未在指令集合中找到 server_name","server_name parameter is required":"必须为 server_name 指令指明参数","Single Directive":"单行指令","Site Logs":"站点列表","Sites List":"站点列表","SSL Certificate Key Path":"SSL证书密钥路径","SSL Certificate Path":"SSL证书路径","SSL Certification Content":"SSL证书内容","SSL Certification Key Content":"SSL证书密钥内容","Status":"状态","Stopped":"已停止","Storage":"存储","Subject Name: %{name}":"主体名称: %{name}","Swap":"Swap","System":"系统","Table":"列表","Terminal":"终端","Terminal Start Command":"终端启动命令","The certificate for the domain will be checked every hour, and will be renewed if it has been more than 1 month since it was last issued.":"系统将会每小时检测一次该域名证书,若距离上次签发已超过1个月,则将自动续签。","The filename cannot contain the following characters: %{c}":"文件名不能包含以下字符: %{c}","The server_name in the current configuration must be the domain name you need to get the certificate.":"当前配置中的 server_name 必须为需要申请证书的域名。","The username or password is incorrect":"用户名或密码错误","Theme":"主题","This auto-cert item is invalid, please remove it.":"这个证书自动续期项目是无效的,请删除。","Type":"类型","Updated at":"修改时间","Updated successfully":"更新成功","Upgrade":"升级","Upgraded successfully":"升级成功","Upgrading Nginx UI, please wait...":"正在升级Nginx UI,请等待...","Uptime:":"运行时间:","Username":"用户名","Username (*)":"用户名 (*)","Using HTTP01 challenge provider":"使用 HTTP01 challenge provider","View":"查看","Warning":"警告","Writes":"写","Writing certificate private key to disk":"正在将证书私钥写入磁盘","Writing certificate to disk":"正在将证书写入磁盘","Yes":"是的","You are using the latest version":"您使用的是最新版本","You can check Nginx UI upgrade at this page.":"你可以在这个页面检查Nginx UI的升级。","License":{"Project":"开源许可"}}}
\ No newline at end of file
diff --git a/frontend/src/language/zh_CN/app.mo b/frontend/src/language/zh_CN/app.mo
index 1bb3ce404..a51bbe2c3 100644
Binary files a/frontend/src/language/zh_CN/app.mo and b/frontend/src/language/zh_CN/app.mo differ
diff --git a/frontend/src/language/zh_CN/app.po b/frontend/src/language/zh_CN/app.po
index 7f96c5456..ad7a1646d 100644
--- a/frontend/src/language/zh_CN/app.po
+++ b/frontend/src/language/zh_CN/app.po
@@ -20,8 +20,8 @@ msgstr "关于"
msgid "Access Logs"
msgstr "访问日志"
-#: src/views/cert/Cert.vue:78 src/views/config/config.ts:36
-#: src/views/domain/DomainList.vue:47 src/views/user/User.vue:43
+#: src/views/cert/Cert.vue:74 src/views/config/config.ts:36
+#: src/views/domain/DomainList.vue:48 src/views/user/User.vue:43
msgid "Action"
msgstr "操作"
@@ -86,11 +86,11 @@ msgstr "作者"
msgid "Auto"
msgstr "自动"
-#: src/views/cert/Cert.vue:41 src/views/domain/cert/ChangeCert.vue:35
+#: src/views/cert/Cert.vue:37 src/views/domain/cert/ChangeCert.vue:35
msgid "Auto Cert"
msgstr "自动更新"
-#: src/views/cert/Cert.vue:8
+#: src/views/cert/Cert.vue:9
msgid "Auto cert is enabled, please do not modify this certification."
msgstr "自动更新已启用,请勿修改此证书配置。"
@@ -98,6 +98,10 @@ msgstr "自动更新已启用,请勿修改此证书配置。"
msgid "Auto Refresh"
msgstr "自动刷新"
+#: src/views/cert/Cert.vue:27
+msgid "Auto-Cert Log"
+msgstr "证书自动续期日志"
+
#: src/views/domain/cert/IssueCert.vue:71
msgid "Auto-renewal disabled for %{name}"
msgstr "成功关闭 %{name} 自动续签"
@@ -151,7 +155,7 @@ msgstr "此证书已过期"
msgid "Certificate is valid"
msgstr "此证书有效"
-#: src/views/cert/Cert.vue:12 src/views/domain/cert/Cert.vue:35
+#: src/views/cert/Cert.vue:34 src/views/domain/cert/Cert.vue:35
msgid "Certificate Status"
msgstr "证书状态"
@@ -173,10 +177,14 @@ msgstr "重新检查"
#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:34
#: src/views/domain/ngx_conf/LocationEditor.vue:35
#: src/views/domain/ngx_conf/LocationEditor.vue:52
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:180
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:181
msgid "Comments"
msgstr "注释"
+#: src/views/cert/Cert.vue:32
+msgid "Config Name"
+msgstr "配置文件名称"
+
#: src/views/domain/ngx_conf/ConfigTemplate.vue:61
msgid "Config Templates"
msgstr "配置"
@@ -254,7 +262,13 @@ msgstr "仪表盘"
msgid "Database (Optional, default: database)"
msgstr "数据库 (可选,默认: database)"
-#: src/components/StdDataDisplay/StdTable.vue:528
+#: src/components/StdDataDisplay/StdTable.vue:31
+#: src/components/StdDataDisplay/StdTable.vue:32
+#: src/components/StdDataDisplay/StdTable.vue:37
+#: src/components/StdDataDisplay/StdTable.vue:50
+#: src/components/StdDataDisplay/StdTable.vue:52
+#: src/components/StdDataDisplay/StdTable.vue:53
+#: src/components/StdDataDisplay/StdTable.vue:57
#: src/views/domain/DomainList.vue:19 src/views/domain/DomainList.vue:20
#: src/views/domain/DomainList.vue:21 src/views/domain/DomainList.vue:28
#: src/views/domain/DomainList.vue:32
@@ -265,7 +279,7 @@ msgstr "删除"
msgid "Delete ID: %{id}"
msgstr "删除 ID: %{id}"
-#: src/views/domain/DomainList.vue:81
+#: src/views/domain/DomainList.vue:82
msgid "Delete site: %{site_name}"
msgstr "删除站点: %{site_name}"
@@ -300,15 +314,15 @@ msgstr "指令"
msgid "Disable auto-renewal failed for %{name}"
msgstr "关闭 %{name} 自动续签失败"
-#: src/views/cert/Cert.vue:51 src/views/domain/cert/ChangeCert.vue:45
+#: src/views/cert/Cert.vue:47 src/views/domain/cert/ChangeCert.vue:45
#: src/views/domain/DomainEdit.vue:10 src/views/domain/DomainEdit.vue:9
-#: src/views/domain/DomainList.vue:16 src/views/domain/DomainList.vue:34
+#: src/views/domain/DomainList.vue:16 src/views/domain/DomainList.vue:35
#: src/views/domain/DomainList.vue:7 src/views/domain/DomainList.vue:8
#: src/views/domain/DomainList.vue:9
msgid "Disabled"
msgstr "禁用"
-#: src/views/domain/DomainEdit.vue:146 src/views/domain/DomainList.vue:69
+#: src/views/domain/DomainEdit.vue:146 src/views/domain/DomainList.vue:70
msgid "Disabled successfully"
msgstr "禁用成功"
@@ -316,14 +330,14 @@ msgstr "禁用成功"
msgid "Disk IO"
msgstr "磁盘 IO"
-#: src/views/cert/Cert.vue:32
-msgid "Domain"
-msgstr "域名"
-
#: src/views/domain/DomainAdd.vue:58
msgid "Domain Config Created Successfully"
msgstr "域名配置文件创建成功"
+#: src/views/cert/Cert.vue:21
+msgid "Domains list is empty, try to reopen auto-cert for %{config}"
+msgstr "域名列表为空,请尝试为%{config}重新打开证书自动续期。"
+
#: src/language/constants.ts:26
msgid "Download latest release error"
msgstr "下载最新版本错误"
@@ -334,9 +348,14 @@ msgstr "下载最新版本"
#: src/views/domain/DomainList.vue:14 src/views/domain/DomainList.vue:15
#: src/views/domain/DomainList.vue:16 src/views/domain/DomainList.vue:23
+#: src/views/domain/SiteDuplicate.vue:2
msgid "Duplicate"
msgstr "复制"
+#: src/views/domain/SiteDuplicate.vue:43
+msgid "Duplicated successfully"
+msgstr "复制成功"
+
#: src/views/domain/DomainEdit.vue:4 src/views/domain/DomainEdit.vue:5
msgid "Edit %{n}"
msgstr "编辑 %{n}"
@@ -365,16 +384,16 @@ msgstr "启用失败"
msgid "Enable TLS"
msgstr "启用 TLS"
-#: src/views/cert/Cert.vue:48 src/views/domain/cert/ChangeCert.vue:42
+#: src/views/cert/Cert.vue:44 src/views/domain/cert/ChangeCert.vue:42
#: src/views/domain/DomainEdit.vue:43 src/views/domain/DomainEdit.vue:6
#: src/views/domain/DomainEdit.vue:7 src/views/domain/DomainList.vue:10
#: src/views/domain/DomainList.vue:11 src/views/domain/DomainList.vue:12
-#: src/views/domain/DomainList.vue:19 src/views/domain/DomainList.vue:31
+#: src/views/domain/DomainList.vue:19 src/views/domain/DomainList.vue:32
msgid "Enabled"
msgstr "启用"
#: src/views/domain/DomainAdd.vue:47 src/views/domain/DomainEdit.vue:137
-#: src/views/domain/DomainList.vue:59
+#: src/views/domain/DomainList.vue:60
msgid "Enabled successfully"
msgstr "启用成功"
@@ -406,11 +425,11 @@ msgstr "过期时间: %{date}"
msgid "Export"
msgstr "导出"
-#: src/views/domain/DomainEdit.vue:149 src/views/domain/DomainList.vue:73
+#: src/views/domain/DomainEdit.vue:149 src/views/domain/DomainList.vue:74
msgid "Failed to disable %{msg}"
msgstr "禁用失败 %{msg}"
-#: src/views/domain/DomainEdit.vue:140 src/views/domain/DomainList.vue:63
+#: src/views/domain/DomainEdit.vue:140 src/views/domain/DomainList.vue:64
msgid "Failed to enable %{msg}"
msgstr "启用失败 %{msg}"
@@ -598,7 +617,7 @@ msgstr "单行指令"
#: src/views/cert/Cert.vue:16 src/views/config/config.ts:9
#: src/views/domain/cert/ChangeCert.vue:19 src/views/domain/DomainEdit.vue:46
-#: src/views/domain/DomainList.vue:15
+#: src/views/domain/DomainList.vue:16 src/views/domain/SiteDuplicate.vue:5
msgid "Name"
msgstr "名称"
@@ -721,6 +740,12 @@ msgstr "执行核心升级错误"
msgid "Performing core upgrade"
msgstr "正在进行核心升级"
+#: src/views/domain/SiteDuplicate.vue:28
+msgid ""
+"Please input name, this will be used as the filename of the new "
+"configuration!"
+msgstr "请输入名称,这将被用作新配置的文件名。"
+
#: src/views/other/Install.vue:36
msgid "Please input your E-mail!"
msgstr "请输入您的邮箱!"
@@ -865,9 +890,8 @@ msgstr "上传"
#: src/components/StdDataDisplay/StdTable.vue:168
#: src/components/StdDataDisplay/StdTable.vue:343
#: src/components/StdDataDisplay/StdTable.vue:463
-#: src/views/config/ConfigEdit.vue:32 src/views/domain/DomainEdit.vue:87
-#: src/views/domain/DomainList.vue:83 src/views/other/Install.vue:71
-#: src/views/preference/Preference.vue:41
+#: src/views/config/ConfigEdit.vue:32 src/views/domain/DomainList.vue:84
+#: src/views/other/Install.vue:71 src/views/preference/Preference.vue:41
msgid "Server error"
msgstr "服务器错误"
@@ -896,23 +920,23 @@ msgstr "站点列表"
msgid "Sites List"
msgstr "站点列表"
-#: src/views/cert/Cert.vue:65
+#: src/views/cert/Cert.vue:61
msgid "SSL Certificate Key Path"
msgstr "SSL证书密钥路径"
-#: src/views/cert/Cert.vue:58
+#: src/views/cert/Cert.vue:54
msgid "SSL Certificate Path"
msgstr "SSL证书路径"
-#: src/views/cert/Cert.vue:19
+#: src/views/cert/Cert.vue:41
msgid "SSL Certification Content"
msgstr "SSL证书内容"
-#: src/views/cert/Cert.vue:22
+#: src/views/cert/Cert.vue:44
msgid "SSL Certification Key Content"
msgstr "SSL证书密钥内容"
-#: src/views/domain/DomainList.vue:24
+#: src/views/domain/DomainList.vue:25
msgid "Status"
msgstr "状态"
@@ -973,12 +997,16 @@ msgstr "用户名或密码错误"
msgid "Theme"
msgstr "主题"
+#: src/views/cert/Cert.vue:15
+msgid "This auto-cert item is invalid, please remove it."
+msgstr "这个证书自动续期项目是无效的,请删除。"
+
#: src/views/config/config.ts:14
msgid "Type"
msgstr "类型"
-#: src/views/cert/Cert.vue:72 src/views/config/config.ts:29
-#: src/views/domain/DomainList.vue:41 src/views/user/User.vue:37
+#: src/views/cert/Cert.vue:68 src/views/config/config.ts:29
+#: src/views/domain/DomainList.vue:42 src/views/user/User.vue:37
msgid "Updated at"
msgstr "修改时间"
@@ -1061,6 +1089,9 @@ msgctxt "Project"
msgid "License"
msgstr "开源许可"
+#~ msgid "Domain"
+#~ msgstr "域名"
+
#~ msgid "Do you want to reload Nginx?"
#~ msgstr "你需要重载 Nginx 吗?"
diff --git a/frontend/src/language/zh_TW/app.mo b/frontend/src/language/zh_TW/app.mo
index 9349a9976..df1952ed5 100644
Binary files a/frontend/src/language/zh_TW/app.mo and b/frontend/src/language/zh_TW/app.mo differ
diff --git a/frontend/src/language/zh_TW/app.po b/frontend/src/language/zh_TW/app.po
index c8fcdf52d..c19b8caa0 100644
--- a/frontend/src/language/zh_TW/app.po
+++ b/frontend/src/language/zh_TW/app.po
@@ -21,8 +21,8 @@ msgstr "關於"
msgid "Access Logs"
msgstr "訪問日誌"
-#: src/views/cert/Cert.vue:78 src/views/config/config.ts:36
-#: src/views/domain/DomainList.vue:47 src/views/user/User.vue:43
+#: src/views/cert/Cert.vue:74 src/views/config/config.ts:36
+#: src/views/domain/DomainList.vue:48 src/views/user/User.vue:43
msgid "Action"
msgstr "操作"
@@ -87,11 +87,11 @@ msgstr "作者"
msgid "Auto"
msgstr "自動"
-#: src/views/cert/Cert.vue:41 src/views/domain/cert/ChangeCert.vue:35
+#: src/views/cert/Cert.vue:37 src/views/domain/cert/ChangeCert.vue:35
msgid "Auto Cert"
msgstr "自動更新"
-#: src/views/cert/Cert.vue:8
+#: src/views/cert/Cert.vue:9
msgid "Auto cert is enabled, please do not modify this certification."
msgstr "自動證書已啟用,請不要修改此證書。"
@@ -99,6 +99,10 @@ msgstr "自動證書已啟用,請不要修改此證書。"
msgid "Auto Refresh"
msgstr "自動刷新"
+#: src/views/cert/Cert.vue:27
+msgid "Auto-Cert Log"
+msgstr "自動證書日誌"
+
#: src/views/domain/cert/IssueCert.vue:71
msgid "Auto-renewal disabled for %{name}"
msgstr "已關閉 %{name} 自動續簽"
@@ -152,7 +156,7 @@ msgstr "此憑證已過期"
msgid "Certificate is valid"
msgstr "此憑證有效"
-#: src/views/cert/Cert.vue:12 src/views/domain/cert/Cert.vue:35
+#: src/views/cert/Cert.vue:34 src/views/domain/cert/Cert.vue:35
msgid "Certificate Status"
msgstr "憑證狀態"
@@ -174,10 +178,14 @@ msgstr "再次檢查"
#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:34
#: src/views/domain/ngx_conf/LocationEditor.vue:35
#: src/views/domain/ngx_conf/LocationEditor.vue:52
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:180
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:181
msgid "Comments"
msgstr "註釋"
+#: src/views/cert/Cert.vue:32
+msgid "Config Name"
+msgstr "配置名稱"
+
#: src/views/domain/ngx_conf/ConfigTemplate.vue:61
msgid "Config Templates"
msgstr "配置模板"
@@ -255,7 +263,13 @@ msgstr "儀表盤"
msgid "Database (Optional, default: database)"
msgstr "資料庫 (可選,預設: database)"
-#: src/components/StdDataDisplay/StdTable.vue:528
+#: src/components/StdDataDisplay/StdTable.vue:31
+#: src/components/StdDataDisplay/StdTable.vue:32
+#: src/components/StdDataDisplay/StdTable.vue:37
+#: src/components/StdDataDisplay/StdTable.vue:50
+#: src/components/StdDataDisplay/StdTable.vue:52
+#: src/components/StdDataDisplay/StdTable.vue:53
+#: src/components/StdDataDisplay/StdTable.vue:57
#: src/views/domain/DomainList.vue:19 src/views/domain/DomainList.vue:20
#: src/views/domain/DomainList.vue:21 src/views/domain/DomainList.vue:28
#: src/views/domain/DomainList.vue:32
@@ -266,7 +280,7 @@ msgstr "刪除"
msgid "Delete ID: %{id}"
msgstr "刪除 ID: %{id}"
-#: src/views/domain/DomainList.vue:81
+#: src/views/domain/DomainList.vue:82
msgid "Delete site: %{site_name}"
msgstr "刪除站點:%{site_name}"
@@ -301,15 +315,15 @@ msgstr "指令"
msgid "Disable auto-renewal failed for %{name}"
msgstr "關閉 %{name} 自動續簽失敗"
-#: src/views/cert/Cert.vue:51 src/views/domain/cert/ChangeCert.vue:45
+#: src/views/cert/Cert.vue:47 src/views/domain/cert/ChangeCert.vue:45
#: src/views/domain/DomainEdit.vue:10 src/views/domain/DomainEdit.vue:9
-#: src/views/domain/DomainList.vue:16 src/views/domain/DomainList.vue:34
+#: src/views/domain/DomainList.vue:16 src/views/domain/DomainList.vue:35
#: src/views/domain/DomainList.vue:7 src/views/domain/DomainList.vue:8
#: src/views/domain/DomainList.vue:9
msgid "Disabled"
msgstr "禁用"
-#: src/views/domain/DomainEdit.vue:146 src/views/domain/DomainList.vue:69
+#: src/views/domain/DomainEdit.vue:146 src/views/domain/DomainList.vue:70
msgid "Disabled successfully"
msgstr "禁用成功"
@@ -317,14 +331,14 @@ msgstr "禁用成功"
msgid "Disk IO"
msgstr "磁碟 IO"
-#: src/views/cert/Cert.vue:32
-msgid "Domain"
-msgstr "網域"
-
#: src/views/domain/DomainAdd.vue:58
msgid "Domain Config Created Successfully"
msgstr "域名配置文件創建成功"
+#: src/views/cert/Cert.vue:21
+msgid "Domains list is empty, try to reopen auto-cert for %{config}"
+msgstr "域列表為空,請嘗試重新打開 %{config} 的自動證書"
+
#: src/language/constants.ts:26
msgid "Download latest release error"
msgstr "下載最新版本錯誤"
@@ -335,9 +349,14 @@ msgstr "正在下載最新版本"
#: src/views/domain/DomainList.vue:14 src/views/domain/DomainList.vue:15
#: src/views/domain/DomainList.vue:16 src/views/domain/DomainList.vue:23
+#: src/views/domain/SiteDuplicate.vue:2
msgid "Duplicate"
msgstr "複製"
+#: src/views/domain/SiteDuplicate.vue:43
+msgid "Duplicated successfully"
+msgstr "複製成功"
+
#: src/views/domain/DomainEdit.vue:4 src/views/domain/DomainEdit.vue:5
msgid "Edit %{n}"
msgstr "編輯 %{n}"
@@ -366,16 +385,16 @@ msgstr "啟用失敗"
msgid "Enable TLS"
msgstr "啟用 TLS"
-#: src/views/cert/Cert.vue:48 src/views/domain/cert/ChangeCert.vue:42
+#: src/views/cert/Cert.vue:44 src/views/domain/cert/ChangeCert.vue:42
#: src/views/domain/DomainEdit.vue:43 src/views/domain/DomainEdit.vue:6
#: src/views/domain/DomainEdit.vue:7 src/views/domain/DomainList.vue:10
#: src/views/domain/DomainList.vue:11 src/views/domain/DomainList.vue:12
-#: src/views/domain/DomainList.vue:19 src/views/domain/DomainList.vue:31
+#: src/views/domain/DomainList.vue:19 src/views/domain/DomainList.vue:32
msgid "Enabled"
msgstr "啟用"
#: src/views/domain/DomainAdd.vue:47 src/views/domain/DomainEdit.vue:137
-#: src/views/domain/DomainList.vue:59
+#: src/views/domain/DomainList.vue:60
msgid "Enabled successfully"
msgstr "啟用成功"
@@ -407,11 +426,11 @@ msgstr "過期時間: %{date}"
msgid "Export"
msgstr "導出"
-#: src/views/domain/DomainEdit.vue:149 src/views/domain/DomainList.vue:73
+#: src/views/domain/DomainEdit.vue:149 src/views/domain/DomainList.vue:74
msgid "Failed to disable %{msg}"
msgstr "禁用失敗 %{msg}"
-#: src/views/domain/DomainEdit.vue:140 src/views/domain/DomainList.vue:63
+#: src/views/domain/DomainEdit.vue:140 src/views/domain/DomainList.vue:64
msgid "Failed to enable %{msg}"
msgstr "啟用失敗 %{msg}"
@@ -599,7 +618,7 @@ msgstr "多行指令"
#: src/views/cert/Cert.vue:16 src/views/config/config.ts:9
#: src/views/domain/cert/ChangeCert.vue:19 src/views/domain/DomainEdit.vue:46
-#: src/views/domain/DomainList.vue:15
+#: src/views/domain/DomainList.vue:16 src/views/domain/SiteDuplicate.vue:5
msgid "Name"
msgstr "名稱"
@@ -722,6 +741,12 @@ msgstr "執行核心升級錯誤"
msgid "Performing core upgrade"
msgstr "正在執行核心升級"
+#: src/views/domain/SiteDuplicate.vue:28
+msgid ""
+"Please input name, this will be used as the filename of the new "
+"configuration!"
+msgstr "請輸入名稱,這將作為新配置的文件名!"
+
#: src/views/other/Install.vue:36
msgid "Please input your E-mail!"
msgstr "請輸入您的郵箱!"
@@ -866,9 +891,8 @@ msgstr "上傳"
#: src/components/StdDataDisplay/StdTable.vue:168
#: src/components/StdDataDisplay/StdTable.vue:343
#: src/components/StdDataDisplay/StdTable.vue:463
-#: src/views/config/ConfigEdit.vue:32 src/views/domain/DomainEdit.vue:87
-#: src/views/domain/DomainList.vue:83 src/views/other/Install.vue:71
-#: src/views/preference/Preference.vue:41
+#: src/views/config/ConfigEdit.vue:32 src/views/domain/DomainList.vue:84
+#: src/views/other/Install.vue:71 src/views/preference/Preference.vue:41
msgid "Server error"
msgstr "伺服器錯誤"
@@ -897,23 +921,23 @@ msgstr "網站日誌"
msgid "Sites List"
msgstr "站點列表"
-#: src/views/cert/Cert.vue:65
+#: src/views/cert/Cert.vue:61
msgid "SSL Certificate Key Path"
msgstr "SSL 證書密鑰路徑"
-#: src/views/cert/Cert.vue:58
+#: src/views/cert/Cert.vue:54
msgid "SSL Certificate Path"
msgstr "SSL證書路徑"
-#: src/views/cert/Cert.vue:19
+#: src/views/cert/Cert.vue:41
msgid "SSL Certification Content"
msgstr "SSL認證內容"
-#: src/views/cert/Cert.vue:22
+#: src/views/cert/Cert.vue:44
msgid "SSL Certification Key Content"
msgstr "SSL 證書密鑰內容"
-#: src/views/domain/DomainList.vue:24
+#: src/views/domain/DomainList.vue:25
msgid "Status"
msgstr "狀態"
@@ -975,12 +999,16 @@ msgstr "用戶名或密碼不正確"
msgid "Theme"
msgstr "外觀樣式"
+#: src/views/cert/Cert.vue:15
+msgid "This auto-cert item is invalid, please remove it."
+msgstr "此自動證書項無效,請將其刪除。"
+
#: src/views/config/config.ts:14
msgid "Type"
msgstr "類型"
-#: src/views/cert/Cert.vue:72 src/views/config/config.ts:29
-#: src/views/domain/DomainList.vue:41 src/views/user/User.vue:37
+#: src/views/cert/Cert.vue:68 src/views/config/config.ts:29
+#: src/views/domain/DomainList.vue:42 src/views/user/User.vue:37
msgid "Updated at"
msgstr "修改時間"
@@ -1063,6 +1091,9 @@ msgctxt "Project"
msgid "License"
msgstr "開源軟體授權條款"
+#~ msgid "Domain"
+#~ msgstr "網域"
+
#~ msgid "Do you want to reload Nginx?"
#~ msgstr "你想重載 Nginx 嗎?"
diff --git a/frontend/src/version.json b/frontend/src/version.json
index fc8c8d79d..a3c1a5111 100644
--- a/frontend/src/version.json
+++ b/frontend/src/version.json
@@ -1 +1 @@
-{"version":"1.7.5","build_id":81,"total_build":151}
\ No newline at end of file
+{"version":"1.7.6","build_id":82,"total_build":152}
\ No newline at end of file
diff --git a/frontend/src/views/cert/Cert.vue b/frontend/src/views/cert/Cert.vue
index edfe8afef..e949f5981 100644
--- a/frontend/src/views/cert/Cert.vue
+++ b/frontend/src/views/cert/Cert.vue
@@ -2,15 +2,15 @@
import {useGettext} from 'vue3-gettext'
import {input} from '@/components/StdDataEntry'
import {customRender, datetime} from '@/components/StdDataDisplay/StdTableTransformer'
-import {h} from 'vue'
import {Badge} from 'ant-design-vue'
import cert from '@/api/cert'
import StdCurd from '@/components/StdDataDisplay/StdCurd.vue'
import Template from '@/views/template/Template.vue'
import CodeEditor from '@/components/CodeEditor/CodeEditor.vue'
import CertInfo from '@/views/domain/cert/CertInfo.vue'
+import {h} from 'vue'
-const {$gettext} = useGettext()
+const {$gettext, interpolate} = useGettext()
const columns = [{
title: () => $gettext('Name'),
@@ -29,14 +29,10 @@ const columns = [{
},
search: true
}, {
- title: () => $gettext('Domain'),
- dataIndex: 'domain',
+ title: () => $gettext('Config Name'),
+ dataIndex: 'filename',
sorter: true,
- pithy: true,
- edit: {
- type: input
- },
- search: true
+ pithy: true
}, {
title: () => $gettext('Auto Cert'),
dataIndex: 'auto_cert',
@@ -85,11 +81,33 @@ const columns = [{
row-key="name"
>
-
+
+
+
+
+
+
+
+ {{ data.log }}
+
+
+
+
diff --git a/frontend/src/views/dashboard/DashBoard.vue b/frontend/src/views/dashboard/DashBoard.vue
index c535270f9..e2487c20f 100644
--- a/frontend/src/views/dashboard/DashBoard.vue
+++ b/frontend/src/views/dashboard/DashBoard.vue
@@ -164,7 +164,7 @@ function wsOnMessage(m: { data: any }) {
{{ $gettext('CPU:') + ' ' }}
- {{ cpu_info[0]?.modelName }}
+ {{ cpu_info[0]?.modelName || 'core' }}
{{ (cpu_info[0]?.mhz / 1000).toFixed(2) + 'GHz' }}
* {{ cpu_info.length }}
@@ -303,10 +303,6 @@ function wsOnMessage(m: { data: any }) {
}
}
-.os-platform {
- text-transform: capitalize;
-}
-
.load-avg-describe {
@media (max-width: 1600px) and (min-width: 1200px) {
display: none;
diff --git a/frontend/src/views/domain/DomainEdit.vue b/frontend/src/views/domain/DomainEdit.vue
index 6d9fe6290..564a7ddb5 100644
--- a/frontend/src/views/domain/DomainEdit.vue
+++ b/frontend/src/views/domain/DomainEdit.vue
@@ -25,7 +25,7 @@ watch(route, () => {
const update = ref(0)
const ngx_config = reactive({
- filename: '',
+ name: '',
upstreams: [],
servers: []
})
diff --git a/frontend/src/views/domain/cert/Cert.vue b/frontend/src/views/domain/cert/Cert.vue
index d32c51cdd..f559432dd 100644
--- a/frontend/src/views/domain/cert/Cert.vue
+++ b/frontend/src/views/domain/cert/Cert.vue
@@ -7,7 +7,7 @@ import ChangeCert from '@/views/domain/cert/ChangeCert.vue'
const {$gettext} = useGettext()
-const props = defineProps(['directivesMap', 'current_server_directives', 'enabled', 'cert_info'])
+const props = defineProps(['config_name', 'directivesMap', 'current_server_directives', 'enabled', 'cert_info'])
const emit = defineEmits(['callback', 'update:enabled'])
@@ -38,6 +38,7 @@ const enabled = computed({
import {useGettext} from 'vue3-gettext'
-import {computed, h, nextTick, onMounted, ref, VNode, watch} from 'vue'
+import {computed, nextTick, ref, watch} from 'vue'
import {message} from 'ant-design-vue'
import domain from '@/api/domain'
import websocket from '@/lib/websocket'
@@ -8,7 +8,7 @@ import Template from '@/views/template/Template.vue'
const {$gettext, interpolate} = useGettext()
-const props = defineProps(['directivesMap', 'current_server_directives', 'enabled'])
+const props = defineProps(['config_name', 'directivesMap', 'current_server_directives', 'enabled'])
const emit = defineEmits(['changeEnabled', 'callback', 'update:enabled'])
@@ -50,7 +50,7 @@ function job() {
})
}
}).then(() => {
- issue_cert(name.value, callback)
+ issue_cert(props.config_name, name.value, callback)
})
}
@@ -61,13 +61,13 @@ function callback(ssl_certificate: string, ssl_certificate_key: string) {
function change_auto_cert(r: boolean) {
if (r) {
- domain.add_auto_cert(name.value).then(() => {
+ domain.add_auto_cert(props.config_name, {domains: name.value.trim().split(' ')}).then(() => {
message.success(interpolate($gettext('Auto-renewal enabled for %{name}'), {name: name.value}))
}).catch(e => {
message.error(e.message ?? interpolate($gettext('Enable auto-renewal failed for %{name}'), {name: name.value}))
})
} else {
- domain.remove_auto_cert(name.value).then(() => {
+ domain.remove_auto_cert(props.config_name).then(() => {
message.success(interpolate($gettext('Auto-renewal disabled for %{name}'), {name: name.value}))
}).catch(e => {
message.error(e.message ?? interpolate($gettext('Disable auto-renewal failed for %{name}'), {name: name.value}))
@@ -86,7 +86,7 @@ function log(msg: string) {
(logContainer.value as any as Element).scroll({top: 320, left: 0, behavior: 'smooth'})
}
-const issue_cert = async (server_name: string, callback: Function) => {
+const issue_cert = async (config_name: string, server_name: string, callback: Function) => {
progressStatus.value = 'active'
modalClosable.value = false
modalVisible.value = true
@@ -95,7 +95,7 @@ const issue_cert = async (server_name: string, callback: Function) => {
log($gettext('Getting the certificate, please wait...'))
- const ws = websocket('/api/cert/issue', false)
+ const ws = websocket(`/api/domain/${config_name}/cert`, false)
ws.onopen = () => {
ws.send(JSON.stringify({
diff --git a/frontend/src/views/domain/ngx_conf/NgxConfigEditor.vue b/frontend/src/views/domain/ngx_conf/NgxConfigEditor.vue
index 1447162ec..ef950c8ca 100644
--- a/frontend/src/views/domain/ngx_conf/NgxConfigEditor.vue
+++ b/frontend/src/views/domain/ngx_conf/NgxConfigEditor.vue
@@ -168,6 +168,7 @@ watch(current_server_index, () => {
> ~/.profile
+source ~/.profile
+
+nginx
+cd /app && air
diff --git a/resources/development/nginx/fastcgi_params b/resources/development/nginx/fastcgi_params
new file mode 100644
index 000000000..28decb955
--- /dev/null
+++ b/resources/development/nginx/fastcgi_params
@@ -0,0 +1,25 @@
+
+fastcgi_param QUERY_STRING $query_string;
+fastcgi_param REQUEST_METHOD $request_method;
+fastcgi_param CONTENT_TYPE $content_type;
+fastcgi_param CONTENT_LENGTH $content_length;
+
+fastcgi_param SCRIPT_NAME $fastcgi_script_name;
+fastcgi_param REQUEST_URI $request_uri;
+fastcgi_param DOCUMENT_URI $document_uri;
+fastcgi_param DOCUMENT_ROOT $document_root;
+fastcgi_param SERVER_PROTOCOL $server_protocol;
+fastcgi_param REQUEST_SCHEME $scheme;
+fastcgi_param HTTPS $https if_not_empty;
+
+fastcgi_param GATEWAY_INTERFACE CGI/1.1;
+fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
+
+fastcgi_param REMOTE_ADDR $remote_addr;
+fastcgi_param REMOTE_PORT $remote_port;
+fastcgi_param SERVER_ADDR $server_addr;
+fastcgi_param SERVER_PORT $server_port;
+fastcgi_param SERVER_NAME $server_name;
+
+# PHP only, required if PHP was built with --enable-force-cgi-redirect
+fastcgi_param REDIRECT_STATUS 200;
diff --git a/resources/development/nginx/mime.types b/resources/development/nginx/mime.types
new file mode 100644
index 000000000..1c00d701a
--- /dev/null
+++ b/resources/development/nginx/mime.types
@@ -0,0 +1,99 @@
+
+types {
+ text/html html htm shtml;
+ text/css css;
+ text/xml xml;
+ image/gif gif;
+ image/jpeg jpeg jpg;
+ application/javascript js;
+ application/atom+xml atom;
+ application/rss+xml rss;
+
+ text/mathml mml;
+ text/plain txt;
+ text/vnd.sun.j2me.app-descriptor jad;
+ text/vnd.wap.wml wml;
+ text/x-component htc;
+
+ image/avif avif;
+ image/png png;
+ image/svg+xml svg svgz;
+ image/tiff tif tiff;
+ image/vnd.wap.wbmp wbmp;
+ image/webp webp;
+ image/x-icon ico;
+ image/x-jng jng;
+ image/x-ms-bmp bmp;
+
+ font/woff woff;
+ font/woff2 woff2;
+
+ application/java-archive jar war ear;
+ application/json json;
+ application/mac-binhex40 hqx;
+ application/msword doc;
+ application/pdf pdf;
+ application/postscript ps eps ai;
+ application/rtf rtf;
+ application/vnd.apple.mpegurl m3u8;
+ application/vnd.google-earth.kml+xml kml;
+ application/vnd.google-earth.kmz kmz;
+ application/vnd.ms-excel xls;
+ application/vnd.ms-fontobject eot;
+ application/vnd.ms-powerpoint ppt;
+ application/vnd.oasis.opendocument.graphics odg;
+ application/vnd.oasis.opendocument.presentation odp;
+ application/vnd.oasis.opendocument.spreadsheet ods;
+ application/vnd.oasis.opendocument.text odt;
+ application/vnd.openxmlformats-officedocument.presentationml.presentation
+ pptx;
+ application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
+ xlsx;
+ application/vnd.openxmlformats-officedocument.wordprocessingml.document
+ docx;
+ application/vnd.wap.wmlc wmlc;
+ application/wasm wasm;
+ application/x-7z-compressed 7z;
+ application/x-cocoa cco;
+ application/x-java-archive-diff jardiff;
+ application/x-java-jnlp-file jnlp;
+ application/x-makeself run;
+ application/x-perl pl pm;
+ application/x-pilot prc pdb;
+ application/x-rar-compressed rar;
+ application/x-redhat-package-manager rpm;
+ application/x-sea sea;
+ application/x-shockwave-flash swf;
+ application/x-stuffit sit;
+ application/x-tcl tcl tk;
+ application/x-x509-ca-cert der pem crt;
+ application/x-xpinstall xpi;
+ application/xhtml+xml xhtml;
+ application/xspf+xml xspf;
+ application/zip zip;
+
+ application/octet-stream bin exe dll;
+ application/octet-stream deb;
+ application/octet-stream dmg;
+ application/octet-stream iso img;
+ application/octet-stream msi msp msm;
+
+ audio/midi mid midi kar;
+ audio/mpeg mp3;
+ audio/ogg ogg;
+ audio/x-m4a m4a;
+ audio/x-realaudio ra;
+
+ video/3gpp 3gpp 3gp;
+ video/mp2t ts;
+ video/mp4 mp4;
+ video/mpeg mpeg mpg;
+ video/quicktime mov;
+ video/webm webm;
+ video/x-flv flv;
+ video/x-m4v m4v;
+ video/x-mng mng;
+ video/x-ms-asf asx asf;
+ video/x-ms-wmv wmv;
+ video/x-msvideo avi;
+}
diff --git a/resources/development/nginx/modules b/resources/development/nginx/modules
new file mode 120000
index 000000000..4b9b33f10
--- /dev/null
+++ b/resources/development/nginx/modules
@@ -0,0 +1 @@
+/usr/lib/nginx/modules
\ No newline at end of file
diff --git a/resources/development/nginx/nginx.conf b/resources/development/nginx/nginx.conf
new file mode 100644
index 000000000..c59d9d350
--- /dev/null
+++ b/resources/development/nginx/nginx.conf
@@ -0,0 +1,32 @@
+
+user nginx;
+worker_processes auto;
+
+error_log /var/log/nginx/error.log notice;
+pid /var/run/nginx.pid;
+
+
+events {
+ worker_connections 1024;
+}
+
+
+http {
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ log_format main '$remote_addr - $remote_user [$time_local] "$request" '
+ '$status $body_bytes_sent "$http_referer" '
+ '"$http_user_agent" "$http_x_forwarded_for"';
+
+ access_log /var/log/nginx/access.log main;
+
+ sendfile on;
+ #tcp_nopush on;
+
+ keepalive_timeout 65;
+
+ #gzip on;
+
+ include /etc/nginx/sites-enabled/*;
+}
diff --git a/resources/development/nginx/scgi_params b/resources/development/nginx/scgi_params
new file mode 100644
index 000000000..6d4ce4f3e
--- /dev/null
+++ b/resources/development/nginx/scgi_params
@@ -0,0 +1,17 @@
+
+scgi_param REQUEST_METHOD $request_method;
+scgi_param REQUEST_URI $request_uri;
+scgi_param QUERY_STRING $query_string;
+scgi_param CONTENT_TYPE $content_type;
+
+scgi_param DOCUMENT_URI $document_uri;
+scgi_param DOCUMENT_ROOT $document_root;
+scgi_param SCGI 1;
+scgi_param SERVER_PROTOCOL $server_protocol;
+scgi_param REQUEST_SCHEME $scheme;
+scgi_param HTTPS $https if_not_empty;
+
+scgi_param REMOTE_ADDR $remote_addr;
+scgi_param REMOTE_PORT $remote_port;
+scgi_param SERVER_PORT $server_port;
+scgi_param SERVER_NAME $server_name;
diff --git a/resources/development/nginx/sites-available/amstourship.jackyu.cn b/resources/development/nginx/sites-available/amstourship.jackyu.cn
new file mode 100644
index 000000000..55a3e643f
--- /dev/null
+++ b/resources/development/nginx/sites-available/amstourship.jackyu.cn
@@ -0,0 +1,28 @@
+server {
+ listen 80;
+ listen [::]:80;
+ server_name amstourship.jackyu.cn t.jackyu.cn;
+ root /var/www/amstourship;
+ index index.html;
+ location /.well-known/acme-challenge {
+ proxy_set_header Host $host;
+ proxy_set_header X-Real_IP $remote_addr;
+ proxy_set_header X-Forwarded-For $remote_addr:$remote_port;
+ proxy_pass http://127.0.0.1:5002;
+ }
+}
+server {
+ listen 443 ssl http2;
+ listen [::]:443 ssl http2;
+ server_name amstourship.jackyu.cn t.jackyu.cn;
+ ssl_certificate /etc/nginx/ssl/amstourship.jackyu.cn_t.jackyu.cn/fullchain.cer;
+ ssl_certificate_key /etc/nginx/ssl/amstourship.jackyu.cn_t.jackyu.cn/private.key;
+ root /var/www/amstourship;
+ index index.html;
+ location /.well-known/acme-challenge {
+ proxy_set_header Host $host;
+ proxy_set_header X-Real_IP $remote_addr;
+ proxy_set_header X-Forwarded-For $remote_addr:$remote_port;
+ proxy_pass http://127.0.0.1:5002;
+ }
+}
\ No newline at end of file
diff --git a/resources/development/nginx/sites-available/homework.jackyu.cn b/resources/development/nginx/sites-available/homework.jackyu.cn
new file mode 100644
index 000000000..ee539926b
--- /dev/null
+++ b/resources/development/nginx/sites-available/homework.jackyu.cn
@@ -0,0 +1,84 @@
+server {
+ listen 80;
+ listen [::]:80;
+
+ server_name homework.jackyu.cn;
+ # rewrite ^(.*)$ https://$host$1 permanent;
+ return 307 https://$server_name$request_uri;
+}
+
+server {
+ listen 443 ssl http2;
+ listen [::]:443 ssl http2;
+
+ server_name homework.jackyu.cn;
+
+ ssl_certificate /etc/nginx/ssl/jackyu.cn/alpha/jackyu.cn_server_cert.pem;
+ ssl_certificate_key /etc/nginx/ssl/jackyu.cn/alpha/jackyu.cn_key.pem;
+
+ root /var/www/homework/frontend;
+
+ # Add index.php to the list if you are using PHP
+ index index.html;
+
+ location / {
+ # First attempt to serve request as file, then
+ # as directory, then fall back to displaying a 404.
+ index index.html;
+ try_files $uri $uri/ /index.html;
+ }
+
+ location /student {
+ index manage.html;
+ try_files $uri $uri/ /student.html;
+ }
+
+ location /teacher {
+ index manage.html;
+ try_files $uri $uri/ /teacher.html;
+ }
+
+ location /admin {
+ index admin.html;
+ try_files $uri $uri/ /admin.html;
+ }
+
+ location ^~/upload/ {
+ alias /var/www/homework/api/upload/;
+ }
+ include error_json;
+ location /api/ {
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection upgrade;
+
+ proxy_pass http://127.0.0.1:9008/;
+ proxy_redirect off;
+
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ client_max_body_size 1000m;
+ }
+
+ location /zigbee-pi {
+ alias /var/www/zigbee-pi/frontend/;
+ index index.html;
+ }
+
+ location /zigbee-pi/api/ {
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection upgrade;
+
+ proxy_pass http://127.0.0.1:9200/;
+ proxy_redirect off;
+
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ client_max_body_size 1000m;
+ }
+}
diff --git a/resources/development/nginx/sites-available/nginx.jackyu.cn b/resources/development/nginx/sites-available/nginx.jackyu.cn
new file mode 100644
index 000000000..254fbbe7b
--- /dev/null
+++ b/resources/development/nginx/sites-available/nginx.jackyu.cn
@@ -0,0 +1,27 @@
+map $http_upgrade $connection_upgrade {
+ default upgrade;
+ '' close;
+}
+server {
+ listen 80;
+ listen [::]:80;
+ server_name nginx.jackyu.cn;
+ rewrite ^(.*)$ https://$host$1 permanent;
+}
+server {
+ listen 443 ssl http2;
+ listen [::]:443 ssl http2;
+ server_name nginx.jackyu.cn;
+ ssl_certificate /etc/nginx/ssl/jackyu.cn/alpha/jackyu.cn_server_cert.pem;
+ ssl_certificate_key /etc/nginx/ssl/jackyu.cn/alpha/jackyu.cn_key.pem;
+ location / {
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection $connection_upgrade;
+ proxy_pass http://127.0.0.1:9000/;
+ }
+}
\ No newline at end of file
diff --git a/resources/development/nginx/sites-available/qi.jackyu.cn b/resources/development/nginx/sites-available/qi.jackyu.cn
new file mode 100644
index 000000000..ba93ac6fc
--- /dev/null
+++ b/resources/development/nginx/sites-available/qi.jackyu.cn
@@ -0,0 +1,26 @@
+server {
+ listen 80;
+ listen [::]:80;
+ server_name qi.jackyu.cn;
+ rewrite ^(.*)$ https://$host$1 permanent;
+
+}
+
+server {
+ server_name qi.jackyu.cn;
+ ssl_certificate /etc/nginx/ssl/jackyu.cn/alpha/jackyu.cn_server_cert.pem;
+ ssl_certificate_key /etc/nginx/ssl/jackyu.cn/alpha/jackyu.cn_key.pem;
+ listen 443 ssl;
+ listen [::]:443 ssl;
+
+ location / {
+ proxy_pass http://127.0.0.1:5001/;
+ proxy_redirect off;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ }
+
+}
+
diff --git a/resources/development/nginx/sites-enabled/amstourship.jackyu.cn b/resources/development/nginx/sites-enabled/amstourship.jackyu.cn
new file mode 120000
index 000000000..e52029a31
--- /dev/null
+++ b/resources/development/nginx/sites-enabled/amstourship.jackyu.cn
@@ -0,0 +1 @@
+/etc/nginx/sites-available/amstourship.jackyu.cn
\ No newline at end of file
diff --git a/resources/development/nginx/ssl/amstourship.jackyu.cn/fullchain.cer b/resources/development/nginx/ssl/amstourship.jackyu.cn/fullchain.cer
new file mode 100644
index 000000000..67d864119
--- /dev/null
+++ b/resources/development/nginx/ssl/amstourship.jackyu.cn/fullchain.cer
@@ -0,0 +1,64 @@
+-----BEGIN CERTIFICATE-----
+MIIFhjCCBG6gAwIBAgITAP/nxy+CumZyaQpnT/Ii3HgcgTANBgkqhkiG9w0BAQsF
+ADBDMQswCQYDVQQGEwJVUzESMBAGA1UEChMJZ29vZCBndXlzMSAwHgYDVQQDExdD
+QSBpbnRlcm1lZGlhdGUgKFJTQSkgQTAeFw0yMzAyMTUwMDUxMDhaFw0yMzA1MTYw
+MDUxMDdaMCAxHjAcBgNVBAMTFWFtc3RvdXJzaGlwLmphY2t5dS5jbjCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBAM8xJTQQiLnb9BPViM8eihPlkJD472vV
+g6u5W6NpmGkvqOmKFcC3IY8D77ATa5IlKeVtgomWAtzQDbQ3+Q8p5gqhotQjLzpp
+xjfK+B9zsrILqDGFpw1n8WKGf/QG6/EsOdecO0ntuhwG80OpsQIDMwjchszC4BPc
+vheCAsRV8pflzEqEqbOzeKcPHa6hDO2Bs70k2zP5SuqTl+GjNzeLKaFpJa5F+rrk
++o4uO8pQOvEGaYW/0i9cN3YKmzfp2yuJY63OsbQ8idSXWTCuSayJhnAjYjgkgcRz
+PWiTNsaQgnPATiZqN81hxq63ISb+kLieq+yMkNAO7xVuItOPDIgawRsCAwEAAaOC
+ApQwggKQMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB
+BQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU2rpC6XfeKM4RRGOOIsXp5MxO
+7yEwKwYDVR0jBCQwIoAgYjWGqKXC1CgUyRtbS1bZxpMqaNdKnoY33nyaZdVEQ/Iw
+cQYIKwYBBQUHAQEEZTBjMCIGCCsGAQUFBzABhhZodHRwOi8vMTI3LjAuMC4xOjQw
+MDIvMD0GCCsGAQUFBzAChjFodHRwOi8vMTI3LjAuMC4xOjQwMDEvYWlhL2lzc3Vl
+ci82NjA1NDQwNDk4MzY5NzQxMCAGA1UdEQQZMBeCFWFtc3RvdXJzaGlwLmphY2t5
+dS5jbjAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8vZXhhbXBsZS5jb20vY3JsMEAG
+A1UdIAQ5MDcwCAYGZ4EMAQIBMCsGAyoDBDAkMCIGCCsGAQUFBwIBFhZodHRwOi8v
+ZXhhbXBsZS5jb20vY3BzMIIBAwYKKwYBBAHWeQIEAgSB9ASB8QDvAHYAOJiMlNA1
+mMOTLd/pI7q68npCDrlsQeFaqAwasPwEvQMAAAGGUsSzKQAABAMARzBFAiEAslVV
+aGQEFOseeWMIcWStkdY5fpgfc/mU8MNr2l2GjRQCIHu/O4lsj4LBupnVN8jwDBZn
+fta/KSsrQ7LvC0SfE5NJAHUAe90gTyc4KkZpHv2pgeSOS224Md6/21UmWIxRF9mX
+veIAAAGGUsSzKgAABAMARjBEAiAOOoRiMeKGRpQckOp04laE6GxN+h+FKeKRCwZ8
+wrWm3QIgCUFPPuJswg1VumJIsLKGdFPgCG8gJM4jVVFdbO/1+5YwDQYJKoZIhvcN
+AQELBQADggEBAEwax/yM6yfUW/ctPvMeiGh4bSaO9xWbQbKrOw5/+K+upqgXUSh0
+Qo0WzcrLZzT9ecp3K6KTbph58IrPGxWmPZo92W9sm2tAR3NIOtSwjCyfeBou0TCO
+yBeHwiJv2JbEZndUAqaz2cd2PfpPbNOWTS9ag6nWRDPPW42VoSYU0EFwzvGfFswA
+vww+gC4kzHjsZuR7T/0qsnvJ/1IHH8KDfzki0TnZ9PAhUvPFBAgxwDElLqUFkSmA
+hc5BJfFrGInnKe1s+DL5oL6oc9RmDdlLV8v0Ita39GlaOs3rdvt611qON9S1Ea6z
+lkGadI0bxxv5GlN+UhNRxyTKseyAsBdzY64=
+-----END CERTIFICATE-----
+
+-----BEGIN CERTIFICATE-----
+MIIFZzCCA0+gAwIBAgIQHPTBy0utaJ82mHJs9V3u8zANBgkqhkiG9w0BAQsFADA5
+MQswCQYDVQQGEwJVUzESMBAGA1UEChMJZ29vZCBndXlzMRYwFAYDVQQDEw1DQSBy
+b290IChSU0EpMB4XDTIwMDEwMTEyMDAwMFoXDTQwMDEwMTEyMDAwMFowQzELMAkG
+A1UEBhMCVVMxEjAQBgNVBAoTCWdvb2QgZ3V5czEgMB4GA1UEAxMXQ0EgaW50ZXJt
+ZWRpYXRlIChSU0EpIEEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCk
+f1rlGJeno27J8UAltWo8PopRsTP93Il6+L+SScaOUQsM+TCbTO5EJ4xC+d3Unp8v
+iZRLAB1/lGhHh/Uifzov2ux2sa9J4kxlfCxVaaCx6maOs9KnfGUug2hcUCh1oUVv
+zD9X9VkWdBdR+kKJvYqWJlU/EJxEa5ERjFp591LQBpR7ksZrsbLvXeywqVS3ek8s
+d7w+ZqpYOfo6DNLl5aEJlk6F6CiSjmT352n8dnsOEIEL+bOusLhP5F8pED85geU5
+rijc38fZ+gfZAVVenz7kqBh7ld6qT5inIM4uQa7oCuFX2dZ0jqm5TFBBtQp9dkFv
+WFz9kEb/CVJr1IsTdp1PAgMBAAGjggFfMIIBWzAOBgNVHQ8BAf8EBAMCAYYwHQYD
+VR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYBAf8CAQAw
+KQYDVR0OBCIEIGI1hqilwtQoFMkbW0tW2caTKmjXSp6GN958mmXVREPyMCsGA1Ud
+IwQkMCKAINmvCHaWHo5MD5lKL52otAsr/TsCX5Hwfy5euQ6zIAWEMFgGCCsGAQUF
+BwEBBEwwSjAjBggrBgEFBQcwAYYXaHR0cDovL2V4YW1wbGUuY29tL29jc3AwIwYI
+KwYBBQUHMAKGF2h0dHA6Ly9leGFtcGxlLmNvbS9yb290MCcGA1UdHwQgMB4wHKAa
+oBiGFmh0dHA6Ly9leGFtcGxlLmNvbS9jcmwwOwYDVR0gBDQwMjAEBgIqAzAqBgIt
+BjAkMCIGCCsGAQUFBwIBFhZodHRwOi8vZXhhbXBsZS5jb20vY3BzMA0GCSqGSIb3
+DQEBCwUAA4ICAQCTLNQlCzHynESAvtPRV1FPaOQhx01RofwS/0Zg3IH5oXxSC98C
+n2L0xHN1gCaJai9XutrFtMCjeBmese48QoPa8MxrB1UpmZ1AuFOQAfHWJZbYPp0V
+PxgY34W9Onb+JPnKTbL9ofKUV0aX67eJ5KKFD1G2z+y9Lz1oA3yJpGzqOY/JCWYz
+q46ik0bmgcGfol6F/T5hoE8pZk8Wr+nNUpSuOSNp7c/g2/pKDRWK8trTrG3owtaJ
+LbQc+W4e97AtTg6DGvR5gftar/+4g2o0xhKSnep+s/bf5NFXVDCTvCmemrbR8Hr7
+NLDKXWuGMoMKIxhyPX6ttpU2Um3rQ1rCQbJ5yWIREZvbdaeK8HSRE3GYE71Z3n/0
+0Kmtg2BKGkrJzcqUSG4o+9mdSjhJ65J76ri5tVQby7Ai7W2KlNjpdI6GYtejUAlf
+vZz5N0e2X36XLPZ8tz04Ix9KLHXMEuA7w/aOglH1Lei+PPp7kBjvXAL66soqCTqu
+49yNbPPGIjGO453+jNzxhbeimh6a5/Fwd4SjsdSBe8AwIGGZTzLiIzCNM5OmcoUf
+Tl5RrVXau4DvX5KvfwOLusl/uJH+7oETJlbi8+fNn2ioYfHg5/Tu3zKZw/Y+6wSA
+LsOIJrFmJEgIBUnWp/B1ZC6TeIokmw5FeJsY1UnFDWsPVcax2T/tg6BZ5Q==
+-----END CERTIFICATE-----
diff --git a/resources/development/nginx/ssl/amstourship.jackyu.cn/private.key b/resources/development/nginx/ssl/amstourship.jackyu.cn/private.key
new file mode 100644
index 000000000..c4388b8a3
--- /dev/null
+++ b/resources/development/nginx/ssl/amstourship.jackyu.cn/private.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEoAIBAAKCAQEAzzElNBCIudv0E9WIzx6KE+WQkPjva9WDq7lbo2mYaS+o6YoV
+wLchjwPvsBNrkiUp5W2CiZYC3NANtDf5DynmCqGi1CMvOmnGN8r4H3OysguoMYWn
+DWfxYoZ/9Abr8Sw515w7Se26HAbzQ6mxAgMzCNyGzMLgE9y+F4ICxFXyl+XMSoSp
+s7N4pw8drqEM7YGzvSTbM/lK6pOX4aM3N4spoWklrkX6uuT6ji47ylA68QZphb/S
+L1w3dgqbN+nbK4ljrc6xtDyJ1JdZMK5JrImGcCNiOCSBxHM9aJM2xpCCc8BOJmo3
+zWHGrrchJv6QuJ6r7IyQ0A7vFW4i048MiBrBGwIDAQABAoIBAFjb3/PLX4gugCh9
+fRYvJ9LOssiqKqyZvsXIUjfsUrRR/2Rhl2C2acsXShW+znS4QZam98QgNQwUorv3
+AXbyZjmLBvVqPZgUwzEseGusyCe5/iF3C6UhPhIeUwQKb+hYKlqBI6BaN2ZLyMrt
+HQAa9SlIwvtQXJ/IHTKhzKP9pHRmETVQCBtJBEl/rxXAZ+Uq+qQde8PJ/UYMTu3f
+Q+r2Rr+pfITJcHBBvB8w03u0hsCpLyN5tJzSKjAUF86lHYEEUh/KM6PrMerYHvsF
+h60VrusUF/jzankkeQFJKsmY043aI3aYjx9+ghj6DOXLhAX52ubB4etsALYpbpTn
+CF6dySkCgYEA66OCWsFl9UrhkAPT+LuKzCs7li+LMCjGjMhXAj0DxVUceG6ZT32H
+rDsEpSPHeL72vAaZIxcnroAJBkz39vYfr49hHUo//GFELiJTlP5S0joZeJvDYhF2
+D1NKzDvLvJq/HkkJor5DGMfTXYbSS8w3tAxacs6o59I1pseOxK333gUCgYEA4Rhf
+73IYw2A44OSBVuy1PxH1zam1AELpUKDHNo1gT9CJhFLiPYuL4Y6jdQl3PVgD23fW
+uTxyu9rC7a/s6HFTx3tIGvUBFKsTLTagHvmZBnx9SKTT//PZELPoObKesNHy3cIS
+NcUoHnRtbvoTIfZXGBOFiByKjz6JrRKNpO4zLJ8Cf1wLgt/wq+MlXPbkh+ihWZZn
+TN9dswEc7HIRz+sP6mkq1fQ1P63NWzHr2+SmFUUVU0wfR7JtWRLQ8LWFW1cczUPG
+viiy2Eu9suRShFOvBKsaBtkV/zxPlc5mutEMiokh6YXrAdiQeaU0aVwfTgZNv3SV
+Xr95+oCTnGGJkjtBuZECgYAXy4vYtCcKAFIC9CT4G23NjQh/BUGtFWW0sEpe6fMU
+fOcMpfZkqyvOXFXLOYTxZyyBNtnsGwfF9ApHNiHFMLW6kL2+m8fRd4Q/c1bZ72jN
+1rsnwsTmedCog1w5PTipWIhkCPAD3yOulIA9CxKatH/ge6/SA7JiEipbsWpn1QZz
+4QKBgCqLsJMIcgZJcJUqor/ta45CppMfM3FXvWyX6tnTK/WyHUHQPN+wfGVB9lNa
+KEHYbF3CfAy2F/RS+aQn8WLCXaurZWsH9spUOPCugj8xtYQ5GFDNrWK9fJclsXFl
+YH0dpcI88pAaKl0ymrjyVp0kFUiaf5JQFKit5UGwGZTtWyvC
+-----END RSA PRIVATE KEY-----
diff --git a/resources/development/nginx/ssl/amstourship.jackyu.cn_t.jackyu.cn/fullchain.cer b/resources/development/nginx/ssl/amstourship.jackyu.cn_t.jackyu.cn/fullchain.cer
new file mode 100644
index 000000000..2428d3aa4
--- /dev/null
+++ b/resources/development/nginx/ssl/amstourship.jackyu.cn_t.jackyu.cn/fullchain.cer
@@ -0,0 +1,64 @@
+-----BEGIN CERTIFICATE-----
+MIIFkzCCBHugAwIBAgITAP9BpIHmYRZFpLiNFNwEvnS6lTANBgkqhkiG9w0BAQsF
+ADBDMQswCQYDVQQGEwJVUzESMBAGA1UEChMJZ29vZCBndXlzMSAwHgYDVQQDExdD
+QSBpbnRlcm1lZGlhdGUgKFJTQSkgQTAeFw0yMzAyMTUwMzM5NDhaFw0yMzA1MTYw
+MzM5NDdaMCAxHjAcBgNVBAMTFWFtc3RvdXJzaGlwLmphY2t5dS5jbjCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBAM31HTeIJ6CcbK1Y1MKb1Pylt2/mXvt8
+M4quWNJjJWWelaRCpe/BD/mBCM9RsQUnf5F8m5OW6QAesoM/QiewTAjf06o5WHfX
+SinH9yMKqXMny4nUBI7U5jJEXbiV82HBsuieU5YldeBCMilefjIG3UNfwNqcA40E
+Miq9xZAGQx9lmabno0iyQlltFyYb1l+4CX0SBm5ygBOyyhb4tReZEB1Sn54n5gtZ
+bK4ZvYgPvKg4wHT6f9A+D4GqE1kZPuGqAKkvAmt8whlEUfXL2zlfNOFfuReQNujs
+FrwnJgSVHa0UPhQEB3zowMjmBSqGZnJap9zXS0W8Eu+D5EWFKUVuRy0CAwEAAaOC
+AqEwggKdMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB
+BQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUxKOLPUnFetKJF7xySU+hY/32
+3FwwKwYDVR0jBCQwIoAgYjWGqKXC1CgUyRtbS1bZxpMqaNdKnoY33nyaZdVEQ/Iw
+cQYIKwYBBQUHAQEEZTBjMCIGCCsGAQUFBzABhhZodHRwOi8vMTI3LjAuMC4xOjQw
+MDIvMD0GCCsGAQUFBzAChjFodHRwOi8vMTI3LjAuMC4xOjQwMDEvYWlhL2lzc3Vl
+ci82NjA1NDQwNDk4MzY5NzQxMC0GA1UdEQQmMCSCFWFtc3RvdXJzaGlwLmphY2t5
+dS5jboILdC5qYWNreXUuY24wJwYDVR0fBCAwHjAcoBqgGIYWaHR0cDovL2V4YW1w
+bGUuY29tL2NybDBABgNVHSAEOTA3MAgGBmeBDAECATArBgMqAwQwJDAiBggrBgEF
+BQcCARYWaHR0cDovL2V4YW1wbGUuY29tL2NwczCCAQMGCisGAQQB1nkCBAIEgfQE
+gfEA7wB2AFLU6MpxhMjJJFwzEHovC5eeKDMFhyNCIngxCrNdv03eAAABhlNfHncA
+AAQDAEcwRQIhAL84cecGwG4bYGHcCxGVwaLPgISazBGaIcOP/11sY78gAiAA+1/3
+XSk0hPTv5zWYwqJIcI0ajGOeiIfaLwpFxnW+DwB1ADiYjJTQNZjDky3f6SO6uvJ6
+Qg65bEHhWqgMGrD8BL0DAAABhlNfHncAAAQDAEYwRAIgS3rH0/r0zBshQN9LwmWv
+JadxbPEJtQuWjhyH/5gln4cCICZlS/B2qYkOZJzQQkjRgnfHrmUc1vRHFNBEGuRR
+HGX2MA0GCSqGSIb3DQEBCwUAA4IBAQBLP7i7PPn3mUtmsYoguW07lQa8abjsHirs
+r5TgfOpWLVFQ8ASWuIu/OTLdKrbfTXseZibLKlPU+Zoz+HF8V3lnCmgXbnlQo/ex
++uEDPkLYyXuWe96nssiVgtUAmkSWQOEwhIz0xtWNgskgRVt2c4CihYbqBB3uXLL1
+TubIFHAizRKcQ/JUDfSnieN4R5tX1MIw/TnUmNxj3KMtF1OHsqGo9Pt2z8oRWGf5
+kx2HtgFyIigkTIlUB5TFmlv5HLAtE4H3cc2NSYZ397WXhil2mTqPTBLRXQJwzQ7C
+5tenpmzazqPOOu23QJaA94a7UeQtowcEDkSMoCe/G31leeEUbRjt
+-----END CERTIFICATE-----
+
+-----BEGIN CERTIFICATE-----
+MIIFZzCCA0+gAwIBAgIQHPTBy0utaJ82mHJs9V3u8zANBgkqhkiG9w0BAQsFADA5
+MQswCQYDVQQGEwJVUzESMBAGA1UEChMJZ29vZCBndXlzMRYwFAYDVQQDEw1DQSBy
+b290IChSU0EpMB4XDTIwMDEwMTEyMDAwMFoXDTQwMDEwMTEyMDAwMFowQzELMAkG
+A1UEBhMCVVMxEjAQBgNVBAoTCWdvb2QgZ3V5czEgMB4GA1UEAxMXQ0EgaW50ZXJt
+ZWRpYXRlIChSU0EpIEEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCk
+f1rlGJeno27J8UAltWo8PopRsTP93Il6+L+SScaOUQsM+TCbTO5EJ4xC+d3Unp8v
+iZRLAB1/lGhHh/Uifzov2ux2sa9J4kxlfCxVaaCx6maOs9KnfGUug2hcUCh1oUVv
+zD9X9VkWdBdR+kKJvYqWJlU/EJxEa5ERjFp591LQBpR7ksZrsbLvXeywqVS3ek8s
+d7w+ZqpYOfo6DNLl5aEJlk6F6CiSjmT352n8dnsOEIEL+bOusLhP5F8pED85geU5
+rijc38fZ+gfZAVVenz7kqBh7ld6qT5inIM4uQa7oCuFX2dZ0jqm5TFBBtQp9dkFv
+WFz9kEb/CVJr1IsTdp1PAgMBAAGjggFfMIIBWzAOBgNVHQ8BAf8EBAMCAYYwHQYD
+VR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYBAf8CAQAw
+KQYDVR0OBCIEIGI1hqilwtQoFMkbW0tW2caTKmjXSp6GN958mmXVREPyMCsGA1Ud
+IwQkMCKAINmvCHaWHo5MD5lKL52otAsr/TsCX5Hwfy5euQ6zIAWEMFgGCCsGAQUF
+BwEBBEwwSjAjBggrBgEFBQcwAYYXaHR0cDovL2V4YW1wbGUuY29tL29jc3AwIwYI
+KwYBBQUHMAKGF2h0dHA6Ly9leGFtcGxlLmNvbS9yb290MCcGA1UdHwQgMB4wHKAa
+oBiGFmh0dHA6Ly9leGFtcGxlLmNvbS9jcmwwOwYDVR0gBDQwMjAEBgIqAzAqBgIt
+BjAkMCIGCCsGAQUFBwIBFhZodHRwOi8vZXhhbXBsZS5jb20vY3BzMA0GCSqGSIb3
+DQEBCwUAA4ICAQCTLNQlCzHynESAvtPRV1FPaOQhx01RofwS/0Zg3IH5oXxSC98C
+n2L0xHN1gCaJai9XutrFtMCjeBmese48QoPa8MxrB1UpmZ1AuFOQAfHWJZbYPp0V
+PxgY34W9Onb+JPnKTbL9ofKUV0aX67eJ5KKFD1G2z+y9Lz1oA3yJpGzqOY/JCWYz
+q46ik0bmgcGfol6F/T5hoE8pZk8Wr+nNUpSuOSNp7c/g2/pKDRWK8trTrG3owtaJ
+LbQc+W4e97AtTg6DGvR5gftar/+4g2o0xhKSnep+s/bf5NFXVDCTvCmemrbR8Hr7
+NLDKXWuGMoMKIxhyPX6ttpU2Um3rQ1rCQbJ5yWIREZvbdaeK8HSRE3GYE71Z3n/0
+0Kmtg2BKGkrJzcqUSG4o+9mdSjhJ65J76ri5tVQby7Ai7W2KlNjpdI6GYtejUAlf
+vZz5N0e2X36XLPZ8tz04Ix9KLHXMEuA7w/aOglH1Lei+PPp7kBjvXAL66soqCTqu
+49yNbPPGIjGO453+jNzxhbeimh6a5/Fwd4SjsdSBe8AwIGGZTzLiIzCNM5OmcoUf
+Tl5RrVXau4DvX5KvfwOLusl/uJH+7oETJlbi8+fNn2ioYfHg5/Tu3zKZw/Y+6wSA
+LsOIJrFmJEgIBUnWp/B1ZC6TeIokmw5FeJsY1UnFDWsPVcax2T/tg6BZ5Q==
+-----END CERTIFICATE-----
diff --git a/resources/development/nginx/ssl/amstourship.jackyu.cn_t.jackyu.cn/private.key b/resources/development/nginx/ssl/amstourship.jackyu.cn_t.jackyu.cn/private.key
new file mode 100644
index 000000000..5273a22e0
--- /dev/null
+++ b/resources/development/nginx/ssl/amstourship.jackyu.cn_t.jackyu.cn/private.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAzfUdN4gnoJxsrVjUwpvU/KW3b+Ze+3wziq5Y0mMlZZ6VpEKl
+78EP+YEIz1GxBSd/kXybk5bpAB6ygz9CJ7BMCN/TqjlYd9dKKcf3IwqpcyfLidQE
+jtTmMkRduJXzYcGy6J5TliV14EIyKV5+MgbdQ1/A2pwDjQQyKr3FkAZDH2WZpuej
+SLJCWW0XJhvWX7gJfRIGbnKAE7LKFvi1F5kQHVKfnifmC1lsrhm9iA+8qDjAdPp/
+0D4PgaoTWRk+4aoAqS8Ca3zCGURR9cvbOV804V+5F5A26OwWvCcmBJUdrRQ+FAQH
+fOjAyOYFKoZmclqn3NdLRbwS74PkRYUpRW5HLQIDAQABAoIBAGCuuRlxfBDEfave
+cHouxwwXCwanoVzzEAsBD0csLckHaf3jH5xuB/67WRRhp/Tgdt0oHqxpAlYBExHT
+p02UUA02bVmSc/pGAVWdvmEfxy38t2qrMbyPKsTcHRbipY71a/QRJvHsAerViFCt
+QBZh7IqNL64v4ObY2mgAD/ctSWM56gqkFR70X7HpMKQ9Z8Q9iT5IKeFdVtfOcdSq
+1HuAiz4p8v8IOzLHTKruY5OTYM9uyr02FtPoWsoZSyzSjaC8BpO+wnFlVsDXDBNv
+/Kgqyetj6Iqo6cInj8dExpdnCZXqO5j4FXgHABLgjgiIJV+L3F0KY1PbO9NejMEh
+TvKgZiECgYEA6aIGaUtdoPXWY1o4sEjHy8rwQnBs25JPx+4+HOC5wHrsn03yTmXC
+cjKapIySQhasekQZGQk++H4cRyToqR0izIfkqEmOLfec2kKy7FgcSXzs2PKH66om
+4mvYSWVQj0r3SwYyG6UHJJCeY8i+RCLMkEXSkNPvhEQXg7zCI2oMpSUCgYEA4azO
+yfHvUKnJbGW+r3ujbszn7IlsobVTL0Rf4nHLSuKrW0v/OFg9lsjsm6IJyWV1H5KO
+NNNVzpubAESvPujAwiYfSeFLjK67hbEVSaVLp+5ubsGKf+0BDPKh3+yVJ6abHmFo
+lqzqUlZhZteMp8BN+n3fwR/W5RJFAju9o3F972kCgYBq3IlCMA6rSMa+us2jFCcO
+t8wdF38zD0EemYIfg0pzF8aTNvvVkAXYZf0FtqZPRD+vzOYN5YS/9C7K+77PW1xH
+YQDdWIeHzvIXgtqD7+lAU7uhn4075Z/TgLB1IbovUIK79iGFM36I4v0PdwpP7/rR
+Ip6lT8sGHH8E6pmByUfvYQKBgQDhG1r6HZY1w+bjdWoL6SxQ7Zu6Wio9830SfQWX
+/yJlhEyhOOFP9tUYfztk0vEoL0fxQmMPVm8VNCoczmZwPgNoplY3f7+4iOMMrGMr
+nvIkhLUrTWs1x9dwbuYBUyBE+O9qEogdJEZn8zodN41aF3yxDLYREg1tWhfz7ltv
+mVmhYQKBgBFtHgvmlw/fA5P4dfn/xnOFYwz2v+f5dxcD9DXP9Hu53m7mVPTEUC1t
+2SiBaaUMqTiOflW8Aq9qHHvOPNoO1GrGt4oUbhrAzKwxmkiCUyspYTRBOWHbLjW0
+07Mu40Y8I0WqEVenAGIsHfrsXzvdzat0bijsAt+P1LMliRjK7qhh
+-----END RSA PRIVATE KEY-----
diff --git a/resources/development/nginx/uwsgi_params b/resources/development/nginx/uwsgi_params
new file mode 100644
index 000000000..09c732cd6
--- /dev/null
+++ b/resources/development/nginx/uwsgi_params
@@ -0,0 +1,17 @@
+
+uwsgi_param QUERY_STRING $query_string;
+uwsgi_param REQUEST_METHOD $request_method;
+uwsgi_param CONTENT_TYPE $content_type;
+uwsgi_param CONTENT_LENGTH $content_length;
+
+uwsgi_param REQUEST_URI $request_uri;
+uwsgi_param PATH_INFO $document_uri;
+uwsgi_param DOCUMENT_ROOT $document_root;
+uwsgi_param SERVER_PROTOCOL $server_protocol;
+uwsgi_param REQUEST_SCHEME $scheme;
+uwsgi_param HTTPS $https if_not_empty;
+
+uwsgi_param REMOTE_ADDR $remote_addr;
+uwsgi_param REMOTE_PORT $remote_port;
+uwsgi_param SERVER_PORT $server_port;
+uwsgi_param SERVER_NAME $server_name;
diff --git a/resources/development/sources.list b/resources/development/sources.list
new file mode 100644
index 000000000..36daae260
--- /dev/null
+++ b/resources/development/sources.list
@@ -0,0 +1,9 @@
+# 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释
+deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy main restricted universe multiverse
+# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy main restricted universe multiverse
+deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse
+# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse
+deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse
+# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse
+deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-security main restricted universe multiverse
+# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-security main restricted universe multiverse
diff --git a/server/api/cert.go b/server/api/cert.go
index 6fd3fd36f..230f96190 100644
--- a/server/api/cert.go
+++ b/server/api/cert.go
@@ -1,352 +1,350 @@
package api
import (
- "github.com/0xJacky/Nginx-UI/server/model"
- "github.com/0xJacky/Nginx-UI/server/pkg/cert"
- "github.com/0xJacky/Nginx-UI/server/pkg/nginx"
- "github.com/gin-gonic/gin"
- "github.com/gorilla/websocket"
- "github.com/spf13/cast"
- "log"
- "net/http"
- "os"
- "path/filepath"
- "strings"
+ "github.com/0xJacky/Nginx-UI/server/model"
+ "github.com/0xJacky/Nginx-UI/server/pkg/cert"
+ "github.com/0xJacky/Nginx-UI/server/pkg/nginx"
+ "github.com/gin-gonic/gin"
+ "github.com/gorilla/websocket"
+ "github.com/spf13/cast"
+ "log"
+ "net/http"
+ "os"
+ "path/filepath"
+ "strings"
)
const (
- Success = "success"
- Info = "info"
- Error = "error"
+ Success = "success"
+ Info = "info"
+ Error = "error"
)
type IssueCertResponse struct {
- Status string `json:"status"`
- Message string `json:"message"`
- SSLCertificate string `json:"ssl_certificate,omitempty"`
- SSLCertificateKey string `json:"ssl_certificate_key,omitempty"`
+ Status string `json:"status"`
+ Message string `json:"message"`
+ SSLCertificate string `json:"ssl_certificate,omitempty"`
+ SSLCertificateKey string `json:"ssl_certificate_key,omitempty"`
}
func handleIssueCertLogChan(conn *websocket.Conn, logChan chan string) {
- defer func() {
- if err := recover(); err != nil {
- log.Println("api.handleIssueCertLogChan recover", err)
- }
- }()
+ defer func() {
+ if err := recover(); err != nil {
+ log.Println("api.handleIssueCertLogChan recover", err)
+ }
+ }()
- for logString := range logChan {
+ for logString := range logChan {
- err := conn.WriteJSON(IssueCertResponse{
- Status: Info,
- Message: logString,
- })
+ err := conn.WriteJSON(IssueCertResponse{
+ Status: Info,
+ Message: logString,
+ })
- if err != nil {
- log.Println("Error handleIssueCertLogChan", err)
- return
- }
+ if err != nil {
+ log.Println("Error handleIssueCertLogChan", err)
+ return
+ }
- }
+ }
}
func IssueCert(c *gin.Context) {
- var upGrader = websocket.Upgrader{
- CheckOrigin: func(r *http.Request) bool {
- return true
- },
- }
-
- // upgrade http to websocket
- ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)
- if err != nil {
- log.Println(err)
- return
- }
-
- defer func(ws *websocket.Conn) {
- err := ws.Close()
- if err != nil {
- log.Println("defer websocket close err", err)
- }
- }(ws)
-
- // read
- var buffer struct {
- ServerName []string `json:"server_name"`
- }
-
- err = ws.ReadJSON(&buffer)
-
- if err != nil {
- log.Println(err)
- return
- }
-
- logChan := make(chan string, 1)
- errChan := make(chan error, 1)
-
- go cert.IssueCert(buffer.ServerName, logChan, errChan)
-
- domain := strings.Join(buffer.ServerName, "_")
-
- go handleIssueCertLogChan(ws, logChan)
-
- // block, unless errChan closed
- for err = range errChan {
- log.Println("Error cert.IssueCert", err)
-
- err = ws.WriteJSON(IssueCertResponse{
- Status: Error,
- Message: err.Error(),
- })
-
- if err != nil {
- log.Println("Error WriteJSON", err)
- return
- }
-
- return
- }
-
- close(logChan)
-
- sslCertificatePath := nginx.GetConfPath("ssl", domain, "fullchain.cer")
- sslCertificateKeyPath := nginx.GetConfPath("ssl", domain, "private.key")
-
- certModel, err := model.FirstOrCreateCert(domain)
-
- if err != nil {
- log.Println(err)
- }
-
- err = certModel.Updates(&model.Cert{
- SSLCertificatePath: sslCertificatePath,
- SSLCertificateKeyPath: sslCertificateKeyPath,
- })
-
- if err != nil {
- log.Println(err)
- }
-
- err = ws.WriteJSON(IssueCertResponse{
- Status: Success,
- Message: "Issued certificate successfully",
- SSLCertificate: sslCertificatePath,
- SSLCertificateKey: sslCertificateKeyPath,
- })
-
- if err != nil {
- log.Println(err)
- return
- }
+ var upGrader = websocket.Upgrader{
+ CheckOrigin: func(r *http.Request) bool {
+ return true
+ },
+ }
+
+ // upgrade http to websocket
+ ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)
+ if err != nil {
+ log.Println(err)
+ return
+ }
+
+ defer func(ws *websocket.Conn) {
+ err := ws.Close()
+ if err != nil {
+ log.Println("defer websocket close err", err)
+ }
+ }(ws)
+
+ // read
+ var buffer struct {
+ ServerName []string `json:"server_name"`
+ }
+
+ err = ws.ReadJSON(&buffer)
+
+ if err != nil {
+ log.Println(err)
+ return
+ }
+
+ certModel, err := model.FirstOrCreateCert(c.Param("name"))
+
+ if err != nil {
+ log.Println(err)
+ }
+
+ logChan := make(chan string, 1)
+ errChan := make(chan error, 1)
+
+ go cert.IssueCert(buffer.ServerName, logChan, errChan)
+
+ go handleIssueCertLogChan(ws, logChan)
+
+ // block, until errChan closes
+ for err = range errChan {
+ errLog := &cert.AutoCertErrorLog{}
+ errLog.SetCertModel(&certModel)
+ errLog.Exit("issue cert", err)
+
+ err = ws.WriteJSON(IssueCertResponse{
+ Status: Error,
+ Message: err.Error(),
+ })
+
+ if err != nil {
+ log.Println("Error WriteJSON", err)
+ return
+ }
+
+ return
+ }
+
+ certDirName := strings.Join(buffer.ServerName, "_")
+ sslCertificatePath := nginx.GetConfPath("ssl", certDirName, "fullchain.cer")
+ sslCertificateKeyPath := nginx.GetConfPath("ssl", certDirName, "private.key")
+
+ err = certModel.Updates(&model.Cert{
+ Domains: buffer.ServerName,
+ SSLCertificatePath: sslCertificatePath,
+ SSLCertificateKeyPath: sslCertificateKeyPath,
+ })
+
+ if err != nil {
+ log.Println(err)
+ err = ws.WriteJSON(IssueCertResponse{
+ Status: Error,
+ Message: err.Error(),
+ })
+ return
+ }
+
+ certModel.ClearLog()
+
+ err = ws.WriteJSON(IssueCertResponse{
+ Status: Success,
+ Message: "Issued certificate successfully",
+ SSLCertificate: sslCertificatePath,
+ SSLCertificateKey: sslCertificateKeyPath,
+ })
+
+ if err != nil {
+ log.Println(err)
+ return
+ }
}
func GetCertList(c *gin.Context) {
- certList := model.GetCertList(c.Query("name"), c.Query("domain"))
+ certList := model.GetCertList(c.Query("name"), c.Query("domain"))
- c.JSON(http.StatusOK, gin.H{
- "data": certList,
- })
+ c.JSON(http.StatusOK, gin.H{
+ "data": certList,
+ })
}
-func getCert(c *gin.Context, certModel model.Cert) {
- type resp struct {
- model.Cert
- SSLCertification string `json:"ssl_certification"`
- SSLCertificationKey string `json:"ssl_certification_key"`
- CertificateInfo *CertificateInfo `json:"certificate_info,omitempty"`
- }
-
- var sslCertificationBytes, sslCertificationKeyBytes []byte
- var certificateInfo *CertificateInfo
- if certModel.SSLCertificatePath != "" {
- if _, err := os.Stat(certModel.SSLCertificatePath); err == nil {
- sslCertificationBytes, _ = os.ReadFile(certModel.SSLCertificatePath)
- }
-
- pubKey, err := cert.GetCertInfo(certModel.SSLCertificatePath)
-
- if err != nil {
- ErrHandler(c, err)
- return
- }
-
- certificateInfo = &CertificateInfo{
- SubjectName: pubKey.Subject.CommonName,
- IssuerName: pubKey.Issuer.CommonName,
- NotAfter: pubKey.NotAfter,
- NotBefore: pubKey.NotBefore,
- }
- }
-
- if certModel.SSLCertificateKeyPath != "" {
- if _, err := os.Stat(certModel.SSLCertificateKeyPath); err == nil {
- sslCertificationKeyBytes, _ = os.ReadFile(certModel.SSLCertificateKeyPath)
- }
- }
-
- c.JSON(http.StatusOK, resp{
- certModel,
- string(sslCertificationBytes),
- string(sslCertificationKeyBytes),
- certificateInfo,
- })
+func getCert(c *gin.Context, certModel *model.Cert) {
+ type resp struct {
+ *model.Cert
+ SSLCertification string `json:"ssl_certification"`
+ SSLCertificationKey string `json:"ssl_certification_key"`
+ CertificateInfo *CertificateInfo `json:"certificate_info,omitempty"`
+ }
+
+ var sslCertificationBytes, sslCertificationKeyBytes []byte
+ var certificateInfo *CertificateInfo
+ if certModel.SSLCertificatePath != "" {
+ if _, err := os.Stat(certModel.SSLCertificatePath); err == nil {
+ sslCertificationBytes, _ = os.ReadFile(certModel.SSLCertificatePath)
+ }
+
+ pubKey, err := cert.GetCertInfo(certModel.SSLCertificatePath)
+
+ if err != nil {
+ ErrHandler(c, err)
+ return
+ }
+
+ certificateInfo = &CertificateInfo{
+ SubjectName: pubKey.Subject.CommonName,
+ IssuerName: pubKey.Issuer.CommonName,
+ NotAfter: pubKey.NotAfter,
+ NotBefore: pubKey.NotBefore,
+ }
+ }
+
+ if certModel.SSLCertificateKeyPath != "" {
+ if _, err := os.Stat(certModel.SSLCertificateKeyPath); err == nil {
+ sslCertificationKeyBytes, _ = os.ReadFile(certModel.SSLCertificateKeyPath)
+ }
+ }
+
+ c.JSON(http.StatusOK, resp{
+ certModel,
+ string(sslCertificationBytes),
+ string(sslCertificationKeyBytes),
+ certificateInfo,
+ })
}
func GetCert(c *gin.Context) {
- certModel, err := model.FirstCertByID(cast.ToInt(c.Param("id")))
+ certModel, err := model.FirstCertByID(cast.ToInt(c.Param("id")))
- if err != nil {
- ErrHandler(c, err)
- return
- }
+ if err != nil {
+ ErrHandler(c, err)
+ return
+ }
- getCert(c, certModel)
+ getCert(c, &certModel)
}
func AddCert(c *gin.Context) {
- var json struct {
- Name string `json:"name"`
- Domain string `json:"domain" binding:"required"`
- SSLCertificatePath string `json:"ssl_certificate_path" binding:"required"`
- SSLCertificateKeyPath string `json:"ssl_certificate_key_path" binding:"required"`
- SSLCertification string `json:"ssl_certification"`
- SSLCertificationKey string `json:"ssl_certification_key"`
- }
- if !BindAndValid(c, &json) {
- return
- }
- certModel, err := model.FirstOrCreateCert(json.Domain)
-
- if err != nil {
- ErrHandler(c, err)
- return
- }
-
- err = certModel.Updates(&model.Cert{
- Name: json.Name,
- Domain: json.Domain,
- SSLCertificatePath: json.SSLCertificatePath,
- SSLCertificateKeyPath: json.SSLCertificateKeyPath,
- })
-
- if err != nil {
- ErrHandler(c, err)
- return
- }
-
- err = os.MkdirAll(filepath.Dir(json.SSLCertificatePath), 0644)
- if err != nil {
- ErrHandler(c, err)
- return
- }
-
- err = os.MkdirAll(filepath.Dir(json.SSLCertificateKeyPath), 0644)
- if err != nil {
- ErrHandler(c, err)
- return
- }
-
- if json.SSLCertification != "" {
- err = os.WriteFile(json.SSLCertificatePath, []byte(json.SSLCertification), 0644)
- if err != nil {
- ErrHandler(c, err)
- return
- }
- }
-
- if json.SSLCertificationKey != "" {
- err = os.WriteFile(json.SSLCertificateKeyPath, []byte(json.SSLCertificationKey), 0644)
- if err != nil {
- ErrHandler(c, err)
- return
- }
- }
-
- getCert(c, certModel)
+ var json struct {
+ Name string `json:"name"`
+ SSLCertificatePath string `json:"ssl_certificate_path" binding:"required"`
+ SSLCertificateKeyPath string `json:"ssl_certificate_key_path" binding:"required"`
+ SSLCertification string `json:"ssl_certification"`
+ SSLCertificationKey string `json:"ssl_certification_key"`
+ }
+ if !BindAndValid(c, &json) {
+ return
+ }
+ certModel := &model.Cert{
+ Name: json.Name,
+ SSLCertificatePath: json.SSLCertificatePath,
+ SSLCertificateKeyPath: json.SSLCertificateKeyPath,
+ }
+
+ err := certModel.Insert()
+
+ if err != nil {
+ ErrHandler(c, err)
+ return
+ }
+
+ err = os.MkdirAll(filepath.Dir(json.SSLCertificatePath), 0644)
+ if err != nil {
+ ErrHandler(c, err)
+ return
+ }
+
+ err = os.MkdirAll(filepath.Dir(json.SSLCertificateKeyPath), 0644)
+ if err != nil {
+ ErrHandler(c, err)
+ return
+ }
+
+ if json.SSLCertification != "" {
+ err = os.WriteFile(json.SSLCertificatePath, []byte(json.SSLCertification), 0644)
+ if err != nil {
+ ErrHandler(c, err)
+ return
+ }
+ }
+
+ if json.SSLCertificationKey != "" {
+ err = os.WriteFile(json.SSLCertificateKeyPath, []byte(json.SSLCertificationKey), 0644)
+ if err != nil {
+ ErrHandler(c, err)
+ return
+ }
+ }
+
+ getCert(c, certModel)
}
func ModifyCert(c *gin.Context) {
- id := cast.ToInt(c.Param("id"))
- certModel, err := model.FirstCertByID(id)
-
- var json struct {
- Name string `json:"name"`
- Domain string `json:"domain" binding:"required"`
- SSLCertificatePath string `json:"ssl_certificate_path" binding:"required"`
- SSLCertificateKeyPath string `json:"ssl_certificate_key_path" binding:"required"`
- SSLCertification string `json:"ssl_certification"`
- SSLCertificationKey string `json:"ssl_certification_key"`
- }
-
- if !BindAndValid(c, &json) {
- return
- }
-
- if err != nil {
- ErrHandler(c, err)
- return
- }
-
- err = certModel.Updates(&model.Cert{
- Name: json.Name,
- Domain: json.Domain,
- SSLCertificatePath: json.SSLCertificatePath,
- SSLCertificateKeyPath: json.SSLCertificateKeyPath,
- })
-
- if err != nil {
- ErrHandler(c, err)
- return
- }
-
- err = os.MkdirAll(filepath.Dir(json.SSLCertificatePath), 0644)
- if err != nil {
- ErrHandler(c, err)
- return
- }
-
- err = os.MkdirAll(filepath.Dir(json.SSLCertificateKeyPath), 0644)
- if err != nil {
- ErrHandler(c, err)
- return
- }
-
- if json.SSLCertification != "" {
- err = os.WriteFile(json.SSLCertificatePath, []byte(json.SSLCertification), 0644)
- if err != nil {
- ErrHandler(c, err)
- return
- }
- }
-
- if json.SSLCertificationKey != "" {
- err = os.WriteFile(json.SSLCertificateKeyPath, []byte(json.SSLCertificationKey), 0644)
- if err != nil {
- ErrHandler(c, err)
- return
- }
- }
-
- GetCert(c)
+ id := cast.ToInt(c.Param("id"))
+ certModel, err := model.FirstCertByID(id)
+
+ var json struct {
+ Name string `json:"name"`
+ SSLCertificatePath string `json:"ssl_certificate_path" binding:"required"`
+ SSLCertificateKeyPath string `json:"ssl_certificate_key_path" binding:"required"`
+ SSLCertification string `json:"ssl_certification"`
+ SSLCertificationKey string `json:"ssl_certification_key"`
+ }
+
+ if !BindAndValid(c, &json) {
+ return
+ }
+
+ if err != nil {
+ ErrHandler(c, err)
+ return
+ }
+
+ err = certModel.Updates(&model.Cert{
+ Name: json.Name,
+ SSLCertificatePath: json.SSLCertificatePath,
+ SSLCertificateKeyPath: json.SSLCertificateKeyPath,
+ })
+
+ if err != nil {
+ ErrHandler(c, err)
+ return
+ }
+
+ err = os.MkdirAll(filepath.Dir(json.SSLCertificatePath), 0644)
+ if err != nil {
+ ErrHandler(c, err)
+ return
+ }
+
+ err = os.MkdirAll(filepath.Dir(json.SSLCertificateKeyPath), 0644)
+ if err != nil {
+ ErrHandler(c, err)
+ return
+ }
+
+ if json.SSLCertification != "" {
+ err = os.WriteFile(json.SSLCertificatePath, []byte(json.SSLCertification), 0644)
+ if err != nil {
+ ErrHandler(c, err)
+ return
+ }
+ }
+
+ if json.SSLCertificationKey != "" {
+ err = os.WriteFile(json.SSLCertificateKeyPath, []byte(json.SSLCertificationKey), 0644)
+ if err != nil {
+ ErrHandler(c, err)
+ return
+ }
+ }
+
+ GetCert(c)
}
func RemoveCert(c *gin.Context) {
- id := cast.ToInt(c.Param("id"))
- certModel, err := model.FirstCertByID(id)
+ id := cast.ToInt(c.Param("id"))
+ certModel, err := model.FirstCertByID(id)
- if err != nil {
- ErrHandler(c, err)
- return
- }
+ if err != nil {
+ ErrHandler(c, err)
+ return
+ }
- err = certModel.Remove()
+ err = certModel.Remove()
- if err != nil {
- ErrHandler(c, err)
- return
- }
+ if err != nil {
+ ErrHandler(c, err)
+ return
+ }
- c.JSON(http.StatusNoContent, nil)
+ c.JSON(http.StatusNoContent, nil)
}
diff --git a/server/api/domain.go b/server/api/domain.go
index 4626de31d..e5faf9f2a 100644
--- a/server/api/domain.go
+++ b/server/api/domain.go
@@ -104,15 +104,8 @@ func GetDomain(c *gin.Context) {
c.Set("maybe_error", "")
certInfoMap := make(map[int]CertificateInfo)
- var serverName string
for serverIdx, server := range config.Servers {
for _, directive := range server.Directives {
-
- if directive.Directive == "server_name" {
- serverName = strings.ReplaceAll(directive.Params, " ", "_")
- continue
- }
-
if directive.Directive == "ssl_certificate" {
pubKey, err := cert.GetCertInfo(directive.Params)
@@ -134,7 +127,7 @@ func GetDomain(c *gin.Context) {
}
}
- certModel, _ := model.FirstCert(serverName)
+ certModel, _ := model.FirstCert(name)
c.Set("maybe_error", "nginx_config_syntax_error")
@@ -304,7 +297,7 @@ func DisableDomain(c *gin.Context) {
}
// delete auto cert record
- certModel := model.Cert{Domain: c.Param("name")}
+ certModel := model.Cert{Filename: c.Param("name")}
err = certModel.Remove()
if err != nil {
ErrHandler(c, err)
@@ -345,7 +338,7 @@ func DeleteDomain(c *gin.Context) {
return
}
- certModel := model.Cert{Domain: name}
+ certModel := model.Cert{Filename: name}
_ = certModel.Remove()
err = os.Remove(availablePath)
@@ -362,9 +355,17 @@ func DeleteDomain(c *gin.Context) {
}
func AddDomainToAutoCert(c *gin.Context) {
- domain := c.Param("domain")
- domain = strings.ReplaceAll(domain, " ", "_")
- certModel, err := model.FirstOrCreateCert(domain)
+ name := c.Param("name")
+
+ var json struct {
+ Domains []string `json:"domains"`
+ }
+
+ if !BindAndValid(c, &json) {
+ return
+ }
+
+ certModel, err := model.FirstOrCreateCert(name)
if err != nil {
ErrHandler(c, err)
@@ -372,6 +373,8 @@ func AddDomainToAutoCert(c *gin.Context) {
}
err = certModel.Updates(&model.Cert{
+ Name: name,
+ Domains: json.Domains,
AutoCert: model.AutoCertEnabled,
})
@@ -384,13 +387,15 @@ func AddDomainToAutoCert(c *gin.Context) {
}
func RemoveDomainFromAutoCert(c *gin.Context) {
- domain := c.Param("domain")
- domain = strings.ReplaceAll(domain, " ", "_")
- certModel := model.Cert{
- Domain: domain,
+ name := c.Param("name")
+ certModel, err := model.FirstCert(name)
+
+ if err != nil {
+ ErrHandler(c, err)
+ return
}
- err := certModel.Updates(&model.Cert{
+ err = certModel.Updates(&model.Cert{
AutoCert: model.AutoCertDisabled,
})
diff --git a/server/model/cert.go b/server/model/cert.go
index ece89584e..2cc9f5e40 100644
--- a/server/model/cert.go
+++ b/server/model/cert.go
@@ -2,6 +2,7 @@ package model
import (
"github.com/0xJacky/Nginx-UI/server/pkg/nginx"
+ "github.com/lib/pq"
"os"
)
@@ -10,30 +11,38 @@ const (
AutoCertDisabled = -1
)
+type CertDomains []string
+
type Cert struct {
Model
- Name string `json:"name"`
- Domain string `json:"domain"`
- SSLCertificatePath string `json:"ssl_certificate_path"`
- SSLCertificateKeyPath string `json:"ssl_certificate_key_path"`
- AutoCert int `json:"auto_cert"`
+ Name string `json:"name"`
+ Domains pq.StringArray `json:"domains" gorm:"type:text[]"`
+ Filename string `json:"filename"`
+ SSLCertificatePath string `json:"ssl_certificate_path"`
+ SSLCertificateKeyPath string `json:"ssl_certificate_key_path"`
+ AutoCert int `json:"auto_cert"`
+ Log string `json:"log"`
}
-func FirstCert(domain string) (c Cert, err error) {
+func FirstCert(confName string) (c Cert, err error) {
err = db.First(&c, &Cert{
- Domain: domain,
+ Filename: confName,
}).Error
return
}
-func FirstOrCreateCert(domain string) (c Cert, err error) {
- err = db.FirstOrCreate(&c, &Cert{Domain: domain}).Error
+func FirstOrCreateCert(confName string) (c Cert, err error) {
+ err = db.FirstOrCreate(&c, &Cert{Filename: confName}).Error
return
}
-func GetAutoCertList() (c []Cert) {
- var t []Cert
+func (c *Cert) Insert() error {
+ return db.Create(c).Error
+}
+
+func GetAutoCertList() (c []*Cert) {
+ var t []*Cert
db.Where("auto_cert", AutoCertEnabled).Find(&t)
// check if this domain is enabled
@@ -49,7 +58,7 @@ func GetAutoCertList() (c []Cert) {
}
for _, v := range t {
- if enabledConfigMap[v.Domain] == true {
+ if enabledConfigMap[v.Filename] == true {
c = append(c, v)
}
}
@@ -76,9 +85,17 @@ func FirstCertByID(id int) (c Cert, err error) {
}
func (c *Cert) Updates(n *Cert) error {
- return db.Model(c).Updates(n).Error
+ return db.Model(&Cert{}).Where("id", c.ID).Updates(n).Error
+}
+
+func (c *Cert) ClearLog() {
+ db.Model(&Cert{}).Where("id", c.ID).Update("log", "")
}
func (c *Cert) Remove() error {
- return db.Where("domain", c.Domain).Delete(c).Error
+ if c.Filename == "" {
+ return db.Delete(c).Error
+ }
+
+ return db.Where("filename", c.Filename).Delete(c).Error
}
diff --git a/server/pkg/cert/auto_cert.go b/server/pkg/cert/auto_cert.go
index 9d5e08044..eb3d13603 100644
--- a/server/pkg/cert/auto_cert.go
+++ b/server/pkg/cert/auto_cert.go
@@ -1,72 +1,123 @@
package cert
import (
- "github.com/0xJacky/Nginx-UI/server/model"
- "log"
- "strings"
- "time"
+ "fmt"
+ "github.com/0xJacky/Nginx-UI/server/model"
+ "github.com/pkg/errors"
+ "log"
+ "time"
)
func handleIssueCertLogChan(logChan chan string) {
- defer func() {
- if err := recover(); err != nil {
- log.Println("[Auto Cert] handleIssueCertLogChan", err)
- }
- }()
-
- for logString := range logChan {
- log.Println("[Auto Cert] Info", logString)
- }
+ defer func() {
+ if err := recover(); err != nil {
+ log.Println("[Auto Cert] handleIssueCertLogChan", err)
+ }
+ }()
+
+ for logString := range logChan {
+ log.Println("[Auto Cert] Info", logString)
+ }
+}
+
+type AutoCertErrorLog struct {
+ buffer []string
+ cert *model.Cert
+}
+
+func (t *AutoCertErrorLog) SetCertModel(cert *model.Cert) {
+ t.cert = cert
+}
+
+func (t *AutoCertErrorLog) Push(text string, err error) {
+ t.buffer = append(t.buffer, text+" "+err.Error())
+ log.Println("[AutoCert Error]", text, err)
+}
+
+func (t *AutoCertErrorLog) Exit(text string, err error) {
+ t.buffer = append(t.buffer, text+" "+err.Error())
+ log.Println("[AutoCert Error]", text, err)
+
+ if t.cert == nil {
+ return
+ }
+
+ _ = t.cert.Updates(&model.Cert{
+ Log: t.ToString(),
+ })
+}
+
+func (t *AutoCertErrorLog) ToString() (content string) {
+
+ for _, v := range t.buffer {
+ content += fmt.Sprintf("[AutoCert Error] %s\n", v)
+ }
+
+ return
}
-func AutoCert() {
- defer func() {
- if err := recover(); err != nil {
- log.Println("[AutoCert] Recover", err)
- }
- }()
- log.Println("[AutoCert] Start")
- autoCertList := model.GetAutoCertList()
- for i := range autoCertList {
- domain := autoCertList[i].Domain
-
- certModel, err := model.FirstCert(domain)
-
- if err != nil {
- log.Println("[AutoCert] Error get certificate from database", err)
- continue
- }
-
- if certModel.SSLCertificatePath == "" {
- log.Println("[AutoCert] Error ssl_certificate_path is empty, " +
- "try to reopen auto-cert for this domain:" + domain)
- continue
- }
-
- cert, err := GetCertInfo(certModel.SSLCertificatePath)
- if err != nil {
- log.Println("GetCertInfo Err", err)
- // Get certificate info error, ignore this domain
- continue
- }
- // before 1 mo
- if time.Now().Before(cert.NotBefore.AddDate(0, 1, 0)) {
- continue
- }
- // after 1 mo, reissue certificate
- logChan := make(chan string, 1)
- errChan := make(chan error, 1)
-
- // support SAN certification
- go IssueCert(strings.Split(domain, "_"), logChan, errChan)
-
- go handleIssueCertLogChan(logChan)
-
- // block, unless errChan closed
- for err = range errChan {
- log.Println("Error cert.IssueCert", err)
- }
-
- close(logChan)
- }
+func AutoObtain() {
+ defer func() {
+ if err := recover(); err != nil {
+ log.Println("[AutoCert] Recover", err)
+ }
+ }()
+ log.Println("[AutoCert] Start")
+ autoCertList := model.GetAutoCertList()
+ for _, certModel := range autoCertList {
+ confName := certModel.Filename
+
+ errLog := &AutoCertErrorLog{}
+ errLog.SetCertModel(certModel)
+
+ if len(certModel.Filename) == 0 {
+ errLog.Exit("", errors.New("filename is empty"))
+ continue
+ }
+
+ if len(certModel.Domains) == 0 {
+ errLog.Exit(confName, errors.New("domains list is empty, "+
+ "try to reopen auto-cert for this config:"+confName))
+ continue
+ }
+
+ if certModel.SSLCertificatePath != "" {
+ cert, err := GetCertInfo(certModel.SSLCertificatePath)
+ if err != nil {
+ errLog.Push("get cert info", err)
+ // Get certificate info error, ignore this domain
+ continue
+ }
+ // every week
+ if time.Now().Sub(cert.NotBefore).Hours()/24 < 7 {
+ continue
+ }
+ }
+ // after 1 mo, reissue certificate
+ logChan := make(chan string, 1)
+ errChan := make(chan error, 1)
+
+ // support SAN certification
+ go IssueCert(certModel.Domains, logChan, errChan)
+
+ go handleIssueCertLogChan(logChan)
+
+ // block, unless errChan closed
+ for err := range errChan {
+ errLog.Push("issue cert", err)
+ }
+
+ logStr := errLog.ToString()
+ if logStr != "" {
+ // store error log to db
+ _ = certModel.Updates(&model.Cert{
+ Log: errLog.ToString(),
+ })
+ } else {
+ certModel.ClearLog()
+ }
+
+ close(logChan)
+ }
+ log.Println("[AutoCert] End")
}
diff --git a/server/pkg/cert/cert.go b/server/pkg/cert/cert.go
index 7979c52df..6e7fecfa6 100644
--- a/server/pkg/cert/cert.go
+++ b/server/pkg/cert/cert.go
@@ -5,6 +5,7 @@ import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
+ "crypto/tls"
"github.com/0xJacky/Nginx-UI/server/pkg/nginx"
"github.com/0xJacky/Nginx-UI/server/settings"
"github.com/go-acme/lego/v4/certcrypto"
@@ -14,6 +15,7 @@ import (
"github.com/go-acme/lego/v4/registration"
"github.com/pkg/errors"
"log"
+ "net/http"
"os"
"path/filepath"
"strings"
@@ -65,6 +67,11 @@ func IssueCert(domain []string, logChan chan string, errChan chan error) {
if settings.ServerSettings.CADir != "" {
config.CADirURL = settings.ServerSettings.CADir
+ if config.HTTPClient != nil {
+ config.HTTPClient.Transport = &http.Transport{
+ TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+ }
+ }
}
config.Certificate.KeyType = certcrypto.RSA2048
@@ -85,7 +92,7 @@ func IssueCert(domain []string, logChan chan string, errChan chan error) {
)
if err != nil {
- errChan <- errors.Wrap(err, "issue cert challenge fail")
+ errChan <- errors.Wrap(err, "fail to challenge")
return
}
@@ -93,7 +100,7 @@ func IssueCert(domain []string, logChan chan string, errChan chan error) {
logChan <- "Registering user"
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
if err != nil {
- errChan <- errors.Wrap(err, "issue cert register fail")
+ errChan <- errors.Wrap(err, "fail to register")
return
}
myUser.Registration = reg
@@ -106,7 +113,7 @@ func IssueCert(domain []string, logChan chan string, errChan chan error) {
logChan <- "Obtaining certificate"
certificates, err := client.Certificate.Obtain(request)
if err != nil {
- errChan <- errors.Wrap(err, "issue cert fail to obtain")
+ errChan <- errors.Wrap(err, "fail to obtain")
return
}
name := strings.Join(domain, "_")
@@ -114,7 +121,7 @@ func IssueCert(domain []string, logChan chan string, errChan chan error) {
if _, err = os.Stat(saveDir); os.IsNotExist(err) {
err = os.MkdirAll(saveDir, 0755)
if err != nil {
- errChan <- errors.Wrap(err, "issue cert fail to create")
+ errChan <- errors.Wrap(err, "fail to mkdir")
return
}
}
@@ -135,7 +142,7 @@ func IssueCert(domain []string, logChan chan string, errChan chan error) {
certificates.PrivateKey, 0644)
if err != nil {
- errChan <- errors.Wrap(err, "error issue cert write key")
+ errChan <- errors.Wrap(err, "fail to write key")
return
}
@@ -145,4 +152,6 @@ func IssueCert(domain []string, logChan chan string, errChan chan error) {
nginx.Reload()
logChan <- "Finished"
+
+ close(logChan)
}
diff --git a/server/pkg/nginx/type.go b/server/pkg/nginx/type.go
index cf803e067..c436c3502 100644
--- a/server/pkg/nginx/type.go
+++ b/server/pkg/nginx/type.go
@@ -1,61 +1,64 @@
package nginx
import (
- "github.com/tufanbarisyildirim/gonginx"
- "strings"
+ "github.com/tufanbarisyildirim/gonginx"
+ "path"
+ "strings"
)
type NgxConfig struct {
- FileName string `json:"file_name"`
- Upstreams []*NgxUpstream `json:"upstreams"`
- Servers []*NgxServer `json:"servers"`
- Custom string `json:"custom"`
- c *gonginx.Config
+ FileName string `json:"file_name"`
+ Name string `json:"name"`
+ Upstreams []*NgxUpstream `json:"upstreams"`
+ Servers []*NgxServer `json:"servers"`
+ Custom string `json:"custom"`
+ c *gonginx.Config
}
type NgxServer struct {
- Directives []*NgxDirective `json:"directives"`
- Locations []*NgxLocation `json:"locations"`
- Comments string `json:"comments"`
+ Directives []*NgxDirective `json:"directives"`
+ Locations []*NgxLocation `json:"locations"`
+ Comments string `json:"comments"`
}
type NgxUpstream struct {
- Name string `json:"name"`
- Directives []*NgxDirective `json:"directives"`
- Comments string `json:"comments"`
+ Name string `json:"name"`
+ Directives []*NgxDirective `json:"directives"`
+ Comments string `json:"comments"`
}
type NgxDirective struct {
- Directive string `json:"directive"`
- Params string `json:"params"`
- Comments string `json:"comments"`
+ Directive string `json:"directive"`
+ Params string `json:"params"`
+ Comments string `json:"comments"`
}
type NgxLocation struct {
- Path string `json:"path"`
- Content string `json:"content"`
- Comments string `json:"comments"`
+ Path string `json:"path"`
+ Content string `json:"content"`
+ Comments string `json:"comments"`
}
func (d *NgxDirective) Orig() string {
- return d.Directive + " " + d.Params
+ return d.Directive + " " + d.Params
}
func (d *NgxDirective) TrimParams() {
- d.Params = strings.TrimRight(strings.TrimSpace(d.Params), ";")
- return
+ d.Params = strings.TrimRight(strings.TrimSpace(d.Params), ";")
+ return
}
func NewNgxServer() *NgxServer {
- return &NgxServer{
- Locations: make([]*NgxLocation, 0),
- Directives: make([]*NgxDirective, 0),
- }
+ return &NgxServer{
+ Locations: make([]*NgxLocation, 0),
+ Directives: make([]*NgxDirective, 0),
+ }
}
func NewNgxConfig(filename string) *NgxConfig {
- return &NgxConfig{
- FileName: filename,
- Upstreams: make([]*NgxUpstream, 0),
- }
+ return &NgxConfig{
+ FileName: filename,
+ Upstreams: make([]*NgxUpstream, 0),
+ Name: path.Base(filename),
+ }
}
diff --git a/server/router/routers.go b/server/router/routers.go
index 834b2afc2..51a988457 100644
--- a/server/router/routers.go
+++ b/server/router/routers.go
@@ -76,6 +76,7 @@ func InitRouter() *gin.Engine {
g.DELETE("domain/:name", api.DeleteDomain)
// duplicate site
g.POST("domain/:name/duplicate", api.DuplicateSite)
+ g.GET("domain/:name/cert", api.IssueCert)
g.GET("configs", api.GetConfigs)
g.GET("config/*name", api.GetConfig)
@@ -90,17 +91,15 @@ func InitRouter() *gin.Engine {
g.GET("template/blocks", api.GetTemplateBlockList)
g.GET("template/block/:name", api.GetTemplateBlock)
- g.GET("cert/issue", api.IssueCert)
-
g.GET("certs", api.GetCertList)
g.GET("cert/:id", api.GetCert)
g.POST("cert", api.AddCert)
g.POST("cert/:id", api.ModifyCert)
g.DELETE("cert/:id", api.RemoveCert)
// Add domain to auto-renew cert list
- g.POST("auto_cert/:domain", api.AddDomainToAutoCert)
+ g.POST("auto_cert/:name", api.AddDomainToAutoCert)
// Delete domain from auto-renew cert list
- g.DELETE("auto_cert/:domain", api.RemoveDomainFromAutoCert)
+ g.DELETE("auto_cert/:name", api.RemoveDomainFromAutoCert)
// pty
g.GET("pty", api.Pty)