mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-11 18:35:51 +02:00
add demo
This commit is contained in:
parent
1b4abab47f
commit
460480c64a
28 changed files with 527 additions and 357 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -7,3 +7,4 @@ app.ini
|
|||
dist
|
||||
*.exe
|
||||
*.po~
|
||||
nginx-ui-server
|
||||
|
|
14
Dockerfile
Normal file
14
Dockerfile
Normal file
|
@ -0,0 +1,14 @@
|
|||
# CGO_ENABLED=1 GOOS=linux CC=x86_64-unknown-linux-gnu-gcc CXX=x86_64-unknown-linux-gnu-g++ GOARCH=amd64 go build -o nginx-ui-server -v main.go
|
||||
FROM --platform=linux/amd64 debian:buster
|
||||
WORKDIR /app
|
||||
COPY ./resources/demo/sources.list /etc/apt/sources.list
|
||||
RUN cd /app && apt-get update -y && apt install nginx curl -y
|
||||
EXPOSE 80
|
||||
COPY ./resources/demo/nginx.conf /etc/nginx/sites-available/default
|
||||
COPY ./resources/demo/app.ini /app/app.ini
|
||||
COPY ./resources/demo/demo.db /app/database.db
|
||||
COPY ./resources/demo/install.sh /app/install.sh
|
||||
COPY ./resources/demo/start.sh /app/start.sh
|
||||
COPY ./nginx-ui-server /app/nginx-ui
|
||||
RUN cd /app && chmod a+x start.sh
|
||||
CMD ["./start.sh"]
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "nginx-ui-frontend",
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
|
|
|
@ -4,6 +4,7 @@ import auth from './auth'
|
|||
import user from './user'
|
||||
import install from './install'
|
||||
import analytic from './analytic'
|
||||
import settings from './settings'
|
||||
|
||||
export default {
|
||||
domain,
|
||||
|
@ -11,5 +12,6 @@ export default {
|
|||
auth,
|
||||
user,
|
||||
install,
|
||||
analytic
|
||||
analytic,
|
||||
settings
|
||||
}
|
||||
|
|
9
frontend/src/api/settings.js
Normal file
9
frontend/src/api/settings.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
import http from '@/lib/http'
|
||||
|
||||
const settings = {
|
||||
get() {
|
||||
return http.get('/settings')
|
||||
}
|
||||
}
|
||||
|
||||
export default settings
|
|
@ -2,27 +2,22 @@ export const settings = {
|
|||
namespace: true,
|
||||
state: {
|
||||
language: '',
|
||||
translations: {},
|
||||
env: {}
|
||||
},
|
||||
mutations: {
|
||||
set_language(state, payload) {
|
||||
state.language = payload
|
||||
},
|
||||
update_translations(state, payload) {
|
||||
state.translations = payload
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
set_language({commit}, data) {
|
||||
commit('set_language', data)
|
||||
},
|
||||
update_translations({commit}, data) {
|
||||
commit('update_translations', data)
|
||||
update_env(state, payload) {
|
||||
state.env = {...payload}
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
current_language(state) {
|
||||
return state.language
|
||||
},
|
||||
env(state) {
|
||||
return state.env
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ msgstr ""
|
|||
msgid "Build with"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/config/ConfigEdit.vue:5 src/views/domain/DomainEdit.vue:24
|
||||
#: src/views/config/ConfigEdit.vue:5 src/views/domain/DomainEdit.vue:23
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
|
@ -52,11 +52,11 @@ msgstr ""
|
|||
msgid "Certificate Auto-renewal"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/domain/CertInfo.vue:11 src/views/domain/CertInfo.vue:2
|
||||
#: src/views/domain/CertInfo.vue:12 src/views/domain/CertInfo.vue:2
|
||||
msgid "Certificate has expired"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/domain/CertInfo.vue:15 src/views/domain/CertInfo.vue:2
|
||||
#: src/views/domain/CertInfo.vue:16 src/views/domain/CertInfo.vue:2
|
||||
msgid "Certificate is valid"
|
||||
msgstr ""
|
||||
|
||||
|
@ -125,7 +125,7 @@ msgstr ""
|
|||
msgid "Do you want to change the template to support the TLS?"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/domain/DomainEdit.vue:38
|
||||
#: src/views/domain/DomainEdit.vue:42
|
||||
msgid "Edit %{n}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -133,7 +133,7 @@ msgstr ""
|
|||
msgid "Edit Configuration"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/domain/DomainEdit.vue:87
|
||||
#: src/views/domain/DomainEdit.vue:95
|
||||
msgid "Edit Configuration File"
|
||||
msgstr ""
|
||||
|
||||
|
@ -182,7 +182,7 @@ msgstr ""
|
|||
msgid "File Not Found"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/domain/DomainEdit.vue:10 src/views/domain/DomainEdit.vue:4
|
||||
#: src/views/domain/DomainEdit.vue:9 src/views/domain/DomainEdit.vue:3
|
||||
msgid "Getting Certificate from Let's Encrypt"
|
||||
msgstr ""
|
||||
|
||||
|
@ -243,7 +243,7 @@ msgstr ""
|
|||
msgid "Logout successful"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/domain/DomainEdit.vue:13 src/views/domain/DomainEdit.vue:7
|
||||
#: src/views/domain/DomainEdit.vue:12 src/views/domain/DomainEdit.vue:6
|
||||
msgid ""
|
||||
"Make sure you have configured a reverse proxy for .well-known directory to "
|
||||
"HTTPChallengePort (default: 9180) before getting the certificate."
|
||||
|
@ -344,7 +344,7 @@ msgid "Root Directory (root)"
|
|||
msgstr ""
|
||||
|
||||
#: src/views/config/ConfigEdit.vue:6 src/views/domain/DomainAdd.vue:6
|
||||
#: src/views/domain/DomainEdit.vue:25
|
||||
#: src/views/domain/DomainEdit.vue:24
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
|
||||
|
@ -418,6 +418,10 @@ msgid ""
|
|||
"changed after it has been created."
|
||||
msgstr ""
|
||||
|
||||
#: src/views/domain/DomainEdit.vue:11 src/views/domain/DomainEdit.vue:5
|
||||
msgid "This feature is not available in demo."
|
||||
msgstr ""
|
||||
|
||||
#: src/views/domain/DomainEdit.vue:134
|
||||
msgid "This operation will lose the custom configuration."
|
||||
msgstr ""
|
||||
|
|
Binary file not shown.
|
@ -46,7 +46,7 @@ msgstr "成功启用 %{name} 自动续签"
|
|||
msgid "Build with"
|
||||
msgstr "构建基于"
|
||||
|
||||
#: src/views/config/ConfigEdit.vue:5 src/views/domain/DomainEdit.vue:24
|
||||
#: src/views/config/ConfigEdit.vue:5 src/views/domain/DomainEdit.vue:23
|
||||
msgid "Cancel"
|
||||
msgstr "取消"
|
||||
|
||||
|
@ -54,11 +54,11 @@ msgstr "取消"
|
|||
msgid "Certificate Auto-renewal"
|
||||
msgstr "证书自动续签"
|
||||
|
||||
#: src/views/domain/CertInfo.vue:11 src/views/domain/CertInfo.vue:2
|
||||
#: src/views/domain/CertInfo.vue:12 src/views/domain/CertInfo.vue:2
|
||||
msgid "Certificate has expired"
|
||||
msgstr "此证书已过期"
|
||||
|
||||
#: src/views/domain/CertInfo.vue:15 src/views/domain/CertInfo.vue:2
|
||||
#: src/views/domain/CertInfo.vue:16 src/views/domain/CertInfo.vue:2
|
||||
msgid "Certificate is valid"
|
||||
msgstr "此证书有效"
|
||||
|
||||
|
@ -127,7 +127,7 @@ msgstr "磁盘 IO"
|
|||
msgid "Do you want to change the template to support the TLS?"
|
||||
msgstr "你想要改变模板以支持 TLS 吗?"
|
||||
|
||||
#: src/views/domain/DomainEdit.vue:38
|
||||
#: src/views/domain/DomainEdit.vue:42
|
||||
msgid "Edit %{n}"
|
||||
msgstr "编辑 %{n}"
|
||||
|
||||
|
@ -135,7 +135,7 @@ msgstr "编辑 %{n}"
|
|||
msgid "Edit Configuration"
|
||||
msgstr "编辑配置"
|
||||
|
||||
#: src/views/domain/DomainEdit.vue:87
|
||||
#: src/views/domain/DomainEdit.vue:95
|
||||
msgid "Edit Configuration File"
|
||||
msgstr "编辑配置文件"
|
||||
|
||||
|
@ -184,7 +184,7 @@ msgstr "启用失败 %{msg}"
|
|||
msgid "File Not Found"
|
||||
msgstr "未找到文件"
|
||||
|
||||
#: src/views/domain/DomainEdit.vue:10 src/views/domain/DomainEdit.vue:4
|
||||
#: src/views/domain/DomainEdit.vue:9 src/views/domain/DomainEdit.vue:3
|
||||
msgid "Getting Certificate from Let's Encrypt"
|
||||
msgstr "从 Let's Encrypt 获取证书"
|
||||
|
||||
|
@ -245,7 +245,7 @@ msgstr "登录成功"
|
|||
msgid "Logout successful"
|
||||
msgstr "登出成功"
|
||||
|
||||
#: src/views/domain/DomainEdit.vue:13 src/views/domain/DomainEdit.vue:7
|
||||
#: src/views/domain/DomainEdit.vue:12 src/views/domain/DomainEdit.vue:6
|
||||
msgid ""
|
||||
"Make sure you have configured a reverse proxy for .well-known directory to "
|
||||
"HTTPChallengePort (default: 9180) before getting the certificate."
|
||||
|
@ -275,7 +275,7 @@ msgstr "名称"
|
|||
|
||||
#: src/views/dashboard/DashBoard.vue:231
|
||||
msgid "Network"
|
||||
msgstr ""
|
||||
msgstr "网络"
|
||||
|
||||
#: src/views/dashboard/DashBoard.vue:165
|
||||
msgid "Network Total Receive"
|
||||
|
@ -348,7 +348,7 @@ msgid "Root Directory (root)"
|
|||
msgstr "网站根目录 (root)"
|
||||
|
||||
#: src/views/config/ConfigEdit.vue:6 src/views/domain/DomainAdd.vue:6
|
||||
#: src/views/domain/DomainEdit.vue:25
|
||||
#: src/views/domain/DomainEdit.vue:24
|
||||
msgid "Save"
|
||||
msgstr "保存"
|
||||
|
||||
|
@ -426,6 +426,10 @@ msgstr ""
|
|||
"只有在您的配置文件中有相应字段时,下列的配置才能生效。配置文件名称创建后不"
|
||||
"可修改。"
|
||||
|
||||
#: src/views/domain/DomainEdit.vue:11 src/views/domain/DomainEdit.vue:5
|
||||
msgid "This feature is not available in demo."
|
||||
msgstr "该功能在 Demo 中不可用。"
|
||||
|
||||
#: src/views/domain/DomainEdit.vue:134
|
||||
msgid "This operation will lose the custom configuration."
|
||||
msgstr "该操作将会丢失自定义配置。"
|
||||
|
|
|
@ -47,7 +47,7 @@ msgstr "成功啟用 %{name} 自動續簽"
|
|||
msgid "Build with"
|
||||
msgstr "構建基於"
|
||||
|
||||
#: src/views/config/ConfigEdit.vue:5 src/views/domain/DomainEdit.vue:24
|
||||
#: src/views/config/ConfigEdit.vue:5 src/views/domain/DomainEdit.vue:23
|
||||
msgid "Cancel"
|
||||
msgstr "取消"
|
||||
|
||||
|
@ -55,11 +55,11 @@ msgstr "取消"
|
|||
msgid "Certificate Auto-renewal"
|
||||
msgstr "證書自動續簽"
|
||||
|
||||
#: src/views/domain/CertInfo.vue:11 src/views/domain/CertInfo.vue:2
|
||||
#: src/views/domain/CertInfo.vue:12 src/views/domain/CertInfo.vue:2
|
||||
msgid "Certificate has expired"
|
||||
msgstr "此證書已過期"
|
||||
|
||||
#: src/views/domain/CertInfo.vue:15 src/views/domain/CertInfo.vue:2
|
||||
#: src/views/domain/CertInfo.vue:16 src/views/domain/CertInfo.vue:2
|
||||
msgid "Certificate is valid"
|
||||
msgstr "此證書有效"
|
||||
|
||||
|
@ -129,7 +129,7 @@ msgstr ""
|
|||
msgid "Do you want to change the template to support the TLS?"
|
||||
msgstr "你想要改變模板以支援 TLS 嗎?"
|
||||
|
||||
#: src/views/domain/DomainEdit.vue:38
|
||||
#: src/views/domain/DomainEdit.vue:42
|
||||
msgid "Edit %{n}"
|
||||
msgstr "編輯 %{n}"
|
||||
|
||||
|
@ -137,7 +137,7 @@ msgstr "編輯 %{n}"
|
|||
msgid "Edit Configuration"
|
||||
msgstr "編輯配置"
|
||||
|
||||
#: src/views/domain/DomainEdit.vue:87
|
||||
#: src/views/domain/DomainEdit.vue:95
|
||||
msgid "Edit Configuration File"
|
||||
msgstr "編輯配置檔案"
|
||||
|
||||
|
@ -186,7 +186,7 @@ msgstr "啟用失敗 %{msg}"
|
|||
msgid "File Not Found"
|
||||
msgstr "未找到檔案"
|
||||
|
||||
#: src/views/domain/DomainEdit.vue:10 src/views/domain/DomainEdit.vue:4
|
||||
#: src/views/domain/DomainEdit.vue:9 src/views/domain/DomainEdit.vue:3
|
||||
msgid "Getting Certificate from Let's Encrypt"
|
||||
msgstr "從 Let's Encrypt 獲取證書"
|
||||
|
||||
|
@ -247,7 +247,7 @@ msgstr "登入成功"
|
|||
msgid "Logout successful"
|
||||
msgstr "登出成功"
|
||||
|
||||
#: src/views/domain/DomainEdit.vue:13 src/views/domain/DomainEdit.vue:7
|
||||
#: src/views/domain/DomainEdit.vue:12 src/views/domain/DomainEdit.vue:6
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"Make sure you have configured a reverse proxy for .well-known directory to "
|
||||
|
@ -351,7 +351,7 @@ msgid "Root Directory (root)"
|
|||
msgstr "網站根目錄 (root)"
|
||||
|
||||
#: src/views/config/ConfigEdit.vue:6 src/views/domain/DomainAdd.vue:6
|
||||
#: src/views/domain/DomainEdit.vue:25
|
||||
#: src/views/domain/DomainEdit.vue:24
|
||||
msgid "Save"
|
||||
msgstr "儲存"
|
||||
|
||||
|
@ -430,6 +430,10 @@ msgstr ""
|
|||
"只有在您的配置檔案中有相應欄位時,下列的配置才能生效。配置檔名稱建立後不可修"
|
||||
"改。"
|
||||
|
||||
#: src/views/domain/DomainEdit.vue:11 src/views/domain/DomainEdit.vue:5
|
||||
msgid "This feature is not available in demo."
|
||||
msgstr ""
|
||||
|
||||
#: src/views/domain/DomainEdit.vue:134
|
||||
msgid "This operation will lose the custom configuration."
|
||||
msgstr "該操作將會丟失自定義配置。"
|
||||
|
|
|
@ -20,6 +20,10 @@ Vue.config.productionTip = false
|
|||
Vue.prototype.$routeConfig = routes
|
||||
Vue.prototype.$api = api
|
||||
|
||||
api.settings.get().then(r => {
|
||||
store.commit('update_env', r)
|
||||
})
|
||||
|
||||
Vue.use(GetTextPlugin, {
|
||||
availableLanguages,
|
||||
defaultLanguage: store.getters.current_language,
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -6,10 +6,16 @@
|
|||
<std-data-entry :data-list="columns" v-model="config"/>
|
||||
<template v-if="config.support_ssl">
|
||||
<cert-info :domain="name" ref="cert-info" v-if="name"/>
|
||||
<a-button @click="issue_cert" type="primary" ghost style="margin: 10px 0">
|
||||
<a-button
|
||||
@click="issue_cert"
|
||||
type="primary" ghost
|
||||
style="margin: 10px 0"
|
||||
:disabled="is_demo"
|
||||
>
|
||||
<translate>Getting Certificate from Let's Encrypt</translate>
|
||||
</a-button>
|
||||
<p v-translate>Make sure you have configured a reverse proxy for .well-known directory to HTTPChallengePort (default: 9180) before getting the certificate.</p>
|
||||
<p v-if="is_demo" v-translate>This feature is not available in demo.</p>
|
||||
<p v-else v-translate>Make sure you have configured a reverse proxy for .well-known directory to HTTPChallengePort (default: 9180) before getting the certificate.</p>
|
||||
</template>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
|
@ -238,6 +244,9 @@ export default {
|
|||
return [...columns]
|
||||
}
|
||||
}
|
||||
},
|
||||
is_demo() {
|
||||
return this.$store.getters.env.demo===true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"version":"1.1.0","build_id":23,"total_build":40}
|
||||
{"version":"1.2.0","build_id":2,"total_build":42}
|
7
go.mod
7
go.mod
|
@ -3,7 +3,6 @@ module github.com/0xJacky/Nginx-UI
|
|||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/0xJacky/pofile v0.0.1
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/gin-contrib/static v0.0.1
|
||||
|
@ -16,6 +15,7 @@ require (
|
|||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/shirou/gopsutil/v3 v3.21.7
|
||||
github.com/spf13/cast v1.3.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/unknwon/com v1.0.1
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
|
||||
gopkg.in/ini.v1 v1.62.0
|
||||
|
@ -26,10 +26,10 @@ require (
|
|||
require (
|
||||
github.com/StackExchange/wmi v1.2.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.1.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-ole/go-ole v1.2.5 // indirect
|
||||
github.com/golang/protobuf v1.3.4 // indirect
|
||||
github.com/itchyny/timefmt-go v0.1.3 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.2 // indirect
|
||||
github.com/json-iterator/go v1.1.9 // indirect
|
||||
|
@ -39,7 +39,7 @@ require (
|
|||
github.com/miekg/dns v1.1.40 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.7 // indirect
|
||||
github.com/tklauser/numcpus v0.2.3 // indirect
|
||||
github.com/ugorji/go/codec v1.1.7 // indirect
|
||||
|
@ -48,4 +48,5 @@ require (
|
|||
golang.org/x/text v0.3.4 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.5.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
)
|
||||
|
|
7
go.sum
7
go.sum
|
@ -23,10 +23,6 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
|
|||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/0xJacky/pofile v0.0.0-20220219101524-60ce48e4de23 h1:nqaxj4ZYzLZzhFQeX1ZrXEBz0haUu7PypGFhQbQfDX0=
|
||||
github.com/0xJacky/pofile v0.0.0-20220219101524-60ce48e4de23/go.mod h1:gSDWobvodMtvwh7FE/F999AQoCwBoXgzyGffYFX9nKA=
|
||||
github.com/0xJacky/pofile v0.0.1 h1:hVRaw6ZHkajSMAuP58WMDTvGF8+OF297jpAchFK/4rQ=
|
||||
github.com/0xJacky/pofile v0.0.1/go.mod h1:gSDWobvodMtvwh7FE/F999AQoCwBoXgzyGffYFX9nKA=
|
||||
github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/go-autorest/autorest v0.1.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||
|
@ -229,8 +225,6 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
|
|||
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/infobloxopen/infoblox-go-client v1.1.1/go.mod h1:BXiw7S2b9qJoM8MS40vfgCNB2NLHGusk1DtO16BD9zI=
|
||||
github.com/itchyny/timefmt-go v0.1.3 h1:7M3LGVDsqcd0VZH2U+x393obrzZisp7C0uEe921iRkU=
|
||||
github.com/itchyny/timefmt-go v0.1.3/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A=
|
||||
github.com/jarcoal/httpmock v1.0.6/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
|
@ -341,7 +335,6 @@ github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrap
|
|||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw=
|
||||
|
|
BIN
resources/demo/demo.db
Normal file
BIN
resources/demo/demo.db
Normal file
Binary file not shown.
78
resources/demo/install.sh
Normal file
78
resources/demo/install.sh
Normal file
|
@ -0,0 +1,78 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
PROXY=""
|
||||
RPROXY="https://ghproxy.com/"
|
||||
|
||||
MACHINE="amd64"
|
||||
|
||||
# Font color
|
||||
FontBlack="\033[30m";
|
||||
FontRed="\033[31m";
|
||||
FontGreen="\033[32m";
|
||||
FontYellow="\033[33m";
|
||||
FontBlue="\033[34m";
|
||||
FontPurple="\033[35m";
|
||||
FontSkyBlue="\033[36m";
|
||||
FontWhite="\033[37m";
|
||||
FontSuffix="\033[0m";
|
||||
|
||||
get_latest_version() {
|
||||
# Get latest release version number
|
||||
local latest_release
|
||||
if ! latest_release=$(curl -x "${PROXY}" -sS -H "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/0xJacky/nginx-ui/releases/latest"); then
|
||||
echo -e "${FontRed}error: Failed to get release list, please check your network.${FontSuffix}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
RELEASE_LATEST="$(echo "$latest_release" | sed 'y/,/\n/' | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')"
|
||||
if [[ -z "$RELEASE_LATEST" ]]; then
|
||||
if echo "$latest_release" | grep -q "API rate limit exceeded"; then
|
||||
echo -e "${FontRed}error: github API rate limit exceeded${FontSuffix}"
|
||||
else
|
||||
echo -e "${FontRed}error: Failed to get the latest release version.${FontSuffix}"
|
||||
echo "Welcome bug report: https://github.com/0xJacky/nginx-ui/issues"
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
RELEASE_LATEST="v${RELEASE_LATEST#v}"
|
||||
}
|
||||
|
||||
download_nginx_ui() {
|
||||
local download_link
|
||||
download_link="${RPROXY}https://github.com/0xJacky/nginx-ui/releases/download/$RELEASE_LATEST/nginx-ui-linux-$MACHINE.tar.gz"
|
||||
|
||||
echo "Downloading Nginx UI archive: $download_link"
|
||||
if ! curl -x "${PROXY}" -R -H 'Cache-Control: no-cache' -L -o "$TAR_FILE" "$download_link"; then
|
||||
echo 'error: Download failed! Please check your network or try again.'
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
decompression() {
|
||||
echo "$1"
|
||||
if ! tar -zxf "$1" -C "$TMP_DIRECTORY"; then
|
||||
echo -e "${FontRed}error: Nginx UI decompression failed.${FontSuffix}"
|
||||
"rm" -r "$TMP_DIRECTORY"
|
||||
echo "removed: $TMP_DIRECTORY"
|
||||
exit 1
|
||||
fi
|
||||
echo "info: Extract the Nginx UI package to $TMP_DIRECTORY and prepare it for installation."
|
||||
}
|
||||
|
||||
install_bin() {
|
||||
NAME="nginx-ui"
|
||||
install -m 755 "${TMP_DIRECTORY}/$NAME" "/app/$NAME"
|
||||
}
|
||||
|
||||
main() {
|
||||
# Important Variables
|
||||
TMP_DIRECTORY="$(mktemp -d)"
|
||||
TAR_FILE="${TMP_DIRECTORY}/nginx-ui-linux-$MACHINE.tar.gz"
|
||||
get_latest_version
|
||||
download_nginx_ui
|
||||
decompression "$TAR_FILE"
|
||||
install_bin
|
||||
}
|
||||
|
||||
main "$@"
|
16
resources/demo/nginx.conf
Normal file
16
resources/demo/nginx.conf
Normal file
|
@ -0,0 +1,16 @@
|
|||
server {
|
||||
listen 80;
|
||||
server_name localhost; # your domain here
|
||||
client_max_body_size 128M; # maximum upload size
|
||||
|
||||
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 upgrade;
|
||||
proxy_pass http://127.0.0.1:9000/;
|
||||
}
|
||||
}
|
4
resources/demo/sources.list
Normal file
4
resources/demo/sources.list
Normal file
|
@ -0,0 +1,4 @@
|
|||
deb http://mirrors.aliyun.com/debian/ buster main non-free contrib
|
||||
deb http://mirrors.aliyun.com/debian-security buster/updates main
|
||||
deb http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib
|
||||
deb http://mirrors.aliyun.com/debian/ buster-backports main non-free contrib
|
3
resources/demo/start.sh
Normal file
3
resources/demo/start.sh
Normal file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
nginx
|
||||
/app/nginx-ui --config app.ini
|
|
@ -1,150 +1,163 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/0xJacky/Nginx-UI/server/tool"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"encoding/json"
|
||||
"github.com/0xJacky/Nginx-UI/server/settings"
|
||||
"github.com/0xJacky/Nginx-UI/server/tool"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
func CertInfo(c *gin.Context) {
|
||||
domain := c.Param("domain")
|
||||
domain := c.Param("domain")
|
||||
|
||||
key := tool.GetCertInfo(domain)
|
||||
key := tool.GetCertInfo(domain)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"subject_name": key.Subject.CommonName,
|
||||
"issuer_name": key.Issuer.CommonName,
|
||||
"not_after": key.NotAfter,
|
||||
"not_before": key.NotBefore,
|
||||
})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"subject_name": key.Subject.CommonName,
|
||||
"issuer_name": key.Issuer.CommonName,
|
||||
"not_after": key.NotAfter,
|
||||
"not_before": key.NotBefore,
|
||||
})
|
||||
}
|
||||
|
||||
func IssueCert(c *gin.Context) {
|
||||
domain := c.Param("domain")
|
||||
var upGrader = websocket.Upgrader{
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
domain := c.Param("domain")
|
||||
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
|
||||
}
|
||||
// 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(err)
|
||||
return
|
||||
}
|
||||
}(ws)
|
||||
defer func(ws *websocket.Conn) {
|
||||
err := ws.Close()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
}(ws)
|
||||
|
||||
for {
|
||||
// read
|
||||
mt, message, err := ws.ReadMessage()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if string(message) == "go" {
|
||||
var m []byte
|
||||
for {
|
||||
// read
|
||||
mt, message, err := ws.ReadMessage()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if string(message) == "go" {
|
||||
var m []byte
|
||||
|
||||
err = tool.IssueCert(domain)
|
||||
if err != nil {
|
||||
m, err = json.Marshal(gin.H{
|
||||
"status": "error",
|
||||
"message": err.Error(),
|
||||
})
|
||||
if settings.ServerSettings.Demo {
|
||||
m, _ = json.Marshal(gin.H{
|
||||
"status": "error",
|
||||
"message": "this feature is not available in demo",
|
||||
})
|
||||
_ = ws.WriteMessage(mt, m)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
err = tool.IssueCert(domain)
|
||||
|
||||
err = ws.WriteMessage(mt, m)
|
||||
if err != nil {
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
log.Println(err)
|
||||
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
m, err = json.Marshal(gin.H{
|
||||
"status": "error",
|
||||
"message": err.Error(),
|
||||
})
|
||||
|
||||
sslCertificatePath := tool.GetNginxConfPath("ssl/" + domain + "/fullchain.cer")
|
||||
_, err = os.Stat(sslCertificatePath)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
err = ws.WriteMessage(mt, m)
|
||||
|
||||
log.Println("[found]", "fullchain.cer")
|
||||
m, err = json.Marshal(gin.H{
|
||||
"status": "success",
|
||||
"message": "[found] fullchain.cer",
|
||||
})
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
err = ws.WriteMessage(mt, m)
|
||||
sslCertificatePath := tool.GetNginxConfPath("ssl/" + domain + "/fullchain.cer")
|
||||
_, err = os.Stat(sslCertificatePath)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
sslCertificateKeyPath := tool.GetNginxConfPath("ssl/" + domain + "/" + domain + ".key")
|
||||
_, err = os.Stat(sslCertificateKeyPath)
|
||||
log.Println("[found]", "fullchain.cer")
|
||||
m, err = json.Marshal(gin.H{
|
||||
"status": "success",
|
||||
"message": "[found] fullchain.cer",
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println("[found]", "cert key")
|
||||
m, err = json.Marshal(gin.H{
|
||||
"status": "success",
|
||||
"message": "[found] cert key",
|
||||
})
|
||||
err = ws.WriteMessage(mt, m)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
err = ws.WriteMessage(mt, m)
|
||||
sslCertificateKeyPath := tool.GetNginxConfPath("ssl/" + domain + "/" + domain + ".key")
|
||||
_, err = os.Stat(sslCertificateKeyPath)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println("申请成功")
|
||||
m, err = json.Marshal(gin.H{
|
||||
"status": "success",
|
||||
"message": "申请成功",
|
||||
"ssl_certificate": sslCertificatePath,
|
||||
"ssl_certificate_key": sslCertificateKeyPath,
|
||||
})
|
||||
log.Println("[found]", "cert key")
|
||||
m, err = json.Marshal(gin.H{
|
||||
"status": "success",
|
||||
"message": "[found] cert key",
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
err = ws.WriteMessage(mt, m)
|
||||
err = ws.WriteMessage(mt, m)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
log.Println("申请成功")
|
||||
m, err = json.Marshal(gin.H{
|
||||
"status": "success",
|
||||
"message": "申请成功",
|
||||
"ssl_certificate": sslCertificatePath,
|
||||
"ssl_certificate_key": sslCertificateKeyPath,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
err = ws.WriteMessage(mt, m)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,18 +2,16 @@ package api
|
|||
|
||||
import (
|
||||
"github.com/0xJacky/Nginx-UI/server/settings"
|
||||
"github.com/0xJacky/Nginx-UI/server/template"
|
||||
"github.com/gin-gonic/gin"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func GetTemplate(c *gin.Context) {
|
||||
name := c.Param("name")
|
||||
path := filepath.Join("template", name)
|
||||
content, err := ioutil.ReadFile(path)
|
||||
content, err := template.DistFS.ReadFile(name)
|
||||
|
||||
_content := string(content)
|
||||
_content = strings.ReplaceAll(_content, "{{ HTTP01PORT }}",
|
||||
|
|
|
@ -1,77 +1,85 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"github.com/0xJacky/Nginx-UI/server/api"
|
||||
"github.com/gin-contrib/static"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"strings"
|
||||
"bufio"
|
||||
"github.com/0xJacky/Nginx-UI/server/api"
|
||||
"github.com/0xJacky/Nginx-UI/server/settings"
|
||||
"github.com/gin-contrib/static"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func InitRouter() *gin.Engine {
|
||||
r := gin.New()
|
||||
r.Use(gin.Logger())
|
||||
r := gin.New()
|
||||
r.Use(gin.Logger())
|
||||
|
||||
r.Use(gin.Recovery())
|
||||
r.Use(gin.Recovery())
|
||||
|
||||
r.Use(static.Serve("/", mustFS("")))
|
||||
r.Use(static.Serve("/", mustFS("")))
|
||||
|
||||
r.NoRoute(func(c *gin.Context) {
|
||||
accept := c.Request.Header.Get("Accept")
|
||||
if strings.Contains(accept, "text/html") {
|
||||
file, _ := mustFS("").Open("index.html")
|
||||
stat, _ := file.Stat()
|
||||
c.DataFromReader(http.StatusOK, stat.Size(), "text/html",
|
||||
bufio.NewReader(file), nil)
|
||||
}
|
||||
})
|
||||
r.NoRoute(func(c *gin.Context) {
|
||||
accept := c.Request.Header.Get("Accept")
|
||||
if strings.Contains(accept, "text/html") {
|
||||
file, _ := mustFS("").Open("index.html")
|
||||
stat, _ := file.Stat()
|
||||
c.DataFromReader(http.StatusOK, stat.Size(), "text/html",
|
||||
bufio.NewReader(file), nil)
|
||||
}
|
||||
})
|
||||
|
||||
g := r.Group("/api")
|
||||
{
|
||||
g.GET("install", api.InstallLockCheck)
|
||||
g.POST("install", api.InstallNginxUI)
|
||||
g := r.Group("/api")
|
||||
{
|
||||
|
||||
g.POST("/login", api.Login)
|
||||
g.DELETE("/logout", api.Logout)
|
||||
g.GET("settings", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"demo": settings.ServerSettings.Demo,
|
||||
})
|
||||
})
|
||||
|
||||
g := g.Group("/", authRequired())
|
||||
{
|
||||
g.GET("/analytic", api.Analytic)
|
||||
g.GET("/analytic/init", api.GetAnalyticInit)
|
||||
g.GET("install", api.InstallLockCheck)
|
||||
g.POST("install", api.InstallNginxUI)
|
||||
|
||||
g.GET("/users", api.GetUsers)
|
||||
g.GET("/user/:id", api.GetUser)
|
||||
g.POST("/user", api.AddUser)
|
||||
g.POST("/user/:id", api.EditUser)
|
||||
g.DELETE("/user/:id", api.DeleteUser)
|
||||
g.POST("/login", api.Login)
|
||||
g.DELETE("/logout", api.Logout)
|
||||
|
||||
g.GET("domains", api.GetDomains)
|
||||
g.GET("domain/:name", api.GetDomain)
|
||||
g.POST("domain/:name", api.EditDomain)
|
||||
g.POST("domain/:name/enable", api.EnableDomain)
|
||||
g.POST("domain/:name/disable", api.DisableDomain)
|
||||
g.DELETE("domain/:name", api.DeleteDomain)
|
||||
g := g.Group("/", authRequired())
|
||||
{
|
||||
g.GET("/analytic", api.Analytic)
|
||||
g.GET("/analytic/init", api.GetAnalyticInit)
|
||||
|
||||
g.GET("configs", api.GetConfigs)
|
||||
g.GET("config/:name", api.GetConfig)
|
||||
g.POST("config", api.AddConfig)
|
||||
g.POST("config/:name", api.EditConfig)
|
||||
g.GET("/users", api.GetUsers)
|
||||
g.GET("/user/:id", api.GetUser)
|
||||
g.POST("/user", api.AddUser)
|
||||
g.POST("/user/:id", api.EditUser)
|
||||
g.DELETE("/user/:id", api.DeleteUser)
|
||||
|
||||
g.GET("backups", api.GetFileBackupList)
|
||||
g.GET("backup/:id", api.GetFileBackup)
|
||||
g.GET("domains", api.GetDomains)
|
||||
g.GET("domain/:name", api.GetDomain)
|
||||
g.POST("domain/:name", api.EditDomain)
|
||||
g.POST("domain/:name/enable", api.EnableDomain)
|
||||
g.POST("domain/:name/disable", api.DisableDomain)
|
||||
g.DELETE("domain/:name", api.DeleteDomain)
|
||||
|
||||
g.GET("template/:name", api.GetTemplate)
|
||||
g.GET("configs", api.GetConfigs)
|
||||
g.GET("config/:name", api.GetConfig)
|
||||
g.POST("config", api.AddConfig)
|
||||
g.POST("config/:name", api.EditConfig)
|
||||
|
||||
g.GET("cert/issue/:domain", api.IssueCert)
|
||||
g.GET("cert/:domain/info", api.CertInfo)
|
||||
g.GET("backups", api.GetFileBackupList)
|
||||
g.GET("backup/:id", api.GetFileBackup)
|
||||
|
||||
// 添加域名到自动续期列表
|
||||
g.POST("cert/:domain", api.AddDomainToAutoCert)
|
||||
// 从自动续期列表中删除域名
|
||||
g.DELETE("cert/:domain", api.RemoveDomainFromAutoCert)
|
||||
}
|
||||
}
|
||||
g.GET("template/:name", api.GetTemplate)
|
||||
|
||||
return r
|
||||
g.GET("cert/issue/:domain", api.IssueCert)
|
||||
g.GET("cert/:domain/info", api.CertInfo)
|
||||
|
||||
// 添加域名到自动续期列表
|
||||
g.POST("cert/:domain", api.AddDomainToAutoCert)
|
||||
// 从自动续期列表中删除域名
|
||||
g.DELETE("cert/:domain", api.RemoveDomainFromAutoCert)
|
||||
}
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ type Server struct {
|
|||
HTTPChallengePort string
|
||||
Email string
|
||||
Database string
|
||||
Demo bool
|
||||
}
|
||||
|
||||
var ServerSettings = &Server{
|
||||
|
@ -22,6 +23,7 @@ var ServerSettings = &Server{
|
|||
RunMode: "debug",
|
||||
HTTPChallengePort: "9180",
|
||||
Database: "database",
|
||||
Demo: false,
|
||||
}
|
||||
|
||||
var ConfPath string
|
||||
|
|
6
server/template/template.go
Normal file
6
server/template/template.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
package template
|
||||
|
||||
import "embed"
|
||||
|
||||
//go:embed http-conf https-conf
|
||||
var DistFS embed.FS
|
|
@ -48,8 +48,8 @@ func TestLego(t *testing.T) {
|
|||
|
||||
config := lego.NewConfig(&myUser)
|
||||
|
||||
// This CA URL is configured for a local dev instance of Boulder running in Docker in a VM.
|
||||
//config.CADirURL = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
// This CA URL is configured for a local dev instance of Boulder running in Dockerfile in a VM.
|
||||
config.CADirURL = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
config.Certificate.KeyType = certcrypto.RSA2048
|
||||
|
||||
// A client facilitates communication with the CA server.
|
||||
|
|
|
@ -1,169 +1,171 @@
|
|||
package tool
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"github.com/0xJacky/Nginx-UI/server/model"
|
||||
"github.com/0xJacky/Nginx-UI/server/settings"
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-acme/lego/v4/certificate"
|
||||
"github.com/go-acme/lego/v4/challenge/http01"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"github.com/0xJacky/Nginx-UI/server/model"
|
||||
"github.com/0xJacky/Nginx-UI/server/settings"
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-acme/lego/v4/certificate"
|
||||
"github.com/go-acme/lego/v4/challenge/http01"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MyUser You'll need a user or account type that implements acme.User
|
||||
type MyUser struct {
|
||||
Email string
|
||||
Registration *registration.Resource
|
||||
key crypto.PrivateKey
|
||||
Email string
|
||||
Registration *registration.Resource
|
||||
key crypto.PrivateKey
|
||||
}
|
||||
|
||||
func (u *MyUser) GetEmail() string {
|
||||
return u.Email
|
||||
return u.Email
|
||||
}
|
||||
func (u MyUser) GetRegistration() *registration.Resource {
|
||||
return u.Registration
|
||||
return u.Registration
|
||||
}
|
||||
func (u *MyUser) GetPrivateKey() crypto.PrivateKey {
|
||||
return u.key
|
||||
return u.key
|
||||
}
|
||||
|
||||
func AutoCert() {
|
||||
for {
|
||||
log.Println("[AutoCert] Start")
|
||||
autoCertList := model.GetAutoCertList()
|
||||
for i := range autoCertList {
|
||||
domain := autoCertList[i].Domain
|
||||
key := GetCertInfo(domain)
|
||||
// 未到一个月
|
||||
if time.Now().Before(key.NotBefore.AddDate(0, 1, 0)) {
|
||||
continue
|
||||
}
|
||||
// 过一个月了
|
||||
err := IssueCert(domain)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
time.Sleep(1 * time.Hour)
|
||||
}
|
||||
for {
|
||||
log.Println("[AutoCert] Start")
|
||||
autoCertList := model.GetAutoCertList()
|
||||
for i := range autoCertList {
|
||||
domain := autoCertList[i].Domain
|
||||
key := GetCertInfo(domain)
|
||||
// 未到一个月
|
||||
if time.Now().Before(key.NotBefore.AddDate(0, 1, 0)) {
|
||||
continue
|
||||
}
|
||||
// 过一个月了
|
||||
err := IssueCert(domain)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
time.Sleep(1 * time.Hour)
|
||||
}
|
||||
}
|
||||
|
||||
func GetCertInfo(domain string) (key *x509.Certificate) {
|
||||
ts := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
ts := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
|
||||
client := &http.Client{Transport: ts}
|
||||
client := &http.Client{Transport: ts}
|
||||
|
||||
response, err := client.Get("https://" + domain)
|
||||
response, err := client.Get("https://" + domain)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
defer func(Body io.ReadCloser) {
|
||||
err = Body.Close()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
}(response.Body)
|
||||
defer func(Body io.ReadCloser) {
|
||||
err = Body.Close()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
}(response.Body)
|
||||
|
||||
key = response.TLS.PeerCertificates[0]
|
||||
key = response.TLS.PeerCertificates[0]
|
||||
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
func IssueCert(domain string) error {
|
||||
// Create a user. New accounts need an email and private key to start.
|
||||
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
// Create a user. New accounts need an email and private key to start.
|
||||
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
myUser := MyUser{
|
||||
Email: settings.ServerSettings.Email,
|
||||
key: privateKey,
|
||||
}
|
||||
myUser := MyUser{
|
||||
Email: settings.ServerSettings.Email,
|
||||
key: privateKey,
|
||||
}
|
||||
|
||||
config := lego.NewConfig(&myUser)
|
||||
config := lego.NewConfig(&myUser)
|
||||
|
||||
//config.CADirURL = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
config.Certificate.KeyType = certcrypto.RSA2048
|
||||
if settings.ServerSettings.Demo {
|
||||
config.CADirURL = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
}
|
||||
config.Certificate.KeyType = certcrypto.RSA2048
|
||||
|
||||
// A client facilitates communication with the CA server.
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
// A client facilitates communication with the CA server.
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = client.Challenge.SetHTTP01Provider(
|
||||
http01.NewProviderServer("",
|
||||
settings.ServerSettings.HTTPChallengePort,
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
err = client.Challenge.SetHTTP01Provider(
|
||||
http01.NewProviderServer("",
|
||||
settings.ServerSettings.HTTPChallengePort,
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// New users will need to register
|
||||
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
myUser.Registration = reg
|
||||
// New users will need to register
|
||||
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
myUser.Registration = reg
|
||||
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: []string{domain},
|
||||
Bundle: true,
|
||||
}
|
||||
certificates, err := client.Certificate.Obtain(request)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
saveDir := GetNginxConfPath("ssl/" + domain)
|
||||
if _, err := os.Stat(saveDir); os.IsNotExist(err) {
|
||||
err = os.Mkdir(saveDir, 0755)
|
||||
if err != nil {
|
||||
log.Println("fail to create", saveDir)
|
||||
return err
|
||||
}
|
||||
}
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: []string{domain},
|
||||
Bundle: true,
|
||||
}
|
||||
certificates, err := client.Certificate.Obtain(request)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
saveDir := GetNginxConfPath("ssl/" + domain)
|
||||
if _, err := os.Stat(saveDir); os.IsNotExist(err) {
|
||||
err = os.Mkdir(saveDir, 0755)
|
||||
if err != nil {
|
||||
log.Println("fail to create", saveDir)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Each certificate comes back with the cert bytes, the bytes of the client's
|
||||
// private key, and a certificate URL. SAVE THESE TO DISK.
|
||||
err = ioutil.WriteFile(filepath.Join(saveDir, "fullchain.cer"),
|
||||
certificates.Certificate, 0644)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(saveDir, domain+".key"),
|
||||
certificates.PrivateKey, 0644)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
// Each certificate comes back with the cert bytes, the bytes of the client's
|
||||
// private key, and a certificate URL. SAVE THESE TO DISK.
|
||||
err = ioutil.WriteFile(filepath.Join(saveDir, "fullchain.cer"),
|
||||
certificates.Certificate, 0644)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(saveDir, domain+".key"),
|
||||
certificates.PrivateKey, 0644)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
ReloadNginx()
|
||||
ReloadNginx()
|
||||
|
||||
return nil
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue