1
0

Compare commits

...

35 Commits

Author SHA1 Message Date
76c274c2a2
Обновил заголовки на странице О... 2024-09-15 17:07:02 +04:00
db624f39af
Translation of a stuff page completed. 2024-09-15 17:05:27 +04:00
02e1b66072
Date format at stuff page was changed. 2024-09-15 16:20:25 +04:00
d4706e53ce
Translation of a stuff page. 2024-09-15 16:15:57 +04:00
94482d0aae
Fixed date format in mindflow. 2024-09-15 15:52:54 +04:00
f366892b59
Updated mindflow translation. 2024-09-15 15:40:14 +04:00
ed273c4815
Fixed RSS. 2024-09-15 15:29:42 +04:00
45d1a71229
Change timedate format in a guestbook. 2024-09-15 15:09:55 +04:00
917bd7e21e
Закончен перевод страницы О... 2024-09-15 15:04:23 +04:00
64057bf9d5
Translate CAPTCHA text in JS. 2024-09-15 15:03:39 +04:00
2904a07088
Make a lang cookie accessible in JS for translation. 2024-09-15 15:03:13 +04:00
439e25b28c
Ещё немного перевода страницы О... . 2024-09-15 04:18:47 +04:00
2b39a0f769
Первая часть перевода страницы О... 2024-09-15 04:11:21 +04:00
77ac80334e
Added a russian translation of privacy statements. Also corrected the text. 2024-09-15 02:57:11 +04:00
20ecf6744f
Updated translations. I found that " can be ommited. 2024-09-15 02:56:19 +04:00
841653f502
A strange escaping is being performed somewhere inside ctxi18n and i18n.M is a way to avoid it. 2024-09-15 02:55:36 +04:00
802c925450
Set lang if a lang= query set, else look for a cookie, otherwise choose a lang from Accept-Language header. 2024-09-15 00:31:00 +04:00
d26bcc58b8
Added links to set site's lang. 2024-09-15 00:29:52 +04:00
d9d7358abf
Fixed en.yaml. 2024-09-15 00:29:02 +04:00
0e889448e3
Updated index page. 2024-09-14 23:54:07 +04:00
ad02399301
Первый шаг к переводу сайта на русский. 2024-09-14 18:36:08 +04:00
50ba6bb74b
Introduce i18n. Теперь переведу сайт на русский! 2024-09-14 18:34:22 +04:00
4b351ed7a9
Update dependencies. 2024-09-14 18:32:57 +04:00
f939a056ad
Updated index.templ. 2024-09-11 01:06:27 +04:00
429284ab3d
Version set to 24.37.0. 2024-09-11 00:14:45 +04:00
028a26a6f5
Updated dependencies. 2024-09-11 00:13:38 +04:00
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
23 changed files with 981 additions and 517 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.1
DESTDIR?=
PREFIX?=/usr/local
FLAGS:=-buildmode=pie -modcacherw -mod=readonly -trimpath
LDFLAGS=-ldflags "-s -w -X 'git.arav.su/Arav/dwelling-home/internal/version.ver=v${VERSION}'" -tags osusergo,netgo
VERSION?=24.37.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 \

View File

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

View File

@ -15,8 +15,10 @@ import (
"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/dwelling-home/web/locales"
"git.arav.su/Arav/httpr"
gb "git.arav.su/Arav/justguestbook"
"github.com/invopop/ctxi18n"
)
var (
@ -50,6 +52,10 @@ func main() {
}
defer mindflowDB.Close()
if err := ctxi18n.LoadWithDefault(locales.Content, "en"); err != nil {
log.Fatalln("Failed to load i18n:", err)
}
hand := dwhttp.NewHandlers(*captchaExpiry, owner, *guestbookPageSize, guestbookDB, mindflowDB)
r := httpr.New()
@ -101,7 +107,7 @@ func main() {
s.Handler(http.MethodPatch, "/category/:id", mindflowApi.EditCategory)
s.Handler(http.MethodDelete, "/category/:id", mindflowApi.DeleteCategory)
srv := dwhttp.NewHttpServer(r)
srv := dwhttp.NewHttpServer(I18nMiddleware(r))
if err := srv.Start(*listenAddress); err != nil {
log.Fatalln(err)
@ -117,3 +123,25 @@ func main() {
<-doneSignal
}
func I18nMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
lang := "en"
if lq := r.URL.Query().Get("lang"); lq != "" {
lc := http.Cookie{Name: "lang", Value: lq, HttpOnly: false, MaxAge: 0}
http.SetCookie(w, &lc)
lang = lq
} else if l, err := r.Cookie("lang"); err == nil {
lang = l.Value
} else if al := r.Header.Get("Accept-Language"); al != "" {
lang = r.Header.Get("Accept-Language")
}
ctx, err := ctxi18n.WithLocale(r.Context(), lang)
if err != nil {
log.Println("i18nmw:", err)
}
next.ServeHTTP(w, r.WithContext(ctx))
})
}

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;
}

11
go.mod
View File

@ -7,18 +7,21 @@ 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.778
github.com/gomarkdown/markdown v0.0.0-20240730141124-034f12af3bf6
github.com/invopop/ctxi18n v0.8.1
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
github.com/invopop/yaml v0.3.1 // indirect
golang.org/x/image v0.20.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
require (
git.arav.su/Arav/httpr v0.3.2
github.com/mattn/go-sqlite3 v1.14.22 // indirect
github.com/mattn/go-sqlite3 v1.14.23 // indirect
)

30
go.sum
View File

