V2 alpha - initial version (#172)

Initial alpha codebase for version 2 of WireGuard Portal.
This version is considered unstable and incomplete (for example, no public REST API)! 
Use with care!


Fixes/Implements the following issues:
 - OAuth support #154, #1 
 - New Web UI with internationalisation support #98, #107, #89, #62
 - Postgres Support #49 
 - Improved Email handling #47, #119 
 - DNS Search Domain support #46 
 - Bugfixes #94, #48 

---------

Co-authored-by: Fabian Wechselberger <wechselbergerf@hotmail.com>
This commit is contained in:
h44z
2023-08-04 13:34:18 +02:00
committed by GitHub
parent b3a5f2ac60
commit 8b820a5adf
788 changed files with 46139 additions and 11281 deletions

View File

@@ -0,0 +1,70 @@
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"strings"
"github.com/sirupsen/logrus"
"github.com/swaggo/swag"
"github.com/swaggo/swag/gen"
)
// this replaces the call to: swag init --propertyStrategy pascalcase --parseDependency --parseInternal --generalInfo base.go
func main() {
wd, err := os.Getwd() // should be the project root
if err != nil {
panic(err)
}
apiBasePath := filepath.Join(wd, "/internal/app/api")
apis := []string{"v0"}
hasError := false
for _, apiVersion := range apis {
apiPath := filepath.Join(apiBasePath, apiVersion, "handlers")
apiVersion = strings.TrimLeft(apiVersion, "api-")
log.Println("")
log.Println("Generate swagger docs for API", apiVersion)
log.Println("Api path:", apiPath)
err := generateApi(apiBasePath, apiPath, apiVersion)
if err != nil {
hasError = true
logrus.Errorf("failed to generate API docs for %s: %v", apiVersion, err)
}
log.Println("Generated swagger docs for API", apiVersion)
}
if hasError {
os.Exit(1)
}
}
func generateApi(basePath, apiPath, version string) error {
err := gen.New().Build(&gen.Config{
SearchDir: apiPath,
Excludes: "",
MainAPIFile: "base.go",
PropNamingStrategy: swag.PascalCase,
OutputDir: filepath.Join(basePath, "core/assets/doc"),
OutputTypes: []string{"json", "yaml"},
ParseVendor: false,
ParseDependency: true,
MarkdownFilesDir: "",
ParseInternal: true,
GeneratedTime: false,
CodeExampleFilesDir: "",
ParseDepth: 3,
InstanceName: version,
})
if err != nil {
return fmt.Errorf("swag failed: %w", err)
}
return nil
}

View File

@@ -1,35 +0,0 @@
// source taken from https://git.prolicht.digital/golib/healthcheck/-/blob/master/cmd/hc/main.go
package main
import (
"net/http"
"os"
"time"
)
// main checks the given URL, if the response is not 200, it will return with exit code 1
// on success, exit code 0 will be returned
func main() {
os.Exit(checkWebEndpointFromArgs())
}
func checkWebEndpointFromArgs() int {
if len(os.Args) < 2 {
return 1
}
if status := checkWebEndpoint(os.Args[1]); !status {
return 1
}
return 0
}
func checkWebEndpoint(url string) bool {
client := &http.Client{
Timeout: time.Second * 2,
}
if resp, err := client.Get(url); err != nil || resp.StatusCode < 200 || resp.StatusCode > 299 {
return false
}
return true
}

View File

