From 89de89671a81e1f1e585ba5c10c42cf9be01ddeb Mon Sep 17 00:00:00 2001 From: "Alexander \"Arav\" Andreev" Date: Wed, 19 Jun 2024 01:08:05 +0400 Subject: [PATCH] Current state of porting to a Templ template engine. Stage 2. --- internal/http/web_handlers.go | 26 +++-- web/about.templ | 11 +++ web/article.templ | 26 +++++ web/assets/css/error.css | 6 +- web/assets/css/guestbook.css | 2 + web/base.templ | 103 ++++++++++---------- web/errorXXX.templ | 39 ++++---- web/guestbook.templ | 148 ++++++++++++++--------------- web/guestbook_admin.templ | 9 ++ web/index.templ | 97 +++++++------------ web/mindflow.templ | 56 +++++++++++ web/mindflow_admin.templ | 9 ++ web/privacy.templ | 29 ++++++ web/rss.templ | 59 ++++++------ web/stuff.templ | 172 ++++++++++++++++++++++++++++++++++ 15 files changed, 543 insertions(+), 249 deletions(-) create mode 100644 web/about.templ create mode 100644 web/article.templ create mode 100644 web/guestbook_admin.templ create mode 100644 web/mindflow.templ create mode 100644 web/mindflow_admin.templ create mode 100644 web/privacy.templ create mode 100644 web/stuff.templ diff --git a/internal/http/web_handlers.go b/internal/http/web_handlers.go index 378e471..ef74357 100644 --- a/internal/http/web_handlers.go +++ b/internal/http/web_handlers.go @@ -37,31 +37,29 @@ func NewHandlers(captchaExpire time.Duration, owner string, gbPageSize int64, } func (h *Handlers) Privacy(w http.ResponseWriter, r *http.Request) { - - web.Privacy("/ Privacy statements", - util.GetServiceByHost(r.Host, util.ServiceRadio), - util.GetServiceByHost(r.Host, util.ServiceRadio), - util.GetServiceByHost(r.Host, util.ServiceUpload), w) + web.Privacy(r).Render(context.Background(), w) } func (h *Handlers) Stuff(w http.ResponseWriter, r *http.Request) { - web.Stuff("/ Stuff", util.GetServiceByHost(r.Host, util.ServiceGit), - util.GetServiceByHost(r.Host, util.ServiceFiles), web.Metadata, w) + web.Stuff(r).Render(context.Background(), w) } func (h *Handlers) Mindflow(w http.ResponseWriter, r *http.Request) { posts, err := h.mindflowDB.Posts() if err != nil { + log.Println("Cannot load posts.", err) + Error(w, http.StatusInternalServerError, "Cannot load posts.", "", r.Referer()) return } categories, err := h.mindflowDB.Categories() if err != nil { - log.Println(err) + log.Println("Cannot load categories.", err) + Error(w, http.StatusInternalServerError, "Cannot load categories.", "", r.Referer()) return } - web.Mindflow("/ Mindflow", posts, categories, r, w) + web.Mindflow(posts, categories, r).Render(context.Background(), w) } func (h *Handlers) MindflowAdmin(w http.ResponseWriter, r *http.Request) { @@ -77,7 +75,7 @@ func (h *Handlers) MindflowAdmin(w http.ResponseWriter, r *http.Request) { return } - web.MindflowAdmin("/ Mindflow Administration", posts, categories, r, w) + web.MindflowAdmin(posts, categories, r).Render(context.Background(), w) } func (h *Handlers) About(w http.ResponseWriter, r *http.Request) { @@ -95,18 +93,18 @@ func (h *Handlers) About(w http.ResponseWriter, r *http.Request) { } } - web.About("/ About", util.GetServiceByHost(r.Host, util.ServiceFiles), lst, w) + web.About(util.GetServiceByHost(r.Host, util.ServiceFiles), lst).Render(context.Background(), w) } func (h *Handlers) Article(w http.ResponseWriter, r *http.Request) { name := r.URL.Path[strings.LastIndex(r.URL.Path, "/")+1:] artcl := web.GetArticle(name) if artcl == nil { - http.Error(w, "an article doesn't exist", http.StatusNotFound) + Error(w, http.StatusNotFound, "", "An article \""+name+"\" doesn't exist.", r.Referer()) return } - web.Article(artcl.Title, artcl.Description, string(artcl.Body), artcl.Date, r.Host, name, r, w) + web.Article(artcl.Title, artcl.Description, string(artcl.Body), name, artcl.Date, r).Render(context.Background(), w) } func (h *Handlers) Guestbook(w http.ResponseWriter, r *http.Request) { @@ -157,7 +155,7 @@ func (h *Handlers) GuestbookAdmin(w http.ResponseWriter, r *http.Request) { entry.Message = strings.ReplaceAll(entry.Message, "\\n", "\n") } - web.GuestbookAdmin("/ Guestbook Administration", h.owner, entries, r, w) + web.GuestbookAdmin(h.owner, entries, r).Render(context.Background(), w) } func (h *Handlers) RSS(w http.ResponseWriter, r *http.Request) { diff --git a/web/about.templ b/web/about.templ new file mode 100644 index 0000000..8548276 --- /dev/null +++ b/web/about.templ @@ -0,0 +1,11 @@ +package web + +import "git.arav.su/Arav/dwelling-home/pkg/servicestat" + +templ About(filesSrvc string, services servicestat.ServiceList) { + @base("About", "About me and my home servers.", "about, me, servcies", "/about", "About", aboutHead()) +} + +templ aboutHead() { + +} diff --git a/web/article.templ b/web/article.templ new file mode 100644 index 0000000..ec4249e --- /dev/null +++ b/web/article.templ @@ -0,0 +1,26 @@ +package web + +import "time" +import "net/http" + +import "git.arav.su/Arav/dwelling-home/pkg/util" + +templ Article(title, description, body, urlName string, date time.Time, r *http.Request) { + @base(title + " - Articles", description, "", "/stuff/article/"+urlName, "Articles", articleHead()) { +
+
+

{ title }

+ +
+ @templ.Raw(body) +
+ } +} + +templ articleHead() { + +} diff --git a/web/assets/css/error.css b/web/assets/css/error.css index 55028f2..2e76222 100644 --- a/web/assets/css/error.css +++ b/web/assets/css/error.css @@ -4,4 +4,8 @@ text-align: center; margin: 6rem 0; } -#error h1 { font-size: 8rem; } \ No newline at end of file +#error h1 { font-size: 8rem; } + +#error p { + font-size: 1.1rem; + text-align: center; } \ No newline at end of file diff --git a/web/assets/css/guestbook.css b/web/assets/css/guestbook.css index 6503a67..9c095b5 100755 --- a/web/assets/css/guestbook.css +++ b/web/assets/css/guestbook.css @@ -58,6 +58,8 @@ button.refresh:disabled { text-align: center; width: 4.5rem; } +article p { white-space: pre; } + input[type="submit"] { background-color: var(--primary-color); border: none; diff --git a/web/base.templ b/web/base.templ index 4ca3133..35cb25a 100644 --- a/web/base.templ +++ b/web/base.templ @@ -1,59 +1,64 @@ package web -import "strings" +// import "strings" templ base(title, description, keywords, canonical, heading string, head templ.Component) { - - - - if title != "" { - { title } - Arav's dwelling - } else { - Arav's dwelling - } - - - - - - - - - - - @head - - -
- - -
- { children... } - - - + + + + if title != "" { + { title } - Arav's dwelling + } else { + Arav's dwelling + } + + + + + + + + + + if canonical != "" { + + } + if head != nil { + @head + } + + +
+ + @navigation(heading) +
+ { children... } + + + } var navElements = []string{"Stuff", "Mindflow", "About", "Guestbook"} templ navigation(heading string) { - for _, n := range navElements { - if n == heading { - continue - } - { n } - } + +} diff --git a/web/errorXXX.templ b/web/errorXXX.templ index c6d16aa..f296b27 100644 --- a/web/errorXXX.templ +++ b/web/errorXXX.templ @@ -4,23 +4,26 @@ import "fmt" import "net/http" templ ErrorXXX(errCode int, reason, message, referer string) { - {{ errText := http.StatusText(errCode) }} - @base(errText, errText, - "", "", errText, nil) { -
-

{ fmt.Sprint(errCode) }

- { errText } - if reason != "" { -

{ reason }

- } - if message != "" { -

{ message }

- } -
- if referer != "" { -
-

Go back

-
- } + {{ errText := http.StatusText(errCode) }} + @base(errText, errText, "", "", errText, errorXXXHead()) { +
+

{ fmt.Sprint(errCode) }

+ { errText } + if reason != "" { +

{ reason }

+ } + if message != "" { +

{ message }

+ } +
+ if referer != "" { +
+

Go back

+
+ } + } } + +templ errorXXXHead() { + } \ No newline at end of file diff --git a/web/guestbook.templ b/web/guestbook.templ index 5ef9c37..5cf0477 100644 --- a/web/guestbook.templ +++ b/web/guestbook.templ @@ -10,79 +10,81 @@ 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()) { -
- - - - - - -
- Use > to make a quote. -
- - - CAPTCHA - - Valid for 10 minutes. - - -
-
- for _, entry := range entries { -
-
- {{ 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 { entry.Name } { website } on -
- for _, line := range strings.Split(entry.Message, "\n") { - if len(line) > 0 && line[0] == '>' { -

{ line }

- } else { -

{ line }

- } - } - if entry.Reply != nil { -
-
- {{ reply_created_tz := util.ToClientTimezone(entry.Reply.Created, r).Format("Monday _2 January 2006 15:04:05 -07:00") }} - Reply by { owner } in -
- for _, line := range strings.Split(entry.Reply.Message, "\n") { - if len(line) > 0 && line[0] == '>' { -

{ line }

- } else { -

{ line }

- } - } -
- } -
- } - if len(entries) == 0 { -

No posts.

- } -
- if pageCount > 1 { -
- {{ var n int64 = 1 }} - for ; n <= pageCount; n++ { - {{ strN := strconv.FormatInt(n, 10) }} - + + + + + + +
+ Use > to make a quote. +
+ + + CAPTCHA + + Valid for 10 minutes. + + + +
+ for _, entry := range entries { +
+
+ {{ 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 { entry.Name } { website } on +
+ for _, line := range strings.Split(entry.Message, "\n") { + if len(line) > 0 && line[0] == '>' { +

@templ.Raw(line) +

+ } else { +

@templ.Raw(line) +

+ } + } + if entry.Reply != nil { +
+
+ {{ reply_created_tz := util.ToClientTimezone(entry.Reply.Created, r).Format("Monday _2 January 2006 15:04:05 -07:00") }} + Reply by { owner } in +
+ for _, line := range strings.Split(entry.Reply.Message, "\n") { + if len(line) > 0 && line[0] == '>' { +

@templ.Raw(line) +

+ } else { +

@templ.Raw(line) +

+ } + } +
+ } +
+ } + if len(entries) == 0 { +

No posts.

+ } +
+ if pageCount > 1 { +
+ {{ var n int64 = 1 }} + for ; n <= pageCount; n++ { + {{ strN := strconv.FormatInt(n, 10) }} + - -} \ No newline at end of file + + +} diff --git a/web/guestbook_admin.templ b/web/guestbook_admin.templ new file mode 100644 index 0000000..12cb008 --- /dev/null +++ b/web/guestbook_admin.templ @@ -0,0 +1,9 @@ +package web + +import "net/http" + +import "git.arav.su/Arav/justguestbook" + +templ GuestbookAdmin(owner string, entries []*justguestbook.Entry, r *http.Request) { + @base("Guestbook Administration", "", "", "/guestbook/admin", "Guestbook Administration", nil) +} diff --git a/web/index.templ b/web/index.templ index ebc5d00..70e3f62 100644 --- a/web/index.templ +++ b/web/index.templ @@ -1,67 +1,36 @@ 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) { -
- - arav.su - . - onion - . - ygg - . - i2p - ah - - arav.neocities.org -
- - radio - . - onion - . - ygg - . - i2p - ah - - - files - . - onion - . - ygg - . - i2p - ah - - - upload - . - onion - . - ygg - . - i2p - ah - - - git - . - onion - . - ygg - . - i2p - ah - -
-
-

- 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. :) -

-
- } -} \ No newline at end of file + @base("", + "A homepage of a russian guy Alexander aka Arav. Not just homepage, but something more...", "/", + "homepage, personal, blog, services, self-hosting", "Home", indexHead()) { +
+ + arav.su.onion.ygg.i2pah + + arav.neocities.org +
+ + radio.onion.ygg.i2pah + + + files.onion.ygg.i2pah + + + upload.onion.ygg.i2pah + + + git.onion.ygg.i2pah + +
+
+

+ 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. :) +

+
+ } +} + +templ indexHead() { + +} diff --git a/web/mindflow.templ b/web/mindflow.templ new file mode 100644 index 0000000..a874848 --- /dev/null +++ b/web/mindflow.templ @@ -0,0 +1,56 @@ +package web + +import "net/http" +import "strings" +import "time" + +import "git.arav.su/Arav/dwelling-home/pkg/mindflow" +import "git.arav.su/Arav/dwelling-home/pkg/util" + +templ Mindflow(posts []mindflow.Post, categories []mindflow.Category, r *http.Request) { + @base("Mindflow", "Updates on my infrastructure, my very important opinions and thoughts.", "updates, thoughts, opinions, blog, diary", "/mindflow", "Mindflow", mindflowHead()) { +

Here I post updates on websites and infrastructure, my very important opinions and thoughts no one asked for. If you'd like to subscribe to this bullshittery then RSS feed at your service. :)

+ +
+ for _, post := range posts { + + } + if len(posts) == 0 { +

Nothing? There must be some... Looks like database went down.

+ } +
+ } +} + +templ mindflowHead() { + + +} diff --git a/web/mindflow_admin.templ b/web/mindflow_admin.templ new file mode 100644 index 0000000..58d1673 --- /dev/null +++ b/web/mindflow_admin.templ @@ -0,0 +1,9 @@ +package web + +import "net/http" + +import "git.arav.su/Arav/dwelling-home/pkg/mindflow" + +templ MindflowAdmin(posts []mindflow.Post, categories []mindflow.Category, r *http.Request) { + @base("Mindflow Administration", "", "", "/mindflow/admin", "Mindflow Administration", nil) +} diff --git a/web/privacy.templ b/web/privacy.templ new file mode 100644 index 0000000..a806992 --- /dev/null +++ b/web/privacy.templ @@ -0,0 +1,29 @@ +package web + +import "net/http" + +import "git.arav.su/Arav/dwelling-home/pkg/util" + +templ Privacy(r *http.Request) { + @base("Privacy", "Privacy statements for all of my services.", "privacy statements", "/privacy", "Privacy", nil) { +
+

Privacy statements

+

General data

+

Across all of my Web-services following data is being collected: date of access, IP-address, User-Agent, referer URL, request URL.

+

Use of JavaScript

+

JS is used on a main website at a guestbook page to refresh CAPTCHA; at mindflow page to filter posts by categories.

+

Radio service uses JS to update Last N songs list section, and to get current radio statistics.

+

Files service uses JS to add functionality such as an overlay to view files without the need to leave a site, and implements some keyboard control for convenience.

+

Upload service specific

+

Every action (upload, download, and delete) is being logged and includes this additional data:

+
    +
  1. File's name it was uploaded/downloaded with;
  2. +
  3. Unsalted SHA-256 hash of a file;
  4. +
  5. A salted hash encoded as base64 in raw URL variant that is used to download/delete a file;
  6. +
  7. File's size.
  8. +
+

And I will cooperate with law enforcements and provide them with all information (logs and a file itself if it is still present).

+

As already stated at Upload service's page, file's content must comply with law of Russian Federation. Anything like extremist materials, CP, and so on is forbidden.

+
+ } +} diff --git a/web/rss.templ b/web/rss.templ index 2849cb0..5e8efe5 100644 --- a/web/rss.templ +++ b/web/rss.templ @@ -1,38 +1,37 @@ package web -import "time" -import "strings" import "net/http" +import "strings" +import "time" 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("") - - - Arav's dwelling - What's going on with me and my machines. - en-gb - { host } - - for _, post := range posts { - - { post.Title } - { post.Category.Name } - { post.PostID() } - { util.ToClientTimezone(post.Date, r).Format(time.RFC1123Z) } - { post.PostURL(r.Host, true) } - { author } - - @templ.Raw("{ line }

- } - @templ.Raw("]]>") -
-
- } -
-
-} \ No newline at end of file + @templ.Raw("") + + + Arav's dwelling + What's going on with me and my machines. + en-gb + { host } + for _, post := range posts { + + { post.Title } + { post.Category.Name } + { post.PostID() } + { util.ToClientTimezone(post.Date, r).Format(time.RFC1123Z) } + { post.PostURL(r.Host, true) } + { author } + + @templ.Raw("{ line }

+ } + @templ.Raw("]]>") +
+
+ } +
+
+} diff --git a/web/stuff.templ b/web/stuff.templ new file mode 100644 index 0000000..317997c --- /dev/null +++ b/web/stuff.templ @@ -0,0 +1,172 @@ +package web + +import "net/http" + +import "git.arav.su/Arav/dwelling-home/pkg/util" + +templ Stuff(r *http.Request) { + {{ gitSite := util.GetServiceByHost(r.Host, util.ServiceGit) }} + + @base("Stuff", "Here I share my programs, scripts, articles, may be other stuff.", "articles, programs, personal projects, own music", "/stuff", "Stuff", nil) { +

Here lies everything I've made that I'm willing to share.

+
+

Articles

+ + + + + + for _, entry := range Metadata { + + + + + } +
Last UpdateArticle
+ + + { entry.Title } +
+
+
+

Programs and scripts

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
mccl0.1.2 (14 December 2023)GoGPLv3 + source + releases +
+

A console Minecraft launcher. Take a look at README.md for explanation.

+
httpr0.3.2 (20 September 2023)GoMIT + source + releases +
+

A simple HTTP router that supports having both regular and parametrised path's parts at the same level, like /assets/*filepath and /:a/:b with the same HTTP method.

+

Yeah, not as efficient as httprouter is, but in my case no performance loose was noticed (on a small amount of paths). Yet I gained prettiness because it allowed me, for example, to ditch /f/ part and leave just /:hash/:name instead of /f/:hash/:name in a dwelling-upload service.

+
justguestbook1.3.2 (22 May 2023)GoMIT + source +
+

A simple guestbook with owner's replies implementation made into a library.

+
justcaptcha2.1.0 (12 August 2023)GoMIT + source +
+

A simple CAPTCHA implementation.

+
kwh-cost1.1.3 (21 May 2024)CMIT+NIGGER + source +
+

KWh cost calculator in C.

+
httpprocprobed3.1.1 (13 January 2024)GoMIT+NIGGER + source +
+

It returns a list of processes and if they are running in a JSON format via HTTP GET request on /processes endpoint. Under the hood it searches in a /proc/ directory. It is being used on about page to show wether service's running or not.

+
ScrapTheChan0.5.0 (3 May 2021)PythonMIT + source + releases +
+

Imageboards file scraper using theirs JSON API. Currently supported: 4chan.org, lainchan.org, 2ch.hk and 8kun.top.

+
OpenNIC active domains extraction script10 July 2020BashMIT + source +
+

Bash script for BIND9 that extracts domains from zone files of OpenNIC and forms a list of domains that have an IP-address.

+
PiggyBank1.0.0 (8 July 2020)PythonMIT + source + releases +
+

A program to help you to keep track of your piggy bank. A simple script I once wrote that I rewrote and made into a package just to learn how to do it.

+
+
+
+

Music

+

There was a period in my life when I was playing with audio sequencers. I lost all project files and only 3 tracks survived in mp3 and they can be found here.

+
+ } +}