@ -4,27 +4,37 @@ 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.778 h1:VzhOuvWECrwOec4790lcLlZpP4Iptt5Q4K9aFxQmtaM=
github.com/a-h/templ v0.2.778/go.mod h1:lq48JXoUvuQrU0VThrK31yFwdRjTCnIE5bcPCM9IP1w=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/invopop/ctxi18n v0.8.1 h1:nfy5Mk6UfvLbGRBwpTi4T1g95+rmRo8bMllUmpCvVwI=
github.com/invopop/ctxi18n v0.8.1/go.mod h1:1Osw+JGYA+anHt0Z4reF36r5FtGHYjGQ+m1X7keIhPc=
github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso=
github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.23 h1:gbShiuAP1W5j9UOksQ06aiiqPMxYecovVGwmTxWtuw0=
github.com/mattn/go-sqlite3 v1.14.23/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
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.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw=
golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM=
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=
@ -53,3 +63,7 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -1,7 +1,6 @@
package http
import (
"context"
"log"
"math"
"net/http"
@ -36,15 +35,15 @@ func NewHandlers(captchaExpire time.Duration, owner string, gbPageSize int64,
}
func (h *Handlers) Index(w http.ResponseWriter, r *http.Request) {
web.Index(r).Render(context.Background(), w)
web.Index(r).Render(r.Context(), w)
}
func (h *Handlers) Privacy(w http.ResponseWriter, r *http.Request) {
web.Privacy(r).Render(context.Background(), w)
web.Privacy(r).Render(r.Context(), w)
}
func (h *Handlers) Stuff(w http.ResponseWriter, r *http.Request) {
web.Stuff(r).Render(context.Background(), w)
web.Stuff(r).Render(r.Context(), w)
}
func (h *Handlers) Mindflow(w http.ResponseWriter, r *http.Request) {
@ -62,7 +61,7 @@ func (h *Handlers) Mindflow(w http.ResponseWriter, r *http.Request) {
return
}
web.Mindflow(posts, categories, r).Render(context.Background(), w)
web.Mindflow(posts, categories, r).Render(r.Context(), w)
}
func (h *Handlers) MindflowAdmin(w http.ResponseWriter, r *http.Request) {
@ -78,7 +77,7 @@ func (h *Handlers) MindflowAdmin(w http.ResponseWriter, r *http.Request) {
return
}
web.MindflowAdmin(posts, categories, r).Render(context.Background(), w)
web.MindflowAdmin(posts, categories, r).Render(r.Context(), w)
}
func (h *Handlers) About(w http.ResponseWriter, r *http.Request) {
@ -96,7 +95,7 @@ func (h *Handlers) About(w http.ResponseWriter, r *http.Request) {
}
}
web.About(&lst, r).Render(context.Background(), w)
web.About(&lst, r).Render(r.Context(), w)
}
func (h *Handlers) Article(w http.ResponseWriter, r *http.Request) {
@ -107,7 +106,7 @@ func (h *Handlers) Article(w http.ResponseWriter, r *http.Request) {
return
}
web.Article(artcl.Title, artcl.Description, string(artcl.Body), name, artcl.Date, r).Render(context.Background(), w)
web.Article(artcl.Title, artcl.Description, string(artcl.Body), name, artcl.Date, r).Render(r.Context(), w)
}
func (h *Handlers) Guestbook(w http.ResponseWriter, r *http.Request) {
@ -144,7 +143,7 @@ func (h *Handlers) Guestbook(w http.ResponseWriter, r *http.Request) {
dwc := dwcaptcha.NewDwellingCaptcha(h.captchaExpire)
_, id := inmemdb.New(r.RemoteAddr, dwc)
web.Guestbook(string(id), h.owner, entries, pageCount, page, r).Render(context.Background(), w)
web.Guestbook(string(id), h.owner, entries, pageCount, page, r).Render(r.Context(), w)
}
func (h *Handlers) GuestbookAdmin(w http.ResponseWriter, r *http.Request) {
@ -158,7 +157,7 @@ func (h *Handlers) GuestbookAdmin(w http.ResponseWriter, r *http.Request) {
entry.Message = strings.ReplaceAll(entry.Message, "\\n", "\n")
}
web.GuestbookAdmin(h.owner, entries, r).Render(context.Background(), w)
web.GuestbookAdmin(h.owner, entries, r).Render(r.Context(), w)
}
func (h *Handlers) RSS(w http.ResponseWriter, r *http.Request) {
@ -172,7 +171,7 @@ func (h *Handlers) RSS(w http.ResponseWriter, r *http.Request) {
scheme = "http"
}
web.RSS(scheme+"://"+r.Host, h.owner, posts, r).Render(context.Background(), w)
web.RSS(scheme+"://"+r.Host, h.owner, posts, r).Render(r.Context(), w)
}
func ServeAsset(path string) func(http.ResponseWriter, *http.Request) {
@ -184,7 +183,7 @@ func ServeAsset(path string) func(http.ResponseWriter, *http.Request) {
func Error(w http.ResponseWriter, code int, reason, message string, r *http.Request) {
w.WriteHeader(code)
web.ErrorXXX(code, reason, message, r).Render(context.Background(), w)
web.ErrorXXX(code, reason, message, r).Render(r.Context(), w)
}
func cleanupNewlines(text string) (out string) {

View File

@ -2,11 +2,265 @@ package web
import "net/http"
import "github.com/invopop/ctxi18n/i18n"
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", r, aboutHead()) {
@base(i18n.T(ctx, "base.section.about"), "About me and my home servers.", "about, me, servcies", "/about", r, aboutHead()) {
if c := i18n.GetLocale(ctx).Code(); c == "ru" {
@aboutRu(services, r)
{{ return }}
}
@aboutEn(services, r)
}
}
templ aboutRu(services *servicestat.ServiceList, r *http.Request) {
<section id="about-me">
<h2>Обо мне</h2>
<p><b class="highlighted">Кто я?</b> Меня зовут Александр Андреев. Я простой русский чел 31 года от роду, который любит возиться с компьютерами.</p>
<p><b class="highlighted">Зачем делаю это всё?</b> Давненько хотел собственный веб-сайт, да и серваки всё равно 24 на 7 пашут, так что почему бы и нет? Да и практика никогда не помешает. :)</p>
<p><b class="highlighted">Почему я почти раскрыл себя?</b> Да, за каким-то хреном я разместил свои настоящие фамилию и имя здесь. А теперь поздно хвататься, т.к. всё уже разнеслось по веб-архивам, да и похер, если честно. :)</p>
<p><b class="highlighted">Почему такой слоган?</b> Хотелось что-нибудь воткнуть под названием сайта и эта фраза пришлась как раз. Не хочу особо филосовствовать. :) Но ведь странники мы, да? Иначе как ещё на подобные места натыкаться, если не странствовать по сети? :)</p>
<p><b class="highlighted"><s>Why English only?</s></b> Для большего охвата аудитории и практики я вёл этот сайт чисто на английском, да и две версии сайта вести было тупо лень, да и технически даже думать не хотелось как организовать перевод. :/ Но теперь всё поменялось! ^_^</p>
<p>А теперь небольшой перерыв ради...</p>
</section>
<section id="my-cat">
<h3>...моего кота Бориса :3</h3>
<div class="columns figs">
<figure>
<a href="/assets/img/photos/my_cat.webp">
<img src="/assets/img/photos/my_cat_thumb.webp" alt="My cat. :3" title=":3"/>
</a>
</figure>
<figure>
<a href="/assets/img/photos/my_cat_2.webp">
<img src="/assets/img/photos/my_cat_2_thumb.webp" alt="xDDDD" title="xDDDD"/>
</a>
</figure>
</div>
<p>А теперь продолжаем. :)</p>
</section>
<section id="more-about-me">
<h3>Хобби</h3>
<p>Видео-игры (с 1997)</p>
<p>Компьютеры (с 11 ноября 2004)</p>
<p><s>Просирание жизни</s> Странствие по интернетам (с февраля 2006)</p>
<p>Маппинг для Half-life 2, Counter-Strike: Source (с 2006, прекратил в 2009) (всё просрал, хотя, возможно лежит на нечитаемом IDE диске)</p>
<p>Написание музыки (с 2007, последний трек закончил в 2010, файлы проектов тоже проёбаны, но могут лежать на том диске)</p>
<p>Программирование (с 2007)</p>
<p><s>(Игры в сисадмина)</s> Домашние сервера (с 1 января 2017, когда настроил Raspberry Pi 3, что пришел днём ранее)</p>
<p>Программно Определяемое Радио (SDR, Software Defined Radio) (с июня 2018)</p>
<h3>Что знаю</h3>
<h4>Иностранные языки</h4>
<p>Покуда я люблю учить языки, отсутствие практической пользы и лень не дают мне нормально что-то выучить, так что я остановился на английском. Ранее брался учить немецкий, норвежский, французский, польский и японский. Конечно же знания у меня так себе, хорошо если прочесть могу, конечно по разному, разместил их в порядке убывания знаний.</p>
<p>Есть ещё один язык который я неплохо понимаю, но при этом никогда специально не учил, разве что смотрел отдельные слова ради интереса, и этот язык &mdash; украинский. За последнюю декаду прям прилип ко мне конкретно, хех. :) Та й взагалi, не розумiю тих, хто говорить, що зовсiм не розумiє українську. Її навiть не треба спецiйно вчити, просто читай контент на нiй, слухай й вона сама прилипне. :) Ну що тут можна додати? Тiльки слава Українi у складi Росiї! Все так, треба-треба. Хохли сосати! Тепер ви руськi всi. хД А якщо серйозно, хай мова продовжає iснувати, нiчого проти ней не маю, дуже гарна. Давайте жити дружно, хохли їбанi. :)</p>
<p>На счёт белорусского, примерно та же ситуация, однако, на нём я не могу разговаривать, так, знаю несколько слов, что случайно запомнились, но вполне понимаю, когда читаю на нём или слушаю песни.</p>
<h4>Компьютерные языки</h4>
<p>Используемые мною ныне языки: Go, JavaScript (vanilla), SQL, Python, Bash, AWK, HTML, CSS, LaTeX, Markdown, JSON, XML.</p>
<p>Ранее использовал (в хронологическом порядке): Pascal, Delphi, C#, C++, C, Java, PHP. Пробовал: Visual Basic, LISP, Haskell, Rust.</p>
<p>Так уж вышло, что я проэтовал весь код из прошлого. Да и 99% того, что былр написано в процессе изучения и по фану никогда не было закончено. Такой код я хладнокровно удалял. Да и на кой хранить, например, хеллоу ворлд для OpenGL?.. :)</p>
<p>А вот опыт, полученный в процессе, как раз никуда ни проэтовался. :) И мой <a href={ templ.SafeURL(util.GetServiceByHost(r.Host, util.ServiceGit)) }>инстанс Gitea</a> вполне может говорить за меня.</p>
<p>У меня до сих пор валяется старый IDE НЖМД, что перестал видиться в системе в 2011. По крайней мере он раскручивается, да к тому же без каких-либо шумов страшных. Уже не помню что на нем, думаю там и совсем старый учебный код лежит, и карты, что создавал тогда для халфы с каэской... :)</p>
<h3>Что нравится</h3>
<h4>Аниме</h4>
<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.</p>
<h4>Фильмы</h4>
<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).</p>
<h4>Сериалы</h4>
<p>X-Files (1993&mdash;2002), Stargate: SG-1 (1997&mdash;2007), Stargate: Atlantis (2004&mdash;2009), Lost (2004&mdash;2010), Breaking Bad (2008&mdash;2013), Narcos (2015&mdash;2017), The Shivering Truth (2018&mdash;2020), Два холма (Two Hills) (2022).</p>
<h4>Игры для Dendy</h4>
<p>У меня была не просто Денди, а оная встроенная в клавиатуру, называлась СЮБОР.</p>
<p>Chip 'n Dale Rescue Rangers, а другая игра была гонкой формула 1. Ещё у сюбора был обучающий картридж, а на нём был клавиатурный тренажер, простой текстовый редактор, калькулятор, G-BASIC и F-BASIC, ещё был раздел с нашей 8-битной музыкой по типу Московских вечеров.</p>
<p>Хоть и был там бейсик, но программистом не стал, ибо мануала не было. Да и в интернете нашел инфу, что там были просто примеры кода без объяснения, дак ещё он и нерабочий был, ЛОЛ.</p>
<h4>Игры для SEGA Mega Drive</h4>
<p>Не много игр было мною играно.</p>
<p>Bubba'n'Stix, Battletoads, Granada, Demolition Man, Road Rash, Doom Troopers - The Mutant Chronicles, The Lost Vikings, Ghostbusters, Mig-29 Fighter Pilot, Street Racer.</p>
<h4>Игры для ПК</h4>
<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, Tell Some Story: Foz.</p>
<h4>Музыка</h4>
<p>Прошу посетить <a href={ templ.SafeURL(util.GetServiceByHost(r.Host, util.ServiceFiles)) + "/music" }>мою файловую шару</a>. :)</p>
</section>
<section id="servers-summary">
<h2>О моих серверах</h2>
<p>У меня два сервера, которые лежат на системнике. Первый это Raspberry Pi 3 rev. B, а второй &mdash; ноутбук Acer Packard Bell TE69CX. Не впечатляет, но то, что на них возложено исполняют в полной мере, конечно под реальной нагрузкой задохнуться, в этом сомнений нет. :)</p>
<p>У ноута следующие характеристики: проц Intel Pentium 2117U 1,8GHz с двумя ядрами, ОЗУ установлено 10ГБ (две плашки 8+2), системный диск это SSD на 120ГБ, а диск с данными на 2ТБ, и да, для данных всего один диск. Но не беспокойтесь, что нужно забекаплено (ну почти, за актуальностью бекапов плохо слежу %)).</p>
<p>К малинке прицеплен внешний НЖМД на 500ГБ и для системы и для данных.</p>
<p>А ещё я обзавёлся дешевой VPS-кой с 1 ядром на 2,2ГГц и 512МБ ОЗУ с диском на 10ГБ. Использую как вторичный сервер DNS и как рилей для отправки почты.</p>
<div class="columns figs">
<figure>
<a href="/assets/img/photos/raspi.webp">
<img src="/assets/img/photos/raspi_thumb.webp" alt="Raspberry Pi 3 rev. B" />
</a>
<figcaption>Raspberry Pi 3 rev. B</figcaption>
</figure>
<figure>
<a href="/assets/img/photos/acer.webp">
<img src="/assets/img/photos/acer_thumb.webp" alt="Acer Packard Bell TE69CX" title="Экран разбит, а клава просто перестала работать." />
</a>
<figcaption>Acer Packard Bell TE69CX</figcaption>
</figure>
</div>
</section>
<section id="services-public">
<h2>Публичные сервисы</h2>
<p class="center">Для всех.</p>
<div class="columns">
<div>
<h3 class={ isServiceUp("radio", services) }>Интернет-радио</h3>
<p>
<a href="https://radio.arav.su">radio.arav.su</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>
</p>
<p>Составлять программу вещания лень, да и не больно-то и нужно. Просто закинуть музыку, перемешать. :)</p>
<p>Технически устроено так: <a href={ templ.SafeURL(util.GetServiceByHost(r.Host, util.ServiceGit) + "/Arav/dwelling-radio") }>dwelling-radio</a> (помимо фронта, ещё и плейлист выдаёт и следит за статами) &xrArr; Ezstream &xrArr; Icecast &xrArr; NGiNX.</p>
</div>
<div>
<h3 class={ isServiceUp("dwelling-files", services) }>Файловая шара</h3>
<p>
<a href="https://files.arav.su">files.arav.su</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>
</p>
<p>Всё чем могу поделиться. Музыка, видосы всякие, книги, игры, программы, немного драйверов, образы с виндой. В общем, заходите.</p>
</div>
<div>
<h3 class={ isServiceUp("dwelling-upload", services) }>Файлообменник</h3>
<p>
<a href="https://upload.arav.su">upload.arav.su</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>
</p>
<p>Сделал свой собственный файлообменник, которым решил поделиться с миром. Лимит 128МБ на файл, хранение 36 часов, а всего выделил 100ГиБ под него.</p>
</div>
<div>
<h3>Ретранслятор Tor</h3>
<p><a href="https://metrics.torproject.org/rs.html#details/CEF2FD0E1973EA04D1444DDAEFF1B0BC3C0C39B1" rel="nofollow noreferrer">metrics.torproject.org</a></p>
<p><b>С 1 декабря 2021 Tor заблокирован в России, так что нет больше релея.</b></p>
</div>
<div>
<h3>Роутер I2P</h3>
<p>Помогаю сети и хосчусь сам с помощью i2pd.</p>
</div>
</div>
</section>
<section id="services-private">
<h2>Приватные сервисы</h2>
<p class="center">Для себя и друзей.</p>
<div class="columns">
<div>
<h3 class={ isServiceUp("mail", services) }>Сервер E-Mail</h3>
<p>Postfix, Dovecot (w/Sieve), rspamd, ClamAV.</p>
</div>
<div>
<h3 class={ isServiceUp("mumble", services) }>Mumble</h3>
<p><a href="mumble://arav.su">mumble://arav.su</a></p>
<p>Работает на uMurmur. Защищен паролем.</p>
</div>
<div>
<h3 class={ isServiceUp("teamspeak3", services) }>TeamSpeak 3</h3>
<p><a href="ts3server://arav.su">ts3server://arav.su</a></p>
<p>Для чужаков (незарегистрированных) доступна только комната Entrance room. Также установил ограничение по минимальному уровню секьюрности идентификатора равным 29.</p>
</div>
<div>
<h3 class={ isServiceUp("git", services) }>Git</h3>
<p>
<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>
</p>
<p>Gitea. Вход на посмотреть/взять свободный. Всё остальное с аккаунтом.</p>
</div>
</div>
</section>
<section id="services-games">
<h2>Игровые сервера</h2>
<div class="columns">
<div>
<h3 class={ isServiceUp("game-minecraft", services) }>Minecraft</h3>
<p class="highlighted">arav.su:25565</p>
<p><a href="/minemap">Карта мира</a></p>
<p>Сейчас работает на версии 1.21 с API Fabric. Для игры здесь требуется только fabric-api.</p>
</div>
</div>
</section>
<section id="services-inner">
<h2>Внутренние сервисы</h2>
<p class="center">Сюда входят вспомогательные сервисы и сугубо мной используемые.</p>
<div class="columns">
<div>
<h3>Web-server</h3>
<p>NGiNX. Что ещё добавить?</p>
</div>
<div>
<h3>Database</h3>
<p>Раньше использовал MariaDB, сейчас сервисы перевёл на SQLite3, в будущем если будет надо, то уже PostgreSQL буду использовать.</p>
</div>
<div>
<h3>VPN</h3>
<p><s>OpenVPN</s> Wireguard.</p>
</div>
<div>
<h3>Сетевое хранилище данных</h3>
<p>Samba и NFSv4.</p>
</div>
<div>
<h3>Торренты</h3>
<p>Использую transmission-cli. Одна вещь есть раздражающая, временами он создаёт .part-файлы для одного из проигнорированных файлов.</p>
</div>
<div>
<h3>Печать</h3>
<p>CUPS с ccpd (Canon CAPT printer).</p>
<p>Хз, то работает и так, а то и после перезагрузки никак не заведешь, уже подзабил и просто перетыкаю в свой комп принтер, один хер дома печатаю. :)</p>
</div>
<div>
<h3>DNS</h3>
<p>BIND9 через DNSCrypt-proxy.</p>
<p>Для локалки остановился на домене arav.home.arpa (для уникальности, а то вдруг с чьей локалкой надо будет объединиться, хех), .home.arpa введён в RFC 8375 как домен специального назначения для пользования внутри ЛВС.</p>
<p>Основной сервер живёт на ноуте, вторичные на малинке и на VPS, но, конечно же, на VPS только внешний домен, и VPS не является рекурсивным, в отличие от первых двух.</p>
</div>
</div>
</section>
<section id="contacts">
<h2>Контакты</h2>
<span>E-Mail:&nbsp;<a href="mailto:me@arav.su">me@arav.su</a></span>
<span>PGP&nbsp;ключ:&nbsp;<a href="/assets/pgp.asc">7398 50CD 5051 DE55 4368 7092 2596 9B23 DCB5 CA34</a></span>
</section>
<section id="donation">
<h2>Пожертвования</h2>
<p class="center">Просто на всякий. %^)</p>
<table>
<tr>
<td>USDT&nbsp;(TRC&nbsp;20):</td>
<td>TGSEiAh9nAhCL6YPVA6zHUsnuoccW8tjep</td>
</tr>
<tr>
<td>USDT&nbsp;(ERC&nbsp;20):</td>
<td>0x584b3ab1b31d7248e2c0ba5d317e9ba88a4fb1b4</td>
</tr>
<tr>
<td>TON:</td>
<td>EQC4L0tZMDZ8IPGbyXQafoh2n-N9HbJwVp5EtrYGsRCuR28G</td>
</tr>
<tr>
<td>Bitcoin:</td>
<td>bc1qrmpahnvtqp34kpjaghnmq8acq27n0eke5w35ec</td>
</tr>
<tr>
<td>Etherium:</td>
<td>0x584b3ab1b31d7248e2c0ba5d317e9ba88a4fb1b4</td>
</tr>
<tr>
<td>Monero:</td>
<td>48namnfX17TX1kEGCpkXaRWhtw8p92cQjd5uQg7ivybgUuW4BTVaX8egxQhEi75JwuUGn3MDLKHYGNhu4eCfM6dRAAL2QAq</td>
</tr>
</table>
<div class="center">
<a href="https://www.donationalerts.com/r/arav">DonationAlerts</a>
<span><a href="https://www.tinkoff.ru/rm/andreev.aleksandr1164/5nVjK98501">T-банк</a> (<a href="https://www.tinkoff.ru/baf/5zRsqEQic6r">рефералка</a>)</span>
<a href="https://ruvds.com/pay/b5a7a24e142e4d30a46c7c8e09e8e07f">Моя VPS-ка</a>
</div>
</section>
}
templ aboutEn(services *servicestat.ServiceList, r *http.Request) {
<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>
@ -247,7 +501,6 @@ templ About(services *servicestat.ServiceList, r *http.Request) {
<a href="https://ruvds.com/pay/b5a7a24e142e4d30a46c7c8e09e8e07f">Help pay my VPS</a>
</div>
</section>
}
}
func isServiceUp(service string, services *servicestat.ServiceList) string {

View File

@ -4,14 +4,15 @@ import "time"
import "net/http"
import "git.arav.su/Arav/dwelling-home/pkg/util"
import "github.com/invopop/ctxi18n/i18n"
templ Article(title, description, body, urlName string, date time.Time, r *http.Request) {
@base(title + " - Stuff", description, "", "/stuff/article/"+urlName, r, articleHead()) {
@base(title + " - " + i18n.T(ctx, "base.section.stuff"), description, "", "/stuff/article/"+urlName, r, articleHead()) {
<article>
<header>
<h2>{ title }</h2>
<div class="menu">
<a href="/stuff#articles">Go back to articles list</a>
<a href="/stuff#articles">{ i18n.T(ctx, "article.go-back") }</a>
{{ dctz := util.ToClientTimezone(date, r) }}
<time datetime={ dctz.Format("2006-01-02") }>{ dctz.Format("02 January 2006") }</time>
</div>

View File

@ -1,6 +1,6 @@
body {
margin-top: 25vh;
min-height: 75vh; }
margin-top: 18vh;
min-height: 82vh; }
header.main {
align-items: center;

View File

@ -13,8 +13,9 @@ async function getNewCaptcha() {
g_captcha_timeout_remain = g_captcha_timeout_seconds;
}
e_captcha.children[3].innerHTML =
`<button id="refresh">Refresh</button>ed in <b><span id="remain">600</span></b> seconds.`;
e_captcha.children[3].innerHTML = document.cookie.includes("lang=ru") ?
`<button id="refresh">Обновит</button>ся через <b><span id="remain">600</span></b> секунд(у).`
: `<button id="refresh">Refresh</button>ed in <b><span id="remain">600</span></b> seconds.`;
const captcha_refresh = document.getElementById("refresh");
const captcha_remain = document.getElementById("remain");

View File

@ -3,6 +3,8 @@ package web
import "strings"
import "net/http"
import "github.com/invopop/ctxi18n/i18n"
import "git.arav.su/Arav/dwelling-home/internal/version"
import "git.arav.su/Arav/dwelling-home/pkg/util"
@ -37,12 +39,12 @@ templ base(title, description, keywords, canonical string, r *http.Request, head
<text y="7" textLength="360" lengthAdjust="spacingAndGlyphs">Arav's dwelling</text>
<text y="25" textLength="360" lengthAdjust="spacingAndGlyphs">Welcome to my sacred place, wanderer</text>
</svg>
{{ mainNavSections := []string{"Stuff", "Mindflow", "About", "Guestbook"} }}
{{ mainNavSections := []string{i18n.T(ctx, "base.section.stuff"), i18n.T(ctx, "base.section.mindflow"), i18n.T(ctx, "base.section.about"), i18n.T(ctx, "base.section.guestbook")} }}
{{ mainNavLinks := []templ.SafeURL{"/stuff", "/mindflow", "/about", "/guestbook"} }}
<nav>
<ul>
if title != "" {
<li><a href="/">Home</a></li>
<li><a href="/">{ i18n.T(ctx, "base.section.home") }</a></li>
}
for i, s := range mainNavSections {
if strings.HasSuffix(title, s) {
@ -53,8 +55,8 @@ templ base(title, description, keywords, canonical string, r *http.Request, head
</ul>
if title != "" {
<h1>
if strings.HasSuffix(title, "- Stuff") {
Stuff
if strings.HasSuffix(title, "- " + i18n.T(ctx, "base.section.stuff")) {
{ i18n.T(ctx, "base.section.stuff") }
} else {
{ title }
}
@ -66,9 +68,12 @@ templ base(title, description, keywords, canonical string, r *http.Request, head
{ children... }
</main>
<footer>
<a href="/rss.xml" title="Stay up to date on what's going on.">RSS feed</a>
<a href="/rss.xml" title={ i18n.T(ctx, "base.rss-feed-title") }>{ i18n.T(ctx, "base.rss-feed") }</a>
<br/>
{ 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>
<a href="?lang=ru">рус</a>
<a href="?lang=en">eng</a>
<br/>
{ version.GetVersion() } &copy; 2017&mdash;2024 { i18n.T(ctx, "base.copy-author") } &lt;<a href="mailto:me@arav.su">me@arav.su</a>&gt; <a href="/privacy">{ i18n.T(ctx, "base.privacy-statements-link") }</a>
</footer>
</body>
</html>

View File

@ -6,40 +6,42 @@ import "strings"
import "time"
import "net/http"
import "github.com/invopop/ctxi18n/i18n"
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", r, guestbookHead()) {
@base(i18n.T(ctx, "base.section.guestbook"), i18n.T(ctx, "guestbook.description"), "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)"/>
<textarea name="message" maxlength="4096" placeholder="Your message" required></textarea>
<input type="text" name="name" maxlength="80" placeholder={ i18n.T(ctx, "guestbook.form.name") }/>
<input type="text" name="website" maxlength="255" placeholder={ i18n.T(ctx, "guestbook.form.website") }/>
<textarea name="message" maxlength="4096" placeholder={ i18n.T(ctx, "guestbook.form.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>
<label for="hide-website">{ i18n.T(ctx, "guestbook.form.hide-website-1") } <small>{ i18n.T(ctx, "guestbook.form.hide-website-2") }</small></label>
<br/>
<small>Use &gt; to make a quote.</small>
<small>@templ.Raw(i18n.T(ctx, "guestbook.form.quote-usage", i18n.M{"sign":">"}))</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>
<small>@templ.Raw(i18n.T(ctx, "guestbook.form.captcha-validity", i18n.M{"valid":"<b>10</b>"}))</small>
</span>
<input type="submit" value="Send a post"/>
<input type="submit" value={ i18n.T(ctx, "guestbook.form.send-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") }}
{{ created_tz := util.ToClientTimezone(entry.Created, r).Format("02.01.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={ entry.Created.Format(time.RFC3339) }>{ created_tz }</time>
{ strconv.FormatInt(entry.ID, 10) } { i18n.T(ctx, "guestbook.post.by") } <span class="highlighted">{ entry.Name }</span> <em>{ website }</em> { i18n.T(ctx, "guestbook.post.on") } <time datetime={ entry.Created.Format(time.RFC3339) }>{ created_tz }</time>
</header>
for _, line := range strings.Split(entry.Message, "\n") {
<p
@ -50,8 +52,8 @@ templ Guestbook(captchaID, owner string, entries []*justguestbook.Entry, pageCou
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={ entry.Reply.Created.Format(time.RFC3339) }>{ reply_created_tz }</time>
{{ reply_created_tz := util.ToClientTimezone(entry.Reply.Created, r).Format("02.01.2006 15:04:05 -07:00") }}
{ i18n.T(ctx, "guestbook.post.reply") } <span class="highlighted">{ owner }</span> { i18n.T(ctx, "guestbook.post.on") } <time datetime={ entry.Reply.Created.Format(time.RFC3339) }>{ reply_created_tz }</time>
</header>
for _, line := range strings.Split(entry.Reply.Message, "\n") {
<p
@ -64,7 +66,7 @@ templ Guestbook(captchaID, owner string, entries []*justguestbook.Entry, pageCou
</article>
}
if len(entries) == 0 {
<p class="center">No posts.</p>
<p class="center">{ i18n.T(ctx, "guestbook.post.no-posts") }</p>
}
</section>
if pageCount > 1 {

View File

@ -24,9 +24,12 @@ templ Index(r *http.Request) {
</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>
<p>Привет, Анон. Я Александр, хожу по интернету под ником Arav и его всякими вариациями. С 2017 года завел у себя домашний сервер в виде третьей малины, а чуть позже добавил ещё один в виде старого ноута, некоторые сервисы могут быть полезны и тебе, они по ссылкам выше.</p>
<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>
<section>
<p>Изначально завел небольшой сайт на neocities, но быстро стало нехватать всякого (например, шаблонов, чтобы везде одно и то же не менять), потому переехал полностью на свой сервер, а там оставил только ссылки.</p>
<p>Initially I had a small website at neocities, but soon I met a need in an advanced stuff like templates, so not to be have to edit the same parts everywhere, so I moved out to my home server and left there just the links.</p>
</section>
}
}

50
web/locales/en/en.yaml Normal file
View File

@ -0,0 +1,50 @@
en:
base:
rss-feed: RSS
rss-feed-title: Stay up to date on what's going on.
copy-author: Alexander ❝Arav❞ Andreev
privacy-statements-link: Privacy statements
section:
home: Home
stuff: Stuff
mindflow: Mindflow
about: About
guestbook: Guestbook
privacy: Privacy
article:
go-back: Back to a list
guestbook:
description: This is my guestbook. Welcome.
form:
name: Name (Anonymous if left blank)
website: Website (optional)
message: Your message
hide-website-1: Hide website
hide-website-2: (only I can see if set)
quote-usage: Use %{sign} to make a quote.
captcha-validity: Valid for %{valid} minutes.
send-post: Send
post:
by: by
on: on
reply: Reply by
no-posts: No posts.
privacy:
description: Privacy statements for all of my services.
mindflow:
description: Updates on my infrastructure, my very important opinions and thoughts.
text: Updates on websites and servers, my very important thoughts and opinions no one asked for.
no-posts: Nothing? There must be some... Looks like database went down.
all: All
stuff:
description: Here I share my programs, scripts, articles, may be other stuff.
text: Here lies everything I've made that I'm willing to share.
article:
h: Articles
tabh-lu: Last Update
tabh-nm: Article
progs: Programs and scripts
music:
h: Music
text-1: 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
text-2: can be found here

9
web/locales/locales.go Normal file
View File

@ -0,0 +1,9 @@
package locales
import (
"embed"
)
//go:embed en
//go:embed ru
var Content embed.FS

50
web/locales/ru/ru.yaml Normal file
View File

@ -0,0 +1,50 @@
ru:
base:
rss-feed: RSS
rss-feed-title: Будь в курсе происходящего.
copy-author: Александр «Arav» Андреев
privacy-statements-link: О приватности
section:
home: Домой
stuff: Всякое
mindflow: Блог
about: О...
guestbook: Гостевая
privacy: Приватность
article:
go-back: К списку
guestbook:
description: Моя гостевая. Добро пожаловать.
form:
name: Имя (Аноним если оставить пустым)
website: Веб-сайт (необязательно)
message: Твоё сообщение
hide-website-1: Скрыть веб-сайт
hide-website-2: (будет видно только мне)
quote-usage: Используй %{sign} для цитаты.
captcha-validity: Действительна %{valid} минут.
send-post: Отправить
post:
by: от
on: " "
reply: Ответ
no-posts: Нет постов.
privacy:
description: Заявление о приватности на моих сервисах.
mindflow:
description: Новости по работе сайта и серверов, мои нахрен никому не упёршиеся мнения и мысли.
text: Новости по работе сайтов и серверов, мои мысли и мнения, которые никому не упёрлись.
no-posts: Ничего? Как это?.. Видать, база накрылась.
all: Всё
stuff:
description: Здесь все программы, скрипты, статьи и прочее, чем хотел бы поделиться.
text: Здесь лежит всё, чем я хотел бы поделиться.
article:
h: Статьи
tabh-lu: Последнее обновление
tabh-nm: Статья
progs: Программы и скрипты
music:
h: Музыка
text-1: В моей жизни был период, когда я пытался в написание музыки в аудио секвенсорах. Я потерял проекты и сохранились лишь три mp3-шки, которые можно
text-2: взять здесь

View File

@ -4,15 +4,17 @@ import "net/http"
import "strings"
import "time"
import "github.com/invopop/ctxi18n/i18n"
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", 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>
@base(i18n.T(ctx, "base.section.mindflow"), i18n.T(ctx, "base.mindflow.description"), "updates, thoughts, opinions, blog, diary", "/mindflow", r, mindflowHead()) {
<p class="center">{ i18n.T(ctx, "mindflow.text") }</p>
<section>
<menu id="filter" class="hidden">
<button name="all">All</button>
<button name="all">{ i18n.T(ctx, "mindflow.all") }</button>
for _, category := range categories {
<button name={ strings.ToLower(category.Name) }>{ category.Name }</button>
}
@ -37,12 +39,12 @@ templ Mindflow(posts []mindflow.Post, categories []mindflow.Category, r *http.Re
<span></span>
}
{{ ctz := util.ToClientTimezone(post.Date, r) }}
<time datetime={ ctz.Format(time.RFC3339) }>{ ctz.Format(time.RFC1123) }</time>
<time datetime={ ctz.Format(time.RFC3339) }>{ ctz.Format("02.01.2006 15:04:05 -07:00") }</time>
</footer>
</article>
}
if len(posts) == 0 {
<p class="center">Nothing? There must be some... Looks like database went down.</p>
<p class="center">{ i18n.T(ctx, "mindflow.no-posts") }</p>
}
</section>
}

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", r, 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

@ -2,12 +2,36 @@ package web
import "net/http"
import "github.com/invopop/ctxi18n/i18n"
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", r, nil) {
@base(i18n.T(ctx, "base.section.privacy"), "Privacy statements for all of my services.", "privacy statements", "/privacy", r, nil) {
if c := i18n.GetLocale(ctx).Code(); c == "ru" {
<section id="privacy">
<h2>Заявление о приватности</h2>
<p>Все собираемые данные не передаются третьим лицам, исключением являются обращения представителей органов правопорядка.</p>
<h3>Общие данные</h3>
<p>На всех сайтах собираются следующие данные: дата посещения, IP-адрес, User-Agent, URL откуда был переход, запрошенный URL.</p>
<h3>Использование JavaScript</h3>
<p>JS используется на <a href="/">основном сайте</a> на странице гостевой книги для обновления CAPTCHA; на странице блога для фильтрации постов по категориям.</p>
<p><a href={ templ.URL(util.GetServiceByHost(r.Host, util.ServiceRadio)) }>Радио</a> использует JS для обновления данных о проигрываемом треке и списке последних песен, также о количестве слушателей.</p>
<p><a href={ templ.URL(util.GetServiceByHost(r.Host, util.ServiceFiles)) }>Файловая шара</a> использует JS для удобства навигации и просмотра.</p>
<h3>Данные специфичные для сервиса загрузки файлов</h3>
<p>Каждое действие (загрузка, скачивание и удаление) логгируются и включают следующие данные:</p>
<ol>
<li>имя файла с которым он был загружен/скачан;</li>
<li>SHA-256 хэш файла;</li>
<li>посоленый хеш кодированный base64 в формате сырого URL по которому осуществляется скачивание/удаление файла;</li>
<li>размер файла.</li>
</ol>
<p>Я себе не враг и буду содействовать органам правопорядка. Загружаемый контент должен соответствовать законам Российской Федерации как и заявлено на странице <a href={ templ.URL(util.GetServiceByHost(r.Host, util.ServiceUpload)) }>сервиса</a>. Таким образом, например, запрещен экстремисткий контент, все что связано с наркотиками и детским порно.</p>
</section>
} else {
<section id="privacy">
<h2>Privacy statements</h2>
<p>All data that is being collected is never being sent to third parties. Exception is the appeals of law enforcements.</p>
<h3>General data</h3>
<p>Across all of my Web-services following data is being collected: date of access, IP-address, User-Agent, referer URL, request URL.</p>
<h3>Use of JavaScript</h3>
@ -26,4 +50,5 @@ templ Privacy(r *http.Request) {
<p>As already stated at <a href={ templ.URL(util.GetServiceByHost(r.Host, util.ServiceUpload)) }>Upload service</a>'s page, file's content must comply with law of Russian Federation. Anything like extremist materials, CP, and so on is forbidden.</p>
</section>
}
}
}

View File

@ -14,14 +14,14 @@ templ RSS(host, author string, posts []mindflow.Post, r *http.Request) {
<title>Arav's dwelling</title>
<description>What's going on with me and my machines.</description>
<language>en-gb</language>
<link>{ host }</link>
<link>{ host }@templ.Raw("</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>
<link>{ post.PostURL(r.Host, true) }@templ.Raw("</link>")
<author>{ author }</author>
<description>
@templ.Raw("<![CDATA[")

View File

@ -2,24 +2,26 @@ package web
import "net/http"
import "github.com/invopop/ctxi18n/i18n"
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", r, nil) {
<p class="center">Here lies everything I've made that I'm willing to share.</p>
@base(i18n.T(ctx, "base.section.stuff"), i18n.T(ctx, "stuff.description"), "articles, programs, personal projects, own music", "/stuff", r, nil) {
<p class="center">{ i18n.T(ctx, "stuff.text") }</p>
<section id="articles">
<h2>Articles</h2>
<h2>{ i18n.T(ctx, "stuff.article.h") }</h2>
<table>
<tr>
<th>Last Update</th>
<th>Article</th>
<th>{ i18n.T(ctx, "stuff.article.tabh-lu") }</th>
<th>{ i18n.T(ctx, "stuff.article.tabh-nm") }</th>
</tr>
for _, entry := range Metadata {
<tr>
<td>
<time datetime={ entry.Date.Format("2006-01-02") }>{ entry.Date.Format("02 January 2006") }</time>
<time datetime={ entry.Date.Format("2006-01-02") }>{ entry.Date.Format("02.01.2006") }</time>
</td>
<td>
<a href={ templ.URL(entry.URL) }>{ entry.Title }</a>
@ -29,144 +31,148 @@ templ Stuff(r *http.Request) {
</table>
</section>
<section id="programs-scripts">
<h2>Programs and scripts</h2>
<h2>{ i18n.T(ctx, "stuff.progs") }</h2>
<table>
for _, prog := range programs {
<tr>
<td><b>mccl</b></td>
<td>0.1.2 (14 December 2023)</td>
<td>Go</td>
<td>GPLv3</td>
<td><b>{ prog.Name }</b></td>
<td>{ prog.Version }</td>
<td>{ prog.Lang }</td>
<td>{ prog.License }</td>
<td>
<a href={ templ.URL(gitSite + "/Arav/mccl") }>source</a>
<a href={ templ.URL(gitSite + "/Arav/mccl/releases") }>releases</a>
<a href={ templ.URL(gitSite + prog.SourceLink) }>source</a>
if prog.ReleasesLink != "" {
<a href={ templ.URL(gitSite + prog.ReleasesLink) }>releases</a>
}
</td>
</tr>
<tr>
<td colspan="5">
<p>A console Minecraft launcher. Take a look at README.md for explanation.</p>
</td>
</tr>
<tr>
<td><b>httpr</b></td>
<td>0.3.2 (20 September 2023)</td>
<td>Go</td>
<td>MIT</td>
<td>
<a href={ templ.URL(gitSite + "/Arav/httpr") }>source</a>
<a href={ templ.URL(gitSite + "/Arav/httpr/releases") }>releases</a>
</td>
</tr>
<tr>
<td colspan="5">
<p>A simple HTTP router that supports having both regular and parametrised path's parts at the same level, like <code>/assets/*filepath</code> and <code>/:a/:b</code> with the same HTTP method.</p>
<p>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 <code>/f/</code> part and leave just <code>/:hash/:name</code> instead of <code>/f/:hash/:name</code> in a dwelling-upload service.</p>
</td>
</tr>
<tr>
<td><b>justguestbook</b></td>
<td>1.3.2 (22 May 2023)</td>
<td>Go</td>
<td>MIT</td>
<td>
<a href={ templ.URL(gitSite + "/Arav/justguestbook") }>source</a>
</td>
</tr>
<tr>
<td colspan="5">
<p>A simple guestbook with owner's replies implementation made into a library.</p>
</td>
</tr>
<tr>
<td><b>justcaptcha</b></td>
<td>2.1.0 (12 August 2023)</td>
<td>Go</td>
<td>MIT</td>
<td>
<a href={ templ.URL(gitSite + "/Arav/justcaptcha") }>source</a>
</td>
</tr>
<tr>
<td colspan="5">
<p>A simple CAPTCHA implementation.</p>
</td>
</tr>
<tr>
<td><b>kwh-cost</b></td>
<td>1.1.3 (21 May 2024)</td>
<td>C</td>
<td>MIT+NIGGER</td>
<td>
<a href={ templ.URL(gitSite + "/Arav/kwh-cost") }>source</a>
</td>
</tr>
<tr>
<td colspan="5">
<p>KWh cost calculator in C.</p>
</td>
</tr>
<tr>
<td><b>httpprocprobed</b></td>
<td>3.1.1 (13 January 2024)</td>
<td>Go</td>
<td>MIT+NIGGER</td>
<td>
<a href={ templ.URL(gitSite + "/Arav/httpprocprobed") }>source</a>
</td>
</tr>
<tr>
<td colspan="5">
<p>It returns a list of processes and if they are running in a JSON format via HTTP GET request on <code>/processes</code> 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.</p>
</td>
</tr>
<tr>
<td><b>ScrapTheChan</b></td>
<td>0.5.0 (3 May 2021)</td>
<td>Python</td>
<td>MIT</td>
<td>
<a href={ templ.URL(gitSite + "/Arav/ScrapTheChan") }>source</a>
<a href={ templ.URL(gitSite + "/Arav/ScrapTheChan/releases") }>releases</a>
</td>
</tr>
<tr>
<td colspan="5">
<p>Imageboards file scraper using theirs JSON API. Currently supported: 4chan.org, lainchan.org, 2ch.hk and 8kun.top.</p>
</td>
</tr>
<tr>
<td><b>OpenNIC active domains extraction script</b></td>
<td>10 July 2020</td>
<td>Bash</td>
<td>MIT</td>
<td>
<a href={ templ.URL(gitSite + "/Arav/opennic-extract-domains") }>source</a>
</td>
</tr>
<tr>
<td colspan="5">
<p>Bash script for BIND9 that extracts domains from zone files of OpenNIC and forms a list of domains that have an IP-address.</p>
</td>
</tr>
<tr>
<td><b>PiggyBank</b></td>
<td>1.0.0 (8 July 2020)</td>
<td>Python</td>
<td>MIT</td>
<td>
<a href={ templ.URL(gitSite + "/Arav/PiggyBank") }>source</a>
<a href={ templ.URL(gitSite + "/Arav/PiggyBank/releases") }>releases</a>
</td>
</tr>
<tr>
<td colspan="5">
<p>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.</p>
for _, line := range prog.Description[i18n.GetLocale(ctx).Code().String()] {
<p>@templ.Raw(line)</p>
}
</td>
</tr>
}
</table>
</section>
<section id="music">
<h2>Music</h2>
<p>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 <a href={ templ.URL(util.GetServiceByHost(r.Host, util.ServiceFiles) + "/music/My%20tracks,%20that%20survived/") }>can be found here</a>.</p>
<h2>{ i18n.T(ctx, "stuff.music.h") }</h2>
<p>{ i18n.T(ctx, "stuff.music.text-1") } <a href={ templ.URL(util.GetServiceByHost(r.Host, util.ServiceFiles) + "/music/My%20tracks,%20that%20survived/") }>{ i18n.T(ctx, "stuff.music.text-2") }</a>.</p>
</section>
}
}
type program struct {
Name string
Version string
Lang string
License string
SourceLink string
ReleasesLink string
Description map[string][]string
}
var programs []program = []program{
{
Name: "mccl",
Version: "0.1.2 (14.12.2023)",
Lang: "Go",
License: "GPLv3",
SourceLink: "/Arav/mccl",
ReleasesLink: "/Arav/mccl/releases",
Description: map[string][]string{
"en": {"A console Minecraft launcher. Take a look at README.md for explanation."},
"ru": {"Консольный лаунчер Minecraft. За подробностями в README.md."}},
},
{
Name: "httpr",
Version: "0.3.2 (20.09.2023)",
Lang: "Go",
License: "MIT",
SourceLink: "/Arav/httpr",
ReleasesLink: "/Arav/httpr/releases",
Description: map[string][]string{
"en": {
"A simple HTTP router that supports having both regular and parametrised path's parts at the same level, like <code>/assets/*filepath</code> and <code>/:a/:b</code> with the same HTTP method."},
"ru": {
"Простой HTTP роутер, который позволяет иметь на одном уровне и параметризованные и обычные пути, например, <code>/assets/*filepath</code> и <code>/:a/:b</code>."}},
},
{
Name: "justguestbook",
Version: "1.3.2 (22.05.2023)",
Lang: "Go",
License: "MIT",
SourceLink: "/Arav/justguestbook",
ReleasesLink: "",
Description: map[string][]string{
"en": {"A simple guestbook with owner's replies implementation made into a library."},
"ru": {"Простая гостевая книга в виде библиотеки с возможностью владельцу отвечать на посты."}},
},
{
Name: "justcaptcha",
Version: "2.1.0 (12.08.2023)",
Lang: "Go",
License: "MIT",
SourceLink: "/Arav/justcaptcha",
ReleasesLink: "",
Description: map[string][]string{
"en": {"A simple CAPTCHA implementation."},
"ru": {"Простая CAPTCHA."}},
},
{
Name: "kwh-cost",
Version: "1.1.3 (21.05.2024)",
Lang: "C",
License: "MIT+NIGGER",
SourceLink: "/Arav/kwh-cost",
ReleasesLink: "",
Description: map[string][]string{
"en": {"KWh cost calculator in C."},
"ru": {"Калькулятор стоимости кВтч на C."}},
},
{
Name: "httpprocprobed",
Version: "3.2.0 (7.07.2024)",
Lang: "Go",
License: "MIT+NIGGER",
SourceLink: "/Arav/httpprocprobed",
ReleasesLink: "",
Description: map[string][]string{
"en": {"It returns a list of processes and if they are running in a JSON format via HTTP GET request on <code>/processes</code> 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."},
"ru": {"Возвращает JSON список процессов и запущены ли они. Процессы ищет считывая директорию <code>/proc/</code>. Использую на странице О... для показа статуса сервиса."}},
},
{
Name: "ScrapTheChan",
Version: "0.5.0 (3.05.2021)",
Lang: "Python",
License: "MIT",
SourceLink: "/Arav/ScrapTheChan",
ReleasesLink: "/Arav/ScrapTheChan/releases",
Description: map[string][]string{
"en": {"Imageboards file scraper using theirs JSON API. Currently supported: 4chan.org, lainchan.org, 2ch.hk and 8kun.top."},
"ru": {"Граббер файлов с имиджборд используя их JSON API. Работает с: 4chan.org, lainchan.org, 2ch.hk and 8kun.top."}},
},
{
Name: "OpenNIC active domains extraction script",
Version: "10.07.2020",
Lang: "Bash",
License: "MIT",
SourceLink: "/Arav/opennic-extract-domains",
ReleasesLink: "",
Description: map[string][]string{
"en": {"Bash script for BIND9 that extracts domains from zone files of OpenNIC and forms a list of domains that have an IP-address."},
"ru": {"Баш-скрипт для BIND9 извлекает домены OpenNIC у которых прописан IP-адрес."}},
},
{
Name: "PiggyBank",
Version: "1.0.0 (8.07.2020)",
Lang: "Python",
License: "MIT",
SourceLink: "/Arav/PiggyBank",
ReleasesLink: "/Arav/PiggyBank/releases",
Description: map[string][]string{
"en": {"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."},
"ru": {"Это просто копилка."}},
},
}

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.