summaryrefslogtreecommitdiff
diff refs
from: back
to: back
| flip
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build-and-deploy-prod.yml (renamed from .github/workflows/build-and-deploy.yml)5
-rw-r--r--.github/workflows/build-and-deploy-stage.yml89
-rw-r--r--Dockerfile9
-rw-r--r--config/config.prod.yaml20
-rw-r--r--config/config.stage.yaml (renamed from config/config.yaml)2
-rw-r--r--deploy/inventory.yml6
-rw-r--r--deploy/playbook.yml1
-rw-r--r--main.go62
-rwxr-xr-xsaya-today-webbin19752119 -> 0 bytes
-rw-r--r--static/input.css26
-rw-r--r--views/layouts/general-page.html17
-rw-r--r--views/pages/blog-catalogue.html22
-rw-r--r--views/partials/catalogue-blog-card-tags.html1
-rw-r--r--views/partials/catalogue-blog-cards.html2
14 files changed, 229 insertions, 33 deletions
diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy-prod.yml
index ac5fa1f..53e307e 100644
--- a/.github/workflows/build-and-deploy.yml
+++ b/.github/workflows/build-and-deploy-prod.yml
@@ -1,4 +1,4 @@
-name: Publish Docker image
+name: Docker image building and publishing onto Production
on:
push:
tags:
@@ -37,6 +37,7 @@ jobs:
push: true
tags: |
ghcr.io/sayaandy/saya-today-web:latest
+ ghcr.io/sayaandy/saya-today-web:stable
ghcr.io/sayaandy/saya-today-web:${{ github.ref_name }}
deploy:
@@ -74,4 +75,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_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_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
new file mode 100644
index 0000000..104d9ac
--- /dev/null
+++ b/.github/workflows/build-and-deploy-stage.yml
@@ -0,0 +1,89 @@
+name: Docker image building and publishing onto Stage
+on:
+ push:
+ branches: [stage]
+jobs:
+ build-and-push:
+ if: github.repository == 'SayaAndy/saya-today-web'
+ runs-on: ubuntu-latest
+ environment: Stage
+ permissions:
+ packages: write
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Generate output.css with tailwindcss
+ uses: ZoeyVid/tailwindcss-update@main
+ with:
+ input: static/input.css
+ output: static/output.css
+ params: "--minify"
+
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v3
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+
+ - name: Login to GitHub Container Registry
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.repository_owner }}
+ password: ${{ secrets.GHCR_TOKEN }}
+
+ - name: Set github short sha
+ id: ghss_vars
+ run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
+
+ - name: Build and push
+ uses: docker/build-push-action@v6
+ with:
+ context: .
+ file: ./Dockerfile
+ push: true
+ tags: |
+ ghcr.io/sayaandy/saya-today-web:latest
+ ghcr.io/sayaandy/saya-today-web:commit-${{ steps.ghss_vars.outputs.sha_short }}
+
+ deploy:
+ if: github.repository == 'SayaAndy/saya-today-web'
+ runs-on: ubuntu-latest
+ environment: Stage
+ needs: [build-and-push]
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Set up SSH connection for pl.saya.today
+ run: |
+ mkdir -p ~/.ssh
+ (cat <<EOF
+ ${{ secrets.SVC_GITHUB_PK }}
+ EOF
+ ) > ~/.ssh/id_ed25519
+ chmod 600 ~/.ssh/id_ed25519
+ sed -i 's/\r$//' ~/.ssh/id_ed25519
+ ssh-keyscan -H pl.saya.today >> ~/.ssh/known_hosts
+
+ - name: Test SSH connection
+ run: |
+ ssh -o ConnectTimeout=10 -i ~/.ssh/id_ed25519 svc_github@pl.saya.today "echo 'SSH connection successful'"
+
+ - name: Install Ansible
+ shell: bash
+ run: |
+ sudo apt update
+ sudo apt install -y ansible
+
+ - name: Set github short sha
+ id: ghss_vars
+ run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
+
+ - name: Run Ansible playbook
+ env:
+ 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 }}
diff --git a/Dockerfile b/Dockerfile
index 45d3e8e..cd3e0eb 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -2,20 +2,21 @@ FROM golang:1.24.5-alpine3.22 AS build-stage
WORKDIR /builddir
COPY . .
-RUN go build -o sayana-demo .
+RUN go build -o sayana-web .
FROM alpine:3.22.1 AS runtime-stage
+ENV ENVIRONMENT=""
ENV B2_KEY_ID=""
ENV B2_APPLICATION_KEY=""
WORKDIR /app
-COPY --from=build-stage /builddir/sayana-demo /app/sayana-demo
+COPY --from=build-stage /builddir/sayana-web /app/sayana-web
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.yaml
+COPY --from=build-stage /builddir/config/config*.yaml /app/config/
RUN apk add --no-cache tzdata
-ENTRYPOINT [ "/app/sayana-demo", "-c", "/app/config.yaml" ]
+ENTRYPOINT /app/sayana-web -c /app/config/config.${ENVIRONMENT}.yaml
diff --git a/config/config.prod.yaml b/config/config.prod.yaml
new file mode 100644
index 0000000..2d050bf
--- /dev/null
+++ b/config/config.prod.yaml
@@ -0,0 +1,20 @@
+logLevel: info
+blogPages:
+ storage:
+ type: b2
+ config:
+ bucketName: sayana-pages
+ region: eu-central-003
+ prefix: 'prod-'
+ 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
diff --git a/config/config.yaml b/config/config.stage.yaml
index dac7b9a..a34a33e 100644
--- a/config/config.yaml
+++ b/config/config.stage.yaml
@@ -5,7 +5,7 @@ blogPages:
config:
bucketName: sayana-pages
region: eu-central-003
- prefix: ''
+ prefix: 'stage-'
keyID: '${B2_KEY_ID}'
applicationKey: '${B2_APPLICATION_KEY}'
localePath: ./locale/
diff --git a/deploy/inventory.yml b/deploy/inventory.yml
index 3c1220d..dde952e 100644
--- a/deploy/inventory.yml
+++ b/deploy/inventory.yml
@@ -2,14 +2,16 @@ all:
children:
prod:
hosts:
- pl.saya.today:
+ node_prod:
+ ansible_host: pl.saya.today
saya_today_web_name: sayana-web
saya_today_web_hostname: sayana-web
saya_today_web_ipv4_address: 172.16.0.18
stage:
hosts:
- pl.saya.today:
+ node_stage:
+ ansible_host: pl.saya.today
saya_today_web_name: sayana-demo
saya_today_web_hostname: sayana-demo
saya_today_web_ipv4_address: 172.16.0.17
diff --git a/deploy/playbook.yml b/deploy/playbook.yml
index 773bb61..ca22ac3 100644
--- a/deploy/playbook.yml
+++ b/deploy/playbook.yml
@@ -12,6 +12,7 @@
env:
B2_KEY_ID: "{{ saya_today_web_b2_key_id }}"
B2_APPLICATION_KEY: "{{ saya_today_web_b2_application_key }}"
+ ENVIRONMENT: "{{ saya_today_web_environment }}"
network_mode: caddy
networks:
- name: caddy
diff --git a/main.go b/main.go
index 4dc9619..9e3f9a4 100644
--- a/main.go
+++ b/main.go
@@ -82,7 +82,7 @@ func main() {
{Name: "blog-page", Files: []string{"views/layouts/general-page.html", "views/pages/blog-page.html"}},
{Name: "blog-catalogue", Files: []string{"views/layouts/general-page.html", "views/pages/blog-catalogue.html"}},
{Name: "index", Files: []string{"views/index.html"}},
- {Name: "catalogue-blog-cards", Files: []string{"views/partials/catalogue-blog-cards.html"}},
+ {Name: "catalogue-blog-cards", Files: []string{"views/partials/catalogue-blog-cards.html", "views/partials/catalogue-blog-card-tags.html"}},
})
if err != nil {
slog.Error("fail to initialize template manager", slog.String("error", err.Error()))
@@ -227,15 +227,49 @@ func main() {
return c.Type("html").Send(content)
})
+ app.Get("/api/v1/tz", func(c *fiber.Ctx) error {
+ timestampString := c.Query("timestamp")
+ tz := c.Query("tz")
+
+ loc, err := time.LoadLocation(tz)
+ if err != nil {
+ loc = time.UTC
+ slog.Warn("unable to parse a client timezone, defaulting to UTC", slog.String("error", err.Error()), slog.String("tz", tz))
+ }
+
+ var format string
+ var timestamp time.Time
+ for _, format = range []string{"2006-01-02 15:04:05 -07:00", time.RFC3339} {
+ if timestamp, err = time.Parse(format, timestampString); err == nil {
+ break
+ }
+ }
+
+ c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
+
+ if err != nil {
+ return c.Status(fiber.StatusBadRequest).SendString("invalid timestamp format")
+ }
+
+ return c.Status(fiber.StatusOK).SendString(timestamp.In(loc).Format(format))
+ })
+
app.Get("/api/v1/blog-search", func(c *fiber.Ctx) error {
sort := c.Query("sort")
lang := c.Query("lang")
+ tz := c.Query("tz")
if !slices.Contains(availableLanguages, lang) {
c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
return c.Status(fiber.ErrNotFound.Code).SendString(fmt.Sprintf("server does not support '%s' language", lang))
}
+ loc, err := time.LoadLocation(tz)
+ if err != nil {
+ loc = time.UTC
+ slog.Warn("unable to parse a client timezone, defaulting to UTC", slog.String("error", err.Error()), slog.String("tz", tz))
+ }
+
pages, err := b2Client.Scan(lang + "/")
if err != nil {
c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
@@ -257,42 +291,42 @@ func main() {
tags = append(tags, string(match[1]))
}
- pageMeta := make([]map[string]string, 0, len(pages))
+ pageMeta := make([]fiber.Map, 0, len(pages))
for _, page := range pages {
for _, tag := range page.Metadata.Tags {
if len(tags) == 0 || slices.Contains(tags, tag) {
- pageMeta = append(pageMeta, map[string]string{
+ pageMeta = append(pageMeta, fiber.Map{
"Link": page.Link,
"ArticleLink": "/" + lang + "/blog/" + page.FileName,
"Title": page.Metadata.Title,
- "PublishedTime": page.Metadata.PublishedTime.Format("2006-01-02 15:04:05 -07:00"),
+ "PublishedTime": page.Metadata.PublishedTime.In(loc).Format("2006-01-02 15:04:05 -07:00"),
"ActionDate": page.Metadata.ActionDate,
"ShortDescription": page.Metadata.ShortDescription,
"Thumbnail": page.Metadata.Thumbnail,
- "Tags": strings.Join(page.Metadata.Tags, ", "),
+ "Tags": page.Metadata.Tags,
})
break
}
}
}
- slices.SortFunc(pageMeta, func(a, b map[string]string) int {
+ slices.SortFunc(pageMeta, func(a, b fiber.Map) int {
switch sort {
case "titleAsc":
- return strings.Compare(a["Title"], b["Title"])
+ return strings.Compare(a["Title"].(string), b["Title"].(string))
case "titleDesc":
- return strings.Compare(b["Title"], a["Title"])
+ return strings.Compare(b["Title"].(string), a["Title"].(string))
case "actionDateAsc":
- return strings.Compare(a["ActionDate"], b["ActionDate"])
+ return strings.Compare(a["ActionDate"].(string), b["ActionDate"].(string))
case "actionDateDesc":
- return strings.Compare(b["ActionDate"], a["ActionDate"])
+ return strings.Compare(b["ActionDate"].(string), a["ActionDate"].(string))
case "publicationDateAsc":
- publishedTimeA, _ := time.Parse("2006-01-02 15:04:05 -07:00", a["PublishedTime"])
- publishedTimeB, _ := time.Parse("2006-01-02 15:04:05 -07:00", b["PublishedTime"])
+ publishedTimeA, _ := time.Parse("2006-01-02 15:04:05 -07:00", a["PublishedTime"].(string))
+ publishedTimeB, _ := time.Parse("2006-01-02 15:04:05 -07:00", b["PublishedTime"].(string))
return publishedTimeA.Compare(publishedTimeB)
case "publicationDateDesc":
- publishedTimeA, _ := time.Parse("2006-01-02 15:04:05 -07:00", a["PublishedTime"])
- publishedTimeB, _ := time.Parse("2006-01-02 15:04:05 -07:00", b["PublishedTime"])
+ publishedTimeA, _ := time.Parse("2006-01-02 15:04:05 -07:00", a["PublishedTime"].(string))
+ publishedTimeB, _ := time.Parse("2006-01-02 15:04:05 -07:00", b["PublishedTime"].(string))
return publishedTimeB.Compare(publishedTimeA)
}
return 0
diff --git a/saya-today-web b/saya-today-web
deleted file mode 100755
index b6522e8..0000000
--- a/saya-today-web
+++ /dev/null
Binary files differ
diff --git a/static/input.css b/static/input.css
index cfc7391..131faa9 100644
--- a/static/input.css
+++ b/static/input.css
@@ -25,6 +25,9 @@
--sidebar-text-color: var(--background-dark-color);
--sidebar-stroke-color: var(--main-dark-color);
--secondary-color: var(--main-light-color);
+ --leaflet-text-color: var(--main-dark-color);
+ --leaflet-link-color: var(--main-medium-color);
+ --leaflet-link-hover-color: var(--main-light-color);
--interlocked-hexagons-background: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%22%20height%3D%22100%22%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22%23ffffdb%22%2F%3E%3Cpath%20fill%3D%22%23777e63%22%20d%3D%22m33.64%200-.17.25h-33L4.3%206h33.07l4-6zm8.33%200-4.33%206.5h-33l3.83%205.75h33.06L49.7%200zm8.34%200-8.5%2012.75h-33l3.83%205.75H45.7L58.03%200zm8.33%200L45.97%2019h-33l3.83%205.75h33.06L66.36%200zm-8.5%2025.25L33.63%2050l3.86%205.8L54.03%2031h33l-3.83-5.75zm4.16%206.25L37.8%2056.25l3.87%205.8%2016.53-24.8h33l-3.84-5.75zm4.17%206.25L41.97%2062.5l3.86%205.8%2016.54-24.8h33l-3.84-5.75zM62.63%2044l-16.5%2024.75%203.87%205.8%2016.53-24.8h33L95.7%2044zM100%2050.45l-3.87%205.8%203.87%205.81v-11.6zM0%2050.46v11.6L12.5%2080.8l3.86-5.8zm95.83%206.25-3.87%205.8L100%2074.55v-11.6zm-4.17%206.24-3.86%205.8%2012.2%2018.3v-11.6zM0%2062.96v11.6l8.33%2012.49%203.86-5.8zm87.5%206.24L83.63%2075%20100%2099.54V87.96zM50%2075.45%2033.64%20100h7.72l12.5-18.75zm-50%200v11.6l4.16%206.24%203.87-5.8zm54.17%206.25L41.97%20100h7.73l8.33-12.5zm4.17%206.25L50.3%20100h7.73l4.17-6.25zM0%2087.95v11.59l3.86-5.8zm62.5%206.25-3.87%205.8h7.73z%22%2F%3E%3Cpath%20fill%3D%22%23a0a182%22%20d%3D%22M66.8.25%2062.97%206h33l4.03%206.05V.45l-.14-.2zM0%20.45v11.61L12.5%2030.8l3.86-5.8zM62.63%206.5l-3.83%205.75h33l8.2%2012.3v-11.6L95.7%206.5zm-4.16%206.25-3.84%205.75h33L100%2037.06v-11.6l-8.47-12.71zM0%2012.96v11.6l8.33%2012.49%203.86-5.8zM54.3%2019l-3.84%205.75h33L100%2049.55l.01-.01V37.96L87.36%2019zm-4.77%206.25H41.8L25.3%2050l16.66%2025-16.5%2024.75h7.74L49.7%2075%2033.03%2050zm-8.33%200h-7.74L16.96%2050l16.67%2025-16.5%2024.75h7.73L41.36%2075%2024.7%2050Zm-8.34%200h-7.73L8.63%2050%2025.3%2075%208.8%2099.75h7.73L33.03%2075%2016.36%2050zm-16.07%200L.3%2050l16.67%2025L.46%2099.75H8.2L24.7%2075%208.02%2050l16.5-24.75zM0%2025.46v11.6l4.16%206.24%203.87-5.8zm0%2012.5v11.58l3.86-5.79zm66.8%2012.29L50.3%2074.99l3.87%205.81%204.16%206.25%204.17%206.25%204.3%206.45h7.73L58.03%2075l16.5-24.75zm16.06%200h-7.73L58.63%2075l16.5%2024.75h7.73L66.36%2075zm.6%200L66.96%2075l16.5%2024.75h7.74L74.7%2075l16.5-24.75zm16.07%200H91.8L75.3%2075l16.5%2024.75h7.73L83.03%2075z%22%2F%3E%3C%2Fsvg%3E");
--interlocked-hexagons-background-repeat: repeat;
--interlocked-hexagons-background-size: 10vh;
@@ -43,6 +46,9 @@
--sidebar-text-color: var(--main-dark-color);
--sidebar-stroke-color: var(--background-dark-color);
--secondary-color: var(--main-medium-color);
+ --leaflet-text-color: var(--main-dark-color);
+ --leaflet-link-color: var(--main-medium-color);
+ --leaflet-link-hover-color: var(--main-light-color);
--interlocked-hexagons-background: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%22%20height%3D%22100%22%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22%23f5ede1%22%2F%3E%3Cpath%20fill%3D%22%239ab663%22%20d%3D%22m33.64%200-.17.25h-33L4.3%206h33.07l4-6zm8.33%200-4.33%206.5h-33l3.83%205.75h33.06L49.7%200zm8.34%200-8.5%2012.75h-33l3.83%205.75H45.7L58.03%200zm8.33%200L45.97%2019h-33l3.83%205.75h33.06L66.36%200zm-8.5%2025.25L33.63%2050l3.86%205.8L54.03%2031h33l-3.83-5.75zm4.16%206.25L37.8%2056.25l3.87%205.8%2016.53-24.8h33l-3.84-5.75zm4.17%206.25L41.97%2062.5l3.86%205.8%2016.54-24.8h33l-3.84-5.75zM62.63%2044l-16.5%2024.75%203.87%205.8%2016.53-24.8h33L95.7%2044zM100%2050.45l-3.87%205.8%203.87%205.81v-11.6zM0%2050.46v11.6L12.5%2080.8l3.86-5.8zm95.83%206.25-3.87%205.8L100%2074.55v-11.6zm-4.17%206.24-3.86%205.8%2012.2%2018.3v-11.6zM0%2062.96v11.6l8.33%2012.49%203.86-5.8zm87.5%206.24L83.63%2075%20100%2099.54V87.96zM50%2075.45%2033.64%20100h7.72l12.5-18.75zm-50%200v11.6l4.16%206.24%203.87-5.8zm54.17%206.25L41.97%20100h7.73l8.33-12.5zm4.17%206.25L50.3%20100h7.73l4.17-6.25zM0%2087.95v11.59l3.86-5.8zm62.5%206.25-3.87%205.8h7.73z%22%2F%3E%3Cpath%20fill%3D%22%23bbd095%22%20d%3D%22M66.8.25%2062.97%206h33l4.03%206.05V.45l-.14-.2zM0%20.45v11.61L12.5%2030.8l3.86-5.8zM62.63%206.5l-3.83%205.75h33l8.2%2012.3v-11.6L95.7%206.5zm-4.16%206.25-3.84%205.75h33L100%2037.06v-11.6l-8.47-12.71zM0%2012.96v11.6l8.33%2012.49%203.86-5.8zM54.3%2019l-3.84%205.75h33L100%2049.55l.01-.01V37.96L87.36%2019zm-4.77%206.25H41.8L25.3%2050l16.66%2025-16.5%2024.75h7.74L49.7%2075%2033.03%2050zm-8.33%200h-7.74L16.96%2050l16.67%2025-16.5%2024.75h7.73L41.36%2075%2024.7%2050Zm-8.34%200h-7.73L8.63%2050%2025.3%2075%208.8%2099.75h7.73L33.03%2075%2016.36%2050zm-16.07%200L.3%2050l16.67%2025L.46%2099.75H8.2L24.7%2075%208.02%2050l16.5-24.75zM0%2025.46v11.6l4.16%206.24%203.87-5.8zm0%2012.5v11.58l3.86-5.79zm66.8%2012.29L50.3%2074.99l3.87%205.81%204.16%206.25%204.17%206.25%204.3%206.45h7.73L58.03%2075l16.5-24.75zm16.06%200h-7.73L58.63%2075l16.5%2024.75h7.73L66.36%2075zm.6%200L66.96%2075l16.5%2024.75h7.74L74.7%2075l16.5-24.75zm16.07%200H91.8L75.3%2075l16.5%2024.75h7.73L83.03%2075z%22%2F%3E%3C%2Fsvg%3E");
--interlocked-hexagons-background-repeat: repeat;
--interlocked-hexagons-background-size: 10vh;
@@ -61,6 +67,9 @@
--sidebar-text-color: var(--main-dark-color);
--sidebar-stroke-color: var(--background-light-color);
--secondary-color: var(--main-light-color);
+ --leaflet-text-color: var(--background-dark-color);
+ --leaflet-link-color: var(--background-dark-color);
+ --leaflet-link-hover-color: var(--background-light-color);
--interlocked-hexagons-background: radial-gradient(33.5% 25% at 100% 82%, rgba(190, 227, 248, 0.2) 0%, rgba(197, 179, 246, 0.13) 33.33%, rgba(255, 0, 0, 0) 100%), radial-gradient(54% 25% at 55% 100%, rgba(190, 227, 248, 0.2) 0%, rgba(197, 179, 246, 0.13) 33.33%, rgba(255, 0, 0, 0) 100%), radial-gradient(37% 19.5% at 20% 100%, rgba(176, 220, 248, 0.3) 0%, rgba(197, 172, 247, 0.2) 33.33%, rgba(255, 0, 0, 0) 100%), radial-gradient(63% 34.5% at 66% 77%, rgba(190, 227, 248, 0.15) 0%, rgba(197, 179, 246, 0.1) 33.33%, rgba(255, 0, 0, 0) 100%), linear-gradient(0deg, #2f5883 0%, #2f4e7a 4.75%, #2e4472 9.5%, #2a3065 19%, #272e57 26.5%, #242b4b 34%, #1f2437 49%, #1c2030 57.75%, #1a1b29 66.5%, #13131b 84%);
--interlocked-hexagons-background-repeat: no-repeat;
--interlocked-hexagons-background-size: 100vw 100vh;
@@ -79,6 +88,9 @@
--sidebar-text-color: var(--main-dark-color);
--sidebar-stroke-color: var(--background-light-color);
--secondary-color: var(--main-light-color);
+ --leaflet-text-color: var(--background-dark-color);
+ --leaflet-link-color: var(--background-dark-color);
+ --leaflet-link-hover-color: var(--background-light-color);
--interlocked-hexagons-background: url('https://f003.backblazeb2.com/file/sayana-static/themes/ram/ram-background.webp');
--interlocked-hexagons-background-repeat: repeat;
--interlocked-hexagons-background-size: auto;
@@ -97,6 +109,20 @@ a:hover {
color: var(--main-light-color) !important;
}
+#location-map {
+ color: var(--leaflet-text-color) !important;
+}
+
+#location-map a {
+ color: var(--leaflet-link-color) !important;
+ text-decoration: underline;
+}
+
+#location-map a:hover {
+ color: var(--leaflet-link-hover-color) !important;
+ text-decoration: underline;
+}
+
.bg-interlocked-hexagons {
background-image: var(--interlocked-hexagons-background);
background-repeat: var(--interlocked-hexagons-background-repeat);
diff --git a/views/layouts/general-page.html b/views/layouts/general-page.html
index eab7cd1..46ef2e6 100644
--- a/views/layouts/general-page.html
+++ b/views/layouts/general-page.html
@@ -12,7 +12,7 @@
<link href="/output.css" rel="stylesheet">
</head>
-<body data-theme="night" class="font-spectral text-main-dark overflow-hidden bg-interlocked-hexagons h-screen flex flex-row ar-lt-0.8:flex-col">
+<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 switchTheme(theme) {
@@ -29,10 +29,14 @@
document.body.setAttribute('data-theme', theme);
}
- const savedTheme = localStorage.getItem('theme') || 'night';
+ const savedTheme = localStorage.getItem('theme') || 'ram';
switchTheme(savedTheme);
</script>
+ <script>
+ const clientTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
+ </script>
+
<div id="sidebar" class="bg-sidebar flex sticky flex-col ar-gt-0.8:items-center shadow-2xl z-20 w-[5vw] ar-lt-0.8:w-[90vw] ar-lt-0.4:w-[100vw] h-screen ar-lt-0.8:h-[8vh] ar-lt-0.8:ml-[5vw] ar-lt-0.4:ml-0">
<div class="logo-custom ar-lt-0.8:hidden text-sidebar font-patua font-extrabold text-center relative z-20">
saya.today
@@ -49,13 +53,12 @@
<div class="pl-[0.5vw] bg-background-dark border-r-[0.4vmin] border-dashed border-main-dark"></div>
<div class="ar-gt-0.8:min-w-fit ar-gt-0.8:max-w-[100vh] flex flex-col bg-background-dark relative pt-[1.6vmin] ml-[1.6vmin] pr-[1.6vmin]">
- <div class="flex flex-row mb-[0.4vmin]">
+ <div {{ if .PublishedDate }} hx-get="/api/v1/tz" hx-vals='js:{timestamp: "{{ .PublishedDate }}", tz: clientTimeZone}' hx-target="#published-date" hx-swap="innerHTML" hx-trigger="load" {{ end }}
+ class="flex flex-row mb-[0.4vmin]">
<i onclick="location.pathname += location.pathname.endsWith('/') ? '../' : '/../';" class="fas fa-circle-left hover:bg-main-dark hover:bg-opacity-10 cursor-pointer transition-colors duration-300 rounded-md text-[2vmax] mt-auto mb-auto px-[0.8vmin]"></i>
<p class="text-[2vmax] font-spectral italic font-extrabold select-none mt-auto pl-[0.8vmin] pr-[1.2vmin]">//</p>
<p class="text-[2vmax] font-spectral italic text-left mt-auto">{{ .Title }}</p>
- {{- if .PublishedDate }}
- <p class="grow text-[1.5vmax] font-spectral text-secondary text-right mt-auto">{{ .PublishedDate }}</p>
- {{- end }}
+ <p id="published-date" class="grow text-[1.5vmax] font-spectral text-secondary text-right mt-auto">{{ .PublishedDate }}</p>
</div>
<hr class="border-t-4 border-dotted border-main-dark mb-[0.8vmin]">
@@ -76,6 +79,8 @@
<div class="pr-[0.5vw] bg-background-dark shadow-2xl border-l-[0.4vmin] border-dotted border-main-dark"></div>
</div>
+ <script src="https://f003.backblazeb2.com/file/sayana-static/libs/htmx/2.0.6/htmx.min.js"></script>
+
{{ block "footer" . }}{{ end }}
</body>
diff --git a/views/pages/blog-catalogue.html b/views/pages/blog-catalogue.html
index 6c0f629..2b248c3 100644
--- a/views/pages/blog-catalogue.html
+++ b/views/pages/blog-catalogue.html
@@ -49,7 +49,7 @@
<div class="w-[0.5vh] border-r-[0.3vh] border-dotted border-main-dark bg-background-dark"></div>
- <div hx-get="/api/v1/blog-search" hx-vals='{"lang": "{{ .Lang }}"}' hx-target="this" hx-swap="innerHTML" hx-trigger="load" hx-include=".tags-list"
+ <div hx-get="/api/v1/blog-search" hx-vals='js:{lang: "{{ .Lang }}", tz: clientTimeZone}' hx-target="this" hx-swap="innerHTML" hx-trigger="load" hx-include=".tags-list"
class="blog-cards grow flex flex-col overflow-y-auto bg-background-light inset-shadow p-[2.4vmin]">
Loading...
</div>
@@ -57,8 +57,6 @@
{{ end }}
{{ define "footer" }}
-<script src="https://f003.backblazeb2.com/file/sayana-static/libs/htmx/2.0.6/htmx.min.js"></script>
-
<script>
function selectAll() {
const tagsContainers = document.querySelectorAll('form.tags-list');
@@ -120,6 +118,24 @@
window.history.pushState({}, '', url.toString());
}
+ function pickedTagSetHrefParams(tag) {
+ const url = new URL(window.location.href);
+ url.searchParams.delete('tags[]');
+ url.searchParams.set('tags[]', tag);
+
+ const tagsContainers = document.querySelectorAll('form.tags-list');
+ tagsContainers.forEach(tagsContainer => {
+ const checkboxes = tagsContainer.querySelectorAll('input[type="checkbox"]');
+ checkboxes.forEach(checkbox => {
+ checkbox.checked = false;
+ });
+ chosenCheckbox = tagsContainer.querySelector(`#tag${tag}Checkbox`);
+ chosenCheckbox.checked = true;
+ });
+
+ window.history.pushState({}, '', url.toString());
+ }
+
document.addEventListener('DOMContentLoaded', contextAll);
</script>
diff --git a/views/partials/catalogue-blog-card-tags.html b/views/partials/catalogue-blog-card-tags.html
new file mode 100644
index 0000000..374314a
--- /dev/null
+++ b/views/partials/catalogue-blog-card-tags.html
@@ -0,0 +1 @@
+{{ range $i, $tag := .Tags }}{{ if ne $i 0 }}, {{ end }}<a hx-trigger="click" hx-get="/api/v1/blog-search" hx-target=".blog-cards" hx-swap="innerHTML" hx-trigger="load" hx-include=".tags-list" class="hx-anchor cursor-pointer" onclick="pickedTagSetHrefParams({{ $tag }})">{{ $tag }}</a>{{ end }} \ No newline at end of file
diff --git a/views/partials/catalogue-blog-cards.html b/views/partials/catalogue-blog-cards.html
index a9e4010..406ef9b 100644
--- a/views/partials/catalogue-blog-cards.html
+++ b/views/partials/catalogue-blog-cards.html
@@ -12,7 +12,7 @@
<p class="flex-3/12 grow-0 text-[1vmax] italic text-secondary text-right">{{ .PublishedTime }}</p>
</div>
<p class="text-[1vmax] text-main-dark">{{ .ShortDescription }}</p>
- <p class="text-[1vmax] text-secondary">{{ .L.TagsLabel }} {{ .Tags }}</p>
+ <p class="text-[1vmax] text-secondary">{{ $.L.TagsLabel }} {{ template "catalogue-blog-card-tags.html" . }}</p>
</div>
</div>
{{- end }} \ No newline at end of file