Templates + build

This commit is contained in:
William Bouzourène 2026-02-16 12:20:21 +01:00
parent 67e2e4d306
commit 7e821296a6
Signed by: bouzoure
GPG key ID: 423440D735B56BE2
13 changed files with 202 additions and 15 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
__debug_bin*
templates/*_templ.go
bin/

14
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,14 @@
{
"version": "0.2.0",
"configurations": [
{
"preLaunchTask": "just: prebuild",
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceRoot}",
"console": "integratedTerminal",
}
]
}

5
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,5 @@
{
"editor.tabSize": 2,
"editor.detectIndentation": false,
"editor.insertSpaces": false,
}

9
.vscode/tasks.json vendored Normal file
View file

@ -0,0 +1,9 @@
{
"version": "2.0.0",
"tasks": [{
"label": "just: prebuild",
"command": "just",
"args": ["prebuild"],
"type": "shell"
}]
}

12
go.mod
View file

@ -10,18 +10,30 @@ require (
)
require (
github.com/a-h/parse v0.0.0-20250122154542-74294addb73e // indirect
github.com/a-h/templ v0.3.977 // indirect
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cli/browser v1.3.0 // indirect
github.com/clipperhouse/uax29/v2 v2.6.0 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/klauspost/compress v1.18.4 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.19 // indirect
github.com/natefinch/atomic v1.0.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.69.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
golang.org/x/mod v0.32.0 // indirect
golang.org/x/net v0.50.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.41.0 // indirect
golang.org/x/text v0.34.0 // indirect
golang.org/x/tools v0.41.0 // indirect
)
tool github.com/a-h/templ/cmd/templ

20
go.sum
View file

@ -1,8 +1,20 @@
github.com/a-h/parse v0.0.0-20250122154542-74294addb73e h1:HjVbSQHy+dnlS6C3XajZ69NYAb5jbGNfHanvm1+iYlo=
github.com/a-h/parse v0.0.0-20250122154542-74294addb73e/go.mod h1:3mnrkvGpurZ4ZrTDbYU84xhwXW2TjTKShSwjRi2ihfQ=
github.com/a-h/templ v0.3.977 h1:kiKAPXTZE2Iaf8JbtM21r54A8bCNsncrfnokZZSrSDg=
github.com/a-h/templ v0.3.977/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo=
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo=
github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk=
github.com/clipperhouse/uax29/v2 v2.6.0 h1:z0cDbUV+aPASdFb2/ndFnS9ts/WNXgTNNGFoKXuhpos=
github.com/clipperhouse/uax29/v2 v2.6.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gofiber/fiber/v2 v2.52.11 h1:5f4yzKLcBcF8ha1GQTWB+mpblWz3Vz6nSAbTL31HkWs=
github.com/gofiber/fiber/v2 v2.52.11/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
github.com/gofiber/helmet/v2 v2.2.26 h1:KreQVUpCIGppPQ6Yt8qQMaIR4fVXMnvBdsda0dJSsO8=
@ -19,6 +31,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
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/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM=
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/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
@ -32,9 +46,13 @@ github.com/valyala/fasthttp v1.69.0 h1:fNLLESD2SooWeh2cidsuFtOcrEi4uB4m1mPrkJMZy
github.com/valyala/fasthttp v1.69.0/go.mod h1:4wA4PfAraPlAsJ5jMSqCE2ug5tqUPwKXxVj8oNECGcw=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -45,5 +63,7 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

11
helpers/templates.go Normal file
View file

@ -0,0 +1,11 @@
package helpers
import (
"github.com/a-h/templ"
"github.com/gofiber/fiber/v2"
)
func TemplRender(c *fiber.Ctx, component templ.Component) error {
c.Set("Content-Type", "text/html")
return component.Render(c.UserContext(), c.Response().BodyWriter())
}

22
justfile Normal file
View file

@ -0,0 +1,22 @@
go := require("go")
templ := require("templ")
air := require("air")
templ:
templ generate
prebuild: templ
run: prebuild
go run main.go
build: prebuild
go build -o bin/gpx-downloader main.go
watch:
CI=1 CLICOLOR_FORCE=1 air \
--build.cmd "just build" \
--build.bin "./bin/gpx-downloader" \
-build.include_ext "go,templ" \
--build.exclude_dir "data" \
--build.exclude_regex "_templ.go"

View file

@ -14,7 +14,7 @@ import (
func SuisseMobileCheckURL(url string) int {
var id int
re := regexp.MustCompile(`https?://(?:www\.)?schweizmobil\.ch/[^/]+/tour/(\d+)`)
re := regexp.MustCompile(`schweizmobil\.ch/[^/]+/tour/(\d+)`)
matches := re.FindStringSubmatch(url)
if len(matches) > 1 {

View file

@ -3,16 +3,25 @@ package routes
import (
"fmt"
"git.readonly.ch/bouzoure/gpx-downloader/helpers"
"git.readonly.ch/bouzoure/gpx-downloader/providers"
"git.readonly.ch/bouzoure/gpx-downloader/templates"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/log"
"github.com/tkrajina/gpxgo/gpx"
)
func Fetch(c *fiber.Ctx) error {
// In case of error
baseParams := templates.BaseTemplateParams{
Title: "Error - GPX downloader",
}
var indexParams templates.IndexParams
sourceUrl := c.FormValue("url")
if len(sourceUrl) <= 0 {
c.SendStatus(422)
return c.SendString("Error: URL is empty")
indexParams.Error = "Error: URL is empty"
return helpers.TemplRender(c, templates.Index(baseParams, indexParams))
}
var gpxFile gpx.GPX
@ -29,18 +38,22 @@ func Fetch(c *fiber.Ctx) error {
gpxFile, err = providers.SuisseMobileFetch(suisseMobileId)
if err != nil {
return err
log.Error(err)
indexParams.Error = "An error occured"
return helpers.TemplRender(c, templates.Index(baseParams, indexParams))
}
}
if len(filename) <= 0 {
c.SendStatus(422)
return c.SendString("Error: URL is not supported")
indexParams.Error = "Error: URL is not supported"
return helpers.TemplRender(c, templates.Index(baseParams, indexParams))
}
xml, err := gpxFile.ToXml(gpx.ToXmlParams{})
if err != nil {
return err
log.Error(err)
indexParams.Error = "An error occured"
return helpers.TemplRender(c, templates.Index(baseParams, indexParams))
}
c.Set("Content-Type", "octet-stream")

View file

@ -1,13 +1,16 @@
package routes
import "github.com/gofiber/fiber/v2"
import (
"git.readonly.ch/bouzoure/gpx-downloader/helpers"
"git.readonly.ch/bouzoure/gpx-downloader/templates"
"github.com/gofiber/fiber/v2"
)
func Index(c *fiber.Ctx) error {
c.Set("Content-Type", "text/html")
return c.SendString(`
<form method="post" action="/fetch">
<input name="url" required />
<button type="submit">Fetch</button>
</form>
`)
baseParams := templates.BaseTemplateParams{
Title: "GPX downloader",
}
var indexParams templates.IndexParams
return helpers.TemplRender(c, templates.Index(baseParams, indexParams))
}

49
templates/base.templ Normal file
View file

@ -0,0 +1,49 @@
package templates
type BaseTemplateParams struct {
Title string
}
templ BaseTemplate(params BaseTemplateParams) {
<!doctype html>
<html lang="en">
<head>
<title>{ params.Title }</title>
<style>
body {
background-color: #000;
color: #fff;
font-family: monospace;
}
#main {
margin: 30px auto;
text-align: center;
max-width: 600px;
}
#main h1 {
margin-bottom: 30px;
}
input[name="url"] {
width: 100%;
max-width: 500px;
}
.error-message {
margin: 30px;
padding: 10px;
background-color: #aa0000;
}
a {
color: #fff;
}
</style>
</head>
<body>
{ children... }
</body>
</html>
}

26
templates/index.templ Normal file
View file

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