deutsche Zeit statt UTC

Dieser Commit ist enthalten in:
2025-06-08 20:21:58 +02:00
Ursprung f9e5823eeb
Commit f269082115
19 geänderte Dateien mit 1411 neuen und 31 gelöschten Zeilen

Datei anzeigen

@@ -48,7 +48,8 @@
"Bash(find:*)", "Bash(find:*)",
"Bash(openssl x509:*)", "Bash(openssl x509:*)",
"Bash(cat:*)", "Bash(cat:*)",
"Bash(openssl dhparam:*)" "Bash(openssl dhparam:*)",
"Bash(rg:*)"
], ],
"deny": [] "deny": []
} }

Datei anzeigen

@@ -1211,3 +1211,84 @@ Die Session-Daten werden erst gefüllt, wenn der License Server API implementier
- ✅ CAPTCHA wird nur angezeigt wenn Keys konfiguriert sind - ✅ CAPTCHA wird nur angezeigt wenn Keys konfiguriert sind
- ✅ Für PoC-Phase ohne CAPTCHA nutzbar - ✅ Für PoC-Phase ohne CAPTCHA nutzbar
- ✅ Produktiv-ready wenn CAPTCHA-Keys eingetragen werden - ✅ Produktiv-ready wenn CAPTCHA-Keys eingetragen werden
### 2025-06-08: Zeitzone auf Europe/Berlin umgestellt
**Problem:**
- Alle Zeitstempel wurden in UTC gespeichert und angezeigt
- Backup-Dateinamen zeigten UTC-Zeit statt deutsche Zeit
- Verwirrung bei Zeitangaben im Admin Panel und Logs
**Lösung:**
1. **Docker Container Zeitzone konfiguriert:**
- Alle Dockerfiles mit `TZ=Europe/Berlin` und tzdata Installation
- PostgreSQL mit `PGTZ=Europe/Berlin` für Datenbank-Zeitzone
- Explizite Zeitzone-Dateien in /etc/localtime und /etc/timezone
2. **Python Code angepasst:**
- Import von `zoneinfo.ZoneInfo` für Zeitzonenunterstützung
- Alle `datetime.now()` Aufrufe mit `ZoneInfo("Europe/Berlin")`
- `.replace(tzinfo=None)` für Kompatibilität mit timezone-unaware Timestamps
3. **PostgreSQL Konfiguration:**
- `SET timezone = 'Europe/Berlin';` in init.sql
- Umgebungsvariablen TZ und PGTZ in docker-compose.yaml
4. **docker-compose.yaml erweitert:**
- `TZ: Europe/Berlin` für alle Services
**Geänderte Dateien:**
- `v2_adminpanel/Dockerfile`: Zeitzone und tzdata hinzugefügt
- `v2_postgres/Dockerfile`: Zeitzone und tzdata hinzugefügt
- `v2_nginx/Dockerfile`: Zeitzone und tzdata hinzugefügt
- `v2_lizenzserver/Dockerfile`: Zeitzone und tzdata hinzugefügt
- `v2_adminpanel/app.py`: 14 datetime.now() Aufrufe mit Zeitzone versehen
- `v2_adminpanel/init.sql`: PostgreSQL Zeitzone gesetzt
- `v2/docker-compose.yaml`: TZ Environment-Variable für alle Services
**Ergebnis:**
- ✅ Alle neuen Zeitstempel werden in deutscher Zeit (Europe/Berlin) gespeichert
- ✅ Backup-Dateinamen zeigen korrekte deutsche Zeit
- ✅ Admin Panel zeigt alle Zeiten in deutscher Zeitzone
- ✅ Automatische Anpassung bei Sommer-/Winterzeit
- ✅ Konsistente Zeitangaben über alle Komponenten
**Hinweis:** Nach diesen Änderungen müssen die Docker Container neu gebaut werden:
```bash
docker-compose down
docker-compose build
docker-compose up -d
```
### 2025-06-08: Zeitzone-Fix - PostgreSQL Timestamps
**Problem nach erster Implementierung:**
- Trotz Zeitzoneneinstellung wurden Zeiten immer noch in UTC angezeigt
- Grund: PostgreSQL Tabellen verwendeten `TIMESTAMP WITHOUT TIME ZONE`
**Zusätzliche Lösung:**
1. **Datenbankschema angepasst:**
- Alle `TIMESTAMP` Spalten auf `TIMESTAMP WITH TIME ZONE` geändert
- Betrifft: created_at, timestamp, started_at, ended_at, last_heartbeat, etc.
- Migration für bestehende Datenbanken berücksichtigt
2. **SQL-Abfragen vereinfacht:**
- `AT TIME ZONE 'Europe/Berlin'` nicht mehr nötig
- PostgreSQL handhabt Zeitzonenkonvertierung automatisch
**Geänderte Datei:**
- `v2_adminpanel/init.sql`: Alle TIMESTAMP Felder mit WITH TIME ZONE
**Wichtig:** Bei bestehenden Installationen muss die Datenbank neu initialisiert oder manuell migriert werden:
```sql
ALTER TABLE customers ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE licenses ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE sessions ALTER COLUMN started_at TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE sessions ALTER COLUMN last_heartbeat TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE sessions ALTER COLUMN ended_at TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE audit_log ALTER COLUMN timestamp TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE backup_history ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE login_attempts ALTER COLUMN first_attempt TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE login_attempts ALTER COLUMN last_attempt TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE login_attempts ALTER COLUMN blocked_until TYPE TIMESTAMP WITH TIME ZONE;
```

Dateidiff unterdrückt, weil mindestens eine Zeile zu lang ist

Dateidiff unterdrückt, weil mindestens eine Zeile zu lang ist

Dateidiff unterdrückt, weil mindestens eine Zeile zu lang ist

Datei anzeigen

