mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-11 02:15:48 +02:00
parent
4a3e32a921
commit
a5b9bb88d6
18 changed files with 622 additions and 295 deletions
5
frontend/src/api/cert.ts
Normal file
5
frontend/src/api/cert.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import Curd from '@/api/curd'
|
||||
|
||||
const cert = new Curd('/cert')
|
||||
|
||||
export default cert
|
|
@ -13,13 +13,13 @@ class Domain extends Curd {
|
|||
get_template() {
|
||||
return http.get('template')
|
||||
}
|
||||
|
||||
|
||||
add_auto_cert(domain: string) {
|
||||
return http.post('cert/' + domain)
|
||||
return http.post('auto_cert/' + domain)
|
||||
}
|
||||
|
||||
remove_auto_cert(domain: string) {
|
||||
return http.delete('cert/' + domain)
|
||||
return http.delete('auto_cert/' + domain)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,8 @@ msgstr "About"
|
|||
msgid "Access Logs"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/domain/DomainList.vue:47 src/views/user/User.vue:43
|
||||
#: src/views/config/config.ts:36 src/views/domain/DomainList.vue:47
|
||||
#: src/views/user/User.vue:43
|
||||
msgid "Action"
|
||||
msgstr "Action"
|
||||
|
||||
|
@ -205,6 +206,10 @@ msgstr ""
|
|||
msgid "Development Mode"
|
||||
msgstr "Development Mode"
|
||||
|
||||
#: src/views/config/config.ts:20
|
||||
msgid "Dir"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/domain/ngx_conf/directive/DirectiveAdd.vue:20
|
||||
msgid "Directive"
|
||||
msgstr "Directive"
|
||||
|
@ -308,6 +313,10 @@ msgstr "Failed to enable %{msg}"
|
|||
msgid "Failed to get certificate information"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/config/config.ts:22
|
||||
msgid "File"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/other/Error.vue:3 src/views/other/Error.vue:4
|
||||
msgid "File Not Found"
|
||||
msgstr "File Not Found"
|
||||
|
@ -444,7 +453,8 @@ msgstr "Modify Config"
|
|||
msgid "Modify Config"
|
||||
msgstr "Modify Config"
|
||||
|
||||
#: src/views/domain/DomainEdit.vue:36 src/views/domain/DomainList.vue:15
|
||||
#: src/views/config/config.ts:9 src/views/domain/DomainEdit.vue:36
|
||||
#: src/views/domain/DomainList.vue:15
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
|
@ -730,7 +740,12 @@ msgstr ""
|
|||
msgid "Theme"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/domain/DomainList.vue:41 src/views/user/User.vue:37
|
||||
#: src/language/constants.ts:23 src/views/config/config.ts:14
|
||||
msgid "Type"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/config/config.ts:29 src/views/domain/DomainList.vue:41
|
||||
#: src/views/user/User.vue:37
|
||||
msgid "Updated at"
|
||||
msgstr "Updated at"
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ msgstr ""
|
|||
msgid "Access Logs"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/config/config.ts:36
|
||||
#: src/views/domain/DomainList.vue:47
|
||||
#: src/views/user/User.vue:43
|
||||
msgid "Action"
|
||||
|
@ -203,6 +204,10 @@ msgstr ""
|
|||
msgid "Development Mode"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/config/config.ts:20
|
||||
msgid "Dir"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/domain/ngx_conf/directive/DirectiveAdd.vue:20
|
||||
msgid "Directive"
|
||||
msgstr ""
|
||||
|
@ -320,6 +325,10 @@ msgstr ""
|
|||
msgid "Failed to get certificate information"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/config/config.ts:22
|
||||
msgid "File"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/other/Error.vue:3
|
||||
#: src/views/other/Error.vue:4
|
||||
msgid "File Not Found"
|
||||
|
@ -456,6 +465,7 @@ msgstr ""
|
|||
msgid "Modify Config"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/config/config.ts:9
|
||||
#: src/views/domain/DomainEdit.vue:36
|
||||
#: src/views/domain/DomainList.vue:15
|
||||
msgid "Name"
|
||||
|
@ -747,6 +757,12 @@ msgstr ""
|
|||
msgid "Theme"
|
||||
msgstr ""
|
||||
|
||||
#: src/language/constants.ts:23
|
||||
#: src/views/config/config.ts:14
|
||||
msgid "Type"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/config/config.ts:29
|
||||
#: src/views/domain/DomainList.vue:41
|
||||
#: src/views/user/User.vue:37
|
||||
msgid "Updated at"
|
||||
|
|
File diff suppressed because one or more lines are too long
Binary file not shown.
|
@ -20,7 +20,8 @@ msgstr "关于"
|
|||
msgid "Access Logs"
|
||||
msgstr "访问日志"
|
||||
|
||||
#: src/views/domain/DomainList.vue:47 src/views/user/User.vue:43
|
||||
#: src/views/config/config.ts:36 src/views/domain/DomainList.vue:47
|
||||
#: src/views/user/User.vue:43
|
||||
msgid "Action"
|
||||
msgstr "操作"
|
||||
|
||||
|
@ -204,6 +205,10 @@ msgstr "删除站点: %{site_name}"
|
|||
msgid "Development Mode"
|
||||
msgstr "开发模式"
|
||||
|
||||
#: src/views/config/config.ts:20
|
||||
msgid "Dir"
|
||||
msgstr "目录"
|
||||
|
||||
#: src/views/domain/ngx_conf/directive/DirectiveAdd.vue:20
|
||||
msgid "Directive"
|
||||
msgstr "指令"
|
||||
|
@ -307,6 +312,10 @@ msgstr "启用失败 %{msg}"
|
|||
msgid "Failed to get certificate information"
|
||||
msgstr "获取证书信息失败"
|
||||
|
||||
#: src/views/config/config.ts:22
|
||||
msgid "File"
|
||||
msgstr "文件"
|
||||
|
||||
#: src/views/other/Error.vue:3 src/views/other/Error.vue:4
|
||||
msgid "File Not Found"
|
||||
msgstr "未找到文件"
|
||||
|
@ -440,7 +449,8 @@ msgstr "修改"
|
|||
msgid "Modify Config"
|
||||
msgstr "修改配置文件"
|
||||
|
||||
#: src/views/domain/DomainEdit.vue:36 src/views/domain/DomainList.vue:15
|
||||
#: src/views/config/config.ts:9 src/views/domain/DomainEdit.vue:36
|
||||
#: src/views/domain/DomainList.vue:15
|
||||
msgid "Name"
|
||||
msgstr "名称"
|
||||
|
||||
|
@ -717,7 +727,12 @@ msgstr "用户名或密码错误"
|
|||
msgid "Theme"
|
||||
msgstr "主题"
|
||||
|
||||
#: src/views/domain/DomainList.vue:41 src/views/user/User.vue:37
|
||||
#: src/language/constants.ts:23 src/views/config/config.ts:14
|
||||
msgid "Type"
|
||||
msgstr "类型"
|
||||
|
||||
#: src/views/config/config.ts:29 src/views/domain/DomainList.vue:41
|
||||
#: src/views/user/User.vue:37
|
||||
msgid "Updated at"
|
||||
msgstr "修改时间"
|
||||
|
||||
|
|
|
@ -21,7 +21,8 @@ msgstr "關於"
|
|||
msgid "Access Logs"
|
||||
msgstr "訪問日誌"
|
||||
|
||||
#: src/views/domain/DomainList.vue:47 src/views/user/User.vue:43
|
||||
#: src/views/config/config.ts:36 src/views/domain/DomainList.vue:47
|
||||
#: src/views/user/User.vue:43
|
||||
msgid "Action"
|
||||
msgstr "操作"
|
||||
|
||||
|
@ -205,6 +206,10 @@ msgstr "刪除站點:%{site_name}"
|
|||
msgid "Development Mode"
|
||||
msgstr "開發模式"
|
||||
|
||||
#: src/views/config/config.ts:20
|
||||
msgid "Dir"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/domain/ngx_conf/directive/DirectiveAdd.vue:20
|
||||
msgid "Directive"
|
||||
msgstr "指令"
|
||||
|
@ -308,6 +313,10 @@ msgstr "啟用失敗 %{msg}"
|
|||
msgid "Failed to get certificate information"
|
||||
msgstr "獲取證書信息失敗"
|
||||
|
||||
#: src/views/config/config.ts:22
|
||||
msgid "File"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/other/Error.vue:3 src/views/other/Error.vue:4
|
||||
msgid "File Not Found"
|
||||
msgstr "未找到檔案"
|
||||
|
@ -443,7 +452,8 @@ msgstr "修改"
|
|||
msgid "Modify Config"
|
||||
msgstr "修改配置"
|
||||
|
||||
#: src/views/domain/DomainEdit.vue:36 src/views/domain/DomainList.vue:15
|
||||
#: src/views/config/config.ts:9 src/views/domain/DomainEdit.vue:36
|
||||
#: src/views/domain/DomainList.vue:15
|
||||
msgid "Name"
|
||||
msgstr "名稱"
|
||||
|
||||
|
@ -724,7 +734,12 @@ msgstr "用戶名或密碼不正確"
|
|||
msgid "Theme"
|
||||
msgstr "外觀樣式"
|
||||
|
||||
#: src/views/domain/DomainList.vue:41 src/views/user/User.vue:37
|
||||
#: src/language/constants.ts:23 src/views/config/config.ts:14
|
||||
msgid "Type"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/config/config.ts:29 src/views/domain/DomainList.vue:41
|
||||
#: src/views/user/User.vue:37
|
||||
msgid "Updated at"
|
||||
msgstr "修改時間"
|
||||
|
||||
|
|
|
@ -10,7 +10,8 @@ import {
|
|||
InfoCircleOutlined,
|
||||
UserOutlined,
|
||||
FileTextOutlined,
|
||||
SettingOutlined
|
||||
SettingOutlined,
|
||||
SafetyCertificateOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
|
||||
const {$gettext} = gettext
|
||||
|
@ -91,6 +92,14 @@ export const routes = [
|
|||
hiddenInSidebar: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'cert',
|
||||
name: () => $gettext('Certification'),
|
||||
component: () => import('@/views/cert/Cert.vue'),
|
||||
meta: {
|
||||
icon: SafetyCertificateOutlined
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'terminal',
|
||||
name: () => $gettext('Terminal'),
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"version":"1.7.0","build_id":62,"total_build":132}
|
||||
{"version":"1.7.0","build_id":63,"total_build":133}
|
88
frontend/src/views/cert/Cert.vue
Normal file
88
frontend/src/views/cert/Cert.vue
Normal file
|
@ -0,0 +1,88 @@
|
|||
<script setup lang="tsx">
|
||||
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'
|
||||
|
||||
const {$gettext} = useGettext()
|
||||
|
||||
const columns = [{
|
||||
title: () => $gettext('Name'),
|
||||
dataIndex: 'name',
|
||||
sorter: true,
|
||||
pithy: true,
|
||||
customRender: (args: customRender) => {
|
||||
const {text, record} = args
|
||||
if (!text) {
|
||||
return h('div', record.domain)
|
||||
}
|
||||
return h('div', text)
|
||||
},
|
||||
edit: {
|
||||
type: input
|
||||
},
|
||||
search: true
|
||||
}, {
|
||||
title: () => $gettext('Domain'),
|
||||
dataIndex: 'domain',
|
||||
sorter: true,
|
||||
pithy: true,
|
||||
edit: {
|
||||
type: input
|
||||
},
|
||||
search: true
|
||||
}, {
|
||||
title: () => $gettext('Auto Cert'),
|
||||
dataIndex: 'auto_cert',
|
||||
customRender: (args: customRender) => {
|
||||
const template: any = []
|
||||
const {text, column} = args
|
||||
if (text === true || text > 0) {
|
||||
template.push(<Badge status="success"/>)
|
||||
template.push($gettext('Enabled'))
|
||||
} else {
|
||||
template.push(<Badge status="error"/>)
|
||||
template.push($gettext('Disabled'))
|
||||
}
|
||||
return h('div', template)
|
||||
},
|
||||
sorter: true,
|
||||
pithy: true
|
||||
}, {
|
||||
title: () => $gettext('SSL Certificate Path'),
|
||||
dataIndex: 'ssl_certificate_path',
|
||||
edit: {
|
||||
type: input
|
||||
},
|
||||
display: false
|
||||
}, {
|
||||
title: () => $gettext('SSL Certificate Key Path'),
|
||||
dataIndex: 'ssl_certificate_key_path',
|
||||
edit: {
|
||||
type: input
|
||||
},
|
||||
display: false
|
||||
}, {
|
||||
title: () => $gettext('Updated at'),
|
||||
dataIndex: 'updated_at',
|
||||
customRender: datetime,
|
||||
sorter: true,
|
||||
pithy: true
|
||||
}, {
|
||||
title: () => $gettext('Action'),
|
||||
dataIndex: 'action'
|
||||
}]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<std-curd :title="$gettext('Certification')" :api="cert" :columns="columns"
|
||||
row-key="name"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
</style>
|
|
@ -3,7 +3,6 @@ import gettext from '@/gettext'
|
|||
|
||||
const {$gettext} = gettext
|
||||
|
||||
import {Badge} from 'ant-design-vue'
|
||||
import {h} from 'vue'
|
||||
|
||||
const configColumns = [{
|
11
frontend/src/views/template/Template.vue
Normal file
11
frontend/src/views/template/Template.vue
Normal file
|
@ -0,0 +1,11 @@
|
|||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
</style>
|
|
@ -1 +1 @@
|
|||
{"version":"1.7.0","build_id":62,"total_build":132}
|
||||
{"version":"1.7.0","build_id":63,"total_build":133}
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/0xJacky/Nginx-UI/server/pkg/nginx"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/spf13/cast"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
@ -117,7 +118,8 @@ func IssueCert(c *gin.Context) {
|
|||
}
|
||||
|
||||
err = certModel.Updates(&model.Cert{
|
||||
SSLCertificatePath: sslCertificatePath,
|
||||
SSLCertificatePath: sslCertificatePath,
|
||||
SSLCertificateKeyPath: sslCertificateKeyPath,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
|
@ -137,3 +139,108 @@ func IssueCert(c *gin.Context) {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
func GetCertList(c *gin.Context) {
|
||||
certList := model.GetCertList(c.Query("name"), c.Query("domain"))
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"data": certList,
|
||||
})
|
||||
}
|
||||
|
||||
func GetCert(c *gin.Context) {
|
||||
certModel, err := model.FirstCertByID(cast.ToInt(c.Param("id")))
|
||||
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, certModel)
|
||||
}
|
||||
|
||||
func AddCert(c *gin.Context) {
|
||||
var json struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Domain string `json:"domain" binding:"required"`
|
||||
SSLCertificatePath string `json:"ssl_certificate_path" binding:"required"`
|
||||
SSLCertificateKeyPath string `json:"ssl_certificate_key_path" binding:"required"`
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, nil)
|
||||
}
|
||||
|
||||
func ModifyCert(c *gin.Context) {
|
||||
id := cast.ToInt(c.Param("id"))
|
||||
certModel, err := model.FirstCertByID(id)
|
||||
|
||||
var json struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Domain string `json:"domain" binding:"required"`
|
||||
SSLCertificatePath string `json:"ssl_certificate_path" binding:"required"`
|
||||
SSLCertificateKeyPath string `json:"ssl_certificate_key_path" binding:"required"`
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, certModel)
|
||||
}
|
||||
|
||||
func RemoveCert(c *gin.Context) {
|
||||
id := cast.ToInt(c.Param("id"))
|
||||
certModel, err := model.FirstCertByID(id)
|
||||
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = certModel.Remove()
|
||||
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, nil)
|
||||
}
|
||||
|
|
|
@ -1,365 +1,381 @@
|
|||
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/config_list"
|
||||
"github.com/0xJacky/Nginx-UI/server/pkg/nginx"
|
||||
"github.com/gin-gonic/gin"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
"github.com/0xJacky/Nginx-UI/server/model"
|
||||
"github.com/0xJacky/Nginx-UI/server/pkg/cert"
|
||||
"github.com/0xJacky/Nginx-UI/server/pkg/config_list"
|
||||
"github.com/0xJacky/Nginx-UI/server/pkg/nginx"
|
||||
"github.com/gin-gonic/gin"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func GetDomains(c *gin.Context) {
|
||||
name := c.Query("name")
|
||||
orderBy := c.Query("order_by")
|
||||
sort := c.DefaultQuery("sort", "desc")
|
||||
name := c.Query("name")
|
||||
orderBy := c.Query("order_by")
|
||||
sort := c.DefaultQuery("sort", "desc")
|
||||
|
||||
mySort := map[string]string{
|
||||
"enabled": "bool",
|
||||
"name": "string",
|
||||
"modify": "time",
|
||||
}
|
||||
mySort := map[string]string{
|
||||
"enabled": "bool",
|
||||
"name": "string",
|
||||
"modify": "time",
|
||||
}
|
||||
|
||||
configFiles, err := os.ReadDir(nginx.GetNginxConfPath("sites-available"))
|
||||
configFiles, err := os.ReadDir(nginx.GetNginxConfPath("sites-available"))
|
||||
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
enabledConfig, err := os.ReadDir(filepath.Join(nginx.GetNginxConfPath("sites-enabled")))
|
||||
enabledConfig, err := os.ReadDir(filepath.Join(nginx.GetNginxConfPath("sites-enabled")))
|
||||
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
enabledConfigMap := make(map[string]bool)
|
||||
for i := range enabledConfig {
|
||||
enabledConfigMap[enabledConfig[i].Name()] = true
|
||||
}
|
||||
enabledConfigMap := make(map[string]bool)
|
||||
for i := range enabledConfig {
|
||||
enabledConfigMap[enabledConfig[i].Name()] = true
|
||||
}
|
||||
|
||||
var configs []gin.H
|
||||
var configs []gin.H
|
||||
|
||||
for i := range configFiles {
|
||||
file := configFiles[i]
|
||||
fileInfo, _ := file.Info()
|
||||
if !file.IsDir() {
|
||||
if name != "" && !strings.Contains(file.Name(), name) {
|
||||
continue
|
||||
}
|
||||
configs = append(configs, gin.H{
|
||||
"name": file.Name(),
|
||||
"size": fileInfo.Size(),
|
||||
"modify": fileInfo.ModTime(),
|
||||
"enabled": enabledConfigMap[file.Name()],
|
||||
})
|
||||
}
|
||||
}
|
||||
for i := range configFiles {
|
||||
file := configFiles[i]
|
||||
fileInfo, _ := file.Info()
|
||||
if !file.IsDir() {
|
||||
if name != "" && !strings.Contains(file.Name(), name) {
|
||||
continue
|
||||
}
|
||||
configs = append(configs, gin.H{
|
||||
"name": file.Name(),
|
||||
"size": fileInfo.Size(),
|
||||
"modify": fileInfo.ModTime(),
|
||||
"enabled": enabledConfigMap[file.Name()],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
configs = config_list.Sort(orderBy, sort, mySort[orderBy], configs)
|
||||
configs = config_list.Sort(orderBy, sort, mySort[orderBy], configs)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"data": configs,
|
||||
})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"data": configs,
|
||||
})
|
||||
}
|
||||
|
||||
type CertificateInfo struct {
|
||||
SubjectName string `json:"subject_name"`
|
||||
IssuerName string `json:"issuer_name"`
|
||||
NotAfter time.Time `json:"not_after"`
|
||||
NotBefore time.Time `json:"not_before"`
|
||||
SubjectName string `json:"subject_name"`
|
||||
IssuerName string `json:"issuer_name"`
|
||||
NotAfter time.Time `json:"not_after"`
|
||||
NotBefore time.Time `json:"not_before"`
|
||||
}
|
||||
|
||||
func GetDomain(c *gin.Context) {
|
||||
rewriteName, ok := c.Get("rewriteConfigFileName")
|
||||
rewriteName, ok := c.Get("rewriteConfigFileName")
|
||||
|
||||
name := c.Param("name")
|
||||
name := c.Param("name")
|
||||
|
||||
// for modify filename
|
||||
if ok {
|
||||
name = rewriteName.(string)
|
||||
}
|
||||
// for modify filename
|
||||
if ok {
|
||||
name = rewriteName.(string)
|
||||
}
|
||||
|
||||
path := filepath.Join(nginx.GetNginxConfPath("sites-available"), name)
|
||||
path := filepath.Join(nginx.GetNginxConfPath("sites-available"), name)
|
||||
|
||||
enabled := true
|
||||
if _, err := os.Stat(filepath.Join(nginx.GetNginxConfPath("sites-enabled"), name)); os.IsNotExist(err) {
|
||||
enabled = false
|
||||
}
|
||||
enabled := true
|
||||
if _, err := os.Stat(filepath.Join(nginx.GetNginxConfPath("sites-enabled"), name)); os.IsNotExist(err) {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
config, err := nginx.ParseNgxConfig(path)
|
||||
config, err := nginx.ParseNgxConfig(path)
|
||||
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
certInfoMap := make(map[int]CertificateInfo)
|
||||
var serverName string
|
||||
for serverIdx, server := range config.Servers {
|
||||
for _, directive := range server.Directives {
|
||||
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 == "server_name" {
|
||||
serverName = strings.ReplaceAll(directive.Params, " ", "_")
|
||||
continue
|
||||
}
|
||||
|
||||
if directive.Directive == "ssl_certificate" {
|
||||
if directive.Directive == "ssl_certificate" {
|
||||
|
||||
pubKey, err := cert.GetCertInfo(directive.Params)
|
||||
pubKey, err := cert.GetCertInfo(directive.Params)
|
||||
|
||||
if err != nil {
|
||||
log.Println("Failed to get certificate information", err)
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
log.Println("Failed to get certificate information", err)
|
||||
break
|
||||
}
|
||||
|
||||
certInfoMap[serverIdx] = CertificateInfo{
|
||||
SubjectName: pubKey.Subject.CommonName,
|
||||
IssuerName: pubKey.Issuer.CommonName,
|
||||
NotAfter: pubKey.NotAfter,
|
||||
NotBefore: pubKey.NotBefore,
|
||||
}
|
||||
certInfoMap[serverIdx] = CertificateInfo{
|
||||
SubjectName: pubKey.Subject.CommonName,
|
||||
IssuerName: pubKey.Issuer.CommonName,
|
||||
NotAfter: pubKey.NotAfter,
|
||||
NotBefore: pubKey.NotBefore,
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_, err = model.FirstCert(serverName)
|
||||
certModel, _ := model.FirstCert(serverName)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"enabled": enabled,
|
||||
"name": name,
|
||||
"config": config.BuildConfig(),
|
||||
"tokenized": config,
|
||||
"auto_cert": err == nil,
|
||||
"cert_info": certInfoMap,
|
||||
})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"enabled": enabled,
|
||||
"name": name,
|
||||
"config": config.BuildConfig(),
|
||||
"tokenized": config,
|
||||
"auto_cert": certModel.AutoCert == model.AutoCertEnabled,
|
||||
"cert_info": certInfoMap,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func EditDomain(c *gin.Context) {
|
||||
name := c.Param("name")
|
||||
name := c.Param("name")
|
||||
|
||||
if name == "" {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{
|
||||
"message": "param name is empty",
|
||||
})
|
||||
return
|
||||
}
|
||||
if name == "" {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{
|
||||
"message": "param name is empty",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var json struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
var json struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
if !BindAndValid(c, &json) {
|
||||
return
|
||||
}
|
||||
if !BindAndValid(c, &json) {
|
||||
return
|
||||
}
|
||||
|
||||
path := filepath.Join(nginx.GetNginxConfPath("sites-available"), name)
|
||||
path := filepath.Join(nginx.GetNginxConfPath("sites-available"), name)
|
||||
|
||||
err := os.WriteFile(path, []byte(json.Content), 0644)
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
enabledConfigFilePath := filepath.Join(nginx.GetNginxConfPath("sites-enabled"), name)
|
||||
// rename the config file if needed
|
||||
if name != json.Name {
|
||||
newPath := filepath.Join(nginx.GetNginxConfPath("sites-available"), json.Name)
|
||||
// recreate soft link
|
||||
log.Println(enabledConfigFilePath)
|
||||
if _, err = os.Stat(enabledConfigFilePath); err == nil {
|
||||
log.Println(enabledConfigFilePath)
|
||||
_ = os.Remove(enabledConfigFilePath)
|
||||
enabledConfigFilePath = filepath.Join(nginx.GetNginxConfPath("sites-enabled"), json.Name)
|
||||
err = os.Symlink(newPath, enabledConfigFilePath)
|
||||
err := os.WriteFile(path, []byte(json.Content), 0644)
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
enabledConfigFilePath := filepath.Join(nginx.GetNginxConfPath("sites-enabled"), name)
|
||||
// rename the config file if needed
|
||||
if name != json.Name {
|
||||
newPath := filepath.Join(nginx.GetNginxConfPath("sites-available"), json.Name)
|
||||
// recreate soft link
|
||||
log.Println(enabledConfigFilePath)
|
||||
if _, err = os.Stat(enabledConfigFilePath); err == nil {
|
||||
log.Println(enabledConfigFilePath)
|
||||
_ = os.Remove(enabledConfigFilePath)
|
||||
enabledConfigFilePath = filepath.Join(nginx.GetNginxConfPath("sites-enabled"), json.Name)
|
||||
err = os.Symlink(newPath, enabledConfigFilePath)
|
||||
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
err = os.Rename(path, newPath)
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
name = json.Name
|
||||
c.Set("rewriteConfigFileName", name)
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
err = os.Rename(path, newPath)
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
name = json.Name
|
||||
c.Set("rewriteConfigFileName", name)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
enabledConfigFilePath = filepath.Join(nginx.GetNginxConfPath("sites-enabled"), name)
|
||||
if _, err = os.Stat(enabledConfigFilePath); err == nil {
|
||||
// Test nginx configuration
|
||||
err = nginx.TestNginxConf()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
enabledConfigFilePath = filepath.Join(nginx.GetNginxConfPath("sites-enabled"), name)
|
||||
if _, err = os.Stat(enabledConfigFilePath); err == nil {
|
||||
// Test nginx configuration
|
||||
err = nginx.TestNginxConf()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
output := nginx.ReloadNginx()
|
||||
output := nginx.ReloadNginx()
|
||||
|
||||
if output != "" && strings.Contains(output, "error") {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"message": output,
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
if output != "" && strings.Contains(output, "error") {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"message": output,
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
GetDomain(c)
|
||||
GetDomain(c)
|
||||
}
|
||||
|
||||
func EnableDomain(c *gin.Context) {
|
||||
configFilePath := filepath.Join(nginx.GetNginxConfPath("sites-available"), c.Param("name"))
|
||||
enabledConfigFilePath := filepath.Join(nginx.GetNginxConfPath("sites-enabled"), c.Param("name"))
|
||||
configFilePath := filepath.Join(nginx.GetNginxConfPath("sites-available"), c.Param("name"))
|
||||
enabledConfigFilePath := filepath.Join(nginx.GetNginxConfPath("sites-enabled"), c.Param("name"))
|
||||
|
||||
_, err := os.Stat(configFilePath)
|
||||
_, err := os.Stat(configFilePath)
|
||||
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = os.Stat(enabledConfigFilePath); os.IsNotExist(err) {
|
||||
err = os.Symlink(configFilePath, enabledConfigFilePath)
|
||||
if _, err = os.Stat(enabledConfigFilePath); os.IsNotExist(err) {
|
||||
err = os.Symlink(configFilePath, enabledConfigFilePath)
|
||||
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Test nginx config, if not pass then rollback.
|
||||
err = nginx.TestNginxConf()
|
||||
if err != nil {
|
||||
_ = os.Remove(enabledConfigFilePath)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
// Test nginx config, if not pass then rollback.
|
||||
err = nginx.TestNginxConf()
|
||||
if err != nil {
|
||||
_ = os.Remove(enabledConfigFilePath)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
output := nginx.ReloadNginx()
|
||||
output := nginx.ReloadNginx()
|
||||
|
||||
if output != "" && strings.Contains(output, "error") {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"message": output,
|
||||
})
|
||||
return
|
||||
}
|
||||
if output != "" && strings.Contains(output, "error") {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"message": output,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "ok",
|
||||
})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "ok",
|
||||
})
|
||||
}
|
||||
|
||||
func DisableDomain(c *gin.Context) {
|
||||
enabledConfigFilePath := filepath.Join(nginx.GetNginxConfPath("sites-enabled"), c.Param("name"))
|
||||
enabledConfigFilePath := filepath.Join(nginx.GetNginxConfPath("sites-enabled"), c.Param("name"))
|
||||
|
||||
_, err := os.Stat(enabledConfigFilePath)
|
||||
_, err := os.Stat(enabledConfigFilePath)
|
||||
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = os.Remove(enabledConfigFilePath)
|
||||
err = os.Remove(enabledConfigFilePath)
|
||||
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// delete auto cert record
|
||||
certModel := model.Cert{Domain: c.Param("name")}
|
||||
err = certModel.Remove()
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
// delete auto cert record
|
||||
certModel := model.Cert{Domain: c.Param("name")}
|
||||
err = certModel.Remove()
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
output := nginx.ReloadNginx()
|
||||
output := nginx.ReloadNginx()
|
||||
|
||||
if output != "" {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"message": output,
|
||||
})
|
||||
return
|
||||
}
|
||||
if output != "" {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"message": output,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "ok",
|
||||
})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "ok",
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteDomain(c *gin.Context) {
|
||||
var err error
|
||||
name := c.Param("name")
|
||||
availablePath := filepath.Join(nginx.GetNginxConfPath("sites-available"), name)
|
||||
enabledPath := filepath.Join(nginx.GetNginxConfPath("sites-enabled"), name)
|
||||
var err error
|
||||
name := c.Param("name")
|
||||
availablePath := filepath.Join(nginx.GetNginxConfPath("sites-available"), name)
|
||||
enabledPath := filepath.Join(nginx.GetNginxConfPath("sites-enabled"), name)
|
||||
|
||||
if _, err = os.Stat(availablePath); os.IsNotExist(err) {
|
||||
c.JSON(http.StatusNotFound, gin.H{
|
||||
"message": "site not found",
|
||||
})
|
||||
return
|
||||
}
|
||||
if _, err = os.Stat(availablePath); os.IsNotExist(err) {
|
||||
c.JSON(http.StatusNotFound, gin.H{
|
||||
"message": "site not found",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = os.Stat(enabledPath); err == nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{
|
||||
"message": "site is enabled",
|
||||
})
|
||||
return
|
||||
}
|
||||
if _, err = os.Stat(enabledPath); err == nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{
|
||||
"message": "site is enabled",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
certModel := model.Cert{Domain: name}
|
||||
_ = certModel.Remove()
|
||||
certModel := model.Cert{Domain: name}
|
||||
_ = certModel.Remove()
|
||||
|
||||
err = os.Remove(availablePath)
|
||||
err = os.Remove(availablePath)
|
||||
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "ok",
|
||||
})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "ok",
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func AddDomainToAutoCert(c *gin.Context) {
|
||||
domain := c.Param("domain")
|
||||
domain := c.Param("domain")
|
||||
domain = strings.ReplaceAll(domain, " ", "_")
|
||||
certModel, err := model.FirstOrCreateCert(domain)
|
||||
|
||||
certModel, err := model.FirstOrCreateCert(domain)
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, certModel)
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = certModel.Updates(&model.Cert{
|
||||
AutoCert: model.AutoCertEnabled,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, certModel)
|
||||
}
|
||||
|
||||
func RemoveDomainFromAutoCert(c *gin.Context) {
|
||||
certModel := model.Cert{
|
||||
Domain: c.Param("domain"),
|
||||
}
|
||||
err := certModel.Remove()
|
||||
domain := c.Param("domain")
|
||||
domain = strings.ReplaceAll(domain, " ", "_")
|
||||
certModel := model.Cert{
|
||||
Domain: domain,
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, nil)
|
||||
err := certModel.Updates(&model.Cert{
|
||||
AutoCert: model.AutoCertDisabled,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, nil)
|
||||
}
|
||||
|
|
|
@ -6,10 +6,18 @@ import (
|
|||
"path/filepath"
|
||||
)
|
||||
|
||||
const (
|
||||
AutoCertEnabled = 1
|
||||
AutoCertDisabled = -1
|
||||
)
|
||||
|
||||
type Cert struct {
|
||||
Model
|
||||
Domain string `json:"domain"`
|
||||
SSLCertificatePath string `json:"ssl_certificate_path"`
|
||||
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"`
|
||||
}
|
||||
|
||||
func FirstCert(domain string) (c Cert, err error) {
|
||||
|
@ -27,7 +35,7 @@ func FirstOrCreateCert(domain string) (c Cert, err error) {
|
|||
|
||||
func GetAutoCertList() (c []Cert) {
|
||||
var t []Cert
|
||||
db.Find(&t)
|
||||
db.Where("auto_cert", AutoCertEnabled).Find(&t)
|
||||
|
||||
// check if this domain is enabled
|
||||
enabledConfig, err := os.ReadDir(filepath.Join(nginx.GetNginxConfPath("sites-enabled")))
|
||||
|
@ -50,6 +58,24 @@ func GetAutoCertList() (c []Cert) {
|
|||
return
|
||||
}
|
||||
|
||||
func GetCertList(name, domain string) (c []Cert) {
|
||||
tx := db
|
||||
if name != "" {
|
||||
tx = tx.Where("name LIKE ? or domain LIKE ?", "%"+name+"%", "%"+name+"%")
|
||||
}
|
||||
if domain != "" {
|
||||
tx = tx.Where("domain LIKE ?", "%"+domain+"%")
|
||||
}
|
||||
tx.Find(&c)
|
||||
return
|
||||
}
|
||||
|
||||
func FirstCertByID(id int) (c Cert, err error) {
|
||||
err = db.First(&c, id).Error
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Cert) Updates(n *Cert) error {
|
||||
return db.Model(c).Updates(n).Error
|
||||
}
|
||||
|
|
|
@ -77,10 +77,15 @@ func InitRouter() *gin.Engine {
|
|||
|
||||
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("cert/:domain", api.AddDomainToAutoCert)
|
||||
g.POST("auto_cert/:domain", api.AddDomainToAutoCert)
|
||||
// Delete domain from auto-renew cert list
|
||||
g.DELETE("cert/:domain", api.RemoveDomainFromAutoCert)
|
||||
g.DELETE("auto_cert/:domain", api.RemoveDomainFromAutoCert)
|
||||
|
||||
// pty
|
||||
g.GET("pty", api.Pty)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue