Update template
This commit is contained in:
parent
ca8b1a2e46
commit
542d28e7a9
52
.air.toml
Normal file
52
.air.toml
Normal file
@ -0,0 +1,52 @@
|
||||
root = "."
|
||||
testdata_dir = "testdata"
|
||||
tmp_dir = "tmp"
|
||||
|
||||
[build]
|
||||
args_bin = []
|
||||
bin = "./tmp/main"
|
||||
cmd = "go build -o ./tmp/main ."
|
||||
delay = 1000
|
||||
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
|
||||
exclude_file = []
|
||||
exclude_regex = ["_test.go"]
|
||||
exclude_unchanged = false
|
||||
follow_symlink = false
|
||||
full_bin = ""
|
||||
include_dir = []
|
||||
include_ext = ["go", "tpl", "tmpl", "html"]
|
||||
include_file = []
|
||||
kill_delay = "0s"
|
||||
log = "build-errors.log"
|
||||
poll = false
|
||||
poll_interval = 0
|
||||
post_cmd = []
|
||||
pre_cmd = []
|
||||
rerun = false
|
||||
rerun_delay = 500
|
||||
send_interrupt = false
|
||||
stop_on_error = false
|
||||
|
||||
[color]
|
||||
app = ""
|
||||
build = "yellow"
|
||||
main = "magenta"
|
||||
runner = "green"
|
||||
watcher = "cyan"
|
||||
|
||||
[log]
|
||||
main_only = false
|
||||
silent = false
|
||||
time = false
|
||||
|
||||
[misc]
|
||||
clean_on_exit = false
|
||||
|
||||
[proxy]
|
||||
app_port = 0
|
||||
enabled = false
|
||||
proxy_port = 0
|
||||
|
||||
[screen]
|
||||
clear_on_rebuild = false
|
||||
keep_scroll = true
|
27
go.mod
27
go.mod
@ -1,33 +1,26 @@
|
||||
module git.clustercockpit.org/moebiusband/go-http-skeleton
|
||||
|
||||
go 1.22
|
||||
go 1.24
|
||||
|
||||
require (
|
||||
golang.org/x/sync v0.7.0
|
||||
google.golang.org/protobuf v1.34.2
|
||||
zombiezen.com/go/sqlite v1.3.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/a-h/templ v0.2.747 // indirect
|
||||
github.com/benbjohnson/hashfs v0.2.2 // indirect
|
||||
github.com/delaneyj/datastar v0.16.2 // indirect
|
||||
github.com/delaneyj/gostar v0.7.3 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/go-sanitize/sanitize v1.1.0 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/gorilla/sessions v1.3.0 // indirect
|
||||
github.com/igrmk/treemap/v2 v2.0.1 // indirect
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/samber/lo v1.44.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/mod v0.18.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
golang.org/x/tools v0.22.0 // indirect
|
||||
modernc.org/libc v1.54.2 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.8.0 // indirect
|
||||
modernc.org/sqlite v1.30.1 // indirect
|
||||
zombiezen.com/go/sqlite v1.3.0 // indirect
|
||||
)
|
||||
|
61
go.sum
61
go.sum
@ -1,68 +1,51 @@
|
||||
github.com/a-h/templ v0.2.747 h1:D0dQ2lxC3W7Dxl6fxQ/1zZHBQslSkTSvl5FxP/CfdKg=
|
||||
github.com/a-h/templ v0.2.747/go.mod h1:69ObQIbrcuwPCU32ohNaWce3Cb7qM5GMiqN1K+2yop4=
|
||||
github.com/benbjohnson/hashfs v0.2.2 h1:vFZtksphM5LcnMRFctj49jCUkCc7wp3NP6INyfjkse4=
|
||||
github.com/benbjohnson/hashfs v0.2.2/go.mod h1:7OMXaMVo1YkfiIPxKrl7OXkUTUgWjmsAKyR+E6xDIRM=
|
||||
github.com/delaneyj/datastar v0.16.2 h1:gFvuVZE1Ze95YcfyrxLb+wAlaMYDCx/k24hxObRYU+s=
|
||||
github.com/delaneyj/datastar v0.16.2/go.mod h1:+h1DvxZr7kOxWhjOxMgYTz17pfVR+fQ2me//3pqNm9I=
|
||||
github.com/delaneyj/gostar v0.7.3 h1:+7vEN7T9Y6HESGMHwlXzsTC/apgVwQy1bR4NQ7IKYfo=
|
||||
github.com/delaneyj/gostar v0.7.3/go.mod h1:mlxRWAVbntRR2VWlpXAzt7y9HY+bQtEm/lsyFnGLx/w=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/go-sanitize/sanitize v1.1.0 h1:wq9tl5+VfkyCacCZIVQf6ksegRpfWl3N2vAyyYD0F1I=
|
||||
github.com/go-sanitize/sanitize v1.1.0/go.mod h1:r+anm3xp/Y1+pTNvPSgHMznwb0VVZgszoMQs3naOf0A=
|
||||
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||
github.com/gorilla/sessions v1.3.0 h1:XYlkq7KcpOB2ZhHBPv5WpjMIxrQosiZanfoy1HLZFzg=
|
||||
github.com/gorilla/sessions v1.3.0/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
|
||||
github.com/igrmk/treemap/v2 v2.0.1 h1:Jhy4z3yhATvYZMWCmxsnHO5NnNZBdueSzvxh6353l+0=
|
||||
github.com/igrmk/treemap/v2 v2.0.1/go.mod h1:PkTPvx+8OHS8/41jnnyVY+oVsfkaOUZGcr+sfonosd4=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/samber/lo v1.44.0 h1:5il56KxRE+GHsm1IR+sZ/6J42NODigFiqCWpSc2dybA=
|
||||
github.com/samber/lo v1.44.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
|
||||
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
|
||||
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
|
||||
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
|
||||
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk=
|
||||
modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY=
|
||||
modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
|
||||
modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
|
||||
modernc.org/ccgo/v4 v4.19.0 h1:f9K5VdC0nVhHKTFMvhjtZ8TbRgFQbASvE5yO1zs8eC0=
|
||||
modernc.org/ccgo/v4 v4.19.0/go.mod h1:CfpAl+673iXNwMG/aqcQn+vDcu4Es/YLya7+9RHjTa4=
|
||||
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
|
||||
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
|
||||
modernc.org/libc v1.54.2 h1:9ymAodb+3v85YfBIZqn62BGgO4L9zF2Hx4LNb6dSU/Q=
|
||||
modernc.org/libc v1.54.2/go.mod h1:B0D6klDmSmnq26T1iocn9kzyX6NtbzjuI3+oX/xfvng=
|
||||
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||
modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
|
||||
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
|
||||
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
|
||||
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
|
||||
modernc.org/sqlite v1.29.1 h1:19GY2qvWB4VPw0HppFlZCPAbmxFU41r+qjKZQdQ1ryA=
|
||||
modernc.org/sqlite v1.29.1/go.mod h1:hG41jCYxOAOoO6BRK66AdRlmOcDzXf7qnwlwjUIOqa0=
|
||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
|
||||
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
|
||||
modernc.org/sqlite v1.30.1 h1:YFhPVfu2iIgUf9kuA1CR7iiHdcEEsI2i+yjRYHscyxk=
|
||||
modernc.org/sqlite v1.30.1/go.mod h1:DUmsiWQDaAvU4abhc/N+djlom/L2o8f7gZ95RCvyoLU=
|
||||
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
zombiezen.com/go/sqlite v1.3.0 h1:98g1gnCm+CNz6AuQHu0gqyw7gR2WU3O3PJufDOStpUs=
|
||||
zombiezen.com/go/sqlite v1.3.0/go.mod h1:yRl27//s/9aXU3RWs8uFQwjkTG9gYNGEls6+6SvrclY=
|
||||
|
34
internal/handlers/root.go
Normal file
34
internal/handlers/root.go
Normal file
@ -0,0 +1,34 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"git.clustercockpit.org/moebiusband/go-http-skeleton/web"
|
||||
)
|
||||
|
||||
type PageData struct {
|
||||
Title string
|
||||
}
|
||||
|
||||
func RootHandler() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
file, err := web.Templates.ReadFile("templates/index.html")
|
||||
if err != nil {
|
||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||
slog.Error("Error reading template", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
tmpl := template.Must(template.New("index.html").Parse(string(file)))
|
||||
|
||||
data := PageData{
|
||||
Title: "Home",
|
||||
}
|
||||
if err := tmpl.Execute(w, data); err != nil {
|
||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||
slog.Error("Error executing template", "error", err)
|
||||
}
|
||||
}
|
||||
}
|
28
internal/middleware/middleware.go
Normal file
28
internal/middleware/middleware.go
Normal file
@ -0,0 +1,28 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Middleware is a function that wraps an http.Handler with custom logic.
|
||||
type Middleware func(http.Handler) http.Handler
|
||||
|
||||
// Chain is a helper to build up a pipeline of middlewares, then apply them to a
|
||||
// final handler.
|
||||
type Chain struct {
|
||||
middlewares []Middleware
|
||||
}
|
||||
|
||||
// Use appends a middleware to the chain.
|
||||
func (c *Chain) Use(m Middleware) {
|
||||
c.middlewares = append(c.middlewares, m)
|
||||
}
|
||||
|
||||
// Then applies the entire chain of middlewares to the final handler in reverse
|
||||
// order.
|
||||
func (c *Chain) Then(h http.Handler) http.Handler {
|
||||
for i := len(c.middlewares) - 1; i >= 0; i-- {
|
||||
h = c.middlewares[i](h)
|
||||
}
|
||||
return h
|
||||
}
|
18
internal/middleware/recover.go
Normal file
18
internal/middleware/recover.go
Normal file
@ -0,0 +1,18 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func RecoverMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
slog.Error("Recovered from panic", "error", err)
|
||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||
}
|
||||
}()
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
107
main.go
Normal file
107
main.go
Normal file
@ -0,0 +1,107 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"git.clustercockpit.org/moebiusband/go-http-skeleton/internal/handlers"
|
||||
"git.clustercockpit.org/moebiusband/go-http-skeleton/internal/middleware"
|
||||
)
|
||||
|
||||
//go:embed web/static/*
|
||||
var static embed.FS
|
||||
|
||||
func init() {
|
||||
_, jsonLogger := os.LookupEnv("JSON_LOGGER")
|
||||
_, debug := os.LookupEnv("DEBUG")
|
||||
|
||||
var programLevel slog.Level
|
||||
if debug {
|
||||
programLevel = slog.LevelDebug
|
||||
}
|
||||
|
||||
if jsonLogger {
|
||||
jsonHandler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
|
||||
Level: programLevel,
|
||||
})
|
||||
slog.SetDefault(slog.New(jsonHandler))
|
||||
} else {
|
||||
textHandler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
|
||||
Level: programLevel,
|
||||
})
|
||||
slog.SetDefault(slog.New(textHandler))
|
||||
}
|
||||
|
||||
slog.Info("Logger initialized", slog.Bool("debug", debug))
|
||||
}
|
||||
|
||||
func main() {
|
||||
port := os.Getenv("PORT")
|
||||
if port == "" {
|
||||
port = "8080"
|
||||
}
|
||||
addr := ":" + port
|
||||
|
||||
mux := http.NewServeMux()
|
||||
|
||||
// Use an embedded filesystem rooted at "web/static"
|
||||
fs, err := fs.Sub(static, "web/static")
|
||||
if err != nil {
|
||||
slog.Error("Failed to create sub filesystem", "error", err)
|
||||
return
|
||||
}
|
||||
// Serve files from the embedded /web/static directory at /static
|
||||
fileServer := http.FileServer(http.FS(fs))
|
||||
mux.Handle("GET /static/", http.StripPrefix("/static/", fileServer))
|
||||
|
||||
mux.HandleFunc("GET /favicon.ico", func(w http.ResponseWriter, r *http.Request) {
|
||||
data, err := static.ReadFile("web/static/img/favicon.ico")
|
||||
if err != nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.Write(data)
|
||||
})
|
||||
mux.HandleFunc("GET /robots.txt", func(w http.ResponseWriter, r *http.Request) {
|
||||
data, err := static.ReadFile("web/static/robots.txt")
|
||||
if err != nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.Write(data)
|
||||
})
|
||||
|
||||
mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.Write([]byte(`OK`))
|
||||
})
|
||||
|
||||
mux.HandleFunc("GET /", handlers.RootHandler())
|
||||
|
||||
chain := &middleware.Chain{}
|
||||
chain.Use(middleware.RecoverMiddleware)
|
||||
wrappedMux := chain.Then(mux)
|
||||
|
||||
server := &http.Server{
|
||||
Addr: fmt.Sprintf(":%s", port),
|
||||
Handler: wrappedMux,
|
||||
// Recommended timeouts from
|
||||
// https://blog.cloudflare.com/exposing-go-on-the-internet/
|
||||
ReadTimeout: 5 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
IdleTimeout: 120 * time.Second,
|
||||
}
|
||||
|
||||
slog.Info("Server listening", "addr", addr)
|
||||
|
||||
if err := server.ListenAndServe(); err != nil {
|
||||
slog.Error("Server failed to start", "error", err)
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
package components
|
||||
|
||||
templ Page(content templ.Component) {
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<meta name="description" content="A sane golang website template."/>
|
||||
<link rel="preload" as="style" href="static/css/fonts.css" media="screen,print"/>
|
||||
<link rel="preload" as="style" href="static/css/site.screen.css" media="screen"/>
|
||||
<link rel="preload" as="font" href="static/css/fonts/poppins-western.woff2" media="screen,print" crossorigin/>
|
||||
<link rel="preload" as="font" href="static/css/fonts/poppins-western-bold.woff2" media="screen,print" crossorigin/>
|
||||
<link rel="preload" as="font" href="static/css/fonts/interface-Regular.woff2" media="screen,print" crossorigin/>
|
||||
<link rel="stylesheet" href="static/css/fonts.css" media="screen,print"/>
|
||||
<link rel="stylesheet" href="static/css/site.screen.css" media="screen"/>
|
||||
<link rel="stylesheet" href="static/css/site.print.css" media="print"/>
|
||||
<link rel="apple-touch-icon" href="static/apple-touch-icon.png"/>
|
||||
<link rel="icon" href="static/favicon.ico"/>
|
||||
<link rel="icon" href="static/favicon.svg" type="image/svg+xml"/>
|
||||
<title>Awesome content</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="fauxBody">
|
||||
@Header()
|
||||
<main>
|
||||
@content
|
||||
</main>
|
||||
<a href="#top" id="backToTop"><span>Back To Top</span></a>
|
||||
@Footer()
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package components
|
||||
|
||||
templ Page404() {
|
||||
<div>Page not found</div>
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package components
|
||||
|
||||
templ Footer() {
|
||||
<footer>
|
||||
<section>
|
||||
<h2 class="optional">Legal Disclaimer</h2>
|
||||
<p>
|
||||
© Jason M. Knight, <span>All Rights Reserved.</span>
|
||||
</p>
|
||||
</section>
|
||||
<section id="socialMedia">
|
||||
<h2 class="optional">Visit Me On Social Media</h2>
|
||||
<ul class="socialMenu">
|
||||
<li>
|
||||
<a href="#" class="icon_facebook">
|
||||
<span>Facebook</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="icon_linkedIn">
|
||||
<span>LinkedIn</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="icon_x">
|
||||
<span>The Artist Formerly Known As Twitter</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="icon_medium">
|
||||
<span>Medium</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</footer>
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
package components
|
||||
|
||||
templ Header() {
|
||||
<header id="top">
|
||||
<a href="./">
|
||||
<h1>Website template</h1>
|
||||
<p>A Golang Templ based skeleton</p>
|
||||
</a>
|
||||
<a href="#mainMenu" class="mainMenuOpen" hidden>
|
||||
<span>Open Main Menu</span>
|
||||
</a>
|
||||
<nav id="mainMenu">
|
||||
<a href="#" class="modalClose" hidden tabindex="-1">
|
||||
<span>Close Main Menu</span>
|
||||
</a>
|
||||
<div data-modalTitle="Main Menu">
|
||||
<a href="#" class="modalClose icon_close" hidden>
|
||||
<span>Close Main Menu</span>
|
||||
</a>
|
||||
<ul>
|
||||
<li><a href="./">Home</a></li>
|
||||
<li><a href="#whatIDo">What I Do</a></li>
|
||||
<li><a href="#testimonials">Testimonials</a></li>
|
||||
<li><a href="#contact">Contact</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package components
|
||||
|
||||
templ IndexPage() {
|
||||
<section class="hero" id="semanticEasy">
|
||||
<div>
|
||||
<header>
|
||||
<h2>Semantic Accessible <span>Markup Is Easy</span></h2>
|
||||
<p>Stop Making It Hard</p>
|
||||
</header>
|
||||
<p>
|
||||
I have spent the past decade and a half helping site owners out of deep legal troubles for accessibility woes, improve workplace conditions, gain essential leadership skills, by way of promoting good practices. I can help you learn to leverage caching models, write accessibible HTML, keep your style within guidelines such as the WCAG, and aid in lawsuits involving the US ADA, UQ EQA, and a plethora of other accessibility laws from around the globe.
|
||||
</p>
|
||||
<a class="action" href="#">Learn More</a>
|
||||
</div>
|
||||
</section>
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.clustercockpit.org/moebiusband/go-http-skeleton/web/components"
|
||||
"github.com/a-h/templ"
|
||||
"github.com/benbjohnson/hashfs"
|
||||
"github.com/gorilla/sessions"
|
||||
)
|
||||
|
||||
func setupRoutes(r *http.ServeMux) error {
|
||||
r.Handle("GET /static/", hashfs.FileServer(staticSys))
|
||||
r.Handle("GET /{$}", templ.Handler(components.Page(components.IndexPage())))
|
||||
r.Handle("GET /404", templ.Handler(components.Page(components.Page404()), templ.WithStatus(http.StatusNotFound)))
|
||||
|
||||
sessionStore := sessions.NewCookieStore([]byte("datastar-session-secret"))
|
||||
sessionStore.MaxAge(int(24 * time.Hour / time.Second))
|
||||
|
||||
return nil
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"git.clustercockpit.org/moebiusband/go-http-skeleton/internal/util"
|
||||
"github.com/benbjohnson/hashfs"
|
||||
)
|
||||
|
||||
//go:embed static/*
|
||||
var staticFS embed.FS
|
||||
|
||||
var staticSys = hashfs.NewFS(staticFS)
|
||||
|
||||
func RunBlocking(port int) util.CtxErrFunc {
|
||||
return func(ctx context.Context) error {
|
||||
r := http.NewServeMux()
|
||||
|
||||
setupRoutes(r)
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: fmt.Sprintf(":%d", port),
|
||||
Handler: r,
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
srv.Shutdown(context.Background())
|
||||
}()
|
||||
|
||||
return srv.ListenAndServe()
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 14 KiB |
7
web/static/robots.txt
Normal file
7
web/static/robots.txt
Normal file
@ -0,0 +1,7 @@
|
||||
User-agent: Googlebot
|
||||
Disallow: /nogooglebot/
|
||||
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
Sitemap: https://www.example.com/sitemap.xml
|
8
web/templates.go
Normal file
8
web/templates.go
Normal file
@ -0,0 +1,8 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"embed"
|
||||
)
|
||||
|
||||
//go:embed templates
|
||||
var Templates embed.FS
|
7
web/templates/index.html
Normal file
7
web/templates/index.html
Normal file
@ -0,0 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="h-screen">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>{{.Title}}</title>
|
||||
<!-- ... -->
|
Loading…
x
Reference in New Issue
Block a user