Moving to a Templ template engine stage 1.
This commit is contained in:
parent
2667ec93ce
commit
52439ee836
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,5 +1,6 @@
|
|||||||
.vscode
|
.vscode
|
||||||
*.pug.go
|
*.pug.go
|
||||||
|
*_templ.go
|
||||||
bin/dwelling-home
|
bin/dwelling-home
|
||||||
*.sqlite
|
*.sqlite
|
||||||
*.sqlite*
|
*.sqlite*
|
||||||
|
8
Makefile
8
Makefile
@ -12,7 +12,7 @@ LDFLAGS=-ldflags "-s -w -X main.version=${VERSION}" -tags osusergo,netgo
|
|||||||
|
|
||||||
.PHONY: run install uninstall clean
|
.PHONY: run install uninstall clean
|
||||||
|
|
||||||
${TARGET}: web/*.pug.go
|
${TARGET}: web/*_templ.go
|
||||||
go build -o bin/$@ ${LDFLAGS} ${FLAGS} cmd/$@/main.go
|
go build -o bin/$@ ${LDFLAGS} ${FLAGS} cmd/$@/main.go
|
||||||
|
|
||||||
web/*.pug.go: web/templates/*.pug
|
web/*.pug.go: web/templates/*.pug
|
||||||
@ -21,6 +21,12 @@ ifeq (,$(wildcard $(shell go env GOPATH)/bin/jade))
|
|||||||
endif
|
endif
|
||||||
go generate web/web.go
|
go generate web/web.go
|
||||||
|
|
||||||
|
web/*_templ.go: web/*.templ
|
||||||
|
ifeq (,$(wildcard $(shell go env GOPATH)/bin/templ))
|
||||||
|
go install github.com/a-h/templ/cmd/templ@latest
|
||||||
|
endif
|
||||||
|
TEMPL_EXPERIMENT=rawgo $(shell go env GOPATH)/bin/templ generate
|
||||||
|
|
||||||
run:
|
run:
|
||||||
bin/${TARGET} -listen 127.0.0.1:18123 -database-path . -captcha-expiry 10m \
|
bin/${TARGET} -listen 127.0.0.1:18123 -database-path . -captcha-expiry 10m \
|
||||||
-guestbook-page-size 10
|
-guestbook-page-size 10
|
||||||
|
5
go.mod
5
go.mod
@ -1,10 +1,13 @@
|
|||||||
module git.arav.su/Arav/dwelling-home
|
module git.arav.su/Arav/dwelling-home
|
||||||
|
|
||||||
go 1.17
|
go 1.21
|
||||||
|
|
||||||
|
toolchain go1.22.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.arav.su/Arav/justcaptcha/v2 v2.1.0
|
git.arav.su/Arav/justcaptcha/v2 v2.1.0
|
||||||
git.arav.su/Arav/justguestbook v1.3.2
|
git.arav.su/Arav/justguestbook v1.3.2
|
||||||
|
github.com/a-h/templ v0.2.707
|
||||||
github.com/gomarkdown/markdown v0.0.0-20230922112808-5421fefb8386
|
github.com/gomarkdown/markdown v0.0.0-20230922112808-5421fefb8386
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
)
|
)
|
||||||
|
5
go.sum
5
go.sum
@ -4,12 +4,16 @@ git.arav.su/Arav/justcaptcha/v2 v2.1.0 h1:EFerW2mP60rDczu1mPbJb7DiDeAQOcw83/KiWc
|
|||||||
git.arav.su/Arav/justcaptcha/v2 v2.1.0/go.mod h1:Ntab6TUAqCo/H9LSWOd4IQ9ANnp8G1PEygXtORcdTOY=
|
git.arav.su/Arav/justcaptcha/v2 v2.1.0/go.mod h1:Ntab6TUAqCo/H9LSWOd4IQ9ANnp8G1PEygXtORcdTOY=
|
||||||
git.arav.su/Arav/justguestbook v1.3.2 h1:B1AzS14dW3bVlp0Qj6pu1PrJlWaPK1mplmx3UNBcxo4=
|
git.arav.su/Arav/justguestbook v1.3.2 h1:B1AzS14dW3bVlp0Qj6pu1PrJlWaPK1mplmx3UNBcxo4=
|
||||||
git.arav.su/Arav/justguestbook v1.3.2/go.mod h1:Umo/AzSOKu+OU+03V7/QQ6m6N0nXyeRAGOZT4P5ol98=
|
git.arav.su/Arav/justguestbook v1.3.2/go.mod h1:Umo/AzSOKu+OU+03V7/QQ6m6N0nXyeRAGOZT4P5ol98=
|
||||||
|
github.com/a-h/templ v0.2.707 h1:T1Gkd2ugbRglZ9rYw/VBchWOSZVKmetDbBkm4YubM7U=
|
||||||
|
github.com/a-h/templ v0.2.707/go.mod h1:5cqsugkq9IerRNucNsI4DEamdHPsoGMQy99DzydLhM8=
|
||||||
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
|
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
|
||||||
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||||
github.com/gomarkdown/markdown v0.0.0-20230922112808-5421fefb8386 h1:EcQR3gusLHN46TAD+G+EbaaqJArt5vHhNpXAa12PQf4=
|
github.com/gomarkdown/markdown v0.0.0-20230922112808-5421fefb8386 h1:EcQR3gusLHN46TAD+G+EbaaqJArt5vHhNpXAa12PQf4=
|
||||||
github.com/gomarkdown/markdown v0.0.0-20230922112808-5421fefb8386/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
|
github.com/gomarkdown/markdown v0.0.0-20230922112808-5421fefb8386/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
|
||||||
|
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/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||||
github.com/mattn/go-sqlite3 v1.14.18 h1:JL0eqdCOq6DJVNPSvArO/bIV9/P7fbGrV00LZHc+5aI=
|
github.com/mattn/go-sqlite3 v1.14.18 h1:JL0eqdCOq6DJVNPSvArO/bIV9/P7fbGrV00LZHc+5aI=
|
||||||
github.com/mattn/go-sqlite3 v1.14.18/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
github.com/mattn/go-sqlite3 v1.14.18/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||||
@ -44,7 +48,6 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -36,7 +37,7 @@ func NewHandlers(captchaExpire time.Duration, owner string, gbPageSize int64,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handlers) Index(w http.ResponseWriter, r *http.Request) {
|
func (h *Handlers) Index(w http.ResponseWriter, r *http.Request) {
|
||||||
web.Index("", w)
|
web.Index().Render(context.Background(), w)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handlers) Privacy(w http.ResponseWriter, r *http.Request) {
|
func (h *Handlers) Privacy(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -146,7 +147,7 @@ func (h *Handlers) Guestbook(w http.ResponseWriter, r *http.Request) {
|
|||||||
dwc := dwcaptcha.NewDwellingCaptcha(h.captchaExpire)
|
dwc := dwcaptcha.NewDwellingCaptcha(h.captchaExpire)
|
||||||
_, id := inmemdb.New(r.RemoteAddr, dwc)
|
_, id := inmemdb.New(r.RemoteAddr, dwc)
|
||||||
|
|
||||||
web.Guestbook("/ Guestbook", h.owner, string(id), pageCount, entries, r, w)
|
web.Guestbook(string(id), h.owner, entries, pageCount, r).Render(context.Background(), w)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handlers) GuestbookAdmin(w http.ResponseWriter, r *http.Request) {
|
func (h *Handlers) GuestbookAdmin(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -174,7 +175,7 @@ func (h *Handlers) RSS(w http.ResponseWriter, r *http.Request) {
|
|||||||
scheme = "http"
|
scheme = "http"
|
||||||
}
|
}
|
||||||
|
|
||||||
web.RSS(scheme+"://"+r.Host, h.owner, posts, r, w)
|
web.RSS(scheme+"://"+r.Host, h.owner, posts, r).Render(context.Background(), w)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ServeAsset(path string) func(http.ResponseWriter, *http.Request) {
|
func ServeAsset(path string) func(http.ResponseWriter, *http.Request) {
|
||||||
@ -186,7 +187,7 @@ func ServeAsset(path string) func(http.ResponseWriter, *http.Request) {
|
|||||||
|
|
||||||
func Error(w http.ResponseWriter, code int, reason, message, referer string) {
|
func Error(w http.ResponseWriter, code int, reason, message, referer string) {
|
||||||
w.WriteHeader(code)
|
w.WriteHeader(code)
|
||||||
web.ErrorXXX("/ "+http.StatusText(code), reason, message, referer, code, w)
|
web.ErrorXXX(code, reason, message, referer).Render(context.Background(), w)
|
||||||
}
|
}
|
||||||
|
|
||||||
func cleanupNewlines(text string) (out string) {
|
func cleanupNewlines(text string) (out string) {
|
||||||
|
59
web/base.templ
Normal file
59
web/base.templ
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
templ base(title, description, keywords, canonical, heading string, head templ.Component) {
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
if title != "" {
|
||||||
|
<title>{ title } - Arav's dwelling</title>
|
||||||
|
} else {
|
||||||
|
<title>Arav's dwelling</title>
|
||||||
|
}
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta name="theme-color" content="#cd2682" />
|
||||||
|
<meta name="description" content={ description } />
|
||||||
|
<meta name="keywords" content={ keywords } />
|
||||||
|
<link rel="icon" href="/assets/img/favicon.svg" sizes="any" type="image/svg+xml" />
|
||||||
|
<link rel="stylesheet" href="/assets/css/main.css" />
|
||||||
|
<link rel="alternate" href="/rss.xml" type="application/rss+xml" title="Arav's dwelling" />
|
||||||
|
<link rel="canonical" href={ string(templ.URL(canonical)) }/>
|
||||||
|
@head
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<svg id="logo" viewBox="0 -25 216 40">
|
||||||
|
<text class="logo">Arav's dwelling</text>
|
||||||
|
<text class="under" y="11">Welcome to my sacred place, wanderer</text>
|
||||||
|
</svg>
|
||||||
|
<nav>
|
||||||
|
@navigation(heading)
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
{ children... }
|
||||||
|
<footer>
|
||||||
|
<a href="/rss.xml" title="Stay up to date on what's going on.">RSS feed</a>
|
||||||
|
<br>
|
||||||
|
© 2017—2024 Alexander "Arav" Andreev <<a href="mailto:me@arav.su">me@arav.su</a>> <a href="/privacy">Privacy statements</a>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
|
||||||
|
var navElements = []string{"Stuff", "Mindflow", "About", "Guestbook"}
|
||||||
|
|
||||||
|
templ navigation(heading string) {
|
||||||
|
for _, n := range navElements {
|
||||||
|
if n == heading {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
<a href={ templ.SafeURL("/" + strings.ToLower(n)) }>{ n }</a>
|
||||||
|
}
|
||||||
|
|
||||||
|
if heading != "" {
|
||||||
|
<h1>{ heading }</h1>
|
||||||
|
}
|
||||||
|
}
|
26
web/errorXXX.templ
Normal file
26
web/errorXXX.templ
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
templ ErrorXXX(errCode int, reason, message, referer string) {
|
||||||
|
{{ errText := http.StatusText(errCode) }}
|
||||||
|
@base(errText, errText,
|
||||||
|
"", "", errText, nil) {
|
||||||
|
<section id="error">
|
||||||
|
<h1>{ fmt.Sprint(errCode) }</h1>
|
||||||
|
{ errText }
|
||||||
|
if reason != "" {
|
||||||
|
<p>{ reason }</p>
|
||||||
|
}
|
||||||
|
if message != "" {
|
||||||
|
<p>{ message }</p>
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
if referer != "" {
|
||||||
|
<section>
|
||||||
|
<h2><a href={ templ.URL(referer) }>Go back</a></h2>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
88
web/guestbook.templ
Normal file
88
web/guestbook.templ
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
import "strconv"
|
||||||
|
import "strings"
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
import "git.arav.su/Arav/justguestbook"
|
||||||
|
|
||||||
|
import "git.arav.su/Arav/dwelling-home/pkg/util"
|
||||||
|
|
||||||
|
templ Guestbook(captchaID, owner string, entries []*justguestbook.Entry, pageCount int64, r *http.Request) {
|
||||||
|
@base("Guestbook",
|
||||||
|
"This is my guestbook. Welcome.",
|
||||||
|
"guestbook, personal", "/guestbook", "Guestbook", guestbookHead()) {
|
||||||
|
<form id="new-post" action="/api/guestbook" method="POST">
|
||||||
|
<input type="text" name="name" maxlength="80" placeholder="Name (Anonymous if left blank)" />
|
||||||
|
<input type="text" name="website" maxlength="255" placeholder="Website (optional)" />
|
||||||
|
<textarea name="message" maxlength="4096" placeholder="Your message" required></textarea>
|
||||||
|
<span class="checkboxes">
|
||||||
|
<input type="checkbox" name="hide_website" id="hide-website" checked />
|
||||||
|
<label for="hide-website">Hide website <small>(only I can see if set)</small></label>
|
||||||
|
<br>
|
||||||
|
<small>Use > to make a quote.</small>
|
||||||
|
</span>
|
||||||
|
<span class="captcha">
|
||||||
|
<input type="hidden" name="captcha_id" value={ captchaID } />
|
||||||
|
<img src={ string(templ.URL(fmt.Sprintf("/api/captcha/%s/image", captchaID))) } alt="CAPTCHA" width="160" height="40" />
|
||||||
|
<input type="text" name="captcha_answer" maxlength="6" placeholder="CAPTCHA" required />
|
||||||
|
<small>Valid for <b>10</b> minutes.</small>
|
||||||
|
</span>
|
||||||
|
<input type="submit" value="Send a post" />
|
||||||
|
</form>
|
||||||
|
<section id="posts">
|
||||||
|
for _, entry := range entries {
|
||||||
|
<article>
|
||||||
|
<header>
|
||||||
|
{{ created_tz := util.ToClientTimezone(entry.Created, r).Format("Monday _2 January 2006 15:04:05 -07:00") }}
|
||||||
|
{{ website := "" }}
|
||||||
|
if !entry.HideWebsite {
|
||||||
|
{{ website = entry.Website }}
|
||||||
|
}
|
||||||
|
{ strconv.FormatInt(entry.ID, 10) } by <span class="highlighted">{ entry.Name }</span> <em>{ website }</em> on <time datetime={ created_tz }>{ created_tz }</time>
|
||||||
|
</header>
|
||||||
|
for _, line := range strings.Split(entry.Message, "\n") {
|
||||||
|
if len(line) > 0 && line[0] == '>' {
|
||||||
|
<p class="quote">{ line }</p>
|
||||||
|
} else {
|
||||||
|
<p>{ line }</p>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if entry.Reply != nil {
|
||||||
|
<div class="reply">
|
||||||
|
<header>
|
||||||
|
{{ reply_created_tz := util.ToClientTimezone(entry.Reply.Created, r).Format("Monday _2 January 2006 15:04:05 -07:00") }}
|
||||||
|
Reply by <span class="highlighted">{ owner }</span> in <time datetime={ reply_created_tz }>{ reply_created_tz }</time>
|
||||||
|
</header>
|
||||||
|
for _, line := range strings.Split(entry.Reply.Message, "\n") {
|
||||||
|
if len(line) > 0 && line[0] == '>' {
|
||||||
|
<p class="quote">{ line }</p>
|
||||||
|
} else {
|
||||||
|
<p>{ line }</p>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</article>
|
||||||
|
}
|
||||||
|
if len(entries) == 0 {
|
||||||
|
<p class="center">No posts.</p>
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
if pageCount > 1 {
|
||||||
|
<section id="pagination">
|
||||||
|
{{ var n int64 = 1 }}
|
||||||
|
for ; n <= pageCount; n++ {
|
||||||
|
{{ strN := strconv.FormatInt(n, 10) }}
|
||||||
|
<a href={ templ.URL("/guestbook?p="+strN) }>{ strN }</a>
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
templ guestbookHead() {
|
||||||
|
<link rel="stylesheet" href="/assets/css/guestbook.css" />
|
||||||
|
<script src="/assets/js/captcha_refresh.js" defer></script>
|
||||||
|
}
|
67
web/index.templ
Normal file
67
web/index.templ
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
templ Index() {
|
||||||
|
@base("",
|
||||||
|
"A homepage of a russian guy Alexander aka Arav. Not just homepage, but something more...", "/",
|
||||||
|
"homepage, personal, blog, services, self-hosting", "", nil) {
|
||||||
|
<section id="services">
|
||||||
|
<span>
|
||||||
|
<a href="https://arav.su">arav.su</a>
|
||||||
|
.
|
||||||
|
<a href="http://moq7aejnf4xk5k2bkaltli3ftkhusy2mbrd3pj23nrca343ku2mgk4yd.onion">onion</a>
|
||||||
|
.
|
||||||
|
<a href="http://[300:a98d:d6d0:8a08::f]">ygg</a>
|
||||||
|
.
|
||||||
|
<a href="http://arav.i2p">i2p</a>
|
||||||
|
<sup><a href="http://arav.i2p/?i2paddresshelper=5Kl-DiWbbk6wf7m0v6zBSNHYq3sXlnrWLIWVeGdpPbPyc9CBS~zrzDYpP43rv1fRiIkbVCD5hTEpY6joQGlk-dFkWWD6201qa6ecsDVQMaE3Q7UTYICd0VEBRoqDUSrvsM-P2y5oG4Z-77RmoGKpbcRgNuMVbQ7AGJNqVSGej-lSyscDWTIZT5dCT505lfRwprdD~emZqkwnn22X16Wpj-X4A4ifph4idrThGioz4UW6PrCpa-oebMCo217s0Zyl9VKaU-o9cx5eFUEwnshoUjqwh7VE-S45NDz854J08xldCATM3wwTRVXhc2NUypsJLKFKiV0z3EXN-ApCdxsV60C-eiXUTX5vYcHHH~imA79v8WKFybjnsyUBst5BBEPQIUifTceLUrTmQ9TUpaMV90EsD5SCshmCfOs8R5y2dK6EfQu8iyYAB5VFSH4M1CLiBZUsDTEFiOomn2JGMDnbPho8lMB8ss4SMuwZShb2LlGqLxJ38kRHlvC68VmJO7InBQAEAAcAAA==" title="Address helper">ah</a></sup>
|
||||||
|
</span>
|
||||||
|
<a href="https://arav.neocities.org">arav.neocities.org</a>
|
||||||
|
<br>
|
||||||
|
<span>
|
||||||
|
<a href="https://radio.arav.su">radio</a>
|
||||||
|
.
|
||||||
|
<a href="http://wsmkgnmhmzqm7kyzv7jnzzafvgm7xlmlfvzhgorpapd5or2arnhuktqd.onion">onion</a>
|
||||||
|
.
|
||||||
|
<a href="http://[300:a98d:d6d0:8a08::e]">ygg</a>
|
||||||
|
.
|
||||||
|
<a href="http://radio.arav.i2p">i2p</a>
|
||||||
|
<sup><a href="http://radio.arav.i2p/?i2paddresshelper=NfCKBu9vjLFiBMEPQGiZT9AzGlhkKHzYrKM66FL-ESeDbnYUY--NzukO9UA28s3WThhDQVge2TmyfYsaZiUw~AjuLsykxS13pebs7lkAVY1jm77La-eFFIAQ22Vtd2YgS0vbhRMzuDxKkCR1vPwNax8R2o6a07xsQvvDml6UQxG4p5vt44JA2geQNvQfm8cEiSa6gNJZJSW3rWuLDg6~1Jy3D70oSVSlNfihmG4JtNV6tVBjJE2h5gUxfhYZACttpGTPM~UNF~lrSujlBQsCqdzvLswdMw~FnvpfGzJcJroeFTerRyH6oUkkDSOK7uWwl0e70vKxrIbFgJjKtjlLWlUCI5N0TnJP4Hzt2pttB~R0hSr2vVl8ky0yJEtN3rwnrJkw7q0ZIH30ngTfxsCTbolAzl6liN9Ez5YF97zDOPnVFmvQ6Eg1PyFdypQO1PiUHqF56SWhx3utGwecUS6jJCvsKIJJVEIMVcD6h7S0z1g1rqQ4jbg5UfXPwFEgFOlzBQAEAAcAAA==" title="Address helper">ah</a></sup>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<a href="https://files.arav.su">files</a>
|
||||||
|
.
|
||||||
|
<a href="http://qf5e43nlhvnrutmikuvbdfj3cmtthokpbaxtkm6mjlslttzvtgm4fxid.onion">onion</a>
|
||||||
|
.
|
||||||
|
<a href="http://[300:a98d:d6d0:8a08::e]">ygg</a>
|
||||||
|
.
|
||||||
|
<a href="http://files.arav.i2p">i2p</a>
|
||||||
|
<sup><a href="http://files.arav.i2p/?i2paddresshelper=48vtYgeVnju7B2FaR0zxUL3MQXN9QjK~Ggya45aANwm86mtpemuEkaskJmEQaFSd4FcDAFIiXHfNpfGqoupLwNmtgBmGRcuVV8xb2W~W6lM0oOhovjB37EUaMWs3AI5aIES84QOqApgwYX-ANIcwa~Kg6AbMuX8D8qnejuhBbuCffYah-TD8e~O0cnyqxzLTmxIGCyk2egdYXwanJyYFDocomIVfcqfJ0MgjIHhFQtkcb0e84bxvDzcAFIpEDrzAo4GVrFn-TCu0Lyf2ccqmVpucFl0UGhuVRxEt19KLd3PxlfwHv2lmzTZtq9CbnfaoPntUPx1sf84QnZDmrXWhVK8p3VvuPZMxjyz9KyhPjrGkO4E0oibDlvKuMsGEm-GkZsKxgXo~CrdcVtN8suAwW6rACAuk8gq2jUMBZBZ12migPZ7miHftEkOFHfgfUiKBwirrw~y9Zi261WX4-EVe2oD4pkhQOrqOKIoI-vv5z9CpQ7PKL531kgkipcBseXybBQAEAAcAAA==" title="Address helper">ah</a></sup>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<a href="https://upload.arav.su">upload</a>
|
||||||
|
.
|
||||||
|
<a href="http://4usftbmjpfexkr2x5xbp5ukmygpmg4fgrnx2wbifsexqctooz5hmviyd.onion">onion</a>
|
||||||
|
.
|
||||||
|
<a href="http://[300:a98d:d6d0:8a08::e]">ygg</a>
|
||||||
|
.
|
||||||
|
<a href="http://upload.arav.i2p">i2p</a>
|
||||||
|
<sup><a href="http://upload.arav.i2p/?i2paddresshelper=b5NWA2vNydWSv6~8KN4e~td2UVGkYsayKPa1PnXI87A3gsg6m978tIehHLVN4XcCfUq4aB-59hqqZicorRnHKfV3lVdx9mdhC8Bhj~bMAcwMgWoXidqZNrWMoFGzWotFsa3nWh4zsRUSfrokecC8u9Y06byfSS1siyak0J6xpsggXRqqgNF0-8ncPeqvzBxHB9NRDXWEVJGS9HSpydWl1UpjgZffcd~NZroxkSAfughHcFAn2OLKkaZRe6WqCJQfJoXTCyz4wkFmYbH1CSddWlddmWaaU7icsbQrZm3XEqKTVKvm86G6ehxmzyHqCumc4GOWswcP0E51UQVOv-WA8R6SWQAj6ZnZhnCoCNFFEfW2lBiDmTnLJbfm-C-AdI6G1~dQ1~3FCH6wXWy-2DebpyoVVt9epzU7l4l2MVeaOUahbf6wcol1UbxPoR0XlGCXDe9700TYePjtpOU9vNkk2B1dQiZ1usgwseYuO26cRogSvbi8poz4BlCNO733HR1XBQAEAAcAAA==" title="Address helper">ah</a></sup>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<a href="https://git.arav.su">git</a>
|
||||||
|
.
|
||||||
|
<a href="http://qqitm7qlsbbubwmjos4cqzmvkqidg34rfnbyhuydhalep33fbvh22xyd.onion">onion</a>
|
||||||
|
.
|
||||||
|
<a href="http://[300:a98d:d6d0:8a08::e]">ygg</a>
|
||||||
|
.
|
||||||
|
<a href="http://git.arav.i2p">i2p</a>
|
||||||
|
<sup><a href="http://git.arav.i2p/?i2paddresshelper=eFIfcBUv3lHFSnglHfncs5XXtYwm9gCpmAYuio~9CeENBAXKRggPiY1tQC-otCon2hCSpr56WlVBeZk1txKuUnbjHTN7GBFaKW5wJEO2WmKEWPKdcjUDOYZN0D3TwXaYfiBuELD3200lBfDmPEJ01iC2o7B5yvpOqtEKDcaqkIp4vafDuPPumJ~XiCGdUAe~vr52w3Tbuz5x7wbltk-gUELY0-ZAQBos4jOJ6QT1W1lhycHPhAK8qslgwfk94opyIl2pkRyuJhU-2VHc6Fsd621VXC86YAMT1SIfTZlFpoGVCFXDM~BXaLvygFaKf62qardAe0T48Ax6GxosAKXe-yLCVRaiD3KErULfwZXl23kQzRfxM4odG4DWeXawtuvypOmTjHT1skQHU0h52ujye5nT~2bOy14HkCoCnxJ7gSj3MjkmWLd1JhBsPH4ymRmI7jFJR1GYl8Wp5IigMBBzWfJUEEjS7QDHaRo5TCZJ9SXz6sgkGdfh74~r8FWL559gBQAEAAcAAA==" title="Address helper">ah</a></sup>
|
||||||
|
</span>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<p>
|
||||||
|
Welcome, Anon. I'm Alexander aka Arav, I self-host some services for myself, friends and you. Not much I have to offer, but maybe you'll find something useful for yourself. :)
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
|
}
|
38
web/rss.templ
Normal file
38
web/rss.templ
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
import "strings"
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
import "git.arav.su/Arav/dwelling-home/pkg/mindflow"
|
||||||
|
import "git.arav.su/Arav/dwelling-home/pkg/util"
|
||||||
|
|
||||||
|
templ RSS(host, author string, posts []mindflow.Post, r *http.Request) {
|
||||||
|
@templ.Raw("<?xml version=\"1.0\" encoding=\"utf-8\" ?>")
|
||||||
|
<rss version="2.0">
|
||||||
|
<channel>
|
||||||
|
<title>Arav's dwelling</title>
|
||||||
|
<description>What's going on with me and my machines.</description>
|
||||||
|
<language>en-gb</language>
|
||||||
|
<link>{ host }</link>
|
||||||
|
|
||||||
|
for _, post := range posts {
|
||||||
|
<item>
|
||||||
|
<title>{ post.Title }</title>
|
||||||
|
<category>{ post.Category.Name }</category>
|
||||||
|
<guid>{ post.PostID() }</guid>
|
||||||
|
<pubDate>{ util.ToClientTimezone(post.Date, r).Format(time.RFC1123Z) }</pubDate>
|
||||||
|
<link>{ post.PostURL(r.Host, true) }</link>
|
||||||
|
<author>{ author }</author>
|
||||||
|
<description>
|
||||||
|
@templ.Raw("<![CDATA[")
|
||||||
|
for _, line := range strings.Split(post.Body, "\n") {
|
||||||
|
<p>{ line }</p>
|
||||||
|
}
|
||||||
|
@templ.Raw("]]>")
|
||||||
|
</description>
|
||||||
|
</item>
|
||||||
|
}
|
||||||
|
</channel>
|
||||||
|
</rss>
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user