@@ -2,103 +2,144 @@ package main
import (
"context"
"io"
"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/audit"
"github.com/h44z/wg-portal/internal/app/auth"
"github.com/h44z/wg-portal/internal/app/configfile"
"github.com/h44z/wg-portal/internal/app/mail"
"github.com/h44z/wg-portal/internal/app/route"
"github.com/h44z/wg-portal/internal/app/users"
"github.com/h44z/wg-portal/internal/app/wireguard"
"os"
"os/signal"
"runtime"
"strings"
"syscall"
"time"
"git.prolicht.digital/golib/healthcheck"
"github.com/h44z/wg-portal/internal/server"
"github.com/h44z/wg-portal/internal"
"github.com/h44z/wg-portal/internal/adapters"
"github.com/h44z/wg-portal/internal/app"
"github.com/h44z/wg-portal/internal/config"
"github.com/sirupsen/logrus"
evbus "github.com/vardius/message-bus"
)
// main entry point for WireGuard Portal
func main() {
_ = setupLogger(logrus.StandardLogger())
ctx := internal.SignalAwareContext(context.Background(), syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
logrus.Infof("Starting WireGuard Portal V2...")
logrus.Infof("WireGuard Portal version: %s", internal.Version)
logrus.Infof("sysinfo: os=%s, arch=%s", runtime.GOOS, runtime.GOARCH)
logrus.Infof("starting WireGuard Portal Server [%s]...", server.Version)
cfg, err := config.GetConfig()
internal.AssertNoError(err)
setupLogging(cfg)
// Context for clean shutdown
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
cfg.LogStartupValues()
// start health check service on port 11223
healthcheck.New(healthcheck.ListenOn("127.0.0.1:11223")).StartWithContext(ctx)
rawDb, err := adapters.NewDatabase(cfg.Database)
internal.AssertNoError(err)
service := server.Server{}
if err := service.Setup(ctx); err != nil {
logrus.Fatalf("setup failed: %v", err)
database, err := adapters.NewSqlRepository(rawDb)
internal.AssertNoError(err)
wireGuard := adapters.NewWireGuardRepository()
wgQuick := adapters.NewWgQuickRepo()
mailer := adapters.NewSmtpMailRepo(cfg.Mail)
cfgFileSystem, err := adapters.NewFileSystemRepository(cfg.Advanced.ConfigStoragePath)
internal.AssertNoError(err)
shouldExit, err := app.HandleProgramArgs(cfg, rawDb)
switch {
case shouldExit && err == nil:
return
case shouldExit && err != nil:
logrus.Errorf("Failed to process program args: %v", err)
os.Exit(1)
case !shouldExit:
internal.AssertNoError(err)
}
// Attach signal handlers to context
go func() {
osCall := <-c
logrus.Tracef("received system call: %v", osCall)
cancel() // cancel the context
}()
queueSize := 100
eventBus := evbus.New(queueSize)
// Start main process in background
go service.Run()
userManager, err := users.NewUserManager(cfg, eventBus, database, database)
internal.AssertNoError(err)
<-ctx.Done() // Wait until the context gets canceled
authenticator, err := auth.NewAuthenticator(&cfg.Auth, eventBus, userManager)
internal.AssertNoError(err)
// Give goroutines some time to stop gracefully
logrus.Info("stopping WireGuard Portal Server...")
time.Sleep(2 * time.Second)
wireGuardManager, err := wireguard.NewWireGuardManager(cfg, eventBus, wireGuard, wgQuick, database)
internal.AssertNoError(err)
logrus.Infof("stopped WireGuard Portal Server...")
logrus.Exit(0)
statisticsCollector, err := wireguard.NewStatisticsCollector(cfg, database, wireGuard)
internal.AssertNoError(err)
cfgFileManager, err := configfile.NewConfigFileManager(cfg, eventBus, database, database, cfgFileSystem)
internal.AssertNoError(err)
mailManager, err := mail.NewMailManager(cfg, mailer, cfgFileManager, database, database)
internal.AssertNoError(err)
auditRecorder, err := audit.NewAuditRecorder(cfg, eventBus, database)
internal.AssertNoError(err)
auditRecorder.StartBackgroundJobs(ctx)
routeManager, err := route.NewRouteManager(cfg, eventBus, database)
internal.AssertNoError(err)
routeManager.StartBackgroundJobs(ctx)
backend, err := app.New(cfg, eventBus, authenticator, userManager, wireGuardManager,
statisticsCollector, cfgFileManager, mailManager)
internal.AssertNoError(err)
err = backend.Startup(ctx)
internal.AssertNoError(err)
apiFrontend := handlersV0.NewRestApi(cfg, backend)
webSrv, err := core.NewServer(cfg, apiFrontend)
internal.AssertNoError(err)
go webSrv.Run(ctx, cfg.Web.ListeningAddress)
// wait until context gets cancelled
<-ctx.Done()
logrus.Infof("Stopping WireGuard Portal")
time.Sleep(5 * time.Second) // wait for (most) goroutines to finish gracefully
logrus.Infof("Stopped WireGuard Portal")
}
func setupLogger(logger *logrus.Logger) error {
// Check environment variables for logrus settings
level, ok := os.LookupEnv("LOG_LEVEL")
if !ok {
level = "debug" // Default logrus level
}
useJSON, ok := os.LookupEnv("LOG_JSON")
if !ok {
useJSON = "false" // Default use human readable logging
}
useColor, ok := os.LookupEnv("LOG_COLOR")
if !ok {
useColor = "true"
}
switch level {
case "off":
logger.SetOutput(io.Discard)
case "info":
logger.SetLevel(logrus.InfoLevel)
case "debug":
logger.SetLevel(logrus.DebugLevel)
func setupLogging(cfg *config.Config) {
switch strings.ToLower(cfg.Advanced.LogLevel) {
case "trace":
logger.SetLevel(logrus.TraceLevel)
logrus.SetLevel(logrus.TraceLevel)
case "debug":
logrus.SetLevel(logrus.DebugLevel)
case "info", "information":
logrus.SetLevel(logrus.InfoLevel)
case "warn", "warning":
logrus.SetLevel(logrus.WarnLevel)
case "error":
logrus.SetLevel(logrus.ErrorLevel)
default:
logrus.SetLevel(logrus.WarnLevel)
}
var formatter logrus.Formatter
if useJSON == "false" {
f := new(logrus.TextFormatter)
f.TimestampFormat = "2006-01-02 15:04:05"
f.FullTimestamp = true
if useColor == "true" {
f.ForceColors = true
}
formatter = f
} else {
f := new(logrus.JSONFormatter)
f.TimestampFormat = "2006-01-02 15:04:05"
formatter = f
switch {
case cfg.Advanced.LogJson:
logrus.SetFormatter(&logrus.JSONFormatter{
PrettyPrint: cfg.Advanced.LogPretty,
})
case cfg.Advanced.LogPretty:
logrus.SetFormatter(&logrus.TextFormatter{
ForceColors: true,
DisableColors: false,
})
}
logger.SetFormatter(formatter)
return nil
}