added new file button in config editor, dockerfile fix

This commit is contained in:
yusing
2024-03-29 01:24:47 +00:00
parent 6bc4c1c49a
commit 9b34dc994d
28 changed files with 329 additions and 185 deletions

38
src/go-proxy/args.go Normal file
View File

@@ -0,0 +1,38 @@
package main
import (
"flag"
"github.com/sirupsen/logrus"
)
type Args struct {
Command string
}
const (
CommandStart = ""
CommandVerify = "verify"
CommandReload = "reload"
)
var ValidCommands = []string{CommandStart, CommandVerify, CommandReload}
func getArgs() Args {
var args Args
flag.Parse()
args.Command = flag.Arg(0)
if err := validateArgs(args.Command, ValidCommands); err != nil {
logrus.Fatal(err)
}
return args
}
func validateArgs[T comparable](arg T, validArgs []T) error {
for _, v := range validArgs {
if arg == v {
return nil
}
}
return NewNestedError("invalid argument").Subjectf("%v", arg)
}

View File

@@ -21,9 +21,9 @@ import (
"github.com/go-acme/lego/v4/registration"
)
type ProviderOptions = map[string]string
type ProviderGenerator = func(ProviderOptions) (challenge.Provider, error)
type CertExpiries = map[string]time.Time
type ProviderOptions map[string]string
type ProviderGenerator func(ProviderOptions) (challenge.Provider, error)
type CertExpiries map[string]time.Time
type AutoCertConfig struct {
Email string `json:"email"`

View File

@@ -3,12 +3,14 @@ package main
import (
"os"
"sync"
"time"
"gopkg.in/yaml.v3"
)
// commented out if unused
type Config interface {
Value() configModel
// Load() error
MustLoad()
GetAutoCertProvider() (AutoCertProvider, error)
@@ -21,7 +23,13 @@ type Config interface {
}
func NewConfig(path string) Config {
cfg := &config{reader: &FileReader{Path: path}}
cfg := &config{
m: &configModel{
TimeoutShutdown: 3 * time.Second,
RedirectToHTTPS: false,
},
reader: &FileReader{Path: path},
}
cfg.watcher = NewFileWatcher(
path,
cfg.MustReload, // OnChange
@@ -35,6 +43,10 @@ func ValidateConfig(data []byte) error {
return cfg.Load()
}
func (cfg *config) Value() configModel {
return *cfg.m
}
func (cfg *config) Load(reader ...Reader) error {
cfg.mutex.Lock()
defer cfg.mutex.Unlock()
@@ -170,12 +182,14 @@ func (cfg *config) StopWatching() {
}
type configModel struct {
Providers map[string]*Provider `yaml:",flow" json:"providers"`
AutoCert AutoCertConfig `yaml:",flow" json:"autocert"`
Providers map[string]*Provider `yaml:",flow" json:"providers"`
AutoCert AutoCertConfig `yaml:",flow" json:"autocert"`
TimeoutShutdown time.Duration `yaml:"timeout_shutdown" json:"timeout_shutdown"`
RedirectToHTTPS bool `yaml:"redirect_to_https" json:"redirect_to_https"`
}
type config struct {
m *configModel
m *configModel
reader Reader
watcher Watcher

View File

@@ -147,6 +147,4 @@ var logLevel = func() logrus.Level {
logrus.SetLevel(logrus.DebugLevel)
}
return logrus.GetLevel()
}()
var redirectToHTTPS = os.Getenv("GOPROXY_REDIRECT_HTTP") != "0" && os.Getenv("GOPROXY_REDIRECT_HTTP") != "false"
}()

View File

@@ -44,8 +44,8 @@ func NewHTTPRoute(config *ProxyConfig) (*HTTPRoute, error) {
PathMode: config.PathMode,
l: hrlog.WithFields(logrus.Fields{
"alias": config.Alias,
"path": config.Path,
"path_mode": config.PathMode,
// "path": config.Path,
// "path_mode": config.PathMode,
}),
}
@@ -157,6 +157,6 @@ func (config *ProxyConfig) pathSubModResp(r *http.Response) error {
}
// alias -> (path -> routes)
type HTTPRoutes = SafeMap[string, pathPoolMap]
type HTTPRoutes SafeMap[string, pathPoolMap]
var httpRoutes HTTPRoutes = NewSafeMapOf[HTTPRoutes](newPathPoolMap)

View File

@@ -1,12 +1,13 @@
package main
import (
"flag"
"net/http"
"os"
"os/signal"
"runtime"
"sync"
"syscall"
"time"
"github.com/sirupsen/logrus"
)
@@ -16,21 +17,27 @@ var cfg Config
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
var verifyOnly bool
flag.BoolVar(&verifyOnly, "verify", false, "verify config without starting server")
flag.Parse()
args := getArgs()
logrus.SetFormatter(&logrus.TextFormatter{
ForceColors: true,
DisableColors: false,
FullTimestamp: true,
ForceColors: true,
DisableColors: false,
FullTimestamp: true,
TimestampFormat: "01-02 15:04:05",
})
if args.Command == CommandReload {
err := utils.reloadServer()
if err != nil {
logrus.Fatal(err)
}
return
}
cfg = NewConfig(configPath)
cfg.MustLoad()
if verifyOnly {
if args.Command == CommandVerify {
logrus.Printf("config OK")
return
}
@@ -63,7 +70,7 @@ func main() {
HTTPAddr: ":80",
HTTPSAddr: ":443",
Handler: http.HandlerFunc(proxyHandler),
RedirectToHTTPS: redirectToHTTPS,
RedirectToHTTPS: cfg.Value().RedirectToHTTPS,
})
panelServer = NewServer(ServerOptions{
Name: "panel",
@@ -71,7 +78,7 @@ func main() {
HTTPAddr: ":8080",
HTTPSAddr: ":8443",
Handler: panelHandler,
RedirectToHTTPS: redirectToHTTPS,
RedirectToHTTPS: cfg.Value().RedirectToHTTPS,
})
proxyServer.Start()
@@ -88,10 +95,32 @@ func main() {
signal.Notify(sig, syscall.SIGHUP)
<-sig
// cfg.StopWatching()
StopFSWatcher()
StopDockerWatcher()
cfg.StopProviders()
panelServer.Stop()
proxyServer.Stop()
logrus.Info("shutting down")
done := make(chan struct{}, 1)
var wg sync.WaitGroup
wg.Add(3)
go func() {
StopFSWatcher()
StopDockerWatcher()
cfg.StopProviders()
wg.Done()
}()
go func() {
panelServer.Stop()
proxyServer.Stop()
wg.Done()
}()
go func() {
wg.Wait()
close(done)
}()
select {
case <-done:
logrus.Info("shutdown complete")
case <-time.After(cfg.Value().TimeoutShutdown * time.Second):
logrus.Info("timeout waiting for shutdown")
}
}

View File

@@ -2,6 +2,7 @@ package main
import (
"errors"
"fmt"
"html/template"
"net/http"
"net/url"
@@ -68,7 +69,7 @@ func panelCheckTargetHealth(w http.ResponseWriter, r *http.Request) {
func panelConfigEditor(w http.ResponseWriter, r *http.Request) {
cfgFiles := make([]string, 0)
cfgFiles = append(cfgFiles, path.Base(configPath))
for _, p := range cfg.(*config).m.Providers {
for _, p := range cfg.Value().Providers {
if p.Kind != ProviderKind_File {
continue
}
@@ -99,12 +100,20 @@ func panelConfigUpdate(w http.ResponseWriter, r *http.Request) {
panelHandleErr(w, r, err)
return
}
err = os.WriteFile(path.Join(configBasePath, p), content, 0644)
p = path.Join(configBasePath, p)
_, err = os.Stat(p)
exists := !errors.Is(err, os.ErrNotExist)
err = os.WriteFile(p, content, 0644)
if err != nil {
panelHandleErr(w, r, NewNestedError("unable to write config file").With(err))
return
}
w.WriteHeader(http.StatusOK)
if !exists {
w.Write([]byte(fmt.Sprintf("Config file %s created, remember to add it to config.yml!", p)))
return
}
w.Write([]byte(fmt.Sprintf("Config file %s updated", p)))
}
func panelServeFile(w http.ResponseWriter, r *http.Request) {
@@ -141,4 +150,4 @@ func panelHandleErr(w http.ResponseWriter, r *http.Request, err error, code ...i
return
}
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}

View File

@@ -15,8 +15,8 @@ type ProxyConfig struct {
provider *Provider
}
type ProxyConfigMap = map[string]ProxyConfig
type ProxyConfigSlice = []ProxyConfig
type ProxyConfigMap map[string]ProxyConfig
type ProxyConfigSlice []ProxyConfig
func NewProxyConfig(provider *Provider) ProxyConfig {
return ProxyConfig{

View File

@@ -47,6 +47,6 @@ func isStreamScheme(s string) bool {
}
// id -> target
type StreamRoutes = SafeMap[string, StreamRoute]
type StreamRoutes SafeMap[string, StreamRoute]
var streamRoutes StreamRoutes = NewSafeMapOf[StreamRoutes]()

View File

@@ -31,11 +31,11 @@ type ServerOptions struct {
}
type LogrusWrapper struct {
l *logrus.Entry
*logrus.Entry
}
func (l LogrusWrapper) Write(b []byte) (int, error) {
return l.l.Logger.WriterLevel(logrus.ErrorLevel).Write(b)
return l.Logger.WriterLevel(logrus.ErrorLevel).Write(b)
}
func NewServer(opt ServerOptions) *Server {

View File

@@ -45,10 +45,8 @@ type StreamRouteBase struct {
func newStreamRouteBase(config *ProxyConfig) (*StreamRouteBase, error) {
var streamType string = StreamType_TCP
var srcPort string
var dstPort string
var srcScheme string
var dstScheme string
var srcPort, dstPort string
var srcScheme, dstScheme string
portSplit := strings.Split(config.Port, ":")
if len(portSplit) != 2 {
@@ -101,8 +99,8 @@ func newStreamRouteBase(config *ProxyConfig) (*StreamRouteBase, error) {
started: false,
l: srlog.WithFields(logrus.Fields{
"alias": config.Alias,
"src": fmt.Sprintf("%s://:%d", srcScheme, srcPortInt),
"dst": fmt.Sprintf("%s://%s:%d", dstScheme, config.Host, dstPortInt),
// "src": fmt.Sprintf("%s://:%d", srcScheme, srcPortInt),
// "dst": fmt.Sprintf("%s://%s:%d", dstScheme, config.Host, dstPortInt),
}),
}, nil
}

View File

@@ -94,6 +94,18 @@ func (*Utils) healthCheckStream(scheme, host string) error {
return nil
}
func (*Utils) reloadServer() error {
resp, err := healthCheckHttpClient.Post("http://localhost:8080/reload", "", nil)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return NewNestedError("server reload failed").Subjectf("%d", resp.StatusCode)
}
return nil
}
func (*Utils) snakeToPascal(s string) string {
toHyphenCamel := http.CanonicalHeaderKey(strings.ReplaceAll(s, "_", "-"))
return strings.ReplaceAll(toHyphenCamel, "-", "")

View File

@@ -89,7 +89,7 @@ func (w *fileWatcher) Stop() {
fileWatchMap.Delete(w.path)
err := fsWatcher.Remove(w.path)
if err != nil {
w.l.WithField("action", "stop").Error(err)
w.l.Error(err)
}
}