diff --git a/.gitea/workflows/registry.yml b/.gitea/workflows/registry.yml new file mode 100644 index 0000000..20912ac --- /dev/null +++ b/.gitea/workflows/registry.yml @@ -0,0 +1,51 @@ +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 + 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 }} \ No newline at end of file diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..1efbf32 --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,124 @@ +# Git(tea) Actions workflow: Build and publish standalone binaries **plus** bundled `static/` assets +# ──────────────────────────────────────────────────────────────────── +# ✧ Builds the Go‑based WoL server for four targets **and** packt das Verzeichnis +# `static` zusammen mit der Binary, sodass es relativ zur ausführbaren Datei +# liegt (wichtig für die eingebauten Bootstrap‑Assets & favicon). +# +# • linux/amd64 → wol-server-linux-amd64.tar.gz +# • linux/arm64 → wol-server-linux-arm64.tar.gz +# • linux/arm/v7 → wol-server-linux-armv7.tar.gz +# • windows/amd64 → wol-server-windows-amd64.zip +# +# ✧ Artefakte landen im Workflow und – bei Tag‑Push (vX.Y.Z) – als Release‑Assets. +# +# Secrets/variables: +# GITEA_TOKEN – optional, falls default token keine Release‑Rechte hat. +# ──────────────────────────────────────────────────────────────────── + +name: build-binaries + +on: + push: + branches: [ "main" ] + tags: [ "v*" ] + +jobs: + build: + if: startsWith(github.ref, 'refs/tags/') + runs-on: ubuntu-fast + + strategy: + matrix: + include: + - goos: linux + goarch: amd64 + ext: "" + - goos: linux + goarch: arm64 + ext: "" + - goos: linux + goarch: arm + goarm: "7" + ext: "" + - goos: windows + goarch: amd64 + ext: ".exe" + + env: + GO_VERSION: "1.24" + BINARY_NAME: advocacy-watchlist + + steps: + - name: Checkout source + uses: actions/checkout@v3 + + - name: Set up Go ${{ env.GO_VERSION }} + uses: actions/setup-go@v4 + with: + go-version: ${{ env.GO_VERSION }} + cache: true + + - name: Build ${{ matrix.goos }}/${{ matrix.goarch }}${{ matrix.goarm && format('/v{0}', matrix.goarm) || '' }} + shell: bash + run: | + set -e + mkdir -p dist/package + if [ -n "${{ matrix.goarm }}" ]; then export GOARM=${{ matrix.goarm }}; fi + CGO_ENABLED=0 GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} go build -trimpath -ldflags "-s -w" \ + -o "dist/package/${BINARY_NAME}${{ matrix.ext }}" . + # Assets: statisches Verzeichnis beilegen + cp -r static dist/package/ + + - name: Package archive with static assets + shell: bash + run: | + set -e + cd dist + if [ "${{ matrix.goos }}" == "windows" ]; then + ZIP_NAME="${BINARY_NAME}-windows-amd64.zip" + (cd package && zip -r "../$ZIP_NAME" .) + else + ARCH_SUFFIX="${{ matrix.goarch }}" + if [ "${{ matrix.goarch }}" == "arm" ]; then ARCH_SUFFIX="armv${{ matrix.goarm }}"; fi + TAR_NAME="${BINARY_NAME}-${{ matrix.goos }}-${ARCH_SUFFIX}.tar.gz" + tar -czf "$TAR_NAME" -C package . + fi + + - name: Upload workflow artifact + uses: actions/upload-artifact@v3 + with: + name: ${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.goarm && format('v{0}', matrix.goarm) || '' }} + path: dist/*.tar.gz + if-no-files-found: ignore + - uses: actions/upload-artifact@v3 + with: + name: windows-amd64 + path: dist/*.zip + if-no-files-found: ignore + + # Release Schritt für Tag‑Pushes + release: + if: startsWith(github.ref, 'refs/tags/') + needs: build + runs-on: ubuntu-fast + permissions: + contents: write + + steps: + - name: Download artifacts + uses: actions/download-artifact@v3 + with: + path: ./dist + + - name: Create / Update release + uses: softprops/action-gh-release@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITEA_TOKEN || github.token }} + with: + name: "Release ${{ github.ref_name }}" + tag_name: ${{ github.ref_name }} + draft: false + prerelease: false + files: | + dist/**/advocacy-watchlist-*.tar.gz + dist/**/advocacy-watchlist-*.zip diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..dddfe0b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,8 @@ +FROM golang:1.24.4 +WORKDIR /app +COPY go.mod ./ +RUN go mod download +COPY *.go ./ +RUN CGO_ENABLED=0 GOOS=linux go build -o /goprg +EXPOSE 8080 +CMD ["/goprg"] \ No newline at end of file diff --git a/data.json b/data.json index 4429b66..5e82bd6 100644 --- a/data.json +++ b/data.json @@ -9,30 +9,239 @@ { "username": "jbergner", "password_hash": "xO0T3HChR2wCqr2/EdGBh5DImelVl1WQZ7pji+MFW84=:yhjVxufYiqdzq0XQZBcRtQ==", - "is_admin": false, + "is_admin": true, "created_at": 1759593689 + }, + { + "username": "B1tK1ll3r", + "password_hash": "y+KukuKzR+/5XKQdiUyvHYcwgbTJanzSwnZ/YTFB0XM=:bquVapMq6at57Ta5z2UCKw==", + "is_admin": false, + "created_at": 1759611544 } ], "reports": [ { - "id": 3, - "name": "B1tK1ll3r", + "id": 5, + "name": "SOLHELFROST", "status": "Bestätigt", "reported_by": "admin", - "confirmed_by": "jbergner", - "created_at": 1759593661, - "confirmed_at": 1759593901, + "confirmed_by": "B1tK1ll3r", + "created_at": 1759612138, + "confirmed_at": 1759612167, "reasons": [ { - "id": 4, - "text": "Trottel", + "id": 5, + "text": "Griefing mit Polaris", "added_by": "admin", - "added_at": 1759593661 + "added_at": 1759612138 } ] } ], "audit": [ + { + "time": 1759651126, + "actor": "admin", + "action": "login", + "details": "Erfolg", + "ip": "127.0.0.1" + }, + { + "time": 1759614492, + "actor": "", + "action": "logout", + "details": "", + "ip": "127.0.0.1" + }, + { + "time": 1759614266, + "actor": "jbergner", + "action": "login", + "details": "Erfolg", + "ip": "127.0.0.1" + }, + { + "time": 1759613631, + "actor": "", + "action": "logout", + "details": "", + "ip": "127.0.0.1" + }, + { + "time": 1759613314, + "actor": "admin", + "action": "login", + "details": "Erfolg", + "ip": "127.0.0.1" + }, + { + "time": 1759612170, + "actor": "", + "action": "logout", + "details": "", + "ip": "127.0.0.1" + }, + { + "time": 1759612167, + "actor": "B1tK1ll3r", + "action": "confirm", + "details": "ID 5 · SOLHELFROST", + "ip": "127.0.0.1" + }, + { + "time": 1759612164, + "actor": "B1tK1ll3r", + "action": "login", + "details": "Erfolg", + "ip": "127.0.0.1" + }, + { + "time": 1759612150, + "actor": "", + "action": "logout", + "details": "", + "ip": "127.0.0.1" + }, + { + "time": 1759612138, + "actor": "admin", + "action": "report", + "details": "ID 5 · SOLHELFROST", + "ip": "127.0.0.1" + }, + { + "time": 1759612060, + "actor": "admin", + "action": "login", + "details": "Erfolg", + "ip": "127.0.0.1" + }, + { + "time": 1759612020, + "actor": "", + "action": "logout", + "details": "", + "ip": "127.0.0.1" + }, + { + "time": 1759611994, + "actor": "admin", + "action": "delete", + "details": "ID 4 · Jan Bergner", + "ip": "127.0.0.1" + }, + { + "time": 1759611990, + "actor": "admin", + "action": "login", + "details": "Erfolg", + "ip": "127.0.0.1" + }, + { + "time": 1759611575, + "actor": "jbergner", + "action": "delete", + "details": "ID 3 · B1tK1ll3r", + "ip": "127.0.0.1" + }, + { + "time": 1759611564, + "actor": "jbergner", + "action": "login", + "details": "Erfolg", + "ip": "127.0.0.1" + }, + { + "time": 1759611559, + "actor": "", + "action": "logout", + "details": "", + "ip": "127.0.0.1" + }, + { + "time": 1759611553, + "actor": "B1tK1ll3r", + "action": "login", + "details": "Erfolg", + "ip": "127.0.0.1" + }, + { + "time": 1759611547, + "actor": "", + "action": "logout", + "details": "", + "ip": "127.0.0.1" + }, + { + "time": 1759611544, + "actor": "jbergner", + "action": "user_create", + "details": "B1tK1ll3r", + "ip": "127.0.0.1" + }, + { + "time": 1759594548, + "actor": "jbergner", + "action": "login", + "details": "Erfolg", + "ip": "127.0.0.1" + }, + { + "time": 1759594542, + "actor": "", + "action": "logout", + "details": "", + "ip": "127.0.0.1" + }, + { + "time": 1759594537, + "actor": "admin", + "action": "user_toggle_admin", + "details": "jbergner", + "ip": "127.0.0.1" + }, + { + "time": 1759594531, + "actor": "admin", + "action": "login", + "details": "Erfolg", + "ip": "127.0.0.1" + }, + { + "time": 1759594526, + "actor": "", + "action": "logout", + "details": "", + "ip": "127.0.0.1" + }, + { + "time": 1759594412, + "actor": "jbergner", + "action": "confirm", + "details": "ID 4 · Jan Bergner", + "ip": "127.0.0.1" + }, + { + "time": 1759594402, + "actor": "jbergner", + "action": "login", + "details": "Erfolg", + "ip": "127.0.0.1" + }, + { + "time": 1759593988, + "actor": "admin", + "action": "report", + "details": "ID 4 · Jan Bergner", + "ip": "127.0.0.1" + }, + { + "time": 1759593979, + "actor": "admin", + "action": "login", + "details": "Erfolg", + "ip": "127.0.0.1" + }, { "time": 1759593901, "actor": "jbergner", @@ -335,6 +544,6 @@ "ip": "127.0.0.1" } ], - "next_id": 4, - "next_rid": 5 + "next_id": 6, + "next_rid": 6 } diff --git a/main.go b/main.go index 06cf13e..6da07a2 100644 --- a/main.go +++ b/main.go @@ -245,12 +245,12 @@ var baseTpl = `{{define "base"}} - {{block "title" .}}Meldesystem{{end}} + {{block "title" .}}Advocacy Watchlist{{end}} + Login · Advocacy Watchlist +

