···
1
1
+
root = "."
2
2
+
testdata_dir = "testdata"
3
3
+
tmp_dir = "tmp"
4
4
+
5
5
+
[build]
6
6
+
args_bin = []
7
7
+
bin = "./tmp/puzzlemethis"
8
8
+
cmd = "go build -o ./tmp/puzzlemethis ./cmd/puzzlemethis/"
9
9
+
delay = 500
10
10
+
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
11
11
+
exclude_file = []
12
12
+
exclude_regex = ["_test.go"]
13
13
+
exclude_unchanged = false
14
14
+
follow_symlink = false
15
15
+
full_bin = ""
16
16
+
include_dir = []
17
17
+
include_ext = ["go", "tpl", "tmpl", "html", "gohtml", "css", "js"]
18
18
+
include_file = []
19
19
+
kill_delay = "0s"
20
20
+
log = "build-errors.log"
21
21
+
poll = false
22
22
+
poll_interval = 0
23
23
+
post_cmd = []
24
24
+
pre_cmd = []
25
25
+
rerun = false
26
26
+
rerun_delay = 500
27
27
+
send_interrupt = false
28
28
+
stop_on_error = true
29
29
+
30
30
+
[color]
31
31
+
app = ""
32
32
+
build = "yellow"
33
33
+
main = "magenta"
34
34
+
runner = "green"
35
35
+
watcher = "cyan"
36
36
+
37
37
+
[log]
38
38
+
main_only = false
39
39
+
silent = false
40
40
+
time = false
41
41
+
42
42
+
[misc]
43
43
+
clean_on_exit = false
44
44
+
45
45
+
[proxy]
46
46
+
app_port = 0
47
47
+
enabled = false
48
48
+
proxy_port = 0
49
49
+
50
50
+
[screen]
51
51
+
clear_on_rebuild = true
52
52
+
keep_scroll = true
···
4
4
puzzles.db-wal
5
5
puzzles.db-shm
6
6
puzzles.db-journal
7
7
+
tmp
···
1
1
+
package main
2
2
+
3
3
+
import (
4
4
+
"context"
5
5
+
"log/slog"
6
6
+
"os"
7
7
+
"os/signal"
8
8
+
"sync"
9
9
+
"syscall"
10
10
+
11
11
+
"github.com/spf13/viper"
12
12
+
"tangled.org/pds.dad/PuzzleMeThis/internal"
13
13
+
"tangled.org/pds.dad/PuzzleMeThis/internal/db"
14
14
+
"tangled.org/pds.dad/PuzzleMeThis/internal/server"
15
15
+
)
16
16
+
17
17
+
func main() {
18
18
+
internal.LoadConfig()
19
19
+
20
20
+
gdb, err := db.Open(viper.GetString("database.url"))
21
21
+
if err != nil {
22
22
+
slog.Error("failed to open database", "err", err)
23
23
+
os.Exit(1)
24
24
+
}
25
25
+
26
26
+
if err := db.Migrate(gdb); err != nil {
27
27
+
slog.Error("failed to migrate database", "err", err)
28
28
+
os.Exit(1)
29
29
+
}
30
30
+
31
31
+
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
32
32
+
defer stop()
33
33
+
34
34
+
var wg sync.WaitGroup
35
35
+
36
36
+
wg.Go(func() {
37
37
+
defer stop()
38
38
+
server.RunHTTPServer(ctx, gdb)
39
39
+
})
40
40
+
41
41
+
wg.Wait()
42
42
+
slog.Info("bye")
43
43
+
44
44
+
}
···
1
1
-
module tangled.org/pds.dad/LichessPuzzles
1
1
+
module tangled.org/pds.dad/PuzzleMeThis
2
2
3
3
go 1.25.5
4
4
···
11
11
)
12
12
13
13
require (
14
14
+
github.com/fsnotify/fsnotify v1.9.0 // indirect
15
15
+
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
16
16
+
github.com/joho/godotenv v1.5.1 // indirect
17
17
+
github.com/labstack/gommon v0.5.0 // indirect
18
18
+
github.com/mattn/go-colorable v0.1.15 // indirect
19
19
+
github.com/mattn/go-isatty v0.0.22 // indirect
20
20
+
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
21
21
+
github.com/sagikazarmark/locafero v0.11.0 // indirect
22
22
+
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
23
23
+
github.com/spf13/afero v1.15.0 // indirect
24
24
+
github.com/spf13/cast v1.10.0 // indirect
25
25
+
github.com/spf13/pflag v1.0.10 // indirect
26
26
+
github.com/spf13/viper v1.21.0 // indirect
27
27
+
github.com/subosito/gotenv v1.6.0 // indirect
28
28
+
github.com/valyala/bytebufferpool v1.0.0 // indirect
29
29
+
github.com/valyala/fasttemplate v1.2.2 // indirect
30
30
+
go.yaml.in/yaml/v3 v3.0.4 // indirect
31
31
+
golang.org/x/crypto v0.53.0 // indirect
32
32
+
golang.org/x/net v0.56.0 // indirect
33
33
+
golang.org/x/time v0.15.0 // indirect
34
34
+
)
35
35
+
36
36
+
require (
14
37
github.com/andybalholm/brotli v1.1.1 // indirect
38
38
+
github.com/go-chi/chi/v5 v5.3.0
39
39
+
github.com/google/go-cmp v0.6.0 // indirect
15
40
github.com/google/uuid v1.6.0 // indirect
16
41
github.com/jinzhu/inflection v1.0.0 // indirect
17
42
github.com/jinzhu/now v1.1.5 // indirect
18
43
github.com/klauspost/compress v1.17.9 // indirect
44
44
+
github.com/labstack/echo/v4 v4.15.4
45
45
+
github.com/labstack/echo/v5 v5.2.1
19
46
github.com/mattn/go-sqlite3 v1.14.22 // indirect
20
47
github.com/parquet-go/bitpack v1.0.0 // indirect
21
48
github.com/parquet-go/jsonlite v1.0.0 // indirect
22
49
github.com/pierrec/lz4/v4 v4.1.21 // indirect
23
50
github.com/twpayne/go-geom v1.6.1 // indirect
24
24
-
golang.org/x/sys v0.38.0 // indirect
25
25
-
golang.org/x/text v0.20.0 // indirect
51
51
+
golang.org/x/sys v0.46.0 // indirect
52
52
+
golang.org/x/text v0.38.0 // indirect
26
53
google.golang.org/protobuf v1.34.2 // indirect
27
54
)
···
10
10
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
11
11
github.com/earthboundkid/versioninfo/v2 v2.24.1 h1:SJTMHaoUx3GzjjnUO1QzP3ZXK6Ee/nbWyCm58eY3oUg=
12
12
github.com/earthboundkid/versioninfo/v2 v2.24.1/go.mod h1:VcWEooDEuyUJnMfbdTh0uFN4cfEIg+kHMuWB2CDCLjw=
13
13
-
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
14
14
-
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
13
13
+
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
14
14
+
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
15
15
+
github.com/go-chi/chi/v5 v5.3.0 h1:halUjDxhshgXHMrao5bB8eNBXo/rnzwr8m5m36glehM=
16
16
+
github.com/go-chi/chi/v5 v5.3.0/go.mod h1:R+tYY2hNuVUUjxoPtqUdgBqevM9s9njzkTLutVsOCto=
17
17
+
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
18
18
+
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
19
19
+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
20
20
+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
15
21
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
16
22
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
17
23
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
···
20
26
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
21
27
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
22
28
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
29
29
+
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
30
30
+
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
23
31
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
24
32
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
33
33
+
github.com/labstack/echo/v4 v4.15.4 h1:DL45vVYa+BWE+XuW+zZNd9H0YEdZ80UAWJGcTVW4EVs=
34
34
+
github.com/labstack/echo/v4 v4.15.4/go.mod h1:CuMetKIRwsuO/qlAgMq+KTAalwGoB/h4tC+yPdrTj1g=
35
35
+
github.com/labstack/echo/v5 v5.2.1 h1:TzpIksY6zLMzV0T0ycYbvTEoj9w6o6AcL5twg182VTY=
36
36
+
github.com/labstack/echo/v5 v5.2.1/go.mod h1:SyvlSdObGjRXeQfCCXW/sybkZdOOQZBmpKF0bvALaeo=
37
37
+
github.com/labstack/gommon v0.5.0 h1:6VSQ2NOzsnEJ5W6+84E0RbcaDDmgB6NIAzWCczTEe6c=
38
38
+
github.com/labstack/gommon v0.5.0/go.mod h1:Rzlg7HHy1maLfzBYGg9NZcVuz1sA68HHhLjhcEllYE0=
39
39
+
github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY=
40
40
+
github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
41
41
+
github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4=
42
42
+
github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
25
43
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
26
44
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
27
45
github.com/parquet-go/bitpack v1.0.0 h1:AUqzlKzPPXf2bCdjfj4sTeacrUwsT7NlcYDMUQxPcQA=
···
30
48
github.com/parquet-go/jsonlite v1.0.0/go.mod h1:nDjpkpL4EOtqs6NQugUsi0Rleq9sW/OtC1NnZEnxzF0=
31
49
github.com/parquet-go/parquet-go v0.30.1 h1:Oy6ganNrAdFiVwy7wNmWagfPTWA2X9Z3tVHBc7JtuX8=
32
50
github.com/parquet-go/parquet-go v0.30.1/go.mod h1:navtkAYr2LGoJVp141oXPlO/sxLvaOe3la2JEoD8+rg=
51
51
+
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
52
52
+
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
33
53
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
34
54
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
35
55
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
36
56
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
57
57
+
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
58
58
+
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
59
59
+
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
60
60
+
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
61
61
+
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
62
62
+
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
63
63
+
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
64
64
+
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
65
65
+
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
66
66
+
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
67
67
+
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
68
68
+
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
37
69
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
38
70
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
71
71
+
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
72
72
+
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
39
73
github.com/twpayne/go-geom v1.6.1 h1:iLE+Opv0Ihm/ABIcvQFGIiFBXd76oBIar9drAwHFhR4=
40
74
github.com/twpayne/go-geom v1.6.1/go.mod h1:Kr+Nly6BswFsKM5sd31YaoWS5PeDDH2NftJTK7Gd028=
41
75
github.com/urfave/cli/v3 v3.10.1 h1:7Kx9H50hrHbRbyxgO1KP6/BcbiGRz0uYh5YyQ30JEEY=
42
76
github.com/urfave/cli/v3 v3.10.1/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
77
77
+
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
78
78
+
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
79
79
+
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
80
80
+
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
43
81
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
44
82
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
83
83
+
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
84
84
+
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
85
85
+
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
86
86
+
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
87
87
+
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
88
88
+
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
45
89
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
46
90
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
47
47
-
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
48
48
-
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
49
49
-
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
50
50
-
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
91
91
+
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
92
92
+
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
93
93
+
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
94
94
+
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
95
95
+
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
96
96
+
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
97
97
+
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
98
98
+
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
51
99
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
52
100
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
101
101
+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
53
102
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
54
103
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
55
104
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
···
1
1
+
package internal
2
2
+
3
3
+
import (
4
4
+
"errors"
5
5
+
"log/slog"
6
6
+
"os"
7
7
+
"strings"
8
8
+
9
9
+
"github.com/earthboundkid/versioninfo/v2"
10
10
+
"github.com/joho/godotenv"
11
11
+
"github.com/spf13/viper"
12
12
+
)
13
13
+
14
14
+
var userAgentStr = "tap/" + versioninfo.Short()
15
15
+
16
16
+
func UserAgent() string {
17
17
+
return userAgentStr
18
18
+
}
19
19
+
20
20
+
// Load initializes the configuration with viper
21
21
+
func LoadConfig() {
22
22
+
if err := godotenv.Load(); err != nil {
23
23
+
slog.Info("No .env file found or error loading it. Using default values and environment variables.")
24
24
+
}
25
25
+
26
26
+
viper.SetDefault("server.port", "8080")
27
27
+
viper.SetDefault("server.host", "localhost")
28
28
+
viper.SetDefault("database.url", "./puzzles.db")
29
29
+
30
30
+
viper.AutomaticEnv()
31
31
+
32
32
+
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
33
33
+
34
34
+
// Install the configured logger now that log.format is resolvable from env
35
35
+
// defaults, so the config-read messages below use the chosen format.
36
36
+
SetupLogger()
37
37
+
38
38
+
viper.SetConfigName("config")
39
39
+
viper.SetConfigType("yaml")
40
40
+
viper.AddConfigPath("./config")
41
41
+
viper.AddConfigPath(".")
42
42
+
43
43
+
if err := viper.ReadInConfig(); err != nil {
44
44
+
var configFileNotFoundError viper.ConfigFileNotFoundError
45
45
+
if !errors.As(err, &configFileNotFoundError) {
46
46
+
slog.Error("Error reading config file", "err", err)
47
47
+
os.Exit(1)
48
48
+
}
49
49
+
slog.Info("Config file not found, using default values and environment variables")
50
50
+
} else {
51
51
+
slog.Info("Using config file", "file", viper.ConfigFileUsed())
52
52
+
}
53
53
+
}
54
54
+
55
55
+
// SetupLogger installs a process-wide slog logger based on the configured
56
56
+
// "log.format". "json" selects a JSON handler; anything else (the default
57
57
+
// "text") selects a human-readable text handler. Output goes to stderr.
58
58
+
func SetupLogger() {
59
59
+
var handler slog.Handler
60
60
+
if viper.GetString("log.format") == "json" {
61
61
+
handler = slog.NewJSONHandler(os.Stderr, nil)
62
62
+
} else {
63
63
+
handler = slog.NewTextHandler(os.Stderr, nil)
64
64
+
}
65
65
+
slog.SetDefault(slog.New(handler))
66
66
+
}
···
8
8
"gorm.io/driver/sqlite"
9
9
"gorm.io/gorm"
10
10
"gorm.io/gorm/logger"
11
11
-
"tangled.org/pds.dad/LichessPuzzles/internal/models"
11
11
+
"tangled.org/pds.dad/PuzzleMeThis/internal/models"
12
12
)
13
13
14
14
// Open opens the SQLite database at path with settings tuned for bulk inserts
···
1
1
+
package server
2
2
+
3
3
+
import (
4
4
+
"net/http"
5
5
+
6
6
+
"github.com/labstack/echo/v5"
7
7
+
)
8
8
+
9
9
+
func (s *Server) home(c *echo.Context) error {
10
10
+
c.Logger().Info("home")
11
11
+
return c.String(http.StatusOK, "hey")
12
12
+
}
···
1
1
+
package server
2
2
+
3
3
+
import (
4
4
+
"context"
5
5
+
"errors"
6
6
+
"fmt"
7
7
+
"log/slog"
8
8
+
"net/http"
9
9
+
"time"
10
10
+
11
11
+
"github.com/labstack/echo/v5"
12
12
+
"github.com/labstack/echo/v5/middleware"
13
13
+
"github.com/spf13/viper"
14
14
+
"gorm.io/gorm"
15
15
+
)
16
16
+
17
17
+
type Server struct {
18
18
+
db *gorm.DB
19
19
+
}
20
20
+
21
21
+
func RunHTTPServer(ctx context.Context, db *gorm.DB) {
22
22
+
23
23
+
srv := &Server{db: db}
24
24
+
25
25
+
e := echo.New()
26
26
+
e.Logger = slog.Default()
27
27
+
e.Use(middleware.RequestLogger())
28
28
+
29
29
+
//Handlers
30
30
+
e.GET("/", srv.home)
31
31
+
32
32
+
port := viper.Get("server.port")
33
33
+
host := viper.Get("server.host")
34
34
+
address := fmt.Sprintf("%s:%s", host, port)
35
35
+
s := http.Server{Addr: address, Handler: e}
36
36
+
37
37
+
go func() {
38
38
+
e.Logger.Info("Starting the server on ", "address", address)
39
39
+
40
40
+
if err := s.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
41
41
+
e.Logger.Error("failed to start server", "error", err)
42
42
+
}
43
43
+
e.Logger.Info("server started", "address", address)
44
44
+
}()
45
45
+
46
46
+
<-ctx.Done()
47
47
+
48
48
+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
49
49
+
defer cancel()
50
50
+
if err := s.Shutdown(ctx); err != nil {
51
51
+
e.Logger.Error("failed to stop server", "error", err)
52
52
+
}
53
53
+
54
54
+
}