From 74b3078baa36d0b641af5ad331a1b598ba22d27d Mon Sep 17 00:00:00 2001 From: Hans-Christian Payer Date: Mon, 27 Jul 2026 10:06:17 +0200 Subject: [PATCH] um zips und batchdateien bereinigt --- countdown_patch.zip | Bin 6731 -> 0 bytes pihole_patch (2)/proxy.conf.json | 37 ----- .../src/app/app-routing.module.ts | 18 --- pihole_patch (2)/src/app/app.component.html | 33 ----- pihole_patch (2)/src/app/app.module.ts | 28 ---- .../pihole-control.component.css | 98 -------------- .../pihole-control.component.html | 56 -------- .../pihole-control.component.ts | 120 ----------------- pihole_patch (2)/src/app/pihole.interface.ts | 21 --- pihole_patch (2)/src/app/pihole.service.ts | 126 ------------------ .../src/environments/environment.prod.ts | 8 -- .../src/environments/environment.ts | 9 -- pihole_patch (2)/webserver-docker/nginx.conf | 54 -------- pihole_patch.zip | Bin 9756 -> 0 bytes pihole_patch/proxy.conf.json | 37 ----- pihole_patch/src/app/app-routing.module.ts | 18 --- pihole_patch/src/app/app.component.html | 33 ----- pihole_patch/src/app/app.module.ts | 28 ---- .../pihole-control.component.css | 98 -------------- .../pihole-control.component.html | 56 -------- .../pihole-control.component.ts | 120 ----------------- pihole_patch/src/app/pihole.interface.ts | 21 --- pihole_patch/src/app/pihole.service.ts | 126 ------------------ .../src/environments/environment.prod.ts | 8 -- pihole_patch/src/environments/environment.ts | 9 -- pihole_patch/webserver-docker/nginx.conf | 54 -------- 26 files changed, 1216 deletions(-) delete mode 100644 countdown_patch.zip delete mode 100644 pihole_patch (2)/proxy.conf.json delete mode 100644 pihole_patch (2)/src/app/app-routing.module.ts delete mode 100644 pihole_patch (2)/src/app/app.component.html delete mode 100644 pihole_patch (2)/src/app/app.module.ts delete mode 100644 pihole_patch (2)/src/app/pihole-control/pihole-control.component.css delete mode 100644 pihole_patch (2)/src/app/pihole-control/pihole-control.component.html delete mode 100644 pihole_patch (2)/src/app/pihole-control/pihole-control.component.ts delete mode 100644 pihole_patch (2)/src/app/pihole.interface.ts delete mode 100644 pihole_patch (2)/src/app/pihole.service.ts delete mode 100644 pihole_patch (2)/src/environments/environment.prod.ts delete mode 100644 pihole_patch (2)/src/environments/environment.ts delete mode 100644 pihole_patch (2)/webserver-docker/nginx.conf delete mode 100644 pihole_patch.zip delete mode 100644 pihole_patch/proxy.conf.json delete mode 100644 pihole_patch/src/app/app-routing.module.ts delete mode 100644 pihole_patch/src/app/app.component.html delete mode 100644 pihole_patch/src/app/app.module.ts delete mode 100644 pihole_patch/src/app/pihole-control/pihole-control.component.css delete mode 100644 pihole_patch/src/app/pihole-control/pihole-control.component.html delete mode 100644 pihole_patch/src/app/pihole-control/pihole-control.component.ts delete mode 100644 pihole_patch/src/app/pihole.interface.ts delete mode 100644 pihole_patch/src/app/pihole.service.ts delete mode 100644 pihole_patch/src/environments/environment.prod.ts delete mode 100644 pihole_patch/src/environments/environment.ts delete mode 100644 pihole_patch/webserver-docker/nginx.conf diff --git a/countdown_patch.zip b/countdown_patch.zip deleted file mode 100644 index c0c8682a76540fc84f3011b2b26dc20c1ba073c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6731 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 - - - - - -
- -
- -
-
- Most of the stuff people worry about ain’t never gonna happen anyway. -
-
diff --git a/pihole_patch (2)/src/app/app.module.ts b/pihole_patch (2)/src/app/app.module.ts deleted file mode 100644 index 620ad38..0000000 --- a/pihole_patch (2)/src/app/app.module.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; - -import { AppRoutingModule } from './app-routing.module'; -import { AppComponent } from './app.component'; -import { HttpClientModule } from '@angular/common/http'; -import { LinksComponent } from './links/links.component'; -import * as _ from 'lodash'; -import { TasmotaControlComponent } from './tasmota-control/tasmota-control.component'; -import { PiholeControlComponent } from './pihole-control/pihole-control.component'; - - -@NgModule({ - declarations: [ - AppComponent, - LinksComponent, - TasmotaControlComponent, - PiholeControlComponent - ], - imports: [ - BrowserModule, - AppRoutingModule, - HttpClientModule - ], - providers: [], - bootstrap: [AppComponent] -}) -export class AppModule { } diff --git a/pihole_patch (2)/src/app/pihole-control/pihole-control.component.css b/pihole_patch (2)/src/app/pihole-control/pihole-control.component.css deleted file mode 100644 index c3ea46a..0000000 --- a/pihole_patch (2)/src/app/pihole-control/pihole-control.component.css +++ /dev/null @@ -1,98 +0,0 @@ -/* src/app/pihole-control/pihole-control.component.css */ -.pihole-control { - padding: 20px; -} - -.status-row { - margin: 15px 0; - font-size: 1.1rem; - display: flex; - align-items: center; - flex-wrap: wrap; - gap: 10px; -} - -.status-label { - font-weight: bold; -} - -/* Status-Badge: farbcodiert */ -.status-badge { - display: inline-block; - padding: 6px 14px; - border-radius: 14px; - color: white; - font-weight: bold; -} - -/* Blocking aktiv -> rot */ -.status-badge.blocking { - background-color: #d9534f; -} - -/* kein Blocking -> grün */ -.status-badge.no-blocking { - background-color: #28a745; -} - -/* unbekannt -> grau */ -.status-badge.unknown { - background-color: #888; -} - -.timer { - color: #555; - font-style: italic; -} - -.control-block { - margin: 20px 0; - display: flex; - flex-wrap: wrap; - gap: 10px; -} - -.control-block button { - padding: 10px 20px; - border-radius: 5px; - border: none; - cursor: pointer; - font-size: 1rem; -} - -.control-block button:disabled { - opacity: 0.6; - cursor: not-allowed; -} - -/* Haupt-Schalter: zeigt die AKTION an. - on (Blocking aktiv) -> Button schaltet AUS -> rötlich - off (kein Blocking) -> Button schaltet EIN -> grünlich */ -.btn-toggle.on { - background-color: #d9534f; - color: white; -} - -.btn-toggle.off { - background-color: #28a745; - color: white; -} - -.btn-pause { - background-color: #f0ad4e; - color: white; -} - -.btn-refresh { - background-color: #e0e0e0; - color: #333; -} - -.info { - color: #555; -} - -.error { - color: #d9534f; - font-weight: bold; -} diff --git a/pihole_patch (2)/src/app/pihole-control/pihole-control.component.html b/pihole_patch (2)/src/app/pihole-control/pihole-control.component.html deleted file mode 100644 index 17e4eb8..0000000 --- a/pihole_patch (2)/src/app/pihole-control/pihole-control.component.html +++ /dev/null @@ -1,56 +0,0 @@ - -
-

Pi-hole Steuerung

- - -
- Status: - - - unbekannt - - - - Blocking aktiv - - - - kein Blocking - - - - - (automatische Reaktivierung in {{ timerLabel }}) - -
- - -
- - - - - - -
- -

Bitte warten …

-

{{ errorMessage }}

-
diff --git a/pihole_patch (2)/src/app/pihole-control/pihole-control.component.ts b/pihole_patch (2)/src/app/pihole-control/pihole-control.component.ts deleted file mode 100644 index c3b79a1..0000000 --- a/pihole_patch (2)/src/app/pihole-control/pihole-control.component.ts +++ /dev/null @@ -1,120 +0,0 @@ -// src/app/pihole-control/pihole-control.component.ts -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { PiholeService } from '../pihole.service'; -import { PiholeBlockingStatus } from '../pihole.interface'; - -@Component({ - selector: 'app-pihole-control', - templateUrl: './pihole-control.component.html', - styleUrls: ['./pihole-control.component.css'] -}) -export class PiholeControlComponent implements OnInit, OnDestroy { - // Aktueller Status: true = Blocking aktiv (rot), false = kein Blocking (grün) - blockingEnabled: boolean | null = null; - - // Verbleibende Sekunden bis Auto-Revert (z.B. bei 30-Min-Pause), sonst null - timer: number | null = null; - - loading = false; - errorMessage: string | null = null; - - // Lokaler Countdown-Timer für die Anzeige der verbleibenden Pausenzeit - private countdownHandle: any = null; - - constructor(private piholeService: PiholeService) {} - - ngOnInit(): void { - this.refreshStatus(); - } - - ngOnDestroy(): void { - this.clearCountdown(); - } - - /** Status vom Pi-hole abrufen. */ - refreshStatus(): void { - this.loading = true; - this.errorMessage = null; - this.piholeService.getStatus().subscribe({ - next: (status) => this.applyStatus(status), - error: (err) => this.handleError('Status konnte nicht abgerufen werden.', err) - }); - } - - /** Blocking ein- bzw. ausschalten (Haupt-Schalter). */ - toggleBlocking(): void { - // Wenn Status noch unbekannt, zuerst aktualisieren - if (this.blockingEnabled === null) { - this.refreshStatus(); - return; - } - this.loading = true; - this.errorMessage = null; - const action = this.blockingEnabled - ? this.piholeService.disableBlocking() - : this.piholeService.enableBlocking(); - - action.subscribe({ - next: (status) => this.applyStatus(status), - error: (err) => this.handleError('Schalten fehlgeschlagen.', err) - }); - } - - /** Blocking für 30 Minuten pausieren (Auto-Revert durch Pi-hole). */ - disableFor30Minutes(): void { - this.loading = true; - this.errorMessage = null; - this.piholeService.disableFor30Minutes().subscribe({ - next: (status) => this.applyStatus(status), - error: (err) => this.handleError('30-Minuten-Pause fehlgeschlagen.', err) - }); - } - - // --- intern --------------------------------------------------------------- - - private applyStatus(status: PiholeBlockingStatus): void { - this.loading = false; - this.blockingEnabled = status.blocking === 'enabled'; - this.timer = status.timer ?? null; - this.startCountdown(); - } - - private handleError(message: string, err: any): void { - this.loading = false; - this.errorMessage = message; - console.error(message, err); - } - - // Lokaler 1s-Countdown nur für die Anzeige; nach Ablauf Status neu laden - private startCountdown(): void { - this.clearCountdown(); - if (this.timer && this.timer > 0) { - this.countdownHandle = setInterval(() => { - if (this.timer !== null && this.timer > 0) { - this.timer--; - if (this.timer <= 0) { - this.clearCountdown(); - this.refreshStatus(); // Pi-hole hat vermutlich automatisch reaktiviert - } - } - }, 1000); - } - } - - private clearCountdown(): void { - if (this.countdownHandle) { - clearInterval(this.countdownHandle); - this.countdownHandle = null; - } - } - - /** mm:ss-Formatierung für die Timer-Anzeige. */ - get timerLabel(): string { - if (!this.timer || this.timer <= 0) { - return ''; - } - const m = Math.floor(this.timer / 60); - const s = this.timer % 60; - return `${m}:${s.toString().padStart(2, '0')}`; - } -} diff --git a/pihole_patch (2)/src/app/pihole.interface.ts b/pihole_patch (2)/src/app/pihole.interface.ts deleted file mode 100644 index d969fbd..0000000 --- a/pihole_patch (2)/src/app/pihole.interface.ts +++ /dev/null @@ -1,21 +0,0 @@ -// src/app/pihole.interface.ts - -// Antwort von GET /api/dns/blocking -export interface PiholeBlockingStatus { - blocking: 'enabled' | 'disabled' | string; // FTL liefert "enabled"/"disabled" - timer: number | null; // verbleibende Sekunden bis Auto-Revert, sonst null - took?: number; -} - -// Antwort von POST /api/auth -export interface PiholeAuthResponse { - session: { - valid: boolean; - totp?: boolean; - sid: string | null; - csrf?: string | null; - validity: number; // Gültigkeit der SID in Sekunden - message?: string | null; - }; - took?: number; -} diff --git a/pihole_patch (2)/src/app/pihole.service.ts b/pihole_patch (2)/src/app/pihole.service.ts deleted file mode 100644 index db3ddff..0000000 --- a/pihole_patch (2)/src/app/pihole.service.ts +++ /dev/null @@ -1,126 +0,0 @@ -// src/app/pihole.service.ts -import { Injectable } from '@angular/core'; -import { HttpClient, HttpHeaders } from '@angular/common/http'; -import { Observable, of, throwError } from 'rxjs'; -import { map, switchMap, catchError, tap } from 'rxjs/operators'; -import { environment } from '../environments/environment'; -import { PiholeBlockingStatus, PiholeAuthResponse } from './pihole.interface'; - -@Injectable({ - providedIn: 'root' -}) -export class PiholeService { - // Basis-Pfad: im Dev über den Angular-Proxy (proxy.conf.json -> /pihole), - // in Prod über den nginx-Reverse-Proxy (location /pihole/). - // So bleibt das Pi-hole-Passwort serverseitig und landet nicht im JS-Bundle für Fremde. - private apiBase = '/pihole/api'; - - // Aktuelle Session-ID (SID) aus POST /api/auth. Wird bei 401 erneuert. - private sid: string | null = null; - - constructor(private http: HttpClient) {} - - // --- Authentifizierung ---------------------------------------------------- - - /** - * Holt eine neue Session-ID per POST /api/auth. - * Das Passwort/App-Passwort kommt aus environment (nur im internen LAN-Build). - */ - private authenticate(): Observable { - const body = { password: environment.piholePassword }; - return this.http - .post(`${this.apiBase}/auth`, body, { - headers: new HttpHeaders({ 'Content-Type': 'application/json' }) - }) - .pipe( - map((res) => { - if (!res?.session?.valid || !res.session.sid) { - throw new Error('Pi-hole-Authentifizierung fehlgeschlagen (kein gültiges SID).'); - } - return res.session.sid; - }), - tap((sid) => (this.sid = sid)) - ); - } - - /** Liefert eine gültige SID (vorhandene oder frisch geholte). */ - private ensureSid(): Observable { - return this.sid ? of(this.sid) : this.authenticate(); - } - - /** HTTP-Header inkl. SID für authentifizierte Requests. */ - private authHeaders(sid: string): HttpHeaders { - return new HttpHeaders({ - 'Content-Type': 'application/json', - 'X-FTL-SID': sid - }); - } - - /** - * Führt einen authentifizierten Request aus und erneuert die SID - * automatisch einmal, falls sie abgelaufen ist (401). - */ - private withAuth(fn: (sid: string) => Observable): Observable { - return this.ensureSid().pipe( - switchMap((sid) => fn(sid)), - catchError((err) => { - if (err?.status === 401) { - // SID abgelaufen -> verwerfen und einmalig neu anmelden - this.sid = null; - return this.authenticate().pipe(switchMap((sid) => fn(sid))); - } - return throwError(() => err); - }) - ); - } - - // --- Blocking-Status ------------------------------------------------------ - - /** Liest den aktuellen Blocking-Status: true = Blocking aktiv. */ - getStatus(): Observable { - return this.withAuth((sid) => - this.http.get(`${this.apiBase}/dns/blocking`, { - headers: this.authHeaders(sid) - }) - ); - } - - /** Convenience: true wenn Blocking aktiv ist. */ - isBlockingEnabled(): Observable { - return this.getStatus().pipe(map((s) => s.blocking === 'enabled')); - } - - // --- Blocking schalten ---------------------------------------------------- - - /** - * Setzt den Blocking-Zustand. - * @param enabled true = Blocking an, false = Blocking aus - * @param timerSeconds optionaler Auto-Revert-Timer in Sekunden (null = dauerhaft) - */ - setBlocking(enabled: boolean, timerSeconds: number | null = null): Observable { - const body: { blocking: boolean; timer: number | null } = { - blocking: enabled, - timer: timerSeconds - }; - return this.withAuth((sid) => - this.http.post(`${this.apiBase}/dns/blocking`, body, { - headers: this.authHeaders(sid) - }) - ); - } - - /** Blocking dauerhaft einschalten. */ - enableBlocking(): Observable { - return this.setBlocking(true, null); - } - - /** Blocking dauerhaft ausschalten. */ - disableBlocking(): Observable { - return this.setBlocking(false, null); - } - - /** Blocking für 30 Minuten deaktivieren (Auto-Revert). */ - disableFor30Minutes(): Observable { - return this.setBlocking(false, 30 * 60); - } -} diff --git a/pihole_patch (2)/src/environments/environment.prod.ts b/pihole_patch (2)/src/environments/environment.prod.ts deleted file mode 100644 index 477c841..0000000 --- a/pihole_patch (2)/src/environments/environment.prod.ts +++ /dev/null @@ -1,8 +0,0 @@ -// environments/environment.prod.ts -export const environment = { - production: true, - apiUrl: 'http://10.0.0.104',// Produktions-URL (leer wenn gleicher Server) - // Pi-hole v6: App-Passwort (Settings > API > App password). - // ACHTUNG: Im LAN-Build akzeptabel, aber Wert nicht öffentlich exponieren. - piholePassword: 'BITTE_PIHOLE_APP_PASSWORT_EINTRAGEN' -}; diff --git a/pihole_patch (2)/src/environments/environment.ts b/pihole_patch (2)/src/environments/environment.ts deleted file mode 100644 index b466b9a..0000000 --- a/pihole_patch (2)/src/environments/environment.ts +++ /dev/null @@ -1,9 +0,0 @@ -// environments/environment.ts -export const environment = { - production: false, - //apiUrl: 'http://localhost:4200' // Entwicklungs-URL - apiUrl: 'http://10.0.0.104', - // Pi-hole v6: App-Passwort (Settings > API > App password). - // ACHTUNG: Im LAN-Build akzeptabel, aber Wert nicht öffentlich exponieren. - piholePassword: 'BITTE_PIHOLE_APP_PASSWORT_EINTRAGEN' -}; diff --git a/pihole_patch (2)/webserver-docker/nginx.conf b/pihole_patch (2)/webserver-docker/nginx.conf deleted file mode 100644 index 8b00465..0000000 --- a/pihole_patch (2)/webserver-docker/nginx.conf +++ /dev/null @@ -1,54 +0,0 @@ -server { - listen 80; - server_name _; - - root /usr/share/nginx/html; - index index.html; - - charset utf-8; - - gzip on; - gzip_vary on; - gzip_proxied any; - gzip_comp_level 6; - gzip_min_length 1024; - gzip_types - text/plain - text/css - text/javascript - application/javascript - application/json - application/xml - application/rss+xml - image/svg+xml; - - # Pi-hole v6 REST API: /pihole/... wird auf den Pi-hole-Host weitergeleitet. - # Der Browser spricht nur diesen gleichen Origin an (kein CORS/Mixed-Content). - location /pihole/ { - proxy_pass http://10.0.0.60/; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - location / { - try_files $uri $uri/ /index.html =404; - } - - location ~* \.(?:js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ { - expires 30d; - add_header Cache-Control "public, max-age=2592000, immutable"; - try_files $uri =404; - } - - location = /index.html { - add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate"; - expires -1; - } - - error_page 500 502 503 504 /50x.html; - location = /50x.html { - internal; - } -} diff --git a/pihole_patch.zip b/pihole_patch.zip deleted file mode 100644 index 630c3cb755035be54f31ee81be07b1abb9bd8530..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9756 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 b1^hX^{v9a7O>6@IaBlv#H&2OVKi>WiBe~&! diff --git a/pihole_patch/proxy.conf.json b/pihole_patch/proxy.conf.json deleted file mode 100644 index 9353bd9..0000000 --- a/pihole_patch/proxy.conf.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "/tasmota1": { - "target": "http://10.0.0.31", - "secure": false, - "changeOrigin": true, - "pathRewrite": { - "^/tasmota1": "" - } - }, - "/tasmota2": { - "target": "http://10.0.0.32", - "secure": false, - "changeOrigin": true, - "pathRewrite": { - "^/tasmota2": "" - } - }, - "/pihole": { - "target": "http://10.0.0.60", - "secure": false, - "changeOrigin": true, - "pathRewrite": { - "^/pihole": "" - } - }, - "/api": { - "target": "http://directus.neugasse.lan", - "secure": false, - "changeOrigin": true, - "pathRewrite": { - "^/api": "" - }, - "headers": { - "Authorization": "Bearer KGjpxZ3tesmhEXHS7C7JbKDyW6YJlgQT" - } - } -} diff --git a/pihole_patch/src/app/app-routing.module.ts b/pihole_patch/src/app/app-routing.module.ts deleted file mode 100644 index c546569..0000000 --- a/pihole_patch/src/app/app-routing.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { LinksComponent } from './links/links.component'; -import { TasmotaControlComponent } from './tasmota-control/tasmota-control.component'; -import { PiholeControlComponent } from './pihole-control/pihole-control.component'; - -const routes: Routes = [ - { path: '', redirectTo: '/list-links', pathMatch: 'full'}, - { path: 'list-links', component:LinksComponent }, - { path: 'steckdosen', component:TasmotaControlComponent}, - { path: 'pihole', component:PiholeControlComponent} -]; - -@NgModule({ - imports: [RouterModule.forRoot(routes, {useHash: true})], - exports: [RouterModule] -}) -export class AppRoutingModule { } diff --git a/pihole_patch/src/app/app.component.html b/pihole_patch/src/app/app.component.html deleted file mode 100644 index b1b424b..0000000 --- a/pihole_patch/src/app/app.component.html +++ /dev/null @@ -1,33 +0,0 @@ -
- - -
- - -
- -
- -
-
- Most of the stuff people worry about ain’t never gonna happen anyway. -
-
diff --git a/pihole_patch/src/app/app.module.ts b/pihole_patch/src/app/app.module.ts deleted file mode 100644 index 620ad38..0000000 --- a/pihole_patch/src/app/app.module.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; - -import { AppRoutingModule } from './app-routing.module'; -import { AppComponent } from './app.component'; -import { HttpClientModule } from '@angular/common/http'; -import { LinksComponent } from './links/links.component'; -import * as _ from 'lodash'; -import { TasmotaControlComponent } from './tasmota-control/tasmota-control.component'; -import { PiholeControlComponent } from './pihole-control/pihole-control.component'; - - -@NgModule({ - declarations: [ - AppComponent, - LinksComponent, - TasmotaControlComponent, - PiholeControlComponent - ], - imports: [ - BrowserModule, - AppRoutingModule, - HttpClientModule - ], - providers: [], - bootstrap: [AppComponent] -}) -export class AppModule { } diff --git a/pihole_patch/src/app/pihole-control/pihole-control.component.css b/pihole_patch/src/app/pihole-control/pihole-control.component.css deleted file mode 100644 index c3ea46a..0000000 --- a/pihole_patch/src/app/pihole-control/pihole-control.component.css +++ /dev/null @@ -1,98 +0,0 @@ -/* src/app/pihole-control/pihole-control.component.css */ -.pihole-control { - padding: 20px; -} - -.status-row { - margin: 15px 0; - font-size: 1.1rem; - display: flex; - align-items: center; - flex-wrap: wrap; - gap: 10px; -} - -.status-label { - font-weight: bold; -} - -/* Status-Badge: farbcodiert */ -.status-badge { - display: inline-block; - padding: 6px 14px; - border-radius: 14px; - color: white; - font-weight: bold; -} - -/* Blocking aktiv -> rot */ -.status-badge.blocking { - background-color: #d9534f; -} - -/* kein Blocking -> grün */ -.status-badge.no-blocking { - background-color: #28a745; -} - -/* unbekannt -> grau */ -.status-badge.unknown { - background-color: #888; -} - -.timer { - color: #555; - font-style: italic; -} - -.control-block { - margin: 20px 0; - display: flex; - flex-wrap: wrap; - gap: 10px; -} - -.control-block button { - padding: 10px 20px; - border-radius: 5px; - border: none; - cursor: pointer; - font-size: 1rem; -} - -.control-block button:disabled { - opacity: 0.6; - cursor: not-allowed; -} - -/* Haupt-Schalter: zeigt die AKTION an. - on (Blocking aktiv) -> Button schaltet AUS -> rötlich - off (kein Blocking) -> Button schaltet EIN -> grünlich */ -.btn-toggle.on { - background-color: #d9534f; - color: white; -} - -.btn-toggle.off { - background-color: #28a745; - color: white; -} - -.btn-pause { - background-color: #f0ad4e; - color: white; -} - -.btn-refresh { - background-color: #e0e0e0; - color: #333; -} - -.info { - color: #555; -} - -.error { - color: #d9534f; - font-weight: bold; -} diff --git a/pihole_patch/src/app/pihole-control/pihole-control.component.html b/pihole_patch/src/app/pihole-control/pihole-control.component.html deleted file mode 100644 index 17e4eb8..0000000 --- a/pihole_patch/src/app/pihole-control/pihole-control.component.html +++ /dev/null @@ -1,56 +0,0 @@ - -
-

Pi-hole Steuerung

- - -
- Status: - - - unbekannt - - - - Blocking aktiv - - - - kein Blocking - - - - - (automatische Reaktivierung in {{ timerLabel }}) - -
- - -
- - - - - - -
- -

Bitte warten …

-

{{ errorMessage }}

-
diff --git a/pihole_patch/src/app/pihole-control/pihole-control.component.ts b/pihole_patch/src/app/pihole-control/pihole-control.component.ts deleted file mode 100644 index c3b79a1..0000000 --- a/pihole_patch/src/app/pihole-control/pihole-control.component.ts +++ /dev/null @@ -1,120 +0,0 @@ -// src/app/pihole-control/pihole-control.component.ts -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { PiholeService } from '../pihole.service'; -import { PiholeBlockingStatus } from '../pihole.interface'; - -@Component({ - selector: 'app-pihole-control', - templateUrl: './pihole-control.component.html', - styleUrls: ['./pihole-control.component.css'] -}) -export class PiholeControlComponent implements OnInit, OnDestroy { - // Aktueller Status: true = Blocking aktiv (rot), false = kein Blocking (grün) - blockingEnabled: boolean | null = null; - - // Verbleibende Sekunden bis Auto-Revert (z.B. bei 30-Min-Pause), sonst null - timer: number | null = null; - - loading = false; - errorMessage: string | null = null; - - // Lokaler Countdown-Timer für die Anzeige der verbleibenden Pausenzeit - private countdownHandle: any = null; - - constructor(private piholeService: PiholeService) {} - - ngOnInit(): void { - this.refreshStatus(); - } - - ngOnDestroy(): void { - this.clearCountdown(); - } - - /** Status vom Pi-hole abrufen. */ - refreshStatus(): void { - this.loading = true; - this.errorMessage = null; - this.piholeService.getStatus().subscribe({ - next: (status) => this.applyStatus(status), - error: (err) => this.handleError('Status konnte nicht abgerufen werden.', err) - }); - } - - /** Blocking ein- bzw. ausschalten (Haupt-Schalter). */ - toggleBlocking(): void { - // Wenn Status noch unbekannt, zuerst aktualisieren - if (this.blockingEnabled === null) { - this.refreshStatus(); - return; - } - this.loading = true; - this.errorMessage = null; - const action = this.blockingEnabled - ? this.piholeService.disableBlocking() - : this.piholeService.enableBlocking(); - - action.subscribe({ - next: (status) => this.applyStatus(status), - error: (err) => this.handleError('Schalten fehlgeschlagen.', err) - }); - } - - /** Blocking für 30 Minuten pausieren (Auto-Revert durch Pi-hole). */ - disableFor30Minutes(): void { - this.loading = true; - this.errorMessage = null; - this.piholeService.disableFor30Minutes().subscribe({ - next: (status) => this.applyStatus(status), - error: (err) => this.handleError('30-Minuten-Pause fehlgeschlagen.', err) - }); - } - - // --- intern --------------------------------------------------------------- - - private applyStatus(status: PiholeBlockingStatus): void { - this.loading = false; - this.blockingEnabled = status.blocking === 'enabled'; - this.timer = status.timer ?? null; - this.startCountdown(); - } - - private handleError(message: string, err: any): void { - this.loading = false; - this.errorMessage = message; - console.error(message, err); - } - - // Lokaler 1s-Countdown nur für die Anzeige; nach Ablauf Status neu laden - private startCountdown(): void { - this.clearCountdown(); - if (this.timer && this.timer > 0) { - this.countdownHandle = setInterval(() => { - if (this.timer !== null && this.timer > 0) { - this.timer--; - if (this.timer <= 0) { - this.clearCountdown(); - this.refreshStatus(); // Pi-hole hat vermutlich automatisch reaktiviert - } - } - }, 1000); - } - } - - private clearCountdown(): void { - if (this.countdownHandle) { - clearInterval(this.countdownHandle); - this.countdownHandle = null; - } - } - - /** mm:ss-Formatierung für die Timer-Anzeige. */ - get timerLabel(): string { - if (!this.timer || this.timer <= 0) { - return ''; - } - const m = Math.floor(this.timer / 60); - const s = this.timer % 60; - return `${m}:${s.toString().padStart(2, '0')}`; - } -} diff --git a/pihole_patch/src/app/pihole.interface.ts b/pihole_patch/src/app/pihole.interface.ts deleted file mode 100644 index d969fbd..0000000 --- a/pihole_patch/src/app/pihole.interface.ts +++ /dev/null @@ -1,21 +0,0 @@ -// src/app/pihole.interface.ts - -// Antwort von GET /api/dns/blocking -export interface PiholeBlockingStatus { - blocking: 'enabled' | 'disabled' | string; // FTL liefert "enabled"/"disabled" - timer: number | null; // verbleibende Sekunden bis Auto-Revert, sonst null - took?: number; -} - -// Antwort von POST /api/auth -export interface PiholeAuthResponse { - session: { - valid: boolean; - totp?: boolean; - sid: string | null; - csrf?: string | null; - validity: number; // Gültigkeit der SID in Sekunden - message?: string | null; - }; - took?: number; -} diff --git a/pihole_patch/src/app/pihole.service.ts b/pihole_patch/src/app/pihole.service.ts deleted file mode 100644 index db3ddff..0000000 --- a/pihole_patch/src/app/pihole.service.ts +++ /dev/null @@ -1,126 +0,0 @@ -// src/app/pihole.service.ts -import { Injectable } from '@angular/core'; -import { HttpClient, HttpHeaders } from '@angular/common/http'; -import { Observable, of, throwError } from 'rxjs'; -import { map, switchMap, catchError, tap } from 'rxjs/operators'; -import { environment } from '../environments/environment'; -import { PiholeBlockingStatus, PiholeAuthResponse } from './pihole.interface'; - -@Injectable({ - providedIn: 'root' -}) -export class PiholeService { - // Basis-Pfad: im Dev über den Angular-Proxy (proxy.conf.json -> /pihole), - // in Prod über den nginx-Reverse-Proxy (location /pihole/). - // So bleibt das Pi-hole-Passwort serverseitig und landet nicht im JS-Bundle für Fremde. - private apiBase = '/pihole/api'; - - // Aktuelle Session-ID (SID) aus POST /api/auth. Wird bei 401 erneuert. - private sid: string | null = null; - - constructor(private http: HttpClient) {} - - // --- Authentifizierung ---------------------------------------------------- - - /** - * Holt eine neue Session-ID per POST /api/auth. - * Das Passwort/App-Passwort kommt aus environment (nur im internen LAN-Build). - */ - private authenticate(): Observable { - const body = { password: environment.piholePassword }; - return this.http - .post(`${this.apiBase}/auth`, body, { - headers: new HttpHeaders({ 'Content-Type': 'application/json' }) - }) - .pipe( - map((res) => { - if (!res?.session?.valid || !res.session.sid) { - throw new Error('Pi-hole-Authentifizierung fehlgeschlagen (kein gültiges SID).'); - } - return res.session.sid; - }), - tap((sid) => (this.sid = sid)) - ); - } - - /** Liefert eine gültige SID (vorhandene oder frisch geholte). */ - private ensureSid(): Observable { - return this.sid ? of(this.sid) : this.authenticate(); - } - - /** HTTP-Header inkl. SID für authentifizierte Requests. */ - private authHeaders(sid: string): HttpHeaders { - return new HttpHeaders({ - 'Content-Type': 'application/json', - 'X-FTL-SID': sid - }); - } - - /** - * Führt einen authentifizierten Request aus und erneuert die SID - * automatisch einmal, falls sie abgelaufen ist (401). - */ - private withAuth(fn: (sid: string) => Observable): Observable { - return this.ensureSid().pipe( - switchMap((sid) => fn(sid)), - catchError((err) => { - if (err?.status === 401) { - // SID abgelaufen -> verwerfen und einmalig neu anmelden - this.sid = null; - return this.authenticate().pipe(switchMap((sid) => fn(sid))); - } - return throwError(() => err); - }) - ); - } - - // --- Blocking-Status ------------------------------------------------------ - - /** Liest den aktuellen Blocking-Status: true = Blocking aktiv. */ - getStatus(): Observable { - return this.withAuth((sid) => - this.http.get(`${this.apiBase}/dns/blocking`, { - headers: this.authHeaders(sid) - }) - ); - } - - /** Convenience: true wenn Blocking aktiv ist. */ - isBlockingEnabled(): Observable { - return this.getStatus().pipe(map((s) => s.blocking === 'enabled')); - } - - // --- Blocking schalten ---------------------------------------------------- - - /** - * Setzt den Blocking-Zustand. - * @param enabled true = Blocking an, false = Blocking aus - * @param timerSeconds optionaler Auto-Revert-Timer in Sekunden (null = dauerhaft) - */ - setBlocking(enabled: boolean, timerSeconds: number | null = null): Observable { - const body: { blocking: boolean; timer: number | null } = { - blocking: enabled, - timer: timerSeconds - }; - return this.withAuth((sid) => - this.http.post(`${this.apiBase}/dns/blocking`, body, { - headers: this.authHeaders(sid) - }) - ); - } - - /** Blocking dauerhaft einschalten. */ - enableBlocking(): Observable { - return this.setBlocking(true, null); - } - - /** Blocking dauerhaft ausschalten. */ - disableBlocking(): Observable { - return this.setBlocking(false, null); - } - - /** Blocking für 30 Minuten deaktivieren (Auto-Revert). */ - disableFor30Minutes(): Observable { - return this.setBlocking(false, 30 * 60); - } -} diff --git a/pihole_patch/src/environments/environment.prod.ts b/pihole_patch/src/environments/environment.prod.ts deleted file mode 100644 index 477c841..0000000 --- a/pihole_patch/src/environments/environment.prod.ts +++ /dev/null @@ -1,8 +0,0 @@ -// environments/environment.prod.ts -export const environment = { - production: true, - apiUrl: 'http://10.0.0.104',// Produktions-URL (leer wenn gleicher Server) - // Pi-hole v6: App-Passwort (Settings > API > App password). - // ACHTUNG: Im LAN-Build akzeptabel, aber Wert nicht öffentlich exponieren. - piholePassword: 'BITTE_PIHOLE_APP_PASSWORT_EINTRAGEN' -}; diff --git a/pihole_patch/src/environments/environment.ts b/pihole_patch/src/environments/environment.ts deleted file mode 100644 index b466b9a..0000000 --- a/pihole_patch/src/environments/environment.ts +++ /dev/null @@ -1,9 +0,0 @@ -// environments/environment.ts -export const environment = { - production: false, - //apiUrl: 'http://localhost:4200' // Entwicklungs-URL - apiUrl: 'http://10.0.0.104', - // Pi-hole v6: App-Passwort (Settings > API > App password). - // ACHTUNG: Im LAN-Build akzeptabel, aber Wert nicht öffentlich exponieren. - piholePassword: 'BITTE_PIHOLE_APP_PASSWORT_EINTRAGEN' -}; diff --git a/pihole_patch/webserver-docker/nginx.conf b/pihole_patch/webserver-docker/nginx.conf deleted file mode 100644 index 8b00465..0000000 --- a/pihole_patch/webserver-docker/nginx.conf +++ /dev/null @@ -1,54 +0,0 @@ -server { - listen 80; - server_name _; - - root /usr/share/nginx/html; - index index.html; - - charset utf-8; - - gzip on; - gzip_vary on; - gzip_proxied any; - gzip_comp_level 6; - gzip_min_length 1024; - gzip_types - text/plain - text/css - text/javascript - application/javascript - application/json - application/xml - application/rss+xml - image/svg+xml; - - # Pi-hole v6 REST API: /pihole/... wird auf den Pi-hole-Host weitergeleitet. - # Der Browser spricht nur diesen gleichen Origin an (kein CORS/Mixed-Content). - location /pihole/ { - proxy_pass http://10.0.0.60/; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - location / { - try_files $uri $uri/ /index.html =404; - } - - location ~* \.(?:js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ { - expires 30d; - add_header Cache-Control "public, max-age=2592000, immutable"; - try_files $uri =404; - } - - location = /index.html { - add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate"; - expires -1; - } - - error_page 500 502 503 504 /50x.html; - location = /50x.html { - internal; - } -}