summaryrefslogtreecommitdiffci
diff options
from:
to:
context:
space:
mode:
authorGravatar SayaAndy <saya.andy@posteo.com> 2025-08-26 00:39:22 +0700
committerGravatar SayaAndy <saya.andy@posteo.com> 2025-08-26 00:39:22 +0700
commit5ed2499a1d8806fe78369b5b492d7990218131c9 (patch)
tree0a9e0548aa145986b654af81554f16c8cf544c41
parentb815b74d7a943d1f71a0ddcf6b343d3958d2b97a (diff)
downloadweb-5ed2499a1d8806fe78369b5b492d7990218131c9.tar.gz
web-5ed2499a1d8806fe78369b5b492d7990218131c9.zip
feat: client hash cache
feat: add local config feat: create a volume for data (for the future) format: unname unused vars
-rw-r--r--.github/workflows/build-and-deploy-prod.yml2
-rw-r--r--.github/workflows/build-and-deploy-stage.yml2
-rw-r--r--Dockerfile1
-rw-r--r--config/config.go5
-rw-r--r--config/config.local.yaml26
-rw-r--r--config/config.prod.yaml6
-rw-r--r--config/config.stage.yaml6
-rw-r--r--deploy/playbook.yml7
-rw-r--r--go.mod8
-rw-r--r--go.sum8
-rw-r--r--internal/router/api-v1-blog-search.go2
-rw-r--r--internal/router/client-cache.go46
-rw-r--r--internal/router/lang-blog-title.go4
-rw-r--r--internal/router/lang-blog.go2
-rw-r--r--main.go6
15 files changed, 122 insertions, 9 deletions
diff --git a/.github/workflows/build-and-deploy-prod.yml b/.github/workflows/build-and-deploy-prod.yml
index 53e307e..a180d39 100644
--- a/.github/workflows/build-and-deploy-prod.yml
+++ b/.github/workflows/build-and-deploy-prod.yml
@@ -75,4 +75,4 @@ jobs:
ANSIBLE_HOST_KEY_CHECKING: False
working-directory: ./deploy
run: |
- ansible-playbook -i inventory.yml -l prod playbook.yml --private-key ~/.ssh/id_ed25519 -u svc_github -e saya_today_web_b2_key_id=${{ secrets.B2_KEY_ID }} -e saya_today_web_b2_application_key=${{ secrets.B2_APPLICATION_KEY }} -e saya_today_web_environment=prod -e saya_today_web_tag=${{ github.ref_name }}
+ ansible-playbook -i inventory.yml -l prod playbook.yml --private-key ~/.ssh/id_ed25519 -u svc_github -e saya_today_web_auth_salt=${{ secrets.AUTH_SALT }} -e saya_today_web_b2_key_id=${{ secrets.B2_KEY_ID }} -e saya_today_web_b2_application_key=${{ secrets.B2_APPLICATION_KEY }} -e saya_today_web_environment=prod -e saya_today_web_tag=${{ github.ref_name }}
diff --git a/.github/workflows/build-and-deploy-stage.yml b/.github/workflows/build-and-deploy-stage.yml
index 104d9ac..0aea0a4 100644
--- a/.github/workflows/build-and-deploy-stage.yml
+++ b/.github/workflows/build-and-deploy-stage.yml
@@ -86,4 +86,4 @@ jobs:
ANSIBLE_HOST_KEY_CHECKING: False
working-directory: ./deploy
run: |
- ansible-playbook -i inventory.yml -l stage playbook.yml --private-key ~/.ssh/id_ed25519 -u svc_github -e saya_today_web_b2_key_id=${{ secrets.B2_KEY_ID }} -e saya_today_web_b2_application_key=${{ secrets.B2_APPLICATION_KEY }} -e saya_today_web_environment=stage -e saya_today_web_tag=commit-${{ steps.ghss_vars.outputs.sha_short }}
+ ansible-playbook -i inventory.yml -l stage playbook.yml --private-key ~/.ssh/id_ed25519 -u svc_github -e saya_today_web_auth_salt=${{ secrets.AUTH_SALT }} -e saya_today_web_b2_key_id=${{ secrets.B2_KEY_ID }} -e saya_today_web_b2_application_key=${{ secrets.B2_APPLICATION_KEY }} -e saya_today_web_environment=stage -e saya_today_web_tag=commit-${{ steps.ghss_vars.outputs.sha_short }}
diff --git a/Dockerfile b/Dockerfile
index cd3e0eb..ffe5ac3 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -7,6 +7,7 @@ RUN go build -o sayana-web .
FROM alpine:3.22.1 AS runtime-stage
ENV ENVIRONMENT=""
+ENV AUTH_SALT=""
ENV B2_KEY_ID=""
ENV B2_APPLICATION_KEY=""
diff --git a/config/config.go b/config/config.go
index 8c0321e..fe0ac72 100644
--- a/config/config.go
+++ b/config/config.go
@@ -13,6 +13,7 @@ type Config struct {
BlogPages BlogPagesConfig `json:"BlogPages" yaml:"blogPages" validate:"required"`
LocalePath string `json:"LocalePath" yaml:"localePath" validate:"required,filepath"`
AvailableLanguages []AvailableLanguageConfig `json:"AvailableLanguages" yaml:"availableLanguages" validate:"required"`
+ Auth AuthConfig `json:"Auth" yaml:"auth" validate:"required"`
}
type BlogPagesConfig struct {
@@ -39,6 +40,10 @@ type AvailableLanguageConfig struct {
LocFile string `json:"LocFile" yaml:"locFile" validate:"required,filepath"`
}
+type AuthConfig struct {
+ Salt string `json:"Salt" yaml:"salt" validate:"required"`
+}
+
func LoadConfig(path string, config *Config) error {
fileBytes, err := os.ReadFile(path)
if err != nil {
diff --git a/config/config.local.yaml b/config/config.local.yaml
new file mode 100644
index 0000000..330e29c
--- /dev/null
+++ b/config/config.local.yaml
@@ -0,0 +1,26 @@
+logLevel: debug
+blogPages:
+ storage:
+ type: b2
+ config:
+ bucketName: sayana-pages
+ region: eu-central-003
+ prefix: 'stage-'
+ keyID: '${B2_KEY_ID}'
+ applicationKey: '${B2_APPLICATION_KEY}'
+localePath: ./locale/
+availableLanguages:
+ - name: ru
+ alt: Русский
+ flag: https://f003.backblazeb2.com/file/sayana-static/flags/ru.svg
+ locFile: localization.ru.yaml
+ - name: en
+ alt: English
+ flag: https://f003.backblazeb2.com/file/sayana-static/flags/gb.svg
+ locFile: localization.en.yaml
+auth:
+ db:
+ type: sqlite
+ config:
+ dsn: 'file:/data/auth.db?cache=shared&mode=memory'
+ salt: '123'
diff --git a/config/config.prod.yaml b/config/config.prod.yaml
index 2d050bf..76288fa 100644
--- a/config/config.prod.yaml
+++ b/config/config.prod.yaml
@@ -18,3 +18,9 @@ availableLanguages:
alt: English
flag: https://f003.backblazeb2.com/file/sayana-static/flags/gb.svg
locFile: localization.en.yaml
+auth:
+ db:
+ 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 a34a33e..f6c16cb 100644
--- a/config/config.stage.yaml
+++ b/config/config.stage.yaml
@@ -18,3 +18,9 @@ availableLanguages:
alt: English
flag: https://f003.backblazeb2.com/file/sayana-static/flags/gb.svg
locFile: localization.en.yaml
+auth:
+ db:
+ 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/deploy/playbook.yml b/deploy/playbook.yml
index ca22ac3..dfceaf7 100644
--- a/deploy/playbook.yml
+++ b/deploy/playbook.yml
@@ -4,6 +4,10 @@
remote_user: svc_github
tasks:
+ - name: Create a volume
+ community.docker.docker_volume:
+ name: "{{ saya_today_web_name }}-volume"
+
- name: Deploy saya-today-web Docker Container
community.docker.docker_container:
name: "{{ saya_today_web_name }}"
@@ -13,10 +17,13 @@
B2_KEY_ID: "{{ saya_today_web_b2_key_id }}"
B2_APPLICATION_KEY: "{{ saya_today_web_b2_application_key }}"
ENVIRONMENT: "{{ saya_today_web_environment }}"
+ AUTH_SALT: "{{ saya_today_web_auth_salt }}"
network_mode: caddy
networks:
- name: caddy
ipv4_address: "{{ saya_today_web_ipv4_address }}"
+ volumes:
+ - "{{ saya_today_web_name }}-volume:/data:rw"
restart_policy: unless-stopped
no_log: true
...
diff --git a/go.mod b/go.mod
index c6f267d..e2e916e 100644
--- a/go.mod
+++ b/go.mod
@@ -25,9 +25,9 @@ require (
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.33.0 // indirect
- golang.org/x/net v0.34.0 // indirect
- golang.org/x/sys v0.30.0 // indirect
- golang.org/x/text v0.22.0 // 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 e418881..b3ae996 100644
--- a/go.sum
+++ b/go.sum
@@ -43,16 +43,24 @@ github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA=
github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
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/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 51ace85..a809783 100644
--- a/internal/router/api-v1-blog-search.go
+++ b/internal/router/api-v1-blog-search.go
@@ -48,7 +48,7 @@ func Api_V1_BlogSearch(l map[string]*locale.LocaleConfig, langs []string, b2Clie
c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
return c.Status(fiber.ErrInternalServerError.Code).SendString("failed to generate regex for tags gathering")
}
- decodedQuery, err := url.QueryUnescape(string(encodedQuery))
+ decodedQuery, _ := url.QueryUnescape(string(encodedQuery))
matches := re.FindAllStringSubmatch(decodedQuery, -1)
tags := make([]string, 0, len(matches))
diff --git a/internal/router/client-cache.go b/internal/router/client-cache.go
new file mode 100644
index 0000000..192d2ed
--- /dev/null
+++ b/internal/router/client-cache.go
@@ -0,0 +1,46 @@
+package router
+
+import (
+ "log/slog"
+ "sync"
+
+ "golang.org/x/crypto/argon2"
+)
+
+type ClientCache struct {
+ hashMap map[string][]byte
+ mutexMap map[string]*sync.Mutex
+ salt []byte
+}
+
+var CCache *ClientCache
+
+func NewClientCache(salt []byte) *ClientCache {
+ return &ClientCache{
+ hashMap: make(map[string][]byte),
+ mutexMap: make(map[string]*sync.Mutex),
+ salt: salt,
+ }
+}
+
+func (c *ClientCache) GetHash(id string) []byte {
+ if val, ok := c.hashMap[id]; ok {
+ slog.Debug("gave an old hash", slog.String("hash", string(val)))
+ return val
+ }
+
+ if _, ok := c.mutexMap[id]; !ok {
+ c.mutexMap[id] = &sync.Mutex{}
+ }
+ c.mutexMap[id].Lock()
+ defer c.mutexMap[id].Unlock()
+
+ if val, ok := c.hashMap[id]; ok {
+ slog.Debug("gave a newly generated hash", slog.String("hash", string(val)))
+ return val
+ }
+
+ c.hashMap[id] = argon2.IDKey([]byte(id), c.salt, 1, 64*1024, 4, 32)
+ slog.Debug("generated hash", slog.String("hash", string(c.hashMap[id])))
+ return c.hashMap[id]
+}
diff --git a/internal/router/lang-blog-title.go b/internal/router/lang-blog-title.go
index b7b5d7e..5b80ec8 100644
--- a/internal/router/lang-blog-title.go
+++ b/internal/router/lang-blog-title.go
@@ -22,6 +22,10 @@ func init() {
func Lang_Blog_Title(l map[string]*locale.LocaleConfig, langs []string, b2Client *b2.B2Client, md goldmark.Markdown) func(c *fiber.Ctx) error {
return func(c *fiber.Ctx) error {
+ ip := c.IP()
+ slog.Debug("client entering blog page", slog.String("ip", ip), slog.String("page", c.Path()))
+ go CCache.GetHash(ip)
+
lang := c.Params("lang")
if !slices.Contains(langs, lang) {
c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
diff --git a/internal/router/lang-blog.go b/internal/router/lang-blog.go
index 650c4b1..0cbba94 100644
--- a/internal/router/lang-blog.go
+++ b/internal/router/lang-blog.go
@@ -37,7 +37,7 @@ func Lang_Blog(l map[string]*locale.LocaleConfig, langs []string, b2Client *b2.B
c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
return c.Status(fiber.ErrInternalServerError.Code).SendString("failed to generate regex for tags gathering")
}
- decodedQuery, err := url.QueryUnescape(string(encodedQuery))
+ decodedQuery, _ := url.QueryUnescape(string(encodedQuery))
matches := re.FindAllStringSubmatch(decodedQuery, -1)
queryTags := make([]string, 0, len(matches))
diff --git a/main.go b/main.go
index 566e796..9f95c04 100644
--- a/main.go
+++ b/main.go
@@ -68,7 +68,9 @@ func main() {
localization[lang.Name] = localeCfg
}
- app := fiber.New(fiber.Config{})
+ app := fiber.New(fiber.Config{
+ ProxyHeader: "X-Forwarded-For",
+ })
app.Use(redirect.New(redirect.Config{
Rules: map[string]string{
@@ -78,6 +80,8 @@ func main() {
StatusCode: 301,
}))
+ router.CCache = router.NewClientCache([]byte(cfg.Auth.Salt))
+
app.Get("/", router.Root(cfg.AvailableLanguages))
app.Get("/:lang/map", router.Lang_Map(localization, availableLanguages, b2Client))
app.Get("/:lang/blog", router.Lang_Blog(localization, availableLanguages, b2Client))