refactor template handling

This commit is contained in:
Christoph Haas 2023-06-21 00:03:44 +02:00
parent 4c7f2b24bc
commit 7a0a3b1e9e
14 changed files with 129 additions and 25 deletions

View File

@ -5,6 +5,7 @@ import (
"github.com/h44z/wg-portal/internal/app/api/core"
handlersV0 "github.com/h44z/wg-portal/internal/app/api/v0/handlers"
"github.com/h44z/wg-portal/internal/app/auth"
"github.com/h44z/wg-portal/internal/app/filetemplate"
"github.com/h44z/wg-portal/internal/app/users"
"github.com/h44z/wg-portal/internal/app/wireguard"
"os"
@ -63,7 +64,12 @@ func main() {
statisticsCollector, err := wireguard.NewStatisticsCollector(cfg, database, wireGuard)
internal.AssertNoError(err)
backend, err := app.New(cfg, eventBus, authenticator, userManager, wireGuardManager, statisticsCollector)
templateManager, err := filetemplate.NewTemplateManager(cfg, database, database)
internal.AssertNoError(err)
backend, err := app.New(cfg, eventBus, authenticator, userManager, wireGuardManager,
statisticsCollector, templateManager)
internal.AssertNoError(err)
err = backend.Startup(ctx)
internal.AssertNoError(err)

View File

@ -201,7 +201,7 @@ func (r *SqlRepo) migrate() error {
func (r *SqlRepo) GetInterface(ctx context.Context, id domain.InterfaceIdentifier) (*domain.Interface, error) {
var in domain.Interface
err := r.db.WithContext(ctx).First(&in, id).Error
err := r.db.WithContext(ctx).Preload("Addresses").First(&in, id).Error
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
return nil, domain.ErrNotFound
@ -356,6 +356,17 @@ func (r *SqlRepo) GetInterfaceIps(ctx context.Context) (map[domain.InterfaceIden
// region peers
func (r *SqlRepo) GetPeer(ctx context.Context, id domain.PeerIdentifier) (*domain.Peer, error) {
var peer domain.Peer
err := r.db.WithContext(ctx).Where("identifier = ?", id).Find(&peer).Error
if err != nil {
return nil, err
}
return &peer, nil
}
func (r *SqlRepo) GetInterfacePeers(ctx context.Context, id domain.InterfaceIdentifier) ([]domain.Peer, error) {
var peers []domain.Peer

View File

@ -5,6 +5,7 @@ import (
"github.com/h44z/wg-portal/internal/app"
"github.com/h44z/wg-portal/internal/app/api/v0/model"
"github.com/h44z/wg-portal/internal/domain"
"io"
"net/http"
)
@ -129,17 +130,23 @@ func (e interfaceEndpoint) handleConfigGet() gin.HandlerFunc {
return
}
c.JSON(http.StatusOK, `[Interface]
Address = 10.0.0.1/32, fd12:3456:789a::1/128
ListenPort = 51820
PrivateKey = <Private Key>
SaveConfig = true
config, err := e.app.GetInterfaceConfig(c.Request.Context(), domain.InterfaceIdentifier(id))
if err != nil {
c.JSON(http.StatusInternalServerError, model.Error{
Code: http.StatusInternalServerError, Message: err.Error(),
})
return
}
[Peer]
PublicKey = <Client public key>
PresharedKey = <Pre-Shared Key>
AllowedIPs = 10.0.0.2/32,fd12:3456:789a::2/128
PersistentKeepalive = 25`)
configString, err := io.ReadAll(config)
if err != nil {
c.JSON(http.StatusInternalServerError, model.Error{
Code: http.StatusInternalServerError, Message: err.Error(),
})
return
}
c.JSON(http.StatusOK, string(configString))
}
}

View File

@ -19,9 +19,10 @@ type App struct {
UserManager
WireGuardManager
StatisticsCollector
TemplateManager
}
func New(cfg *config.Config, bus evbus.MessageBus, authenticator Authenticator, users UserManager, wireGuard WireGuardManager, stats StatisticsCollector) (*App, error) {
func New(cfg *config.Config, bus evbus.MessageBus, authenticator Authenticator, users UserManager, wireGuard WireGuardManager, stats StatisticsCollector, templates TemplateManager) (*App, error) {
a := &App{
Config: cfg,
@ -31,6 +32,7 @@ func New(cfg *config.Config, bus evbus.MessageBus, authenticator Authenticator,
UserManager: users,
WireGuardManager: wireGuard,
StatisticsCollector: stats,
TemplateManager: templates,
}
startupContext, cancel := context.WithTimeout(context.Background(), 30*time.Second)

View File

@ -0,0 +1,52 @@
package filetemplate
import (
"context"
"fmt"
"github.com/h44z/wg-portal/internal/config"
"github.com/h44z/wg-portal/internal/domain"
"io"
)
type Manager struct {
cfg *config.Config
tplHandler *TemplateHandler
users UserDatabaseRepo
wg WireguardDatabaseRepo
}
func NewTemplateManager(cfg *config.Config, users UserDatabaseRepo, wg WireguardDatabaseRepo) (*Manager, error) {
tplHandler, err := newTemplateHandler()
if err != nil {
return nil, fmt.Errorf("failed to initialize template handler: %w", err)
}
m := &Manager{
cfg: cfg,
tplHandler: tplHandler,
users: users,
wg: wg,
}
return m, nil
}
func (m Manager) GetInterfaceConfig(ctx context.Context, id domain.InterfaceIdentifier) (io.Reader, error) {
iface, peers, err := m.wg.GetInterfaceAndPeers(ctx, id)
if err != nil {
return nil, fmt.Errorf("failed to fetch interface %s: %w", id, err)
}
return m.tplHandler.GetInterfaceConfig(iface, peers)
}
func (m Manager) GetPeerConfig(ctx context.Context, id domain.PeerIdentifier) (io.Reader, error) {
peer, err := m.wg.GetPeer(ctx, id)
if err != nil {
return nil, fmt.Errorf("failed to fetch peer %s: %w", id, err)
}
return m.tplHandler.GetPeerConfig(peer)
}

View File

@ -0,0 +1,16 @@
package filetemplate
import (
"context"
"github.com/h44z/wg-portal/internal/domain"
)
type UserDatabaseRepo interface {
GetUser(ctx context.Context, id domain.UserIdentifier) (*domain.User, error)
}
type WireguardDatabaseRepo interface {
GetInterfaceAndPeers(ctx context.Context, id domain.InterfaceIdentifier) (*domain.Interface, []domain.Peer, error)
GetPeer(ctx context.Context, id domain.PeerIdentifier) (*domain.Peer, error)
GetInterface(ctx context.Context, id domain.InterfaceIdentifier) (*domain.Interface, error)
}

View File

@ -1,4 +1,4 @@
package app
package filetemplate
import (
"bytes"
@ -14,30 +14,34 @@ import (
//go:embed tpl_files/*
var TemplateFiles embed.FS
type templateHandler struct {
type TemplateHandler struct {
wireGuardTemplates *template.Template
mailHtmlTemplates *htmlTemplate.Template
mailTextTemplates *template.Template
}
func newTemplateHandler() (*templateHandler, error) {
templateCache, err := template.New("WireGuard").ParseFS(TemplateFiles, "tpl_files/*.tpl")
func newTemplateHandler() (*TemplateHandler, error) {
tplFuncs := template.FuncMap{
"CidrsToString": domain.CidrsToString,
}
templateCache, err := template.New("WireGuard").Funcs(tplFuncs).ParseFS(TemplateFiles, "tpl_files/*.tpl")
if err != nil {
return nil, err
}
mailHtmlTemplateCache, err := htmlTemplate.New("WireGuard").ParseFS(TemplateFiles, "tpl_files/*.gohtml")
mailHtmlTemplateCache, err := htmlTemplate.New("WireGuard").Funcs(tplFuncs).ParseFS(TemplateFiles, "tpl_files/*.gohtml")
if err != nil {
return nil, fmt.Errorf("failed to parse html template files: %w", err)
}
mailTxtTemplateCache, err := template.New("WireGuard").ParseFS(TemplateFiles, "tpl_files/*.gotpl")
mailTxtTemplateCache, err := template.New("WireGuard").Funcs(tplFuncs).ParseFS(TemplateFiles, "tpl_files/*.gotpl")
if err != nil {
return nil, fmt.Errorf("failed to parse text template files: %w", err)
}
handler := &templateHandler{
handler := &TemplateHandler{
wireGuardTemplates: templateCache,
mailHtmlTemplates: mailHtmlTemplateCache,
mailTextTemplates: mailTxtTemplateCache,
@ -46,7 +50,7 @@ func newTemplateHandler() (*templateHandler, error) {
return handler, nil
}
func (c templateHandler) GetInterfaceConfig(cfg *domain.Interface, peers []*domain.Peer) (io.Reader, error) {
func (c TemplateHandler) GetInterfaceConfig(cfg *domain.Interface, peers []domain.Peer) (io.Reader, error) {
var tplBuff bytes.Buffer
err := c.wireGuardTemplates.ExecuteTemplate(&tplBuff, "wg_interface.tpl", map[string]interface{}{
@ -63,7 +67,7 @@ func (c templateHandler) GetInterfaceConfig(cfg *domain.Interface, peers []*doma
return &tplBuff, nil
}
func (c templateHandler) GetPeerConfig(peer *domain.Peer) (io.Reader, error) {
func (c TemplateHandler) GetPeerConfig(peer *domain.Peer) (io.Reader, error) {
var tplBuff bytes.Buffer
err := c.wireGuardTemplates.ExecuteTemplate(&tplBuff, "wg_peer.tpl", map[string]interface{}{
@ -79,7 +83,7 @@ func (c templateHandler) GetPeerConfig(peer *domain.Peer) (io.Reader, error) {
return &tplBuff, nil
}
func (c templateHandler) GetConfigMail(user *domain.User, peer *domain.Peer, link string) (io.Reader, io.Reader, error) {
func (c TemplateHandler) GetConfigMail(user *domain.User, peer *domain.Peer, link string) (io.Reader, io.Reader, error) {
var tplBuff bytes.Buffer
var htmlTplBuff bytes.Buffer
@ -104,7 +108,7 @@ func (c templateHandler) GetConfigMail(user *domain.User, peer *domain.Peer, lin
return &tplBuff, &htmlTplBuff, nil
}
func (c templateHandler) GetConfigMailWithAttachment(user *domain.User, peer *domain.Peer) (io.Reader, io.Reader, error) {
func (c TemplateHandler) GetConfigMailWithAttachment(user *domain.User, peer *domain.Peer) (io.Reader, io.Reader, error) {
var tplBuff bytes.Buffer
var htmlTplBuff bytes.Buffer

View File

@ -12,7 +12,7 @@
# Core settings
PrivateKey = {{ .Interface.KeyPair.PrivateKey }}
Address = {{ .Interface.AddressStr }}
Address = {{ CidrsToString .Interface.Addresses }}
# Misc. settings (optional)
{{- if ne .Interface.ListenPort 0}}

View File

@ -3,6 +3,7 @@ package app
import (
"context"
"github.com/h44z/wg-portal/internal/domain"
"io"
)
type Authenticator interface {
@ -41,3 +42,8 @@ type WireGuardManager interface {
type StatisticsCollector interface {
StartBackgroundJobs(ctx context.Context)
}
type TemplateManager interface {
GetInterfaceConfig(ctx context.Context, id domain.InterfaceIdentifier) (io.Reader, error)
GetPeerConfig(ctx context.Context, id domain.PeerIdentifier) (io.Reader, error)
}