@@ -0,0 +1,624 @@
--
-- PostgreSQL database dump
--
-- Dumped from database version 14.18 (Debian 14.18-1.pgdg120+1)
-- Dumped by pg_dump version 14.18 (Debian 14.18-1.pgdg120+1)
SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;
SET default_tablespace = '';
SET default_table_access_method = heap;
--
-- Name: audit_log; Type: TABLE; Schema: public; Owner: adminuser
--
CREATE TABLE public.audit_log (
id integer NOT NULL,
"timestamp" timestamp without time zone DEFAULT CURRENT_TIMESTAMP,
username text NOT NULL,
action text NOT NULL,
entity_type text NOT NULL,
entity_id integer,
old_values jsonb,
new_values jsonb,
ip_address text,
user_agent text,
additional_info text
);
ALTER TABLE public.audit_log OWNER TO adminuser;
--
-- Name: audit_log_id_seq; Type: SEQUENCE; Schema: public; Owner: adminuser
--
CREATE SEQUENCE public.audit_log_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE public.audit_log_id_seq OWNER TO adminuser;
--
-- Name: audit_log_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: adminuser
--
ALTER SEQUENCE public.audit_log_id_seq OWNED BY public.audit_log.id;
--
-- Name: backup_history; Type: TABLE; Schema: public; Owner: adminuser
--
CREATE TABLE public.backup_history (
id integer NOT NULL,
filename text NOT NULL,
filepath text NOT NULL,
filesize bigint,
backup_type text NOT NULL,
status text NOT NULL,
error_message text,
created_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP,
created_by text NOT NULL,
tables_count integer,
records_count integer,
duration_seconds numeric,
is_encrypted boolean DEFAULT true
);
ALTER TABLE public.backup_history OWNER TO adminuser;
--
-- Name: backup_history_id_seq; Type: SEQUENCE; Schema: public; Owner: adminuser
--
CREATE SEQUENCE public.backup_history_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE public.backup_history_id_seq OWNER TO adminuser;
--
-- Name: backup_history_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: adminuser
--
ALTER SEQUENCE public.backup_history_id_seq OWNED BY public.backup_history.id;
--
-- Name: customers; Type: TABLE; Schema: public; Owner: adminuser
--
CREATE TABLE public.customers (
id integer NOT NULL,
name text NOT NULL,
email text,
created_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP
);
ALTER TABLE public.customers OWNER TO adminuser;
--
-- Name: customers_id_seq; Type: SEQUENCE; Schema: public; Owner: adminuser
--
CREATE SEQUENCE public.customers_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE public.customers_id_seq OWNER TO adminuser;
--
-- Name: customers_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: adminuser
--
ALTER SEQUENCE public.customers_id_seq OWNED BY public.customers.id;
--
-- Name: licenses; Type: TABLE; Schema: public; Owner: adminuser
--
CREATE TABLE public.licenses (
id integer NOT NULL,
license_key text NOT NULL,
customer_id integer,
license_type text NOT NULL,
valid_from date NOT NULL,
valid_until date NOT NULL,
is_active boolean DEFAULT true
);
ALTER TABLE public.licenses OWNER TO adminuser;
--
-- Name: licenses_id_seq; Type: SEQUENCE; Schema: public; Owner: adminuser
--
CREATE SEQUENCE public.licenses_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE public.licenses_id_seq OWNER TO adminuser;
--
-- Name: licenses_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: adminuser
--
ALTER SEQUENCE public.licenses_id_seq OWNED BY public.licenses.id;
--
-- Name: login_attempts; Type: TABLE; Schema: public; Owner: adminuser
--
CREATE TABLE public.login_attempts (
ip_address character varying(45) NOT NULL,
attempt_count integer DEFAULT 0,
first_attempt timestamp without time zone DEFAULT CURRENT_TIMESTAMP,
last_attempt timestamp without time zone DEFAULT CURRENT_TIMESTAMP,
blocked_until timestamp without time zone,
last_username_tried text,
last_error_message text
);
ALTER TABLE public.login_attempts OWNER TO adminuser;
--
-- Name: sessions; Type: TABLE; Schema: public; Owner: adminuser
--
CREATE TABLE public.sessions (
id integer NOT NULL,
license_id integer,
session_id text NOT NULL,
ip_address text,
user_agent text,
started_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP,
last_heartbeat timestamp without time zone DEFAULT CURRENT_TIMESTAMP,
ended_at timestamp without time zone,
is_active boolean DEFAULT true
);
ALTER TABLE public.sessions OWNER TO adminuser;
--
-- Name: sessions_id_seq; Type: SEQUENCE; Schema: public; Owner: adminuser
--
CREATE SEQUENCE public.sessions_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE public.sessions_id_seq OWNER TO adminuser;
--
-- Name: sessions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: adminuser
--
ALTER SEQUENCE public.sessions_id_seq OWNED BY public.sessions.id;
--
-- Name: audit_log id; Type: DEFAULT; Schema: public; Owner: adminuser
--
ALTER TABLE ONLY public.audit_log ALTER COLUMN id SET DEFAULT nextval('public.audit_log_id_seq'::regclass);
--
-- Name: backup_history id; Type: DEFAULT; Schema: public; Owner: adminuser
--
ALTER TABLE ONLY public.backup_history ALTER COLUMN id SET DEFAULT nextval('public.backup_history_id_seq'::regclass);
--
-- Name: customers id; Type: DEFAULT; Schema: public; Owner: adminuser
--
ALTER TABLE ONLY public.customers ALTER COLUMN id SET DEFAULT nextval('public.customers_id_seq'::regclass);
--
-- Name: licenses id; Type: DEFAULT; Schema: public; Owner: adminuser
--
ALTER TABLE ONLY public.licenses ALTER COLUMN id SET DEFAULT nextval('public.licenses_id_seq'::regclass);
--
-- Name: sessions id; Type: DEFAULT; Schema: public; Owner: adminuser
--
ALTER TABLE ONLY public.sessions ALTER COLUMN id SET DEFAULT nextval('public.sessions_id_seq'::regclass);
--
-- Data for Name: audit_log; Type: TABLE DATA; Schema: public; Owner: adminuser
--
COPY public.audit_log (id, "timestamp", username, action, entity_type, entity_id, old_values, new_values, ip_address, user_agent, additional_info) FROM stdin;
1 2025-06-07 14:40:45.833151 rac00n LOGIN user \N \N \N 172.19.0.1 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung
2 2025-06-07 14:41:11.190149 rac00n LOGOUT user \N \N \N 172.19.0.1 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Abmeldung
3 2025-06-07 14:41:17.105596 rac00n LOGIN user \N \N \N 172.19.0.1 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung
4 2025-06-07 15:15:06.256545 rac00n LOGIN user \N \N \N 172.19.0.1 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung
5 2025-06-07 15:15:33.972469 rac00n LOGIN user \N \N \N 172.19.0.1 curl/8.5.0 Erfolgreiche Anmeldung
6 2025-06-07 15:15:34.109986 rac00n BACKUP database 1 \N \N 172.19.0.1 curl/8.5.0 Backup erstellt: backup_v2docker_20250607_151534_encrypted.sql.gz.enc (3640 bytes)
7 2025-06-07 15:15:43.824357 rac00n LOGIN user \N \N \N 172.19.0.1 curl/8.5.0 Erfolgreiche Anmeldung
8 2025-06-07 15:15:43.912466 rac00n BACKUP database 2 \N \N 172.19.0.1 curl/8.5.0 Backup erstellt: backup_v2docker_20250607_151543_encrypted.sql.gz.enc (3768 bytes)
9 2025-06-07 15:16:23.724322 rac00n DOWNLOAD backup 2 \N \N 172.19.0.1 curl/8.5.0 Backup heruntergeladen: backup_v2docker_20250607_151543_encrypted.sql.gz.enc
76 2025-06-08 07:55:31.892854 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
78 2025-06-08 07:59:05.01361 rac00n DOWNLOAD backup 5 \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Backup heruntergeladen: backup_v2docker_20250608_075834_encrypted.sql.gz.enc
80 2025-06-08 08:06:18.479631 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/28.0 Chrome/130.0.0.0 Mobile Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
82 2025-06-08 14:36:01.730846 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
84 2025-06-08 16:13:31.837749 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
86 2025-06-08 17:47:45.942574 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 python-requests/2.31.0 Erfolgreiche Anmeldung von IP: 172.19.0.1
10 2025-06-07 17:01:30.363152 w@rh@mm3r LOGOUT user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Abmeldung
11 2025-06-07 17:11:08.302021 w@rh@mm3r LOGIN user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung
12 2025-06-07 17:11:10.058681 w@rh@mm3r LOGOUT user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Abmeldung
13 2025-06-07 17:11:17.726581 rac00n LOGIN user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung
14 2025-06-07 17:37:01.770454 rac00n LOGIN user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung
15 2025-06-07 17:45:53.454599 rac00n LOGIN user \N \N \N 172.19.0.5 curl/8.5.0 Erfolgreiche Anmeldung
16 2025-06-07 17:46:45.804984 rac00n BACKUP database 3 \N \N 172.19.0.5 curl/8.5.0 Backup erstellt: backup_v2docker_20250607_174645_encrypted.sql.gz.enc (4108 bytes)
17 2025-06-07 17:54:25.805772 rac00n LOGIN user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung
18 2025-06-07 17:54:30.18286 rac00n LOGOUT user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Abmeldung
19 2025-06-07 18:02:18.1851 rac00n LOGIN user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung
20 2025-06-07 18:55:11.584957 system LOGIN_FAILED user \N \N \N 172.19.0.5 curl/8.5.0 IP: 172.19.0.1, User: testuser, Message: YOU FAILED
21 2025-06-07 18:55:13.112779 system LOGIN_FAILED user \N \N \N 172.19.0.5 curl/8.5.0 IP: 172.19.0.1, User: testuser, Message: WRONG! 🚫
22 2025-06-07 19:00:59.418824 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
23 2025-06-07 19:01:54.566099 rac00n LOGOUT user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Abmeldung
24 2025-06-07 19:03:45.046523 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
25 2025-06-07 19:31:13.237177 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
26 2025-06-07 19:32:20.003266 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
27 2025-06-07 19:33:26.379968 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
28 2025-06-07 19:34:09.072085 rac00n LOGOUT user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Abmeldung
29 2025-06-07 19:36:17.489882 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 curl/8.5.0 Erfolgreiche Anmeldung von IP: 172.19.0.1
30 2025-06-07 19:44:27.078563 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
31 2025-06-07 19:51:17.993707 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 curl/8.5.0 Erfolgreiche Anmeldung von IP: 172.19.0.1
32 2025-06-07 19:58:17.448169 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
33 2025-06-07 19:58:52.291492 rac00n LOGOUT user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Abmeldung
34 2025-06-07 20:03:15.20645 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
35 2025-06-07 20:05:19.654401 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 curl/8.5.0 Erfolgreiche Anmeldung von IP: 172.19.0.1
36 2025-06-07 20:11:25.331298 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 curl/8.5.0 Erfolgreiche Anmeldung von IP: 172.19.0.1
37 2025-06-07 20:49:37.631513 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 curl/8.5.0 Erfolgreiche Anmeldung von IP: 172.19.0.1
38 2025-06-07 20:52:27.354576 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 curl/8.5.0 Erfolgreiche Anmeldung von IP: 172.19.0.1
39 2025-06-07 20:52:27.38643 rac00n EXPORT license \N \N \N 172.19.0.5 curl/8.5.0 Export aller Lizenzen als CSV
40 2025-06-07 20:54:04.783715 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
41 2025-06-07 20:56:50.950356 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
42 2025-06-07 21:06:51.653154 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 curl/8.5.0 Erfolgreiche Anmeldung von IP: 172.19.0.1
43 2025-06-07 21:12:07.627636 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
44 2025-06-07 21:45:19.164825 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
45 2025-06-07 21:56:57.616336 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
46 2025-06-07 21:58:35.15561 rac00n CREATE license 1 \N {"valid_from": "2025-06-07", "license_key": "AF-202506T-75R4-9M9H-DFEY", "valid_until": "2025-06-13", "license_type": "test", "customer_name": "Demo Firma GmbH", "customer_email": "demo@firma.de"} 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 \N
47 2025-06-07 22:01:01.548875 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
48 2025-06-07 22:01:28.294051 rac00n CREATE license 2 \N {"valid_from": "2025-06-07", "license_key": "AF-202506T-3DF2-ELJN-RQDR", "valid_until": "2025-06-20", "license_type": "test", "customer_name": "Test Kunde", "customer_email": "test@example.com"} 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 \N
49 2025-06-07 22:19:17.155373 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
50 2025-06-07 22:19:29.307415 rac00n CREATE license 3 \N {"valid_from": "2025-06-07", "license_key": "AF-202506F-CRS3-PL6W-9CCP", "valid_until": "2026-06-06", "license_type": "full", "customer_name": "Test vor Restore", "customer_email": "test@restore.de"} 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 \N
51 2025-06-07 22:19:36.9697 rac00n CREATE license 4 \N {"valid_from": "2025-06-07", "license_key": "AF-202506F-GGKA-CJKV-P2PF", "valid_until": "2026-06-06", "license_type": "full", "customer_name": "Test vor Restore", "customer_email": "test@restore.de"} 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 \N
52 2025-06-07 22:19:51.747722 rac00n CREATE_BATCH license \N \N {"type": "full", "customer": "Müller GmbH & Co. KG", "quantity": 10} 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Batch-Generierung von 10 Lizenzen
53 2025-06-07 22:23:56.496775 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
54 2025-06-07 22:24:29.52332 rac00n UPDATE customer 4 {"name": "Demo Firma GmbH", "email": "demo@firma.de"} {"name": "Demo Firma GmbH oder so ähnlich", "email": "demo@firma.de"} 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 \N
55 2025-06-07 22:24:53.387461 rac00n UPDATE license 14 {"is_active": true, "valid_from": "2025-06-07", "license_key": "AF-202506F-Z6KT-QY28-CTZ9", "valid_until": "2026-06-06", "license_type": "full"} {"is_active": false, "valid_from": "2025-06-07", "license_key": "AF-202506F-Z6KT-QY28-CTZ9", "valid_until": "2026-06-06", "license_type": "full"} 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 \N
56 2025-06-07 22:25:42.636503 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
57 2025-06-07 22:26:04.176345 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
58 2025-06-07 22:32:36.918402 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
59 2025-06-07 22:33:04.289738 rac00n UPDATE license 12 {"is_active": true, "valid_from": "2025-06-07", "license_key": "AF-202506F-F6E8-2KXL-7GVB", "valid_until": "2026-06-06", "license_type": "full"} {"is_active": false, "valid_from": "2025-06-07", "license_key": "AF-202506F-F6E8-2KXL-7GVB", "valid_until": "2026-06-06", "license_type": "full"} 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 \N
60 2025-06-07 22:37:11.24327 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
61 2025-06-07 22:40:11.528994 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
62 2025-06-07 23:02:03.858286 system LOGIN_FAILED user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 IP: 172.19.0.1, User: rac00n, Message: YOU FAILED
63 2025-06-07 23:02:13.590411 system LOGIN_FAILED user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 IP: 172.19.0.1, User: rac00n, Message: COMPUTER SAYS NO
64 2025-06-07 23:18:04.017897 system LOGIN_FAILED user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 IP: 172.19.0.1, User: rac00n, Message: NOPE!
65 2025-06-07 23:18:45.316866 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
66 2025-06-07 23:24:10.758514 rac00n CREATE customer 6 \N {"name": "Bli Bla Blub GmbH", "email": "test@bliblablu.info"} 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 \N
67 2025-06-07 23:24:10.763695 rac00n CREATE license 15 \N {"valid_from": "2025-06-08", "license_key": "AF-202506T-R5TC-9KT3-ZPRD", "valid_until": "2025-07-07", "license_type": "full", "customer_name": "Bli Bla Blub GmbH", "customer_email": "test@bliblablu.info"} 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 \N
68 2025-06-07 23:25:25.589172 rac00n UPDATE license 14 {"is_active": false, "valid_from": "2025-06-07", "license_key": "AF-202506F-Z6KT-QY28-CTZ9", "valid_until": "2026-06-06", "license_type": "full"} {"is_active": false, "valid_from": "2025-06-07", "license_key": "AF-202506F-Z6KT-QY28-CTZ9", "valid_until": "2026-06-06", "license_type": "full"} 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 \N
69 2025-06-07 23:25:33.134625 rac00n UPDATE license 11 {"is_active": true, "valid_from": "2025-06-07", "license_key": "AF-202506F-A6BV-6KAX-KU4J", "valid_until": "2026-06-06", "license_type": "full"} {"is_active": false, "valid_from": "2025-06-07", "license_key": "AF-202506F-A6BV-6KAX-KU4J", "valid_until": "2026-06-06", "license_type": "full"} 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 \N
70 2025-06-07 23:25:52.026955 rac00n DELETE license 14 {"license_key": "AF-202506F-Z6KT-QY28-CTZ9", "license_type": "full", "customer_name": "Müller GmbH & Co. KG"} \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 \N
71 2025-06-07 23:26:34.32667 rac00n DELETE customer 5 {"name": "Max Mustermann", "email": "max@mustermann.de"} \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 \N
72 2025-06-07 23:27:11.170417 rac00n CREATE_BATCH license \N \N {"type": "full", "customer": "Bli Bla Blub GmbH", "quantity": 5} 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Batch-Generierung von 5 Lizenzen
73 2025-06-07 23:28:45.926809 rac00n BACKUP database 4 \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Backup erstellt: backup_v2docker_20250607_232845_encrypted.sql.gz.enc (7116 bytes)
74 2025-06-07 23:29:10.885674 rac00n DOWNLOAD backup 4 \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Backup heruntergeladen: backup_v2docker_20250607_232845_encrypted.sql.gz.enc
75 2025-06-08 07:55:19.354464 system LOGIN_FAILED user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 IP: 172.19.0.1, User: rac00n, Message: NOPE!
77 2025-06-08 07:58:34.270119 rac00n BACKUP database 5 \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Backup erstellt: backup_v2docker_20250608_075834_encrypted.sql.gz.enc (7308 bytes)
79 2025-06-08 07:59:21.636378 rac00n LOGOUT user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Abmeldung
81 2025-06-08 08:51:01.683507 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/28.0 Chrome/130.0.0.0 Mobile Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
83 2025-06-08 14:36:24.980042 rac00n LOGOUT user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Abmeldung
85 2025-06-08 17:21:27.353725 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
87 2025-06-08 17:49:18.527739 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 python-requests/2.31.0 Erfolgreiche Anmeldung von IP: 172.19.0.1
88 2025-06-08 17:49:31.018007 rac00n BACKUP database 6 \N \N 172.19.0.5 python-requests/2.31.0 Backup erstellt: backup_v2docker_20250608_174930_encrypted.sql.gz.enc (7692 bytes)
89 2025-06-08 17:50:12.342288 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 python-requests/2.31.0 Erfolgreiche Anmeldung von IP: 172.19.0.1
90 2025-06-08 18:01:50.515914 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
91 2025-06-08 18:02:24.836845 rac00n BACKUP database 7 \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Backup erstellt: backup_v2docker_20250608_200224_encrypted.sql.gz.enc (7864 bytes)
92 2025-06-08 18:02:35.914193 rac00n LOGOUT user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Abmeldung
93 2025-06-08 18:04:57.805724 system LOGOUT user \N \N \N 172.19.0.5 python-requests/2.31.0 Abmeldung
94 2025-06-08 18:04:59.88908 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 python-requests/2.31.0 Erfolgreiche Anmeldung von IP: 172.19.0.1
95 2025-06-08 18:06:51.462396 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 python-requests/2.31.0 Erfolgreiche Anmeldung von IP: 172.19.0.1
96 2025-06-08 18:10:13.065749 rac00n LOGIN_SUCCESS user \N \N \N 172.19.0.5 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Erfolgreiche Anmeldung von IP: 172.19.0.1
\.
--
-- Data for Name: backup_history; Type: TABLE DATA; Schema: public; Owner: adminuser
--
COPY public.backup_history (id, filename, filepath, filesize, backup_type, status, error_message, created_at, created_by, tables_count, records_count, duration_seconds, is_encrypted) FROM stdin;
1 backup_v2docker_20250607_151534_encrypted.sql.gz.enc /app/backups/backup_v2docker_20250607_151534_encrypted.sql.gz.enc 3640 manual success \N 2025-06-07 15:15:34.003903 rac00n 5 6 0.1055765151977539 t
2 backup_v2docker_20250607_151543_encrypted.sql.gz.enc /app/backups/backup_v2docker_20250607_151543_encrypted.sql.gz.enc 3768 manual success \N 2025-06-07 15:15:43.853153 rac00n 5 9 0.058580875396728516 t
3 backup_v2docker_20250607_174645_encrypted.sql.gz.enc /app/backups/backup_v2docker_20250607_174645_encrypted.sql.gz.enc 4108 manual success \N 2025-06-07 17:46:45.701839 rac00n 5 19 0.10155487060546875 t
4 backup_v2docker_20250607_232845_encrypted.sql.gz.enc /app/backups/backup_v2docker_20250607_232845_encrypted.sql.gz.enc 7116 manual success \N 2025-06-07 23:28:45.823584 rac00n 6 102 0.10189580917358398 t
5 backup_v2docker_20250608_075834_encrypted.sql.gz.enc /app/backups/backup_v2docker_20250608_075834_encrypted.sql.gz.enc 7308 manual success \N 2025-06-08 07:58:34.205649 rac00n 6 107 0.06303167343139648 t
6 backup_v2docker_20250608_174930_encrypted.sql.gz.enc /app/backups/backup_v2docker_20250608_174930_encrypted.sql.gz.enc 7692 manual success \N 2025-06-08 17:49:30.953077 rac00n 6 119 0.06305289268493652 t
7 backup_v2docker_20250608_200224_encrypted.sql.gz.enc /app/backups/backup_v2docker_20250608_200224_encrypted.sql.gz.enc 7864 manual success \N 2025-06-08 18:02:24.771159 rac00n 6 123 0.06486988067626953 t
\.
--
-- Data for Name: customers; Type: TABLE DATA; Schema: public; Owner: adminuser
--
COPY public.customers (id, name, email, created_at) FROM stdin;
1 Müller GmbH & Co. KG kontakt@müller-köln.de 2025-06-07 14:39:21.554354
2 Test vor Restore test@restore.de 2025-06-07 15:16:51.26188
3 Test Kunde test@example.com 2025-06-07 21:43:48.308699
4 Demo Firma GmbH oder so ähnlich demo@firma.de 2025-06-07 21:43:48.308699
6 Bli Bla Blub GmbH test@bliblablu.info 2025-06-07 23:24:10.753449
\.
--
-- Data for Name: licenses; Type: TABLE DATA; Schema: public; Owner: adminuser
--
COPY public.licenses (id, license_key, customer_id, license_type, valid_from, valid_until, is_active) FROM stdin;
1 AF-202506T-75R4-9M9H-DFEY 4 test 2025-06-07 2025-06-13 t
2 AF-202506T-3DF2-ELJN-RQDR 3 test 2025-06-07 2025-06-20 t
3 AF-202506F-CRS3-PL6W-9CCP 2 full 2025-06-07 2026-06-06 t
4 AF-202506F-GGKA-CJKV-P2PF 2 full 2025-06-07 2026-06-06 t
5 AF-202506F-FDZQ-MH3T-6EWP 1 full 2025-06-07 2026-06-06 t
6 AF-202506F-QYM9-58NT-SRCV 1 full 2025-06-07 2026-06-06 t
7 AF-202506F-9AVT-SY3C-KZ6H 1 full 2025-06-07 2026-06-06 t
8 AF-202506F-TS34-U93G-X74Y 1 full 2025-06-07 2026-06-06 t
9 AF-202506F-PC2K-SPEC-9MDS 1 full 2025-06-07 2026-06-06 t
10 AF-202506F-R2VL-J7YF-6XLA 1 full 2025-06-07 2026-06-06 t
13 AF-202506F-JU47-AS8Y-8VFP 1 full 2025-06-07 2026-06-06 t
12 AF-202506F-F6E8-2KXL-7GVB 1 full 2025-06-07 2026-06-06 f
15 AF-202506T-R5TC-9KT3-ZPRD 6 full 2025-06-08 2025-07-07 t
11 AF-202506F-A6BV-6KAX-KU4J 1 full 2025-06-07 2026-06-06 f
16 AF-202506F-F4WM-RUBG-RR7A 6 full 2025-06-08 2025-07-07 t
17 AF-202506F-ZFYY-D2ZG-9D2B 6 full 2025-06-08 2025-07-07 t
18 AF-202506F-KS7T-Z676-7V5L 6 full 2025-06-08 2025-07-07 t
19 AF-202506F-3NJ4-X5ET-RWY2 6 full 2025-06-08 2025-07-07 t
20 AF-202506F-B984-PHN6-BUDX 6 full 2025-06-08 2025-07-07 t
\.
--
-- Data for Name: login_attempts; Type: TABLE DATA; Schema: public; Owner: adminuser
--
COPY public.login_attempts (ip_address, attempt_count, first_attempt, last_attempt, blocked_until, last_username_tried, last_error_message) FROM stdin;
192.168.1.50 3 2025-06-07 18:56:54.538998 2025-06-07 18:56:54.538998 2025-06-07 20:56:54.538998 attacker1 NOPE\\!
10.0.0.25 5 2025-06-07 18:56:54.538998 2025-06-07 18:56:54.538998 2025-06-08 14:56:54.538998 scanner ACCESS DENIED, TRY HARDER
192.168.100.200 7 2025-06-07 18:56:54.538998 2025-06-07 18:56:54.538998 2025-06-08 17:56:54.538998 brute_forcer YOU FAILED
\.
--
-- Data for Name: sessions; Type: TABLE DATA; Schema: public; Owner: adminuser
--
COPY public.sessions (id, license_id, session_id, ip_address, user_agent, started_at, last_heartbeat, ended_at, is_active) FROM stdin;
\.
--
-- Name: audit_log_id_seq; Type: SEQUENCE SET; Schema: public; Owner: adminuser
--
SELECT pg_catalog.setval('public.audit_log_id_seq', 96, true);
--
-- Name: backup_history_id_seq; Type: SEQUENCE SET; Schema: public; Owner: adminuser
--
SELECT pg_catalog.setval('public.backup_history_id_seq', 7, true);
--
-- Name: customers_id_seq; Type: SEQUENCE SET; Schema: public; Owner: adminuser
--
SELECT pg_catalog.setval('public.customers_id_seq', 6, true);
--
-- Name: licenses_id_seq; Type: SEQUENCE SET; Schema: public; Owner: adminuser
--
SELECT pg_catalog.setval('public.licenses_id_seq', 20, true);
--
-- Name: sessions_id_seq; Type: SEQUENCE SET; Schema: public; Owner: adminuser
--
SELECT pg_catalog.setval('public.sessions_id_seq', 1, false);
--
-- Name: audit_log audit_log_pkey; Type: CONSTRAINT; Schema: public; Owner: adminuser
--
ALTER TABLE ONLY public.audit_log
ADD CONSTRAINT audit_log_pkey PRIMARY KEY (id);
--
-- Name: backup_history backup_history_pkey; Type: CONSTRAINT; Schema: public; Owner: adminuser
--
ALTER TABLE ONLY public.backup_history
ADD CONSTRAINT backup_history_pkey PRIMARY KEY (id);
--
-- Name: customers customers_pkey; Type: CONSTRAINT; Schema: public; Owner: adminuser
--
ALTER TABLE ONLY public.customers
ADD CONSTRAINT customers_pkey PRIMARY KEY (id);
--
-- Name: licenses licenses_license_key_key; Type: CONSTRAINT; Schema: public; Owner: adminuser
--
ALTER TABLE ONLY public.licenses
ADD CONSTRAINT licenses_license_key_key UNIQUE (license_key);
--
-- Name: licenses licenses_pkey; Type: CONSTRAINT; Schema: public; Owner: adminuser
--
ALTER TABLE ONLY public.licenses
ADD CONSTRAINT licenses_pkey PRIMARY KEY (id);
--
-- Name: login_attempts login_attempts_pkey; Type: CONSTRAINT; Schema: public; Owner: adminuser
--
ALTER TABLE ONLY public.login_attempts
ADD CONSTRAINT login_attempts_pkey PRIMARY KEY (ip_address);
--
-- Name: sessions sessions_pkey; Type: CONSTRAINT; Schema: public; Owner: adminuser
--
ALTER TABLE ONLY public.sessions
ADD CONSTRAINT sessions_pkey PRIMARY KEY (id);
--
-- Name: sessions sessions_session_id_key; Type: CONSTRAINT; Schema: public; Owner: adminuser
--
ALTER TABLE ONLY public.sessions
ADD CONSTRAINT sessions_session_id_key UNIQUE (session_id);
--
-- Name: idx_audit_log_entity; Type: INDEX; Schema: public; Owner: adminuser
--
CREATE INDEX idx_audit_log_entity ON public.audit_log USING btree (entity_type, entity_id);
--
-- Name: idx_audit_log_timestamp; Type: INDEX; Schema: public; Owner: adminuser
--
CREATE INDEX idx_audit_log_timestamp ON public.audit_log USING btree ("timestamp" DESC);
--
-- Name: idx_audit_log_username; Type: INDEX; Schema: public; Owner: adminuser
--
CREATE INDEX idx_audit_log_username ON public.audit_log USING btree (username);
--
-- Name: idx_backup_history_created_at; Type: INDEX; Schema: public; Owner: adminuser
--
CREATE INDEX idx_backup_history_created_at ON public.backup_history USING btree (created_at DESC);
--
-- Name: idx_backup_history_status; Type: INDEX; Schema: public; Owner: adminuser
--
CREATE INDEX idx_backup_history_status ON public.backup_history USING btree (status);
--
-- Name: idx_login_attempts_blocked_until; Type: INDEX; Schema: public; Owner: adminuser
--
CREATE INDEX idx_login_attempts_blocked_until ON public.login_attempts USING btree (blocked_until);
--
-- Name: idx_login_attempts_last_attempt; Type: INDEX; Schema: public; Owner: adminuser
--
CREATE INDEX idx_login_attempts_last_attempt ON public.login_attempts USING btree (last_attempt DESC);
--
-- Name: licenses licenses_customer_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: adminuser
--
ALTER TABLE ONLY public.licenses
ADD CONSTRAINT licenses_customer_id_fkey FOREIGN KEY (customer_id) REFERENCES public.customers(id);
--
-- Name: sessions sessions_license_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: adminuser
--
ALTER TABLE ONLY public.sessions
ADD CONSTRAINT sessions_license_id_fkey FOREIGN KEY (license_id) REFERENCES public.licenses(id);
--
-- PostgreSQL database dump complete
--

