v1.0.0
Some checks failed
release-tag / release-image (push) Successful in 4m8s
build-binaries / build (, amd64, linux) (push) Failing after 40s
build-binaries / build (, arm, 7, linux) (push) Failing after 37s
build-binaries / build (, arm64, linux) (push) Failing after 37s
build-binaries / build (.exe, amd64, windows) (push) Failing after 36s
build-binaries / release (push) Has been skipped
Some checks failed
release-tag / release-image (push) Successful in 4m8s
build-binaries / build (, amd64, linux) (push) Failing after 40s
build-binaries / build (, arm, 7, linux) (push) Failing after 37s
build-binaries / build (, arm64, linux) (push) Failing after 37s
build-binaries / build (.exe, amd64, windows) (push) Failing after 36s
build-binaries / release (push) Has been skipped
This commit is contained in:
51
.gitea/workflows/registry.yml
Normal file
51
.gitea/workflows/registry.yml
Normal file
@@ -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 }}
|
||||
124
.gitea/workflows/release.yml
Normal file
124
.gitea/workflows/release.yml
Normal file
@@ -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
|
||||
8
Dockerfile
Normal file
8
Dockerfile
Normal file
@@ -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"]
|
||||
231
data.json
231
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
|
||||
}
|
||||
|
||||
117
main.go
117
main.go
@@ -245,12 +245,12 @@ var baseTpl = `{{define "base"}}
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>{{block "title" .}}Meldesystem{{end}}</title>
|
||||
<title>{{block "title" .}}Advocacy Watchlist{{end}}</title>
|
||||
<style>
|
||||
:root { --bg:#0f172a; --card:#111827; --muted:#9ca3af; --text:#e5e7eb; --acc:#06b6d4; --ok:#10b981; --warn:#f59e0b; --err:#ef4444; }
|
||||
*{box-sizing:border-box} body{margin:0;background:linear-gradient(180deg,#0b1220,#0f172a);color:var(--text);font:16px/1.45 system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial}
|
||||
a{color:var(--acc);text-decoration:none} a:hover{text-decoration:underline}
|
||||
.container{max-width:980px;margin:40px auto;padding:0 16px}
|
||||
.container{max-width:1200px;margin:40px auto;padding:0 16px}
|
||||
header{display:flex;align-items:center;justify-content:space-between;margin-bottom:24px}
|
||||
.brand{font-weight:800;font-size:20px;letter-spacing:.2px}
|
||||
.card{background:var(--card);border:1px solid #1f2937;border-radius:16px;box-shadow:0 10px 30px rgba(0,0,0,.25);padding:20px;margin-bottom:16px}
|
||||
@@ -278,7 +278,7 @@ var baseTpl = `{{define "base"}}
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<div class="brand">Meldesystem</div>
|
||||
<div class="brand">Advocacy Watchlist</div>
|
||||
<div class="flex">
|
||||
{{if .IsAdmin}}<a class="badge" href="/users">👥 Nutzer</a>{{end}}
|
||||
<a class="badge" href="/audit">🪪 Audit</a>
|
||||
@@ -288,7 +288,7 @@ var baseTpl = `{{define "base"}}
|
||||
</div>
|
||||
</header>
|
||||
{{template "content" .}}
|
||||
<footer>Ohne externe Assets · Zeit: {{.Now}}</footer>
|
||||
<footer>Advocacy Watchlist · Zeit: {{.Now}}</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -300,12 +300,24 @@ var loginTpl = `{{define "login"}}
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Login · Meldesystem</title>
|
||||
<style>*{box-sizing:border-box} body{margin:0;display:grid;place-items:center;height:100vh;background:linear-gradient(180deg,#0b1220,#0f172a);color:#e5e7eb;font:16px/1.45 system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial} .card{background:#111827;border:1px solid #1f2937;border-radius:16px;box-shadow:0 10px 30px rgba(0,0,0,.25);padding:24px;width:min(420px,94vw)} input{width:100%;max-width:100%;display:block;margin:0;border:1px solid #273244;border-radius:12px;background:#0b1220;color:#e5e7eb;padding:12px 14px} label{display:block;margin:8px 0 6px;color:#9ca3af} button{width:100%;margin-top:14px;padding:12px;border:0;border-radius:12px;background:#06b6d4;color:#001018;font-weight:800;cursor:pointer} .muted{color:#9ca3af;font-size:12px;text-align:center;margin-top:10px}</style>
|
||||
<title>Login · Advocacy Watchlist</title>
|
||||
<style>
|
||||
*{box-sizing:border-box}
|
||||
body{margin:0;display:grid;place-items:center;height:100vh;background:linear-gradient(180deg,#0b1220,#0f172a);color:#e5e7eb;font:16px/1.45 system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial}
|
||||
.card{background:#111827;border:1px solid #1f2937;border-radius:16px;box-shadow:0 10px 30px rgba(0,0,0,.25);padding:24px;width:min(420px,94vw)}
|
||||
input{width:100%;max-width:100%;display:block;margin:0;border:1px solid #273244;border-radius:12px;background:#0b1220;color:#e5e7eb;padding:12px 14px}
|
||||
label{display:block;margin:8px 0 6px;color:#9ca3af}
|
||||
button{width:100%;margin-top:14px;padding:12px;border:0;border-radius:12px;background:#06b6d4;color:#001018;font-weight:800;cursor:pointer}
|
||||
.badge{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:999px;background:#0b1220;border:1px solid #1f2937;color:#9ca3af;font-size:12px} <!-- NEU -->
|
||||
.muted{color:#9ca3af;font-size:12px;text-align:center;margin-top:10px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<h2 style="margin:0 0 8px">Anmeldung</h2>
|
||||
<div style="margin-top:12px;text-align:center">
|
||||
<a class="badge" href="/public">🌐 View public Entries</a>
|
||||
</div>
|
||||
<form method="POST" action="/login">
|
||||
<label>Nutzername</label>
|
||||
<input name="username" required />
|
||||
@@ -422,7 +434,7 @@ var dashboardTpl = `{{define "dashboard"}}{{template "base" .}}{{end}}
|
||||
<button type="submit">Bestätigen</button>
|
||||
</form>
|
||||
{{else}}
|
||||
<span class="badge">⏳ Wartet auf Bestätigung durch andere</span>
|
||||
<span class="badge">⏳ Wartet auf Bestätigung</span>
|
||||
{{end}}
|
||||
{{else}}
|
||||
<span class="badge">✔ Bereits bestätigt</span>
|
||||
@@ -443,6 +455,65 @@ var dashboardTpl = `{{define "dashboard"}}{{template "base" .}}{{end}}
|
||||
</div>
|
||||
{{end}}`
|
||||
|
||||
var publicTpl = `{{define "public_page"}}
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Advocacy Watchlist - Public</title>
|
||||
<style>
|
||||
:root { --bg:#0f172a; --card:#111827; --muted:#9ca3af; --text:#e5e7eb; --acc:#06b6d4; }
|
||||
*{box-sizing:border-box} body{margin:0;background:linear-gradient(180deg,#0b1220,#0f172a);color:var(--text);font:16px/1.45 system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial}
|
||||
.container{max-width:880px;margin:40px auto;padding:0 16px}
|
||||
header{display:flex;align-items:center;justify-content:space-between;margin-bottom:24px}
|
||||
.card{background:#111827;border:1px solid #1f2937;border-radius:16px;box-shadow:0 10px 30px rgba(0,0,0,.25);padding:20px;margin-bottom:16px}
|
||||
.row{display:flex;gap:12px;flex-wrap:wrap;align-items:center}
|
||||
input[type=text]{flex:1 1 260px;padding:12px 14px;border:1px solid #273244;border-radius:12px;background:#0b1220;color:#e5e7eb}
|
||||
button{padding:10px 14px;border:0;border-radius:12px;background:#06b6d4;color:#001018;font-weight:700;cursor:pointer}
|
||||
table{width:100%;border-collapse:separate;border-spacing:0}
|
||||
th,td{padding:12px 10px;border-bottom:1px solid #1f2937;font-size:14px}
|
||||
th{text-align:left;color:#9ca3af;font-weight:600}
|
||||
.muted{color:#9ca3af}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<div class="brand">Advocacy Watchlist</div>
|
||||
<div class="flex">
|
||||
<a class="badge" href="/">🔒 Login</a>
|
||||
</div>
|
||||
</header>
|
||||
<div class="card">
|
||||
<h2 style="margin:0 0 10px">Public list of confirmed entries</h2>
|
||||
<form class="row" method="GET" action="/public">
|
||||
<input name="q" placeholder="Search…" value="{{.Q}}" />
|
||||
<button type="submit">Filter</button>
|
||||
</form>
|
||||
<div class="muted" style="margin-top:8px">Only <b>confirmed</b> entries are displayed.</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<table>
|
||||
<thead><tr><th>Benutzername</th><th>Confirmed on</th></tr></thead>
|
||||
<tbody>
|
||||
{{range .Rows}}
|
||||
<tr>
|
||||
<td>{{.Name}}</td>
|
||||
<td class="muted">{{.Since}}</td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<tr><td colspan="2" class="muted">No matching entries</td></tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}`
|
||||
|
||||
var auditTpl = `{{define "audit_page"}}
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user