Anmeldung

+
+ 🌐 View public Entries +
@@ -422,7 +434,7 @@ var dashboardTpl = `{{define "dashboard"}}{{template "base" .}}{{end}}
{{else}} - ⏳ Wartet auf Bestätigung durch andere + ⏳ Wartet auf Bestätigung {{end}} {{else}} ✔ Bereits bestätigt @@ -443,6 +455,65 @@ var dashboardTpl = `{{define "dashboard"}}{{template "base" .}}{{end}}
{{end}}` +var publicTpl = `{{define "public_page"}} + + + + + + Advocacy Watchlist - Public + + + +
+
+
Advocacy Watchlist
+ +
+
+

Public list of confirmed entries

+
+ + +
+
Only confirmed entries are displayed.
+
+ +
+ + + + {{range .Rows}} + + + + + {{else}} + + {{end}} + +
BenutzernameConfirmed on
{{.Name}}{{.Since}}
No matching entries
+
+
+ + +{{end}}` + var auditTpl = `{{define "audit_page"}} @@ -625,8 +696,10 @@ func main() { template.Must(tpl.New("audit_bundle").Parse(auditTpl)) template.Must(tpl.New("users_bundle").Parse(usersTpl)) template.Must(tpl.New("me_bundle").Parse(meTpl)) + template.Must(tpl.New("public_bundle").Parse(publicTpl)) http.HandleFunc("/", withAuth(handleIndex)) + http.HandleFunc("/public", handlePublic) http.HandleFunc("/reasons/add", withAuth(handleReasonAdd)) http.HandleFunc("/reasons/delete", withAuth(handleReasonDelete)) http.HandleFunc("/report", withAuth(handleReport)) @@ -895,6 +968,36 @@ func handleIndex(w http.ResponseWriter, r *http.Request) { _ = tpl.ExecuteTemplate(w, "dashboard", data) } +func handlePublic(w http.ResponseWriter, r *http.Request) { + q := strings.TrimSpace(r.URL.Query().Get("q")) + ql := strings.ToLower(q) + + mu.Lock() + reports := make([]Report, 0, len(db.Reports)) + for _, rep := range db.Reports { + if rep.Status != StatusBestaetigt { + continue // nur bestätigte öffentlich anzeigen + } + if q == "" || strings.Contains(strings.ToLower(rep.Name), ql) { + reports = append(reports, rep) + } + } + mu.Unlock() + + rows := make([]map[string]any, 0, len(reports)) + for _, rep := range reports { + rows = append(rows, map[string]any{ + "Name": template.HTMLEscapeString(rep.Name), + "Since": time.Unix(rep.ConfirmedAt, 0).Format("02.01.2006 15:04"), + }) + } + + _ = tpl.ExecuteTemplate(w, "public_page", map[string]any{ + "Q": q, + "Rows": rows, + }) +} + func handleReport(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { w.WriteHeader(http.StatusMethodNotAllowed)