Datei anzeigen

@@ -12,6 +12,8 @@ services:
POSTGRES_INITDB_ARGS: '--encoding=UTF8 --locale=de_DE.UTF-8' POSTGRES_INITDB_ARGS: '--encoding=UTF8 --locale=de_DE.UTF-8'
POSTGRES_COLLATE: 'de_DE.UTF-8' POSTGRES_COLLATE: 'de_DE.UTF-8'
POSTGRES_CTYPE: 'de_DE.UTF-8' POSTGRES_CTYPE: 'de_DE.UTF-8'
TZ: Europe/Berlin
PGTZ: Europe/Berlin
volumes: volumes:
# Persistente Speicherung der Datenbank auf dem Windows-Host # Persistente Speicherung der Datenbank auf dem Windows-Host
- postgres_data:/var/lib/postgresql/data - postgres_data:/var/lib/postgresql/data
@@ -33,6 +35,8 @@ services:
ports: ports:
- "8443:8443" - "8443:8443"
env_file: .env env_file: .env
environment:
TZ: Europe/Berlin
depends_on: depends_on:
- postgres - postgres
networks: networks:
@@ -50,6 +54,8 @@ services:
restart: always restart: always
# Port-Mapping entfernt - nur über nginx erreichbar # Port-Mapping entfernt - nur über nginx erreichbar
env_file: .env env_file: .env
environment:
TZ: Europe/Berlin
depends_on: depends_on:
- postgres - postgres
networks: networks:
@@ -71,6 +77,8 @@ services:
ports: ports:
- "80:80" - "80:80"
- "443:443" - "443:443"
environment:
TZ: Europe/Berlin
depends_on: depends_on:
- admin-panel - admin-panel
- license-server - license-server

