1
0

Compare commits

...

18 Commits

Author SHA1 Message Date
c3f9b8db14
Version set to 24.34.0. 2024-08-19 02:05:14 +04:00
51699ca458
Добавил русский текст на главную. :) 2024-08-19 01:50:45 +04:00
ea87940595
Update dependencies. 2024-08-19 01:47:44 +04:00
6726a7989c
Ported mindflow admin page to Templ. 2024-07-13 19:58:12 +04:00
f59da2a76e
Updated Makefile. 2024-07-12 17:09:21 +04:00
4cc8b0c761
In Makefile remove commented out sed command. 2024-07-12 16:19:17 +04:00
52e1117b7d
Updated templ dependency. 2024-07-12 16:18:22 +04:00
4b1f7ea763
In Stuff version of httpprocprobed was set to 3.2.0. 2024-07-07 19:54:21 +04:00
a5ccd81f28
Added listen option for IPv6. Added allow option for my IPv6-prefix. 2024-07-07 19:40:57 +04:00
b4171bed0d
Version set to 24.27.1. 2024-07-04 04:00:21 +04:00
2f339f6e3e
Updated sitemap.xml. 2024-07-04 03:57:10 +04:00
6e693e3b88
Introduced correct canonical link (includes schema://host). 2024-07-04 03:56:57 +04:00
da5745b368
Fixed Makefile clean. 2024-07-04 03:55:49 +04:00
3bed5474c4
Hotfix for mistaken keywords and canonical args in templ Index(). 2024-07-04 03:44:17 +04:00
20cf45bfeb
Do not convert mindflow's post date to client's TZ twice. 2024-07-02 17:28:38 +04:00
eceb06aa57
Remove about.pug and banners. 2024-07-02 17:26:58 +04:00
b13f287d5f
Place a program's version in a footer of a site. 2024-07-02 04:12:50 +04:00
cdabb77c39
Implemented a package for storing program's version. 2024-07-02 04:12:26 +04:00
28 changed files with 156 additions and 368 deletions

View File

@ -2,25 +2,28 @@ TARGET=dwelling-home
SYSDDIR_=${shell pkg-config systemd --variable=systemdsystemunitdir}
SYSDDIR=${SYSDDIR_:/%=%}
DESTDIR:=
PREFIX:=/usr/local
VERSION=24.27.0
DESTDIR?=
PREFIX?=/usr/local
FLAGS:=-buildmode=pie -modcacherw -mod=readonly -trimpath
LDFLAGS=-ldflags "-s -w -X main.version=${VERSION}" -tags osusergo,netgo
VERSION?=24.34.0
XVER:=git.arav.su/Arav/dwelling-home/internal/version.ver=v${VERSION}
GOFLAGS:=-buildmode=pie -trimpath -mod=readonly -modcacherw
GOFLAGS+=-ldflags="-linkmode=external -s -w -X '${XVER}' -extldflags=${LDFLAGS}"
GOFLAGS+=-tags osusergo,netgo
.PHONY: run install uninstall clean
${TARGET}: web/*_templ.go
go build -o bin/$@ ${LDFLAGS} ${FLAGS} cmd/$@/main.go
go build ${GOFLAGS} -o bin/$@ cmd/$@/main.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
# sed -ri 's/a> "/a>"/g' web/*_templ.go
run: | ${TARGET}
bin/${TARGET} -listen 127.0.0.1:18123 -database-path . -captcha-expiry 10m \
@ -42,4 +45,4 @@ uninstall:
clean:
rm -f bin/${TARGET}
rm -f web/*.pug.go web/jade.go
rm -f web/*_templ.go

View File

@ -1,6 +1,6 @@
# Maintainer: Alexander "Arav" Andreev <me@arav.su>
pkgname=dwelling-home
pkgver=24.27.0
pkgver=24.34.0
pkgrel=1
pkgdesc="Arav's dwelling / Home"
arch=('i686' 'x86_64' 'arm' 'armv6h' 'armv7h' 'aarch64')

View File

@ -12,15 +12,13 @@ import (
"time"
dwhttp "git.arav.su/Arav/dwelling-home/internal/http"
"git.arav.su/Arav/dwelling-home/internal/version"
mfsqlite "git.arav.su/Arav/dwelling-home/pkg/mindflow/database/sqlite"
"git.arav.su/Arav/dwelling-home/web"
"git.arav.su/Arav/httpr"
gb "git.arav.su/Arav/justguestbook"
"github.com/a-h/templ"
)
var version string
var (
showVersion = flag.Bool("v", false, "show version")
listenAddress = flag.String("listen", "/var/run/dwelling-home/sock", "listen address (ip:port|unix_path)")
@ -34,7 +32,7 @@ func main() {
flag.Parse()
if *showVersion {
fmt.Println("dwelling-home Ver.", version, "\nCopyright (c) 2023 Alexander \"Arav\" Andreev <me@arav.su>")
fmt.Println("dwelling-home", version.GetVersion(), "\nCopyright (c) 2023,2024 Alexander \"Arav\" Andreev <me@arav.su>")
return
}
@ -57,7 +55,7 @@ func main() {
r := httpr.New()
r.NotFoundHandler = func(w http.ResponseWriter, r *http.Request) {
dwhttp.Error(w, http.StatusNotFound, "", "", r.Referer())
dwhttp.Error(w, http.StatusNotFound, "", "", r)
}
r.ServeStatic("/assets/*filepath", web.Assets())
@ -66,7 +64,7 @@ func main() {
r.Handler(http.MethodGet, "/rss.xml", hand.RSS)
r.Handler(http.MethodGet, "/sitemap.xml", dwhttp.ServeAsset("sitemap.xml"))
r.Handler(http.MethodGet, "/", templ.Handler(web.Index()).ServeHTTP)
r.Handler(http.MethodGet, "/", hand.Index)
r.Handler(http.MethodGet, "/privacy", hand.Privacy)
r.Handler(http.MethodGet, "/stuff", hand.Stuff)
r.Handler(http.MethodGet, "/stuff/article/*filepath", hand.Article)

View File

@ -1,5 +1,6 @@
server {
listen 443 http2;
listen [::]:443 http2;
listen 8090; # Tor I2P
listen [300:a98d:d6d0:8a08::f]:80; # Yggdrasil
@ -38,6 +39,7 @@ server {
proxy_set_header Schema $scheme;
allow 192.168.144.0/25;
allow 2a03:e2c0:4a0f::/48;
deny all;
}
@ -50,6 +52,7 @@ server {
proxy_set_header Schema $scheme;
allow 192.168.144.0/25;
allow 2a03:e2c0:4a0f::/48;
deny all;
}
@ -66,6 +69,7 @@ server {
proxy_buffering off;
allow 192.168.144.0/25;
allow 2a03:e2c0:4a0f::/48;
deny all;
}
@ -74,6 +78,7 @@ server {
proxy_buffering off;
allow 192.168.144.0/25;
allow 2a03:e2c0:4a0f::/48;
deny all;
}

6
go.mod
View File

@ -7,15 +7,15 @@ toolchain go1.22.4
require (
git.arav.su/Arav/justcaptcha/v2 v2.1.0
git.arav.su/Arav/justguestbook v1.3.2
github.com/a-h/templ v0.2.731
github.com/gomarkdown/markdown v0.0.0-20240626202925-2eda941fd024
github.com/a-h/templ v0.2.747
github.com/gomarkdown/markdown v0.0.0-20240730141124-034f12af3bf6
github.com/pkg/errors v0.9.1
)
require (
github.com/fogleman/gg v1.3.0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
golang.org/x/image v0.18.0 // indirect
golang.org/x/image v0.19.0 // indirect
)
require (

12
go.sum
View File

@ -4,14 +4,14 @@ 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/justguestbook v1.3.2 h1:B1AzS14dW3bVlp0Qj6pu1PrJlWaPK1mplmx3UNBcxo4=
git.arav.su/Arav/justguestbook v1.3.2/go.mod h1:Umo/AzSOKu+OU+03V7/QQ6m6N0nXyeRAGOZT4P5ol98=
github.com/a-h/templ v0.2.731 h1:yiv4C7whSUsa36y65O06DPr/U/j3+WGB0RmvLOoVFXc=
github.com/a-h/templ v0.2.731/go.mod h1:IejA/ecDD0ul0dCvgCwp9t7bUZXVpGClEAdsqZQigi8=
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/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/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/gomarkdown/markdown v0.0.0-20240626202925-2eda941fd024 h1:saBP362Qm7zDdDXqv61kI4rzhmLFq3Z1gx34xpl6cWE=
github.com/gomarkdown/markdown v0.0.0-20240626202925-2eda941fd024/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
github.com/gomarkdown/markdown v0.0.0-20240730141124-034f12af3bf6 h1:ZPy+2XJ8u0bB3sNFi+I72gMEMS7MTg7aZCCXPOjV8iw=
github.com/gomarkdown/markdown v0.0.0-20240730141124-034f12af3bf6/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=
@ -23,8 +23,8 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8=
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
golang.org/x/image v0.19.0 h1:D9FX4QWkLfkeqaC62SonffIIuYdOk/UE2XKUBgRIBIQ=
golang.org/x/image v0.19.0/go.mod h1:y0zrRqlQRWQ5PXaYCOMLTW2fpsxZ8Qh9I/ohnInJEys=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=

View File

@ -30,7 +30,7 @@ func (h *GuestbookApiHandlers) New(w http.ResponseWriter, r *http.Request) {
if !inmemdb.Solve(captcha.ID(r.FormValue("captcha_id")), captcha.Answer(r.FormValue("captcha_answer"))) {
Error(w, http.StatusForbidden, "Wrong answer given.",
"Here's your message:"+r.FormValue("message"), r.Referer())
"Here's your message:"+r.FormValue("message"), r)
return
}
@ -44,14 +44,14 @@ func (h *GuestbookApiHandlers) New(w http.ResponseWriter, r *http.Request) {
r.FormValue("website"), r.FormValue("hide_website") != "")
if err != nil {
Error(w, http.StatusInternalServerError, err.Error(),
"Here's your message:"+r.FormValue("message"), r.Referer())
"Here's your message:"+r.FormValue("message"), r)
return
}
}
if err = h.db.NewEntry(entry); err != nil {
Error(w, http.StatusInternalServerError, err.Error(),
"Here's your message:"+r.FormValue("message"), r.Referer())
"Here's your message:"+r.FormValue("message"), r)
return
}

View File

@ -51,7 +51,7 @@ func (h *MindflowApiHandlers) NewPost(w http.ResponseWriter, r *http.Request) {
if err = h.db.NewPost(post); err != nil {
msg := strings.Join([]string{"Title:", r.FormValue("title"), "| Body:", r.FormValue("body")}, " ")
Error(w, http.StatusInternalServerError, err.Error(), msg, r.Referer())
Error(w, http.StatusInternalServerError, err.Error(), msg, r)
return
}

View File

@ -35,6 +35,10 @@ func NewHandlers(captchaExpire time.Duration, owner string, gbPageSize int64,
guestbookPageSize: gbPageSize}
}
func (h *Handlers) Index(w http.ResponseWriter, r *http.Request) {
web.Index(r).Render(context.Background(), w)
}
func (h *Handlers) Privacy(w http.ResponseWriter, r *http.Request) {
web.Privacy(r).Render(context.Background(), w)
}
@ -47,14 +51,14 @@ 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())
Error(w, http.StatusInternalServerError, "Cannot load posts.", "", r)
return
}
categories, err := h.mindflowDB.Categories()
if err != nil {
log.Println("Cannot load categories.", err)
Error(w, http.StatusInternalServerError, "Cannot load categories.", "", r.Referer())
Error(w, http.StatusInternalServerError, "Cannot load categories.", "", r)
return
}
@ -64,13 +68,13 @@ func (h *Handlers) Mindflow(w http.ResponseWriter, r *http.Request) {
func (h *Handlers) MindflowAdmin(w http.ResponseWriter, r *http.Request) {
posts, err := h.mindflowDB.Posts()
if err != nil {
Error(w, http.StatusInternalServerError, err.Error(), "failed to load posts", r.Referer())
Error(w, http.StatusInternalServerError, err.Error(), "failed to load posts", r)
return
}
categories, err := h.mindflowDB.Categories()
if err != nil {
Error(w, http.StatusInternalServerError, err.Error(), "failed to load categories", r.Referer())
Error(w, http.StatusInternalServerError, err.Error(), "failed to load categories", r)
return
}
@ -99,7 +103,7 @@ 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 {
Error(w, http.StatusNotFound, "", "An article \""+name+"\" doesn't exist.", r.Referer())
Error(w, http.StatusNotFound, "", "An article \""+name+"\" doesn't exist.", r)
return
}
@ -147,7 +151,7 @@ func (h *Handlers) GuestbookAdmin(w http.ResponseWriter, r *http.Request) {
entriesCount, _ := h.guestbookDB.Count()
entries, err := h.guestbookDB.Entries(1, entriesCount)
if err != nil {
Error(w, http.StatusInternalServerError, err.Error(), "cannot load gb", r.Referer())
Error(w, http.StatusInternalServerError, err.Error(), "cannot load gb", r)
return
}
for _, entry := range entries {
@ -178,9 +182,9 @@ 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 string, r *http.Request) {
w.WriteHeader(code)
web.ErrorXXX(code, reason, message, referer).Render(context.Background(), w)
web.ErrorXXX(code, reason, message, r).Render(context.Background(), w)
}
func cleanupNewlines(text string) (out string) {

View File

@ -0,0 +1,7 @@
package version
var ver string
func GetVersion() string {
return ver
}

View File

@ -6,7 +6,7 @@ import "git.arav.su/Arav/dwelling-home/pkg/servicestat"
import "git.arav.su/Arav/dwelling-home/pkg/util"
templ About(services *servicestat.ServiceList, r *http.Request) {
@base("About", "About me and my home servers.", "about, me, servcies", "/about", aboutHead()) {
@base("About", "About me and my home servers.", "about, me, servcies", "/about", r, aboutHead()) {
<section id="about-me">
<h2>Me</h2>
<p><b class="highlighted">Who am I?</b> My name is <span class="highlighted">A</span>lexande<span class="highlighted">r</span> <span class="highlighted">A</span>ndree<span class="highlighted">v</span>. I'm a russian guy of age 31 who likes tinkering with computers.</p>

View File

@ -6,7 +6,7 @@ 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 + " - Stuff", description, "", "/stuff/article/"+urlName, articleHead()) {
@base(title + " - Stuff", description, "", "/stuff/article/"+urlName, r, articleHead()) {
<article>
<header>
<h2>{ title }</h2>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -34,7 +34,7 @@
</url>
<url>
<loc>https://arav.su/about</loc>
<lastmod>2024-04-16</lastmod>
<lastmod>2024-07-01</lastmod>
</url>
<url>
<loc>https://arav.su/guestbook</loc>

View File

@ -1,8 +1,12 @@
package web
import "strings"
import "net/http"
templ base(title, description, keywords, canonical string, head templ.Component) {
import "git.arav.su/Arav/dwelling-home/internal/version"
import "git.arav.su/Arav/dwelling-home/pkg/util"
templ base(title, description, keywords, canonical string, r *http.Request, head templ.Component) {
<!DOCTYPE html>
<html>
<head>
@ -21,7 +25,7 @@ templ base(title, description, keywords, canonical string, head templ.Component)
<link rel="stylesheet" href="/assets/css/main.css"/>
<link rel="alternate" href="/rss.xml" type="application/rss+xml" title="Arav's dwelling"/>
if canonical != "" {
<link rel="canonical" href={ string(templ.URL(canonical)) }/>
<link rel="canonical" href={ util.GetServiceByHost(r.Host, util.ServiceHome) + canonical }/>
}
if head != nil {
@head
@ -64,7 +68,7 @@ templ base(title, description, keywords, canonical string, head templ.Component)
<footer>
<a href="/rss.xml" title="Stay up to date on what's going on.">RSS feed</a>
<br/>
&copy; 2017&mdash;2024 Alexander &quot;Arav&quot; Andreev &lt;<a href="mailto:me@arav.su">me@arav.su</a>&gt; <a href="/privacy">Privacy statements</a>
{ version.GetVersion() } &copy; 2017&mdash;2024 Alexander &quot;Arav&quot; Andreev &lt;<a href="mailto:me@arav.su">me@arav.su</a>&gt; <a href="/privacy">Privacy statements</a>
</footer>
</body>
</html>

View File

@ -3,9 +3,9 @@ package web
import "fmt"
import "net/http"
templ ErrorXXX(errCode int, reason, message, referer string) {
templ ErrorXXX(errCode int, reason, message string, r *http.Request) {
{{ errText := http.StatusText(errCode) }}
@base(errText, errText, "", "", errorXXXHead()) {
@base(errText, errText, "", "", r, errorXXXHead()) {
<section id="error">
<h1>{ fmt.Sprint(errCode) }</h1>
{ errText }
@ -16,9 +16,9 @@ templ ErrorXXX(errCode int, reason, message, referer string) {
<p>{ message }</p>
}
</section>
if referer != "" {
if r.Referer() != "" {
<section>
<h2><a href={ templ.URL(referer) }>Go back</a></h2>
<h2><a href={ templ.URL(r.Referer()) }>Go back</a></h2>
</section>
}
}

View File

@ -11,7 +11,7 @@ import "git.arav.su/Arav/justguestbook"
import "git.arav.su/Arav/dwelling-home/pkg/util"
templ Guestbook(captchaID, owner string, entries []*justguestbook.Entry, pageCount, pageCur int64, r *http.Request) {
@base("Guestbook", "This is my guestbook. Welcome.", "guestbook, personal", "/guestbook", guestbookHead()) {
@base("Guestbook", "This is my guestbook. Welcome.", "guestbook, personal", "/guestbook", r, 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)"/>

View File

@ -5,5 +5,5 @@ import "net/http"
import "git.arav.su/Arav/justguestbook"
templ GuestbookAdmin(owner string, entries []*justguestbook.Entry, r *http.Request) {
@base("Guestbook Administration", "", "", "/guestbook/admin", nil)
@base("Guestbook Administration", "", "", "/guestbook/admin", r, nil)
}

View File

@ -1,7 +1,9 @@
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", indexHead()) {
import "net/http"
templ Index(r *http.Request) {
@base("", "A homepage of a russian guy Alexander aka Arav. Not just homepage, but something more...", "homepage, personal, blog, services, self-hosting", "/", r, indexHead()) {
<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>
@ -26,6 +28,11 @@ templ Index() {
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>
<section>
<p>Привет, Анон. Я Александр, хожу по интернету под ником Arav и его всякими вариациями. С 2017 года завел у себя домашний сервер в виде третьей малины, а чуть позже добавил ещё один в виде старого ноута, и теперь размещаю различные сервисы на них, некоторые могут быть полезны и тебе, они по ссылкам выше. Но если кратко: файловая шара, радио, файловый хостинг с ограниченным временем хранения, ну и git сервер мой, можешь порыться в нем.</p>
<p>Изначально завел небольшой сайт на neocities, но быстро стало нехватать всякого (например, шаблонов, чтобы везде одно и то же не менять), потому переехал полностью на свой сервер, а там, по сути, оставил только ссылки.</p>
<p>Да, я веду сайт изначально на английском чисто для большего охвата аудитории и практики, ибо как ни крути, а английский не скоро перестанет быть доминирующим языком международного общения, да и тупо лень вести на двух языках сайт, да и технически так и не решил пока как локализовать. Однако, один посетитель справедливо пожурил меня, что надо хотя бы на главной, но чтобы русский был. :)</p>
</section>
}
}

View File

@ -8,7 +8,7 @@ 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", mindflowHead()) {
@base("Mindflow", "Updates on my infrastructure, my very important opinions and thoughts.", "updates, thoughts, opinions, blog, diary", "/mindflow", r, mindflowHead()) {
<p class="center">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 <a href="/rss.xml">RSS feed</a> at your service. :)</p>
<section>
<menu id="filter" class="hidden">
@ -36,7 +36,8 @@ templ Mindflow(posts []mindflow.Post, categories []mindflow.Category, r *http.Re
} else {
<span></span>
}
<time datetime={ util.ToClientTimezone(post.Date, r).Format(time.RFC3339) }>{ util.ToClientTimezone(post.Date, r).Format(time.RFC1123) }</time>
{{ ctz := util.ToClientTimezone(post.Date, r) }}
<time datetime={ ctz.Format(time.RFC3339) }>{ ctz.Format(time.RFC1123) }</time>
</footer>
</article>
}

View File

@ -3,7 +3,78 @@ package web
import "net/http"
import "git.arav.su/Arav/dwelling-home/pkg/mindflow"
import "git.arav.su/Arav/dwelling-home/pkg/util"
import "strconv"
import "time"
templ MindflowAdmin(posts []mindflow.Post, categories []mindflow.Category, r *http.Request) {
@base("Mindflow Administration", "", "", "/mindflow/admin", nil)
@base("Mindflow Administration", "", "", "/mindflow/admin", r, mindflowAdminHead()) {
<section>
<h2>Manage categories</h2>
<form id="manage-categories">
<select name="category" required>
for _, c := range categories {
<option value={ strconv.FormatInt(c.ID, 10) }>{ c.Name }</option>
}
<option value="0">-- New category --</option>
</select>
<input type="text" name="name" placeholder="New category name"/>
<button type="submit" name="add">Add</button>
<button type="submit" name="edit">Edit</button>
<button type="submit" name="delete">Delete</button>
</form>
</section>
<section>
<h2>Create a new post</h2>
<form id="add" action="/api/mindflow" method="POST">
<select name="category" required>
for _, c := range categories {
<option value={ strconv.FormatInt(c.ID, 10) }>{ c.Name }</option>
}
</select>
<input type="text" name="title" placeholder="Title" required/>
<input type="text" name="url" placeholder="URL"/>
<textarea name="body" placeholder="Body post" required></textarea>
<button type="submit">Add</button>
</form>
</section>
<section>
for _, post := range posts {
<article id={ post.PostID() }>
<header>
<a href="#{ templ.SafeURL(post.PostID()) }"><h3>{ post.Category.Name }: { post.Title }</h3></a>
</header>
<form class="edit">
<select name="category" required>
for _, c := range categories {
if c.ID == post.Category.ID {
<option value={ strconv.FormatInt(c.ID, 10) } selected>{ c.Name }</option>
} else {
<option value={ strconv.FormatInt(c.ID, 10) }>{ c.Name }</option>
}
}
</select>
<input type="hidden" name="post-id" value={ strconv.FormatInt(post.ID, 10) }/>
<input type="text" name="title" placeholder="Title" required/>
<input type="text" name="url" placeholder="URL" value={ post.URL }>
<textarea name="body" placeholder="Body post" required>@templ.Raw(post.Body)</textarea>
<button name="edit-post">Edit</button>
<button name="delete-post">Delete</button>
</form>
<footer>
<time datetime={ util.ToClientTimezone(post.Date, r).Format(time.RFC3339) }>{ util.ToClientTimezone(post.Date, r).Format(time.RFC1123) }</time>
</footer>
</article>
}
if len(posts) == 0 {
<p class="center">Nothing? There must be some... Looks like database went down.</p>
}
</section>
}
}
templ mindflowAdminHead() {
<link rel="stylesheet" href="/assets/css/mindflow.css">
<link rel="stylesheet" href="/assets/css/mindflow_admin.css">
<script src="/assets/js/mindflow_admin.js" defer></script>
}

View File

@ -5,7 +5,7 @@ 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", nil) {
@base("Privacy", "Privacy statements for all of my services.", "privacy statements", "/privacy", r, nil) {
<section id="privacy">
<h2>Privacy statements</h2>
<h3>General data</h3>

View File

@ -7,7 +7,7 @@ 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", nil) {
@base("Stuff", "Here I share my programs, scripts, articles, may be other stuff.", "articles, programs, personal projects, own music", "/stuff", r, nil) {
<p class="center">Here lies everything I've made that I'm willing to share.</p>
<section id="articles">
<h2>Articles</h2>
@ -106,7 +106,7 @@ templ Stuff(r *http.Request) {
</tr>
<tr>
<td><b>httpprocprobed</b></td>
<td>3.1.1 (13 January 2024)</td>
<td>3.2.0 (7 July 2024)</td>
<td>Go</td>
<td>MIT+NIGGER</td>
<td>

View File

@ -1,246 +0,0 @@
extends base.pug
block meta_description
meta(name='description' content="About me and my home servers.")
block append head
link(href='/assets/css/about.css' rel='stylesheet')
link(rel='canonical' href='/about')
block nav
a(href='/') Home
a(href='/stuff') Stuff
a(href='/mindflow') Mindflow
a(href='/guestbook') Guestbook
h1 About
mixin isServiceUp(header, service)
if v, ok := services[service]; ok && v
h3.service-up= header
else
h3.service-down= header
block content
:go:func About(title, files_site string, services servicestat.ServiceList)
section#about-me
h2 Me
p #[span.highlighted Who am I?] My name is Alexander Andreev. I'm a russian guy of age 31 who likes tinkering with computers.
p #[span.highlighted Why am I doing all of this?] Machines are up 24/7 anyway, so why not?
p #[span.highlighted Why am I almost &ldquo;doxxing&rdquo; myself?] Yes, my real first and last names and age are here, and since I self-host you can get my city as well. And I simply don't see this as a problem. :|
p #[span.highlighted Why particularly that slogan?] I just wanted sort of a slogan that would fit under the logo and this was the first thing came on mind. I actually like it. Don't you think of your websites as of very special place of your own? Of course you do. :) And who we are if not the wanderers who are looking for something in the vastness of Intenet? xD And yes, it's a pain in the ass to make it fit under logo across different browsers. That was the moment when I found out that different browsers render text differently.
p #[span.highlighted Why English only?] I want this place to be accessible for a wide audience and English happen to be todays lingua franca, and to practice the language of course.
p Now a little break for...
section#my-cat
h2 My kot Boris :3
div.columns.figs
figure
a(href='/assets/img/photos/my_cat.webp')
img(src='/assets/img/photos/my_cat_thumb.webp' title=':3' alt='My cat. :3')
figure
a(href='/assets/img/photos/my_cat_2.webp')
img(src='/assets/img/photos/my_cat_2_thumb.webp' title='xDDDD' alt='xDDDD')
p And here we continue. :)
section#more-about-me
h3 Hobbies
p Video games (since 1997)
p Computers (since 11th November 2004)
p #[s Wasting my life on] Surfing the Internets (since February 2006)
p Mapping for Half-life 2, Counter-Strike: Source (since 2006, discontinued in 2009) (everything's fuarrkin' lost)
p Music writing (since 2007, last track was made in 2010)
p Programming (since 2007)
p #[s (LARP'ing as a system administrator)] Home servers (since 1st January 2017)
p Software Defined Radio (since June 2018)
h3 What I know
h4 Human languages
p #[span.highlighted English] is my main foreign language and I think that I know it quite well, but every time I reread what I wrote here I constantly catch cringe vibes. xD I have a nice pronunciation (my teacher once said so :)) and tend to British non-rhotic variant. Of course I end up with a mix of all the flavours of different dialects. What I struggle from is a lack of speaking practice and vocabulary is still a problem as well.
p #[span.highlighted Ukrainian (Українська)] is a special case. :) Russian and ukrainian are related languages, so are mutually understandable (at least must be), yet many russians are struggling to understand it, and I genuinely don't get why. It is just a matter of putting some little effort to negatiate it. I never learnt it, aside from reading it here and there, and looking up some words in a dictionary, and especially throughout past #[s 8] #[s 9] 10 years I saw enough material to learn it to a decent level of understanding and some speaking.
p Агась, нiколи спецiйно не вчив українську, користував перекладач тiльки як словник, за цi #[s вiсiм ] #[s вже дев'ять] десять рокiв було досить матерiалiв. :) P.S.: Слава Українi у складi Росiї! :)
p I like learning languages, but me being a lazy fuck won't let me learn something up to a reasonable level. I ended up with being able to +- read some languages, like norwegian, german, polish, french, japanese (hiragana + some yet not forgotten kanji). So, now I main only English.
h4 Computer languages
p #[span.highlighted For programming and scripting:] Python, Go, JavaScript, and Bash for now are most used by me. Later I used or tasted: Pascal, Delphi, C#, C, C++, Java, PHP. And wrote hello worlds in some others like LISP and Haskell.
p #[span.highlighted Markup and declarative:] SQL (SQLite3, MariaDB), HTML5, CSS3, LaTeX, Markdown, XML, and JSON.
p And I don't know what else to put here. :^)
h3 What I liked
h4 Anime
p Ghost in the Shell, Shaman King, Hellsing, Steins;Gate, Cowboy Beebop, Ergo Proxy, Jin-Rou, Black Lagoon, Jojo's Bizzare Adventures, Spice and Wolf, Konosuba, Demon Slayer: Kimetsu no Yaiba
h4 Films
p Paths of Glory (1957), Boss Nigger (The Black Bounty Killer) (1974), Movies with Jackie Chan, Blade Runner (1982), WarGames (1983), Robocop (1987), Talk Radio (1988), Stargate (1994), Hackers (1995), Johnny Mnemonic (1995), Contact (1997), Matrix (1999, 2003), Snatch (2000), Oldboy (2003), The Day After Tomorrow (2004), Alpha Dog (2005), Constantine (2005), The Gingerdead Man (2005), Lucky Number Slevin (2006), 99 francs (2007), I Am Legend (2007), Inglourious Basterds (2009), Valhalla Rising (2009), The Guard (2011), Filth (2013), Mandariinid (2013), Gingerdead Man vs. Evil Bong (2013), Gone Girl (2014), Who am I (2014), Arrival (2016), Contratiempo (2016), Wandering Earth (2019), Wandering Earth 2 (2019), Greyhound (2020), I Care A Lot (2020), The Greatest Beer Run Ever (2022), Everything Everywhere All at Once (2022), Dungeons & Dragons: Honor Among Thieves (2023)
h4 TV shows
p X-Files (1993&mdash;2002), Lost (2004&mdash;2010), Stargate: SG-1 (1997&mdash;2007), Stargate: Atlantis (2004&mdash;2009), The Shivering Truth (2018&mdash;2020), Два холма (Two Hills) (2022), Narcos (2015&mdash;2017)
h4 Games for SEGA Mega Drive
p Not a lot of games I have played.
p Bubba'n'Stix, Battletoads, Granada, Demolition Man, Road Rash, Doom Troopers - The Mutant Chronicles, The Lost Vikings, Ghostbusters, Mig-29 Fighter Pilot.
h4 Games for PC
p Grand Theft Auto: Vice City, Half-Life (all), StarCraft, Diablo 2, Far Cry (2004), Battlefield: Vietnam, Delta Force: Black Hawk Down, Silent Hunter 3, Grand Theft Auto: San Andreas, Boiling Point: Road to Hell, Portal, Freelancer, F.E.A.R., S.T.A.L.K.E.R., Grand Theft Auto 4, Lineage 2, The Elder Scrolls V: Skyrim, Battlefield 3, The Walking Dead, The Wolf Among Us, Payday 2, The Witcher (1st, 2nd, 3rd didn't finished yet), Minecraft, Terraria, Starbound, Euro Truck Simulator 2, Mount &amp; Blade: Warband, Papers, Please, Insurgency, Elite: Dangerous, theHunter: Call of the Wild, American Truck Simulator, Rocket League, Sea Of Thieves.
p I'm CMDR Arav in Elite: Dangerous. My <a href="https://www.edsm.net/en/user/profile/id/22541/cmdr/Arav" rel="noopener noreferrer">EDSM profile</a>.
p I play TruckersMP mod for Euro Truck Simulator 2 and American Truck Simulator. Nickname is Arav with a tag [RU64].
h4 Music
p You can see what I like in #[a(href=files_site+"/music") a file share].
section#servers-summary
h2 My servers
p I have two of them, the first one is a #[a(href='https://www.raspberrypi.org/products/raspberry-pi-3-model-b/' rel='nofollow noreferrer') Raspberry Pi 3 rev. B] single board computer and the second one is a laptop. Yeah, not quite impressive, but they do their work just fine.
p Laptop is Acer Packard Bell TE69CX that has a 2 core Intel Pentium 2117U 1.8GHz and 10GB RAM (2GB + 8GB), the system disk is a 120GB SSD and the other one is 2TB HDD where all the shit I have is stored. I've replaced my good old 500GB disk with 2TB one, since it became a little too tight, now it is a system disk for Raspberry Pi. Everything that have just a bit of importantance to me is backed up, encrypted and stored in the clouds. :)
p Also now I have a helper VPS with a 1 core 2.2GHz CPU and 512M RAM, and a 10GB disk I use as a slave DNS server for my domain and a postfix relay to send a mail.
section#servers-photos
div.columns.figs
figure
a(href='/assets/img/photos/raspi.webp')
img(src='/assets/img/photos/raspi_thumb.webp' alt='Raspberry Pi 3 rev. B')
figcaption Raspberry Pi 3 rev. B
figure
a(href='/assets/img/photos/acer.webp')
img(src='/assets/img/photos/acer_thumb.webp' title='Screen and keyboard are broken anyway.' alt='Laptop')
figcaption Acer Packard Bell TE69CX
section#servers-public-services
h2 Public services
p.center That are available for everyone.
div.columns
div
+isServiceUp("Internet-radio", "radio")
p
a(href='https://radio.arav.su') radio.arav.su
| .
a(href='http://wsmkgnmhmzqm7kyzv7jnzzafvgm7xlmlfvzhgorpapd5or2arnhuktqd.onion') onion
| .
a(href='http://radio.arav.i2p') i2p
| .
a(href='http://[300:a98d:d6d0:8a08::e]') ygg
p Runs using Ezstream through Icecast which goes through NGiNX. Ezstream fetches a playlist from a self-written solution. I broadcast almost everything I have. I'm too lazy to make some kind of broadcasting program to stream so I just randomise the playlist and throw new music there from time to time.
div
+isServiceUp("File share", "dwelling-files")
p
a(href='https://files.arav.su') files.arav.su
| .
a(href='http://qf5e43nlhvnrutmikuvbdfj3cmtthokpbaxtkm6mjlslttzvtgm4fxid.onion') onion
| .
a(href='http://files.arav.i2p') i2p
| .
a(href='http://[300:a98d:d6d0:8a08::d]') ygg
p There you'll find music, videos, films, animes, TV shows, books, games, programs, drivers (a very few), OS images (Winblows mainly). Go check it out!
div
+isServiceUp("File upload", "dwelling-upload")
p
a(href='https://upload.arav.su') upload.arav.su
| .
a(href='http://4usftbmjpfexkr2x5xbp5ukmygpmg4fgrnx2wbifsexqctooz5hmviyd.onion') onion
| .
a(href='http://upload.arav.i2p') i2p
| .
a(href='http://[300:a98d:d6d0:8a08::c]') ygg
p I've made a file upload service for ya with a limit of 128MiB per file, keeping it for 36 hours, and overall storage I dedicated for it is 100GiB.
div
+isServiceUp("Git", "git")
p
a(href='https://git.arav.su') git.arav.su
| .
a(href='http://qqitm7qlsbbubwmjos4cqzmvkqidg34rfnbyhuydhalep33fbvh22xyd.onion') onion
| .
a(href='http://git.arav.i2p') i2p
| .
a(href='http://[300:a98d:d6d0:8a08::b]') ygg
p Gitea. This is a place where I put source code of everything I ever wrote and consider useful and worth sharing.
div
h3 Tor relay
p #[a(href='https://metrics.torproject.org/rs.html#details/CEF2FD0E1973EA04D1444DDAEFF1B0BC3C0C39B1' rel='nofollow noreferrer') metrics.torproject.org]
p #[b Since 1st December 2021 Tor is blocked in Russia, so the relay is down.]
div
h3 I2P router
p I help the network using an official #[a(href='https://geti2p.net' rel='nofollow noreferrer') I2P router].
section#servers-private-services
h2 Private services
p.center That are available for a narrow circle of people.
div.columns
div
+isServiceUp("E-Mail server", "mail")
p Postfix, Dovecot (w/Sieve), rspamd, ClamAV.
div
+isServiceUp("Mumble", "mumble")
p #[a(href="mumble://arav.su") mumble://arav.su]
p Certificate is necessary. Only an entrance room is open for non-members.
div
+isServiceUp("TeamSpeak 3", "teamspeak3")
p #[a(href="ts3server://arav.su") ts3server://arav.su]
p Same as for Mumble. Your identifier should have a security level greater or equal to 29.
section#servers-games
h2 Game servers
div.columns
div
+isServiceUp("Minecraft", "game-minecraft")
p.highlighted arav.su:25565
p #[a(href='/minemap') Web map].
p Now run a version 1.20.4 with fabric. Here's a #[a(href='https://files.arav.su/file/games/minecraft/1.20.4-fabric-modpack.zip') modpack].
div
+isServiceUp("Avorion", "game-avorion")
p Server is listed in a server list as &ldquo;Arav's dwelling / Avorion&rdquo;. Whitelist is enabled. Bring your SteamID64 if you want to play here.
div
+isServiceUp("Starbound", "game-starbound")
p.highlighted arav.su:21065
p Server is using a Frackin' Universe mod.
p Don't forget to set &ldquo;Allow assets mismatch&rdquo; option that can be found on the first page of the &ldquo;Options&rdquo; menu.
p Access is restricted using accounts. Contact me if you want to play here, you would need to give me a nickname and a password.
div
+isServiceUp("Project Zomboid", "game-pzomboid")
p.highlighted arav.su:16261
p Server is listed as &ldquo;Arav's dwelling / Project Zomboid&rdquo;. Whitelist is enabled.
div
+isServiceUp("Don't Starve Together", "game-dontstarve")
p.highlighted arav.su:10899
p Server is listed as &ldquo;Arav's dwelling / Don't Starve Together&rdquo;.
section#servers-inner-services
h2 Inner services
p.center Services that maintains other services or used exclusively by me.
div.columns
div
h3 Web-server
p NGiNX FTW.
div
h3 Database
p #[s MariaDB managed with phpMyAdmin] was replaced by SQLite3 on my services.
div
h3 VPN
p #[s OpenVPN] Wireguard.
div
h3 Network file share
p Samba and NFSv4.
div
h3 Torrents
p Transmission-cli gets shit done.
p The only annoying thing is that it sometimes creates .part files for one of unchecked files.
div
h3 Printing
p CUPS with CCP (Canon CAPT printer).
p Holy shit, it finally works! #[s Wow, even after kernel update it doesn't require a reboot anymore!] #[s Actually, requires. :)] Sometimes do, sometimes not. xD
div
h3 DNS
p BIND9 via DNSCrypt-proxy.
p For LAN I use &ldquo;home.arpa&rdquo; special-use domain introduced by RFC 8375. And for uniqueness &ldquo;arav.home.arpa&rdquo; specifically.
p Now I serve my domain using a VPS as a slave.
section#contacts
h2 Contacts
span E-Mail:&nbsp;#[a(href="mailto:me@arav.su") me@arav.su]
br
span
| PGP&nbsp;key:&nbsp;#[a(href="/~arav/739850CD5051DE554368709225969B23DCB5CA34.asc") 7398 50CD 5051 DE55 4368 7092 2596 9B23 DCB5 CA34] (available through #[a(href="https://wiki.gnupg.org/WKD" rel="nofollow noreferrer") Web Key Directory])
section#donation
h2 Donation
a(href="monero:48namnfX17TX1kEGCpkXaRWhtw8p92cQjd5uQg7ivybgUuW4BTVaX8egxQhEi75JwuUGn3MDLKHYGNhu4eCfM6dRAAL2QAq") monero:
| 48namnfX17TX1kEGCpkXaRWhtw8p92cQjd5uQg7ivybgUuW4BTVaX8egxQhEi75JwuUGn3MDLKHYGNhu4eCfM6dRAAL2QAq
br
| #[a(href="https://www.donationalerts.com/r/arav") DonationAlerts]
| #[a(href="https://www.tinkoff.ru/rm/andreev.aleksandr1164/5nVjK98501") Tinkoff]
| #[a(href="https://www.tinkoff.ru/baf/5zRsqEQic6r") Tinkoff рефералка]
section#banners
h2 Banners
p In case you found my site anyhow useful and would like to add me to your links page here are the banners for you.
div.banners
figure
img(src="/assets/img/banners/8831l.gif" alt="Light banner 88x31")
figure
img(src="/assets/img/banners/8831d.gif" alt="Dark banner 88x31")
div.banners
figure
img(src="/assets/img/banners/24060l.gif" alt="Light banner 240x60")
figure
img(src="/assets/img/banners/24060d.gif" alt="Dark banner 240x60")

View File

@ -1,66 +0,0 @@
extends base.pug
block meta_description
meta(name='description' content='Here I will post updates on my infrastructure, my very important opinions and thoughts.')
block append head
link(href='/assets/css/mindflow.css' rel='stylesheet')
link(href='/assets/css/mindflow_admin.css' rel='stylesheet')
script(src='/assets/js/mindflow_admin.js' defer='')
block nav
a(href='/') Home
a(href='/stuff') Stuff
a(href='/mindflow') Mindflow
a(href='/about') About
a(href='/guestbook') Guestbook
h1 Mindflow Admin
block content
:go:func MindflowAdmin(title string, posts []mindflow.Post, categories []mindflow.Category, r *http.Request)
section
h2 Manage categories
form(id='manage-categories')
select(name='category' required='')
each category in categories
option(value=category.ID) #{category.Name}
option(value='0') -- New category --
input(type='text', placeholder='New category name' name='name')
button(type="submit" name="add") Add
button(type="submit" name="edit") Edit
button(type="submit" name="delete") Delete
section
h2 Create a new post
form(id='add' action='/api/mindflow', method='POST')
select(name='category' required='')
each category in categories
option(value=category.ID) #{category.Name}
input(type='text', placeholder='Title' name='title' required='')
input(type='text', placeholder='URL' name='url')
textarea(placeholder='Body post' name='body' required='')
button(type="submit") Add
section
if (len(posts) > 0)
each post in posts
article(id=post.PostID())
header
a(href=`#${post.PostID()}`)
h3= post.Category.Name + ": " + post.Title
form.edit
select(name='category' required='')
each category in categories
if (category.ID == post.Category.ID)
option(value=category.ID selected='') #{category.Name}
else
option(value=category.ID) #{category.Name}
input(type='hidden', name='post-id' value=post.ID)
input(type='text', placeholder='Title' name='title' value=post.Title required='')
input(type='text', placeholder='URL' name='url' value=post.URL)
textarea(placeholder='Body post' name='body' required='')!= post.Body
button(name='edit-post') Edit
button(name='delete-post') Delete
footer
time(datetime=util.ToClientTimezone(post.Date, r))= util.ToClientTimezone(post.Date, r).Format(time.RFC1123)
else
p.center Nothing? There must be some... Looks like database went down.