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",
|
"username": "jbergner",
|
||||||
"password_hash": "xO0T3HChR2wCqr2/EdGBh5DImelVl1WQZ7pji+MFW84=:yhjVxufYiqdzq0XQZBcRtQ==",
|
"password_hash": "xO0T3HChR2wCqr2/EdGBh5DImelVl1WQZ7pji+MFW84=:yhjVxufYiqdzq0XQZBcRtQ==",
|
||||||
"is_admin": false,
|
"is_admin": true,
|
||||||
"created_at": 1759593689
|
"created_at": 1759593689
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "B1tK1ll3r",
|
||||||
|
"password_hash": "y+KukuKzR+/5XKQdiUyvHYcwgbTJanzSwnZ/YTFB0XM=:bquVapMq6at57Ta5z2UCKw==",
|
||||||
|
"is_admin": false,
|
||||||
|
"created_at": 1759611544
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"reports": [
|
"reports": [
|
||||||
{
|
{
|
||||||
"id": 3,
|
"id": 5,
|
||||||
"name": "B1tK1ll3r",
|
"name": "SOLHELFROST",
|
||||||
"status": "Bestätigt",
|
"status": "Bestätigt",
|
||||||
"reported_by": "admin",
|
"reported_by": "admin",
|
||||||
"confirmed_by": "jbergner",
|
"confirmed_by": "B1tK1ll3r",
|
||||||
"created_at": 1759593661,
|
"created_at": 1759612138,
|
||||||
"confirmed_at": 1759593901,
|
"confirmed_at": 1759612167,
|
||||||
"reasons": [
|
"reasons": [
|
||||||
{
|
{
|
||||||
"id": 4,
|
"id": 5,
|
||||||
"text": "Trottel",
|
"text": "Griefing mit Polaris",
|
||||||
"added_by": "admin",
|
"added_by": "admin",
|
||||||
"added_at": 1759593661
|
"added_at": 1759612138
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"audit": [
|
"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,
|
"time": 1759593901,
|
||||||
"actor": "jbergner",
|
"actor": "jbergner",
|
||||||
@@ -335,6 +544,6 @@
|
|||||||
"ip": "127.0.0.1"
|
"ip": "127.0.0.1"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"next_id": 4,
|
"next_id": 6,
|
||||||
"next_rid": 5
|
"next_rid": 6
|
||||||
}
|
}
|
||||||
|
|||||||
117
main.go
117
main.go
@@ -245,12 +245,12 @@ var baseTpl = `{{define "base"}}
|
|||||||
<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" />
|
||||||
<title>{{block "title" .}}Meldesystem{{end}}</title>
|
<title>{{block "title" .}}Advocacy Watchlist{{end}}</title>
|
||||||
<style>
|
<style>
|
||||||
:root { --bg:#0f172a; --card:#111827; --muted:#9ca3af; --text:#e5e7eb; --acc:#06b6d4; --ok:#10b981; --warn:#f59e0b; --err:#ef4444; }
|
: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}
|
*{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}
|
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}
|
header{display:flex;align-items:center;justify-content:space-between;margin-bottom:24px}
|
||||||
.brand{font-weight:800;font-size:20px;letter-spacing:.2px}
|
.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}
|
.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>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<header>
|
<header>
|
||||||
<div class="brand">Meldesystem</div>
|
<div class="brand">Advocacy Watchlist</div>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
{{if .IsAdmin}}<a class="badge" href="/users">👥 Nutzer</a>{{end}}
|
{{if .IsAdmin}}<a class="badge" href="/users">👥 Nutzer</a>{{end}}
|
||||||
<a class="badge" href="/audit">🪪 Audit</a>
|
<a class="badge" href="/audit">🪪 Audit</a>
|
||||||
@@ -288,7 +288,7 @@ var baseTpl = `{{define "base"}}
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
{{template "content" .}}
|
{{template "content" .}}
|
||||||
<footer>Ohne externe Assets · Zeit: {{.Now}}</footer>
|
<footer>Advocacy Watchlist · Zeit: {{.Now}}</footer>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -300,12 +300,24 @@ var loginTpl = `{{define "login"}}
|
|||||||
<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" />
|
||||||
<title>Login · Meldesystem</title>
|
<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} .muted{color:#9ca3af;font-size:12px;text-align:center;margin-top:10px}</style>
|
<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>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h2 style="margin:0 0 8px">Anmeldung</h2>
|
<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">
|
<form method="POST" action="/login">
|
||||||
<label>Nutzername</label>
|
<label>Nutzername</label>
|
||||||
<input name="username" required />
|
<input name="username" required />
|
||||||
@@ -422,7 +434,7 @@ var dashboardTpl = `{{define "dashboard"}}{{template "base" .}}{{end}}
|
|||||||
<button type="submit">Bestätigen</button>
|
<button type="submit">Bestätigen</button>
|
||||||
</form>
|
</form>
|
||||||
{{else}}
|
{{else}}
|
||||||
<span class="badge">⏳ Wartet auf Bestätigung durch andere</span>
|
<span class="badge">⏳ Wartet auf Bestätigung</span>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<span class="badge">✔ Bereits bestätigt</span>
|
<span class="badge">✔ Bereits bestätigt</span>
|
||||||
@@ -443,6 +455,65 @@ var dashboardTpl = `{{define "dashboard"}}{{template "base" .}}{{end}}
|
|||||||
</div>
|
</div>
|
||||||
{{end}}`
|
{{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"}}
|
var auditTpl = `{{define "audit_page"}}
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="de">
|
<html lang="de">
|
||||||
@@ -625,8 +696,10 @@ func main() {
|
|||||||
template.Must(tpl.New("audit_bundle").Parse(auditTpl))
|
template.Must(tpl.New("audit_bundle").Parse(auditTpl))
|
||||||
template.Must(tpl.New("users_bundle").Parse(usersTpl))
|
template.Must(tpl.New("users_bundle").Parse(usersTpl))
|
||||||
template.Must(tpl.New("me_bundle").Parse(meTpl))
|
template.Must(tpl.New("me_bundle").Parse(meTpl))
|
||||||
|
template.Must(tpl.New("public_bundle").Parse(publicTpl))
|
||||||
|
|
||||||
http.HandleFunc("/", withAuth(handleIndex))
|
http.HandleFunc("/", withAuth(handleIndex))
|
||||||
|
http.HandleFunc("/public", handlePublic)
|
||||||
http.HandleFunc("/reasons/add", withAuth(handleReasonAdd))
|
http.HandleFunc("/reasons/add", withAuth(handleReasonAdd))
|
||||||
http.HandleFunc("/reasons/delete", withAuth(handleReasonDelete))
|
http.HandleFunc("/reasons/delete", withAuth(handleReasonDelete))
|
||||||
http.HandleFunc("/report", withAuth(handleReport))
|
http.HandleFunc("/report", withAuth(handleReport))
|
||||||
@@ -895,6 +968,36 @@ func handleIndex(w http.ResponseWriter, r *http.Request) {
|
|||||||
_ = tpl.ExecuteTemplate(w, "dashboard", data)
|
_ = 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) {
|
func handleReport(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method != http.MethodPost {
|
if r.Method != http.MethodPost {
|
||||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
|
|||||||
Reference in New Issue
Block a user