Datei anzeigen

@@ -5,15 +5,21 @@ ENV LANG=de_DE.UTF-8
ENV LC_ALL=de_DE.UTF-8 ENV LC_ALL=de_DE.UTF-8
ENV PYTHONIOENCODING=utf-8 ENV PYTHONIOENCODING=utf-8
# Zeitzone auf Europe/Berlin setzen
ENV TZ=Europe/Berlin
WORKDIR /app WORKDIR /app
# System-Dependencies inkl. PostgreSQL-Tools installieren # System-Dependencies inkl. PostgreSQL-Tools installieren
RUN apt-get update && apt-get install -y \ RUN apt-get update && apt-get install -y \
locales \ locales \
postgresql-client \ postgresql-client \
tzdata \
&& sed -i '/de_DE.UTF-8/s/^# //g' /etc/locale.gen \ && sed -i '/de_DE.UTF-8/s/^# //g' /etc/locale.gen \
&& locale-gen \ && locale-gen \
&& update-locale LANG=de_DE.UTF-8 \ && update-locale LANG=de_DE.UTF-8 \
&& ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime \
&& echo "Europe/Berlin" > /etc/timezone \
&& apt-get clean \ && apt-get clean \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*

Datei anzeigen

@@ -7,6 +7,7 @@ from functools import wraps
from dotenv import load_dotenv from dotenv import load_dotenv
import pandas as pd import pandas as pd
from datetime import datetime, timedelta from datetime import datetime, timedelta
from zoneinfo import ZoneInfo
import io import io
import subprocess import subprocess
import gzip import gzip
@@ -72,7 +73,7 @@ def login_required(f):
# Prüfe ob Session abgelaufen ist # Prüfe ob Session abgelaufen ist
if 'last_activity' in session: if 'last_activity' in session:
last_activity = datetime.fromisoformat(session['last_activity']) last_activity = datetime.fromisoformat(session['last_activity'])
time_since_activity = datetime.now() - last_activity time_since_activity = datetime.now(ZoneInfo("Europe/Berlin")).replace(tzinfo=None) - last_activity
# Debug-Logging # Debug-Logging
app.logger.info(f"Session check for {session.get('username', 'unknown')}: " app.logger.info(f"Session check for {session.get('username', 'unknown')}: "
@@ -170,7 +171,7 @@ def get_or_create_encryption_key():
def create_backup(backup_type="manual", created_by=None): def create_backup(backup_type="manual", created_by=None):
"""Erstellt ein verschlüsseltes Backup der Datenbank""" """Erstellt ein verschlüsseltes Backup der Datenbank"""
start_time = time.time() start_time = time.time()
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") timestamp = datetime.now(ZoneInfo("Europe/Berlin")).strftime("%Y%m%d_%H%M%S")
filename = f"backup_v2docker_{timestamp}_encrypted.sql.gz.enc" filename = f"backup_v2docker_{timestamp}_encrypted.sql.gz.enc"
filepath = BACKUP_DIR / filename filepath = BACKUP_DIR / filename
@@ -398,7 +399,7 @@ def check_ip_blocked(ip_address):
conn.close() conn.close()
if result and result[0]: if result and result[0]:
if result[0] > datetime.now(): if result[0] > datetime.now(ZoneInfo("Europe/Berlin")).replace(tzinfo=None):
return True, result[0] return True, result[0]
return False, None return False, None
@@ -425,7 +426,7 @@ def record_failed_attempt(ip_address, username):
blocked_until = None blocked_until = None
if new_count >= MAX_LOGIN_ATTEMPTS: if new_count >= MAX_LOGIN_ATTEMPTS:
blocked_until = datetime.now() + timedelta(hours=BLOCK_DURATION_HOURS) blocked_until = datetime.now(ZoneInfo("Europe/Berlin")).replace(tzinfo=None) + timedelta(hours=BLOCK_DURATION_HOURS)
# E-Mail-Benachrichtigung (wenn aktiviert) # E-Mail-Benachrichtigung (wenn aktiviert)
if os.getenv("EMAIL_ENABLED", "false").lower() == "true": if os.getenv("EMAIL_ENABLED", "false").lower() == "true":
send_security_alert_email(ip_address, username, new_count) send_security_alert_email(ip_address, username, new_count)
@@ -505,7 +506,7 @@ def send_security_alert_email(ip_address, username, attempt_count):
IP-Adresse: {ip_address} IP-Adresse: {ip_address}
Versuchter Benutzername: {username} Versuchter Benutzername: {username}
Anzahl Versuche: {attempt_count} Anzahl Versuche: {attempt_count}
Zeit: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} Zeit: {datetime.now(ZoneInfo("Europe/Berlin")).strftime('%Y-%m-%d %H:%M:%S')}
Die IP-Adresse wurde für 24 Stunden gesperrt. Die IP-Adresse wurde für 24 Stunden gesperrt.
@@ -565,7 +566,7 @@ def generate_license_key(license_type='full'):
chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789' chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'
# Datum-Teil # Datum-Teil
now = datetime.now() now = datetime.now(ZoneInfo("Europe/Berlin"))
date_part = now.strftime('%Y%m') date_part = now.strftime('%Y%m')
type_char = 'F' if license_type == 'full' else 'T' type_char = 'F' if license_type == 'full' else 'T'
@@ -606,7 +607,7 @@ def login():
# Prüfen ob IP gesperrt ist # Prüfen ob IP gesperrt ist
is_blocked, blocked_until = check_ip_blocked(ip_address) is_blocked, blocked_until = check_ip_blocked(ip_address)
if is_blocked: if is_blocked:
time_remaining = (blocked_until - datetime.now()).total_seconds() / 3600 time_remaining = (blocked_until - datetime.now(ZoneInfo("Europe/Berlin")).replace(tzinfo=None)).total_seconds() / 3600
error_msg = f"IP GESPERRT! Noch {time_remaining:.1f} Stunden warten." error_msg = f"IP GESPERRT! Noch {time_remaining:.1f} Stunden warten."
return render_template("login.html", error=error_msg, error_type="blocked") return render_template("login.html", error=error_msg, error_type="blocked")
@@ -668,7 +669,7 @@ def login():
session.permanent = True # Aktiviert das Timeout session.permanent = True # Aktiviert das Timeout
session['logged_in'] = True session['logged_in'] = True
session['username'] = username session['username'] = username
session['last_activity'] = datetime.now().isoformat() session['last_activity'] = datetime.now(ZoneInfo("Europe/Berlin")).replace(tzinfo=None).isoformat()
reset_login_attempts(ip_address) reset_login_attempts(ip_address)
log_audit('LOGIN_SUCCESS', 'user', log_audit('LOGIN_SUCCESS', 'user',
additional_info=f"Erfolgreiche Anmeldung von IP: {ip_address}") additional_info=f"Erfolgreiche Anmeldung von IP: {ip_address}")
@@ -710,7 +711,7 @@ def logout():
def heartbeat(): def heartbeat():
"""Endpoint für Session Keep-Alive - aktualisiert last_activity""" """Endpoint für Session Keep-Alive - aktualisiert last_activity"""
# Aktualisiere last_activity nur wenn explizit angefordert # Aktualisiere last_activity nur wenn explizit angefordert
session['last_activity'] = datetime.now().isoformat() session['last_activity'] = datetime.now(ZoneInfo("Europe/Berlin")).replace(tzinfo=None).isoformat()
# Force session save # Force session save
session.modified = True session.modified = True
@@ -975,7 +976,7 @@ def dashboard():
'ip_address': event[0], 'ip_address': event[0],
'attempt_count': event[1], 'attempt_count': event[1],
'last_attempt': event[2].strftime('%d.%m %H:%M'), 'last_attempt': event[2].strftime('%d.%m %H:%M'),
'blocked_until': event[3].strftime('%d.%m %H:%M') if event[3] and event[3] > datetime.now() else None, 'blocked_until': event[3].strftime('%d.%m %H:%M') if event[3] and event[3] > datetime.now(ZoneInfo("Europe/Berlin")).replace(tzinfo=None) else None,
'username_tried': event[4], 'username_tried': event[4],
'error_message': event[5] 'error_message': event[5]
}) })
@@ -1249,7 +1250,7 @@ def batch_licenses():
'licenses': generated_licenses, 'licenses': generated_licenses,
'valid_from': valid_from, 'valid_from': valid_from,
'valid_until': valid_until, 'valid_until': valid_until,
'timestamp': datetime.now().isoformat() 'timestamp': datetime.now(ZoneInfo("Europe/Berlin")).replace(tzinfo=None).isoformat()
} }
flash(f'{quantity} Lizenzen erfolgreich generiert!', 'success') flash(f'{quantity} Lizenzen erfolgreich generiert!', 'success')
@@ -1308,7 +1309,7 @@ def export_batch():
io.BytesIO(output.getvalue().encode('utf-8-sig')), io.BytesIO(output.getvalue().encode('utf-8-sig')),
mimetype='text/csv', mimetype='text/csv',
as_attachment=True, as_attachment=True,
download_name=f"batch_licenses_{batch_data['customer'].replace(' ', '_')}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv" download_name=f"batch_licenses_{batch_data['customer'].replace(' ', '_')}_{datetime.now(ZoneInfo('Europe/Berlin')).strftime('%Y%m%d_%H%M%S')}.csv"
) )
@app.route("/licenses") @app.route("/licenses")
@@ -1778,7 +1779,7 @@ def export_licenses():
# Audit-Log # Audit-Log
log_audit('EXPORT', 'license', log_audit('EXPORT', 'license',
additional_info=f"Export aller Lizenzen als {export_format.upper()}") additional_info=f"Export aller Lizenzen als {export_format.upper()}")
filename = f'lizenzen_export_{datetime.now().strftime("%Y%m%d_%H%M%S")}' filename = f'lizenzen_export_{datetime.now(ZoneInfo("Europe/Berlin")).strftime("%Y%m%d_%H%M%S")}'
if export_format == 'csv': if export_format == 'csv':
# CSV Export # CSV Export
@@ -1859,7 +1860,7 @@ def export_customers():
# Audit-Log # Audit-Log
log_audit('EXPORT', 'customer', log_audit('EXPORT', 'customer',
additional_info=f"Export aller Kunden als {export_format.upper()}") additional_info=f"Export aller Kunden als {export_format.upper()}")
filename = f'kunden_export_{datetime.now().strftime("%Y%m%d_%H%M%S")}' filename = f'kunden_export_{datetime.now(ZoneInfo("Europe/Berlin")).strftime("%Y%m%d_%H%M%S")}'
if export_format == 'csv': if export_format == 'csv':
# CSV Export # CSV Export
@@ -2108,7 +2109,7 @@ def blocked_ips():
'first_attempt': ip[2].strftime('%d.%m.%Y %H:%M'), 'first_attempt': ip[2].strftime('%d.%m.%Y %H:%M'),
'last_attempt': ip[3].strftime('%d.%m.%Y %H:%M'), 'last_attempt': ip[3].strftime('%d.%m.%Y %H:%M'),
'blocked_until': ip[4].strftime('%d.%m.%Y %H:%M'), 'blocked_until': ip[4].strftime('%d.%m.%Y %H:%M'),
'is_active': ip[4] > datetime.now(), 'is_active': ip[4] > datetime.now(ZoneInfo("Europe/Berlin")).replace(tzinfo=None),
'last_username': ip[5], 'last_username': ip[5],
'last_error': ip[6] 'last_error': ip[6]
}) })

