Compare commits
35 Commits
cd869b610e
...
staging
| Author | SHA1 | Date | |
|---|---|---|---|
| a38c883450 | |||
| 2f01625cff | |||
| a78eab43d9 | |||
| 7ef6b551ac | |||
| dce90ca88b | |||
| 7493ca278e | |||
| 445db0ec10 | |||
| 87493fe40c | |||
| ca22dd8596 | |||
| 4c41bb8838 | |||
| 98de9b1b98 | |||
| 621db424f0 | |||
| 7e21b10a6c | |||
| 8cd66c9f59 | |||
| 96894516a7 | |||
| 2e31bfdc77 | |||
| 08ef9c7132 | |||
| 847d860144 | |||
| 8b7f1127f8 | |||
| 278d3aa48f | |||
| 9dca6a57df | |||
| 12ce629b31 | |||
| d7726b57e4 | |||
| c25c60ab27 | |||
| 7ee0c9e9f7 | |||
| aba08de902 | |||
| 4764d1536c | |||
| 3357061c79 | |||
| a83e360317 | |||
| 7b598a04be | |||
| 8554e13ebf | |||
| f6806c5ad0 | |||
| c0f985982e | |||
| 17424d399d | |||
| 2e5f9b6fa5 |
@@ -43,9 +43,25 @@ jobs:
|
||||
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 }}
|
||||
${{ 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 }}
|
||||
@@ -48,4 +48,17 @@ jobs:
|
||||
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 }}
|
||||
${{ 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 }}
|
||||
17
Dockerfile
17
Dockerfile
@@ -1,7 +1,7 @@
|
||||
############################
|
||||
# 1) Go‑Build
|
||||
############################
|
||||
FROM golang:1.23.1 AS build
|
||||
FROM golang:1.25 AS build
|
||||
|
||||
WORKDIR /app
|
||||
COPY go.mod go.sum ./
|
||||
@@ -27,10 +27,11 @@ RUN git clone --depth 1 --branch ${CONTENT_REF} ${CONTENT_REPO} /src
|
||||
# • statische Seiten: /pages/* → /out/pages
|
||||
# (Pfad‑Anpassung hier nach DEINEM Repository‑Layout)
|
||||
|
||||
RUN mkdir -p /out/content /out/static /out/pages
|
||||
RUN mkdir -p /out/content /out/static /out/pages /out/templates/
|
||||
RUN find /src/articles -name '*.md' -exec cp {} /out/content/ \;
|
||||
RUN cp -r /src/static/* /out/static/
|
||||
RUN cp -r /src/pages/* /out/pages/
|
||||
RUN cp -r /src/templates/* /out/templates/
|
||||
|
||||
|
||||
############################
|
||||
@@ -40,22 +41,30 @@ FROM debian:bookworm-slim
|
||||
|
||||
# (optional) MySQL‑Client für später
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
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
|
||||
RUN mkdir -p /content /static /pages /app /templates /ticks
|
||||
COPY . /app
|
||||
COPY --from=content /out/content /content
|
||||
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_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"]
|
||||
|
||||
41
Dockerfile_Slim
Normal file
41
Dockerfile_Slim
Normal file
@@ -0,0 +1,41 @@
|
||||
############################
|
||||
# 1) Go‑Build
|
||||
############################
|
||||
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) Runtime‑Image
|
||||
############################
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
# (optional) MySQL‑Client 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"]
|
||||
14
README.md
14
README.md
@@ -1,13 +1,25 @@
|
||||
# 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
|
||||
- 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?
|
||||
331
cmd/blog/main.go
331
cmd/blog/main.go
@@ -1,17 +1,74 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"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 {
|
||||
if v := os.Getenv(k); v != "" {
|
||||
return v
|
||||
@@ -39,44 +96,66 @@ var (
|
||||
tplArticle *template.Template
|
||||
)
|
||||
|
||||
var Xarticles []article.Article
|
||||
|
||||
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 := os.Getenv("BLOG_CONTENT_DIR")
|
||||
if contentDir == "" {
|
||||
contentDir = "/content" // Fallback für local run
|
||||
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")
|
||||
storeEnabled := enabled("STORE_ENABLE", false)
|
||||
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)
|
||||
}
|
||||
|
||||
staticDir := os.Getenv("BLOG_STATIC_DIR")
|
||||
if staticDir == "" {
|
||||
staticDir = "/app/internal/web/static"
|
||||
}
|
||||
fmt.Println("Geladener Katalog:", TickCatalog)
|
||||
|
||||
pagesDir := os.Getenv("BLOG_PAGES_DIR")
|
||||
if staticDir == "" {
|
||||
staticDir = "/pages"
|
||||
}
|
||||
cloneRepo(gitRepo, gitBranch, gitDir)
|
||||
|
||||
funcs := template.FuncMap{
|
||||
"now": time.Now, // jetzt‑Zeit bereitstellen
|
||||
}
|
||||
|
||||
// Basislayout zuerst parsen
|
||||
layout := template.Must(
|
||||
template.New("base").Funcs(funcs).
|
||||
ParseFiles("/app/internal/web/templates/base.html"),
|
||||
)
|
||||
layout := template.Must(template.New("base").Funcs(funcs).ParseFiles(templatesDir + "/base.html"))
|
||||
|
||||
var tplStore *template.Template
|
||||
if storeEnabled {
|
||||
tplStore = template.Must(template.New("store").Funcs(funcs).ParseFiles(templatesDir + "/store.html"))
|
||||
}
|
||||
|
||||
// LIST‑Seite: base + list.html
|
||||
tplList = template.Must(layout.Clone())
|
||||
template.Must(tplList.Funcs(funcs).ParseFiles("/app/internal/web/templates/list.html"))
|
||||
template.Must(tplList.Funcs(funcs).ParseFiles(templatesDir + "/list.html"))
|
||||
|
||||
// ARTICLE‑Instanz
|
||||
tplArticle = template.Must(layout.Clone())
|
||||
template.Must(tplArticle.Funcs(funcs).ParseFiles("/app/internal/web/templates/article.html"))
|
||||
template.Must(tplArticle.Funcs(funcs).ParseFiles(templatesDir + "/article.html"))
|
||||
|
||||
tplPage := template.Must(layout.Clone())
|
||||
template.Must(tplPage.ParseFiles("/app/internal/web/templates/page.html"))
|
||||
template.Must(tplPage.ParseFiles(templatesDir + "/page.html"))
|
||||
|
||||
mux := http.NewServeMux()
|
||||
|
||||
@@ -85,6 +164,8 @@ func main() {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
Xarticles = articles
|
||||
|
||||
staticPages, err := article.LoadStatic(pagesDir)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
@@ -98,10 +179,19 @@ func main() {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
/* */
|
||||
|
||||
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: articles,
|
||||
Articles: Xarticles,
|
||||
}); err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
}
|
||||
@@ -109,8 +199,11 @@ func main() {
|
||||
|
||||
mux.HandleFunc("/post/", func(w http.ResponseWriter, r *http.Request) {
|
||||
slug := strings.TrimPrefix(r.URL.Path, "/post/")
|
||||
for _, a := range articles {
|
||||
for _, a := range Xarticles {
|
||||
if a.Slug == slug {
|
||||
IncTick(slug)
|
||||
t := getTick(slug)
|
||||
a.Counter = t
|
||||
if err := tplArticle.ExecuteTemplate(w, "layout", a); err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
}
|
||||
@@ -133,9 +226,199 @@ func main() {
|
||||
}
|
||||
})
|
||||
|
||||
mux.Handle("/static/",
|
||||
cacheControl(http.StripPrefix("/static/",
|
||||
http.FileServer(http.Dir(staticDir)))))
|
||||
mux.Handle("/static/", cacheControl(http.StripPrefix("/static/", http.FileServer(http.Dir(staticDir)))))
|
||||
|
||||
mux.HandleFunc("/store", func(w http.ResponseWriter, r *http.Request) {
|
||||
if storeEnabled {
|
||||
if err := tplStore.ExecuteTemplate(w, "store", nil); err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
}
|
||||
} else {
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
if gitEnable {
|
||||
xMinute, _ := strconv.Atoi(gitInterval)
|
||||
xDuration := time.Duration(xMinute) * time.Minute
|
||||
go startAutoClone(gitRepo, gitBranch, gitDir, xDuration)
|
||||
}
|
||||
|
||||
StopServer(http.ListenAndServe(":8080", mux))
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
2
go.mod
2
go.mod
@@ -1,5 +1,5 @@
|
||||
module git.send.nrw/sendnrw/b1tsblog
|
||||
|
||||
go 1.23.1
|
||||
go 1.25.3
|
||||
|
||||
require github.com/gomarkdown/markdown v0.0.0-20250311123330-531bef5e742b
|
||||
|
||||
@@ -13,6 +13,7 @@ type Article struct {
|
||||
Cover string
|
||||
Body template.HTML
|
||||
Description string
|
||||
Counter string
|
||||
}
|
||||
|
||||
type ListPage struct {
|
||||
|
||||
@@ -16,10 +16,11 @@
|
||||
<h1><a href="/" class="no-underline">B1tsblog</a></h1>
|
||||
<nav class="main-nav">
|
||||
<ul>
|
||||
<li><a class="no-underline" href="/">Startseite</a></li>
|
||||
<li><a class="no-underline" href="/page/welcome">Hallo! und Datenschutz</a></li>
|
||||
<li><a class="no-underline" href="/">Start</a></li>
|
||||
<li><a class="no-underline" href="/page/welcome">Hallo</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>
|
||||
<!-- später: <li><a href="/page/datenschutz">Datenschutz</a></li> -->
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
@@ -32,10 +33,6 @@
|
||||
© {{ now.Year }} · Jan Bergner / B1tsBlog
|
||||
<hr>
|
||||
Ich verzichte auf Cookies, affiliate Links, Tracking und die Einbindung von Drittanbieter-Diensten.
|
||||
<hr>
|
||||
<a class="no-underline" href="/page/welcome"><img class="footer" src="/static/img/about-me.webp" alt="Über mich"></a>
|
||||
<a class="no-underline" href="/page/impressum"><img class="footer" src="/static/img/contact-me.webp" alt="Über mich"></a>
|
||||
<a class="no-underline" href="/page/welcome"><img class="footer" src="/static/img/youtube.webp" alt="Über mich"></a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
{{ end }}
|
||||
<div class="card-content">
|
||||
<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>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
Reference in New Issue
Block a user