Compare commits

...

81 Commits

Author SHA1 Message Date
c38fd7f435 Merge pull request 'staging' (#10) from staging into main
All checks were successful
release-tag / release-image (push) Successful in 2m21s
Reviewed-on: #10
2025-05-11 16:15:26 +00:00
7ef6b551ac Bugfix Einlesen der Artikel vor Erstellen der Ressourcen
All checks were successful
release-tag / release-image (push) Successful in 2m24s
2025-05-11 17:10:56 +02:00
dce90ca88b bugfix
All checks were successful
release-tag / release-image (push) Successful in 2m20s
2025-05-11 16:56:08 +02:00
7493ca278e fix
All checks were successful
release-tag / release-image (push) Successful in 2m21s
2025-05-11 16:39:14 +02:00
445db0ec10 fix
All checks were successful
release-tag / release-image (push) Successful in 2m21s
2025-05-11 16:29:16 +02:00
87493fe40c removeall fix
All checks were successful
release-tag / release-image (push) Successful in 2m17s
2025-05-11 16:22:48 +02:00
ca22dd8596 Test für Git AutoClone
All checks were successful
release-tag / release-image (push) Successful in 2m14s
2025-05-11 16:16:52 +02:00
e4d47b46e4 Merge pull request 'staging' (#9) from staging into main
All checks were successful
release-tag / release-image (push) Successful in 2m11s
Reviewed-on: #9
2025-05-11 10:18:49 +00:00
4c41bb8838 Anpassung Datenschutzerklärung + ReleaseCandidate
All checks were successful
release-tag / release-image (push) Successful in 2m8s
2025-05-11 12:02:29 +02:00
98de9b1b98 Potentieller Persistenz-Fix
All checks were successful
release-tag / release-image (push) Successful in 2m10s
2025-05-11 11:49:10 +02:00
621db424f0 Fix für Boot-Loop
All checks were successful
release-tag / release-image (push) Successful in 2m12s
2025-05-11 11:34:48 +02:00
7e21b10a6c Persistenz implementiert + Anpassungen
All checks were successful
release-tag / release-image (push) Successful in 2m7s
2025-05-11 11:30:52 +02:00
8cd66c9f59 Anpassungen Counter-Design
All checks were successful
release-tag / release-image (push) Successful in 2m5s
2025-05-11 11:17:45 +02:00
96894516a7 Fix
All checks were successful
release-tag / release-image (push) Successful in 2m9s
2025-05-11 11:11:50 +02:00
2e31bfdc77 Card-Fix für Counter und Berechnungs-Fix
All checks were successful
release-tag / release-image (push) Successful in 2m8s
2025-05-11 11:07:25 +02:00
08ef9c7132 Implementierung eines anonymen Counters - Nicht persistent
All checks were successful
release-tag / release-image (push) Successful in 2m9s
2025-05-11 10:57:36 +02:00
847d860144 Patch
All checks were successful
release-tag / release-image (push) Successful in 2m8s
2025-05-10 22:26:03 +02:00
8b7f1127f8 Fix
All checks were successful
release-tag / release-image (push) Successful in 2m14s
2025-05-10 22:19:11 +02:00
278d3aa48f Invictus 2955 - Das Jahr der Idris-P?
All checks were successful
release-tag / release-image (push) Successful in 2m11s
2025-05-10 22:10:09 +02:00
9dca6a57df Bugfix
All checks were successful
release-tag / release-image (push) Successful in 2m11s
2025-05-10 19:17:30 +02:00
16b5ddfb7b Merge pull request 'Anpassung für dynamische Content-Gits' (#8) from staging into main
Some checks failed
release-tag / release-image (push) Failing after 45s
Reviewed-on: #8
2025-05-10 17:14:28 +00:00
12ce629b31 Anpassung für dynamische Content-Gits
Some checks failed
release-tag / release-image (push) Failing after 43s
2025-05-10 19:13:29 +02:00
5f089d727e Merge pull request 'staging' (#7) from staging into main
All checks were successful
release-tag / release-image (push) Successful in 1m52s
Reviewed-on: #7
2025-05-10 13:09:28 +00:00
d7726b57e4 Windows Server 2025 Domänen-Controller legt Netzwerkprofil auf Öffentlich fest
All checks were successful
release-tag / release-image (push) Successful in 1m51s
2025-05-10 14:54:13 +02:00
c25c60ab27 README.md aktualisiert
All checks were successful
release-tag / release-image (push) Successful in 1m51s
2025-05-08 06:33:34 +00:00
7ee0c9e9f7 TLS-Zertifikat mit SHA3 auf Windows Server 2016 einspielen
All checks were successful
release-tag / release-image (push) Successful in 1m54s
2025-05-08 06:45:56 +02:00
0d98bd064e Merge pull request 'staging' (#6) from staging into main
All checks were successful
release-tag / release-image (push) Successful in 1m49s
Reviewed-on: #6
2025-05-07 20:28:52 +00:00
aba08de902 Source-Patch
All checks were successful
release-tag / release-image (push) Successful in 1m51s
2025-05-07 22:28:23 +02:00
4764d1536c Content-Patch
All checks were successful
release-tag / release-image (push) Successful in 1m48s
2025-05-07 22:18:02 +02:00
3357061c79 Warum personenbezogene Daten nie in die Betreffzeile einer E‑Mail gehören
All checks were successful
release-tag / release-image (push) Successful in 1m50s
2025-05-07 21:27:53 +02:00
2a63985564 Merge pull request 'Update Datenschutz und Impressum Telefonnummer' (#5) from staging into main
All checks were successful
release-tag / release-image (push) Successful in 1m50s
Reviewed-on: #5
2025-05-07 16:18:58 +00:00
a83e360317 Update Datenschutz und Impressum Telefonnummer
All checks were successful
release-tag / release-image (push) Successful in 1m55s
2025-05-07 18:07:49 +02:00
33c0f9d954 Merge pull request 'staging' (#4) from staging into main
All checks were successful
release-tag / release-image (push) Successful in 1m56s
Reviewed-on: #4
2025-05-06 22:00:17 +00:00
7b598a04be Patch
All checks were successful
release-tag / release-image (push) Successful in 1m52s
2025-05-06 23:34:50 +02:00
8554e13ebf Patch KI-Nutzung und Umsetzung AI-Act
All checks were successful
release-tag / release-image (push) Successful in 1m49s
2025-05-06 23:25:28 +02:00
f6806c5ad0 minor fix
All checks were successful
release-tag / release-image (push) Successful in 1m56s
2025-05-06 22:17:37 +02:00
c0f985982e Anpassung Datenschutz und Nav-Links
All checks were successful
release-tag / release-image (push) Successful in 1m53s
2025-05-06 22:10:58 +02:00
08cb7b689d Merge pull request 'staging' (#3) from staging into main
All checks were successful
release-tag / release-image (push) Successful in 1m50s
Reviewed-on: #3
2025-05-06 18:03:54 +00:00
17424d399d Warum meldet der Client, dass die Default Domain Policy nicht gelesen werden kann
All checks were successful
release-tag / release-image (push) Successful in 1m52s
2025-05-06 19:58:20 +02:00
2e5f9b6fa5 Update Entfernen der Footer-Bilder auf grund von Design 2025-05-06 16:54:42 +02:00
cd869b610e Anpassung base html Klasse footer
All checks were successful
release-tag / release-image (push) Successful in 1m55s
2025-05-06 15:58:43 +02:00
7873cbe132 Anpassungen
All checks were successful
release-tag / release-image (push) Successful in 1m55s
2025-05-06 15:54:10 +02:00
32d3fc33d6 Merge pull request 'Contentpatch 20250506' (#2) from staging into main
All checks were successful
release-tag / release-image (push) Successful in 1m58s
Reviewed-on: #2
2025-05-06 12:52:51 +00:00
8d77eb94e8 Contentpatch 20250506
All checks were successful
release-tag / release-image (push) Successful in 1m51s
2025-05-06 14:42:51 +02:00
7afa89a66b Merge pull request 'staging' (#1) from staging into main
All checks were successful
release-tag / release-image (push) Successful in 1m55s
Reviewed-on: #1
2025-05-06 10:20:47 +00:00
0fb3924382 Logo entfernt + Links design angepasst
All checks were successful
release-tag / release-image (push) Successful in 1m56s
2025-05-06 12:16:18 +02:00
b2dc65e024 Header für Logo angepast
All checks were successful
release-tag / release-image (push) Successful in 1m51s
2025-05-06 11:27:29 +02:00
1b40a248d2 Base HTML auf Logo + Alt-Text angepasst
All checks were successful
release-tag / release-image (push) Successful in 1m52s
2025-05-06 11:20:55 +02:00
d912799a9e Logo Anpassungen
All checks were successful
release-tag / release-image (push) Successful in 1m51s
2025-05-06 11:16:53 +02:00
477539928c Anpassung Logo + Content
All checks were successful
release-tag / release-image (push) Successful in 1m50s
2025-05-06 11:11:28 +02:00
d4840c936a added staging workflow
All checks were successful
release-tag / release-image (push) Successful in 1m49s
2025-05-06 10:39:27 +02:00
f534182c20 Open Source als Innovationsmotor: Warum Unternehmen auf offene Software setzen
All checks were successful
release-tag / release-image (push) Successful in 1m49s
2025-05-05 22:58:04 +02:00
16d2548976 Neue Blogserie: Open Source im Unternehmen
All checks were successful
release-tag / release-image (push) Successful in 1m52s
2025-05-05 22:37:31 +02:00
6017bd2cd3 CSS für BITV angepasst
All checks were successful
release-tag / release-image (push) Successful in 1m58s
2025-05-05 21:46:31 +02:00
e266c9c78f Alte Ressourcen entfernt, Favicon gesetzt
All checks were successful
release-tag / release-image (push) Successful in 1m49s
2025-05-05 21:37:29 +02:00
229f78a7ee Navigation "zurück" angepasst + Webp-Ressourcen angepasst 2025-05-05 21:34:02 +02:00
c88cf623f0 Meta Description Bugfix #2
All checks were successful
release-tag / release-image (push) Successful in 2m6s
2025-05-05 21:21:57 +02:00
b1536ad724 Meta Description Bugfix #1
All checks were successful
release-tag / release-image (push) Successful in 2m9s
2025-05-05 21:08:06 +02:00
38fa612a18 Test Meta Description
All checks were successful
release-tag / release-image (push) Successful in 2m22s
2025-05-05 20:58:17 +02:00
10172d1fdc optimized for webp
All checks were successful
release-tag / release-image (push) Successful in 2m7s
2025-05-05 17:09:53 +02:00
eeb2709330 README.md aktualisiert
All checks were successful
release-tag / release-image (push) Successful in 2m13s
2025-05-05 11:48:11 +00:00
0773b2a8e1 README.md aktualisiert
All checks were successful
release-tag / release-image (push) Successful in 2m10s
2025-05-05 09:33:21 +00:00
fb26c8af92 README.md aktualisiert
All checks were successful
release-tag / release-image (push) Successful in 2m11s
2025-05-05 07:23:49 +00:00
c54b4e652a Neue Sortierung und Welcome + Datenschutz
All checks were successful
release-tag / release-image (push) Successful in 2m11s
2025-05-04 22:58:46 +02:00
73c068c84f MySQL auf Linux installieren + Footer
All checks were successful
release-tag / release-image (push) Successful in 2m10s
2025-05-04 22:36:11 +02:00
962d3fdd02 PHPMyAdmin mit Serverauswahl im Homelab mittels Docker bereitstellen
All checks were successful
release-tag / release-image (push) Successful in 2m5s
Windows Fehler 0x80072F8F - Installation optionaler Features schlägt fehl
2025-05-04 22:15:32 +02:00
a179c7e51a In Ubuntu den Port 53 - DNS - selber nutzen
All checks were successful
release-tag / release-image (push) Successful in 2m8s
2025-05-04 21:53:51 +02:00
8ca50097fd CSS für Code-Element aktualisiert
All checks were successful
release-tag / release-image (push) Successful in 2m4s
2025-05-04 21:44:15 +02:00
f746cea41a CSS für Code-Elemet aktualisiert
All checks were successful
release-tag / release-image (push) Successful in 2m1s
2025-05-04 21:06:18 +02:00
7e85585d6c Ein eigenes Docker-Image erstellen - so geht's
All checks were successful
release-tag / release-image (push) Successful in 2m11s
2025-05-04 21:00:06 +02:00
89f7d21483 Der eigene DNS-Server im Homelab
All checks were successful
release-tag / release-image (push) Successful in 2m14s
2025-05-04 20:36:00 +02:00
b5a0c1d64e Content-Patch
All checks were successful
release-tag / release-image (push) Successful in 2m3s
2025-05-04 20:19:43 +02:00
e4b060b753 Fonts auf lokal geändert
All checks were successful
release-tag / release-image (push) Successful in 1m59s
2025-05-04 20:01:26 +02:00
2cdc782395 Content-Patch
All checks were successful
release-tag / release-image (push) Successful in 2m5s
2025-05-04 18:45:46 +02:00
b83eff82a0 Testfix for loop restart #3
All checks were successful
release-tag / release-image (push) Successful in 1m58s
2025-05-04 18:39:54 +02:00
1d09537ff0 Testfix for loop restart #2
All checks were successful
release-tag / release-image (push) Successful in 1m50s
2025-05-04 18:19:20 +02:00
efac316b4c Testfix for loop restart #1
All checks were successful
release-tag / release-image (push) Successful in 1m54s
2025-05-04 18:13:27 +02:00
128aa11273 Fix GoVersion to 1.23.1
All checks were successful
release-tag / release-image (push) Successful in 2m3s
2025-05-04 18:04:14 +02:00
dfe7d03445 Fix Dockerfile src
Some checks failed
release-tag / release-image (push) Failing after 1m18s
2025-05-04 18:01:07 +02:00
b0eea7ae62 Workflow hinzugefügt
Some checks failed
release-tag / release-image (push) Failing after 47s
2025-05-04 17:58:21 +02:00
8f7a0b359c Anpassung auf dynamisches GIT 2025-05-04 17:56:46 +02:00
22 changed files with 610 additions and 83 deletions

View File

@@ -0,0 +1,67 @@
name: release-tag
on:
push:
branches:
- 'main'
jobs:
release-image:
runs-on: ubuntu-fast
env:
DOCKER_ORG: ${{ vars.DOCKER_ORG }}
DOCKER_LATEST: latest
RUNNER_TOOL_CACHE: /toolcache
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker BuildX
uses: docker/setup-buildx-action@v2
with: # replace it with your local IP
config-inline: |
[registry."${{ vars.DOCKER_REGISTRY }}"]
http = true
insecure = true
- name: Login to DockerHub
uses: docker/login-action@v2
with:
registry: ${{ vars.DOCKER_REGISTRY }} # replace it with your local IP
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Get Meta
id: meta
run: |
echo REPO_NAME=$(echo ${GITHUB_REPOSITORY} | awk -F"/" '{print $2}') >> $GITHUB_OUTPUT
echo REPO_VERSION=$(git describe --tags --always | sed 's/^v//') >> $GITHUB_OUTPUT
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile
build-args: |
CONTENT_REPO=https://git.send.nrw/b1tsblog/blogcontent.git
CONTENT_REF=main
platforms: |
linux/amd64
push: true
tags: | # replace it with your local IP and tags
${{ vars.DOCKER_REGISTRY }}/${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ steps.meta.outputs.REPO_VERSION }}
${{ vars.DOCKER_REGISTRY }}/${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ env.DOCKER_LATEST }}
- name: Build and push StarCitizen
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile
build-args: |
CONTENT_REPO=https://git.send.nrw/b1tsblog/sccontent.git
CONTENT_REF=main
platforms: |
linux/amd64
push: true
tags: | # replace it with your local IP and tags
${{ vars.DOCKER_REGISTRY }}/${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:sc-${{ env.DOCKER_LATEST }}

View File

@@ -0,0 +1,64 @@
name: release-tag
on:
push:
branches:
- 'staging'
jobs:
release-image:
runs-on: ubuntu-fast
env:
DOCKER_ORG: ${{ vars.DOCKER_ORG }}
DOCKER_LATEST: staging
RUNNER_TOOL_CACHE: /toolcache
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker BuildX
uses: docker/setup-buildx-action@v2
with: # replace it with your local IP
config-inline: |
[registry."${{ vars.DOCKER_REGISTRY }}"]
http = true
insecure = true
- name: Login to DockerHub
uses: docker/login-action@v2
with:
registry: ${{ vars.DOCKER_REGISTRY }} # replace it with your local IP
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Get Meta
id: meta
run: |
echo REPO_NAME=$(echo ${GITHUB_REPOSITORY} | awk -F"/" '{print $2}') >> $GITHUB_OUTPUT
echo REPO_VERSION=$(git describe --tags --always | sed 's/^v//') >> $GITHUB_OUTPUT
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile
platforms: |
linux/amd64
push: true
tags: | # replace it with your local IP and tags
${{ vars.DOCKER_REGISTRY }}/${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ steps.meta.outputs.REPO_VERSION }}
${{ vars.DOCKER_REGISTRY }}/${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ env.DOCKER_LATEST }}
- name: Build and push StarCitizen
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile
build-args: |
CONTENT_REPO=https://git.send.nrw/b1tsblog/sccontent.git
CONTENT_REF=main
platforms: |
linux/amd64
push: true
tags: | # replace it with your local IP and tags
${{ vars.DOCKER_REGISTRY }}/${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:sc-${{ env.DOCKER_LATEST }}

View File

@@ -1,7 +1,7 @@
############################ ############################
# 1) GoBuild # 1) GoBuild
############################ ############################
FROM golang:1.22 AS build FROM golang:1.23.1 AS build
WORKDIR /app WORKDIR /app
COPY go.mod go.sum ./ COPY go.mod go.sum ./
@@ -16,19 +16,22 @@ RUN CGO_ENABLED=0 go build -o /blog ./cmd/blog
FROM alpine/git AS content FROM alpine/git AS content
# Parameterisierbar beim docker build --build-arg … # Parameterisierbar beim docker build --build-arg …
ARG CONTENT_REPO=https://github.com/username/blog-content.git ARG CONTENT_REPO=https://git.send.nrw/b1tsblog/blogcontent.git
ARG CONTENT_REF=main ARG CONTENT_REF=main
RUN git clone --depth 1 --branch ${CONTENT_REF} ${CONTENT_REPO} /src RUN git clone --depth 1 --branch ${CONTENT_REF} ${CONTENT_REPO} /src
# ─── Repack: bring alles in eine saubere Struktur ──────────────── # ─── Repack: bring alles in eine saubere Struktur ────────────────
# • MarkdownPosts: /src/articles/*.md → /out/content # • MarkdownPosts: /articles/*.md → /out/content
# • Bilder + CSS + JS: /src/web/static/**/* → /out/static # • Bilder + CSS + JS: /static/**/* → /out/static
# • statische Seiten: /pages/* → /out/pages
# (PfadAnpassung hier nach DEINEM RepositoryLayout) # (PfadAnpassung hier nach DEINEM RepositoryLayout)
RUN mkdir -p /out/content /out/static RUN mkdir -p /out/content /out/static /out/pages /out/templates/
RUN find /src/articles -name '*.md' -exec cp {} /out/content/ \; RUN find /src/articles -name '*.md' -exec cp {} /out/content/ \;
RUN cp -r /src/web/static/* /out/static/ RUN cp -r /src/static/* /out/static/
RUN cp -r /src/pages/* /out/pages/
RUN cp -r /src/templates/* /out/templates/
############################ ############################
@@ -38,18 +41,30 @@ FROM debian:bookworm-slim
# (optional) MySQLClient für später # (optional) MySQLClient für später
RUN apt-get update && apt-get install -y --no-install-recommends \ RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \ ca-certificates git \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# ─── Binärdatei ───── # ─── Binärdatei ─────
COPY --from=build /blog /usr/local/bin/blog COPY --from=build /blog /usr/local/bin/blog
# ─── Content + Assets ─── # ─── Content + Assets ───
RUN mkdir -p /content /static /pages /app /templates /ticks
COPY . /app
COPY --from=content /out/content /content COPY --from=content /out/content /content
COPY --from=content /out/static /static COPY --from=content /out/static /static
COPY --from=content /out/pages /pages
COPY --from=content /out/templates /templates
ENV BLOG_CONTENT_DIR=/content ENV BLOG_CONTENT_DIR=/content
ENV BLOG_STATIC_DIR=/static ENV BLOG_STATIC_DIR=/static
ENV BLOG_PAGES_DIR=/pages
ENV BLOG_TEMPLATES_DIR=/templates
ENV BLOG_TICKS_DIR=/ticks
ENV GIT_ENABLE=false
ENV GIT_REPO=null
ENV GIT_BRANCH=main
ENV GIT_DIR=/git-temp
ENV GIT_INTERVAL=10
EXPOSE 8080 EXPOSE 8080
CMD ["blog"] CMD ["blog"]

41
Dockerfile_Slim Normal file
View File

@@ -0,0 +1,41 @@
############################
# 1) GoBuild
############################
FROM golang:1.23.1 AS build
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /blog ./cmd/blog
############################
# 3) RuntimeImage
############################
FROM debian:bookworm-slim
# (optional) MySQLClient für später
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates git \
&& rm -rf /var/lib/apt/lists/*
# ─── Binärdatei ─────
COPY --from=build /blog /usr/local/bin/blog
# ─── Content + Assets ───
RUN mkdir -p /content /static /pages /app /templates /ticks /git-temp
COPY . /app
ENV BLOG_CONTENT_DIR=/content
ENV BLOG_STATIC_DIR=/static
ENV BLOG_PAGES_DIR=/pages
ENV BLOG_TEMPLATES_DIR=/templates
ENV BLOG_TICKS_DIR=/ticks
ENV GIT_ENABLE=false
ENV GIT_REPO=null
ENV GIT_BRANCH=main
ENV GIT_DIR=/git-temp
ENV GIT_INTERVAL=10
EXPOSE 8080
CMD ["blog"]

View File

@@ -1,2 +1,25 @@
# b1tsblog # b1tsblog
## B1tsBlog
- Anpassung Datenschutzerklärung
- Windows Server 2025 Domänen-Controller legt Netzwerkprofil auf Öffentlich fest
- TLS-Zertifikat mit SHA3 auf Windows Server 2016 einspielen
- Warum personenbezogene Daten nie in die Betreffzeile einer E-Mail gehören
- Warum meldet der Client, dass die Default Domain Policy nicht gelesen werden kann
- Open Source als Innovationsmotor: Warum Unternehmen auf offene Software setzen
- Neue Blogserie: Open Source im Unternehmen
- Optimiert für schnelleres Laden mit webp-Inhalten
- docker-swarm-mit-abweichendem-port-einrichten
- Content-Update 2025-05-05
- PHPMyAdmin mit Serverauswahl im Homelab mittels Docker bereitstellen
- Windows Fehler 0x80072F8F - Installation optionaler Features schlägt fehl
- In Ubuntu den Port 53 - DNS - selber nutzen
- Ein eigenes Docker-Image erstellen - so geht's
- Der eigene DNS-Server im Homelab
- Content-Update 2025-05-04
## B1ts Star Citizen Blog
- Anpassung Datenschutzerklärung
- Invictus 2955 - Das Jahr der Idris-P?

View File

@@ -1,17 +1,74 @@
package main package main
import ( import (
"encoding/json"
"fmt" "fmt"
"html/template" "html/template"
"io"
"net/http" "net/http"
"os" "os"
"os/exec"
"os/signal"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
"syscall"
"time" "time"
"git.send.nrw/sendnrw/b1tsblog/internal/article" "git.send.nrw/sendnrw/b1tsblog/internal/article"
) )
var TickCatalog []TickEntry
func SaveTickCatalog(filename string) error {
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
return encoder.Encode(TickCatalog)
}
func LoadTickCatalog(filename string) error {
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close()
return json.NewDecoder(file).Decode(&TickCatalog)
}
func IncTick(xSlug string) error {
for a, b := range TickCatalog {
if b.Slug == xSlug {
TickCatalog[a].Count = TickCatalog[a].Count + 1
return nil
}
}
newEntry := TickEntry{Slug: xSlug, Count: 1}
TickCatalog = append(TickCatalog, newEntry)
return fmt.Errorf("")
}
func getTick(xSlug string) string {
for _, b := range TickCatalog {
if b.Slug == xSlug {
var n int64 = b.Count
return strconv.FormatInt(n, 10)
}
}
return "0"
}
type TickEntry struct {
Slug string `json:"slug"`
Count int64 `json:"count"`
}
func getenv(k, d string) string { func getenv(k, d string) string {
if v := os.Getenv(k); v != "" { if v := os.Getenv(k); v != "" {
return v return v
@@ -39,39 +96,69 @@ var (
tplArticle *template.Template tplArticle *template.Template
) )
var Xarticles []article.Article
func main() { func main() {
// Signal-Kanal einrichten
stop := make(chan os.Signal, 1)
signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
// Goroutine, die auf Signale wartet
go func() {
<-stop
fmt.Println("Stop Sign...")
prepareExit()
os.Exit(0)
}()
// --- Verzeichnisse konfigurierbar machen -------------------------
contentDir := getenv("BLOG_CONTENT_DIR", "/content")
staticDir := getenv("BLOG_STATIC_DIR", "/app/internal/web/static")
pagesDir := getenv("BLOG_PAGES_DIR", "/pages")
templatesDir := getenv("BLOG_TEMPLATES_DIR", "/templates")
ticksDir := getenv("BLOG_TICKS_DIR", "/ticks")
gitEnable := enabled("GIT_ENABLE", false)
gitRepo := getenv("GIT_REPO", "null")
gitBranch := getenv("GIT_BRANCH", "main")
gitDir := getenv("GIT_DIR", "/git-temp")
gitInterval := getenv("GIT_INTERVAL", "10")
TickCatalog = nil
if err := LoadTickCatalog(ticksDir + "/ticks.json"); err != nil {
fmt.Println("Fehler beim Laden:", err)
}
fmt.Println("Geladener Katalog:", TickCatalog)
funcs := template.FuncMap{ funcs := template.FuncMap{
"now": time.Now, // jetztZeit bereitstellen "now": time.Now, // jetztZeit bereitstellen
} }
// Basislayout zuerst parsen // Basislayout zuerst parsen
layout := template.Must( layout := template.Must(template.New("base").Funcs(funcs).ParseFiles(templatesDir + "/base.html"))
template.New("base").Funcs(funcs).
ParseFiles("internal/web/templates/base.html"),
)
// LISTSeite: base + list.html // LISTSeite: base + list.html
tplList = template.Must(layout.Clone()) tplList = template.Must(layout.Clone())
template.Must(tplList.Funcs(funcs).ParseFiles("internal/web/templates/list.html")) template.Must(tplList.Funcs(funcs).ParseFiles(templatesDir + "/list.html"))
// ARTICLEInstanz // ARTICLEInstanz
tplArticle = template.Must(layout.Clone()) tplArticle = template.Must(layout.Clone())
template.Must(tplArticle.Funcs(funcs).ParseFiles("internal/web/templates/article.html")) template.Must(tplArticle.Funcs(funcs).ParseFiles(templatesDir + "/article.html"))
tplPage := template.Must(layout.Clone()) tplPage := template.Must(layout.Clone())
template.Must(tplPage.ParseFiles("internal/web/templates/page.html")) template.Must(tplPage.ParseFiles(templatesDir + "/page.html"))
mux := http.NewServeMux() mux := http.NewServeMux()
staticDir := "internal/web/static" articles, err := article.LoadDir(contentDir)
articles, err := article.LoadDir("content")
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }
staticPages, err := article.LoadStatic("pages") Xarticles = articles
staticPages, err := article.LoadStatic(pagesDir)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} else { } else {
@@ -84,15 +171,31 @@ func main() {
http.NotFound(w, r) http.NotFound(w, r)
return return
} }
if err := tplList.ExecuteTemplate(w, "layout", articles); err != nil {
/* */
for a, b := range Xarticles {
Xarticles[a].Counter = getTick(b.Slug)
}
/* */
if err := tplList.ExecuteTemplate(w, "layout", article.ListPage{
Title: "Startseite",
Description: "Alle Artikel im Überblick",
Articles: Xarticles,
}); err != nil {
http.Error(w, err.Error(), 500) http.Error(w, err.Error(), 500)
} }
}) })
mux.HandleFunc("/post/", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("/post/", func(w http.ResponseWriter, r *http.Request) {
slug := strings.TrimPrefix(r.URL.Path, "/post/") slug := strings.TrimPrefix(r.URL.Path, "/post/")
for _, a := range articles { for _, a := range Xarticles {
if a.Slug == slug { if a.Slug == slug {
IncTick(slug)
t := getTick(slug)
a.Counter = t
if err := tplArticle.ExecuteTemplate(w, "layout", a); err != nil { if err := tplArticle.ExecuteTemplate(w, "layout", a); err != nil {
http.Error(w, err.Error(), 500) http.Error(w, err.Error(), 500)
} }
@@ -115,9 +218,188 @@ func main() {
} }
}) })
mux.Handle("/static/", mux.Handle("/static/", cacheControl(http.StripPrefix("/static/", http.FileServer(http.Dir(staticDir)))))
cacheControl(http.StripPrefix("/static/",
http.FileServer(http.Dir(staticDir)))))
http.ListenAndServe(":8080", mux) if gitEnable {
xMinute, _ := strconv.Atoi(gitInterval)
xDuration := time.Duration(xMinute) * time.Minute
go startAutoClone(gitRepo, gitBranch, gitDir, xDuration)
}
StopServer(http.ListenAndServe(":8080", mux))
}
func prepareExit() {
fmt.Println("~", "Running exit tasks...")
if err := SaveTickCatalog(getenv("BLOG_TICKS_DIR", "/ticks") + "/ticks.json"); err != nil {
fmt.Println("Fehler beim Speichern:", err)
}
fmt.Println("Geladener Katalog:", TickCatalog)
fmt.Println("~", "Exit completed.")
}
func StopServer(e error) {
fmt.Println("~", "Stopping server...")
prepareExit()
fmt.Println("~", "Server stopped!")
}
func cloneRepo(repoURL, branch, dir string) {
fmt.Printf("Starte Klonvorgang für Branch '%s'...\n", branch)
// Verzeichnis löschen
if err := os.RemoveAll(dir); err != nil {
fmt.Println("Fehler beim Löschen des Verzeichnisses:", err)
return
}
// Git-Clone mit Branch
cmd := exec.Command("git", "clone", "--branch", branch, "--single-branch", repoURL, dir)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Println("Fehler beim Klonen:", err)
} else {
fmt.Println("Repo erfolgreich geklont.")
}
contentDir := getenv("BLOG_CONTENT_DIR", "/content")
err := os.RemoveAll("/content")
if err != nil {
fmt.Println(err, "/content")
}
err = os.RemoveAll("/static")
if err != nil {
fmt.Println(err, "/static")
}
err = os.RemoveAll("/pages")
if err != nil {
fmt.Println(err, "/pages")
}
err = os.RemoveAll("/templates")
if err != nil {
fmt.Println(err, "/templates")
}
if err := os.MkdirAll("/content", 0755); err != nil {
fmt.Println("Fehler beim Erstellen des Zielordners:", err)
return
}
if err := os.MkdirAll("/static", 0755); err != nil {
fmt.Println("Fehler beim Erstellen des Zielordners:", err)
return
}
if err := os.MkdirAll("/pages", 0755); err != nil {
fmt.Println("Fehler beim Erstellen des Zielordners:", err)
return
}
if err := os.MkdirAll("/templates", 0755); err != nil {
fmt.Println("Fehler beim Erstellen des Zielordners:", err)
return
}
if err := copyDirContents("/git-temp/articles", "/content"); err != nil {
fmt.Println("Fehler beim Kopieren:", err)
} else {
fmt.Println("Kopieren abgeschlossen.")
}
if err := copyDirContents("/git-temp/static", "/static"); err != nil {
fmt.Println("Fehler beim Kopieren:", err)
} else {
fmt.Println("Kopieren abgeschlossen.")
}
if err := copyDirContents("/git-temp/pages", "/pages"); err != nil {
fmt.Println("Fehler beim Kopieren:", err)
} else {
fmt.Println("Kopieren abgeschlossen.")
}
if err := copyDirContents("/git-temp/templates", "/templates"); err != nil {
fmt.Println("Fehler beim Kopieren:", err)
} else {
fmt.Println("Kopieren abgeschlossen.")
}
articles, err := article.LoadDir(contentDir)
if err != nil {
fmt.Println(err)
}
Xarticles = articles
}
func copyDirContents(srcDir, destDir string) error {
entries, err := os.ReadDir(srcDir)
if err != nil {
return fmt.Errorf("Fehler beim Lesen von %s: %w", srcDir, err)
}
for _, entry := range entries {
srcPath := filepath.Join(srcDir, entry.Name())
destPath := filepath.Join(destDir, entry.Name())
info, err := entry.Info()
if err != nil {
return err
}
if info.IsDir() {
if err := os.MkdirAll(destPath, info.Mode()); err != nil {
return fmt.Errorf("Fehler beim Erstellen von Ordner %s: %w", destPath, err)
}
// rekursiv kopieren
if err := copyDirContents(srcPath, destPath); err != nil {
return err
}
} else {
if err := copyFile(srcPath, destPath, info); err != nil {
return err
}
}
}
return nil
}
func copyFile(src, dest string, info os.FileInfo) error {
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(dest)
if err != nil {
return err
}
defer out.Close()
if _, err := io.Copy(out, in); err != nil {
return err
}
return os.Chmod(dest, info.Mode())
}
func startAutoClone(repoURL, branch, dir string, interval time.Duration) {
go cloneRepo(repoURL, branch, dir) // sofortiger Start
ticker := time.NewTicker(interval)
defer ticker.Stop()
for range ticker.C {
go cloneRepo(repoURL, branch, dir)
}
} }

View File

@@ -1,3 +0,0 @@
<!--{"title": "ChatGPT + Go: So geht's", "date": "2025-05-04", "slug": "chatgpt-go", "cover": ""}-->
# Hello Gophers 🤖
*(MarkdownInhalt)*

View File

@@ -1,5 +0,0 @@
<!--{"title": "Mein erster MarkdownPosty", "date": "2025-05-04", "slug": "mein-erster-markdown-posty", "cover": "/static/img/placeholder.png"}-->
# Hello Markdown 🌟
Dies ist *kursiv*, **fett** und ein [Link](https://example.com).

View File

@@ -1,5 +0,0 @@
<!--{"title": "Mein erster MarkdownPost", "date": "2025-05-04", "slug": "mein-erster-markdown-post", "cover": "/static/img/placeholder.png"}-->
# Hello Markdown 🌟
Dies ist *kursiv*, **fett** und ein [Link](https://example.com).

View File

@@ -62,6 +62,7 @@ func LoadDir(root string) ([]Article, error) {
Date string `json:"date"` Date string `json:"date"`
Slug string `json:"slug"` Slug string `json:"slug"`
Cover string `json:"cover"` Cover string `json:"cover"`
Description string `json:"description"`
} }
if err := json.Unmarshal(headerJSON, &meta); err != nil { if err := json.Unmarshal(headerJSON, &meta); err != nil {
return fmt.Errorf("%s: %w", path, err) return fmt.Errorf("%s: %w", path, err)
@@ -91,6 +92,7 @@ func LoadDir(root string) ([]Article, error) {
Slug: meta.Slug, Slug: meta.Slug,
Date: date, Date: date,
Cover: meta.Cover, Cover: meta.Cover,
Description: meta.Description,
Body: template.HTML(body), Body: template.HTML(body),
}) })
return nil return nil
@@ -107,6 +109,7 @@ func LoadDir(root string) ([]Article, error) {
type StaticPage struct { type StaticPage struct {
Title string Title string
Slug string Slug string
Description string
Body template.HTML Body template.HTML
} }
@@ -166,6 +169,7 @@ func LoadStatic(dir string) (map[string]StaticPage, error) {
var meta struct { var meta struct {
Title string `json:"title"` Title string `json:"title"`
Slug string `json:"slug"` Slug string `json:"slug"`
Description string `json:"description"`
} }
if err := json.Unmarshal(headerJSON, &meta); err != nil { if err := json.Unmarshal(headerJSON, &meta); err != nil {
return nil, fmt.Errorf("%s: %w", path, err) return nil, fmt.Errorf("%s: %w", path, err)
@@ -187,6 +191,7 @@ func LoadStatic(dir string) (map[string]StaticPage, error) {
pages[meta.Slug] = StaticPage{ pages[meta.Slug] = StaticPage{
Title: meta.Title, Title: meta.Title,
Slug: meta.Slug, Slug: meta.Slug,
Description: meta.Description,
Body: template.HTML(body), Body: template.HTML(body),
} }
} }

View File

@@ -10,6 +10,14 @@ type Article struct {
Title string Title string
Slug string Slug string
Date time.Time Date time.Time
Cover string //  NEW Cover string
Body template.HTML // already trusted Body template.HTML
Description string
Counter string
}
type ListPage struct {
Title string
Description string
Articles []Article
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -1,11 +1,27 @@
/* ---------- Local WebFonts ---------- */
@font-face {
font-family: "Fira Code";
src: url("/static/fonts/FiraCode-VariableFont.woff2") format("woff2");
font-weight: 400 700;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Inter";
src: url("/static/fonts/Inter-VariableFont.woff2") format("woff2");
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
/* ---------- Farbpalette ---------- */ /* ---------- Farbpalette ---------- */
:root { :root {
/* Light theme */ /* Light theme */
--bg: #f5f7fa; --bg: #f5f7fa;
--bg-alt: #ffffff; --bg-alt: #ffffff;
--card-bg: #ffffff; --card-bg: #ffffff;
--text: #1f2933; --text: #000000;
--text-muted: #64748b; --text-muted: #1f2933;
--accent: #2563eb; /* Indigo600 */ --accent: #2563eb; /* Indigo600 */
--accent-light: #3b82f6; /* Indigo500 */ --accent-light: #3b82f6; /* Indigo500 */
--code-bg: #f1f5f9; --code-bg: #f1f5f9;
@@ -14,7 +30,7 @@
--gap: 2rem; --gap: 2rem;
--shadow: 0 4px 16px rgba(0,0,0,.08); --shadow: 0 4px 16px rgba(0,0,0,.08);
font-size: 16px; font-size: 16px;
font-family: "Inter", system-ui, "Segoe UI", Roboto, sans-serif; font-family: "Inter", system-ui, sans-serif;
color-scheme: light; color-scheme: light;
} }
@@ -24,8 +40,8 @@
--bg: #0d1117; --bg: #0d1117;
--bg-alt: #161b22; --bg-alt: #161b22;
--card-bg: #161b22; --card-bg: #161b22;
--text: #c9d1d9; --text: #ffffff;
--text-muted: #8b949e; --text-muted: #e4e8ec;
--accent: #3b82f6; --accent: #3b82f6;
--accent-light:#60a5fa; --accent-light:#60a5fa;
--code-bg: #1e242c; --code-bg: #1e242c;
@@ -44,11 +60,15 @@ body {
} }
a { a {
color: var(--accent); color: var(--accent);
text-decoration: none; text-decoration: underline dotted;
transition: color .15s; transition: color .15s;
} }
a:hover { color: var(--accent-light); } a:hover { color: var(--accent-light); }
a.no-underline {
text-decoration: none;
}
/* Container in der Mitte */ /* Container in der Mitte */
.wrapper { .wrapper {
max-width: 1200px; max-width: 1200px;
@@ -56,6 +76,13 @@ a:hover { color: var(--accent-light); }
padding: var(--gap) calc(var(--gap) / 1.5); padding: var(--gap) calc(var(--gap) / 1.5);
} }
img.footer {
max-width: 150px;
max-height: 60px; /* oder ein anderer fixer Wert, z. B. 500px */
width: auto;
height: auto;
}
/* ---------- Kopf & Fuß ---------- */ /* ---------- Kopf & Fuß ---------- */
header, footer { header, footer {
background: var(--bg-alt); background: var(--bg-alt);
@@ -64,7 +91,7 @@ header, footer {
header { header {
position: sticky; top: 0; z-index: 10; position: sticky; top: 0; z-index: 10;
display: flex; justify-content: space-between; align-items: center; display: flex; justify-content: space-between; align-items: center;
padding: 1rem calc(var(--gap) / 1.2); padding: 10px calc(var(--gap) / 1.2);
} }
header h1 { margin: 0; font-size: 1.4rem; } header h1 { margin: 0; font-size: 1.4rem; }
footer { footer {
@@ -128,6 +155,10 @@ article blockquote {
background: var(--code-bg); background: var(--code-bg);
color: var(--text-muted); color: var(--text-muted);
} }
code {
background: var(--code-bg);
color: var(--text-muted);
}
.main-nav ul { .main-nav ul {
display: flex; display: flex;
gap: 1.25rem; gap: 1.25rem;

View File

@@ -5,7 +5,7 @@
{{ if .Cover }} {{ if .Cover }}
<img class="hero" src="{{ .Cover }}" alt=""> <img class="hero" src="{{ .Cover }}" alt="">
{{ end }} {{ end }}
<p><a class="no-underline" href="/">Zurück zur Übersicht</a></p>
<h1>{{ .Title }}</h1> <h1>{{ .Title }}</h1>
<time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "02.01.2006" }}</time> <time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "02.01.2006" }}</time>
@@ -13,7 +13,7 @@
{{ .Body }} {{ .Body }}
</div> </div>
<p><a href="/">Zurück zur Übersicht</a></p> <p><a class="no-underline" href="/">Zurück zur Übersicht</a></p>
</article> </article>
{{ end }} {{ end }}

View File

@@ -4,22 +4,23 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="viewport" content="width=device-width,initial-scale=1">
{{ if .Description }}
<meta name="description" content="{{ .Description }}">
{{ end }}
<title>{{ block "title" . }}B1tsblog{{ end }}</title> <title>{{ block "title" . }}B1tsblog{{ end }}</title>
<link rel="icon" type="image/vnd.icon" href="/static/img/favicon.ico">
<link rel="stylesheet" href="/static/main.css"> <link rel="stylesheet" href="/static/main.css">
<link rel="preconnect" href="https://fonts.gstatic.com">
<!-- MonospaceFont für Code (optional) -->
<link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet">
</head> </head>
<body> <body>
<header> <header>
<h1><a href="/">B1tsblog</a></h1> <h1><a href="/" class="no-underline">B1tsblog</a></h1>
<nav class="main-nav"> <nav class="main-nav">
<ul> <ul>
<li><a href="/">Startseite</a></li> <li><a class="no-underline" href="/">Start</a></li>
<li><a href="/page/impressum">Impressum</a></li> <li><a class="no-underline" href="/page/welcome">Hallo</a></li>
<!-- später: <li><a href="/page/datenschutz">Datenschutz</a></li> --> <li><a class="no-underline" href="/page/ai">KI</a></li>
<li><a class="no-underline" href="/page/datenschutzerklaerung">Datenschutz</a></li>
<li><a class="no-underline" href="/page/impressum">Impressum</a></li>
</ul> </ul>
</nav> </nav>
</header> </header>
@@ -29,7 +30,9 @@
</main> </main>
<footer class="wrapper"> <footer class="wrapper">
© {{ now.Year }} · B1tK1ll3r © {{ now.Year }} · Jan Bergner / B1tsBlog
<hr>
Ich verzichte auf Cookies, affiliate Links, Tracking und die Einbindung von Drittanbieter-Diensten.
</footer> </footer>
</body> </body>
</html> </html>

View File

@@ -1,16 +1,16 @@
{{ define "body" }} {{ define "body" }}
<ul class="post-list"> <ul class="post-list">
{{ range . }} {{ range .Articles }}
<li> <li>
<a class="card" href="/post/{{ .Slug }}"> <a class="card no-underline" href="/post/{{ .Slug }}">
{{ if .Cover }} {{ if .Cover }}
<img src="{{ .Cover }}" alt=""> <img src="{{ .Cover }}" alt="">
{{ else }} {{ else }}
<img src="/static/img/placeholder.png" alt=""> <img src="/static/img/placeholder.webp" alt="">
{{ end }} {{ end }}
<div class="card-content"> <div class="card-content">
<h2>{{ .Title }}</h2> <h2>{{ .Title }}</h2>
<time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "02.01.2006" }}</time> <time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "02.01.2006" }}</time> ({{ .Counter }})
</div> </div>
</a> </a>
</li> </li>

View File

@@ -2,9 +2,10 @@
{{ define "body" }} {{ define "body" }}
<article> <article>
<p><a class="no-underline" href="/">Zurück</a></p>
<h1>{{ .Title }}</h1> <h1>{{ .Title }}</h1>
<div class="article-content">{{ .Body }}</div> <div class="article-content">{{ .Body }}</div>
<p><a href="/">Zurück</a></p> <p><a class="no-underline" href="/">Zurück</a></p>
</article> </article>
{{ end }} {{ end }}