diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 7fc576c..6640087 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -48,7 +48,8 @@ "Bash(find:*)", "Bash(openssl x509:*)", "Bash(cat:*)", - "Bash(openssl dhparam:*)" + "Bash(openssl dhparam:*)", + "Bash(rg:*)" ], "deny": [] } diff --git a/JOURNAL.md b/JOURNAL.md index 2bbebe4..d55544d 100644 --- a/JOURNAL.md +++ b/JOURNAL.md @@ -1210,4 +1210,85 @@ Die Session-Daten werden erst gefüllt, wenn der License Server API implementier - ✅ Login funktioniert wieder nach 2+ Fehlversuchen - ✅ CAPTCHA wird nur angezeigt wenn Keys konfiguriert sind - ✅ Für PoC-Phase ohne CAPTCHA nutzbar -- ✅ Produktiv-ready wenn CAPTCHA-Keys eingetragen werden \ No newline at end of file +- ✅ 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; +``` \ No newline at end of file diff --git a/backups/backup_v2docker_20250608_075834_encrypted.sql.gz.enc b/backups/backup_v2docker_20250608_075834_encrypted.sql.gz.enc new file mode 100644 index 0000000..4c0d6bd --- /dev/null +++ b/backups/backup_v2docker_20250608_075834_encrypted.sql.gz.enc @@ -0,0 +1 @@ +gAAAAABoRUKqa6JmvYu3c6xIwYDltw4q6cJgFggFhee20YKw7GpzLLvT6mEjtqnws-moMdGF7b_eyvqVJd25zvmRnGfBL-ZxfOd2BwfT0a_iMXheg6c8BSyj6laCJFkMwuN8Rc3QYztYuMzpK14NQF_AIDCZV9NK43XH5a1ltgIlGjxH_iyNdbCoNwsJ4LCuEawgOhs8hSgF920G0MmnYIkAfsEKGR7F-IZRZQR6OhqPVadsvS7LJi335isvVy0Ilen7OuCQfYFUnEZWgh_HImiycVTAfW2bU2m93zIPqaH769bAoBTswsTeXXfzOLB0_xQVZr_Gq4ADAwg5G_HYVuv_TcPAixxBu9EVbIDr0DymLrZnesZaT8PjV1NNgfsFLW3a21NO6mclr-cYJVxWo8Jm_917d-dkiTc4avrQdnmkDqXEBvKSrI89mw-A-CuC2u-lGa4lxK55uQwtkaFHDHzNjOL8-3XD7WMrj_duuSQAnh_LM2aSzm0aGX2maKuWjBD2GWhFPJNkje1apVgZfurw0JQeg9y07uPcqcrLV6k7Jn0y-UULRQcpn4TGLf-V6W5YLqtG-yfHRWGFd831CJjqAWWS_ytn4f0KngNUCuATgU4ksiBzmCon5EzogEgcU2oGr3i7RBUI2CRsPKhkugm5bEPuGXCGh1hLMQgNWSiBO_w-2k9rD6ae_-2C2Pc5SxnUY7zCgy9D1HxU0Gxs55zoD94j3vO-ZdncUprnMmLek9PkKVRJdzShhMF6dy7lqBXinEaTfoJDOoMw4nhftd8KQymMLGyOJ9T6bWnPebzFwnK0yKsg-EwQyyzFCHEAIQp03EG8bQQtYNYsrEtmTEY1egarKUkr84c6-o2vYzxe_Wa4PwCQYxzcAp5N5dSK1aLC-K4b0UITQwLuAXhcfNsd6VGXkzQA0gjwFTs78AmfCBgHU7DlWqelEVidwgaXC88JJbxZ-m0qrbVpSdrBqZGroWliDxqZu_wKfsr7U8pd3zvu-St8JFREBuKLXj5y007ZgFciCDl3dchtgMW4VeI6Ro7GVh-rFEkQoAqpIHJs3adGEm0603RuYUzOyz5HI4XG8gdNR5pyrtfA2R1MRG5RP_LCCrsWkn2-8AP4HuC2uVLxTXh6-1iyLZGXKqq8mjg8ndCKtrw1avcFaLgzPRJm5ARv3nyy47nYgZbtLSUI-LeoKzH3dTvwenx4Ni6ie6Ls-9H3p9o3sO8vKxY28P8JZ6Lrg5jJTQn5VBQBldXuERN_6y-FbFR-mu06rq3Df85wI4BjMV-nH9786VIIptKdsPl2QElgT8qdgQR6IoeyyQF_LXF1jhAw3wUSkNp4agj4VftZepP8hEhj8tu7QTH57AED1vJ-LeA7W5m3FMh2JjpyyXY5yXTJLf_hOEc-LmYc9CoEsL72fLhFxm-xnvGIWeb9qEjJIFGOTWb5Zn8pnchF1Xu_iy3kNolgfG7XvusNra33uiXy3eix4X5De5XJ0g879uMQ6USj-_rrZiPINUpY4C80_e9GMU94wIJkFr9u60FU6P3a648DGUGNP5GGD90EfwXhPF4OWeGLEsFKArdGpW4t1JBW6G2lR3lJO9N3tfvxynLCwlh1oWtr9drlSt-36gC0i9X3I52YuHnMWSKrchH6a0QU5-pVEVMHTHMyDmfgpBwAG-XlQwtTPzzQzTluZO4F91VmCVFKFg_ZGMmcH-_mglPRVxhGUCd92VPcbEspynQHoy2eGt17KnFC-WTsvb49sGsk9BaA95_ed8IOtFkfnwIM_oQ064sqQnGquvulNxh8mglXu94gVP6dwm7fQM29CXcC5rBtbKr0pkGqtFAsrdFBwj1N7MSgrGmXjPOpWtXFININ8LROtFtlDdp2UXIrE50F9PVA_5RfExtnf0Ma6ieaP2EjsZFGahCb8SgEUKHt403JAgvBw8MUL9W0McPLSfOn2hq_AGr-3dXCrZkpFID1Bzac89iyO5LP588liHBClLiP-2NVMsUvRh5OZKBl3ALKrypjUFF2QcV0v55AL78auuMDawvbfy-2UT2vhnzwkAP9VStxp_MRnRn6qKOLi2uyNafmFA3fd1kUT-ZOp8umG12136-AOVqoTIV8-DcWmTOWjA_pDPmSM1DIIm8VAZY9VMt7JDI4_ExO7V06UKgvvhPZyqVyUqa2urnQM0Jz2wUvTErQ_5654qafZXhX9Dqu4WZT0E5RxsCB-9Meq7LLGQXNpEMg28zHLlwRErGDmQYnPJ0mjobNMmfj7qyPz-CcB8tA6qLwrcA9DKkbQAT8Lqtw8U5E9aYKfBWqenJqKb-8n8c7b6nJ7uMUAtAE_W3e9w3i408by-nnzgXrhwGqrBDWP7gR7E0KSx5KIKnii4Yo7yVahp7_DCjAb_eWhlrTWEzKTr0xkZlZmhTwskV7oA9Pq3DfDSOCuTW-J_8bHpkH_eddgo-nBIqxBQGqE8_g75H02y56iIfDHpVII8XwNuwzDeQXkfsOnOg7NS1ivNWe4AJ-NpLscoSv1x1WgwlcfiI73DjLBuZsIJf4ccelr5fkP2lMJ6QJuEKvLgrBXpDwPotnVG-G0gqmYuNhzrov3Yapk0ds8qnPtBNFD8HatWmhfGLjvTdnpakzM_NguAmn05bFnAjyWnRT-MwmATrngeKmrY9dpK0DJrtdDUCWibqPKTWNfPxm_qTGglyltqBdFiTIbQwqhs1byKS2M5ni6QhVo2rQa0PFftK_26Mf4se5bapN_TNUAyfzrwlDUXnBrZvNLn1LLM5mBDlXec80zE6uD1TXkYK1pX3fbs4kOmwTUaXmAXuIIi3xe5f9B6THt8ehWvLgNzbpY0LhX_3voRJKx4iuewtIiyM2_XkhOra8E2-0uXAVCTZYH48AWnt0Vcq7X1mUwdfVlJ4HSkRia5eaui17jvI8H9wosCKqPsWKK72c3CSlJlhK9RlvD_tNXCZyO4DjjuLhPOEaHYMIVm7T9AYztDfYdmdorP3ZCNWw-Qs_492sp7ufzQwpMdS6xCOeB2P3aqZZaSEY5xb4I-qD7xSBYR9blr7kkRc_sWIL4jMirQ-iJlCmTdaKmBNOKZ5WnTNuC9Pf1y1P7_6Lv570oVvzZikHiE6wll0asR3d4_BzdO4mvF406QNRsdX_mGnYXepoTgWmQhzKSJvtFkYzjeW3Wv3ztu-NVlW1Lj96H-s7pYzK7sfXaTpH5UR0x5LRrfdh4iQRLzZQipFkrff4Q8Kl4Y-eNR0CHHetrLF06ya_eDI5zqiAVPAH5Eidc8phohnN_G0EHCGXoNRcHF6m0bJdtkMYg4lgcvZrz7VsAFXPa0KPMrjfTTL2C1nYgOUu-pQ3gvjzRcUu4uMyWmq_u2K86iMelIivWWZ6X-4hqpaFXlCD7Z55JOm8iparxN8vz1Y9A7NBkFF5AuHSKu7atbYYPK_2B92ANDjN2slSehtcMwGaRYACCPWOiFeOMju0B9PoFIa3oDKUu_jk8FOK-9C2Z2He9Ptk63GeEBzpP5T0o1HDlyuMKRmwbRMzHvB6pdhnwt7ihTuPC2YHHJlAXyxVa6uoHDKjfQ49iIKMsVfFUee3x1cXsNN_E4Nza8gKn_6rZWYwQIbJ5o3lAd3NUvpygcXlQTDZnwikwqUXbJbnn3G-0NI1udstSu-SELJy9RyliWKNiUF0nBvju4B_DCYZl67m8uTamjroSGvddl7xB2BowkPJCaha_nUUzyU7N_FavBzue4V9WZFP5b8_Hbptm29Qdc5XsDkjmYR9xfrA7Dm-LD9SyxPqBYCpRYLCaYObhepsBDprsCoRXxtnKeytxEsg9nbZ75tJCENWAwp0thYhYNLBhHUh75uoJqXpGUCR7JBeXLrZBCcJI15GEkOwqKYMlCYqs4jB24Q0hQhQh2-06KjFEDcLiQQuVvTMyNDbXtPAWVqEDhf4-Y6OJ-u5ncVOvrx4t35V9h938Y6-Nhj2IWLmJxwDc5ieBC9rTSSvZRGzoBt-FgoMVBFbI-Bkrub5vONDZYSUJXD3-H53sihpfMCEkJZ1rDZLkEBFVBCDyv4Wh4xWvXjNWU4KDIO4mt_q8-gJKQe36fRZpw4Vc_16k0T9Zn2Ft-TwOLgS8arrICe3JRhwVH4EQfWTvREejCf8JKHWIcWjF0CvjqaAhaCXL5fVwWhg80cVs77xthv4qBqjOuyDuVFfxXEXzZy08YUcXSOwhPpO8cYieNYLgFo2aoIso6PUeu0K58iB36dXXD0A81P7OyRpyIayM10bRxjTxmPQuG-Keet5axNuvMsuAXB00bpTa6nKWeztwFoo6R5J3J_Hvz0XSH0tceVkbXe2Rd_f_gCx50auta2uAxAzSUhTHyKawRVMMYlDb3JlhHC7lwm-nUw5Yin75Sa1kfV3OWfpXEmno6NkXx-qC3BovGyxy6Ywaxj3yjXQFD-QVm8ZM6OSZC3JV7bX42L2qDAkebQpEIWSlsNQqJKWBscSu0Qtl_4ZIcgsblJWHIKc4Ra147fYRiqJsdkfkk5-69G1-Ob1EDDpkg6_z-kjHB7tZOa5tmdsDo89i1GEqSn-CyQJ4SA6EC9huXmA0lYrni-pGDXmu4hFXZZcHVSfiak0jR-TAZaMtKPWbIAGiz9b19paxTWw140uQvgl3U9N-dkHN8owzpIsigSZPtR2FGQ2ffOPUC0d-Me8s-nFQH8bRTeng-swum-7YtxVhiWgixojAEnyZlSnaxNckUoaZFtikQeSx4Bn9wYpxHIP-gJkNLOpIS0awn6uyr1q6lEgkJrZr9VcKGvRdLvKQ19cDwiUGzPDb8D8usjB9Jc-YLW3cN21aFbSRIofoihUPAUIVE1sJ6x-PUklJn_3qHxzhLuu1b5wou_KBWyK1izPpxE4ahNe9kcVj6WOJM6FiNw0n0X1Plnt--Tsc5Lma3oQljYq-jaSWIaCDYT-6mjcpP6CTCnUZigXvi1PXFxdFy80X36ed27iIiLDK8Sdkf54sr-dKkJJFcuLsMO1yKvrkYsbWfmM2j5lewHuEb5CnrWUwELXlQyU5RjiRviUj_s4WQJIko5BEGlzb-u-vIux08FJcjszLZ15_deUEUI6D-XJ6v4JHEeJyk2Ddklmev8ftUP_2Rm8ruSdZYOdayeYHWjqu6gVeSpt2120oY2CgQFS1xZ0uEjmJ-uppIhDWFcbEJqnFxvUD4owMg00KfjaH1QekzH-hWXiqIr9dOcfXYY6ybWaotYGn5Q8RSoWWb9Rds_LFuLxudHuthEY_h3peUO7CcNFm9MSUniNbderg0W9Ifuw6PSHIb5qJmHm1hpOdTkL9RcmWGUx2btECCr2_E_xl2Ocl4vjVHttLK6vyPwaPfClM4eAA3Uyxz51MP6HJxfMXx_o9n_ADNvB6qUFF0SVl-Mv3zp7SGN-YCF5XIKopriOVWtQVc8VRFNgw1c78Jquw3eUlOBRBPNSm1w3fNJI_R3E7VKCN4xFoITZ7J0n3b3NRqY-RqX3iThyvfzmB4-Bolc6jUPwks3IKwTOD-hETFj7f9Zjvox_Uty_gZniHhTViPzHgV9QsXJmPEMQCShED8us4cjVzVWH-UcQVzKoPpbWVQ5MY0mMF8NG16j2vNG_6FbwWUkg_5J57JLj09anYJN3VMvcagKTIbN6oo7Pcj5cWRra_xbK8CcbsEvegFxlNi-pCGcr40hAJtd9zDomcXqGeVQVMhac0ZSY5gnfze7-4NsDS7CpHVN3rOl8B6qojEZPKY8GXWWaqJqjnJmQepkSLSgn6wN-ThV1bGc8K1DQyTIdhUkja3jBBlAKAawc6WXL7tgJyBrZ51LoVtI3A1cmUeaTFBclb_VzDBmEX6rivvr-VboVqRn9TITh7O-03JJxQ9R-orKtcJe_IQ-NGKNIdvK-xScEv6WTh3gZd7tmi9wTVtb55voXDfLf53DLIGtcH3AyJXBU_3FUhsAVBXGWqEtsYEqw848nr4FJm6NcImOQvm_VyQcechwMv2vMnjTxYGFsT4F53dOCUmiEJjBxgU5ortHerkKvYOIb6bVPLyVngtGIO0OjY9fhldP83RbUY7YoHMofqTXoYjiH9fXPu00umh87N5hYE3i5MC8n-_qjpvT-dxCsaVP8Ud1d9vKlysZyLziwOqGgnRfG1VuOMoVhADfIIEIlLW3bnMaxvuIFMEvmnjPgIvOeHeX3D5nMgVGfHCxDWpjiF5hhW7_DBuCoF-QNYslQjLFxKhF_BDoxoYe8OpD5oPZsuoWCBXYt_K1am5vs5pRd8s6fv9FmUpvpEHGRSF7UtzBAs25kph7sQwqNOTDzWO8pwaUL5UL8J8yqdpWgZHue5o_ktxgWjadEkaYljw-pBzJ8b8aifYWF3ogrJ2Vvn4W9HBq1RH6WWc-OF72MQbjUBiZ2eU5MoQitCwn6A5zLAgnD0nxSsslPrjP0ZqRTImcTYcXWJviEK_o_7xnkPmwGI_E7km5fEP-owYRH7tnkB23CqGuryiN52zfaL7vpJozfa7wKABRmHjTn6CGC0zziDybaah7TTgR9ARkTRNy7UfyJU08Bms8ho5fL8GANpqxff_QDMNeMSSzRPj73b3ntnx93RE90GFgy8B4H7tFQElZ37imiHg0dALdETI1qE90qTFbaoFldv-HNPbRq-8kqklu5juhE3UR3w4H_XNJonLivtU0oQPRk-FpV7yyVdLIrhqrzfAjB07uzRak9AToYfg4iLIniWgfCQf7MVh66x4Nb2_yDZFAP7bmE0MY5VjuEAZ18NFjLegCMzAiGc2yCMlqXJyT3CX4rZ2QQm_B6UaHW4U34TV1RWo_P4r5fO50M3oM1truj_gvExe56niuHNqpo_LRP2RLKwbogJGqUUKQs8-mL_sDuPCrr9aVGq7JbzrunQLwd8-lmqHLsfEPhPpEh_WTgm1BwTsSWofUhaUFAMGSOoYdsnBPzynqZ_gt_C2DKly1GurzTIPm2mBcHC8BNlLUnt-Ai-2sO6eE9qhJPJH8sYVuvS3IlqpPS00jr4sElZTclYJs8uWxagagFQNsyYixn-aBFDO4OogyF2wZLVHhjFPZWW2G1UchU71aH_XzCCt3ONFpSr13QFXWFERQpl7yjP8C6tJDnZVnheavjPhH8xXYgrOJiWL1CyXLCa8rT8bnp_onIYX0f5yssl-Zw5tRsz0Lf-kdqShcaXO7GiTVzVpyqO-ARzzcsXp0TU9-c2lRoQRHwAJDbKIJ1VJDDHgszG2DXVEYQK7fVZwvXhslpkWZmdbAwvvYAY8oofzmFxpaxDxuRRSX9002JNlLKhxxdY-HT \ No newline at end of file diff --git a/backups/backup_v2docker_20250608_174930_encrypted.sql.gz.enc b/backups/backup_v2docker_20250608_174930_encrypted.sql.gz.enc new file mode 100644 index 0000000..797c966 --- /dev/null +++ b/backups/backup_v2docker_20250608_174930_encrypted.sql.gz.enc @@ -0,0 +1 @@ +gAAAAABoRc0qjs3vUkRcedDtrMGB3IUJVQQFDiJEdSLhhw3_ipY3Zyqvz3Zk4PcmfZJlF1tSQzJA7uwn1wEx5oSlu53F1Wxaxr8bHB2OgL51GiotdzlQUBFVswbqyVo9-tOo0CmlWWPp0B6Zzib6i0hpAe3IaCK_aTUZFNeTta-fwLUsKPgYEpuQADPFCJ8eY52zpahJL4GbmVcIIMn1Rge8ImBQuwhio6xXClRaoUVItYJjbpHF6h-rC37dKaK1593HHU4-TBForZRt1UBq_A4Z659hcdPYPG5xhe-8diop2vBHuJ2fEvIGNLC8NSyqUTSj8NiqsNcdBbVVDd_qpzvFPfD2M-aPz7puah94odfbr6CJp9fB4bjDdTEvP5esi8PsbgtpTZ2l0EGqcIGgpvgPeH4J7R9BSq9NcFmuCBm9tjYPv2P3SFPxC2EfMuqM1ZzD8G3UxPJSWn5grttVVxM3i6lQeOcsCo1tet03e93LnRcco2WA_PdcnKpKQUB_gZ23Ll81yIbQu8fXW5pqIo_RlObCgpnhPcQwXcabJeVOGAiSxM_PZLh_OZnlgYiySYnWXZMSDjbogbATui_OKEnNqHreONm3ELJ7u2730ZVqD192B-3y0AJgXWAZcIUnuM9c__sHwoOFa3IOFn-xXgdv_sWUSr3CyxUdc_DseyESL50iAHtYny17PccEmC5eo39xrcvdaOxSt3_rsmqKYDm9fPuIT5UnrHOcdrRaKGJPovmRdkVlvY6hk5SRALgZttxf9TF6zr_MfX3bbPn3hxFzuHemxHFH9gnD4ePmG5PGW6-Cw6G2nya5Riyw2OwbWwrgWmHKOlcvGkfMJBMM8CXoNjqGYjcFmMWSHEEa-9KJQDUBtvbIYMWZ5cK6QEdlhnjsEsYPkYkqxIvwzMdlIPNKX1EcqD4sI30OvTgb5ylYFjqPCnOOIoYlolTZ-diPKVehHbRJ1Dne_jnM-Z5XJlZ8C1xp865VxMUWAMqcCIaUIb8jq3sgIXQ0Evsw6HsfldVRnBFOxkqVEHEv6VtPBKmqqthubIcTE2rGAi0PWJ99Af2fPmP5Nb8UfmPxzi350EC5Ud50HpCCY0jUQsUL8ARPSSt6oqe1y8cfc5n0ymzaCz-Gr1wsVDimWusMVwDwciieM3fL36ffsX4WYz0D0rYBySvmsWAqbOWtdjPgg_28cVIRElnpkfwIvNUsfYw3lK_RtzdlpcjfvGqvRcpZrm3dSKsT2YogAPoA3XQTG_eoQl6zqJ9WCDYqnayiQdhzUILjyE1Dt70FYM-dHIzYTQwP5eto27gGYPDT5PruOQk22H2Wls8UXX5fJaMpY2cuOw0apPv3dXUtXC5yzzT7I6GpporMfaF2YHw-uMLgzRZKIwxpZU0OC5EQePtrZVWYNy_OtA2BNEsBQl8Ls1MA-nSpe91ki6mT70vsSRa3ynYLgD-R5SsRY_pHPdxq-Ps6vLyvTQH1lhOVVD_nbwvIDvGTR45ddKqomVKNBdagx8ahvJ0pDkwhZyk-W39BQJEoQBU9nRBmRn52zFtGH1zoiQ40K_1p8H-eOS8zDRxky7Myx1uEZLYYEUC_sEiiHA3Ywc3vxqUeogS_S7ulvdMDSOmwwk_NXbjd1pvF6ujp9XdFUql0yDXOS0xYdi0I8-K5t5gJrdhbmybSIR02HV0xJvYZPq0vxzYwUVsCdw61wcnQX_c8SsiNE7VgZJSbPwRQOGC2vUyo1j_VmDTrfM-rcIV47Nc9AzCtd60eb_bbia-PFDzW-acle8PyDuQkgTSYqKd0zfBgItcO9MI4H_5LPx8qgbEHruFEKjuJ6Cs1DIjXluuRKV1LOG9fe2ZVp9SEcUE7-M_yifA2cHyKfALRdKvFdporvTkwApxc_5FkHjAsEprPFZ-SWllJgjqk3mWlvWuSu3jOmZNCK1a03T2layTmRn7M9WjCSirPXRnhd2Xn8nVu1y0OEkiE034fO7Qwcg95vHWm6z4bbLPnglI2QvLxIjFLP6z7nETKv4ErNqLYSpoxUVuxNdlna9A6EmpRJwgAvOJta1mOlW11aOzXlK2EqXt6woO6H1VCQTuGbT1zFNqJV1jkTYf0QQctmg-hJ--ct0qQcNQh95VLiW3RwMYgBbT__RMSDX7PnA-f0w8FApvw8B-LAHiYgN5d5j7Di7XHwkDtrS-Hn5cTHbZ6Sy4TzgrsgYBuEGFxRKJh3_EsbWdh764Ecl3yyfAmaSIbWsoxOcxccqDuFqa97IHo6Qi_hDlj3UIEgvujF4Bao60UArYpbkmhViBXMh6fJ5Y85naPkcqQFITKS5xMnucgPOWKKRXJu3bYRcUdRZskSRkgAae15KLB29dtj_vUw1U7gnnSTpHf3sQXHP96zsYs0PerZveyE0qWmyc6Fyfr8IjAca9LLGD0NZI6ACFKKCRexkmuovc1JvbF6HEITcbUtdQHiCUWcmeI_9QYM8VT1BTEHjMecEcb1AKbSe_GdL7nVKV9OBhTT74kt-iCAD_ahyhIbT1ugsllZmHHh4Er7tPGkY3aYhX43OYmyAHXTZf3jeZnBhavWkWv7u1pvZLYxg2-ZtcntwgkFVphrFRl7Hy6F2-aQse3bgC5v6mopojw_lAG0xvnD8keAOH1I1DF7KiJxJ-KJMaqnyLtFKkh3jq9KfCqMpe_OgPS0OnLof89J5hVM1e90sinyqZzl154icm6wwCgftZi8EAbJeQyFeyjLk-X5SGWS4tnb6WBNPrkCYtPlv4CZjcLkfs3EonX2v7KBO9YAgF0Xc_iGKrg8UsYw5rZjI-0C8Kv3CQp6QCjJM4kfvA9HQPEmmgevhiBPWGdCGcaK9zfdNECVa7h9tt0ZTpEiMoKr55M0YecUMZlwjfINAoyxAxVmgo4YxJlgbMj9Yo3PcqNUKigeTFCTNldJGibLklV-5IJ4lH3NBW-6ojh-dGxZuX2BXN5HtEIj_awkFdJrRVa7JdYRNKTYumcZ_R16rpkk7weRORG30-q-PTN-spbTNk9qqeCrm8WtQoGlJsgEUyW4VR_Dv37fq-6-LjhV4FkW9OYaULwKgjZoNBIa4PogvkZuI_VWzbwAhptebw9n4zxI_wGbeUW4QTYTBgHAdRYaBJCPzU15qSEymgwxGazdgPUsw2AeWbvY_da1eq0yq6lsc9Ac6VW-s-NNNkk_tDP2XcuTHZYX-oDNml_ULD31YpDHnQrXfBklsVUOYamVXxYq5vEYAVZgxkZ9VQQjvAqlAANZQCZ46kPHN19hG084Xukq3TtH8RS5BpBmxmo-3TCZUECSylmzlLdJ5cAUSThKlVAat-AdQ2YvcS1xYMggUKL6CCHMIRT_DFSnsWCwZl9N_h89kpsYVg6K17pNlx38MPWFBPGADnR4UrpQvWefevETbezO1oeXl8c9LHiEysjXoAfjK7jFdjhXm92O57CUtkf_-D_KM_6MJqfCjFLH5nM59o4gSNfbsDvttltVIklnoqQjTtFv6gJNQNNcbhXgYqrKJoLjKAN83wfHJVQGF5JSlrsIBh-FQptZPYxiEB4aknmMgzoaHp-hdm2zv1fZyrZZpydW6uycOvBM5MqVNTY7JWWpo9_mvgeKR-HAUruBwglZ8pVabatmgWcVkGpEsRs-ULpnTrL59Lph34bniEPAfzTmMXr0Pk66epmvsPFrpJkBHZtWNw_sDmFHCGmdfUQoNjRhjxjEMgxf0uP0lD6pTwh4SrNBZcu7Tg48P6kH8ONgSQu699FKSFmP6ptFyIoBFt_f7MLuwg8jHKdwXZvhweshxp_a2cgiV_LtsZBtJGDKtao62ggSGYly0gdX9xBsxCoiSFWY-rfKcR6olWC-331qKbOc0_EQEZDcNBAm6WAq7NHBLtZlC4O32SIeZLTX5TUBu274SEww6hdNF0dL7en-EdhspXezG4jKRyheviN8foE_QAq_paSGMyzQzfxLTyNEoRAYq_R7_s6Ion2FY8KtIACZMAORP2n1lS_hVDpzu1-7JoJw8f8FygZnGwffKqL2XWgqyiTHeaa9NVeutnxhr1fHpVaNBKh05CSHbEW1eTgC6xlBTbOwCOBg3dmaQa0e3gAM554Bkr8aqS0auz_2z6H6Q3nhLad5KJzFlN6pJlUBKiMF4gxhalPXdMEKZSwyMjaYPqdKS9svPUI6WJkgdpElg4hG79fB3H1DV6UVgHPALF4NipN-xo_swOEivLUtqr_oBrfmRmYO7k5UDlZQbXjcIbzxNEKfEer-RW0weFB2u_8uJbEEN89HRmPouHSgtMQFfG-Z0I02VBuKFzI8PnZPmd9ImG33nrQYUCCi-oyOELkZJiFw595FYZEnheOpKHRiK3X4a0W3uDJts98qHMZ8Zb6w-gwKUoGwrI4MHXu5hhP_3btyDhXBq4eBi5o5X0594SKHvY02F1KmJ3BsE2_V8kd0UsBXsfLiPURsTJzDT5UqgMTXktBNSNqKKWxCPGB74P6pi29MWgaQyw7UQNhsTCO8-fqYYe47ENXBc8GYQ-EocMvaGUQBkEFy7-CAeb_wY4B55Sh2DdFik3nSmc7uYUzjY-pSCKzJ0NUxgLP61d8PjQCO7UJVK4crAwvF25GYTxobI43eyCnkePbDu1K3c98tkR51WWqIT5s_uOPM6pZjZPIkj1FliUFr-ygN0AtihYza1JMKG6Hh38buUbYWxWyrEmGP6SxdnNEngrkaPWO79Nt_29ytrdDS_Jbpcd4I8zx7MOhSbuPy3T8Xz9NvAMnAB1a2lpi79rvkOWdAJeHnRKv4pKsqhHjYX9m24bgiv-wRolUO2H18SYpqQJnM_XwzchzggZXIi7I4YN3j6qBX3puPEM1DNVYh6vZtMeTZkF8rydXPxD0vu6eH5MrL08d4Kir6jhEZ7KirT1G2nnvFf5QSSSx8nXlIVU93BYimlevV1fJ7SGoH6vhpwdClLtt5aBnKt2bx380QdVHGv6b4xQi9WSJ8rCyynY4BGjXYtNfRxiXvtuSMm_fXgQGKXfs7zQ97VLJV2VQ28JO8XmNTtBawje5ZNeDrgIu3LcRA0eZttKpX6v7znYeCUIctjOBlWJFnCp-crhGjBjoKtoNHNpZ18TjgqV9UdUcsg748dNvFtwxamCKJ8Ndaf3ITFac5sdoYH_GBMR6493lp4cQMMABFk_YH1_Dfgd6DRhBYkj2ahKFmHuFvClRf9BTCUmh6jGw-TzAWTByv2xuCl55HjN6VsyszOkrzAJov1GWZU6Ef3fhj1ElcH9AGhsAfxNKQk2sVl5HzeAA6EsJ0GSceQQTCbWueYqSOMzUEo_xmHizAggUXE6JWu00QG6L2lbCBSG6WOiJWtICFAemsOUEo7C0EtyG4cCclcbAdvHJ4Wk8D91ZIkb1C1XGa_OpgJ3fzoj4giEcJ4rzaplIJD6PYk28brvBLWbX98Wb2Xyi67ykEAsqHXOVnFT4BvBGNwkGJNrhvvCroZJYYtLdSewTPqV805xcj1lfypfgm1bKb-vbizii2f2hnwWGcc_eRuk5A5r1SMAW82Li6I1vYZpERtDkwvXHAgvFRYQZzMq-RbEdt4Mrbb5LQXOFtIBdbmO9N0hpdDQmKZlRkOsyRrgXYlJ0KnTpKp2uS63Torc_u8HmvUI7zxNVoAi4xud8w89d3K_EaucXwwu8OOK1c_hSM9GZQti2KuW9aaCjcI_J4FQR72KHrZW8xOFidJiWQfxXQpVOHLBpru20AkDPF-Gt5Rj65ZADAt8AKsffe4TzF3hAQno5zcGOffuSYY2WeKt3hOqV7H0VRXW_hrLnbipk_pB4pQ0pPKYzqKrJftiGp57UeruWMkjbPwVV_RvkVgtSrhegVX0IcVZt9dChTc03pgBzTK01tZ-UavAs3AKOQ2Thv8q4r8Orc_v_V9C4rVDrl_ncGsJYQx60JHSysqBBxTbxcuITMUJBOOQJYBQrwgAxHEZILnomFo2G9g6lVJuif_jWRz9bZS4XT4q2NNejVjAmpdUefG_hPjHIGegLQeybdfzovAKA9yUGbMG4E2Javx4zm8ocCD_pbZNEJGEzw-_uC_PM8kIGD1n6wV3LEpkzhR4I9PcnUFXMd9R3an-kI6-DyK43TYCIYvxUnIogkchusit8-dDGiaAoBBN_5LNXWc-nl2Yztsy109UapJo_sM8eTMmjJXvctqFdXaCCLl7Oso7_MwMBq2q_DMYh0wjG2n2rkVAyuz24JN9UH8-mjPIPlVqzgObB4658e_IAPKlfZApHT27HHqtRhW7HWyRivj5g5vKlUylyd5vAAKgmjvti6e1EMmJ2w3eGzuS_dn9l685R34ESX8UTtgg52aTd5omqysAwwrN7PKRm7-ier3EwM3N-gDFhR9jT3sOKk3WlSnaeu5YiuUAAWjnH5vXp-Xq8aW3Ej8soVRWlwkqsEiTW70DLDYgUEtG17FmZT0YQ7XhEgAZkKeDPhJhKpLoIbiC53YVYZQ7CEP1DOAu5aoWcTL_JnSx8ZB9qriRRmJid-dlgYu1ce0SkKPA0mHpXctKhbaFU6Jcmm1nIvN9prJh3dJKcJzVqJHGG-rxd7ul9pm4UsL2zhfgQ5KnJ1-FUMAutKzRAZOIbdkUsCwaDq0n4AHo7N5urcNr1mpXROfYb3eaO4hIM1KwmydxDU8OxJ0eKLS_MGk_EL5FPz2CI1hBr3aPGqn-i7wmIje1A0yh4J9XfL4oEWkA8eXuJUQihF5G5jrxSYV63YgYfRl8q_cj6cPLLPWfQYZaAmfOYh29kD9nyH_78FMvcJ7GqJqXTPanodcfgb04-Oh_fHORjwXMG3ZY8aQ-68X2G7HejmlTNU6pX_4cl0hfjqTMdQnN1OyTMB2TStOwJcvyAURblCY73EnMZ3rArB5IF3hSd9u_7OoctOZEuZskSKr9VZ-30lAnDzs5GjFee7hana98esJ1muIelLByYOafwOq-DlsDMECwY_gByINeK2uPZ0Gdd5PBuq4WXswC84O5v48m3ioGDRO0wfAemfKYFPWcCyBINHAXKIHdRBzOEIMh0XjNCV5b55ZvRlPtUjUgOREHbXSyMBa0CQuEQh9hN6QBVVAMX9qM2Iy8Jp_yU4Z4LhCJZ2UGGxJbjKDabFLz46PcWjYhARK0s0O4j5IoHG9x2tQXShpP7VqQML6dxwGmciTI_z8bZKbcH6526tjh4neVbroGe-lLeW-cRPTPkQyYd6kFEFHiLqDpAnX_QCXI8gA8_Env4HrrRBvfVF_ITRcr_Dgh5x7dEsKlpcZYmPOvY-JrEpurnMWhyNWnCKCeLd616HnBnf6xkRfKjsEs_Ais0mHDnBRAK8JWxWD_QtFVYJJ2yfCds3KT2J47fhbjRWk5GJdQ5vXGP2ScMd1PWlql-afYZTix_6si5uMe0ayyHu5KAvzX6zjHWRnuuoN07LLuTYRwQVzbPohCCb9WlcdtxRuarf7HVn5rfUUVJp5xDCFWiWfkviLNggLKiBp5ZBU1y0q8W4pgqjXZ5Vz9P-yZ7Mj1VdtOtKAoqk92LV7R3V7vw_ZLX-Lx2kgxYt3whiWuGHe_GHEuLdfJB0VTSOFwIdD9q6-Dk5nZ_kU8gg58jyuxc9ZsKzD8UGyf97t3skmOo-6ZOIYeBkMewmQksAOAZ6KhPB7DARkY9emjClcaqdZhthkh5FMjkMwmhvCZI_36s_iSWC2bg \ No newline at end of file diff --git a/backups/backup_v2docker_20250608_200224_encrypted.sql.gz.enc b/backups/backup_v2docker_20250608_200224_encrypted.sql.gz.enc new file mode 100644 index 0000000..35de0e3 --- /dev/null +++ b/backups/backup_v2docker_20250608_200224_encrypted.sql.gz.enc @@ -0,0 +1 @@ +gAAAAABoRdAwCQUOlw7zhwRLq8LG2ktZxVwQcoKA-TnAcaAOGUTSxZiBdvUbDWB_2CQHcipz-ajGra03ksX3k96ysDT2wKSImL3fIsxtKl5y9YtcBwmJV2F3ENpAlQonFliHmNQJHOuA44t-nRro9oCgBjzuwJiSJSDvQMiJDmR8Ap63sOj1gHeCWI4Kl2khCfqNfalJymmfAyPj2wP7VTt0JIiws7kUO1qiWZUVRReD4pZEEk2Pzs5AO6pfRWAi_gQ3MxofxVHhNPYGeF4Byklm7LBKbWWCJUc02EZBYV27nGduxX4PUP4DjCsI3uH1Qb0xySa6yBt8x-U4MmmOfFq9VRllaFJDndQDfkZJgiEqn4klMihnlGxmm9wS_qELnl8iA2mr2lRp0nW4g5u_QrPx5Dr-JCV5jCHzdd26cVoX9bzPYGTaezUhLdGRiC0Iwu8rOglpBcqFQ74nOR_b2DHq6eHwsTGBnJfl7IVEhEVMX5R3CvrwfppjvhXhibipCZnfP1-DsgjXDrwJs5CrkkGlxk0kcLve2rcm7gDjLnKaTu_WNMntnufDpwRwNQuSutefPqalUwiqw14oE20sMwdG8gOqFp7pPi-sZcLZKSBAXxE51-BfO9QS963eZ982KTJzFV_YAXIK-AK9g8YtNwNd9E86Ee40HDhKu1OfbAVQDZKGlGfVh_fgTSYrkNQ9P4B0g-Chrs4nvlSQFjhJyddlojNVYLl-RjXcNqYg9_MxIawVS5xwFvNW_HgjV_r7fWRd5YMObDgA1cgmoRkA9ewSaTMv2SyeOfktuvG3eK51N2Iui2FcxwGoKKTps8Vp9KFJYQzLc05ZaYPH-YwOx7sXMQXbKA1BWb4OYPYUlFwNr_64C-YruRSODF4ZKUhBlY4a2KAeksStGJCB1lmfLD1fHdMZml1ckfXzdyz7CuArfU6jXLFTzTdyJ1lfHFYduEce1RifRkNc1ZjljCfbxv4wCMF-LqjF592YncXrIT0SrYW_IlcWiWOvkxupTLMl8IsnbZL4R4C30xw6CEWll5byRJkLV61bFLmHD7m99EoFJGJV9K50yywzxzd8VkfWr_Yxxd-oVKtM2LlWbkjtrRn8xlmwWj_Fi_KbnoLJN-UQwG5JQoekQC_8bivRmAeHbUeaIfhIRWD9MM8bB6qPVJKBUI0d6H9DyD0XFPKr0bl-7jBfrNFUZOTeND84Xje3p-WSn1cPHqiqtyujHsSQhZ4DqABEb1mLGaCuGIB-3usY56-J4SA6dLuwfZBa5ST2IR1rAQetW_1ecictfJ31sC8Qj490WFEMWxgoDXjd8bZJ-Dhdgwp8r7qyY23frFplxfH_ashxV-99NCtyxP5ggrJHA8QLpfu2U9ydMHJdVgWUprICritbe6VxOw-UTGydJOWKBQx1mDzzUyGEL4vxFI3O1IOKdxRVDRzz5FmwutwGUytySKcDbMou7oiIi8GDPsQCuKGB2OCeepeMSkNP5K4yiIN_Nez10kyUKYWkcPwW8lQOOuqIkTT8kzAv-VCyEipeZwXoNnk37EWpE_wDyXNFiATPznXXS27wLnVQJ4-sjFBHRaDA1XCKbv67u1jyZpHrzPb3SJPPZs1pzKohVSVogdIDzoxEmxV5SgbHa8P8gtjcihg5K0qQDUVRvBf5ACogoHArpx5MU-PZu7BNqB0gTqdTuSAEet_ksFqrRjgmu5J5e4_tKUlrkN1wfR9mGWOFtTVSwOK8mNTZ9RuWDnKk6xy32ywtcY51GlLVG6SV-syoMed330xolP_NsBEqqII6MZFZrO4ZHpr86GS2JVJ0WP2IXoYbYP5mz7kaV5ei9iGpfh_PTSgAg3AaqdOLv3PJtu1ybhZ1zfsQPWkcexO1lFFirBCazm8O0aPGYKuJe0D3chLPIbLTesUa7PZ-LQuHXSHYIs-uP6-CRz32oHgioBcvZ8fyQcNn5Ixb4Wa-cyX6QqaYogeFZKx-RUZvZ8AELjt6LN9oWeA0fX5RIbO-FjsLs-tbXVT-gRwlY0IszsQopJBXHtdPD7KM6F0kTMo9ReKWikBzu36LlI-X1NI2Fx5eX-hSV_ZAid_Qiawqo-U57VCgXIgZUgTO5TkJO18ywfWw9TvOeI-qBbokIid6TED-wTT31pwMw_u7HDTN0Ih0P4UhWZq7ZTFnul32447F_RjOJ9h-A11lhBxI3yG0RxG9INnYB56zdCF2dMcz9CVQ2D1abB_0aFciE6WiOw3AueQ0QffHw3ffosJKT75grd3jn-ykFOfX5ceKXxJ-nioCggM53OhX495CgpaigwRf5zTHTCBncIAY7BpGdKwrHbXXSWJmpPKP_0yGsCTg14RFjKcM4r56qbqgkqpMHVMAYGlQPYyISjUb5pubB5sEBozpcM3Bg_rhup1MpphXADr-zJE5G9l0-5oMqqKZP-uALOQIwD2r4DHxr8RpEV01acWmp-zp2UrSd43H52btWEjkzpOA5_NgPaS3f-Z0CZNGLZtN73Q0L7SyrYBBF5d0XEDSh49y2QYga9vqynug9tPq_GqhlzmJ_hKGOEG3cNaqAeYQQMp9fyGKc238fkDt60rmkG_vnKAn5z9JLoEeRJoSVxJK44RxOoeAgWzKLdSgpJ6kxYqqMYbbt1oMuJnhW80gC2UUxKrPgzchv8iIq8cVGejFCJVcdwIua_9GR9p0alTFiJPyp3K_3Ss2OZkSxh2fUMnArzsZHLzBY1a3OhuEdE8e6uhhVBqCrt2DNCVvyPqv5yx6PJKTcIepdI6KsBiEywMxHdNtAXjum16KLnkdJqjHhQDgbLY0iuVIDVB1Eq1fUnvS8jCy2IpBNEDtesjzG9MienNZd6W_RE5pFx63w6b-eeq2ZKDAV7QU6SnDOEK0n_jCVk6a3eDkm2rqFSvxjRu7hh0Riwr5FMoRp-_MIPjyTCD4J1bQDQUD83caw8ZNvqLDsjF5qW6LSxgPJAibFu0FiKcp7iRr-EqBLiIOJ6Y1Hu5y07cjPMfvwM72Xf89xYdtBD6CS9MhE5j79S0t7aY6HDsnpVdHM9luAzcxh--rPi10lhHS3apyZEQrAYa9s3-CMtleda6BbirAinvWUaV8aBkUtO57XBBJaQ8RpxMmdly7BjAnLOgJZQCigFmNxcl0heeUPIE7l5qtdgmPOxPYa-37TEbpRA2MKlHON2WEMyfcT3Or5wIoBnfNuBAZLQr7aZiMARknD7JJ1G_EuKw9_DJJwpjEr-pRQ-LAi4V42CzUxYDnAWi3KXJRydfqeXIjk-MbKEcNcolg_jqJlo_o4htV02NfqwsKteUSv4qCvxRMxTdxi0EAsB0MMg-lZPo4cCtH0fnneO5y3s7ig7wp-LxoN6fsJofl4iSq4hvp3k3cpi9Uw1wRDEYdoPeptv3bENxryCTgaLF9B22b22-9ABFL-UVFqApr7VN8xSA8154KgU5h9Aby-wYnR4apVDk_Fm-vfZoW7bmHUtYtpvPzpELxgnUkEbhJloGj1epFsQnA4zOkhyXipGbrX67ND8H0e84tCwUezkRKWIBi_pJ7i1QRup6R2qiGgzE0-wiRShlN0CHiHxwRtNRbrbuVo3dbYCLcezshCJz01AW7YJEMJ8Vgx5A5f64zmh2uqqaxCTduPGY851439fZUbJ2AaSVZPvTzqDDUtmUFeqbbjSwQDrFyRuFHroFTAwkDg-B1foSOIfAL94svi0lXMN3oi2gXkWvyTG4wOuXHCXvX_lqbDLVq9h633XJVPVcTqcLxMKUOY77wuLE3vxxYVIcM_gDafb7RNpNsZltQ0aTrwakbDTZ2-N8OMb--qymvP9ybxogmkvglCj8uhIBCkFsMKX-mElEZnCiBFOWXX1azNVhoqIn2fQ3KF-YZDak3Y00ftn-o6FItvSMSjnNnIcY1eFjHRcLALfBCF_BmC159Dwqhij9VV89YcPKYTBDa5TsUoQVZtHcH4HKCjxjWD0ZpSGaU52ioezPWeggbBXbM7vrfgQ4rOlDIv98qpbu67Uq3JhqyWYIQZMrEp3qhGhDlmb5-9WzYfMPChu6jMcMr7qKxpLtFcHSEbr5L_kAnntVdT4M54T7eMs62goGFClp79xgPhdODTQndx5_D4Nx7f8kUMcdCniGdrEIMbBssqFubkCE9pvP575s52Z2NToZVm6V3zXwStN_2guzAr3H2wAnv7o-Ry1YTpdY7gOv343HIVHOO-_RihOo_TZT0eyEhgnpY6reuMYMT-yVO56dZPzTdEB2rWKyppaTjOGhyBrALfB5zv9gn4zw0nJ5LYiDZycTs52sxXDOosw1FZznRP2lc--X2QvkBDzqL5fnRGrKT8wbhQul_QX-ADWf6C0XKy1WhYzkLoypuEdZ87LMPeOAshvz7Wv0zvsO77UvrCc6QkptFO9kGvShzFKIU_hdcjTaX0BYCePkmcE4Hbfw96QiqbTutXTF3RJwHU9z9egIRecWK_pFsmfosYel2LXoSFfCKKYaoenQCHZBc3Tg4luPqQcNop6Xgqxm0_PN6xVw8wzxlATnxxU_EbvuegCa83I4Sjh4mOlenaD__Ab68wvzlPM8mZ5Fmofy35FZ0WJonEK6q6daUGMJKfyaRO3-9Wus17ZBcP5hyi8YO187s0OnJme6LKhfuFYIkTJuiDx_IUaSTO9NyLLmKrguUOAgLzvSURamXyfWX2HzNwR1dbmhxSWeNH6xcoadsAOitq3dk91GJOreTGJSphZyI0LNqqYBzSsK78I88LrCLhnmBXd0qRHqGUl0rD4EMfzl1uZ4PPa9IiXIHqrFsLCnNF2Y3mDBp2Utb_BKoFym69lREZWTK67moQO4k6FxgwirXtHTcgl51Ra-kz3vjzIVaLOtVsuiOCkbH7iDwSyjOHQtbfv4AjQf-v1F-z-WBVv-xrV-9bxp8xzqgQhxgVzkqCF9BEP7h_0WPBvC5Bqwu2xemxTYdkqluHhgFEy7yOX_3nzpLv_Ja9AmqZM7uG_MqPJXo11uoSS6nXLz6TRP5Lo6DA-oAWG-vpoPxvEVJYJv_iCoHtuQBa4kWRRukn5l0o5Cg4ITnJCEwSRih4jf1my7bjTNCytXrN6hvBy56Hizx8T52iZxZ7sNjOQxRX3LYUQ0G6EpawtmYaX9SG2JFijb78WCPk-2AuZRMtmBiQOXXvq8M5H929T4F29AEorWy5_CGrcx8LSbYK-Ea5-5diMdBluNlgB4eIe0nPGwmmA4BoNT1S21Pti-ZmG8zt88W_3JmGCcMSqVzTsX03IQ3jHXI5NbonIqNdacVzcoY7xDbsY1htpKd1kmf6YgmyNu8DSrh9IweYqny5-7NwLOddw2HDwcKlD8gjokLU-PEfFN3gWAzfI3A_sXvrysseCkO2RKYbe0zzMdbNp8uX7Gd054Hz41H4D98vLPPlLyS8vFlsh0PKjcormAijC9IEcgLy0lf_JZ095uUnzrX4_asai193HC-DjgVNhVrepAdh_XpT43RD3mUvS7Dg9AiKxnl-gH6BHYUAgMVY6wrBibwNUfH-96rgjaKh8Gwo1S_-q9vO_2-HllXcLAc4IJ6OWjXv1m_cy2JEcJ1-Vlwml03P4G8WKpGJINAG7iWFBKFvDnEYqxBQ4c7kXAvLfpGDkV27TjuY0NLuZTLRsrK60-PESJbM92_TjYROR1oMoIO8OvpnXLwgLMNo4e7ch0tWE6jeynLHHk_LL_LrvSarx3nIqb3RfDy527UeiCdrs2VQLMEVFy7G6nemDLDE6HbYwfOjr3gc9IYzfrImyj5jzea8D9cWh_C3QLtKcu202jo5xbn89YMJAee2940z7s7JZKiBtyvvyXTw8CozxpjWBK9Wi6eFdRN1mEN14mdsU9xJakEZ7BBA3Iub7P3x-PHNclwJP2nE1lv4rjjR3577pATN3DDKVkh1MwRmA_WGUFL5dCHcHCuTBNA9YTo0hfI1BMKvGTByG2ytw7vhrMOv8y2wxmMkYUj9IC2Dpn0Vro25FPj4ATOepZu2QAgSkgIni5GjhSiztc1OWex-2AJoKmFefgcaqZkCTv8D6ep0HkeSnKSJ4yr3LUbmrY4hw9e3y5lVCBmP-wkvBUnwaPVnfs78JFYeDDENFJfInxSMfvrljm1yyABv0GoNqGTVRMJ3W_QiK3PYDBK-E8mermfOyxA5nOk_xcJ6qMSCe18VgkgHnrn6z0sVGlUepTwueTORfo0aphVblZbKVhzCH-8ywPI84TiknGFcQcLYAhF8TODrINHtkj6ldpeDI9ry6TViExUWvtbT5IDt6PaK7Pbuqnxbxb1qQYD-T3qi_KjGTQHh-NcfhWB8Q4Bb1ORIHS02YUkWH_xRP-J3IRZtGNwUfSpLONrYpRcerW847dhGoAqUiJDWu35iLlwNkQrixaN1JWNKdCwL0pM2_ysBRVk81fD0LqZvCQrYemL6V1QnnDSsDXyTAnl6de9E1IxOP8SxJVmqwBpKf3PX6FlgAgKQ463orghNw4peKzyidoW5ZB7c4bgIwoz0ccJIWmtAv-9Mdo--I7zW_EXPFMrTlu76aaJdGFxwbAk-LEDxoCjwI6Y99IThv8mN1W1eVasjsRbRG8-D47cDJIpls0yAjHEiWs4TdlJ0ZIDIkPbHpS2mhvkH-jMGL4X3Gqx80Hz8E-7BOehqXuYUqMqlY0eYdRyMgDE4xXugeZnbaaI5bOGS038isXQ8rhN2lJ1reJH88THxVuWgMyoMjeBqMr4lR7hxkRcCexHvtrlakGlTStHg-yfInv3oNLj9NmvLgq8v8azkEKSZIfi1KeXESwnNLVT1leFDCtSjU0byi2am7XMht7RUBuyV36vG9V94aKozLnFF2vA0tG-d9o8zogW7F167V1QBhYqXumdmMyRhcnImQ_VD6ZkI2RU6wrZHsZixx3iInicPzYPVsyhRm8rdx548mD3Uzpgd3jDTqumYcynIYASM9fqs8LjG_uLcTqw-Kxy03itZkBaz35ZucVu-nfxDP0U8vSh2KZm9Bwj-q_qoz5J_9UuMMJOtDyZZYAbOh4o_d8t1ZvOnO50dFxIIj529zjmJDupGyFVXD3B4Iq_P7C-kBcNSAyyW-RcAWIdtAZTkbZO2ZUwWnugBXDffqAVLlcvIVuxJESOh-eTSYfeLT9i9LptszYBqSvFZurO-J4dDn_jbnwHVk9dvBWZjOFxnlngcBz99jb_Z1Y-cOFnlm6-GfL8BPOs8kuIVonlaJS8LepSUqEdu1LCV6OY-ElVPlJwgvlUsjgoCgFMfTYT1_e3KXcZuKbnZPcKvoygznd5WJTpHUD2UlE7U4k1-Kz_hYL2O7kr4QF5du5j5E0VxiJ_rPp7Q5yQ6h49TAmyXFR8sKTEszOUe5bSNkWukktuQa1DpH_T7Zu6mo7NHY-JJRZZsmntNKh9dt0DHqualT-NAopTU7HE_UPJC_ZoAqww_dtCrgvxcsg0VD4yTub_xrqrUanZddNJQD3-f6Gqc-hXcEEck-3mfs3LLDwMDh-23BAoVk7RaYwk7taxIyq49cQOikasb1mz8-habTimcjoNPj4samQepR4nRDNECNX61OqZXkntnUgDlxVrfov4UTEDdCBW-91ISOU8ZCbjAcFcPa9Or3kt5U0fjALQpbR12OLZw3hhkqCG6s_MaEsRo6n3vo25GS516_cuMNqEJZTaxa0IniX9JzjoZKaZhih3nFbqpTs5CIxn_mQUvKK6H40hvqvOgl1xEY0n1tP9Z6VyExU97PbSfaCxp2QnDOjmceBNkXEj-SswJxzfDI-tBwhWukXnNHg0NaL5YadFKdaA76co8b-3lrn2xtXRe0g65eGwJiHpUbrHcCxiXVBvs_XTUuqpGQwmqrQ= \ No newline at end of file diff --git a/v2/backup_before_timezone_change.sql b/v2/backup_before_timezone_change.sql new file mode 100644 index 0000000..a1ddb79 --- /dev/null +++ b/v2/backup_before_timezone_change.sql @@ -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 +-- + diff --git a/v2/docker-compose.yaml b/v2/docker-compose.yaml index a477f35..2825e77 100644 --- a/v2/docker-compose.yaml +++ b/v2/docker-compose.yaml @@ -12,6 +12,8 @@ services: POSTGRES_INITDB_ARGS: '--encoding=UTF8 --locale=de_DE.UTF-8' POSTGRES_COLLATE: 'de_DE.UTF-8' POSTGRES_CTYPE: 'de_DE.UTF-8' + TZ: Europe/Berlin + PGTZ: Europe/Berlin volumes: # Persistente Speicherung der Datenbank auf dem Windows-Host - postgres_data:/var/lib/postgresql/data @@ -33,6 +35,8 @@ services: ports: - "8443:8443" env_file: .env + environment: + TZ: Europe/Berlin depends_on: - postgres networks: @@ -50,6 +54,8 @@ services: restart: always # Port-Mapping entfernt - nur über nginx erreichbar env_file: .env + environment: + TZ: Europe/Berlin depends_on: - postgres networks: @@ -71,6 +77,8 @@ services: ports: - "80:80" - "443:443" + environment: + TZ: Europe/Berlin depends_on: - admin-panel - license-server diff --git a/v2_adminpanel/Dockerfile b/v2_adminpanel/Dockerfile index 16870a7..cee53bf 100644 --- a/v2_adminpanel/Dockerfile +++ b/v2_adminpanel/Dockerfile @@ -5,15 +5,21 @@ ENV LANG=de_DE.UTF-8 ENV LC_ALL=de_DE.UTF-8 ENV PYTHONIOENCODING=utf-8 +# Zeitzone auf Europe/Berlin setzen +ENV TZ=Europe/Berlin + WORKDIR /app # System-Dependencies inkl. PostgreSQL-Tools installieren RUN apt-get update && apt-get install -y \ locales \ postgresql-client \ + tzdata \ && sed -i '/de_DE.UTF-8/s/^# //g' /etc/locale.gen \ && locale-gen \ && update-locale LANG=de_DE.UTF-8 \ + && ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime \ + && echo "Europe/Berlin" > /etc/timezone \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* diff --git a/v2_adminpanel/app.py b/v2_adminpanel/app.py index c5f74e9..e39be6d 100644 --- a/v2_adminpanel/app.py +++ b/v2_adminpanel/app.py @@ -7,6 +7,7 @@ from functools import wraps from dotenv import load_dotenv import pandas as pd from datetime import datetime, timedelta +from zoneinfo import ZoneInfo import io import subprocess import gzip @@ -72,7 +73,7 @@ def login_required(f): # Prüfe ob Session abgelaufen ist if 'last_activity' in session: 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 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): """Erstellt ein verschlüsseltes Backup der Datenbank""" 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" filepath = BACKUP_DIR / filename @@ -398,7 +399,7 @@ def check_ip_blocked(ip_address): conn.close() 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 False, None @@ -425,7 +426,7 @@ def record_failed_attempt(ip_address, username): blocked_until = None 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) if os.getenv("EMAIL_ENABLED", "false").lower() == "true": 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} Versuchter Benutzername: {username} 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. @@ -565,7 +566,7 @@ def generate_license_key(license_type='full'): chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789' # Datum-Teil - now = datetime.now() + now = datetime.now(ZoneInfo("Europe/Berlin")) date_part = now.strftime('%Y%m') type_char = 'F' if license_type == 'full' else 'T' @@ -606,7 +607,7 @@ def login(): # Prüfen ob IP gesperrt ist is_blocked, blocked_until = check_ip_blocked(ip_address) 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." return render_template("login.html", error=error_msg, error_type="blocked") @@ -668,7 +669,7 @@ def login(): session.permanent = True # Aktiviert das Timeout session['logged_in'] = True 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) log_audit('LOGIN_SUCCESS', 'user', additional_info=f"Erfolgreiche Anmeldung von IP: {ip_address}") @@ -710,7 +711,7 @@ def logout(): def heartbeat(): """Endpoint für Session Keep-Alive - aktualisiert last_activity""" # 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 session.modified = True @@ -975,7 +976,7 @@ def dashboard(): 'ip_address': event[0], 'attempt_count': event[1], '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], 'error_message': event[5] }) @@ -1249,7 +1250,7 @@ def batch_licenses(): 'licenses': generated_licenses, 'valid_from': valid_from, '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') @@ -1308,7 +1309,7 @@ def export_batch(): io.BytesIO(output.getvalue().encode('utf-8-sig')), mimetype='text/csv', 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") @@ -1778,7 +1779,7 @@ def export_licenses(): # Audit-Log log_audit('EXPORT', 'license', 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': # CSV Export @@ -1859,7 +1860,7 @@ def export_customers(): # Audit-Log log_audit('EXPORT', 'customer', 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': # CSV Export @@ -2108,7 +2109,7 @@ def blocked_ips(): 'first_attempt': ip[2].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'), - 'is_active': ip[4] > datetime.now(), + 'is_active': ip[4] > datetime.now(ZoneInfo("Europe/Berlin")).replace(tzinfo=None), 'last_username': ip[5], 'last_error': ip[6] }) diff --git a/v2_adminpanel/init.sql b/v2_adminpanel/init.sql index 73a863c..de93763 100644 --- a/v2_adminpanel/init.sql +++ b/v2_adminpanel/init.sql @@ -1,11 +1,14 @@ -- UTF-8 Encoding für deutsche Sonderzeichen sicherstellen SET client_encoding = 'UTF8'; +-- Zeitzone auf Europe/Berlin setzen +SET timezone = 'Europe/Berlin'; + CREATE TABLE IF NOT EXISTS customers ( id SERIAL PRIMARY KEY, name TEXT NOT NULL, email TEXT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, CONSTRAINT unique_email UNIQUE (email) ); @@ -17,7 +20,7 @@ CREATE TABLE IF NOT EXISTS licenses ( valid_from DATE NOT NULL, valid_until DATE NOT NULL, 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 ( @@ -26,16 +29,16 @@ CREATE TABLE IF NOT EXISTS sessions ( session_id TEXT UNIQUE NOT NULL, ip_address TEXT, user_agent TEXT, - started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - last_heartbeat TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - ended_at TIMESTAMP, + started_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + last_heartbeat TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + ended_at TIMESTAMP WITH TIME ZONE, is_active BOOLEAN DEFAULT TRUE ); -- Audit-Log-Tabelle für Änderungsprotokolle CREATE TABLE IF NOT EXISTS audit_log ( id SERIAL PRIMARY KEY, - timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + timestamp TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, username TEXT NOT NULL, action 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' status TEXT NOT NULL, -- 'success', 'failed', 'in_progress' error_message TEXT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, created_by TEXT NOT NULL, tables_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 ( ip_address VARCHAR(45) PRIMARY KEY, attempt_count INTEGER DEFAULT 0, - first_attempt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - last_attempt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - blocked_until TIMESTAMP NULL, + first_attempt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + last_attempt TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + blocked_until TIMESTAMP WITH TIME ZONE NULL, last_username_tried TEXT, last_error_message TEXT ); @@ -93,7 +96,7 @@ DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM information_schema.columns 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 UPDATE licenses SET created_at = valid_from WHERE created_at IS NULL; diff --git a/v2_lizenzserver/Dockerfile b/v2_lizenzserver/Dockerfile index 94ed784..69b8d95 100644 --- a/v2_lizenzserver/Dockerfile +++ b/v2_lizenzserver/Dockerfile @@ -1,5 +1,15 @@ 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 # Placeholder für Lizenzserver diff --git a/v2_nginx/Dockerfile b/v2_nginx/Dockerfile index 7149194..30ee1fe 100644 --- a/v2_nginx/Dockerfile +++ b/v2_nginx/Dockerfile @@ -1,5 +1,11 @@ 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 RUN mkdir -p /etc/nginx/ssl diff --git a/v2_postgres/Dockerfile b/v2_postgres/Dockerfile index 25a7c97..55e8e21 100644 --- a/v2_postgres/Dockerfile +++ b/v2_postgres/Dockerfile @@ -1,14 +1,20 @@ FROM postgres:14 -# Deutsche Locale installieren -RUN apt-get update && apt-get install -y locales \ +# Deutsche Locale und Zeitzone installieren +RUN apt-get update && apt-get install -y locales tzdata \ && sed -i '/de_DE.UTF-8/s/^# //g' /etc/locale.gen \ && locale-gen \ && update-locale LANG=de_DE.UTF-8 \ + && ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime \ + && echo "Europe/Berlin" > /etc/timezone \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* # Locale-Umgebungsvariablen setzen ENV LANG de_DE.UTF-8 ENV LANGUAGE de_DE:de -ENV LC_ALL de_DE.UTF-8 \ No newline at end of file +ENV LC_ALL de_DE.UTF-8 + +# Zeitzone setzen +ENV TZ=Europe/Berlin +ENV PGTZ=Europe/Berlin \ No newline at end of file diff --git a/v2_testing/.claude/settings.local.json b/v2_testing/.claude/settings.local.json new file mode 100644 index 0000000..3f171dd --- /dev/null +++ b/v2_testing/.claude/settings.local.json @@ -0,0 +1,10 @@ +{ + "permissions": { + "allow": [ + "Bash(python3:*)", + "Bash(pip install:*)", + "Bash(pip3 install:*)" + ], + "deny": [] + } +} \ No newline at end of file diff --git a/v2_testing/test_audit_raw.py b/v2_testing/test_audit_raw.py new file mode 100644 index 0000000..d1df8f0 --- /dev/null +++ b/v2_testing/test_audit_raw.py @@ -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() \ No newline at end of file diff --git a/v2_testing/test_audit_simple.py b/v2_testing/test_audit_simple.py new file mode 100644 index 0000000..1e282f1 --- /dev/null +++ b/v2_testing/test_audit_simple.py @@ -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'
| ]*>([^<]+) | ', 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() \ No newline at end of file diff --git a/v2_testing/test_audit_timezone.py b/v2_testing/test_audit_timezone.py new file mode 100644 index 0000000..68316cf --- /dev/null +++ b/v2_testing/test_audit_timezone.py @@ -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() \ No newline at end of file diff --git a/v2_testing/test_timezone.py b/v2_testing/test_timezone.py new file mode 100644 index 0000000..ad45fa8 --- /dev/null +++ b/v2_testing/test_timezone.py @@ -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() \ No newline at end of file diff --git a/v2_testing/test_timezone_simple.py b/v2_testing/test_timezone_simple.py new file mode 100644 index 0000000..5275b1a --- /dev/null +++ b/v2_testing/test_timezone_simple.py @@ -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() \ No newline at end of file