Skip to content

Commit 3db7dbc

Browse files
authored
Merge pull request #967 from gotify/env
Rework config parsing
2 parents 67b6589 + 14a8368 commit 3db7dbc

25 files changed

Lines changed: 1289 additions & 315 deletions

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ coverage.txt
99
**/*-packr.go
1010
config.yml
1111
data/
12-
images/
12+
images/
13+
/gotify-server.env

api/oidc.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,6 @@ import (
2323
)
2424

2525
func NewOIDC(conf *config.Configuration, db *database.GormDatabase, userChangeNotifier *UserChangeNotifier) *OIDCAPI {
26-
scopes := conf.OIDC.Scopes
27-
if len(scopes) == 0 {
28-
scopes = []string{"openid", "profile", "email"}
29-
}
30-
3126
cookieKey := make([]byte, 32)
3227
if _, err := rand.Read(cookieKey); err != nil {
3328
log.Fatal().Err(err).Msg("failed to generate OIDC cookie key")
@@ -46,7 +41,7 @@ func NewOIDC(conf *config.Configuration, db *database.GormDatabase, userChangeNo
4641
conf.OIDC.ClientID,
4742
conf.OIDC.ClientSecret,
4843
conf.OIDC.RedirectURL,
49-
scopes,
44+
conf.OIDC.Scopes,
5045
opts...,
5146
)
5247
if err != nil {

api/stream/stream.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"github.com/gin-gonic/gin"
1212
"github.com/gorilla/websocket"
1313
"github.com/gotify/server/v2/auth"
14-
"github.com/gotify/server/v2/mode"
1514
"github.com/gotify/server/v2/model"
1615
)
1716

@@ -214,9 +213,6 @@ func newUpgrader(allowedWebSocketOrigins []string) *websocket.Upgrader {
214213
ReadBufferSize: 1024,
215214
WriteBufferSize: 1024,
216215
CheckOrigin: func(r *http.Request) bool {
217-
if mode.IsDev() {
218-
return true
219-
}
220216
return isAllowedOrigin(r, compiledAllowedOrigins)
221217
},
222218
}

app.go

Lines changed: 83 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
package main
22

33
import (
4+
"errors"
5+
"flag"
6+
"fmt"
7+
"io"
48
"os"
9+
"runtime/debug"
510
"time"
611

712
"github.com/gotify/server/v2/config"
13+
"github.com/gotify/server/v2/config/migrate"
814
"github.com/gotify/server/v2/database"
915
"github.com/gotify/server/v2/mode"
1016
"github.com/gotify/server/v2/model"
@@ -27,26 +33,83 @@ var (
2733
)
2834

2935
func main() {
30-
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339, NoColor: noColor()})
36+
os.Exit(run(os.Args[1:], os.Stdout, os.Stderr))
37+
}
3138

39+
func run(args []string, stdout, stderr io.Writer) int {
3240
vInfo := &model.VersionInfo{Version: Version, Commit: Commit, BuildDate: BuildDate}
41+
fs := flag.NewFlagSet("gotify", flag.ContinueOnError)
42+
fs.SetOutput(stderr)
43+
fs.Usage = func() { printUsage(stderr) }
44+
if err := fs.Parse(args); err != nil {
45+
if errors.Is(err, flag.ErrHelp) {
46+
return 0
47+
}
48+
return 2
49+
}
50+
51+
command := fs.Arg(0)
52+
switch command {
53+
case "serve", "":
54+
return serve(vInfo)
55+
case "version":
56+
fmt.Fprintln(stdout, "Version:", vInfo.Version)
57+
fmt.Fprintln(stdout, "Commit:", vInfo.Commit)
58+
fmt.Fprintln(stdout, "Build Date:", vInfo.BuildDate)
59+
fmt.Fprintln(stdout, "Go Build Info:")
60+
b, ok := debug.ReadBuildInfo()
61+
if ok {
62+
fmt.Fprintln(stdout, b)
63+
}
64+
return 0
65+
case "migrate-config":
66+
content, err := migrate.Config(fs.Arg(1))
67+
if err != nil {
68+
fmt.Fprintln(stderr, err)
69+
return 1
70+
}
71+
fmt.Fprintln(stdout, content)
72+
return 0
73+
default:
74+
if command != "" {
75+
fmt.Fprintf(stderr, "gotify: unknown command %q\n\n", command)
76+
}
77+
printUsage(stderr)
78+
return 2
79+
}
80+
}
81+
82+
func serve(vInfo *model.VersionInfo) int {
3383
mode.Set(Mode)
3484

85+
conf, futureLogs := config.Get()
86+
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339, NoColor: noColor(conf.NoColor)}).Level(zerolog.Level(conf.LogLevel))
3587
log.Info().Str("version", vInfo.Version).Str("build_date", BuildDate).Msg("Gotify")
36-
conf := config.Get()
88+
89+
exit := false
90+
for _, futureLog := range futureLogs {
91+
log.WithLevel(futureLog.Level).Msg(futureLog.Msg)
92+
exit = exit || futureLog.Level == zerolog.FatalLevel || futureLog.Level == zerolog.PanicLevel
93+
}
94+
if exit {
95+
return 1
96+
}
3797

3898
if conf.PluginsDir != "" {
3999
if err := os.MkdirAll(conf.PluginsDir, 0o755); err != nil {
40-
panic(err)
100+
log.Error().Err(err).Str("dir", conf.PluginsDir).Msg("Cannot create plugins directory")
101+
return 1
41102
}
42103
}
43104
if err := os.MkdirAll(conf.UploadedImagesDir, 0o755); err != nil {
44-
panic(err)
105+
log.Error().Err(err).Str("dir", conf.UploadedImagesDir).Msg("Cannot create uploaded images directory")
106+
return 1
45107
}
46108

47109
db, err := database.New(conf.Database.Dialect, conf.Database.Connection, conf.DefaultUser.Name, conf.DefaultUser.Pass, conf.PassStrength, true, time.Now)
48110
if err != nil {
49-
panic(err)
111+
log.Error().Err(err).Msg("Cannot initialize database")
112+
return 1
50113
}
51114
defer db.Close()
52115

@@ -55,13 +118,25 @@ func main() {
55118

56119
if err := runner.Run(engine, conf); err != nil {
57120
log.Error().Err(err).Msg("Server error")
58-
os.Exit(1)
121+
return 1
59122
}
123+
return 0
124+
}
125+
126+
func printUsage(w io.Writer) {
127+
fmt.Fprint(w, `Usage: gotify [flags] <command> [arguments]
128+
129+
Commands:
130+
serve Start the Gotify server.
131+
migrate-config <file.yml> Convert an old YAML config file to the new env
132+
format and print it to stdout.
133+
version Show version information
134+
`)
60135
}
61136

62-
func noColor() bool {
137+
func noColor(noColorEnv string) bool {
63138
// https://no-color.org/
64-
if os.Getenv("NO_COLOR") == "1" {
139+
if noColorEnv == "1" {
65140
return true
66141
}
67142

app_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func TestRun(t *testing.T) {
11+
cases := []struct {
12+
name string
13+
args []string
14+
wantCode int
15+
stdout string // substring expected on stdout
16+
stderr string // substring expected on stderr
17+
}{
18+
{"version", []string{"version"}, 0, "Version: ", ""},
19+
{"unknown command", []string{"bogus"}, 2, "", "unknown command"},
20+
{"unknown flag", []string{"--nope"}, 2, "", "not defined"},
21+
}
22+
for _, c := range cases {
23+
t.Run(c.name, func(t *testing.T) {
24+
var stdout, stderr bytes.Buffer
25+
code := run(c.args, &stdout, &stderr)
26+
assert.Equal(t, c.wantCode, code)
27+
assert.Contains(t, stdout.String(), c.stdout)
28+
assert.Contains(t, stderr.String(), c.stderr)
29+
})
30+
}
31+
}

auth/cors.go

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77

88
"github.com/gin-contrib/cors"
99
"github.com/gotify/server/v2/config"
10-
"github.com/gotify/server/v2/mode"
1110
)
1211

1312
// CorsConfig generates a config to use in gin cors middleware based on server configuration.
@@ -16,28 +15,19 @@ func CorsConfig(conf *config.Configuration) cors.Config {
1615
MaxAge: 12 * time.Hour,
1716
AllowBrowserExtensions: true,
1817
}
19-
if mode.IsDev() {
20-
corsConf.AllowAllOrigins = true
21-
corsConf.AllowMethods = []string{"GET", "POST", "DELETE", "OPTIONS", "PUT"}
22-
corsConf.AllowHeaders = []string{
23-
"X-Gotify-Key", "Authorization", "Content-Type", "Upgrade", "Origin",
24-
"Connection", "Accept-Encoding", "Accept-Language", "Host",
25-
}
26-
} else {
27-
compiledOrigins := compileAllowedCORSOrigins(conf.Server.Cors.AllowOrigins)
28-
corsConf.AllowMethods = conf.Server.Cors.AllowMethods
29-
corsConf.AllowHeaders = conf.Server.Cors.AllowHeaders
30-
corsConf.AllowOriginFunc = func(origin string) bool {
31-
for _, compiledOrigin := range compiledOrigins {
32-
if compiledOrigin.MatchString(strings.ToLower(origin)) {
33-
return true
34-
}
18+
compiledOrigins := compileAllowedCORSOrigins(conf.Server.Cors.AllowOrigins)
19+
corsConf.AllowMethods = conf.Server.Cors.AllowMethods
20+
corsConf.AllowHeaders = conf.Server.Cors.AllowHeaders
21+
corsConf.AllowOriginFunc = func(origin string) bool {
22+
for _, compiledOrigin := range compiledOrigins {
23+
if compiledOrigin.MatchString(strings.ToLower(origin)) {
24+
return true
3525
}
36-
return false
37-
}
38-
if allowedOrigin := headerIgnoreCase(conf, "access-control-allow-origin"); allowedOrigin != "" && len(compiledOrigins) == 0 {
39-
corsConf.AllowOrigins = append(corsConf.AllowOrigins, allowedOrigin)
4026
}
27+
return false
28+
}
29+
if allowedOrigin := headerIgnoreCase(conf, "access-control-allow-origin"); allowedOrigin != "" && len(compiledOrigins) == 0 {
30+
corsConf.AllowOrigins = append(corsConf.AllowOrigins, allowedOrigin)
4131
}
4232

4333
return corsConf

auth/cors_test.go

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -50,24 +50,3 @@ func TestEmptyCorsConfigWithResponseHeaders(t *testing.T) {
5050
AllowBrowserExtensions: true,
5151
}, actual)
5252
}
53-
54-
func TestDevCorsConfig(t *testing.T) {
55-
mode.Set(mode.Dev)
56-
serverConf := config.Configuration{}
57-
serverConf.Server.Cors.AllowOrigins = []string{"http://test.com"}
58-
serverConf.Server.Cors.AllowHeaders = []string{"content-type"}
59-
serverConf.Server.Cors.AllowMethods = []string{"GET"}
60-
61-
actual := CorsConfig(&serverConf)
62-
63-
assert.Equal(t, cors.Config{
64-
AllowHeaders: []string{
65-
"X-Gotify-Key", "Authorization", "Content-Type", "Upgrade", "Origin",
66-
"Connection", "Accept-Encoding", "Accept-Language", "Host",
67-
},
68-
AllowMethods: []string{"GET", "POST", "DELETE", "OPTIONS", "PUT"},
69-
MaxAge: 12 * time.Hour,
70-
AllowAllOrigins: true,
71-
AllowBrowserExtensions: true,
72-
}, actual)
73-
}

config.example.yml

Lines changed: 0 additions & 68 deletions
This file was deleted.

0 commit comments

Comments
 (0)