Import wiki from GitHub
commit
c3434ba45d
13 changed files with 1049 additions and 0 deletions
182
API.md
Normal file
182
API.md
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
# API
|
||||
|
||||
REST-API unter `/api/v1/*`. Versioniert und stabil. JSON-only.
|
||||
|
||||
## Auth
|
||||
|
||||
Tokens in der UI unter **Einstellungen → API-Tokens** erstellen. Token wird nur einmalig angezeigt, danach nur als sha256-Hash in der DB.
|
||||
|
||||
Format: `nrx_<64-hex>`. Im Header senden:
|
||||
|
||||
```
|
||||
Authorization: Bearer nrx_<your-token>
|
||||
```
|
||||
|
||||
## Scopes
|
||||
|
||||
| Scope | Zugriff |
|
||||
|---|---|
|
||||
| `read:domains` | Domains lesen |
|
||||
| `write:domains` | Domains anlegen / löschen |
|
||||
| `read:analytics` | Aggregierte Statistiken |
|
||||
| `read:hits` | Roh-Hits (nur ip_hash, kein Klartext-IP) |
|
||||
|
||||
Fehler-Format:
|
||||
|
||||
```json
|
||||
{ "error": "forbidden", "code": "missing_scope", "required": "read:domains" }
|
||||
```
|
||||
|
||||
## Endpoints
|
||||
|
||||
### `GET /api/v1/health`
|
||||
|
||||
Liveness-Check. Kein Token nötig.
|
||||
|
||||
```bash
|
||||
curl https://admin.firma.de/api/v1/health
|
||||
# {"ok":true,"ts":1714500000000}
|
||||
```
|
||||
|
||||
### `GET /api/v1/version`
|
||||
|
||||
Aktuelle Version + Update-Status. Kein Token nötig.
|
||||
|
||||
```json
|
||||
{
|
||||
"current": "0.1.19",
|
||||
"latest": "0.1.19",
|
||||
"update_available": false,
|
||||
"auto_update": false
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/domains`
|
||||
|
||||
Scope: `read:domains`
|
||||
|
||||
```json
|
||||
{
|
||||
"domains": [
|
||||
{
|
||||
"id": 1, "domain": "alt-firma.de", "status": "active",
|
||||
"target_url": "https://www.firma.de", "redirect_code": 302,
|
||||
"preserve_path": 1, "include_www": 1,
|
||||
"total_hits": 142, "last_hit": 1714499000000
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v1/domains`
|
||||
|
||||
Scope: `write:domains`
|
||||
|
||||
Body:
|
||||
|
||||
```json
|
||||
{
|
||||
"domain": "alt-firma.de",
|
||||
"target_url": "https://www.firma.de",
|
||||
"redirect_code": 302,
|
||||
"preserve_path": true,
|
||||
"include_www": true
|
||||
}
|
||||
```
|
||||
|
||||
Response (`201`):
|
||||
|
||||
```json
|
||||
{
|
||||
"domain": { "id": 5, "status": "pending", ... },
|
||||
"dns_records": [
|
||||
{ "type": "A", "name": "alt-firma.de", "value": "203.0.113.42" },
|
||||
{ "type": "A", "name": "www.alt-firma.de", "value": "203.0.113.42" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/domains/:id`
|
||||
|
||||
Scope: `read:domains`
|
||||
|
||||
### `DELETE /api/v1/domains/:id`
|
||||
|
||||
Scope: `write:domains`
|
||||
|
||||
### `GET /api/v1/domains/:id/stats?days=30`
|
||||
|
||||
Scope: `read:analytics`
|
||||
|
||||
```json
|
||||
{
|
||||
"domain_id": 1, "days": 30, "total": 412,
|
||||
"daily": [{"day":"2026-04-01","hits":12}, ...],
|
||||
"by_country": [{"country":"DE","hits":380}, ...]
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/analytics/summary?days=30`
|
||||
|
||||
Scope: `read:analytics`
|
||||
|
||||
```json
|
||||
{
|
||||
"days": 30, "total": 12480,
|
||||
"daily": [...],
|
||||
"top": [{"id":1,"domain":"alt-firma.de","hits":412}, ...],
|
||||
"by_country": [...]
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/v1/hits?domain_id=1&limit=100`
|
||||
|
||||
Scope: `read:hits`
|
||||
|
||||
```json
|
||||
{
|
||||
"hits": [
|
||||
{
|
||||
"id": 9001, "domain_id": 1, "ts": 1714499000000,
|
||||
"ip_hash": "9f4a...", "country": "DE",
|
||||
"user_agent": "Mozilla/5.0 ...", "referer": null, "path": "/"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`ip_hash` = `sha256(ip + täglicher Salt)`. Kein Klartext-IP wird gespeichert.
|
||||
|
||||
## Versionierung
|
||||
|
||||
`/api/v1` ist stabil. Breaking-Changes bekommen `/api/v2`. Deprecation-Hinweise im `Sunset`-Header.
|
||||
|
||||
## Rate-Limits
|
||||
|
||||
Aktuell kein hartes Limit. Empfehlung: max. 60 req/min pro Token.
|
||||
|
||||
## Beispiele
|
||||
|
||||
**Uptime-Check eines Tokens:**
|
||||
|
||||
```bash
|
||||
curl -fsS -H "Authorization: Bearer $NRX" https://admin.firma.de/api/v1/health || echo "DOWN"
|
||||
```
|
||||
|
||||
**Liste tote Domains (0 Hits / 90d):**
|
||||
|
||||
```bash
|
||||
curl -s -H "Authorization: Bearer $NRX" \
|
||||
"https://admin.firma.de/api/v1/analytics/summary?days=90" \
|
||||
| jq '.top | map(select(.hits == 0))'
|
||||
```
|
||||
|
||||
**Domain anlegen + DNS-Records anzeigen:**
|
||||
|
||||
```bash
|
||||
curl -X POST -H "Authorization: Bearer $NRX" -H "Content-Type: application/json" \
|
||||
-d '{"domain":"shop.alt.de","target_url":"https://shop.firma.de"}' \
|
||||
https://admin.firma.de/api/v1/domains | jq '.dns_records'
|
||||
```
|
||||
|
||||
→ Weiter mit [[Updates]]
|
||||
73
Analytics-&-Reports.md
Normal file
73
Analytics-&-Reports.md
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
# Analytics & Reports
|
||||
|
||||
## Was wird getrackt?
|
||||
|
||||
Pro echtem Hit (siehe [[Bot Filter]]):
|
||||
|
||||
- Zeitstempel (ms)
|
||||
- Domain-ID
|
||||
- Land (ISO-2, falls GeoLite2 installiert)
|
||||
- IP-Hash (sha256(ip + täglicher Salt) — DSGVO, kein Klartext)
|
||||
- User-Agent (max 500 Zeichen)
|
||||
- Referer (max 500 Zeichen)
|
||||
- Pfad (max 500 Zeichen)
|
||||
|
||||
## Dashboard `/dashboard`
|
||||
|
||||
- Total / Aktiv / Wartend Domains
|
||||
- Hits 24h + Eindeutige Besucher
|
||||
- Top-10-Domains-Bar
|
||||
- Daily-Hits-Line letzte 30 Tage
|
||||
|
||||
## Analytics `/analytics`
|
||||
|
||||
- Daily-Line letzte 30 Tage
|
||||
- Top 10 Domains
|
||||
- Top Länder (Pie)
|
||||
- Tote Domains (aktiv aber 0 Hits / 90 Tage)
|
||||
|
||||
## Domain-Detail
|
||||
|
||||
`/domains/<id>` zeigt:
|
||||
|
||||
- Hits 24h / 30 Tage / Gesamt
|
||||
- Eindeutige Besucher (30d, gesamt)
|
||||
- Daily-Line der letzten 30 Tage **dieser Domain**
|
||||
- DNS-Records-Übersicht (live, refreshbar)
|
||||
- Sunset-Editor
|
||||
|
||||
## PDF-Report-Export
|
||||
|
||||
`/analytics` → "PDF Export" Button:
|
||||
|
||||
- 3 Vorlagen: **Minimal** / **Basic** / **Detailliert**
|
||||
- Titel + Zeitraum (1–365 Tage) frei wählbar
|
||||
- Sektionen einzeln an/aus: Zusammenfassung, Daily-Chart, Top-Domains, Geo, Tote, Per-Domain-Detail, Letzte 200 Hits
|
||||
|
||||
Server-side Generation via headless Chromium (puppeteer-core). Direkt-Download als PDF. A4 Hochformat, NexRedirect-Branding, sauberes Page-Break-Verhalten.
|
||||
|
||||
Token-basierter Internal-Access (60s gültig, HMAC-signed) — Report-URL ist nicht öffentlich abrufbar.
|
||||
|
||||
## CSV-Export
|
||||
|
||||
- `/domains` → "CSV" lädt komplette Domain-Liste mit Hit-Counts
|
||||
- `/analytics` → "Hits CSV" lädt letzte 30 Tage Hits (max 100k Zeilen)
|
||||
|
||||
CSV ist UTF-8 mit Komma-Trenner, RFC 4180-konform (Quotes für Sonderzeichen).
|
||||
|
||||
## Audit-Log `/audit`
|
||||
|
||||
500 letzte administrative Aktionen mit Zeit, Benutzer-Email, Aktion, Ziel, Details:
|
||||
|
||||
- domain.create / .update / .delete / .verify / .bulk_delete
|
||||
- group.create / .update / .delete
|
||||
- sunset.bulk
|
||||
- (kann erweitert werden)
|
||||
|
||||
## Eindeutige Besucher
|
||||
|
||||
`COUNT(DISTINCT ip_hash)` über den Zeitraum. **Wichtig**: IP-Hash rotiert täglich (DSGVO-Anforderung). Gleicher User der heute und morgen kommt zählt als 2 verschiedene Besucher.
|
||||
|
||||
Innerhalb eines Tages ist die Zählung präzise. Über mehrere Tage ist die Besucher-Zahl eine **Obergrenze** (überschätzt).
|
||||
|
||||
→ Weiter mit [[Bot Filter]]
|
||||
125
Architecture.md
Normal file
125
Architecture.md
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
# Architecture
|
||||
|
||||
## Komponenten
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ Internet │
|
||||
└──────────────────┬───────────────────────────────────────────┘
|
||||
│ Port 80 / 443
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ Caddy (auto-HTTPS via Let's Encrypt) │
|
||||
│ │
|
||||
│ /etc/caddy/Caddyfile (auto-generated) │
|
||||
│ ┌────────────────────────────────────────────────────┐ │
|
||||
│ │ admin.example.de, server-ip → reverse_proxy :3000 │ │
|
||||
│ │ alt-firma.de, www.alt-firma.de → reverse_proxy │ │
|
||||
│ │ handle_errors { redir https://target.de/ 302 } │ │
|
||||
│ │ ... │ │
|
||||
│ └────────────────────────────────────────────────────┘ │
|
||||
└──────────────────┬───────────────────────────────────────────┘
|
||||
│ reverse_proxy localhost:3000
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ server.ts (Custom Node Server, läuft via tsx) │
|
||||
│ │
|
||||
│ ① Host-Check │
|
||||
│ ├─ Admin-Host (server-IP / base_domain) → Next.js │
|
||||
│ ├─ Aktive Redirect-Domain → Hit loggen + 302 │
|
||||
│ │ (Sunset → HTML-Notice) │
|
||||
│ └─ Unbekannter Host → 404 "Domain not configured" │
|
||||
│ │
|
||||
│ ② Bei Admin-Host: │
|
||||
│ Next.js handleRequest() — App Router │
|
||||
│ ├─ middleware.ts (Edge): Auth-Check via JWT-Cookie │
|
||||
│ ├─ /(app)/* — eingeloggte Routes │
|
||||
│ ├─ /(auth)/login — NextAuth Credentials │
|
||||
│ ├─ /(setup)/setup — First-Run-Wizard │
|
||||
│ ├─ /api/v1/* — Public API (Bearer Token) │
|
||||
│ ├─ /api/* — Admin-API (Session-Cookie) │
|
||||
│ └─ /r/[token] — Internal Report-Page für PDF-Export │
|
||||
└──────────────────┬───────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ better-sqlite3 │
|
||||
│ /var/lib/corex-nexredirect/nexredirect.db │
|
||||
│ Tables: users, domains, domain_groups, hits, settings, │
|
||||
│ api_tokens, update_log, audit_log │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Request-Flow für einen Domain-Hit
|
||||
|
||||
1. `GET https://alt-firma.de/foo` → DNS löst auf Server-IP
|
||||
2. Caddy nimmt an, ACME hat schon Cert für `alt-firma.de`
|
||||
3. Caddy `reverse_proxy localhost:3000`
|
||||
4. server.ts `req.headers.host = "alt-firma.de"`
|
||||
5. `isAdminHost("alt-firma.de")` → false (kein Match auf server_ip / base_domain / localhost)
|
||||
6. `resolveHost("alt-firma.de")` → Resolved-Eintrag aus In-Memory-Cache (5s TTL)
|
||||
7. Bot-Filter (`shouldRecord`) prüft Method, UA, Pfad, Browser-Signals, IP-Scan-Detector
|
||||
8. Wenn echter Hit: `recordHit()` → Buffer (5s batch) → SQLite INSERT
|
||||
9. Sunset aktiv? → HTML-Notice + `Cache-Control: no-store`
|
||||
10. Sonst: 302 zum Target mit `no-store` Headers
|
||||
|
||||
## Request-Flow für Admin
|
||||
|
||||
1. `GET https://admin.example.de/dashboard` → Caddy → server.ts → isAdminHost = true → Next.js
|
||||
2. `middleware.ts` checkt JWT-Session-Cookie
|
||||
3. `(app)/layout.tsx` checkt `isSetupComplete()` + `getServerSession()` (force-dynamic)
|
||||
4. Render Sidebar + Page
|
||||
|
||||
## Datenfluss bei Domain-Add
|
||||
|
||||
```
|
||||
UI POST /api/domains
|
||||
├─ Zod-Validation
|
||||
├─ INSERT INTO domains (status='pending')
|
||||
└─ Audit-Log
|
||||
|
||||
UI POST /api/domains/[id]/verify
|
||||
├─ DNS-Lookup für domain + www-subdomain
|
||||
├─ Vergleich mit getSetting('server_ip')
|
||||
├─ Wenn Match: status='active', verified_at=now
|
||||
├─ invalidateRedirectCache()
|
||||
├─ Caddy: writeCaddyfile() + `caddy reload`
|
||||
└─ Response { ok, caddy_reloaded }
|
||||
```
|
||||
|
||||
## Update-Flow
|
||||
|
||||
```
|
||||
UI Click "Update installieren"
|
||||
└─ POST /api/update/apply (admin-only)
|
||||
└─ exec(`sudo update.sh <tag>`)
|
||||
├─ git checkout tag
|
||||
├─ npm ci
|
||||
├─ Prebuilt .next aus GitHub-Release-Asset (oder npm run build)
|
||||
├─ ln -sf bin/nexredirect /usr/local/bin/
|
||||
└─ ( sleep 2 && systemctl restart ) & disown
|
||||
(Detached — script exitet, API kann response zurückgeben)
|
||||
└─ UI polled /api/v1/health → window.location.reload()
|
||||
```
|
||||
|
||||
## In-Memory-State
|
||||
|
||||
- **Redirect-Cache** (`lib/redirect-resolver.ts`): Map<host, ResolvedRedirect> mit 5s TTL. Invalidiert bei Domain/Group-Mutationen.
|
||||
- **Hits-Buffer** (`lib/hits.ts`): Array von Pending Hits, alle 5s als Transaction in DB geschrieben.
|
||||
- **Geo-Reader** (`lib/geo.ts`): MaxMind-mmdb-Reader, lazy load on first lookup.
|
||||
- **IP-Scan-Tracker** (`lib/hits.ts`): Map<ip_hash, {paths: Set, firstSeen}> mit 30s Window.
|
||||
|
||||
Alle in-memory — bei Restart weg, kein Problem (Cache füllt sich neu).
|
||||
|
||||
## Sicherheit
|
||||
|
||||
- **NextAuth JWT** mit `NEXTAUTH_SECRET` (zufällig generiert beim Install, 30d Session)
|
||||
- **bcryptjs** für Passwort-Hash (cost 12)
|
||||
- **API-Tokens** als sha256-Hash in DB; Klartext nur einmalig bei Anlage angezeigt
|
||||
- **HMAC-signierte Tokens** für PDF-Internal-Access (60s TTL)
|
||||
- **DSGVO**: IP wird nie im Klartext gespeichert. `sha256(ip + daily_salt)`. Daily-Salt rotiert täglich → keine Re-Identifizierung über Tagesgrenzen.
|
||||
- **CSRF**: NextAuth handhabt das für Auth-Routes. Admin-Mutations brauchen Session-Cookie. API-Routes brauchen Bearer-Token.
|
||||
- **Bot-Filter** auch als implizite Schutzschicht gegen Scanner-Probing
|
||||
- **No exposed admin paths**: alle Mutations brauchen Auth. `/api/v1/*` Bearer.
|
||||
|
||||
→ Weiter mit [[Troubleshooting]]
|
||||
85
Bot-Filter.md
Normal file
85
Bot-Filter.md
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
# Bot Filter
|
||||
|
||||
NexRedirect zählt nur "echte" Besucher — Scanner, Crawler und Monitoring-Tools werden serverseitig herausgefiltert. Kombination mehrerer Heuristiken in [`lib/hits.ts`](https://forgejo.mgmt.corexmanagement.de/admin_hg/cx-nexredirect/blob/main/lib/hits.ts):
|
||||
|
||||
## 1. HTTP-Method
|
||||
|
||||
`HEAD` und `OPTIONS` werden nie gezählt (Pre-Flight, Health-Checks).
|
||||
|
||||
## 2. Pfad-Pattern
|
||||
|
||||
Bekannte Scanner-Pfade werden ignoriert. Auszug:
|
||||
|
||||
```
|
||||
/.env* /.git* /.DS_Store /.vscode /.svn /.hg /.idea
|
||||
/wp-(admin|login|content|includes|json) /xmlrpc.php /wordpress/
|
||||
/admin/ /administrator/ /phpmyadmin /pma/ /myadmin
|
||||
/server-status /server-info /info.php /test.php
|
||||
/login.action /console/ /manager/ /jenkins /jolokia
|
||||
/actuator /telescope /horizon /debug /trace.axd /elmah.axd
|
||||
/cgi-bin /about
|
||||
/swagger* /api-docs* /api/swagger /v[23]/api-docs /v2/_catalog
|
||||
/webjars/ /graphql* /api/gql /api/?$
|
||||
/_next/ /@vite /__webpack
|
||||
/ecp/ /owa/ /___proxy_subdomain
|
||||
/(js|assets/js|static)/ /(css|assets/css)/
|
||||
/bot-connect.* /config.json /composer.* /package.*
|
||||
\.\./ %2e%2e /s/[0-9a-f]{20,}
|
||||
rest_route=
|
||||
/favicon.* /apple-touch-icon /robots.txt /sitemap* /ads.txt
|
||||
/.well-known/ /browserconfig.xml
|
||||
```
|
||||
|
||||
Komplett-Liste in `lib/hits.ts`.
|
||||
|
||||
## 3. User-Agent-Pattern
|
||||
|
||||
Bot-Keywords werden gematched:
|
||||
|
||||
```
|
||||
bot, crawl, spider, slurp, curl, wget, httpclient, python-requests,
|
||||
axios, node-fetch, monitor, uptime, pingdom, datadog, prometheus,
|
||||
scanner, fetch, preview, whatsapp, telegrambot, facebookexternalhit,
|
||||
linkedinbot, twitterbot, discordbot, skypeuripreview, mastodon,
|
||||
matrix-bot, preconnect, dnsperf, sentry, newrelic, gtmetrix,
|
||||
lighthouse, headlesschrome, phantomjs, puppeteer, playwright,
|
||||
go-http-client, java/, okhttp, libwww, mechanize, nikto, sqlmap,
|
||||
nmap, masscan, zgrab, nuclei, acunetix, netcraft, expanse, censys,
|
||||
shodan, fuzz, burp, arachni, w3af, wpscan, gobuster, ffuf, dirb,
|
||||
dirbuster
|
||||
```
|
||||
|
||||
Plus:
|
||||
- UA muss `Mozilla/` Prefix haben (echte Browser; Bots ohne Mozilla-Faked werden auch via UA-Keywords gefangen)
|
||||
- UA muss mindestens 15 Zeichen lang sein
|
||||
|
||||
## 4. Browser-Signal-Heuristik
|
||||
|
||||
Echte Browser senden bei jeder Top-Level-Navigation:
|
||||
|
||||
- **`Sec-Fetch-Mode`** Header (Chrome/FF/Safari/Edge seit ~2020)
|
||||
- **`Accept-Language`** Header (jeder Browser hat eine Spracheinstellung)
|
||||
- **`Accept`** mit `text/html` (Browser fragen explizit nach HTML; Scanner senden `*/*`)
|
||||
|
||||
**Mindestens 2 von 3** müssen gesetzt sein. Plus: `Sec-Fetch-Dest` muss `document` / `empty` / `iframe` sein (filtert Image- / Script-Probes).
|
||||
|
||||
Das ist der wichtigste Filter — Scanner mit gefälschtem `Mozilla/5.0`-UA fallen hier durch.
|
||||
|
||||
## 5. Per-IP Scan-Detektor
|
||||
|
||||
In-Memory: gleiche IP-Hash, ≥5 verschiedene Pfade in 30s = Scanner. Alle weiteren Hits dieser IP im Zeitfenster werden verworfen.
|
||||
|
||||
## Ergebnis
|
||||
|
||||
Bei einem typischen Vanity-Redirect-Server:
|
||||
|
||||
- Vorher: ~80% der Hits sind Bots (vor allem nach DNS-Aktivierung kommen Scanner-Wellen)
|
||||
- Nachher: <5% (vereinzelte Lighthouse / Pingdom etc.)
|
||||
|
||||
## False-Positives anzeigen
|
||||
|
||||
Bot-Hits werden gar nicht gespeichert — wenn du sie sehen willst, müsstest du temporär die Filter ausschalten. Es gibt keinen "ignored hits"-Counter.
|
||||
|
||||
Wenn ein Browser fälschlicherweise ausgefiltert wird (uralter Browser ohne Sec-Fetch?): bitte im GitHub-Issue melden mit User-Agent + Headers.
|
||||
|
||||
→ Weiter mit [[CLI]]
|
||||
69
CLI.md
Normal file
69
CLI.md
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
# CLI
|
||||
|
||||
Auf dem Server: `nexredirect <command>` (Symlink in `/usr/local/bin/`).
|
||||
|
||||
## Subcommands
|
||||
|
||||
```
|
||||
status Service-Status (systemctl status)
|
||||
start Service starten
|
||||
stop Service stoppen
|
||||
restart Service neu starten
|
||||
logs [-n N] Logs streamen (default: -f live tail)
|
||||
update [tag] Auf neueste Version (oder bestimmten Tag); skip wenn schon aktuell
|
||||
update -f [tag] Update erzwingen auch wenn Version gleich
|
||||
version Aktuelle + neueste Version (von GitHub)
|
||||
caddy reload Caddyfile reload via Admin-API
|
||||
caddy regen Caddyfile aus DB neu generieren + reloaden
|
||||
caddy fix-perms /etc/caddy/Caddyfile dem Service-User übertragen
|
||||
caddy show Aktuellen Caddyfile anzeigen
|
||||
db SQLite-Shell auf der Datenbank öffnen
|
||||
domains Aktive Domains listen
|
||||
hits [N] Letzte N Hits (default 20)
|
||||
tokens API-Tokens auflisten
|
||||
backup [PATH] DB + Caddyfile sichern (default: /tmp/...)
|
||||
uninstall Service + Files entfernen (DB bleibt)
|
||||
help Hilfe
|
||||
```
|
||||
|
||||
## Beispiele
|
||||
|
||||
```bash
|
||||
# Status checken
|
||||
nexredirect status
|
||||
|
||||
# Live-Logs
|
||||
nexredirect logs
|
||||
|
||||
# Letzte 50 Hits
|
||||
sudo nexredirect hits 50
|
||||
|
||||
# Update auf neueste Version
|
||||
sudo nexredirect update
|
||||
|
||||
# Update auf bestimmten Tag (auch downgrade)
|
||||
sudo nexredirect update v0.1.10
|
||||
|
||||
# Force-Update (gleiche Version neu installieren)
|
||||
sudo nexredirect update -f
|
||||
|
||||
# DB anschauen
|
||||
sudo nexredirect db
|
||||
sqlite> SELECT id, domain, status FROM domains;
|
||||
sqlite> .quit
|
||||
|
||||
# Caddyfile anzeigen
|
||||
nexredirect caddy show
|
||||
|
||||
# Backup
|
||||
sudo nexredirect backup
|
||||
sudo nexredirect backup /backup/nexredirect-$(date +%F).tgz
|
||||
```
|
||||
|
||||
## Service-User-Konzept
|
||||
|
||||
Service läuft als unprivileged user `nexredirect` (UID via `useradd --system`). Die meisten Commands brauchen `sudo` weil sie systemctl / DB-Schreibzugriff benötigen.
|
||||
|
||||
`sudo update.sh` ist über `/etc/sudoers.d/corex-nexredirect` als einzige Privileg-Eskalation erlaubt — vom Service-Process selbst aufrufbar wenn das UI "Update installieren" triggert.
|
||||
|
||||
→ Weiter mit [[API]]
|
||||
43
DNS-Setup.md
Normal file
43
DNS-Setup.md
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# DNS Setup
|
||||
|
||||
Damit eine Domain auf NexRedirect zeigt, müssen DNS-Records auf die Server-IP zeigen.
|
||||
|
||||
## Records
|
||||
|
||||
Beim Hinzufügen einer Domain im UI bekommst du genau angezeigt was einzutragen ist. Standard:
|
||||
|
||||
| Type | Name | Value |
|
||||
|---|---|---|
|
||||
| A | @ | `<server-ipv4>` |
|
||||
| AAAA | @ | `<server-ipv6>` _(optional)_ |
|
||||
| A | www | `<server-ipv4>` |
|
||||
|
||||
`@` steht für die Root-Domain. Manche DNS-Provider erwarten stattdessen den vollen Domain-Namen oder ein leeres Feld — egal, alle drei Schreibweisen meinen dasselbe.
|
||||
|
||||
## Validierung
|
||||
|
||||
Nach dem Eintragen klick im UI auf **Prüfen**. Server fragt selbst per `dns.resolve()` ab und vergleicht mit der eigenen IP. Bei Match → Domain wird auf `active` gestellt → Caddy reload → Auto-HTTPS-Cert via Let's Encrypt.
|
||||
|
||||
DNS-Propagation kann ein paar Minuten dauern. Nochmal prüfen → meistens da.
|
||||
|
||||
## DNS-Records-Übersicht
|
||||
|
||||
In der Domain-Detail-Page gibt's eine Card "DNS-Records" — listet **alle** Records die für diese Domain veröffentlicht sind: A, AAAA, CNAME, MX, NS, TXT, SOA, CAA. A/AAAA-Werte die auf den Server zeigen werden grün markiert.
|
||||
|
||||
Refresh-Button löst neue Abfrage aus.
|
||||
|
||||
## Auto-HTTPS
|
||||
|
||||
Sobald eine Domain `active` ist, holt Caddy automatisch ein Let's-Encrypt-Cert. Erste HTTPS-Anfrage kann ~10s dauern, danach gecacht.
|
||||
|
||||
Voraussetzung: Server muss aus dem Internet auf Port 80 erreichbar sein (für ACME-Challenge).
|
||||
|
||||
## Häufige Probleme
|
||||
|
||||
**DNS validiert nicht**: A-Record zeigt auf falsche IP, oder TTL noch nicht abgelaufen. `dig +short example.de` und `dig +short example.de @8.8.8.8` vergleichen.
|
||||
|
||||
**HTTPS funktioniert nicht trotz active**: Caddy braucht eingehend Port 80 frei. Firewall prüfen. Logs: `nexredirect logs | grep -i caddy` oder `journalctl -u caddy -n 50`.
|
||||
|
||||
**Cert-Renewal-Errors**: Let's Encrypt Rate-Limit (50/Woche pro Domain). Bei vielen Subdomains: Wildcard-Cert nötig — wird aktuell nicht unterstützt, jede Subdomain braucht eigenen Cert.
|
||||
|
||||
→ Weiter mit [[Domain Management]]
|
||||
52
Domain-Management.md
Normal file
52
Domain-Management.md
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# Domain Management
|
||||
|
||||
## Domain hinzufügen
|
||||
|
||||
`/domains/new` öffnen. 3 Schritte:
|
||||
|
||||
1. **Domain & Ziel**: Domain-Name + Ziel-URL (oder Gruppe), Status-Code (302 empfohlen), Pfad-Preserve, www-Subdomain
|
||||
2. **DNS-Records anzeigen**: A-Records eintragen wie angezeigt
|
||||
3. **Validieren**: Klick → Server prüft → bei Erfolg ist die Domain sofort aktiv
|
||||
|
||||
## Status-Code 301 vs 302
|
||||
|
||||
**302 (Default)**: Browser cached den Redirect nicht. Jeder Aufruf hittet den Server, Analytics zählt sauber. Dafür minimaler SEO-Verlust.
|
||||
|
||||
**301**: Browser cached permanent. Folge-Aufrufe gehen direkt zum Ziel ohne deinen Server zu sehen — Hit-Counter bleibt bei 1, egal wie oft. NexRedirect setzt zwar `Cache-Control: no-store`, viele Browser ignorieren das aber bei 301.
|
||||
|
||||
**Empfehlung**: 302 außer bei expliziter SEO-Strategie.
|
||||
|
||||
## Pfad-Preserve
|
||||
|
||||
`alt-domain.de/foo/bar` → wenn aktiv: `https://ziel.de/foo/bar`. Wenn aus: alle Pfade landen auf `https://ziel.de/`.
|
||||
|
||||
Bei Domains die nur als Vanity-Redirect dienen → Aus. Bei echten Migrationen → An.
|
||||
|
||||
## www-Subdomain
|
||||
|
||||
Aktiviert hinzufügen: `www.example.de` zeigt aufs gleiche Ziel. Caddy holt dafür ein zweites Cert.
|
||||
|
||||
## Gruppen
|
||||
|
||||
Mehrere Domains zum gleichen Ziel = Gruppe.
|
||||
|
||||
`/groups` → "Neue Gruppe" → Name + Ziel-URL. Beim Domain-Anlegen unter "Ziel" → "Gruppe" wählen statt Einzel-URL. Vorteil: Ziel-URL einmal ändern → alle Domains in der Gruppe folgen.
|
||||
|
||||
Edit-Funktion über Bleistift-Icon. Löschen blockiert solange Domains die Gruppe nutzen.
|
||||
|
||||
## Bulk-Aktionen
|
||||
|
||||
Auf `/domains` Checkbox-Spalte links. Auswahl löst Selection-Bar oben aus:
|
||||
|
||||
- **Löschen** mehrere Domains (Bestätigung zeigt Hit-Count)
|
||||
- **Sunset-Hinweis** für N Domains gleichzeitig konfigurieren (siehe [[Sunset Pages]])
|
||||
|
||||
## Domain bearbeiten
|
||||
|
||||
`/domains/<id>` → "Konfiguration"-Card hat ein inline Edit-Form: Ziel, Code, Gruppe, Pfad-Preserve, www. Speichern triggert automatisch Caddy-Reload.
|
||||
|
||||
## Domain löschen
|
||||
|
||||
Detail-Page → "Aktionen" → "Domain löschen". Confirm-Dialog warnt wie viele Hits mitgelöscht werden. Auch via [[CLI]]: `nexredirect db` und SQL.
|
||||
|
||||
→ Weiter mit [[Sunset Pages]]
|
||||
43
Home.md
Normal file
43
Home.md
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# CoreX NexRedirect
|
||||
|
||||
Self-hosted Domain-Redirect-Server mit Web-Admin-UI, Per-Domain-Analytics und One-Line Install.
|
||||
|
||||
## Was macht NexRedirect?
|
||||
|
||||
Du hast viele Domains, die nur auf eine andere Webseite weiterleiten sollen — und willst wissen, welche davon überhaupt noch genutzt werden? NexRedirect:
|
||||
|
||||
- nimmt alle deine Redirect-Domains entgegen (DNS auf den Server)
|
||||
- leitet jeden Aufruf konfigurierbar zum Ziel weiter (302/301)
|
||||
- protokolliert jeden echten Besuch (Bots gefiltert) mit Land, User-Agent, Referer
|
||||
- zeigt dir im Web-UI welche Domains tatsächlich genutzt werden — und welche tot sind
|
||||
|
||||
## Schnellstart
|
||||
|
||||
```bash
|
||||
curl -sSL https://forgejo.mgmt.corexmanagement.de/admin_hg/cx-nexredirect/main/scripts/install.sh | sudo bash
|
||||
```
|
||||
|
||||
Setup-Wizard danach: `http://<server-ip>/setup`
|
||||
|
||||
→ Detail: [[Installation]]
|
||||
|
||||
## Themen
|
||||
|
||||
- [[Installation]] — Server aufsetzen
|
||||
- [[DNS Setup]] — Domains korrekt verbinden
|
||||
- [[Domain Management]] — Domains, Gruppen, Sunset-Pages
|
||||
- [[Analytics & Reports]] — Hit-Tracking, PDF-Export, CSV-Export
|
||||
- [[Bot Filter]] — Wie echte Besucher von Scannern unterschieden werden
|
||||
- [[CLI]] — Server-CLI `nexredirect`
|
||||
- [[API]] — REST-API mit Token-Auth
|
||||
- [[Updates]] — Self-Update und Versions-Strategie
|
||||
- [[Architecture]] — Wie alles zusammenhängt
|
||||
- [[Troubleshooting]] — Häufige Probleme und Lösungen
|
||||
|
||||
## Stack
|
||||
|
||||
Next.js 15 + TypeScript + TailwindCSS + better-sqlite3 (eine Datei) + Caddy (Auto-HTTPS) + MaxMind GeoLite2 + Recharts. Läuft auf Debian/Ubuntu via systemd. Eine `.db`, eine `Caddyfile`, ein Node-Prozess.
|
||||
|
||||
## Lizenz
|
||||
|
||||
[MIT](https://forgejo.mgmt.corexmanagement.de/admin_hg/cx-nexredirect/blob/main/LICENSE) — viel Spaß damit.
|
||||
95
Installation.md
Normal file
95
Installation.md
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
# Installation
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
- Debian 11+ oder Ubuntu 20.04+ Server
|
||||
- Root-Zugriff (sudo)
|
||||
- Server-IP öffentlich erreichbar (Port 80 + 443 offen)
|
||||
- Mindestens 1 GB RAM, 5 GB Disk
|
||||
|
||||
## One-Line Install
|
||||
|
||||
```bash
|
||||
curl -sSL https://forgejo.mgmt.corexmanagement.de/admin_hg/cx-nexredirect/main/scripts/install.sh | sudo bash
|
||||
```
|
||||
|
||||
Das Script:
|
||||
|
||||
1. Detect Debian/Ubuntu
|
||||
2. `apt install`: curl, ca-certificates, gnupg, git, sqlite3, chromium, sudo
|
||||
3. Caddy via offiziellem Repo
|
||||
4. Node.js 20 via NodeSource
|
||||
5. Holt neuestes Release (Tag), zieht Prebuilt `.next`-Tarball aus GitHub-Release
|
||||
6. Legt Service-User `nexredirect` an, klont Repo nach `/opt/corex-nexredirect`
|
||||
7. systemd Unit + sudoers für `update.sh`
|
||||
8. Caddy Bootstrap-Config + reload
|
||||
9. CLI-Symlink `/usr/local/bin/nexredirect`
|
||||
10. Service start
|
||||
|
||||
Nach `Fertig!`-Meldung: Setup-Wizard unter `http://<server-ip>/setup` aufrufen, Admin-Account anlegen.
|
||||
|
||||
## Optional: GeoIP
|
||||
|
||||
Land pro Hit nur wenn MaxMind-DB installiert. Entweder beim Install:
|
||||
|
||||
```bash
|
||||
sudo MAXMIND_LICENSE_KEY=xxx ... | sudo -E bash
|
||||
```
|
||||
|
||||
…oder später im UI unter Einstellungen → GeoIP-Tracking → Account-ID + License-Key eintragen → Installieren.
|
||||
|
||||
[Lizenz-Key kostenlos hier](https://www.maxmind.com/en/geolite2/signup) generieren.
|
||||
|
||||
## Manueller Install
|
||||
|
||||
Wer das Curl-Pipe-Bash nicht mag:
|
||||
|
||||
```bash
|
||||
sudo apt install -y caddy nodejs git sqlite3 chromium
|
||||
sudo useradd --system --home /opt/corex-nexredirect --shell /usr/sbin/nologin nexredirect
|
||||
sudo mkdir -p /opt/corex-nexredirect /var/lib/corex-nexredirect
|
||||
sudo git clone https://forgejo.mgmt.corexmanagement.de/admin_hg/cx-nexredirect /opt/corex-nexredirect
|
||||
sudo chown -R nexredirect:nexredirect /opt/corex-nexredirect /var/lib/corex-nexredirect
|
||||
sudo -u nexredirect bash -c "cd /opt/corex-nexredirect && npm ci && npm run build"
|
||||
sudo cp /opt/corex-nexredirect/systemd/corex-nexredirect.service /etc/systemd/system/
|
||||
sudo chown nexredirect:nexredirect /etc/caddy/Caddyfile
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now caddy corex-nexredirect
|
||||
```
|
||||
|
||||
## Verzeichnisstruktur
|
||||
|
||||
| Pfad | Zweck |
|
||||
|---|---|
|
||||
| `/opt/corex-nexredirect` | Code (git checkout) |
|
||||
| `/var/lib/corex-nexredirect/nexredirect.db` | SQLite (alle Daten) |
|
||||
| `/var/lib/corex-nexredirect/GeoLite2-Country.mmdb` | Geo-DB (optional) |
|
||||
| `/etc/caddy/Caddyfile` | Caddy-Config (auto-generated) |
|
||||
| `/etc/systemd/system/corex-nexredirect.service` | systemd-Unit |
|
||||
| `/etc/sudoers.d/corex-nexredirect` | Sudo-Privileg für `update.sh` |
|
||||
| `/usr/local/bin/nexredirect` | CLI-Symlink |
|
||||
|
||||
## Backup
|
||||
|
||||
```bash
|
||||
nexredirect backup [/path/zu.tar.gz]
|
||||
```
|
||||
|
||||
Sichert `nexredirect.db` (+ WAL/SHM) und `Caddyfile`. Restore:
|
||||
|
||||
```bash
|
||||
sudo systemctl stop corex-nexredirect caddy
|
||||
sudo tar -xzf nexredirect-backup-XXX.tar.gz -C /
|
||||
sudo chown nexredirect:nexredirect /var/lib/corex-nexredirect/*.db
|
||||
sudo systemctl start caddy corex-nexredirect
|
||||
```
|
||||
|
||||
## Deinstallation
|
||||
|
||||
```bash
|
||||
sudo nexredirect uninstall
|
||||
```
|
||||
|
||||
Entfernt Service, Files, Sudoers, CLI. **DB unter `/var/lib/corex-nexredirect/` bleibt erhalten** — separat löschen wenn gewollt.
|
||||
|
||||
→ Weiter mit [[DNS Setup]]
|
||||
48
Sunset-Pages.md
Normal file
48
Sunset-Pages.md
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# Sunset Pages
|
||||
|
||||
Statt sofortigem Redirect kann eine **Hinweisseite** kommen — schlichte weiße Seite mit Custom-Text und einem "Weiter"-Button. Klickt der User → echter Redirect.
|
||||
|
||||
## Wann brauchst du das?
|
||||
|
||||
- **Domain wird abgeschaltet**: "Diese Domain wird zum 31.12.2026 abgeschaltet. Bitte verwende ab sofort https://neue-domain.de"
|
||||
- **Marken-Migration**: Hinweis dass die Firma umfirmiert hat
|
||||
- **Information** über kommenden Besitzerwechsel
|
||||
|
||||
## Konfiguration per Domain
|
||||
|
||||
Domain-Detail-Page → Card "Abschaltungs-Hinweis" → Toggle aktivieren:
|
||||
|
||||
| Feld | Beispiel |
|
||||
|---|---|
|
||||
| Titel | Diese Domain wird abgeschaltet |
|
||||
| Nachricht | Die Domain alt-firma.de wird zum 31.12.2026 abgeschaltet. Bitte verwenden Sie ab sofort https://neue-firma.de |
|
||||
| Button-Text | Weiter |
|
||||
| Abschaltdatum | 31.12.2026 _(optional, nur Anzeige)_ |
|
||||
|
||||
Speichern. Beim nächsten Aufruf der Domain bekommt der User die Notice statt Redirect. Klick auf "Weiter" → `?nr_continue=1` → echter Redirect.
|
||||
|
||||
## Bulk-Konfiguration
|
||||
|
||||
Auf `/domains` mehrere Domains markieren → Selection-Bar → "Sunset-Hinweis" → Form ausfüllen → "Auf N anwenden". Setzt für alle markierten Domains die gleiche Konfiguration.
|
||||
|
||||
Zum Deaktivieren für mehrere: Checkbox "Aktivieren" im Bulk-Dialog ausschalten und anwenden.
|
||||
|
||||
## Implementierung
|
||||
|
||||
Custom Server (`server.ts`) prüft beim Resolve eines Hosts:
|
||||
|
||||
1. Wenn Domain Sunset hat UND Request **kein** `?nr_continue=1` → HTML-Notice mit `Cache-Control: no-store`
|
||||
2. Wenn `?nr_continue=1` → ganz normaler 302/301 zum Target
|
||||
|
||||
Hit wird beim ERSTEN Request gezählt (Notice-Render). Continue-Click zählt NICHT als zweiter Hit (würde sonst doppeln).
|
||||
|
||||
HTML-Page ist in [`lib/sunset-html.ts`](https://forgejo.mgmt.corexmanagement.de/admin_hg/cx-nexredirect/blob/main/lib/sunset-html.ts) — schwarz auf weiß, kein JS, kein externes Asset, kein Tracking. Funktioniert auch ohne JS-Browser.
|
||||
|
||||
## Wichtige Hinweise
|
||||
|
||||
- Sunset-Page wird **nicht** vom Browser gecacht (`Cache-Control: no-store`)
|
||||
- Sunset-Page ist `noindex,nofollow` für Suchmaschinen
|
||||
- Robots.txt etc. werden NICHT durch die Notice geleitet, gehen direkt zum Target
|
||||
- Bei aktivem Sunset wird der Caddy-Fallback (bei App-Down: Direkt-Redirect via `handle_errors`) trotzdem ausgelöst — Notice nur wenn App läuft
|
||||
|
||||
→ Weiter mit [[Analytics & Reports]]
|
||||
136
Troubleshooting.md
Normal file
136
Troubleshooting.md
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
# Troubleshooting
|
||||
|
||||
## Service läuft nicht
|
||||
|
||||
```bash
|
||||
nexredirect status
|
||||
nexredirect logs -n 100
|
||||
```
|
||||
|
||||
Häufigste Ursachen:
|
||||
|
||||
- **Port 3000 belegt**: anderer Service auf 3000? `ss -tlnp | grep 3000`
|
||||
- **better-sqlite3 native module fehlt**: nach Update `sudo nexredirect update -f` zwingt npm ci
|
||||
- **DB-Dateirechte**: `ls -la /var/lib/corex-nexredirect/`. Sollte `nexredirect:nexredirect` gehören
|
||||
- **NEXTAUTH_SECRET nicht gesetzt**: `systemctl cat corex-nexredirect.service` prüfen
|
||||
|
||||
## /domains gibt "Application error"
|
||||
|
||||
Spalte fehlt (bei Update von alter Version). Logs prüfen:
|
||||
|
||||
```bash
|
||||
nexredirect logs -n 50 | grep SqliteError
|
||||
```
|
||||
|
||||
Falls `no such column`: Migration nicht durchgelaufen. v0.1.15+ hat Self-Healing (`PRAGMA table_info` Check). Update auf neueste Version + Restart:
|
||||
|
||||
```bash
|
||||
sudo nexredirect update
|
||||
```
|
||||
|
||||
## Domain-Add: DNS-Verify schlägt fehl
|
||||
|
||||
```bash
|
||||
dig +short example.de @8.8.8.8
|
||||
dig +short example.de @1.1.1.1
|
||||
```
|
||||
|
||||
Output muss die Server-IP sein. Falls nicht:
|
||||
|
||||
- DNS noch nicht propagiert (TTL warten)
|
||||
- A-Record falsch eingetragen (manche Provider erwarten leeres Name-Feld statt `@`)
|
||||
- DNSSEC-Problem beim Registrar
|
||||
|
||||
Server-IP korrekt? `nexredirect db` und `SELECT * FROM settings WHERE key LIKE 'server_%';`
|
||||
|
||||
## HTTPS funktioniert nicht trotz active
|
||||
|
||||
Caddy braucht eingehend Port 80 für ACME-Challenge:
|
||||
|
||||
```bash
|
||||
sudo nexredirect logs caddy 2>&1 | grep -i acme
|
||||
journalctl -u caddy -n 50 | grep -i error
|
||||
```
|
||||
|
||||
Häufigste Ursachen:
|
||||
|
||||
- Firewall blockt Port 80 (UFW / iptables)
|
||||
- Cloudflare-Proxy davor → Caddy bekommt CF-IP, ACME schlägt fehl. Cloudflare auf "DNS-only" stellen
|
||||
- Let's Encrypt Rate-Limit (50 Certs/Woche pro Domain)
|
||||
|
||||
## Caddyfile kann nicht geschrieben werden
|
||||
|
||||
Bei manueller Migration vor v0.1.9:
|
||||
|
||||
```bash
|
||||
sudo nexredirect caddy fix-perms
|
||||
sudo nexredirect caddy regen
|
||||
```
|
||||
|
||||
Setzt Owner auf Service-User und regeneriert + reloaded.
|
||||
|
||||
## Update läuft, UI zeigt aber alte Version
|
||||
|
||||
Browser-Cache. Hard-Reload (Ctrl+Shift+R) oder Inkognito. v0.1.5+ hat Auto-Reload nach Update integriert.
|
||||
|
||||
## Hit-Counter steigt nicht
|
||||
|
||||
Wenn Domain `redirect_code = 301`:
|
||||
Browser cached den Redirect, alle weiteren Aufrufe gehen direkt zum Ziel. NexRedirect setzt `Cache-Control: no-store`, viele Browser ignorieren das aber bei 301.
|
||||
|
||||
**Fix**: Auf 302 ändern. v0.1.7+ migriert beim ersten Boot automatisch.
|
||||
|
||||
## Hits werden gefiltert obwohl echte User
|
||||
|
||||
Modernere Filter (v0.1.19+) prüft Browser-Signal-Header. Sehr alte Browser ohne `Sec-Fetch-Mode` UND ohne `Accept-Language` werden gefiltert.
|
||||
|
||||
Im Hit-Log nachschauen: `nexredirect db` und `SELECT * FROM hits ORDER BY ts DESC LIMIT 50;`. Wenn der Browser geloggt wird, ist alles OK. Sonst Bot-Filter zu aggressiv.
|
||||
|
||||
## Update.sh: "dubious ownership"
|
||||
|
||||
Wenn `update.sh` als root aufgerufen wird aber Repo `nexredirect` gehört. Fix:
|
||||
|
||||
```bash
|
||||
sudo nexredirect update
|
||||
```
|
||||
|
||||
(CLI ruft update.sh über sudo auf und führt git als Service-User aus.) Bei Manuell:
|
||||
|
||||
```bash
|
||||
sudo -u nexredirect git -C /opt/corex-nexredirect pull --ff-only
|
||||
sudo /opt/corex-nexredirect/scripts/update.sh
|
||||
```
|
||||
|
||||
## PDF-Download: "chrome_not_found"
|
||||
|
||||
Chromium fehlt. Beim Update wird's installiert; ältere Installs:
|
||||
|
||||
```bash
|
||||
sudo apt install -y chromium
|
||||
```
|
||||
|
||||
Pfad in der App ist `/usr/bin/chromium` oder `/usr/bin/chromium-browser`. Override via `NEXREDIRECT_CHROME_PATH` in der systemd-Unit-File.
|
||||
|
||||
## Logs zu Caddy-Reload-Fehlern
|
||||
|
||||
```bash
|
||||
sudo nexredirect caddy show > /tmp/cur.caddyfile
|
||||
caddy validate --config /tmp/cur.caddyfile
|
||||
```
|
||||
|
||||
Validate-Output zeigt Syntax-Fehler. Bei Bedarf manuell editieren / Settings im UI korrigieren → `sudo nexredirect caddy regen`.
|
||||
|
||||
## Backup wiederherstellen
|
||||
|
||||
```bash
|
||||
sudo systemctl stop corex-nexredirect caddy
|
||||
sudo tar -xzf nexredirect-backup-XXX.tar.gz -C /
|
||||
sudo chown nexredirect:nexredirect /var/lib/corex-nexredirect/*.db*
|
||||
sudo systemctl start caddy corex-nexredirect
|
||||
```
|
||||
|
||||
## Fragen / Bugs
|
||||
|
||||
GitHub Issues: https://forgejo.mgmt.corexmanagement.de/admin_hg/cx-nexredirect/issues
|
||||
|
||||
→ Zurück zu [[Home]]
|
||||
78
Updates.md
Normal file
78
Updates.md
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
# Updates
|
||||
|
||||
NexRedirect prüft alle 60 Minuten gegen die GitHub-Releases-API auf neue Versionen. **Keine Auto-Updates** außer aktiviert.
|
||||
|
||||
## Update-Verhalten
|
||||
|
||||
| Setting | Verhalten |
|
||||
|---|---|
|
||||
| Default | Stündlicher Check, Banner in der UI bei neuem Release. Nichts wird ohne Klick installiert. |
|
||||
| `update_auto = true` | Bei jedem Check wird verfügbares Update sofort installiert. |
|
||||
| `update_include_prereleases = true` | Auch Pre-Releases als Update angezeigt. |
|
||||
|
||||
## Manuell aktualisieren
|
||||
|
||||
**UI:**
|
||||
|
||||
1. Banner oben oder Settings → "Update v0.1.X installieren" klicken
|
||||
2. Bestätigen
|
||||
3. UI zeigt Spinner, polled `/api/v1/health`, lädt Seite neu sobald Server zurück
|
||||
|
||||
**CLI:**
|
||||
|
||||
```bash
|
||||
sudo nexredirect update # auf neueste, skip wenn schon aktuell
|
||||
sudo nexredirect update v0.1.10 # auf bestimmten Tag (auch downgrade)
|
||||
sudo nexredirect update -f # erzwingen auch wenn gleiche Version
|
||||
```
|
||||
|
||||
## Update-Mechanik
|
||||
|
||||
1. `update.sh` prüft latest Release-Tag (oder nimmt übergebenen Tag)
|
||||
2. Skip wenn current === latest und kein `-f`
|
||||
3. `git fetch --tags && git checkout <tag>` als Service-User
|
||||
4. `npm ci`
|
||||
5. **Prebuilt `.next`-Tarball** aus Release-Asset ziehen — spart ~25s gegenüber lokal bauen
|
||||
6. Falls kein Asset (oder Download fehlschlägt): `npm run build` als Fallback
|
||||
7. CLI-Symlink + Permissions aktualisieren
|
||||
8. **Detached restart** in 2s (Hauptscript exitet zuerst sauber → API kann response zurückgeben → DANN restart)
|
||||
|
||||
## Auto-Update aktivieren
|
||||
|
||||
Settings → Toggle "Auto-Update aktivieren". Ab dann wird bei jedem stündlichen Check ein verfügbares Update direkt installiert.
|
||||
|
||||
**Empfehlung**: Nur in Test-Umgebungen. In Prod manuell prüfen, Release-Notes lesen.
|
||||
|
||||
## Rollback
|
||||
|
||||
```bash
|
||||
sudo nexredirect update v0.1.10 # auf Vorgänger-Version
|
||||
```
|
||||
|
||||
`update.sh` läuft `git checkout <tag>` egal ob vorwärts oder rückwärts. Schema-Migrationen sind additiv (`ALTER TABLE ADD COLUMN`) — Downgrade fragmenten ungenutzte Spalten ignorieren.
|
||||
|
||||
Falls Schema-Inkompatibilität: vorher Backup, ggf. DB händisch downgraden.
|
||||
|
||||
## Schema-Migrationen
|
||||
|
||||
Beim Start:
|
||||
|
||||
1. `ensureSchema(db)` legt fehlende Tabellen idempotent an (`CREATE TABLE IF NOT EXISTS`)
|
||||
2. `runMigrations(db)` läuft definierte Schritte (Settings-Flag-basiert + Schema-Check)
|
||||
|
||||
Aktuelle Migrationen:
|
||||
|
||||
- `m_301_to_302`: alle existierenden 301-Codes auf 302 ändern (Browser-Cache-Fix)
|
||||
- `sunset_config`-Spalte: Self-healing — prüft via `PRAGMA table_info` ob Spalte existiert, fügt hinzu wenn nicht
|
||||
|
||||
## Update-Log
|
||||
|
||||
Jeder Update-Versuch wird in der `update_log`-Tabelle protokolliert:
|
||||
|
||||
```sql
|
||||
SELECT ts, from_version, to_version, status FROM update_log ORDER BY ts DESC LIMIT 10;
|
||||
```
|
||||
|
||||
Status: `success` oder `failed` (Log-Auszug in der `log`-Spalte).
|
||||
|
||||
→ Weiter mit [[Architecture]]
|
||||
20
_Sidebar.md
Normal file
20
_Sidebar.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
**CoreX NexRedirect**
|
||||
|
||||
- [[Home]]
|
||||
- [[Installation]]
|
||||
- [[DNS Setup]]
|
||||
- [[Domain Management]]
|
||||
- [[Sunset Pages]]
|
||||
- [[Analytics & Reports]]
|
||||
- [[Bot Filter]]
|
||||
- [[CLI]]
|
||||
- [[API]]
|
||||
- [[Updates]]
|
||||
- [[Architecture]]
|
||||
- [[Troubleshooting]]
|
||||
|
||||
---
|
||||
|
||||
[Repo](https://forgejo.mgmt.corexmanagement.de/admin_hg/cx-nexredirect)
|
||||
[Releases](https://forgejo.mgmt.corexmanagement.de/admin_hg/cx-nexredirect/releases)
|
||||
[Issues](https://forgejo.mgmt.corexmanagement.de/admin_hg/cx-nexredirect/issues)
|
||||
Loading…
Reference in a new issue