1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-31 14:08:06 +08:00

feat: 增加https设置

This commit is contained in:
zhengkunwang223 2022-11-20 18:32:56 +08:00 committed by zhengkunwang223
parent 55aed7943e
commit fb90fae87f
16 changed files with 346 additions and 62 deletions

View File

@ -166,3 +166,31 @@ func (b *BaseApi) UpdateNginxConfig(c *gin.Context) {
}
helper.SuccessWithData(c, nil)
}
func (b *BaseApi) GetHTTPSConfig(c *gin.Context) {
id, err := helper.GetParamID(c)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInternalServer, nil)
return
}
res, err := websiteService.GetWebsiteHTTPS(id)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, res)
}
func (b *BaseApi) UpdateHTTPSConfig(c *gin.Context) {
var req dto.WebsiteHTTPSOp
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
res, err := websiteService.OpWebsiteHTTPS(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, res)
}

View File

@ -5,23 +5,33 @@ import (
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/gin-gonic/gin"
"reflect"
)
func (b *BaseApi) PageWebsiteSSL(c *gin.Context) {
var req dto.PageInfo
var req dto.WebsiteSSLSearch
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
total, accounts, err := websiteSSLService.Page(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
if !reflect.DeepEqual(req.PageInfo, dto.PageInfo{}) {
total, accounts, err := websiteSSLService.Page(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Total: total,
Items: accounts,
})
} else {
list, err := websiteSSLService.Search()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, list)
}
helper.SuccessWithData(c, dto.PageResult{
Total: total,
Items: accounts,
})
}
func (b *BaseApi) CreateWebsiteSSL(c *gin.Context) {
@ -51,20 +61,6 @@ func (b *BaseApi) RenewWebsiteSSL(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
func (b *BaseApi) ApplyWebsiteSSL(c *gin.Context) {
var req dto.WebsiteSSLApply
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
res, err := websiteSSLService.Apply(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, res)
}
func (b *BaseApi) GetDNSResolve(c *gin.Context) {
var req dto.WebsiteDNSReq
if err := c.ShouldBindJSON(&req); err != nil {

View File

@ -69,3 +69,23 @@ type WebSiteDomainCreate struct {
Port int `json:"port"`
Domain string `json:"domain"`
}
type WebsiteHTTPS struct {
Enable bool `json:"enable"`
SSL model.WebSiteSSL `json:"SSL"`
}
type SSlType string
const (
SSLExisted SSlType = "existed"
SSLAuto SSlType = "auto"
SSLManual SSlType = "manual"
)
type WebsiteHTTPSOp struct {
WebsiteID uint `json:"websiteId"`
Enable bool `json:"enable"`
WebsiteSSLID uint `json:"websiteSSLId"`
Type SSlType `json:"type"`
}

View File

@ -14,6 +14,10 @@ const (
Http = "http"
)
type WebsiteSSLSearch struct {
PageInfo
}
type WebsiteSSLCreate struct {
PrimaryDomain string `json:"primaryDomain" validate:"required"`
OtherDomains string `json:"otherDomains"`
@ -24,7 +28,7 @@ type WebsiteSSLCreate struct {
type WebsiteSSLApply struct {
WebsiteID uint `json:"websiteId" validate:"required"`
SSLID uint `json:"SSLId" validate:"required"`
Enable bool `json:"enable" validate:"required"`
}
type WebsiteDNSReq struct {

View File

@ -33,6 +33,15 @@ func (w WebsiteSSLRepo) GetFirst(opts ...DBOption) (model.WebSiteSSL, error) {
return website, nil
}
func (w WebsiteSSLRepo) List(opts ...DBOption) ([]model.WebSiteSSL, error) {
var websites []model.WebSiteSSL
db := getDb(opts...).Model(&model.WebSiteSSL{})
if err := db.Find(&websites).Error; err != nil {
return websites, err
}
return websites, nil
}
func (w WebsiteSSLRepo) Create(ctx context.Context, ssl *model.WebSiteSSL) error {
return getTx(ctx).Create(ssl).Error
}

View File

@ -271,3 +271,52 @@ func (w WebsiteService) GetWebsiteNginxConfig(websiteId uint) (dto.FileInfo, err
}
return dto.FileInfo{FileInfo: *info}, nil
}
func (w WebsiteService) GetWebsiteHTTPS(websiteId uint) (dto.WebsiteHTTPS, error) {
website, err := websiteRepo.GetFirst(commonRepo.WithByID(websiteId))
if err != nil {
return dto.WebsiteHTTPS{}, err
}
var res dto.WebsiteHTTPS
if website.WebSiteSSLID == 0 {
res.Enable = false
return res, nil
}
websiteSSL, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(website.WebSiteSSLID))
if err != nil {
return dto.WebsiteHTTPS{}, err
}
res.SSL = websiteSSL
res.Enable = true
return res, nil
}
func (w WebsiteService) OpWebsiteHTTPS(req dto.WebsiteHTTPSOp) (dto.WebsiteHTTPS, error) {
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
if err != nil {
return dto.WebsiteHTTPS{}, err
}
var res dto.WebsiteHTTPS
res.Enable = req.Enable
ssl, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(req.WebsiteSSLID))
if err != nil {
return dto.WebsiteHTTPS{}, err
}
if req.Type == dto.SSLExisted {
website.WebSiteSSLID = ssl.ID
if err := websiteRepo.Save(context.TODO(), &website); err != nil {
return dto.WebsiteHTTPS{}, err
}
res.SSL = ssl
}
if req.Enable {
if err := applySSL(website, ssl); err != nil {
return dto.WebsiteHTTPS{}, err
}
}
return res, nil
}

View File

@ -7,15 +7,17 @@ import (
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/utils/ssl"
"path"
"strings"
)
type WebSiteSSLService struct {
}
func (w WebSiteSSLService) Page(search dto.PageInfo) (int64, []dto.WebsiteSSLDTO, error) {
func (w WebSiteSSLService) Page(search dto.WebsiteSSLSearch) (int64, []dto.WebsiteSSLDTO, error) {
total, sslList, err := websiteSSLRepo.Page(search.Page, search.PageSize, commonRepo.WithOrderBy("created_at desc"))
if err != nil {
return 0, nil, err
}
var sslDTOs []dto.WebsiteSSLDTO
for _, ssl := range sslList {
sslDTOs = append(sslDTOs, dto.WebsiteSSLDTO{
@ -25,6 +27,20 @@ func (w WebSiteSSLService) Page(search dto.PageInfo) (int64, []dto.WebsiteSSLDTO
return total, sslDTOs, err
}
func (w WebSiteSSLService) Search() ([]dto.WebsiteSSLDTO, error) {
sslList, err := websiteSSLRepo.List()
if err != nil {
return nil, err
}
var sslDTOs []dto.WebsiteSSLDTO
for _, ssl := range sslList {
sslDTOs = append(sslDTOs, dto.WebsiteSSLDTO{
WebSiteSSL: ssl,
})
}
return sslDTOs, err
}
func (w WebSiteSSLService) Create(create dto.WebsiteSSLCreate) (dto.WebsiteSSLCreate, error) {
var res dto.WebsiteSSLCreate
@ -142,38 +158,6 @@ func (w WebSiteSSLService) Renew(sslId uint) error {
return websiteSSLRepo.Save(websiteSSL)
}
func (w WebSiteSSLService) Apply(apply dto.WebsiteSSLApply) (dto.WebsiteSSLApply, error) {
websiteSSL, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(apply.SSLID))
if err != nil {
return dto.WebsiteSSLApply{}, err
}
website, err := websiteRepo.GetFirst(commonRepo.WithByID(apply.WebsiteID))
if err != nil {
return dto.WebsiteSSLApply{}, err
}
if err := createPemFile(websiteSSL); err != nil {
return dto.WebsiteSSLApply{}, err
}
nginxParams := getNginxParamsFromStaticFile(dto.SSL)
for i, param := range nginxParams {
if param.Name == "ssl_certificate" {
nginxParams[i].Params = []string{path.Join("/etc/nginx/ssl", websiteSSL.PrimaryDomain, "fullchain.pem")}
}
if param.Name == "ssl_certificate_key" {
nginxParams[i].Params = []string{path.Join("/etc/nginx/ssl", websiteSSL.PrimaryDomain, "privkey.pem")}
}
}
if err := updateNginxConfig(website, nginxParams, dto.SSL); err != nil {
return dto.WebsiteSSLApply{}, err
}
website.WebSiteSSLID = websiteSSL.ID
if err := websiteRepo.Save(context.TODO(), &website); err != nil {
return dto.WebsiteSSLApply{}, err
}
return apply, nil
}
func (w WebSiteSSLService) GetDNSResolve(req dto.WebsiteDNSReq) (dto.WebsiteDNSRes, error) {
acmeAccount, err := websiteAcmeRepo.GetFirst(commonRepo.WithByID(req.AcmeAccountID))
if err != nil {

View File

@ -308,7 +308,7 @@ func deleteNginxConfig(website model.WebSite, keys []string) error {
return nginxCheckAndReload(nginxConfig.OldContent, nginxConfig.FilePath, nginxConfig.ContainerName)
}
func createPemFile(websiteSSL model.WebSiteSSL) error {
func createPemFile(website model.WebSite, websiteSSL model.WebSiteSSL) error {
nginxApp, err := appRepo.GetFirst(appRepo.WithKey("nginx"))
if err != nil {
return err
@ -318,7 +318,7 @@ func createPemFile(websiteSSL model.WebSiteSSL) error {
return err
}
configDir := path.Join(constant.AppInstallDir, "nginx", nginxInstall.Name, "ssl", websiteSSL.PrimaryDomain)
configDir := path.Join(constant.AppInstallDir, "nginx", nginxInstall.Name, "ssl", website.Alias)
fileOp := files.NewFileOp()
if !fileOp.Stat(configDir) {
@ -350,6 +350,27 @@ func createPemFile(websiteSSL model.WebSiteSSL) error {
return nil
}
func applySSL(website model.WebSite, websiteSSL model.WebSiteSSL) error {
if err := createPemFile(website, websiteSSL); err != nil {
return err
}
nginxParams := getNginxParamsFromStaticFile(dto.SSL)
for i, param := range nginxParams {
if param.Name == "ssl_certificate" {
nginxParams[i].Params = []string{path.Join("/etc/nginx/ssl", website.Alias, "fullchain.pem")}
}
if param.Name == "ssl_certificate_key" {
nginxParams[i].Params = []string{path.Join("/etc/nginx/ssl", website.Alias, "privkey.pem")}
}
}
if err := updateNginxConfig(website, nginxParams, dto.SSL); err != nil {
return err
}
return nil
}
func getParamArray(key string, param interface{}) []string {
var res []string
switch p := param.(type) {

View File

@ -26,5 +26,7 @@ func (a *WebsiteRouter) InitWebsiteRouter(Router *gin.RouterGroup) {
groupRouter.POST("/domains", baseApi.CreateWebDomain)
groupRouter.POST("/config", baseApi.GetNginxConfig)
groupRouter.POST("/config/update", baseApi.UpdateNginxConfig)
groupRouter.GET("/:id/https", baseApi.GetHTTPSConfig)
groupRouter.POST("/:id/https", baseApi.UpdateHTTPSConfig)
}
}

View File

@ -18,7 +18,6 @@ func (a *WebsiteSSLRouter) InitWebsiteSSLRouter(Router *gin.RouterGroup) {
groupRouter.POST("/search", baseApi.PageWebsiteSSL)
groupRouter.POST("/renew", baseApi.RenewWebsiteSSL)
groupRouter.POST("", baseApi.CreateWebsiteSSL)
groupRouter.POST("/apply", baseApi.ApplyWebsiteSSL)
groupRouter.POST("/resolve", baseApi.GetDNSResolve)
groupRouter.DELETE("/:id", baseApi.DeleteWebsiteSSL)
groupRouter.GET("/:websiteId", baseApi.GetWebsiteSSL)

View File

@ -165,4 +165,20 @@ export namespace WebSite {
value: string;
type: string;
}
export interface SSLReq {
name?: string;
}
export interface HTTPSReq {
websiteId: number;
enable: boolean;
websiteSSLId: number;
type: string;
}
export interface HTTPSConfig {
enable: boolean;
SSL: SSL;
}
}

View File

@ -95,6 +95,10 @@ export const SearchSSL = (req: ReqPage) => {
return http.post<ResPage<WebSite.SSL>>(`/websites/ssl/search`, req);
};
export const ListSSL = (req: WebSite.SSLReq) => {
return http.post<WebSite.SSL[]>(`/websites/ssl/search`, req);
};
export const CreateSSL = (req: WebSite.SSLCreate) => {
return http.post<WebSite.SSLCreate>(`/websites/ssl`, req);
};
@ -118,3 +122,11 @@ export const RenewSSL = (req: WebSite.SSLRenew) => {
export const GetDnsResolve = (req: WebSite.DNSResolveReq) => {
return http.post<WebSite.DNSResolve>(`/websites/ssl/resolve`, req);
};
export const GetHTTPSConfig = (id: number) => {
return http.get<WebSite.HTTPSConfig>(`/websites/${id}/https`);
};
export const UpdateHTTPSConfig = (req: WebSite.HTTPSReq) => {
return http.post<WebSite.HTTPSConfig>(`/websites/${req.websiteId}/https`, req);
};

View File

@ -739,5 +739,6 @@ export default {
renewHelper: '确定续签证书',
renewSuccess: '续签证书',
config: '配置',
enableHTTPS: '启用HTTPS',
},
};

View File

@ -185,6 +185,7 @@ const submit = async (formEl: FormInstance | undefined) => {
loading.value = true;
CreateSSL(ssl.value)
.then(() => {
handleClose();
ElMessage.success(i18n.global.t('commons.msg.createSuccess'));
})
.finally(() => {

View File

@ -0,0 +1,139 @@
<template>
<el-row :gutter="20">
<el-col :span="20" :offset="2">
<el-form
ref="httpsForm"
label-position="left"
label-width="auto"
:model="form"
:rules="rules"
:loading="loading"
>
<el-form-item prop="websiteSSLId">
<el-checkbox v-model="form.enable">
{{ $t('website.enableHTTPS') }}
</el-checkbox>
</el-form-item>
<el-form-item :label="$t('website.ssl')" prop="type">
<el-select v-model="form.type" @change="changeType()">
<el-option :label="'选择已有证书'" :value="'existed'"></el-option>
<el-option :label="'手动导入证书'" :value="'manual'"></el-option>
<el-option :label="'自动生成证书'" :value="'auto'"></el-option>
</el-select>
</el-form-item>
<el-form-item :label="' '" prop="websiteSSLId" v-if="form.type === 'existed'">
<el-select
v-model="form.websiteSSLId"
placeholder="选择证书"
@change="changeSSl(form.websiteSSLId)"
>
<el-option
v-for="(ssl, index) in ssls"
:key="index"
:label="ssl.primaryDomain"
:value="ssl.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item :label="' '" v-if="websiteSSL && websiteSSL.id > 0">
<el-descriptions :column="3" border direction="vertical">
<el-descriptions-item label="主域名">{{ websiteSSL.primaryDomain }}</el-descriptions-item>
<el-descriptions-item label="备用域名">{{ websiteSSL.otherDomains }}</el-descriptions-item>
<el-descriptions-item label="过期时间">
{{ dateFromat(1, 1, websiteSSL.expireDate) }}
</el-descriptions-item>
</el-descriptions>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submit(httpsForm)" :loading="loading">
{{ $t('commons.button.save') }}
</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
</template>
<script lang="ts" setup>
import { WebSite } from '@/api/interface/website';
import { GetHTTPSConfig, ListSSL, UpdateHTTPSConfig } from '@/api/modules/website';
import { ElMessage, FormInstance } from 'element-plus';
import { computed, onMounted, reactive, ref } from 'vue';
import { dateFromat } from '@/utils/util';
import i18n from '@/lang';
import { Rules } from '@/global/form-rules';
const props = defineProps({
id: {
type: Number,
default: 0,
},
});
const id = computed(() => {
return props.id;
});
const httpsForm = ref<FormInstance>();
let form = reactive({
enable: false,
websiteId: id.value,
websiteSSLId: undefined,
type: 'existed',
});
let loading = ref(false);
const ssls = ref();
let websiteSSL = ref();
let rules = ref({
type: [Rules.requiredSelect],
});
const listSSL = () => {
ListSSL({}).then((res) => {
ssls.value = res.data;
});
};
const changeSSl = (sslid: number) => {
const res = ssls.value.filter((element: WebSite.SSL) => {
return element.id == sslid;
});
websiteSSL.value = res[0];
};
const changeType = () => {
websiteSSL.value = {};
form.websiteSSLId = undefined;
};
const get = () => {
GetHTTPSConfig(id.value).then((res) => {
if (res.data) {
form.enable = res.data.enable;
}
if (res.data?.SSL) {
form.websiteSSLId = res.data.SSL.id;
websiteSSL.value = res.data.SSL;
}
listSSL();
});
};
const submit = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
await formEl.validate((valid) => {
if (!valid) {
return;
}
loading.value = true;
form.websiteId = id.value;
UpdateHTTPSConfig(form)
.then(() => {
ElMessage.success(i18n.global.t('commons.msg.updateSuccess'));
})
.finally(() => {
loading.value = false;
});
});
};
onMounted(() => {
get();
});
</script>

View File

@ -9,7 +9,9 @@
<el-tab-pane :label="$t('website.rate')">
<LimitConn :id="id" v-if="index == '2'"></LimitConn>
</el-tab-pane>
<el-tab-pane :label="'HTTPS'"></el-tab-pane>
<el-tab-pane :label="'HTTPS'">
<HTTPS :id="id" v-if="index == '3'"></HTTPS>
</el-tab-pane>
<el-tab-pane :label="$t('website.other')">
<Other :id="id" v-if="index == '4'"></Other>
</el-tab-pane>
@ -23,6 +25,7 @@ import Doamin from './domain/index.vue';
import Default from './default-doc/index.vue';
import LimitConn from './limit-conn/index.vue';
import Other from './other/index.vue';
import HTTPS from './https/index.vue';
const props = defineProps({
id: {