Skip to content

Commit

Permalink
feat: 支持上传本地证书
Browse files Browse the repository at this point in the history
  • Loading branch information
devhaozi committed Oct 26, 2024
1 parent cf5f7bd commit 56ae7eb
Show file tree
Hide file tree
Showing 16 changed files with 301 additions and 112 deletions.
4 changes: 3 additions & 1 deletion internal/biz/cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/TheTNB/panel/internal/http/request"
"github.com/TheTNB/panel/pkg/acme"
"github.com/TheTNB/panel/pkg/types"
)

type Cert struct {
Expand All @@ -27,9 +28,10 @@ type Cert struct {
}

type CertRepo interface {
List(page, limit uint) ([]*Cert, int64, error)
List(page, limit uint) ([]*types.CertList, int64, error)
Get(id uint) (*Cert, error)
GetByWebsite(WebsiteID uint) (*Cert, error)
Upload(req *request.CertUpload) (*Cert, error)
Create(req *request.CertCreate) (*Cert, error)
Update(req *request.CertUpdate) error
Delete(id uint) error
Expand Down
62 changes: 60 additions & 2 deletions internal/data/cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@ import (
"context"
"errors"
"fmt"
"slices"
"strings"
"time"

"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/internal/http/request"
"github.com/TheTNB/panel/pkg/acme"
pkgcert "github.com/TheTNB/panel/pkg/cert"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/shell"
"github.com/TheTNB/panel/pkg/systemctl"
"github.com/TheTNB/panel/pkg/types"
)

type certRepo struct {
Expand All @@ -24,11 +27,37 @@ func NewCertRepo() biz.CertRepo {
return &certRepo{}
}

func (r *certRepo) List(page, limit uint) ([]*biz.Cert, int64, error) {
func (r *certRepo) List(page, limit uint) ([]*types.CertList, int64, error) {
var certs []*biz.Cert
var total int64
err := app.Orm.Model(&biz.Cert{}).Preload("Website").Preload("Account").Preload("DNS").Order("id desc").Count(&total).Offset(int((page - 1) * limit)).Limit(int(limit)).Find(&certs).Error
return certs, total, err

var list []*types.CertList
for cert := range slices.Values(certs) {
item := &types.CertList{
ID: cert.ID,
AccountID: cert.AccountID,
WebsiteID: cert.WebsiteID,
DNSID: cert.DNSID,
Type: cert.Type,
Domains: cert.Domains,
AutoRenew: cert.AutoRenew,
Cert: cert.Cert,
Key: cert.Key,
CreatedAt: cert.CreatedAt,
UpdatedAt: cert.UpdatedAt,
}
if decode, err := pkgcert.ParseCert(cert.Cert); err == nil {
item.NotBefore = decode.NotBefore
item.NotAfter = decode.NotAfter
item.Issuer = decode.Issuer.CommonName
item.OCSPServer = decode.OCSPServer
item.DNSNames = decode.DNSNames
}
list = append(list, item)
}

return list, total, err
}

func (r *certRepo) Get(id uint) (*biz.Cert, error) {
Expand All @@ -43,6 +72,25 @@ func (r *certRepo) GetByWebsite(WebsiteID uint) (*biz.Cert, error) {
return cert, err
}

func (r *certRepo) Upload(req *request.CertUpload) (*biz.Cert, error) {
info, err := pkgcert.ParseCert(req.Cert)
if err != nil {
return nil, err
}

cert := &biz.Cert{
Type: "upload",
Domains: info.DNSNames,
Cert: req.Cert,
Key: req.Key,
}
if err = app.Orm.Create(cert).Error; err != nil {
return nil, err
}

return cert, nil
}

func (r *certRepo) Create(req *request.CertCreate) (*biz.Cert, error) {
cert := &biz.Cert{
AccountID: req.AccountID,
Expand All @@ -59,12 +107,22 @@ func (r *certRepo) Create(req *request.CertCreate) (*biz.Cert, error) {
}

func (r *certRepo) Update(req *request.CertUpdate) error {
info, err := pkgcert.ParseCert(req.Cert)
if err != nil {
return err
}
if req.Type == "upload" {
req.Domains = info.DNSNames
}

return app.Orm.Model(&biz.Cert{}).Where("id = ?", req.ID).Select("*").Updates(&biz.Cert{
ID: req.ID,
AccountID: req.AccountID,
WebsiteID: req.WebsiteID,
DNSID: req.DNSID,
Type: req.Type,
Cert: req.Cert,
Key: req.Key,
Domains: req.Domains,
AutoRenew: req.AutoRenew,
}).Error
Expand Down
5 changes: 3 additions & 2 deletions internal/data/website.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"path/filepath"
"slices"
"strings"
"time"

"github.com/samber/lo"
"github.com/spf13/cast"
Expand Down Expand Up @@ -137,8 +138,8 @@ func (r *websiteRepo) Get(id uint) (*types.WebsiteSetting, error) {
setting.SSLCertificateKey = key
// 解析证书信息
if decode, err := cert.ParseCert(crt); err == nil {
setting.SSLNotBefore = decode.NotBefore.Format("2006-01-02 15:04:05")
setting.SSLNotAfter = decode.NotAfter.Format("2006-01-02 15:04:05")
setting.SSLNotBefore = decode.NotBefore.Format(time.DateTime)
setting.SSLNotAfter = decode.NotAfter.Format(time.DateTime)
setting.SSLIssuer = decode.Issuer.CommonName
setting.SSLOCSPServer = decode.OCSPServer
setting.SSLDNSNames = decode.DNSNames
Expand Down
9 changes: 8 additions & 1 deletion internal/http/request/cert.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package request

type CertUpload struct {
Cert string `form:"cert" json:"cert" validate:"required"`
Key string `form:"key" json:"key" validate:"required"`
}

type CertCreate struct {
Type string `form:"type" json:"type" validate:"required,oneof=P256 P384 2048 3072 4096"`
Domains []string `form:"domains" json:"domains" validate:"min=1,dive,required"`
Expand All @@ -11,8 +16,10 @@ type CertCreate struct {

type CertUpdate struct {
ID uint `form:"id" json:"id" validate:"required,exists=certs id"`
Type string `form:"type" json:"type" validate:"required,oneof=P256 P384 2048 3072 4096"`
Type string `form:"type" json:"type" validate:"required,oneof=upload P256 P384 2048 3072 4096"`
Domains []string `form:"domains" json:"domains" validate:"min=1,dive,required"`
Cert string `form:"cert" json:"cert" validate:"required"`
Key string `form:"key" json:"key" validate:"required"`
AutoRenew bool `form:"auto_renew" json:"auto_renew"`
AccountID uint `form:"account_id" json:"account_id"`
DNSID uint `form:"dns_id" json:"dns_id"`
Expand Down
2 changes: 1 addition & 1 deletion internal/job/cert_renew.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func (r *CertRenew) Run() {
}

for _, cert := range certs {
if !cert.AutoRenew {
if cert.Type == "upload" || !cert.AutoRenew {
continue
}

Expand Down
2 changes: 1 addition & 1 deletion internal/job/monitoring.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func (r *Monitoring) Run() {
if day <= 0 || app.Status != app.StatusNormal {
return
}
if err = app.Orm.Where("created_at < ?", time.Now().AddDate(0, 0, -day).Format("2006-01-02 15:04:05")).Delete(&biz.Monitor{}).Error; err != nil {
if err = app.Orm.Where("created_at < ?", time.Now().AddDate(0, 0, -day).Format(time.DateTime)).Delete(&biz.Monitor{}).Error; err != nil {
app.Logger.Error("删除过期系统监控失败", zap.Error(err))
return
}
Expand Down
1 change: 1 addition & 0 deletions internal/route/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ func Http(r chi.Router) {
r.Route("/cert", func(r chi.Router) {
r.Get("/", cert.List)
r.Post("/", cert.Create)
r.Post("/upload", cert.Upload)
r.Put("/{id}", cert.Update)
r.Get("/{id}", cert.Get)
r.Delete("/{id}", cert.Delete)
Expand Down
16 changes: 16 additions & 0 deletions internal/service/cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,22 @@ func (s *CertService) List(w http.ResponseWriter, r *http.Request) {
})
}

func (s *CertService) Upload(w http.ResponseWriter, r *http.Request) {
req, err := Bind[request.CertUpload](r)
if err != nil {
Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}

cert, err := s.certRepo.Upload(req)
if err != nil {
Error(w, http.StatusInternalServerError, "%v", err)
return
}

Success(w, cert)
}

func (s *CertService) Create(w http.ResponseWriter, r *http.Request) {
req, err := Bind[request.CertCreate](r)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion internal/service/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ func (s *CliService) UserList(ctx context.Context, cmd *cli.Command) error {
}

for _, user := range users {
fmt.Printf("ID: %d, 用户名: %s, 邮箱: %s, 创建日期: %s\n", user.ID, user.Username, user.Email, user.CreatedAt.Format("2006-01-02 15:04:05"))
fmt.Printf("ID: %d, 用户名: %s, 邮箱: %s, 创建日期: %s\n", user.ID, user.Username, user.Email, user.CreatedAt.Format(time.DateTime))
}

return nil
Expand Down
8 changes: 4 additions & 4 deletions pkg/ntp/ntp.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,13 @@ func Now(address ...string) (time.Time, error) {
return now, nil
}

func UpdateSystemTime(time time.Time) error {
_, err := shell.Execf(`date -s '%s'`, time.Format("2006-01-02 15:04:05"))
func UpdateSystemTime(t time.Time) error {
_, err := shell.Execf(`date -s '%s'`, t.Format(time.DateTime))
return err
}

func UpdateSystemTimeZone(timezone string) error {
_, err := shell.Execf(`timedatectl set-timezone '%s'`, timezone)
func UpdateSystemTimeZone(tz string) error {
_, err := shell.Execf(`timedatectl set-timezone '%s'`, tz)
return err
}

Expand Down
22 changes: 22 additions & 0 deletions pkg/types/cert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package types

import "time"

type CertList struct {
ID uint `json:"id"`
AccountID uint `json:"account_id"`
WebsiteID uint `json:"website_id"`
DNSID uint `json:"dns_id"`
Type string `json:"type"`
Domains []string `json:"domains"`
AutoRenew bool `json:"auto_renew"`
Cert string `json:"cert"`
Key string `json:"key"`
NotBefore time.Time `json:"not_before"`
NotAfter time.Time `json:"not_after"`
Issuer string `json:"issuer"`
OCSPServer []string `json:"ocsp_server"`
DNSNames []string `json:"dns_names"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
2 changes: 2 additions & 0 deletions web/src/api/panel/cert/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export default {
request.get('/cert/cert', { params: { page, limit } }),
// 证书详情
certInfo: (id: number): Promise<AxiosResponse<any>> => request.get(`/cert/cert/${id}`),
// 证书上传
certUpload: (data: any): Promise<AxiosResponse<any>> => request.post('/cert/cert/upload', data),
// 证书添加
certCreate: (data: any): Promise<AxiosResponse<any>> => request.post('/cert/cert', data),
// 证书更新
Expand Down
5 changes: 4 additions & 1 deletion web/src/styles/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ body {
height: 100%;
overflow: hidden;
background-color: #f2f2f2;
font-family: -apple-system, "Noto Sans", "Helvetica Neue", Helvetica, "Nimbus Sans L", Arial, "Liberation Sans", "PingFang SC", "Hiragino Sans GB", "Noto Sans CJK SC", "Noto Sans SC", "Source Han Sans SC", "Source Han Sans CN", "Microsoft YaHei", "Wenquanyi Micro Hei", "WenQuanYi Zen Hei", "ST Heiti", SimHei, "WenQuanYi Zen Hei Sharp", sans-serif;;
font-family: -apple-system, 'Noto Sans', 'Helvetica Neue', Helvetica, 'Nimbus Sans L', Arial,
'Liberation Sans', 'PingFang SC', 'Hiragino Sans GB', 'Noto Sans CJK SC', 'Noto Sans SC',
'Source Han Sans SC', 'Source Han Sans CN', 'Microsoft YaHei', 'Wenquanyi Micro Hei',
'WenQuanYi Zen Hei', 'ST Heiti', SimHei, 'WenQuanYi Zen Hei Sharp', sans-serif;
}

#app {
Expand Down
Loading

0 comments on commit 56ae7eb

Please sign in to comment.