summaryrefslogtreecommitdiff
diff refs
from: back
to: back
| flip
diff options
context:
space:
mode:
-rw-r--r--Dockerfile9
-rw-r--r--config/config.go12
-rw-r--r--config/config.local.yaml4
-rw-r--r--config/config.prod.yaml2
-rw-r--r--config/config.stage.yaml2
-rw-r--r--go.mod39
-rw-r--r--go.sum72
-rw-r--r--internal/router/api-v1-blog-search.go1
-rw-r--r--internal/router/api-v1-like.go15
-rw-r--r--internal/router/client-cache.go179
-rw-r--r--internal/router/lang-blog.go2
-rw-r--r--locale/localization.en.yaml1
-rw-r--r--locale/localization.go1
-rw-r--r--locale/localization.ru.yaml1
-rw-r--r--main.go69
-rw-r--r--migrations/1_create_stats_tables.down.sql2
-rw-r--r--migrations/1_create_stats_tables.up.sql11
-rw-r--r--static/favicon.icobin5694 -> 0 bytes
-rw-r--r--static/input.css13
-rw-r--r--views/layouts/general-page.html126
-rw-r--r--views/pages/blog-catalogue.html75
-rw-r--r--views/pages/blog-page.html6
-rw-r--r--views/partials/blog-page-like-button.html5
-rw-r--r--views/partials/catalogue-blog-cards.html3
24 files changed, 147 insertions, 503 deletions
diff --git a/Dockerfile b/Dockerfile
index eeee6c5..ffe5ac3 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,10 +1,8 @@
-FROM golang:1.24.6-alpine3.22 AS build-stage
-
-RUN apk add --no-cache sqlite-dev musl-dev gcc
+FROM golang:1.24.5-alpine3.22 AS build-stage
WORKDIR /builddir
COPY . .
-RUN CGO_ENABLED=1 go build -o sayana-web .
+RUN go build -o sayana-web .
FROM alpine:3.22.1 AS runtime-stage
@@ -15,12 +13,11 @@ ENV B2_APPLICATION_KEY=""
WORKDIR /app
COPY --from=build-stage /builddir/sayana-web /app/sayana-web
-COPY --from=build-stage /builddir/migrations /app/migrations
COPY --from=build-stage /builddir/static /app/static
COPY --from=build-stage /builddir/views /app/views
COPY --from=build-stage /builddir/locale/*.yaml /app/locale/
COPY --from=build-stage /builddir/config/config*.yaml /app/config/
-RUN apk add --no-cache tzdata sqlite
+RUN apk add --no-cache tzdata
ENTRYPOINT /app/sayana-web -c /app/config/config.${ENVIRONMENT}.yaml
diff --git a/config/config.go b/config/config.go
index 7438fe1..fe0ac72 100644
--- a/config/config.go
+++ b/config/config.go
@@ -41,17 +41,7 @@ type AvailableLanguageConfig struct {
}
type AuthConfig struct {
- Salt string `json:"Salt" yaml:"salt" validate:"required"`
- Db DbConfig `json:"Db" yaml:"db" validate:"required"`
-}
-
-type DbConfig struct {
- Type string `json:"Type" yaml:"type" validate:"required,oneof=sqlite3"`
- Cfg Sqlite3Config `json:"Config" yaml:"config"`
-}
-
-type Sqlite3Config struct {
- DSN string `json:"DSN" yaml:"dsn" validate:"required"`
+ Salt string `json:"Salt" yaml:"salt" validate:"required"`
}
func LoadConfig(path string, config *Config) error {
diff --git a/config/config.local.yaml b/config/config.local.yaml
index 0b48e47..330e29c 100644
--- a/config/config.local.yaml
+++ b/config/config.local.yaml
@@ -20,7 +20,7 @@ availableLanguages:
locFile: localization.en.yaml
auth:
db:
- type: sqlite3
+ type: sqlite
config:
- dsn: 'file:/tmp/auth.db?cache=shared&mode=rwc'
+ dsn: 'file:/data/auth.db?cache=shared&mode=memory'
salt: '123'
diff --git a/config/config.prod.yaml b/config/config.prod.yaml
index e1294c0..76288fa 100644
--- a/config/config.prod.yaml
+++ b/config/config.prod.yaml
@@ -20,7 +20,7 @@ availableLanguages:
locFile: localization.en.yaml
auth:
db:
- type: sqlite3
+ type: sqlite
config:
dsn: 'file:/data/auth.db?cache=private&mode=rwc&_locking_mode=EXCLUSIVE&_mutex=no&_auto_vacuum=2'
salt: '${AUTH_SALT}'
diff --git a/config/config.stage.yaml b/config/config.stage.yaml
index 30233c1..f6c16cb 100644
--- a/config/config.stage.yaml
+++ b/config/config.stage.yaml
@@ -20,7 +20,7 @@ availableLanguages:
locFile: localization.en.yaml
auth:
db:
- type: sqlite3
+ type: sqlite
config:
dsn: 'file:/data/auth.db?cache=private&mode=rwc&_locking_mode=EXCLUSIVE&_mutex=no&_auto_vacuum=2'
salt: '${AUTH_SALT}'
diff --git a/go.mod b/go.mod
index 71aa20b..e2e916e 100644
--- a/go.mod
+++ b/go.mod
@@ -1,36 +1,33 @@
module github.com/SayaAndy/saya-today-web
-go 1.24.6
+go 1.24.5
-require (
- github.com/Backblaze/blazer v0.7.2
- github.com/go-playground/validator/v10 v10.27.0
- github.com/gofiber/fiber/v2 v2.52.9
- github.com/golang-migrate/migrate/v4 v4.18.3
- github.com/mattn/go-sqlite3 v1.14.32
- github.com/yuin/goldmark v1.7.13
- golang.org/x/crypto v0.41.0
- gopkg.in/yaml.v3 v3.0.1
-)
+require github.com/gofiber/fiber/v2 v2.52.9
require (
- github.com/andybalholm/brotli v1.2.0 // indirect
- github.com/gabriel-vasile/mimetype v1.4.10 // indirect
+ github.com/Backblaze/blazer v0.7.2 // indirect
+ github.com/andybalholm/brotli v1.1.0 // indirect
+ github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
+ github.com/go-playground/validator/v10 v10.27.0 // indirect
+ github.com/gofiber/template v1.8.3 // indirect
+ github.com/gofiber/template/html/v2 v2.1.3 // indirect
+ github.com/gofiber/utils v1.1.0 // indirect
github.com/google/uuid v1.6.0 // indirect
- github.com/hashicorp/errwrap v1.1.0 // indirect
- github.com/hashicorp/go-multierror v1.1.1 // indirect
- github.com/klauspost/compress v1.18.0 // indirect
+ github.com/klauspost/compress v1.17.9 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
- github.com/mattn/go-colorable v0.1.14 // indirect
+ github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
- github.com/rivo/uniseg v0.4.7 // indirect
- github.com/stretchr/testify v1.10.0 // indirect
+ github.com/rivo/uniseg v0.2.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
- github.com/valyala/fasthttp v1.65.0 // indirect
- go.uber.org/atomic v1.7.0 // indirect
+ github.com/valyala/fasthttp v1.51.0 // indirect
+ github.com/valyala/tcplisten v1.0.0 // indirect
+ github.com/yuin/goldmark v1.7.13 // indirect
+ golang.org/x/crypto v0.41.0 // indirect
+ golang.org/x/net v0.42.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/text v0.28.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/go.sum b/go.sum
index 22ef8ff..b3ae996 100644
--- a/go.sum
+++ b/go.sum
@@ -1,14 +1,9 @@
github.com/Backblaze/blazer v0.7.2 h1:UWNHMLB+Nf+UmbO2qkVvgriODLEMz4kIyr2Hm+DVXQM=
github.com/Backblaze/blazer v0.7.2/go.mod h1:T4y3EYa9IQ5J0PKc/C/J8/CEnSd3qa/lgNw938wZg10=
-github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
-github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-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/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
-github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
-github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
-github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
+github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
+github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
+github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
@@ -17,56 +12,55 @@ github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHO
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/gofiber/fiber/v2 v2.52.9 h1:YjKl5DOiyP3j0mO61u3NTmK7or8GzzWzCFzkboyP5cw=
github.com/gofiber/fiber/v2 v2.52.9/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
-github.com/golang-migrate/migrate/v4 v4.18.3 h1:EYGkoOsvgHHfm5U/naS1RP/6PL/Xv3S4B/swMiAmDLs=
-github.com/golang-migrate/migrate/v4 v4.18.3/go.mod h1:99BKpIi6ruaaXRM1A77eqZ+FWPQ3cfRa+ZVy5bmWMaY=
+github.com/gofiber/template v1.8.3 h1:hzHdvMwMo/T2kouz2pPCA0zGiLCeMnoGsQZBTSYgZxc=
+github.com/gofiber/template v1.8.3/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8=
+github.com/gofiber/template/html/v2 v2.1.3 h1:n1LYBtmr9C0V/k/3qBblXyMxV5B0o/gpb6dFLp8ea+o=
+github.com/gofiber/template/html/v2 v2.1.3/go.mod h1:U5Fxgc5KpyujU9OqKzy6Kn6Qup6Tm7zdsISR+VpnHRE=
+github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM=
+github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
-github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
-github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
-github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
-github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
-github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
-github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
+github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
+github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
-github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
-github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
-github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
-github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
-github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
-github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
-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/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
-github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
-github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
-github.com/valyala/fasthttp v1.65.0 h1:j/u3uzFEGFfRxw79iYzJN+TteTJwbYkru9uDp3d0Yf8=
-github.com/valyala/fasthttp v1.65.0/go.mod h1:P/93/YkKPMsKSnATEeELUCkG8a7Y+k99uxNHVbKINr4=
-github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
-github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
+github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
+github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
+github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
+github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA=
github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
-go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
-go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
+golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
+golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
+golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
+golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
+golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
+golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
+golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
+golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
-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=
diff --git a/internal/router/api-v1-blog-search.go b/internal/router/api-v1-blog-search.go
index e22fabe..a809783 100644
--- a/internal/router/api-v1-blog-search.go
+++ b/internal/router/api-v1-blog-search.go
@@ -69,7 +69,6 @@ func Api_V1_BlogSearch(l map[string]*locale.LocaleConfig, langs []string, b2Clie
"ShortDescription": page.Metadata.ShortDescription,
"Thumbnail": page.Metadata.Thumbnail,
"Tags": page.Metadata.Tags,
- "LikeCount": CCache.GetLikeCount(page.FileName),
})
break
}
diff --git a/internal/router/api-v1-like.go b/internal/router/api-v1-like.go
index 327dafa..1732c77 100644
--- a/internal/router/api-v1-like.go
+++ b/internal/router/api-v1-like.go
@@ -8,7 +8,6 @@ import (
"strings"
"github.com/SayaAndy/saya-today-web/internal/b2"
- "github.com/SayaAndy/saya-today-web/locale"
"github.com/gofiber/fiber/v2"
)
@@ -16,7 +15,7 @@ func init() {
tm.Add("blog-page-like-button", "views/partials/blog-page-like-button.html")
}
-func Api_V1_Like_Put(l map[string]*locale.LocaleConfig, b2 *b2.B2Client) func(c *fiber.Ctx) error {
+func Api_V1_Like_Put(b2 *b2.B2Client) func(c *fiber.Ctx) error {
return func(c *fiber.Ctx) error {
c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
@@ -42,12 +41,12 @@ func Api_V1_Like_Put(l map[string]*locale.LocaleConfig, b2 *b2.B2Client) func(c
return c.Status(fiber.ErrNotFound.Code).SendString(fmt.Sprintf("server did not find '%s' article", pageLink))
}
+ ip := c.IP()
newLikeStatus, err := strconv.ParseBool(c.FormValue("like", "true"))
if err != nil {
return c.Status(fiber.ErrBadRequest.Code).SendString("invalid 'like' value")
}
- ip := c.IP()
if newLikeStatus {
CCache.LikeOn(ip, page)
} else {
@@ -57,9 +56,7 @@ func Api_V1_Like_Put(l map[string]*locale.LocaleConfig, b2 *b2.B2Client) func(c
slog.Debug("someone pressed the like button!", slog.String("ip", ip), slog.String("page", page), slog.String("new_like_status", fmt.Sprint(newLikeStatus)))
if c.Get("HX-Request", "false") == "true" {
content, err := tm.Render("blog-page-like-button", fiber.Map{
- "L": l[lang],
- "Liked": newLikeStatus,
- "LikedCount": CCache.GetLikeCount(page),
+ "Liked": newLikeStatus,
})
if err != nil {
slog.Warn("failed to generate div", slog.String("path", path), slog.String("error", err.Error()))
@@ -73,7 +70,7 @@ func Api_V1_Like_Put(l map[string]*locale.LocaleConfig, b2 *b2.B2Client) func(c
}
}
-func Api_V1_Like_Get(l map[string]*locale.LocaleConfig, b2 *b2.B2Client) func(c *fiber.Ctx) error {
+func Api_V1_Like_Get(b2 *b2.B2Client) func(c *fiber.Ctx) error {
return func(c *fiber.Ctx) error {
c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
@@ -105,9 +102,7 @@ func Api_V1_Like_Get(l map[string]*locale.LocaleConfig, b2 *b2.B2Client) func(c
slog.Debug("someone requested the like status!", slog.String("ip", ip), slog.String("page", page), slog.Bool("like_status", likeStatus))
if c.Get("HX-Request", "false") == "true" {
content, err := tm.Render("blog-page-like-button", fiber.Map{
- "L": l[lang],
- "Liked": likeStatus,
- "LikedCount": CCache.GetLikeCount(page),
+ "Liked": likeStatus,
})
if err != nil {
slog.Warn("failed to generate div", slog.String("path", path), slog.String("error", err.Error()))
diff --git a/internal/router/client-cache.go b/internal/router/client-cache.go
index 81d7641..3104a66 100644
--- a/internal/router/client-cache.go
+++ b/internal/router/client-cache.go
@@ -1,145 +1,44 @@
package router
import (
- "database/sql"
"encoding/base64"
- "fmt"
"log/slog"
- "strings"
"sync"
"golang.org/x/crypto/argon2"
)
-type PageLike struct {
- PageRef string
- UserId string
-}
-
type ClientCache struct {
hashMap map[string]string
- hashMapMutex sync.RWMutex
-
- likePageMap map[string]map[string]struct{}
- pageMutexMap map[string]*sync.RWMutex
- pageMutexMapMutex sync.Mutex
-
- salt []byte
- db *sql.DB
+ mutexLikeMap map[string]*sync.Mutex
+ mutexHashMap map[string]*sync.Mutex
+ likePageMap map[string]map[string]struct{}
+ salt []byte
}
var CCache *ClientCache
-func NewClientCache(db *sql.DB, salt []byte) (*ClientCache, error) {
- tx, err := db.Begin()
- if err != nil {
- return nil, fmt.Errorf("fail to init transaction with db to fill cache: %w", err)
- }
-
- rows, err := tx.Query("select * from blog_likes;")
- if err != nil {
- tx.Rollback()
- return nil, fmt.Errorf("fail to query db for blog_likes to fill cache: %w", err)
- }
-
- likePageMap := make(map[string]map[string]struct{})
- pageMutexMap := make(map[string]*sync.RWMutex)
-
- for rows.Next() {
- var pageRef string
- var userId []byte
- if err = rows.Scan(&pageRef, &userId); err != nil {
- tx.Rollback()
- return nil, fmt.Errorf("fail scanning blog_likes to fill cache: %w", err)
- }
- userIdString := base64.RawStdEncoding.EncodeToString(userId)
- if _, ok := likePageMap[pageRef]; !ok {
- likePageMap[pageRef] = make(map[string]struct{})
- pageMutexMap[pageRef] = &sync.RWMutex{}
- }
- likePageMap[pageRef][userIdString] = struct{}{}
- }
-
- if err = tx.Commit(); err != nil {
- return nil, fmt.Errorf("fail to commit transaction in db: %w", err)
- }
-
+func NewClientCache(salt []byte) *ClientCache {
return &ClientCache{
hashMap: make(map[string]string),
- likePageMap: likePageMap,
- pageMutexMap: pageMutexMap,
+ mutexLikeMap: make(map[string]*sync.Mutex),
+ mutexHashMap: make(map[string]*sync.Mutex),
+ likePageMap: make(map[string]map[string]struct{}),
salt: salt,
- db: db,
- }, nil
-}
-
-func (c *ClientCache) Close() error {
- tx, err := c.db.Begin()
- if err != nil {
- return fmt.Errorf("fail to init transaction with db to dump cache: %w", err)
- }
-
- if _, err = tx.Exec("delete from blog_likes;"); err != nil {
- tx.Rollback()
- return fmt.Errorf("fail to truncate table blog_likes: %w", err)
- }
-
- userIdBytes := make(map[string][]byte)
-
- sqlStatement := fmt.Sprintf(`
- INSERT OR IGNORE INTO blog_likes (page_ref, user_id)
- VALUES %s(?, ?);
- `, strings.Repeat("(?, ?), ", 99))
- sqlStatementVars := make([]any, 0, 200)
-
- for pageRef, userSet := range c.likePageMap {
- for userId := range userSet {
- if _, ok := userIdBytes[userId]; !ok {
- userIdBytes[userId], err = base64.RawStdEncoding.DecodeString(userId)
- if err != nil {
- slog.Warn("couldn't parse one of user hashes into bytes back", slog.String("hash", userId), slog.String("error", err.Error()))
- continue
- }
- }
-
- sqlStatementVars = append(sqlStatementVars, any(pageRef), any(userIdBytes[userId]))
- if len(sqlStatementVars) < 200 {
- continue
- }
-
- if _, err := tx.Exec(sqlStatement, sqlStatementVars...); err != nil {
- slog.Warn("couldn't insert blog like pairs into db", slog.String("error", err.Error()))
- }
-
- sqlStatementVars = make([]any, 0, 200)
- }
}
-
- if len(sqlStatementVars) > 0 {
- sqlStatement = fmt.Sprintf(`
- INSERT OR IGNORE INTO blog_likes (page_ref, user_id)
- VALUES %s(?, ?);
- `, strings.Repeat("(?, ?), ", len(sqlStatementVars)/2-1))
-
- if _, err := tx.Exec(sqlStatement, sqlStatementVars...); err != nil {
- slog.Warn("couldn't insert blog like pairs into db", slog.String("error", err.Error()))
- }
- }
-
- return tx.Commit()
}
func (c *ClientCache) GetHash(id string) string {
- c.hashMapMutex.RLock()
if val, ok := c.hashMap[id]; ok {
- c.hashMapMutex.RUnlock()
slog.Debug("gave an old hash", slog.String("hash", val))
return val
}
- c.hashMapMutex.RUnlock()
- c.hashMapMutex.Lock()
- defer c.hashMapMutex.Unlock()
+ if _, ok := c.mutexHashMap[id]; !ok {
+ c.mutexHashMap[id] = &sync.Mutex{}
+ }
+ c.mutexHashMap[id].Lock()
+ defer c.mutexHashMap[id].Unlock()
if val, ok := c.hashMap[id]; ok {
slog.Debug("gave a newly generated hash", slog.String("hash", val))
@@ -151,25 +50,7 @@ func (c *ClientCache) GetHash(id string) string {
return c.hashMap[id]
}
-func (c *ClientCache) getPageMutex(page string) *sync.RWMutex {
- c.pageMutexMapMutex.Lock()
- defer c.pageMutexMapMutex.Unlock()
-
- if mutex, ok := c.pageMutexMap[page]; ok {
- return mutex
- }
-
- c.pageMutexMap[page] = &sync.RWMutex{}
- return c.pageMutexMap[page]
-}
-
func (c *ClientCache) GetLikeStatus(id string, page string) bool {
- page = strings.Clone(page)
-
- mutex := c.getPageMutex(page)
- mutex.RLock()
- defer mutex.RUnlock()
-
if _, ok := c.likePageMap[page]; !ok {
return false
}
@@ -177,27 +58,15 @@ func (c *ClientCache) GetLikeStatus(id string, page string) bool {
return ok
}
-func (c *ClientCache) GetLikeCount(page string) int {
- page = strings.Clone(page)
-
- mutex := c.getPageMutex(page)
- mutex.RLock()
- defer mutex.RUnlock()
-
- if userSet, ok := c.likePageMap[page]; ok {
- return len(userSet)
+func (c *ClientCache) LikeOn(id string, page string) (alreadyLiked bool) {
+ if _, ok := c.mutexLikeMap[id]; !ok {
+ c.mutexLikeMap[id] = &sync.Mutex{}
}
- return 0
-}
+ c.mutexLikeMap[id].Lock()
+ defer c.mutexLikeMap[id].Unlock()
-func (c *ClientCache) LikeOn(id string, page string) (alreadyLiked bool) {
- page = strings.Clone(page)
hash := c.GetHash(id)
- mutex := c.getPageMutex(page)
- mutex.Lock()
- defer mutex.Unlock()
-
if userSet, ok := c.likePageMap[page]; ok {
_, alreadyLiked = userSet[hash]
c.likePageMap[page][hash] = struct{}{}
@@ -210,16 +79,16 @@ func (c *ClientCache) LikeOn(id string, page string) (alreadyLiked bool) {
}
func (c *ClientCache) LikeOff(id string, page string) (alreadyUnliked bool) {
- page = strings.Clone(page)
- hash := c.GetHash(id)
-
- mutex := c.getPageMutex(page)
- mutex.Lock()
- defer mutex.Unlock()
+ if _, ok := c.mutexLikeMap[id]; !ok {
+ c.mutexLikeMap[id] = &sync.Mutex{}
+ }
+ c.mutexLikeMap[id].Lock()
+ defer c.mutexLikeMap[id].Unlock()
if _, ok := c.likePageMap[page]; !ok {
return true
}
+ hash := c.GetHash(id)
if _, ok := c.likePageMap[page][hash]; !ok {
return true
}
diff --git a/internal/router/lang-blog.go b/internal/router/lang-blog.go
index dd3f079..e5220c6 100644
--- a/internal/router/lang-blog.go
+++ b/internal/router/lang-blog.go
@@ -83,7 +83,7 @@ func Lang_Blog(l map[string]*locale.LocaleConfig, langs []string, b2Client *b2.B
"Title": l[lang].BlogSearch.Header,
})
if err != nil {
- slog.Warn("failed to generate page", slog.String("path", c.Path()), slog.String("error", err.Error()))
+ slog.Warn("failed to generate page", slog.String("page", "/"+lang+"/blog"), slog.String("error", err.Error()))
c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
return c.Status(fiber.ErrInternalServerError.Code).SendString("failed to generate page")
}
diff --git a/locale/localization.en.yaml b/locale/localization.en.yaml
index 48ef9da..f5ee6c9 100644
--- a/locale/localization.en.yaml
+++ b/locale/localization.en.yaml
@@ -9,4 +9,3 @@ BlogSearch:
ChooseAllTags: 'Choose All'
GlobalMap:
Header: 'Global Map'
-LikeButton: 'Like!'
diff --git a/locale/localization.go b/locale/localization.go
index b557e08..56b6462 100644
--- a/locale/localization.go
+++ b/locale/localization.go
@@ -11,7 +11,6 @@ type LocaleConfig struct {
TagsLabel string `yaml:"TagsLabel" json:"TagsLabel"`
BlogSearch BlogSearchConfig `yaml:"BlogSearch" json:"BlogSearch"`
GlobalMap GlobalMapConfig `yaml:"GlobalMap" json:"GlobalMap"`
- LikeButton string `yaml:"LikeButton" json:"LikeButton"`
}
type BlogSearchConfig struct {
diff --git a/locale/localization.ru.yaml b/locale/localization.ru.yaml
index d6e9fe4..c236d1e 100644
--- a/locale/localization.ru.yaml
+++ b/locale/localization.ru.yaml
@@ -9,4 +9,3 @@ BlogSearch:
ChooseAllTags: 'Выбрать все'
GlobalMap:
Header: 'Глобальная карта'
-LikeButton: 'Нраица!'
diff --git a/main.go b/main.go
index 020f8ef..1853721 100644
--- a/main.go
+++ b/main.go
@@ -1,13 +1,10 @@
package main
import (
- "database/sql"
- "errors"
"flag"
+ "log"
"log/slog"
"os"
- "os/signal"
- "syscall"
"github.com/SayaAndy/saya-today-web/config"
"github.com/SayaAndy/saya-today-web/internal/b2"
@@ -17,14 +14,9 @@ import (
"github.com/SayaAndy/saya-today-web/locale"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/redirect"
- "github.com/golang-migrate/migrate/v4"
- "github.com/golang-migrate/migrate/v4/database/sqlite3"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/parser"
gmhtml "github.com/yuin/goldmark/renderer/html"
-
- _ "github.com/golang-migrate/migrate/v4/source/file"
- _ "github.com/mattn/go-sqlite3"
)
var (
@@ -60,32 +52,6 @@ func main() {
slog.SetLogLoggerLevel(cfg.LogLevel)
slog.Info("starting sayana-web server...")
- db, err := sql.Open(cfg.Auth.Db.Type, cfg.Auth.Db.Cfg.DSN)
- if err != nil {
- slog.Error("fail to initialize db", slog.String("error", err.Error()))
- os.Exit(1)
- }
-
- driver, err := sqlite3.WithInstance(db, &sqlite3.Config{})
- if err != nil {
- slog.Error("fail to initialize driver for migrating db", slog.String("error", err.Error()))
- os.Exit(1)
- }
-
- m, err := migrate.NewWithDatabaseInstance(
- "file://migrations",
- cfg.Auth.Db.Type, driver)
- if err != nil {
- slog.Error("fail to initialize migration client", slog.String("error", err.Error()))
- os.Exit(1)
- }
-
- if err = m.Up(); err != nil && err == errors.New("no change") {
- slog.Error("fail to apply migrations", slog.String("error", err.Error()))
- os.Exit(1)
- }
- slog.Info("successfully applied migrations")
-
b2Client, err = b2.NewB2Client(&cfg.BlogPages.Storage.Config)
if err != nil {
slog.Error("fail to initialize b2 client", slog.String("error", err.Error()))
@@ -114,11 +80,7 @@ func main() {
StatusCode: 301,
}))
- router.CCache, err = router.NewClientCache(db, []byte(cfg.Auth.Salt))
- if err != nil {
- slog.Error("fail to initialize cache", slog.String("error", err.Error()))
- os.Exit(1)
- }
+ router.CCache = router.NewClientCache([]byte(cfg.Auth.Salt))
app.Get("/", router.Root(cfg.AvailableLanguages))
app.Get("/:lang/map", router.Lang_Map(localization, availableLanguages, b2Client))
@@ -126,31 +88,10 @@ func main() {
app.Get("/:lang/blog/:title", router.Lang_Blog_Title(localization, availableLanguages, b2Client, md))
app.Get("/api/v1/tz", router.Api_V1_TZ())
app.Get("/api/v1/blog-search", router.Api_V1_BlogSearch(localization, availableLanguages, b2Client))
- app.Get("/api/v1/like", router.Api_V1_Like_Get(localization, b2Client))
- app.Put("/api/v1/like", router.Api_V1_Like_Put(localization, b2Client))
+ app.Get("/api/v1/like", router.Api_V1_Like_Get(b2Client))
+ app.Put("/api/v1/like", router.Api_V1_Like_Put(b2Client))
app.Static("/", "./static")
- go func() {
- if err := app.Listen(":3000"); err != nil {
- slog.Error("error while running fiber server", slog.String("error", err.Error()))
- panic(err)
- }
- }()
-
- sigChan := make(chan os.Signal, 1)
- signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
-
- <-sigChan
- slog.Info("gracefully shutting down...")
- if err = app.Shutdown(); err != nil {
- slog.Error("fail to shutdown fiber server", slog.String("error", err.Error()))
- }
- if err = router.CCache.Close(); err != nil {
- slog.Error("fail to dump cache", slog.String("error", err.Error()))
- }
- if err = db.Close(); err != nil {
- slog.Error("fail to close db connection", slog.String("error", err.Error()))
- }
- db.Close()
+ log.Fatal(app.Listen(":3000"))
}
diff --git a/migrations/1_create_stats_tables.down.sql b/migrations/1_create_stats_tables.down.sql
deleted file mode 100644
index ee4593c..0000000
--- a/migrations/1_create_stats_tables.down.sql
+++ /dev/null
@@ -1,2 +0,0 @@
-DROP TABLE IF EXISTS blog_views;
-DROP TABLE IF EXISTS blog_likes;
diff --git a/migrations/1_create_stats_tables.up.sql b/migrations/1_create_stats_tables.up.sql
deleted file mode 100644
index 8852268..0000000
--- a/migrations/1_create_stats_tables.up.sql
+++ /dev/null
@@ -1,11 +0,0 @@
-CREATE TABLE IF NOT EXISTS blog_likes (
- page_ref VARCHAR(32) NOT NULL,
- user_id VARCHAR(32) NOT NULL,
- PRIMARY KEY (page_ref, user_id)
-) WITHOUT ROWID;
-
-CREATE TABLE IF NOT EXISTS blog_views (
- page_ref VARCHAR(32) NOT NULL,
- user_id VARCHAR(32) NOT NULL,
- PRIMARY KEY (page_ref, user_id)
-) WITHOUT ROWID;
diff --git a/static/favicon.ico b/static/favicon.ico
deleted file mode 100644
index 33eb226..0000000
--- a/static/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/static/input.css b/static/input.css
index 47e7d16..18910eb 100644
--- a/static/input.css
+++ b/static/input.css
@@ -581,10 +581,6 @@ body[data-theme~="ram"] .leaflet-tile {
width: 100vw;
}
- .ar-lt-0\.8\:max-h-\[25\%\] {
- max-height: 25%;
- }
-
.ar-lt-0\.8\:max-h-\[80\%\] {
max-height: 80%;
}
@@ -616,7 +612,7 @@ body[data-theme~="ram"] .leaflet-tile {
}
}
-.night-star {
+.star {
color: var(--color-yellow-100);
animation-name: blink;
animation-duration: 5s;
@@ -625,13 +621,6 @@ body[data-theme~="ram"] .leaflet-tile {
-webkit-user-select: none;
}
-.ram-frame {
- width: 20vmin;
- height: 20vmin;
- user-select: none;
- -webkit-user-select: none;
-}
-
@keyframes blink {
0% {
opacity: 1;
diff --git a/views/layouts/general-page.html b/views/layouts/general-page.html
index a7cf66e..9885d66 100644
--- a/views/layouts/general-page.html
+++ b/views/layouts/general-page.html
@@ -15,112 +15,6 @@
<body data-theme="ram" class="font-spectral text-main-dark overflow-hidden bg-interlocked-hexagons h-screen flex flex-row ar-lt-0.8:flex-col">
<script>
- function sfc32(a, b, c, d) {
- return function() {
- a |= 0; b |= 0; c |= 0; d |= 0;
- let t = (a + b | 0) + d | 0;
- d = d + 1 | 0;
- a = b ^ b >>> 9;
- b = c + (c << 3) | 0;
- c = (c << 21 | c >>> 11);
- c = c + t | 0;
- return (t >>> 0) / 4294967296;
- }
- }
-
- function cyrb128(str) {
- let h1 = 1779033703, h2 = 3144134277,
- h3 = 1013904242, h4 = 2773480762;
- for (let i = 0, k; i < str.length; i++) {
- k = str.charCodeAt(i);
- h1 = h2 ^ Math.imul(h1 ^ k, 597399067);
- h2 = h3 ^ Math.imul(h2 ^ k, 2869860233);
- h3 = h4 ^ Math.imul(h3 ^ k, 951274213);
- h4 = h1 ^ Math.imul(h4 ^ k, 2716044179);
- }
- h1 = Math.imul(h3 ^ (h1 >>> 18), 597399067);
- h2 = Math.imul(h4 ^ (h2 >>> 22), 2869860233);
- h3 = Math.imul(h1 ^ (h3 >>> 17), 951274213);
- h4 = Math.imul(h2 ^ (h4 >>> 19), 2716044179);
- h1 ^= (h2 ^ h3 ^ h4), h2 ^= h1, h3 ^= h1, h4 ^= h1;
- return [h1>>>0, h2>>>0, h3>>>0, h4>>>0];
- }
-
- var seed = cyrb128("{{ .Title }}");
- var rand = sfc32(seed[0], seed[1], seed[2], seed[3]);
-
- function calculateVmin(percent) {
- const viewportWidth = window.innerWidth;
- const viewportHeight = window.innerHeight;
-
- const smallerDimension = Math.min(viewportWidth, viewportHeight);
-
- const vminValue = (smallerDimension / 100) * percent;
-
- return vminValue;
- }
-
- function switchThemeDecor(theme) {
- document.querySelectorAll('.theme-decor').forEach((e) => {
- e.remove();
- });
-
- if (theme == "" || typeof theme == "undefined") {
- theme = document.body.getAttribute('data-theme');
- }
-
- vmin = calculateVmin(1);
- var clWidth = document.documentElement.clientWidth;
- var clHeight = document.documentElement.clientHeight;
-
- switch (theme) {
- case "olive": break;
- case "lettuce": break;
- case "night":
- for (i = 0; i < 25; i++) {
- let star = document.createElement("div");
- star.append("*");
- star.classList.add("night-star", "theme-decor", "z-0", "absolute");
- const x = Math.floor(rand() * clWidth);
- const y = Math.floor(rand() * clHeight * 0.8);
- const rot = rand() * 90;
- const animDuration = (rand() * 10) + 3;
- const sz = Math.floor(rand() * 8) + 12;
- star.style.transform = `translateX(${x}px) translateY(${y}px) rotate(${rot}deg)`;
- star.style.animationDuration = `${animDuration}s`;
- star.style.fontSize = `${sz}px`;
- document.body.append(star);
- }
- break;
- case "ram":
- const frameCenters = new Array(4);
- for (i = 1; i <= 4; i++) {
- let frame = document.createElement("img");
- frame.src = `https://f003.backblazeb2.com/file/sayana-static/themes/ram/ram-frame${i}.webp`;
- frame.classList.add("ram-frame", "theme-decor", "z-0", "absolute");
- let x, y;
- outerLoop:
- while (true) {
- x = Math.floor(rand() * clWidth - 20*vmin);
- y = Math.floor(rand() * clHeight - 20*vmin);
- for (j = 0; j < i-1; j++) {
- const xd = frameCenters[j].x - x;
- const yd = frameCenters[j].y - y;
- if (Math.sqrt(xd*xd + yd*yd) < 30*vmin) {
- continue outerLoop;
- }
- }
- break;
- }
- frameCenters[i-1] = {x: x, y: y};
- const rot = rand() * 90 - 45;
- frame.style.transform = `translateX(${x}px) translateY(${y}px) rotate(${rot}deg)`;
- document.body.append(frame);
- }
- break;
- }
- }
-
function switchTheme(theme) {
if (theme == "" || typeof theme == "undefined") {
const currentTheme = document.body.getAttribute('data-theme');
@@ -133,7 +27,6 @@
}
localStorage.setItem('theme', theme);
document.body.setAttribute('data-theme', theme);
- switchThemeDecor(theme);
}
const savedTheme = localStorage.getItem('theme') || 'ram';
@@ -207,6 +100,17 @@
}
});
+ function calculateVmin(percent) {
+ const viewportWidth = window.innerWidth;
+ const viewportHeight = window.innerHeight;
+
+ const smallerDimension = Math.min(viewportWidth, viewportHeight);
+
+ const vminValue = (smallerDimension / 100) * percent;
+
+ return vminValue;
+ }
+
function calculateVmax(percent) {
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
@@ -307,14 +211,6 @@
});
</script>
- <script>
- let decorResizeTimeout;
- window.addEventListener('resize', function() {
- clearTimeout(decorResizeTimeout);
- decorResizeTimeout = setTimeout(switchThemeDecor, 300);
- });
- </script>
-
{{ block "bottom-embeds" . }}{{ end }}
</body>
diff --git a/views/pages/blog-catalogue.html b/views/pages/blog-catalogue.html
index 1d0a2de..25b0685 100644
--- a/views/pages/blog-catalogue.html
+++ b/views/pages/blog-catalogue.html
@@ -1,47 +1,44 @@
{{ define "body" }}
<div class="flex flex-row ar-lt-0.8:flex-col max-h-[calc(100%-7vmax)] ar-lt-0.8:max-h-[calc(100%-25vh)] flex-1 text-[1vmax]">
<form hx-get="/api/v1/blog-search" hx-vals='{"lang": "{{ .Lang }}"}' hx-target=".blog-cards" hx-swap="innerHTML"
- class="tags-list ar-gt-0.8:min-w-[10vh] ar-gt-0.8:max-w-[20vh] ar-gt-0.8:w-fit ar-lt-0.8:w-[100%] ar-lt-0.8:max-h-[25%] flex shrink-0 flex-col ar-gt-0.8:mr-[1.2vmin]">
- <div class="flex flex-col overflow-y-auto">
- <fieldset>
- <legend class="font-bold w-[100%] text-center">{{ .L.BlogSearch.TagsHeader }}</legend>
- <div class="flex flex-col ar-lt-0.8:grid grid-cols-5 grid-flow-row-dense">
- <div class="m-[0.4vmin]">
- <label class="font-bold"><input type="checkbox" id="tagsAllCheckbox" onclick="selectAll();"> {{ .L.BlogSearch.ChooseAllTags }}</label>
- </div>
- {{- range .Tags }}
- <div class="m-[0.4vmin]">
- <label><input type="checkbox" id="tag{{ .Name }}Checkbox" name="tags[]" value="{{ .Name }}" onclick="contextAll();" {{ if contains $.QueryTags .Name }}checked{{ end }}> {{ .Name }} <span class="text-secondary italic">{{ .Count }}</span></label>
- </div>
- {{- end }}
+ class="tags-list ar-gt-0.8:min-w-[10vh] ar-gt-0.8:max-w-[20vh] ar-gt-0.8:w-fit ar-lt-0.8:w-[100%] flex shrink-0 flex-col overflow-y-auto ar-gt-0.8:mr-[1.2vmin]">
+ <fieldset>
+ <legend class="font-bold w-[100%] text-center">{{ .L.BlogSearch.TagsHeader }}</legend>
+ <div class="flex flex-col ar-lt-0.8:grid grid-cols-5 grid-flow-row-dense">
+ <div class="m-[0.4vmin]">
+ <label class="font-bold"><input type="checkbox" id="tagsAllCheckbox" onclick="selectAll();"> {{ .L.BlogSearch.ChooseAllTags }}</label>
</div>
- </fieldset>
- <fieldset class="mt-[0.8vmin]">
- <legend class="font-bold w-[100%] text-center">{{ .L.BlogSearch.OrderByHeader }}</legend>
- <div class="flex flex-col ar-lt-0.8:grid grid-cols-3 grid-rows-2 grid-flow-col">
- <div class="m-[0.4vmin]">
- <label><input type="radio" id="sortTitleAsc" name="sort" value="titleAsc" {{ if eq .QuerySort "titleAsc" }}checked{{ end }}> {{ .L.BlogSearch.TitleOrdered }} <i class="fas fa-arrow-down-a-z"></i></label>
- </div>
- <div class="m-[0.4vmin]">
- <label><input type="radio" id="sortTitleDesc" name="sort" value="titleDesc" {{ if eq .QuerySort "titleDesc" }}checked{{ end }}> {{ .L.BlogSearch.TitleOrdered }} <i class="fas fa-arrow-down-z-a"></i></label>
- </div>
- <div class="m-[0.4vmin]">
- <label><input type="radio" id="sortActionDateAsc" name="sort" value="actionDateAsc" {{ if eq .QuerySort "actionDateAsc" }}checked{{ end }}> {{ .L.BlogSearch.ActionDateOrdered }} <i class="fas fa-arrow-down-1-9"></i></label>
- </div>
- <div class="m-[0.4vmin]">
- <label><input type="radio" id="sortActionDateDesc" name="sort" value="actionDateDesc" {{ if eq .QuerySort "actionDateDesc" }}checked{{ end }}> {{ .L.BlogSearch.ActionDateOrdered }} <i class="fas fa-arrow-down-9-1"></i></label>
- </div>
- <div class="m-[0.4vmin]">
- <label><input type="radio" id="sortPublicationDateAsc" name="sort" value="publicationDateAsc" {{ if eq .QuerySort "publicationDateAsc" }}checked{{ end }}> {{ .L.BlogSearch.PublicationDateOrdered }} <i class="fas fa-arrow-down-1-9"></i></label>
- </div>
- <div class="m-[0.4vmin]">
- <label><input type="radio" id="sortPublicationDateDesc" name="sort" value="publicationDateDesc" {{ if eq .QuerySort "publicationDateDesc" }}checked{{ end }}> {{ .L.BlogSearch.PublicationDateOrdered }} <i class="fas fa-arrow-down-9-1"></i></label>
- </div>
+ {{- range .Tags }}
+ <div class="m-[0.4vmin]">
+ <label><input type="checkbox" id="tag{{ .Name }}Checkbox" name="tags[]" value="{{ .Name }}" onclick="contextAll();" {{ if contains $.QueryTags .Name }}checked{{ end }}> {{ .Name }} <span class="text-secondary italic">{{ .Count }}</span></label>
</div>
- </fieldset>
- </div>
- <hr class="border-t-[0.2vmax] border-dotted border-main-dark my-[0.4vmin]">
- <div class="flex flex-row grow bg-background-light my-[2vmin] p-[0.4vmin] rounded-[0.4vmin] justify-items-center">
+ {{- end }}
+ </div>
+ </fieldset>
+ <fieldset class="mt-[0.8vmin]">
+ <legend class="font-bold w-[100%] text-center">{{ .L.BlogSearch.OrderByHeader }}</legend>
+ <div class="flex flex-col ar-lt-0.8:grid grid-cols-3 grid-rows-2 grid-flow-col">
+ <div class="m-[0.4vmin]">
+ <label><input type="radio" id="sortTitleAsc" name="sort" value="titleAsc" {{ if eq .QuerySort "titleAsc" }}checked{{ end }}> {{ .L.BlogSearch.TitleOrdered }} <i class="fas fa-arrow-down-a-z"></i></label>
+ </div>
+ <div class="m-[0.4vmin]">
+ <label><input type="radio" id="sortTitleDesc" name="sort" value="titleDesc" {{ if eq .QuerySort "titleDesc" }}checked{{ end }}> {{ .L.BlogSearch.TitleOrdered }} <i class="fas fa-arrow-down-z-a"></i></label>
+ </div>
+ <div class="m-[0.4vmin]">
+ <label><input type="radio" id="sortActionDateAsc" name="sort" value="actionDateAsc" {{ if eq .QuerySort "actionDateAsc" }}checked{{ end }}> {{ .L.BlogSearch.ActionDateOrdered }} <i class="fas fa-arrow-down-1-9"></i></label>
+ </div>
+ <div class="m-[0.4vmin]">
+ <label><input type="radio" id="sortActionDateDesc" name="sort" value="actionDateDesc" {{ if eq .QuerySort "actionDateDesc" }}checked{{ end }}> {{ .L.BlogSearch.ActionDateOrdered }} <i class="fas fa-arrow-down-9-1"></i></label>
+ </div>
+ <div class="m-[0.4vmin]">
+ <label><input type="radio" id="sortPublicationDateAsc" name="sort" value="publicationDateAsc" {{ if eq .QuerySort "publicationDateAsc" }}checked{{ end }}> {{ .L.BlogSearch.PublicationDateOrdered }} <i class="fas fa-arrow-down-1-9"></i></label>
+ </div>
+ <div class="m-[0.4vmin]">
+ <label><input type="radio" id="sortPublicationDateDesc" name="sort" value="publicationDateDesc" {{ if eq .QuerySort "publicationDateDesc" }}checked{{ end }}> {{ .L.BlogSearch.PublicationDateOrdered }} <i class="fas fa-arrow-down-9-1"></i></label>
+ </div>
+ </div>
+ </fieldset>
+ <div class="flex flex-row bg-background-light my-[2vmin] p-[0.4vmin] rounded-[0.4vmin] justify-items-center">
<i onclick="defaultSearchParams(); cleanHrefParams();" class="fas fa-rotate-left flex-1 text-center text-[1.5vmax] hover:bg-background-dark cursor-pointer transition-colors duration-300"></i>
<div class="w-[0.5vmin] bg-background-dark grow-0"></div>
<button class="flex-1" type="submit">
diff --git a/views/pages/blog-page.html b/views/pages/blog-page.html
index 65ee31d..d685fda 100644
--- a/views/pages/blog-page.html
+++ b/views/pages/blog-page.html
@@ -74,10 +74,10 @@
{{ .MapLocationAreaMeters }}
)});
- let galleryResizeTimeout;
+ let resizeTimeout;
window.addEventListener('resize', function() {
- clearTimeout(galleryResizeTimeout);
- galleryResizeTimeout = setTimeout(() => {
+ clearTimeout(resizeTimeout);
+ resizeTimeout = setTimeout(() => {
map.invalidateSize();
var oldGalleryMap = new Map(galleryMap);
oldGalleryMap.forEach((createFunc, g, map) => {
diff --git a/views/partials/blog-page-like-button.html b/views/partials/blog-page-like-button.html
index afc3027..50a9d7a 100644
--- a/views/partials/blog-page-like-button.html
+++ b/views/partials/blog-page-like-button.html
@@ -1,7 +1,6 @@
<button
hx-put="/api/v1/like" hx-vals='{"like": {{ not .Liked }}}' hx-target="this" hx-swap="outerHTML" hx-trigger="click"
- class="text-[0.8vmax]/[0.9] font-spectral text-left px-[0.4vmax] py-[0.2vmax] {{ if .Liked }}bg-main-light hover:bg-main-medium text-background-dark border-inset{{ else }}bg-main-dark hover:bg-main-medium text-background-light border-outset{{ end }} cursor-pointer border-[0.2vmax] border-background-dark">
+ class="text-[0.8vmax]/[0.9] font-spectral text-left px-[0.4vmax] py-[0.2vmax] {{ if .Liked }}bg-main-light hover:bg-main-medium{{ else }}bg-main-dark hover:bg-main-medium{{ end }} text-background-dark cursor-pointer {{ if .Liked }}border-inset{{ else }}border-outset{{ end }} border-[0.3vmax] border-background-dark">
<i class="fas fa-thumbs-up w-[0.8vmax] h-[0.8vmax] mr-[0.4vmax]"></i>
- <span>{{ .L.LikeButton }}</span>
- <span class="italic ml-[0.2vmax] text-background-light">({{ .LikedCount }})</span>
+ <span>Нраица!</span>
</button> \ No newline at end of file
diff --git a/views/partials/catalogue-blog-cards.html b/views/partials/catalogue-blog-cards.html
index 35c7ad7..62e931a 100644
--- a/views/partials/catalogue-blog-cards.html
+++ b/views/partials/catalogue-blog-cards.html
@@ -8,9 +8,6 @@
<span class="font-extrabold">{{ .Title }}</span>
<span class="font-extrabold select-none text-secondary">//</span>
<span class="italic text-secondary">{{ .ActionDate }}</span>
- <span class="font-extrabold select-none text-secondary">//</span>
- <i class="fas fa-thumbs-up w-[1vmax] h-[1vmax]"></i>
- <span class="italic text-secondary">{{ .LikeCount }}</span>
</a>
<p class="flex-3/12 grow-0 text-[1vmax] italic text-secondary text-right">{{ .PublishedTime }}</p>
</div>