Datei anzeigen

@@ -1,11 +1,14 @@
-- UTF-8 Encoding für deutsche Sonderzeichen sicherstellen -- UTF-8 Encoding für deutsche Sonderzeichen sicherstellen
SET client_encoding = 'UTF8'; SET client_encoding = 'UTF8';
-- Zeitzone auf Europe/Berlin setzen
SET timezone = 'Europe/Berlin';
CREATE TABLE IF NOT EXISTS customers ( CREATE TABLE IF NOT EXISTS customers (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
name TEXT NOT NULL, name TEXT NOT NULL,
email TEXT, email TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_email UNIQUE (email) CONSTRAINT unique_email UNIQUE (email)
); );
@@ -17,7 +20,7 @@ CREATE TABLE IF NOT EXISTS licenses (
valid_from DATE NOT NULL, valid_from DATE NOT NULL,
valid_until DATE NOT NULL, valid_until DATE NOT NULL,
is_active BOOLEAN DEFAULT TRUE, is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
); );
CREATE TABLE IF NOT EXISTS sessions ( CREATE TABLE IF NOT EXISTS sessions (
@@ -26,16 +29,16 @@ CREATE TABLE IF NOT EXISTS sessions (
session_id TEXT UNIQUE NOT NULL, session_id TEXT UNIQUE NOT NULL,
ip_address TEXT, ip_address TEXT,
user_agent TEXT, user_agent TEXT,
started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, started_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
last_heartbeat TIMESTAMP DEFAULT CURRENT_TIMESTAMP, last_heartbeat TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
ended_at TIMESTAMP, ended_at TIMESTAMP WITH TIME ZONE,
is_active BOOLEAN DEFAULT TRUE is_active BOOLEAN DEFAULT TRUE
); );
-- Audit-Log-Tabelle für Änderungsprotokolle -- Audit-Log-Tabelle für Änderungsprotokolle
CREATE TABLE IF NOT EXISTS audit_log ( CREATE TABLE IF NOT EXISTS audit_log (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, timestamp TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
username TEXT NOT NULL, username TEXT NOT NULL,
action TEXT NOT NULL, action TEXT NOT NULL,
entity_type TEXT NOT NULL, entity_type TEXT NOT NULL,
@@ -61,7 +64,7 @@ CREATE TABLE IF NOT EXISTS backup_history (
backup_type TEXT NOT NULL, -- 'manual' oder 'scheduled' backup_type TEXT NOT NULL, -- 'manual' oder 'scheduled'
status TEXT NOT NULL, -- 'success', 'failed', 'in_progress' status TEXT NOT NULL, -- 'success', 'failed', 'in_progress'
error_message TEXT, error_message TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
created_by TEXT NOT NULL, created_by TEXT NOT NULL,
tables_count INTEGER, tables_count INTEGER,
records_count INTEGER, records_count INTEGER,
@@ -77,9 +80,9 @@ CREATE INDEX idx_backup_history_status ON backup_history(status);
CREATE TABLE IF NOT EXISTS login_attempts ( CREATE TABLE IF NOT EXISTS login_attempts (
ip_address VARCHAR(45) PRIMARY KEY, ip_address VARCHAR(45) PRIMARY KEY,
attempt_count INTEGER DEFAULT 0, attempt_count INTEGER DEFAULT 0,
first_attempt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, first_attempt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
last_attempt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, last_attempt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
blocked_until TIMESTAMP NULL, blocked_until TIMESTAMP WITH TIME ZONE NULL,
last_username_tried TEXT, last_username_tried TEXT,
last_error_message TEXT last_error_message TEXT
); );
@@ -93,7 +96,7 @@ DO $$
BEGIN BEGIN
IF NOT EXISTS (SELECT 1 FROM information_schema.columns IF NOT EXISTS (SELECT 1 FROM information_schema.columns
WHERE table_name = 'licenses' AND column_name = 'created_at') THEN WHERE table_name = 'licenses' AND column_name = 'created_at') THEN
ALTER TABLE licenses ADD COLUMN created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP; ALTER TABLE licenses ADD COLUMN created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP;
-- Setze created_at für bestehende Einträge auf das valid_from Datum -- Setze created_at für bestehende Einträge auf das valid_from Datum
UPDATE licenses SET created_at = valid_from WHERE created_at IS NULL; UPDATE licenses SET created_at = valid_from WHERE created_at IS NULL;

Datei anzeigen

@@ -1,5 +1,15 @@
FROM python:3.11-slim FROM python:3.11-slim
# Zeitzone setzen
ENV TZ=Europe/Berlin
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y tzdata \
&& ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime \
&& echo "Europe/Berlin" > /etc/timezone \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app WORKDIR /app
# Placeholder für Lizenzserver # Placeholder für Lizenzserver

Datei anzeigen

@@ -1,5 +1,11 @@
FROM nginx:alpine FROM nginx:alpine
# Zeitzone setzen
ENV TZ=Europe/Berlin
RUN apk add --no-cache tzdata \
&& ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime \
&& echo "Europe/Berlin" > /etc/timezone
# SSL-Verzeichnis erstellen # SSL-Verzeichnis erstellen
RUN mkdir -p /etc/nginx/ssl RUN mkdir -p /etc/nginx/ssl

Datei anzeigen

@@ -1,10 +1,12 @@
FROM postgres:14 FROM postgres:14
# Deutsche Locale installieren # Deutsche Locale und Zeitzone installieren
RUN apt-get update && apt-get install -y locales \ RUN apt-get update && apt-get install -y locales tzdata \
&& sed -i '/de_DE.UTF-8/s/^# //g' /etc/locale.gen \ && sed -i '/de_DE.UTF-8/s/^# //g' /etc/locale.gen \
&& locale-gen \ && locale-gen \
&& update-locale LANG=de_DE.UTF-8 \ && update-locale LANG=de_DE.UTF-8 \
&& ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime \
&& echo "Europe/Berlin" > /etc/timezone \
&& apt-get clean \ && apt-get clean \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
@@ -12,3 +14,7 @@ RUN apt-get update && apt-get install -y locales \
ENV LANG de_DE.UTF-8 ENV LANG de_DE.UTF-8
ENV LANGUAGE de_DE:de ENV LANGUAGE de_DE:de
ENV LC_ALL de_DE.UTF-8 ENV LC_ALL de_DE.UTF-8
# Zeitzone setzen
ENV TZ=Europe/Berlin
ENV PGTZ=Europe/Berlin

Datei anzeigen

@@ -0,0 +1,10 @@
{
"permissions": {
"allow": [
"Bash(python3:*)",
"Bash(pip install:*)",
"Bash(pip3 install:*)"
],
"deny": []
}
}

104
v2_testing/test_audit_raw.py Normale Datei
Datei anzeigen

@@ -0,0 +1,104 @@
#!/usr/bin/env python3
import requests
import urllib3
from datetime import datetime
from zoneinfo import ZoneInfo
from bs4 import BeautifulSoup
import time
# Disable SSL warnings for self-signed certificate
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# Test configuration
base_url = "https://admin-panel-undso.z5m7q9dk3ah2v1plx6ju.com"
fallback_url = "https://localhost:443"
admin_user = {"username": "rac00n", "password": "1248163264"}
def test_audit_raw():
"""Test audit log timezone by parsing HTML"""
session = requests.Session()
print("Audit Log Raw HTML Test")
print("=" * 50)
# Test connection
try:
test_response = session.get(base_url, verify=True, timeout=5)
verify_ssl = True
base_url_local = base_url
print(f"✓ Using external URL: {base_url}")
except:
base_url_local = fallback_url
verify_ssl = False
print(f" Using fallback URL: {fallback_url}")
# Show current times
utc_now = datetime.now(ZoneInfo("UTC"))
berlin_now = datetime.now(ZoneInfo("Europe/Berlin"))
print(f"\nCurrent times:")
print(f" UTC: {utc_now.strftime('%Y-%m-%d %H:%M:%S')}")
print(f" Berlin: {berlin_now.strftime('%Y-%m-%d %H:%M:%S')}")
# Logout first
session.get(f"{base_url_local}/logout", verify=verify_ssl)
time.sleep(1)
# Login
response = session.get(f"{base_url_local}/login", verify=verify_ssl)
login_data = {
"username": admin_user["username"],
"password": admin_user["password"]
}
response = session.post(f"{base_url_local}/login", data=login_data, verify=verify_ssl, allow_redirects=False)
if response.status_code != 302:
print(f"✗ Login failed")
return
print("✓ Login successful")
# Get audit log
print("\nFetching audit log...")
response = session.get(f"{base_url_local}/audit", verify=verify_ssl)
if response.status_code == 200:
# Save raw HTML for inspection
with open("audit_log_raw.html", "w", encoding="utf-8") as f:
f.write(response.text)
print("✓ Saved raw HTML to audit_log_raw.html")
# Parse with BeautifulSoup
soup = BeautifulSoup(response.text, 'html.parser')
# Find all table rows
rows = soup.find_all('tr')
print(f"✓ Found {len(rows)} table rows")
# Look for LOGIN_SUCCESS in recent entries
for i, row in enumerate(rows[:5]): # Check first 5 rows
cells = row.find_all('td')
if cells and len(cells) > 1:
timestamp_cell = cells[0].text.strip()
action_cell = cells[2].text.strip() if len(cells) > 2 else ""
if i == 0:
print(f"\nFirst row details:")
print(f" Timestamp: {timestamp_cell}")
print(f" Action: {action_cell}")
if "LOGIN_SUCCESS" in action_cell:
print(f"\nFound LOGIN_SUCCESS entry:")
print(f" Timestamp: {timestamp_cell}")
# Check if timestamp contains current Berlin hour
if berlin_now.strftime("%H:") in timestamp_cell:
print(f" ✓ Contains Berlin hour ({berlin_now.strftime('%H:')})")
elif utc_now.strftime("%H:") in timestamp_cell:
print(f" ✗ Contains UTC hour ({utc_now.strftime('%H:')})")
break
else:
print(f"✗ Failed to fetch audit log: {response.status_code}")
if __name__ == "__main__":
test_audit_raw()

Datei anzeigen

@@ -0,0 +1,113 @@
#!/usr/bin/env python3
import requests
import urllib3
from datetime import datetime
from zoneinfo import ZoneInfo
import time
import re
# Disable SSL warnings for self-signed certificate
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# Test configuration
base_url = "https://admin-panel-undso.z5m7q9dk3ah2v1plx6ju.com"
fallback_url = "https://localhost:443"
admin_user = {"username": "rac00n", "password": "1248163264"}
def test_audit_simple():
"""Simple test to check audit log timestamps"""
session = requests.Session()
print("Audit Log Timezone Check")
print("=" * 50)
# Test connection
try:
test_response = session.get(base_url, verify=True, timeout=5)
verify_ssl = True
base_url_local = base_url
print(f"✓ Using external URL: {base_url}")
except:
base_url_local = fallback_url
verify_ssl = False
print(f" Using fallback URL: {fallback_url}")
# Show current times
utc_now = datetime.now(ZoneInfo("UTC"))
berlin_now = datetime.now(ZoneInfo("Europe/Berlin"))
print(f"\nCurrent times:")
print(f" UTC: {utc_now.strftime('%Y-%m-%d %H:%M:%S')}")
print(f" Berlin: {berlin_now.strftime('%Y-%m-%d %H:%M:%S')}")
print(f" Difference: {(berlin_now.hour - utc_now.hour) % 24} hours")
# Login
response = session.get(f"{base_url_local}/login", verify=verify_ssl)
login_data = {
"username": admin_user["username"],
"password": admin_user["password"]
}
response = session.post(f"{base_url_local}/login", data=login_data, verify=verify_ssl, allow_redirects=False)
if response.status_code != 302:
print(f"✗ Login failed")
return
print("✓ Login successful")
# Create a distinctive action to find in audit log
print("\nCreating a test action...")
# Try to access customers page (this will create an audit entry)
response = session.get(f"{base_url_local}/customers", verify=verify_ssl)
# Now check audit log
print("\nChecking audit log...")
response = session.get(f"{base_url_local}/audit", verify=verify_ssl)
if response.status_code == 200:
# Look for table rows with class="table"
# Extract just the table content
table_match = re.search(r'<table class="table[^>]*>(.*?)</table>', response.text, re.DOTALL)
if table_match:
table_content = table_match.group(1)
# Find all <tr> tags
rows = re.findall(r'<tr[^>]*>(.*?)</tr>', table_content, re.DOTALL)
print(f"✓ Found {len(rows)} audit log entries")
if rows:
# Get the first data row (skip header)
first_row = rows[1] if len(rows) > 1 else rows[0]
# Extract timestamp from first cell
timestamp_match = re.search(r'<td[^>]*>([^<]+)</td>', first_row)
if timestamp_match:
timestamp = timestamp_match.group(1).strip()
print(f"\nMost recent audit entry timestamp: {timestamp}")
# Check if it contains current Berlin hour
berlin_hour = berlin_now.strftime("%H:")
utc_hour = utc_now.strftime("%H:")
if berlin_hour in timestamp:
print(f"✓ Timestamp contains Berlin hour ({berlin_hour})")
print("✓ Audit log is using Berlin timezone!")
elif utc_hour in timestamp:
print(f"✗ Timestamp contains UTC hour ({utc_hour})")
print("✗ Audit log is still using UTC")
print("\nTo fix this:")
print("1. cd ../v2")
print("2. docker-compose down")
print("3. docker-compose build --no-cache")
print("4. docker-compose up -d")
else:
print("⚠ Could not determine timezone from timestamp")
else:
print(f"✗ Failed to fetch audit log: {response.status_code}")
if __name__ == "__main__":
test_audit_simple()

Datei anzeigen

@@ -0,0 +1,123 @@
#!/usr/bin/env python3
import requests
import urllib3
from datetime import datetime
from zoneinfo import ZoneInfo
import time
import re
# Disable SSL warnings for self-signed certificate
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# Test configuration
base_url = "https://admin-panel-undso.z5m7q9dk3ah2v1plx6ju.com"
fallback_url = "https://localhost:443"
admin_user = {"username": "rac00n", "password": "1248163264"}
def test_audit_timezone():
"""Test audit log timezone specifically"""
session = requests.Session()
print("Audit Log Timezone Test")
print("=" * 50)
# Test connection
try:
test_response = session.get(base_url, verify=True, timeout=5)
verify_ssl = True
base_url_local = base_url
print(f"✓ Using external URL: {base_url}")
except:
base_url_local = fallback_url
verify_ssl = False
print(f" Using fallback URL: {fallback_url}")
# Show current times
utc_now = datetime.now(ZoneInfo("UTC"))
berlin_now = datetime.now(ZoneInfo("Europe/Berlin"))
print(f"\nCurrent times:")
print(f" UTC: {utc_now.strftime('%Y-%m-%d %H:%M:%S')}")
print(f" Berlin: {berlin_now.strftime('%Y-%m-%d %H:%M:%S')}")
print(f" Offset: +{(berlin_now.hour - utc_now.hour) % 24} hours")
# First logout if already logged in
session.get(f"{base_url_local}/logout", verify=verify_ssl)
time.sleep(1)
# Login (this will create a new audit log entry)
print(f"\nPerforming login at {berlin_now.strftime('%H:%M:%S')} Berlin time...")
response = session.get(f"{base_url_local}/login", verify=verify_ssl)
if response.status_code != 200:
print(f"✗ Failed to access login page: {response.status_code}")
return
login_data = {
"username": admin_user["username"],
"password": admin_user["password"]
}
response = session.post(f"{base_url_local}/login", data=login_data, verify=verify_ssl, allow_redirects=False)
if response.status_code != 302:
print(f"✗ Login failed: {response.status_code}")
return
print("✓ Login successful")
# Immediately check audit log
print("\nChecking audit log for fresh LOGIN_SUCCESS entry...")
response = session.get(f"{base_url_local}/audit", verify=verify_ssl)
if response.status_code == 200:
# Look for timestamps in the audit log
# Pattern to find timestamps in format YYYY-MM-DD HH:MM:SS
timestamp_pattern = r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})'
# Find all timestamps
timestamps = re.findall(timestamp_pattern, response.text)
if timestamps:
print(f"✓ Found {len(timestamps)} timestamps in audit log")
# Check the most recent timestamp (should be first in list)
if timestamps:
latest_timestamp = timestamps[0]
print(f"\nMost recent timestamp: {latest_timestamp}")
# Extract hour from timestamp
hour_match = re.search(r' (\d{2}):', latest_timestamp)
if hour_match:
log_hour = int(hour_match.group(1))
berlin_hour = berlin_now.hour
utc_hour = utc_now.hour
print(f" Timestamp hour: {log_hour:02d}")
print(f" Berlin hour: {berlin_hour:02d}")
print(f" UTC hour: {utc_hour:02d}")
if log_hour == berlin_hour:
print("\n✓ Audit log is using Berlin time!")
elif log_hour == utc_hour:
print("\n✗ Audit log appears to be using UTC time")
print(" → Docker containers may need to be rebuilt")
else:
print("\n⚠ Could not determine timezone (timestamp doesn't match either timezone)")
else:
print("✗ No timestamps found in audit log")
# Also check for "vor X Minuten" text which might indicate timezone
if "vor 0 Minuten" in response.text or "vor 1 Minute" in response.text:
print("\n✓ Recent entries found (German time format)")
else:
print(f"✗ Failed to access audit log: {response.status_code}")
print("\n" + "=" * 50)
print("If timestamps show UTC instead of Berlin time:")
print("1. Run: docker-compose down")
print("2. Run: docker-compose build --no-cache")
print("3. Run: docker-compose up -d")
if __name__ == "__main__":
test_audit_timezone()

159
v2_testing/test_timezone.py Normale Datei
Datei anzeigen

@@ -0,0 +1,159 @@
#!/usr/bin/env python3
import requests
import urllib3
from datetime import datetime
from zoneinfo import ZoneInfo
import json
import time
# Disable SSL warnings for self-signed certificate
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# Test configuration
# Use external URL for testing
base_url = "https://admin-panel-undso.z5m7q9dk3ah2v1plx6ju.com"
# Fallback to localhost if external URL is not accessible
fallback_url = "https://localhost:443"
admin_user = {"username": "rac00n", "password": "1248163264"}
def test_timezone_functionality():
"""Test if timestamps are in German timezone (Europe/Berlin)"""
session = requests.Session()
print("Testing Timezone Functionality (Europe/Berlin)")
print("=" * 50)
# Test connection to external URL first
global base_url
try:
test_response = session.get(base_url, verify=True, timeout=5)
print(f"✓ Using external URL: {base_url}")
except:
base_url = fallback_url
print(f" Using fallback URL: {base_url}")
# Get current time in UTC and Berlin
utc_now = datetime.now(ZoneInfo("UTC"))
berlin_now = datetime.now(ZoneInfo("Europe/Berlin"))
print(f"Current UTC time: {utc_now.strftime('%Y-%m-%d %H:%M:%S %Z')}")
print(f"Current Berlin time: {berlin_now.strftime('%Y-%m-%d %H:%M:%S %Z')}")
print(f"Time difference: {(berlin_now.hour - utc_now.hour) % 24} hours")
print()
# Login
verify_ssl = base_url != fallback_url # Verify SSL for external URL only
response = session.get(f"{base_url}/login", verify=verify_ssl)
if response.status_code != 200:
print(f"✗ Failed to access login page: {response.status_code}")
return
login_data = {
"username": admin_user["username"],
"password": admin_user["password"]
}
response = session.post(f"{base_url}/login", data=login_data, verify=verify_ssl, allow_redirects=False)
if response.status_code != 302:
print(f"✗ Login failed: {response.status_code}")
return
print("✓ Login successful")
print()
# Test 1: Create a test license to check created_at timestamp
print("Test 1: Creating test license to check timestamps...")
# First, get a customer ID or create one
license_data = {
"customer_select": "new",
"customer_name": f"Timezone Test Customer {int(time.time())}",
"customer_email": f"timezone-test-{int(time.time())}@example.com",
"license_key": f"AF-{berlin_now.strftime('%Y%m')}T-TEST-TIME-ZONE",
"license_type": "test",
"max_sessions": "1",
"start_date": berlin_now.strftime("%Y-%m-%d"),
"duration": "1",
"duration_unit": "days"
}
response = session.post(f"{base_url}/create", data=license_data, verify=verify_ssl)
if response.status_code == 200:
print("✓ Test license created")
else:
print(f"✗ Failed to create license: {response.status_code}")
# Test 2: Check audit log for timezone
print("\nTest 2: Checking audit log timestamps...")
response = session.get(f"{base_url}/audit", verify=verify_ssl)
if response.status_code == 200:
# Extract the most recent audit log entry timestamp from HTML
if "CREATE" in response.text and license_data["license_key"] in response.text:
print("✓ Audit log entry created")
# Check if timestamps in audit log appear to be in German time
# This is a simple check - in production you'd parse the HTML properly
current_hour = berlin_now.strftime("%H:")
if current_hour in response.text:
print(f"✓ Audit log shows Berlin time (contains {current_hour})")
else:
print(f"⚠ Audit log might show UTC time (doesn't contain {current_hour})")
else:
print(f"✗ Failed to access audit log: {response.status_code}")
# Test 3: Check backup functionality for filename timezone
print("\nTest 3: Testing backup filename timezone...")
response = session.post(f"{base_url}/backup/create", verify=verify_ssl)
if response.status_code == 302: # Redirect after backup
print("✓ Backup created")
# Check backup list
response = session.get(f"{base_url}/backups", verify=verify_ssl)
if response.status_code == 200:
# Check if backup filename contains current Berlin hour
berlin_hour = berlin_now.strftime("%H")
if f"_{berlin_hour}" in response.text:
print(f"✓ Backup filename uses Berlin time (contains hour {berlin_hour})")
else:
utc_hour = utc_now.strftime("%H")
if f"_{utc_hour}" in response.text:
print(f"✗ Backup filename might use UTC time (contains hour {utc_hour})")
else:
print("⚠ Could not determine timezone from backup filename")
else:
print(f"✗ Failed to create backup: {response.status_code}")
# Test 4: Check session timestamps
print("\nTest 4: Checking session timestamps...")
response = session.get(f"{base_url}/sessions", verify=verify_ssl)
if response.status_code == 200:
print("✓ Accessed sessions page")
# Active sessions should show current time
if "Keine aktiven Sessions" not in response.text:
print("✓ Session data available")
else:
print(" No active sessions to check (this is normal)")
else:
print(f"✗ Failed to access sessions: {response.status_code}")
# Test 5: Database timezone check
print("\nTest 5: Summary")
print("-" * 30)
time_diff = (berlin_now.hour - utc_now.hour) % 24
if time_diff == 1:
print("✓ Timezone offset is +1 hour (CET - Central European Time)")
elif time_diff == 2:
print("✓ Timezone offset is +2 hours (CEST - Central European Summer Time)")
else:
print(f"⚠ Unexpected timezone offset: {time_diff} hours")
print("\nNote: All new timestamps should be in Europe/Berlin timezone")
print("Existing data might still show UTC until updated")
if __name__ == "__main__":
test_timezone_functionality()

Datei anzeigen

@@ -0,0 +1,122 @@
#!/usr/bin/env python3
import requests
import urllib3
from datetime import datetime
from zoneinfo import ZoneInfo
import re
# Disable SSL warnings for self-signed certificate
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# Test configuration
base_url = "https://admin-panel-undso.z5m7q9dk3ah2v1plx6ju.com"
fallback_url = "https://localhost:443"
admin_user = {"username": "rac00n", "password": "1248163264"}
def test_timezone_simple():
"""Simple test to verify timezone is Europe/Berlin"""
session = requests.Session()
print("Timezone Verification Test (Europe/Berlin)")
print("=" * 50)
# Test connection
try:
test_response = session.get(base_url, verify=True, timeout=5)
verify_ssl = True
print(f"✓ Using external URL: {base_url}")
except:
base_url_local = fallback_url
verify_ssl = False
print(f" Using fallback URL: {fallback_url}")
else:
base_url_local = base_url
# Show current times
utc_now = datetime.now(ZoneInfo("UTC"))
berlin_now = datetime.now(ZoneInfo("Europe/Berlin"))
print(f"\nCurrent times:")
print(f" UTC: {utc_now.strftime('%Y-%m-%d %H:%M:%S')}")
print(f" Berlin: {berlin_now.strftime('%Y-%m-%d %H:%M:%S')}")
# Calculate offset
time_diff = (berlin_now.hour - utc_now.hour) % 24
if berlin_now.day != utc_now.day:
# Handle day boundary
if berlin_now.day > utc_now.day:
time_diff = (berlin_now.hour + 24 - utc_now.hour) % 24
else:
time_diff = (berlin_now.hour - utc_now.hour - 24) % 24
print(f" Offset: +{time_diff} hours")
# Login
response = session.get(f"{base_url_local}/login", verify=verify_ssl)
if response.status_code != 200:
print(f"\n✗ Failed to access login page: {response.status_code}")
return
login_data = {
"username": admin_user["username"],
"password": admin_user["password"]
}
response = session.post(f"{base_url_local}/login", data=login_data, verify=verify_ssl, allow_redirects=False)
if response.status_code != 302:
print(f"\n✗ Login failed: {response.status_code}")
return
print(f"\n✓ Login successful")
# Check dashboard for any timestamps
print("\nChecking for timestamps in dashboard...")
response = session.get(f"{base_url_local}/", verify=verify_ssl)
if response.status_code == 200:
# Look for time patterns in dashboard
time_pattern = r'\d{2}:\d{2}:\d{2}'
times_found = re.findall(time_pattern, response.text)
if times_found:
print(f"✓ Found {len(times_found)} timestamps in dashboard")
# Check if any match current Berlin hour
berlin_hour = berlin_now.strftime("%H:")
if any(berlin_hour in time for time in times_found):
print(f"✓ Timestamps appear to use Berlin time (found {berlin_hour}xx)")
else:
print(f" Could not confirm timezone from timestamps")
else:
print(" No timestamps found in dashboard")
# Check audit log
print("\nChecking audit log for LOGIN timestamp...")
response = session.get(f"{base_url_local}/audit", verify=verify_ssl)
if response.status_code == 200:
# Extract the most recent LOGIN entry
if "LOGIN_SUCCESS" in response.text:
# Look for timestamp near LOGIN_SUCCESS
berlin_time_str = berlin_now.strftime("%Y-%m-%d %H:")
if berlin_time_str in response.text:
print(f"✓ Audit log shows Berlin time (contains {berlin_time_str}xx)")
else:
utc_time_str = utc_now.strftime("%Y-%m-%d %H:")
if utc_time_str in response.text:
print(f"✗ Audit log might show UTC time (contains {utc_time_str}xx)")
else:
print(f" Could not determine timezone from audit log")
# Summary
print("\n" + "=" * 50)
print("Summary:")
if time_diff == 1:
print("✓ System timezone offset: +1 hour (CET - Winter time)")
elif time_diff == 2:
print("✓ System timezone offset: +2 hours (CEST - Summer time)")
else:
print(f"⚠ Unexpected timezone offset: +{time_diff} hours")
print("\nNote: The system should use Europe/Berlin timezone for all new data.")
print("Existing data created before the timezone change may still show UTC.")
if __name__ == "__main__":
test_timezone_simple()