Error page + config + docker

This commit is contained in:
William Bouzourène 2026-02-16 14:58:18 +01:00
parent 7e821296a6
commit c6c533a503
Signed by: bouzoure
GPG key ID: 423440D735B56BE2
13 changed files with 110 additions and 31 deletions

3
.gitignore vendored
View file

@ -1,3 +1,6 @@
__debug_bin* __debug_bin*
templates/*_templ.go templates/*_templ.go
bin/ bin/
config.toml
tmp/
bin/

16
Dockerfile Normal file
View file

@ -0,0 +1,16 @@
FROM golang:alpine
RUN apk add just
RUN go install github.com/a-h/templ/cmd/templ@latest && \
go install github.com/air-verse/air@latest
COPY . /src
RUN cd /src && go get -u && just build
RUN mkdir /app && mv /src/bin/gpx-downloader /app/gpx-downloader
WORKDIR /app
ENTRYPOINT [ "/app/gpx-downloader" ]

8
compose.yml Normal file
View file

@ -0,0 +1,8 @@
services:
gpx-download:
build: .
restart: unless-stopped
ports:
- "127.0.0.1:3000:3000"
volumes:
- ./config.toml:/app/config.toml

3
config.example.toml Normal file
View file

@ -0,0 +1,3 @@
[server]
address = "127.0.0.1"
port = 3000

1
go.mod
View file

@ -24,6 +24,7 @@ require (
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.19 // indirect github.com/mattn/go-runewidth v0.0.19 // indirect
github.com/natefinch/atomic v1.0.1 // indirect github.com/natefinch/atomic v1.0.1 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/rivo/uniseg v0.4.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.69.0 // indirect github.com/valyala/fasthttp v1.69.0 // indirect

2
go.sum
View file

@ -33,6 +33,8 @@ github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byF
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0A= github.com/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0A=
github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM= github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=

29
helpers/config.go Normal file
View file

@ -0,0 +1,29 @@
package helpers
import (
"fmt"
"github.com/pelletier/go-toml"
)
type Configuration struct {
Server struct {
Address string `toml:"address"`
Port int `toml:"port"`
} `toml:"server"`
}
func ParseConfig(filePath string) (*Configuration, error) {
var config Configuration
data, err := toml.LoadFile(filePath)
if err != nil {
return nil, fmt.Errorf("error parsing config file: %v", err)
}
err = data.Unmarshal(&config)
if err != nil {
return nil, fmt.Errorf("error parsing config file: %v", err)
}
return &config, nil
}

View file

@ -11,12 +11,13 @@ run: prebuild
go run main.go go run main.go
build: prebuild build: prebuild
go build -o bin/gpx-downloader main.go mkdir bin
go build -o ./bin/gpx-downloader main.go
watch: watch:
CI=1 CLICOLOR_FORCE=1 air \ CI=1 CLICOLOR_FORCE=1 air \
--build.cmd "just build" \ --build.cmd "just build" \
--build.bin "./bin/gpx-downloader" \ --build.bin "./bin/gpx-downloader" \
-build.include_ext "go,templ" \ -build.include_ext "go,templ" \
--build.exclude_dir "data" \ --build.exclude_dir "data,bin" \
--build.exclude_regex "_templ.go" --build.exclude_regex "_templ.go"

12
main.go
View file

@ -1,8 +1,10 @@
package main package main
import ( import (
"fmt"
"log" "log"
"git.readonly.ch/bouzoure/gpx-downloader/helpers"
"git.readonly.ch/bouzoure/gpx-downloader/routes" "git.readonly.ch/bouzoure/gpx-downloader/routes"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/gofiber/helmet/v2" "github.com/gofiber/helmet/v2"
@ -22,7 +24,15 @@ func main() {
app.Get("/", routes.Index) app.Get("/", routes.Index)
app.Post("/fetch", routes.Fetch) app.Post("/fetch", routes.Fetch)
err := app.Listen("127.0.0.1:3000") config, err := helpers.ParseConfig("config.toml")
if err != nil {
log.Fatal(err)
}
err = app.Listen(fmt.Sprintf(
"%s:%d",
config.Server.Address, config.Server.Port,
))
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

View file

@ -16,12 +16,12 @@ func Fetch(c *fiber.Ctx) error {
baseParams := templates.BaseTemplateParams{ baseParams := templates.BaseTemplateParams{
Title: "Error - GPX downloader", Title: "Error - GPX downloader",
} }
var indexParams templates.IndexParams var errorParams templates.ErrorParams
sourceUrl := c.FormValue("url") sourceUrl := c.FormValue("url")
if len(sourceUrl) <= 0 { if len(sourceUrl) <= 0 {
indexParams.Error = "Error: URL is empty" errorParams.Error = "Error: URL is empty"
return helpers.TemplRender(c, templates.Index(baseParams, indexParams)) return helpers.TemplRender(c, templates.Error(baseParams, errorParams))
} }
var gpxFile gpx.GPX var gpxFile gpx.GPX
@ -39,21 +39,21 @@ func Fetch(c *fiber.Ctx) error {
gpxFile, err = providers.SuisseMobileFetch(suisseMobileId) gpxFile, err = providers.SuisseMobileFetch(suisseMobileId)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
indexParams.Error = "An error occured" errorParams.Error = "An error occured"
return helpers.TemplRender(c, templates.Index(baseParams, indexParams)) return helpers.TemplRender(c, templates.Error(baseParams, errorParams))
} }
} }
if len(filename) <= 0 { if len(filename) <= 0 {
indexParams.Error = "Error: URL is not supported" errorParams.Error = "Error: URL is not supported"
return helpers.TemplRender(c, templates.Index(baseParams, indexParams)) return helpers.TemplRender(c, templates.Error(baseParams, errorParams))
} }
xml, err := gpxFile.ToXml(gpx.ToXmlParams{}) xml, err := gpxFile.ToXml(gpx.ToXmlParams{})
if err != nil { if err != nil {
log.Error(err) log.Error(err)
indexParams.Error = "An error occured" errorParams.Error = "An error occured"
return helpers.TemplRender(c, templates.Index(baseParams, indexParams)) return helpers.TemplRender(c, templates.Error(baseParams, errorParams))
} }
c.Set("Content-Type", "octet-stream") c.Set("Content-Type", "octet-stream")

View file

@ -10,7 +10,6 @@ func Index(c *fiber.Ctx) error {
baseParams := templates.BaseTemplateParams{ baseParams := templates.BaseTemplateParams{
Title: "GPX downloader", Title: "GPX downloader",
} }
var indexParams templates.IndexParams
return helpers.TemplRender(c, templates.Index(baseParams, indexParams)) return helpers.TemplRender(c, templates.Index(baseParams))
} }

19
templates/errors.templ Normal file
View file

@ -0,0 +1,19 @@
package templates
type ErrorParams struct {
Error string
}
templ Error(baseParams BaseTemplateParams, errorParams ErrorParams) {
@BaseTemplate(baseParams) {
<div id="main">
<h1>GPX downloader</h1>
<div class="error-message">
{ errorParams.Error }
</div>
<a href="/">Retour</a>
</div>
}
}

View file

@ -1,26 +1,14 @@
package templates package templates
type IndexParams struct { templ Index(baseParams BaseTemplateParams) {
Error string
}
templ Index(baseParams BaseTemplateParams, indexParams IndexParams) {
@BaseTemplate(baseParams) { @BaseTemplate(baseParams) {
<div id="main"> <div id="main">
<h1>GPX downloader</h1> <h1>GPX downloader</h1>
if len(indexParams.Error) <= 0 { <form method="post" action="/fetch">
<form method="post" action="/fetch"> <input name="url" required />
<input name="url" required /> <button type="submit">Fetch</button>
<button type="submit">Fetch</button> </form>
</form>
} else {
<div class="error-message">
{ indexParams.Error }
</div>
<a href="/">Retour</a>
}
</div> </div>
} }
} }