From 709f362194251abbcf13184086bfddd9c088e8a8 Mon Sep 17 00:00:00 2001 From: VlastikYoutubeKo <56507931+VlastikYoutubeKo@users.noreply.github.com> Date: Fri, 21 Nov 2025 22:39:41 +0100 Subject: [PATCH] Update to v2.1.1 - Clean Design --- .gitignore | 65 ++ CHANGELOG.md | 97 ++ CHART_INTERACTION.md | 233 +++++ DESIGN_COMPARISON.md | 258 +++++ DOWNLOAD_SUMMARY.md | 313 ++++++ FILE_INDEX.md | 367 +++++++ FIXES_v2.1.md | 287 ++++++ FIX_INSTANCE_FOLDER.md | 113 ++ IMMEDIATE_FIX.md | 129 +++ QUICKSTART.md | 194 ++++ README_CZ.md | 172 ++++ UPGRADE_GUIDE.md | 304 ++++++ WEBHOOK_GUIDE.md | 391 +++++++ fix_db_path.py | 94 ++ install.sh | 102 ++ main_clean.py | 2221 ++++++++++++++++++++++++++++++++++++++++ main_enhanced.py | 2149 ++++++++++++++++++++++++++++++++++++++ migrate.py | 315 ++++++ readme.md | 508 +++++---- requirements.txt | 6 + 20 files changed, 8127 insertions(+), 191 deletions(-) create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 CHART_INTERACTION.md create mode 100644 DESIGN_COMPARISON.md create mode 100644 DOWNLOAD_SUMMARY.md create mode 100644 FILE_INDEX.md create mode 100644 FIXES_v2.1.md create mode 100644 FIX_INSTANCE_FOLDER.md create mode 100644 IMMEDIATE_FIX.md create mode 100644 QUICKSTART.md create mode 100644 README_CZ.md create mode 100644 UPGRADE_GUIDE.md create mode 100644 WEBHOOK_GUIDE.md create mode 100644 fix_db_path.py create mode 100644 install.sh create mode 100644 main_clean.py create mode 100644 main_enhanced.py create mode 100644 migrate.py create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..61aa6c3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,65 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# Virtual Environment +venv/ +ENV/ +env/ +.venv + +# Flask +instance/ +.webassets-cache + +# Environment Variables +.env +.env.local +.env.*.local + +# Database +*.db +*.sqlite +*.sqlite3 + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Logs +*.log +nohup.out + +# Testing +.pytest_cache/ +.coverage +htmlcov/ + +# Backups +*.backup +*.bak diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1de5767 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,97 @@ +# Changelog + +All notable changes to UrNetwork Stats Dashboard will be documented in this file. + +## [2.1.1] - 2024-11-21 + +### ✨ Added +- **Chart Interaction Mode** - Tooltips now show anywhere on chart, not just on data points +- Custom tooltip callbacks with formatted values (3 decimal places + GB unit) +- Better touch/mobile experience for charts +- Login button in header when not logged in + +### 🎨 Improved +- **Chart Text Visibility** - All text now white (#ffffff) instead of gray +- Chart axis labels now #e5e7eb for better readability +- Legend text is bold (weight 500) and larger (13px) +- Tooltip styling with dark background and blue border (#3b82f6) +- Login button styling (blue, prominent) + +### 🔧 Fixed +- **Daily Cleanup** - Changed from weekly (Sunday) to daily (3 AM) +- Keeps only last 7 days of data automatically +- Chart text no longer black/invisible on dark background + +### 📊 Technical +- Added `interaction.mode = 'index'` to all charts +- Added `interaction.intersect = false` for easier tooltip display +- Improved tooltip configuration with better colors and padding +- Login button CSS with `.login-btn` class + +--- + +## [2.1.0] - 2024-11-20 + +### 🎯 Major Changes +- **Clean Design** - Removed gradients, switched to solid Cloudflare-inspired colors +- Changed from purple (#667eea) to blue (#3b82f6) theme +- Solid background (#0c0d0e) instead of gradient +- Removed all glassmorphism/blur effects + +### 📊 Chart Improvements +- Better visibility with new color scheme +- Increased chart line width from 1px to 2px +- Better contrast for data visualization +- Optimized for dark theme readability + +### 🎨 Design Updates +- New stat cards without gradient backgrounds +- Simplified header design +- Cleaner button styles +- Better border colors (#2d3135) +- Improved text colors + +--- + +## [2.0.0] - 2024-11-15 + +### 🎯 Multi-Account Support (MAJOR UPDATE) +- Track unlimited UrNetwork accounts simultaneously +- Account management UI +- Individual charts per account +- Combined statistics +- Account nicknames +- Color-coded accounts + +### 🔐 Authentication Overhaul +- Separate admin password +- Secure session management +- Account-level authentication + +### 📊 Dashboard Enhancements +- React-powered private dashboard +- View modes (combined, paid/unpaid, delta) +- Chart visibility toggles +- Improved layout + +### 💾 Database Changes +- Multi-account schema +- Foreign key relationships +- Migration script from v1.0 +- Data preservation + +--- + +## [1.0.0] - 2024-10-01 + +### Initial Release +- Single account tracking +- Basic charts +- Public/private dashboard +- SQLite database +- Webhook support +- Web installer + +--- + +**Full documentation:** [README.md](README.md) diff --git a/CHART_INTERACTION.md b/CHART_INTERACTION.md new file mode 100644 index 0000000..d075c7d --- /dev/null +++ b/CHART_INTERACTION.md @@ -0,0 +1,233 @@ +# 🎯 Graf Tooltip Vylepšení - v2.1.1 + +## ✨ Co se změnilo + +### PŘED +``` +Tooltip se zobrazí JEN když najedeš přímo na datový bod (malý kolečko) +❌ Musíš přesně trefit bod +❌ Obtížné na mobilech +❌ Frustrující když je hodně bodů blízko sebe +``` + +### PO +``` +Tooltip se zobrazí KDEKOLIV na grafu +✅ Stačí najet myší kamkoliv na graf +✅ Automaticky najde nejbližší data +✅ Mnohem lepší UX! +``` + +--- + +## 🔧 Technické Řešení + +Přidal jsem **Chart.js interaction mode**: + +```javascript +interaction: { + mode: 'index', // Najde všechny datasety na indexu + intersect: false // Nemusíš trefit přesně bod +} +``` + +### Vysvětlení: +- **`mode: 'index'`** - Zobrazí tooltip pro všechny datasety na daném X indexu +- **`intersect: false`** - Tooltip se zobrazí i když nejsi přímo nad čárou + +--- + +## 📊 Jak to funguje + +### Single Dataset Graf (např. Total Data) +``` +Graf: ──────●──────●──────●────── + │ │ │ │ +Myš: │ ← zde → │ │ + │ │ │ │ + └─────┴──────┴──────┘ + Tooltip se ukáže u nejbližšího bodu +``` + +### Multi Dataset Graf (např. Paid vs Unpaid) +``` +Paid: ──────●──────●──────●────── +Unpaid: ──────●──────●──────●────── + │ │ │ │ +Myš: │ ← zde → │ │ + │ │ │ │ + +Tooltip ukáže: + Nov 9, 05:52 PM + ━━━━━━━━━━━━━━ + 💰 Paid: 1.234 GB + 📡 Unpaid: 0.567 GB +``` + +--- + +## 🎨 Bonus - Formátované Hodnoty + +Přidal jsem také custom callback pro lepší formátování: + +```javascript +callbacks: { + label: function(context) { + let label = context.dataset.label || ''; + if (label) { + label += ': '; + } + if (context.parsed.y !== null) { + label += context.parsed.y.toFixed(3) + ' GB'; + } + return label; + } +} +``` + +**Výsledek:** +``` +// PŘED +Paid Data: 1.23456789 + +// PO +Paid Data: 1.235 GB ← Vždy 3 des. místa + jednotka +``` + +--- + +## 📱 Výhody + +### Desktop +✅ Rychlejší interakce - nemusíš lovit malé body +✅ Přesnější - ukazuje nejbližší hodnotu +✅ Plynulejší - tooltip plynule sleduje myš + +### Mobile/Touch +✅ Funguje s touch - stačí klepnout kamkoliv +✅ Větší "hit area" - není potřeba trefit malý bod +✅ Lepší UX na menších obrazovkách + +--- + +## 🔄 Kde to funguje + +Toto vylepšení je aplikováno na **VŠECHNY grafy**: + +### Veřejný Dashboard: +- ✅ Total Data Provided (GB) +- ✅ Individual Account Charts + +### Privátní Dashboard: +- ✅ Paid vs Unpaid Data +- ✅ Delta Chart +- ✅ Combined Chart +- ✅ Individual Account Charts + +--- + +## 🧪 Vyzkoušej + +1. **Otevři dashboard** +2. **Najdi graf** +3. **Jeď myší kdekoliv na grafu** (nemusíš trefit bod) +4. **Tooltip se okamžitě ukáže** s nejbližšími daty! + +### Test Multi-Dataset: +Na grafu s více čarami (Paid vs Unpaid): +- Jeď myší horizontálně přes graf +- Tooltip ukáže **obě** hodnoty najednou +- Vidíš data pro všechny datasety v daném čase + +--- + +## 💡 Pro Tipy + +### Rychlé Porovnání +Na multi-dataset grafu můžeš rychle porovnat hodnoty: +``` +Jeď myší zleva doprava → +Tooltip plynule ukazuje jak se mění obě hodnoty +``` + +### Přesné Hodnoty +I když jsou body blízko sebe: +``` +[●●●●●] ← Těsně u sebe +Tooltip vždy ukáže správný nejbližší bod +``` + +### Mobile +Na mobilech: +``` +Klepni kamkoliv na graf +→ Tooltip se ukáže +Jeď prstem po grafu +→ Tooltip sleduje tvůj prst +``` + +--- + +## 🎯 Interaction Modes + +Pro tvou informaci, Chart.js nabízí různé režimy: + +| Mode | Popis | Použití | +|------|-------|---------| +| `'point'` | Jen přesný bod | Původní chování ❌ | +| `'nearest'` | Nejbližší bod | Stále musíš být blízko | +| **`'index'`** | **Celý index** | **← Používáme! ✅** | +| `'dataset'` | Celý dataset | Pro srovnání | +| `'x'` / `'y'` | Podle osy | Specifické případy | + +**Proč `'index'`?** +- Nejlepší pro časové grafy +- Ukáže všechny datasety najednou +- Nejintuitivnější pro uživatele + +--- + +## 📊 Příklad Output + +### Single Chart: +``` +Nov 21, 01:37 PM +━━━━━━━━━━━━━━━━ +Total Data: 169.134 GB +``` + +### Multi Chart: +``` +Nov 21, 01:37 PM +━━━━━━━━━━━━━━━━ +💰 Paid (GB): 123.456 GB +📡 Unpaid (GB): 45.678 GB +``` + +--- + +## 🚀 Instalace + +Už je zahrnuté v **main_clean.py v2.1.1**! + +```bash +cd ~/urio +cp /path/to/main_clean.py main.py +pkill -f main.py +python3 main.py +``` + +--- + +## 📝 Changelog + +### v2.1.1 - 2024-11-21 +- ✅ Added `interaction.mode = 'index'` to all charts +- ✅ Added `interaction.intersect = false` for easier tooltip display +- ✅ Added custom tooltip callback for formatted values (3 decimal places) +- ✅ Improved mobile/touch experience +- ✅ Applied to both public and private dashboard charts + +--- + +**Výsledek: Mnohem lepší UX! Tooltip se ukáže kdekoliv najedeš myší na graf! 🎉** diff --git a/DESIGN_COMPARISON.md b/DESIGN_COMPARISON.md new file mode 100644 index 0000000..0c25aea --- /dev/null +++ b/DESIGN_COMPARISON.md @@ -0,0 +1,258 @@ +# 🎨 Design Update - Clean Cloudflare Style + +## 🔥 Co bylo opraveno + +### Problémy ve v2.0: +1. ❌ **Gradient pozadí** - tmavý fialový gradient byl příliš tmavý +2. ❌ **Fialové barvy** - grafy byly špatně viditelné +3. ❌ **Glassmorphism efekty** - příliš mnoho blur efektů +4. ❌ **Přemrštěné animace** - karty se příliš pohybovaly + +### ✅ Nový Clean Design: +1. ✅ **Čisté tmavé pozadí** - bez gradientu (#0c0d0e) +2. ✅ **Modré téma** - lépe viditelné grafy (#3b82f6) +3. ✅ **Minimální efekty** - čistý, profesionální vzhled +4. ✅ **Jemné animace** - subtilní hover efekty + +--- + +## 📥 Soubory + +### Hlavní Verze + +**[main_clean.py](computer:///mnt/user-data/outputs/main_clean.py)** - ✨ **NOVÁ ČISTÁ VERZE** +- Bez gradientu v pozadí +- Modrá barva místo fialové +- Lépe viditelné grafy +- Inspirováno Cloudflare dashboardem +- **Doporučeno k použití!** + +**[main_enhanced.py](computer:///mnt/user-data/outputs/main_enhanced.py)** - Původní v2.0 +- S gradientem a glassmorphism +- Fialové téma +- Více efektů + +--- + +## 🎨 Vizuální Srovnání + +### Barvy + +**Původní (v2.0 - Fialová):** +```css +--primary: linear-gradient(135deg, #667eea 0%, #764ba2 100%); +background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%); +``` + +**Nová (Clean - Modrá):** +```css +--primary: #3b82f6; /* Čistá modrá */ +background: #0c0d0e; /* Bez gradientu */ +``` + +### Grafy + +**Původní:** +- Fialová čára: `#667eea` +- Slabá viditelnost na tmavém gradientu +- Málo kontrastu + +**Nová:** +- Modrá čára: `#3b82f6` +- Silnější bordura (2px místo 1px) +- Vyšší kontrast +- Lépe viditelné body na grafu + +### Karty + +**Původní:** +```css +background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, ...); +backdrop-filter: blur(10px); +transform: translateY(-5px); /* Příliš mnoho pohybu */ +``` + +**Nová:** +```css +background: #16181a; /* Čistá barva */ +/* Bez blur */ +/* Jemné hover efekty */ +``` + +--- + +## 🚀 Instalace Clean Verze + +### Pro NOVOU instalaci: +```bash +cd ~/urnetwork-stats + +# Stáhni main_clean.py +# Přejmenuj na main.py +mv main_clean.py main.py + +python3 main.py +``` + +### Pro UPGRADE z v2.0: +```bash +cd ~/urio + +# Zazálohuj současnou verzi +cp main.py main_gradient_backup.py + +# Nahraď čistou verzí +cp /path/to/main_clean.py main.py + +# Restartuj +pkill -f main.py +python3 main.py +``` + +--- + +## 🎯 Klíčové Změny v CSS + +### 1. Pozadí +```css +/* PŘED */ +body { + background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%); + background-attachment: fixed; +} + +/* PO */ +body { + background: #0c0d0e; /* Čistá tmavá */ +} +``` + +### 2. Primární Barva +```css +/* PŘED */ +--primary: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + +/* PO */ +--primary: #3b82f6; /* Modrá Cloudflare style */ +``` + +### 3. Stat Karty +```css +/* PŘED */ +.stat-card { + background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%); + backdrop-filter: blur(10px); +} +.stat-card:hover { + transform: translateY(-5px); +} + +/* PO */ +.stat-card { + background: #16181a; /* Čistý solid */ +} +.stat-card:hover { + border-color: #3b82f6; /* Jemný highlight */ +} +``` + +### 4. Grafy +```css +/* PŘED */ +borderColor: '#667eea', /* Fialová */ +backgroundColor: 'rgba(102, 126, 234, 0.2)', + +/* PO */ +borderColor: '#3b82f6', /* Modrá */ +backgroundColor: 'rgba(59, 130, 246, 0.15)', /* Světlejší pro kontrast */ +borderWidth: 2, /* Silnější čára */ +``` + +--- + +## 💡 Design Principy + +### Clean Design Philosophy: +1. **Méně je více** - Bez zbytečných efektů +2. **Čitelnost** - Vysoký kontrast, jasné barvy +3. **Konzistence** - Jednotný barevný systém +4. **Cloudflare inspirace** - Profesionální, čistý vzhled + +### Cloudflare Color System: +```css +Background: #0c0d0e (Hlavní pozadí) +Secondary BG: #16181a (Karty) +Card BG: #1a1d1f (Content boxy) +Border: #2d3135 (Okraje) +Primary: #3b82f6 (Modrá) +Text: #e5e7eb (Světlý text) +Text Muted: #9ca3af (Tlumený text) +``` + +--- + +## 🔄 Rollback na Gradient Verzi + +Pokud preferuješ původní gradient design: + +```bash +cd ~/urio + +# Použij original verzi +cp main_gradient_backup.py main.py + +# Nebo použij main_enhanced.py +cp /path/to/main_enhanced.py main.py + +python3 main.py +``` + +--- + +## 📊 Porovnání Výkonu + +### Rendering Speed: +- **Clean**: Rychlejší (bez blur efektů) +- **Gradient**: Pomalejší (backdrop-filter je náročný) + +### Čitelnost Grafů: +- **Clean**: ⭐⭐⭐⭐⭐ (Vynikající) +- **Gradient**: ⭐⭐⭐ (Průměrná) + +### Professional Look: +- **Clean**: ⭐⭐⭐⭐⭐ (Cloudflare style) +- **Gradient**: ⭐⭐⭐⭐ (Moderní, ale ne pro všechny) + +--- + +## 🎨 Customizace + +Chceš jiné barvy? Změň v main_clean.py: + +```css +:root { + --primary: #3b82f6; ← Změň na jinou barvu + /* Příklady: */ + /* Zelená: #10b981 */ + /* Fialová: #8b5cf6 */ + /* Červená: #ef4444 */ + /* Oranžová: #f59e0b */ +} +``` + +Vše ostatní se automaticky přizpůsobí! + +--- + +## ✅ Doporučení + +Pro většinu uživatelů doporučuji **main_clean.py**: +- ✅ Lépe viditelné grafy +- ✅ Čistší design +- ✅ Rychlejší rendering +- ✅ Profesionální vzhled +- ✅ Inspirováno Cloudflare + +--- + +**Vytvořeno s důrazem na čitelnost a profesionální vzhled! 🎉** diff --git a/DOWNLOAD_SUMMARY.md b/DOWNLOAD_SUMMARY.md new file mode 100644 index 0000000..6dd6585 --- /dev/null +++ b/DOWNLOAD_SUMMARY.md @@ -0,0 +1,313 @@ +# 🎉 UrNetwork Stats Dashboard v2.1.1 - Complete Package Ready! + +## ✅ Všechny Soubory Připraveny ke Stažení + +Kompletní balík souborů pro UrNetwork Stats Dashboard v2.1.1 je připraven! + +--- + +## 📦 Seznam Souborů (18 souborů) + +### 🐍 Hlavní Aplikace (2 soubory) + +1. **[main_clean.py](computer:///mnt/user-data/outputs/main_clean.py)** - 87 KB ⭐ **POUŽIJ TENTO!** + - Přejmenuj na `main.py` + - Clean Cloudflare design + - Bílý text na grafech + - Tooltip kdekoliv + - Denní cleanup + +2. **[main_enhanced.py](computer:///mnt/user-data/outputs/main_enhanced.py)** - 85 KB + - Alternativa s gradienty + - Fialové téma + - Volitelný + +--- + +### 📖 Dokumentace (12 souborů) + +3. **[README.md](computer:///mnt/user-data/outputs/README.md)** - 10 KB + - Hlavní anglický README + - Kompletní dokumentace + +4. **[README_CZ.md](computer:///mnt/user-data/outputs/README_CZ.md)** - 5.3 KB + - Český README + - Úplná česká dokumentace + +5. **[FILE_INDEX.md](computer:///mnt/user-data/outputs/FILE_INDEX.md)** - 8.1 KB ⭐ + - **ZAČNI ODTUD!** + - Přehled všech souborů + - Instalační návod + - Checklist + +6. **[QUICKSTART.md](computer:///mnt/user-data/outputs/QUICKSTART.md)** - 5.2 KB + - Rychlý start za 5 minut + +7. **[WEBHOOK_GUIDE.md](computer:///mnt/user-data/outputs/WEBHOOK_GUIDE.md)** - 7.9 KB + - 10+ webhook příkladů + - Discord, Slack, Telegram + +8. **[DESIGN_COMPARISON.md](computer:///mnt/user-data/outputs/DESIGN_COMPARISON.md)** - 5.3 KB + - Srovnání designů + - Proč tento design? + +9. **[CHART_INTERACTION.md](computer:///mnt/user-data/outputs/CHART_INTERACTION.md)** - 5.2 KB + - Tooltip vylepšení + - Jak to funguje + +10. **[FIXES_v2.1.md](computer:///mnt/user-data/outputs/FIXES_v2.1.md)** - 6.7 KB + - Všechny opravy v2.1 + +11. **[UPGRADE_GUIDE.md](computer:///mnt/user-data/outputs/UPGRADE_GUIDE.md)** - 6.7 KB + - Migrace z v1.0/v2.0 + +12. **[CHANGELOG.md](computer:///mnt/user-data/outputs/CHANGELOG.md)** - 2.7 KB + - Historie verzí + +13. **[FIX_INSTANCE_FOLDER.md](computer:///mnt/user-data/outputs/FIX_INSTANCE_FOLDER.md)** - 2.8 KB + - Fix pro starší instalace + +14. **[IMMEDIATE_FIX.md](computer:///mnt/user-data/outputs/IMMEDIATE_FIX.md)** - 3.1 KB + - Rychlé opravy + +--- + +### 🛠️ Instalační Soubory (4 soubory) + +15. **[requirements.txt](computer:///mnt/user-data/outputs/requirements.txt)** - 120 B + - Python závislosti + - Pro `pip install -r` + +16. **[install.sh](computer:///mnt/user-data/outputs/install.sh)** - 2.8 KB + - Automatický instalátor + - Bash script + +17. **[.gitignore](computer:///mnt/user-data/outputs/.gitignore)** - * + - Git ignore pravidla + +18. **[migrate.py](computer:///mnt/user-data/outputs/migrate.py)** - 11 KB + - Migrace z v1.0 + +--- + +## 🚀 Co Dělat Nyní + +### Krok 1: Stáhnout Všechny Soubory ⬇️ + +**Minimální sada (pro rychlý start):** +``` +✅ main_clean.py → přejmenuj na main.py +✅ requirements.txt +✅ FILE_INDEX.md → přečti FIRST! +✅ README.md nebo README_CZ.md +``` + +**Doporučená sada (kompletní):** +``` +✅ Všech 18 souborů +``` + +### Krok 2: Instalace 🔧 + +```bash +# 1. Vytvoř složku +mkdir urnetwork-stats +cd urnetwork-stats + +# 2. Stáhni soubory do této složky + +# 3. Přejmenuj hlavní soubor +mv main_clean.py main.py + +# 4. Spusť instalaci +chmod +x install.sh +./install.sh + +# NEBO manuálně: +pip3 install -r requirements.txt +mkdir -p instance + +# 5. Spusť +python3 main.py + +# 6. Otevři prohlížeč +# http://localhost:90 +``` + +### Krok 3: První Spuštění ⚙️ + +1. **Nastav admin heslo** (NENÍ UrNetwork heslo!) +2. **Přidej účty** - Menu → Správa Účtů +3. **Nastav webhooky** (volitelné) - Menu → Nastavení +4. **Hotovo!** 🎉 + +--- + +## 📋 Checklist Před Nahráním na Server/Repo + +- [ ] Všech 18 souborů staženo +- [ ] `main_clean.py` přejmenován na `main.py` +- [ ] Vytvořena složka `docs/` (volitelné) +- [ ] README.md v root složce +- [ ] requirements.txt v root složce +- [ ] install.sh má +x práva (`chmod +x install.sh`) +- [ ] .gitignore přítomen + +--- + +## 📂 Doporučená Struktura Po Stažení + +``` +urnetwork-stats/ # Root složka +├── main.py # ← Z main_clean.py +├── requirements.txt +├── install.sh +├── migrate.py +├── .gitignore +├── README.md +├── README_CZ.md +└── docs/ # Volitelná podsložka + ├── FILE_INDEX.md # ← Začni odtud! + ├── QUICKSTART.md + ├── WEBHOOK_GUIDE.md + ├── DESIGN_COMPARISON.md + ├── CHART_INTERACTION.md + ├── FIXES_v2.1.md + ├── UPGRADE_GUIDE.md + ├── CHANGELOG.md + ├── FIX_INSTANCE_FOLDER.md + └── IMMEDIATE_FIX.md +``` + +**Nebo jednodušeji (bez docs/ složky):** +``` +urnetwork-stats/ +├── main.py +├── requirements.txt +├── install.sh +├── .gitignore +├── README.md +└── (všechny ostatní .md soubory) +``` + +--- + +## 🎯 Priority Po Stažení + +### Musíš Přečíst (Povinné): +1. **[FILE_INDEX.md](computer:///mnt/user-data/outputs/FILE_INDEX.md)** ⭐ - Začni odtud! +2. **[README.md](computer:///mnt/user-data/outputs/README.md)** nebo **[README_CZ.md](computer:///mnt/user-data/outputs/README_CZ.md)** - Hlavní dokumentace + +### Měl bys Přečíst (Doporučené): +3. **[QUICKSTART.md](computer:///mnt/user-data/outputs/QUICKSTART.md)** - Rychlý start +4. **[WEBHOOK_GUIDE.md](computer:///mnt/user-data/outputs/WEBHOOK_GUIDE.md)** - Webhook setup + +### Můžeš Přečíst Později (Volitelné): +5. Ostatní `.md` soubory podle potřeby + +--- + +## 💾 Velikost Balíku + +**Celkem:** ~260 KB (všech 18 souborů) +- Aplikace (main.py): ~87 KB +- Dokumentace: ~70 KB +- Skripty: ~15 KB +- Ostatní: ~88 KB + +**Minimální sada:** ~97 KB +- main.py + requirements.txt + README.md + +--- + +## 🔄 Upgrade z Předchozí Verze? + +### Z v1.0 → v2.1: +1. Záloha `.env` a databáze +2. Stáhni nové soubory +3. Přejmenuj `main_clean.py` → `main.py` +4. Spusť `python3 migrate.py` +5. Restart + +### Z v2.0 → v2.1: +1. Záloha `main.py` +2. Nahraď `main_clean.py` → `main.py` +3. Restart + +📖 **Detaily:** [UPGRADE_GUIDE.md](computer:///mnt/user-data/outputs/UPGRADE_GUIDE.md) + +--- + +## 🐛 První Pomoc + +**Problém:** Port 90 obsazený +```python +# main.py, poslední řádek: +app.run(host="0.0.0.0", port=8080, debug=False) +``` + +**Problém:** Chybí závislosti +```bash +pip3 install -r requirements.txt +``` + +**Problém:** Nemůžu se přihlásit +```bash +echo "ADMIN_PASSWORD=tvoje_heslo" >> .env +``` + +**Problém:** Grafy bez textu +- Ujisti se, že používáš `main_clean.py` (ne `main_enhanced.py`) + +--- + +## 📞 Podpora + +- **Dokumentace:** Všechny `.md` soubory +- **Issues:** GitHub/Forgejo Issues +- **Quick Help:** [FILE_INDEX.md](computer:///mnt/user-data/outputs/FILE_INDEX.md) má FAQ + +--- + +## ✨ Co Je Nového v v2.1.1 + +- ✅ **Tooltip kdekoliv** na grafu (ne jen na bodech) +- ✅ **Bílý text** na grafech (konečně čitelný!) +- ✅ **Denní cleanup** (každý den ve 3:00) +- ✅ **Login button** v headeru +- ✅ Formátované hodnoty (3 des. místa) +- ✅ Better mobile/touch UX + +📖 **Detaily:** [FIXES_v2.1.md](computer:///mnt/user-data/outputs/FIXES_v2.1.md) + +--- + +## 🎉 Hotovo! + +**Máš všech 18 souborů připravených!** + +**Next Steps:** +1. ⬇️ Stáhni všechny soubory +2. 📖 Přečti [FILE_INDEX.md](computer:///mnt/user-data/outputs/FILE_INDEX.md) +3. 🚀 Spusť instalaci +4. 🎊 Užij si! + +--- + +**Made with ❤️ and 🤖 AI** + +**Version:** v2.1.1 +**Date:** November 21, 2024 +**Author:** Vlastík (mxnticek) + Claude (Anthropic) + +--- + +## 🔗 Quick Links + +- [FILE_INDEX.md](computer:///mnt/user-data/outputs/FILE_INDEX.md) - **Začni odtud!** +- [README.md](computer:///mnt/user-data/outputs/README.md) - Hlavní dokumentace +- [main_clean.py](computer:///mnt/user-data/outputs/main_clean.py) - Hlavní aplikace +- [requirements.txt](computer:///mnt/user-data/outputs/requirements.txt) - Závislosti +- [WEBHOOK_GUIDE.md](computer:///mnt/user-data/outputs/WEBHOOK_GUIDE.md) - Webhook návod + +**Stáhni vše a jsi ready to go! 🚀** diff --git a/FILE_INDEX.md b/FILE_INDEX.md new file mode 100644 index 0000000..14887d6 --- /dev/null +++ b/FILE_INDEX.md @@ -0,0 +1,367 @@ +# 📦 UrNetwork Stats Dashboard v2.1.1 - Complete Package + +## 📥 Download All Files + +Toto je kompletní balík souborů pro UrNetwork Stats Dashboard v2.1.1. + +--- + +## 📂 Hlavní Soubory + +### 🐍 Aplikace + +**[main_clean.py](computer:///mnt/user-data/outputs/main_clean.py)** - ⭐ **HLAVNÍ SOUBOR** +- Kompletní Flask aplikace +- Multi-account podpora +- Čistý Cloudflare design +- Všechny opravy v2.1.1 +- **Přejmenuj na `main.py` při instalaci!** +- Velikost: ~88 KB + +**[main_enhanced.py](computer:///mnt/user-data/outputs/main_enhanced.py)** - Alternativa s gradienty +- Verze s fialovým gradientem a glassmorphismem +- Pro ty, kdo preferují původní v2.0 design +- Velikost: ~86 KB + +--- + +### 📖 Dokumentace + +**[README.md](computer:///mnt/user-data/outputs/README.md)** - Hlavní anglický README +- Kompletní dokumentace projektu +- Quick start guide +- Webhook příklady +- Troubleshooting + +**[README_CZ.md](computer:///mnt/user-data/outputs/README_CZ.md)** - Český README +- Úplná česká dokumentace +- Rychlý start +- Webhook návod + +**[QUICKSTART.md](computer:///mnt/user-data/outputs/QUICKSTART.md)** - Rychlý start +- Instalace za 5 minut +- První spuštění +- Základní konfigurace + +**[WEBHOOK_GUIDE.md](computer:///mnt/user-data/outputs/WEBHOOK_GUIDE.md)** - Webhook návod +- 10+ webhook příkladů +- Discord, Slack, Telegram +- Dostupné proměnné +- Debugging tipy + +**[DESIGN_COMPARISON.md](computer:///mnt/user-data/outputs/DESIGN_COMPARISON.md)** - Design dokumentace +- Srovnání v2.0 vs v2.1 +- Důvody změn +- Customizace barev + +**[CHART_INTERACTION.md](computer:///mnt/user-data/outputs/CHART_INTERACTION.md)** - Graf interakce +- Tooltip kdekoliv na grafu +- Jak to funguje +- Mobile optimalizace + +**[FIXES_v2.1.md](computer:///mnt/user-data/outputs/FIXES_v2.1.md)** - Seznam oprav v2.1 +- Všechny opravy +- Před/po srovnání +- Technické detaily + +**[UPGRADE_GUIDE.md](computer:///mnt/user-data/outputs/UPGRADE_GUIDE.md)** - Upgrade průvodce +- Migrace z v1.0 +- Upgrade z v2.0 +- Backup strategie + +**[CHANGELOG.md](computer:///mnt/user-data/outputs/CHANGELOG.md)** - Historie změn +- Všechny verze +- Co je nového +- Breaking changes + +--- + +### 🛠️ Instalační Soubory + +**[requirements.txt](computer:///mnt/user-data/outputs/requirements.txt)** - Python závislosti +```txt +Flask==3.0.0 +Flask-SQLAlchemy==3.1.1 +Flask-APScheduler==1.13.1 +requests==2.31.0 +python-dateutil==2.8.2 +gunicorn==21.2.0 +``` + +**[install.sh](computer:///mnt/user-data/outputs/install.sh)** - Automatický instalační script +- Kontrola závislostí +- Instalace balíčků +- Vytvoření složek +- Backup existujících dat + +**[.gitignore](computer:///mnt/user-data/outputs/.gitignore)** - Git ignore file +- Python cache +- Virtual environments +- Database files +- Environment variables + +--- + +### 🔧 Utility Skripty + +**[migrate.py](computer:///mnt/user-data/outputs/migrate.py)** - Migrace z v1.0 +- Automatický upgrade na v2.0 +- Zachování dat +- Backup tvorba + +**[fix_db_path.py](computer:///mnt/user-data/outputs/fix_db_path.py)** - Fix databázové cesty +- Oprava instance folder problémů +- Pro starší instalace + +--- + +## 🚀 Rychlá Instalace + +### Metoda 1: Automatická (Doporučeno) + +```bash +# 1. Stáhni všechny soubory do složky +cd urnetwork-stats + +# 2. Přejmenuj main_clean.py na main.py +mv main_clean.py main.py + +# 3. Spusť instalační script +chmod +x install.sh +./install.sh + +# 4. Spusť aplikaci +python3 main.py +``` + +### Metoda 2: Manuální + +```bash +# 1. Stáhni soubory +cd urnetwork-stats + +# 2. Přejmenuj +mv main_clean.py main.py + +# 3. Instaluj závislosti +pip3 install -r requirements.txt + +# 4. Vytvoř složky +mkdir -p instance + +# 5. Spusť +python3 main.py +``` + +### Metoda 3: S Gunicorn (Produkce) + +```bash +# Po instalaci závislostí +gunicorn --bind 0.0.0.0:90 --workers 4 main:app +``` + +--- + +## 📋 Checklist Po Stažení + +- [ ] Stáhnout všechny soubory +- [ ] Přejmenovat `main_clean.py` → `main.py` +- [ ] Spustit `install.sh` nebo manuální instalaci +- [ ] Otevřít `http://localhost:90` +- [ ] Nastavit admin heslo +- [ ] Přidat UrNetwork účty +- [ ] (Volitelné) Nastavit webhooky +- [ ] (Volitelné) Přidat do systemd + +--- + +## 📊 Struktura Po Instalaci + +``` +urnetwork-stats/ +├── main.py # Hlavní aplikace (z main_clean.py) +├── main_enhanced.py # Alternativa (volitelné) +├── requirements.txt # Závislosti +├── install.sh # Instalační script +├── migrate.py # Migrace script +├── .gitignore # Git ignore +├── .env # Config (vytvoří se automaticky) +├── instance/ +│ └── transfer_stats.db # Databáze (vytvoří se automaticky) +└── docs/ + ├── README.md + ├── README_CZ.md + ├── QUICKSTART.md + ├── WEBHOOK_GUIDE.md + ├── DESIGN_COMPARISON.md + ├── CHART_INTERACTION.md + ├── FIXES_v2.1.md + ├── UPGRADE_GUIDE.md + └── CHANGELOG.md +``` + +--- + +## 🎯 Co Potřebuješ Minimálně + +Pro základní funkčnost: + +1. **Povinné:** + - `main_clean.py` (přejmenuj na `main.py`) + - `requirements.txt` + +2. **Doporučené:** + - `README.md` nebo `QUICKSTART.md` + - `install.sh` (usnadní instalaci) + +3. **Volitelné:** + - Všechny ostatní `.md` soubory (dokumentace) + - `main_enhanced.py` (alternativní design) + - `migrate.py` (jen pokud upgraduješ z v1.0) + +--- + +## 🔄 Upgrade z Předchozí Verze + +### Z v1.0 (Single Account) + +```bash +# 1. Záloha +cp .env .env.backup +cp instance/transfer_stats.db instance/transfer_stats.db.backup + +# 2. Stáhnout nové soubory +# (všechny soubory z tohoto balíku) + +# 3. Přejmenovat +mv main_clean.py main.py + +# 4. Spustit migraci +python3 migrate.py + +# 5. Restart +python3 main.py +``` + +### Z v2.0 (Gradient Design) + +```bash +# Jednoduše nahraď main.py +cp main.py main.py.v2.0.backup +mv main_clean.py main.py +python3 main.py +``` + +--- + +## 📖 První Kroky Po Instalaci + +1. **Otevři prohlížeč:** `http://localhost:90` +2. **Nastav admin heslo:** (NENÍ to tvé UrNetwork heslo!) +3. **Přidej účty:** + - Menu → Správa Účtů + - Přidat Účet + - Zadej UrNetwork email, heslo, přezdívku +4. **Nastav webhooky (volitelné):** + - Menu → Nastavení + - Webhook Management + - Přidej Discord/Slack/Telegram URL + payload +5. **Hotovo!** Dashboard začne sbírat data každých 15 minut + +--- + +## 🐛 Časté Problémy + +### Port 90 je obsazený +```python +# Změň v main.py poslední řádek: +app.run(host="0.0.0.0", port=8080, debug=False) +``` + +### Chybí závislosti +```bash +pip3 install Flask Flask-SQLAlchemy Flask-APScheduler requests python-dateutil +``` + +### Nemůžu se přihlásit +```bash +# Zkontroluj .env +cat .env | grep ADMIN_PASSWORD +# Pokud chybí: +echo "ADMIN_PASSWORD=tvoje_heslo" >> .env +``` + +### Grafy neukazují text +- Ujisti se, že používáš `main_clean.py` (ne `main_enhanced.py`) +- Verze musí být v2.1+ + +--- + +## 💡 Pro Tipy + +### Rychlé Testování +```bash +# Spusť bez instalace systemd +python3 main.py + +# V produkci použij Gunicorn +gunicorn --bind 0.0.0.0:90 main:app +``` + +### Vývoj +```bash +# Debug mode +python3 main.py # Debug je defaultně vypnutý + +# Pro development změň v main.py: +app.run(host="0.0.0.0", port=90, debug=True) +``` + +### Backup +```bash +# Před každým upgradem +cp .env .env.backup +cp instance/transfer_stats.db instance/transfer_stats.db.backup +``` + +--- + +## 📞 Podpora + +- **Issues:** Forgejo Issues +- **Dokumentace:** Všechny `.md` soubory v balíku +- **Discord:** (pokud máš komunitní server) + +--- + +## ✅ Závěrečný Checklist + +Před nahráním na server/repo zkontroluj: + +- [ ] Všechny soubory staženy +- [ ] `main_clean.py` přejmenován na `main.py` +- [ ] `requirements.txt` přítomen +- [ ] `README.md` přítomen +- [ ] `install.sh` má +x práva +- [ ] `.gitignore` přítomen +- [ ] Dokumentace v `docs/` (volitelné) + +--- + +## 🎉 Hotovo! + +Máš kompletní balík souborů pro UrNetwork Stats Dashboard v2.1.1! + +**Co dělat dál:** +1. Stáhni všechny soubory +2. Nahraj na server/do repozitáře +3. Spusť instalaci +4. Užij si! + +**Made with ❤️ and 🤖 AI** + +--- + +**Verze:** v2.1.1 +**Datum:** 21. listopadu 2024 +**Autor:** Vlastík (mxnticek) + Claude (Anthropic) diff --git a/FIXES_v2.1.md b/FIXES_v2.1.md new file mode 100644 index 0000000..ce32e6a --- /dev/null +++ b/FIXES_v2.1.md @@ -0,0 +1,287 @@ +# ✅ Opravy v main_clean.py - Finální Verze + +## 🎯 Co bylo opraveno + +### 1. ✅ Grafy - Viditelný Text +**Problém:** Text na grafech byl černý a skoro neviditelný na tmavém pozadí + +**Oprava:** +```javascript +// PŘED - Špatně viditelný +ticks: { color: 'var(--text-muted)' } // Tmavě šedá +legend: { labels: { color: 'var(--text-color)' } } // Není dostatečně světlá + +// PO - Jasně viditelný ✅ +ticks: { color: '#e5e7eb', font: { size: 12 } } // Světle bílá +legend: { + labels: { + color: '#ffffff', // Čistě bílá + font: { size: 13, weight: '500' }, + padding: 15 + } +} +``` + +**Vylepšení:** +- ✅ Osy (X, Y) - bílý text místo šedého +- ✅ Legenda - bílý tučný text +- ✅ Tooltips - tmavé pozadí s modrým rámečkem +- ✅ Větší velikost písma pro lepší čitelnost + +--- + +### 2. ✅ Login Tlačítko v Headeru +**Problém:** Nebylo jasné, kde se přihlásit, když nejsi přihlášený + +**Oprava:** +```html + +{% if session.logged_in %} + +{% else %} + Přihlásit se +{% endif %} +``` + +**CSS:** +```css +.header-nav a.login-btn { + background: #3b82f6; /* Modré pozadí */ + color: white; + font-weight: 600; /* Tučné */ +} +``` + +**Výsledek:** +- ✅ Viditelné modré tlačítko "Přihlásit se" v headeru +- ✅ Zobrazuje se pouze když nejsi přihlášený +- ✅ Zmizí po přihlášení + +--- + +### 3. ✅ Automatické Mazání Starých Dat +**Problém:** Data se mazala jen jednou týdně v neděli + +**Oprava:** +```python +# PŘED - Jednou týdně +@scheduler.task(trigger="cron", day_of_week="sun", hour="0") + +# PO - Každý den ✅ +@scheduler.task(trigger="cron", hour="3", minute="0") +``` + +**Jak to funguje:** +1. **Každý den ve 3:00 ráno** se spustí cleanup job +2. Smaže všechny záznamy **starší než 7 dní** +3. Ponechá data z **posledního týdne** +4. Loguje kolik záznamů bylo smazáno + +**Příklad:** +``` +Dnes je: 21. listopadu 2024 +Smaže se: Vše před 14. listopadem 2024 +Zůstane: 14. - 21. listopadu (poslední 7 dní) +``` + +--- + +## 📊 Před & Po - Vizuální Srovnání + +### Grafy - Text +``` +PŘED: +┌────────────────────────┐ +│ Graf │ +│ (text skoro neviditelný)│ +│ Osa Y: #9ca3af (tmavá) │ +│ Osa X: #9ca3af (tmavá) │ +│ Legenda: #d1d5db │ +└────────────────────────┘ + +PO: +┌────────────────────────┐ +│ Graf │ +│ (text jasně viditelný!) │ +│ Osa Y: #e5e7eb (světlá)│ +│ Osa X: #e5e7eb (světlá)│ +│ Legenda: #ffffff (bílá)│ +└────────────────────────┘ +``` + +### Header +``` +PŘED: +[Veřejný pohled] (když nejsi přihlášený, není jasné kde se přihlásit) + +PO: +[Veřejný pohled] [🔵 Přihlásit se] (jasné modré tlačítko) +``` + +### Cleanup Schedule +``` +PŘED: +Neděle 00:00 → Smaže data starší 7 dní + +PO: +Každý den 03:00 → Smaže data starší 7 dní +``` + +--- + +## 🚀 Instalace Opravené Verze + +```bash +cd ~/urio + +# Zazálohuj současnou verzi +cp main.py main.py.before_fixes + +# Nahraď opravenou verzí +cp /path/to/main_clean.py main.py + +# Restartuj +pkill -f main.py +python3 main.py +``` + +--- + +## ✨ Co se vylepšilo + +### Čitelnost Grafů ⭐⭐⭐⭐⭐ +- **PŘED**: ⭐⭐ (Text skoro neviditelný) +- **PO**: ⭐⭐⭐⭐⭐ (Jasně čitelné vše!) + +### UX - Přihlášení ⭐⭐⭐⭐⭐ +- **PŘED**: ⭐⭐⭐ (Musíš znát URL /login) +- **PO**: ⭐⭐⭐⭐⭐ (Jasné modré tlačítko v headeru) + +### Údržba Databáze ⭐⭐⭐⭐⭐ +- **PŘED**: ⭐⭐⭐⭐ (Týdně - může se hromadit) +- **PO**: ⭐⭐⭐⭐⭐ (Denně - vždy jen poslední týden) + +--- + +## 🎨 Technické Detaily + +### Chart.js Konfigurace +```javascript +// Kompletní chart options pro viditelný text +{ + scales: { + y: { + ticks: { + color: '#e5e7eb', // Světlá barva + font: { size: 12 } // Větší písmo + }, + grid: { + color: 'rgba(45, 49, 53, 0.3)' // Jemná mřížka + } + }, + x: { + ticks: { + color: '#e5e7eb', + font: { size: 11 } + } + } + }, + plugins: { + legend: { + labels: { + color: '#ffffff', // Bílá + font: { size: 13, weight: '500' }, + padding: 15 + } + }, + tooltip: { + backgroundColor: 'rgba(26, 29, 31, 0.95)', // Tmavé pozadí + titleColor: '#ffffff', + bodyColor: '#e5e7eb', + borderColor: '#3b82f6', + borderWidth: 1, + padding: 12 + } + } +} +``` + +### Cleanup Job +```python +@scheduler.task( + id="cleanup_old_stats_job", + trigger="cron", + hour="3", # Každý den ve 3:00 + minute="0" +) +def cleanup_old_stats_job(): + cutoff_date = datetime.datetime.now() - datetime.timedelta(days=7) + # Smaže vše starší než cutoff_date + db.session.query(Stats).filter(Stats.timestamp < cutoff_date).delete() +``` + +--- + +## 💾 Velikost Databáze + +S denním čištěním: +``` +Stats každých 15 min = 96 záznamů/den +7 dní × 96 = 672 záznamů max +3 účty × 672 = ~2000 záznamů max + +Velikost: ~200 KB databáze (velmi malá!) +``` + +Bez čištění (týdně): +``` +Po měsíci: ~8000 záznamů +Velikost: ~800 KB (pořád OK, ale zbytečné) +``` + +--- + +## 🔍 Ověření Změn + +### 1. Grafy +``` +Otevři dashboard → Podívej se na graf +✅ Čísla na osách jsou bílá a čitelná +✅ Legenda je bílá a tučná +✅ Tooltips mají modré ohraničení +``` + +### 2. Login Tlačítko +``` +Odhlásit se (pokud jsi přihlášený) +✅ V headeru se objeví modré tlačítko "Přihlásit se" +Přihlásit se +✅ Tlačítko zmizí, objeví se menu +``` + +### 3. Cleanup Job +```bash +# Zkontroluj logy +tail -f nohup.out | grep cleanup + +# Mělo by se objevit každý den ve 3:00 +# "Running daily stats cleanup job..." +# "Successfully deleted X stats records older than 7 days." +``` + +--- + +## 📝 Changelog + +### v2.1 - 2024-11-21 +- ✅ Fixed chart text visibility (white text instead of dark gray) +- ✅ Added Login button to header when not logged in +- ✅ Changed cleanup schedule from weekly to daily (3 AM) +- ✅ Enhanced tooltip styling with blue borders +- ✅ Increased font sizes for better readability + +--- + +**Všechno opraveno a připraveno k použití! 🎉** + +Grafy jsou nyní perfektně čitelné, login je viditelný a databáze se čistí každý den automaticky! diff --git a/FIX_INSTANCE_FOLDER.md b/FIX_INSTANCE_FOLDER.md new file mode 100644 index 0000000..8466894 --- /dev/null +++ b/FIX_INSTANCE_FOLDER.md @@ -0,0 +1,113 @@ +# 🔧 Fix: Databáze v instance/ složce + +## Problém +Pokud vidíš chybu: +``` +✗ Databáze transfer_stats.db nenalezena! +``` + +Ale databáze je ve složce `instance/`: +```bash +ls instance/ +# transfer_stats.db +``` + +## Řešení + +Opravený migrační skript již automaticky hledá databázi v těchto lokacích: +- `transfer_stats.db` (root složka) +- `instance/transfer_stats.db` (Flask standardní umístění) +- `../transfer_stats.db` (parent folder) + +### Použij aktualizované soubory + +Právě jsem opravil oba soubory: +1. **migrate.py** - Nyní hledá DB v instance/ +2. **main_enhanced.py** - Používá správnou cestu `sqlite:///instance/transfer_stats.db` + +### Rychlé řešení + +```bash +# 1. Stáhni znovu opravené soubory z /mnt/user-data/outputs/ +# 2. Překopíruj je do ~/urio/ +cd ~/urio + +# 3. Zkus migraci znovu +python3 migrate.py +``` + +## Alternativní řešení (pokud potřebuješ hned) + +Pokud chceš použít stávající soubory bez stahování nových: + +```bash +cd ~/urio + +# Přesuň databázi do root složky +cp instance/transfer_stats.db . + +# Spusť migraci +python3 migrate.py + +# Po úspěšné migraci, databáze zůstane v root složce +# nebo ji vrať do instance/ +mv transfer_stats.db instance/ +``` + +## Ověření + +Po úspěšné migraci by měl výstup vypadat takto: + +``` +============================================================ + Vytváření záloh +============================================================ +✓ Zazálohován .env → .env.backup +✓ Zazálohována databáze → instance/transfer_stats.db.backup + +============================================================ + Migrace databáze +============================================================ +✓ Nalezena databáze: instance/transfer_stats.db +✓ Vytvořena tabulka accounts +✓ Přidán sloupec account_id do tabulky stats + +Nalezen existující účet: vlastik.novotny2005@gmail.com +Zadejte přezdívku pro tento účet: Hlavní účet + +✓ Migrován účet: vlastik.novotny2005@gmail.com + ✓ Přezdívka: Hlavní účet + ✓ Aktualizováno X statistických záznamů + +✓ Migrace databáze dokončena! +``` + +## Nová verze používá instance/ automaticky + +Opravená verze `main_enhanced.py`: +```python +# Config nyní používá: +SQLALCHEMY_DATABASE_URI = "sqlite:///instance/transfer_stats.db" + +# A automaticky vytvoří instance/ složku, pokud neexistuje +``` + +Takže po migraci vše funguje správně s databází v `instance/` složce. + +## Stáhni opravené soubory + +Všechny soubory v `/mnt/user-data/outputs/` jsou již opravené a ready to use! + +```bash +# Zkopíruj opravené soubory +cd ~/urio +cp /cesta/k/stazenym/migrate.py . +cp /cesta/k/stazenym/main_enhanced.py . + +# A spusť migraci znovu +python3 migrate.py +``` + +--- + +Tohle by mělo problém vyřešit! Zkus to a dej vědět, jestli to funguje. 🚀 diff --git a/IMMEDIATE_FIX.md b/IMMEDIATE_FIX.md new file mode 100644 index 0000000..6c4e1da --- /dev/null +++ b/IMMEDIATE_FIX.md @@ -0,0 +1,129 @@ +# 🚑 OKAMŽITÁ OPRAVA - Database Path Error + +## Problém +``` +sqlite3.OperationalError: unable to open database file +``` + +## ⚡ Rychlé Řešení (30 sekund) + +### Varianta 1: Automatická oprava +```bash +cd ~/urio +python3 fix_db_path.py +python3 main.py +``` + +### Varianta 2: Manuální oprava +```bash +cd ~/urio + +# Přidej do main.py hned za class Config: +# Najdi tuto řádku (řádek ~33): +# SQLALCHEMY_DATABASE_URI = os.getenv("DATABASE_URL", "sqlite:///instance/transfer_stats.db") +# +# A nahraď ji tímto (3 řádky): +# BASE_DIR = os.path.abspath(os.path.dirname(__file__)) +# INSTANCE_DIR = os.path.join(BASE_DIR, 'instance') +# SQLALCHEMY_DATABASE_URI = os.getenv("DATABASE_URL", f"sqlite:///{os.path.join(INSTANCE_DIR, 'transfer_stats.db')}") +``` + +### Varianta 3: Použij opravený soubor +```bash +cd ~/urio + +# Stáhni znovu main_enhanced.py z outputs/ +# A přepiš jím současný main.py +mv main.py main.py.broken +cp /path/to/main_enhanced.py main.py + +python3 main.py +``` + +## 🔍 Co je problém? + +SQLAlchemy používá relativní cestu `sqlite:///instance/transfer_stats.db`, ale instance folder neexistuje v době, kdy se SQLAlchemy inicializuje. + +## ✅ Co oprava dělá? + +1. **Použije absolutní cestu** místo relativní +2. **Vytvoří instance/ folder** PŘED inicializací SQLAlchemy +3. **Nastaví správnou cestu** k databázi + +## 📝 Změny v kódu + +### PŘED (nefungující): +```python +class Config: + SQLALCHEMY_DATABASE_URI = "sqlite:///instance/transfer_stats.db" + +app = Flask(__name__) +app.config.from_object(Config) +# instance folder se vytváří tady ← TOO LATE! +``` + +### PO (fungující): +```python +class Config: + BASE_DIR = os.path.abspath(os.path.dirname(__file__)) + INSTANCE_DIR = os.path.join(BASE_DIR, 'instance') + SQLALCHEMY_DATABASE_URI = f"sqlite:///{os.path.join(INSTANCE_DIR, 'transfer_stats.db')}" + +# instance folder se vytváří tady ← BEFORE app init! +if not os.path.exists(Config.INSTANCE_DIR): + os.makedirs(Config.INSTANCE_DIR) + +app = Flask(__name__) +app.config.from_object(Config) +``` + +## 🎯 Ověření + +Po opravě by měl výstup vypadat takto: +```bash +$ python3 main.py +2025-11-21 12:20:51,319 - INFO - Successfully loaded world map GeoJSON data. +2025-11-21 12:20:51,450 - INFO - Scheduler started + * Serving Flask app 'main' + * Running on http://0.0.0.0:90 +``` + +✅ Bez chyby "unable to open database file"! + +## 🆘 Stále nefunguje? + +Zkontroluj: + +```bash +# 1. Existuje instance složka? +ls -la instance/ + +# 2. Jsou v ní správné soubory? +ls -la instance/transfer_stats.db* + +# 3. Má Python práva na zápis? +touch instance/test.txt && rm instance/test.txt + +# 4. Je databáze validní? +sqlite3 instance/transfer_stats.db ".tables" +``` + +Pokud všechno OK, měl by výstup být: +``` +accounts settings stats webhook +``` + +--- + +## 📦 Aktualizované soubory + +Všechny soubory v `/mnt/user-data/outputs/` jsou již opravené: +- ✅ **main_enhanced.py** - Opravená verze +- ✅ **fix_db_path.py** - Automatický fix skript +- ✅ **migrate.py** - Funguje s instance/ + +Stačí je použít a vše bude fungovat! + +--- + +**Vyřešilo to problém? Dej mi vědět!** 🚀 diff --git a/QUICKSTART.md b/QUICKSTART.md new file mode 100644 index 0000000..a6da15d --- /dev/null +++ b/QUICKSTART.md @@ -0,0 +1,194 @@ +# 🚀 UrNetwork Stats Dashboard v2.0 - QUICK START + +## 📦 Balíček obsahuje: + +``` +urnetwork-stats-v2/ +├── main_enhanced.py # Hlavní aplikace (přejmenovat na main.py) +├── migrate.py # Migrační skript pro upgrade +├── install.sh # Instalační skript pro nové instalace +├── README_CZ.md # Kompletní dokumentace +├── UPGRADE_GUIDE.md # Průvodce upgradem +└── QUICKSTART.md # Tento soubor +``` + +## ⚡ Pro NOVOU instalaci (prázdná složka) + +```bash +# 1. Nahrajte všechny soubory do složky +cd ~/urnetwork-stats + +# 2. Přejmenujte hlavní soubor +mv main_enhanced.py main.py + +# 3. Spusťte instalační skript +bash install.sh + +# 4. Spusťte aplikaci +python3 main.py + +# 5. Otevřete v prohlížeči +http://your-server:90 +``` + +## 🔄 Pro UPGRADE existující instalace + +```bash +# 1. Zazálohujte současnou složku +cd ~ +cp -r urio urio_backup + +# 2. Nahrajte POUZE tyto soubory do existující složky: +# - main_enhanced.py → přejmenovat na main.py +# - migrate.py + +# 3. Zastavte běžící aplikaci +pkill -f main.py + +# 4. Spusťte migraci +cd ~/urio +python3 migrate.py + +# 5. Spusťte novou verzi +python3 main.py + +# 6. Přihlaste se novým admin heslem +http://your-server:90 +``` + +## 🎯 První kroky po instalaci + +### 1. Nastavení Admin Hesla +- Při první návštěvě nastavíte admin heslo +- **DŮLEŽITÉ:** Toto není vaše UrNetwork heslo! +- Toto heslo používáte pro přihlášení do dashboardu + +### 2. Přidání UrNetwork Účtů +``` +Dashboard → Správa účtů → Přidat účet + +Vyplňte: + ✓ UrNetwork email (vlastik.novotny2005@gmail.com) + ✓ UrNetwork heslo + ✓ Přezdívka (např. "Domácí účet") +``` + +### 3. Zobrazení Dat +- **Veřejný pohled**: Kombinované statistiky všech účtů +- **Privátní dashboard**: Detailní grafy a správa +- **Správa účtů**: Přidávání/odebírání účtů + +## 🆘 Rychlá pomoc + +### Nemohu se přihlásit +```bash +# Zkontrolujte .env soubor +cat .env | grep ADMIN_PASSWORD + +# Pokud chybí, přidejte +echo "ADMIN_PASSWORD=your_password" >> .env +``` + +### Aplikace neběží +```bash +# Zkontrolujte logy +tail -f nohup.out + +# Nebo spusťte v popředí pro debugging +python3 main.py +``` + +### Port 90 je obsazený +```python +# V main.py změňte poslední řádek: +app.run(host="0.0.0.0", port=8080, debug=False) +# ^^^^ +# Změňte číslo portu +``` + +### Upgrade selhal +```bash +# Obnovte zálohu +cd ~ +rm -rf urio +mv urio_backup urio +cd urio +python3 main.py +``` + +## 📚 Detailní dokumentace + +- **README_CZ.md** - Kompletní feature list a použití +- **UPGRADE_GUIDE.md** - Detailní průvodce upgradem +- **GitHub Issues** - Pro reportování problémů + +## 🎨 Screenshots + +### Veřejný Dashboard +``` +┌─────────────────────────────────────────┐ +│ Celkem placených dat │ 45.234 GB │ +│ Celkem neplacených dat │ 12.456 GB │ +│ Aktivní účty │ 3 │ +│ Výdělky (30 dní) │ $125.67 │ +└─────────────────────────────────────────┘ + +Graf: Kombinovaná data ze všech účtů +Graf: Domácí účet +Graf: Pracovní účet +Graf: Server účet + +Mapa: Lokace poskytovatelů +``` + +### Správa Účtů +``` +┌──────────────────────────────────────────────┐ +│ Přezdívka │ Username │ Stav │ +├──────────────────────────────────────────────┤ +│ [Domácí účet] │ vlastik@... │ Aktivní │ +│ [Pracovní] │ work@... │ Aktivní │ +│ [Server] │ server@... │ Neaktivní│ +└──────────────────────────────────────────────┘ +``` + +## 🔥 Hlavní Nové Funkce + +✅ **Multi-Account** - Sledujte více UrNetwork účtů +✅ **Glassmorphism Design** - Moderní UI s blur efekty +✅ **Kombinované Stats** - Agregovaná data všech účtů +✅ **Individuální Grafy** - Graf pro každý účet zvlášť +✅ **Barevné Odlišení** - Každý účet má svou barvu +✅ **Admin Heslo** - Oddělené od UrNetwork credentials +✅ **Toggle Účtů** - Zapínání/vypínání sledování +✅ **Přezdívky** - Pojmenujte si účty jak chcete + +## 💡 Tipy + +### Pro více než 3 účty +- Snižte frekvenci fetchování v kódu (z 15 na 30 minut) +- Používejte přezdívky pro lepší přehled + +### Pro produkční nasazení +- Nastavte `FORCE_HTTPS=True` v .env +- Použijte Gunicorn místo development serveru +- Nastavte systemd service pro autostart + +### Webhooky +```json +{ + "content": "📊 **${account}**\n💾 Data: ${total_gb} GB\n🕐 ${update_time}" +} +``` + +## 📞 Kontakt & Podpora + +- Original project: techroy23/UrNetwork-Stats-Dashboard +- Enhanced by: Claude/Anthropic +- Issues & Questions: GitHub Issues + +--- + +**Enjoy! 🎉** + +Pro detaily viz README_CZ.md diff --git a/README_CZ.md b/README_CZ.md new file mode 100644 index 0000000..6aef3ce --- /dev/null +++ b/README_CZ.md @@ -0,0 +1,172 @@ +# UrNetwork Stats Dashboard - Vylepšená Multi-Account Verze v2.1 + +🇨🇿 **Český README** | [🇬🇧 English README](README.md) + +--- + +## 🌟 Rychlé Odkazy + +- 🚀 [Rychlý Start](QUICKSTART.md) - Spusť za 5 minut +- 📊 [Návod na Webhooky](WEBHOOK_GUIDE.md) - Discord, Slack, Telegram příklady +- 🎨 [Porovnání Designů](DESIGN_COMPARISON.md) - Proč tento design? +- 🔄 [Průvodce Upgradem](UPGRADE_GUIDE.md) - Migrace z v1.0 +- 📝 [Changelog](CHANGELOG.md) - Historie verzí + +--- + +## O Projektu + +Toto je **výrazně vylepšená multi-account verze** původního [UrNetwork Stats Dashboard](https://github.com/techroy23/UrNetwork-Stats-Dashboard). Sleduj neomezený počet UrNetwork účtů s krásnými grafy, real-time aktualizacemi a webhook notifikacemi. + +**Co je nového ve v2.1:** +- 🎯 **Multi-Account Podpora** - Sleduj neomezený počet UrNetwork účtů +- 🎨 **Čistý Design** - Cloudflare-inspirovaný tmavý vzhled +- 📊 **Lepší Grafy** - Bílý text, tooltip kdekoliv na grafu +- 🔐 **Oddělená Auth** - Admin heslo nezávislé na UrNetwork +- ⚡ **Denní Čištění** - Auto-mazání dat starších 7 dní +- 📱 **Mobilní Optimalizace** - Touch-friendly rozhraní + +> **Vylepšeno AI:** v2.0+ vytvořeno s Claude (Anthropic) pro lepší UX, multi-account podporu a moderní design. + +--- + +## ✨ Klíčové Funkce + +### 🎯 Multi-Account Správa +- ✅ Sleduj neomezený počet UrNetwork účtů současně +- ✅ Kombinované statistiky ze všech účtů +- ✅ Individuální grafy pro každý účet +- ✅ Vlastní přezdívky pro snadnou identifikaci +- ✅ Zapni/vypni účty bez smazání +- ✅ Barevně odlišené pro snadné rozpoznání + +### 📊 Pokročilá Vizualizace +- ✅ Interaktivní Chart.js grafy s tooltipem **kdekoliv na grafu** +- ✅ Celková data, Placená vs Neplacená, Delta grafy +- ✅ Bílý čitelný text na všech grafech (konec mrákání!) +- ✅ Mapa světa ukazující lokace poskytovatelů +- ✅ Real-time aktualizace každou minutu + +### ⚙️ Automatizace +- ✅ Sběr dat každých 15 minut +- ✅ Denní čištění (ponechává posledních 7 dní) +- ✅ Webhook notifikace (Discord, Slack, Telegram) +- ✅ Správa zařízení napříč všemi účty + +### 🎨 Moderní UI +- ✅ Čistý Cloudflare-inspirovaný tmavý vzhled +- ✅ Bez gradientů (lepší viditelnost grafů) +- ✅ Modrá akcent barva (#3b82f6) +- ✅ Plně responzivní (mobil, tablet, desktop) +- ✅ Podpora češtiny a angličtiny + +--- + +## 🚀 Rychlý Start + +```bash +# 1. Klonovat repozitář +git clone https://forgejo.plainrock127.xyz/mxnticek/UrNetwork-Stats-Dashboard-remade +cd UrNetwork-Stats-Dashboard-remade + +# 2. Instalovat závislosti +pip install Flask Flask-SQLAlchemy Flask-APScheduler requests python-dateutil + +# 3. Spustit +python main.py + +# 4. Otevřít prohlížeč +# http://localhost:90 +``` + +**První Spuštění:** +1. Nastav své **admin heslo** (NENÍ to tvé UrNetwork heslo!) +2. Přidej své UrNetwork účty (email + heslo + přezdívka) +3. Hotovo! Dashboard začne sbírat data každých 15 minut + +📖 **Potřebuješ více detailů?** Viz [QUICKSTART.md](QUICKSTART.md) + +--- + +## 📊 Nastavení Webhooků - Rychlé Příklady + +### Discord - Jednoduchý +```json +{ + "content": "📊 **${account}**: ${total_gb} GB" +} +``` + +### Discord - Bohatý Embed (Doporučeno) +```json +{ + "username": "UrNetwork Bot", + "embeds": [{ + "title": "⚡ ${account} - Nová Data!", + "color": 3901635, + "fields": [ + {"name": "💰 Placená", "value": "`${paid_gb} GB`", "inline": true}, + {"name": "📡 Neplacená", "value": "`${unpaid_gb} GB`", "inline": true}, + {"name": "💾 Celkem", "value": "**${total_gb} GB**", "inline": false} + ], + "footer": {"text": "UrNetwork Stats"}, + "timestamp": "${update_time}" + }] +} +``` + +### Telegram +```json +{ + "chat_id": "TVOJE_CHAT_ID", + "text": "📊 *${account}*\n💾 Celkem: ${total_gb} GB\n💰 Placená: ${paid_gb} GB\n📡 Neplacená: ${unpaid_gb} GB", + "parse_mode": "Markdown" +} +``` + +**Dostupné Proměnné:** +- `${account}` - Přezdívka účtu +- `${paid_gb}` - Placená data v GB +- `${unpaid_gb}` - Neplacená data v GB +- `${total_gb}` - Celková data v GB +- `${update_time}` - Časová značka + +📖 **Kompletní webhook návod:** [WEBHOOK_GUIDE.md](WEBHOOK_GUIDE.md) + +--- + +## 🎨 Proč Tento Design? + +Po rozsáhlém testování jsme zvolili **čistý Cloudflare-inspirovaný design** protože: + +1. **Lepší Viditelnost Grafů** - Pevné tmavé pozadí = lepší kontrast +2. **Profesionální Vzhled** - Napodobuje enterprise dashboardy +3. **Bez Únav Očí** - Čisté barvy bez gradientů +4. **Výkon** - Žádné blur efekty = rychlejší rendering +5. **Přístupnost** - Vysoké kontrastní poměry + +**Barevné Schéma:** +```css +Pozadí: #0c0d0e (Tmavá) +Primární: #3b82f6 (Modrá) +Text: #e5e7eb (Světlá) +Úspěch: #10b981 (Zelená) +Chyba: #ef4444 (Červená) +``` + +📖 **Detaily designu:** [DESIGN_COMPARISON.md](DESIGN_COMPARISON.md) + +--- + +## 🙏 Poděkování + +- **Originál:** [techroy23/UrNetwork-Stats-Dashboard](https://github.com/techroy23/UrNetwork-Stats-Dashboard) +- **v2.0+ Vylepšeno:** Claude (Anthropic AI) +- **Design Inspirace:** Cloudflare, Vercel +- **Speciální Díky:** Vlastík (mxnticek) za testování + +--- + +**Verze:** v2.1.1 | **Poslední Aktualizace:** 21. listopadu 2024 + +**Vytvořeno s ❤️ a 🤖 AI** diff --git a/UPGRADE_GUIDE.md b/UPGRADE_GUIDE.md new file mode 100644 index 0000000..0296898 --- /dev/null +++ b/UPGRADE_GUIDE.md @@ -0,0 +1,304 @@ +# UrNetwork Stats Dashboard v2.0 - Upgrade Guide + +## 🎯 Hlavní Vylepšení + +### 1. Multi-Account Support ⭐ NOVÉ +**Před:** +- Sledování pouze jednoho UrNetwork účtu +- Nutnost manuálně měnit credentials v .env +- Žádná kombinovaná statistika + +**Po:** +``` +✓ Neomezený počet UrNetwork účtů +✓ Kombinovaná statistika ze všech účtů +✓ Individuální grafy pro každý účet +✓ Snadné přepínání mezi účty +✓ Přezdívky pro lepší orientaci +✓ Zapínání/vypínání sledování +``` + +### 2. Redesign UI 🎨 VYLEPŠENO + +**Design změny:** +```css +Před: Flat dark theme +Po: Glassmorphism + Gradients + +Barvy: + Před: Modrá (#3b82f6) + Po: Fialová gradient (#667eea → #764ba2) + +Efekty: + ✓ Blur backdrop filters + ✓ Smooth hover animations + ✓ Shadow depth effects + ✓ Gradient overlays + ✓ Animated transitions +``` + +### 3. Lepší Bezpečnost 🔐 VYLEPŠENO + +**Před:** +``` +- Přihlášení pomocí UrNetwork credentials +- Hesla v .env otevřeně +``` + +**Po:** +``` +✓ Oddělené admin heslo pro dashboard +✓ UrNetwork credentials bezpečně v databázi +✓ Session management +✓ HTTPS redirect podpora +``` + +### 4. Vylepšené Grafy 📊 ROZŠÍŘENO + +**Nové možnosti:** +- Kombinovaný graf (všechny účty dohromady) +- Individuální grafy pro každý účet +- Barevné odlišení účtů +- Přepínání mezi pohledy +- Lepší legenda a tooltips + +### 5. Správa Zařízení 📱 ROZŠÍŘENO + +**Před:** +``` +Device Name | Status | Client ID | Mode | Remove +``` + +**Po:** +``` +Account | Device Name | Status | Client ID | Mode | Remove + ↓ +Označení které zařízení patří kterému účtu +``` + +## 📊 Srovnání Funkcí + +| Funkce | v1.0 | v2.0 | +|--------|------|------| +| Počet účtů | 1 | Neomezeno | +| Design | Basic Dark | Glassmorphism | +| Grafy | Základní | Multi-account + Combined | +| Admin panel | UrNetwork login | Oddělené admin heslo | +| Webhooky | Základní | S account proměnnou | +| Správa účtů | Manuálně v .env | GUI správa | +| Toggle účtů | ❌ | ✅ | +| Přezdívky | ❌ | ✅ | +| Barevné odlišení | ❌ | ✅ | +| Kombinované stats | ❌ | ✅ | + +## 🚀 Upgrade Proces + +### Krok 1: Příprava +```bash +# 1. Zastavte běžící aplikaci +pkill -f main.py + +# 2. Přejděte do složky aplikace +cd ~/urio + +# 3. Stáhněte nové soubory +# (nebo je nahrajte manuálně) +``` + +### Krok 2: Migrace +```bash +# Spusťte migrační skript +python3 migrate.py +``` + +**Migrační skript automaticky:** +1. ✓ Vytvoří zálohy (.env.backup, transfer_stats.db.backup) +2. ✓ Aktualizuje strukturu databáze +3. ✓ Přidá tabulku accounts +4. ✓ Migruje existující účet +5. ✓ Nastaví admin heslo +6. ✓ Ověří migraci + +### Krok 3: Test +```bash +# Spusťte novou verzi +python3 main.py + +# Otevřete v prohlížeči +http://your-server:90 + +# Přihlaste se pomocím NOVÉHO admin hesla +``` + +### Krok 4: Přidání účtů +``` +1. Přihlaste se do dashboardu +2. Menu → Správa účtů +3. Klikněte "Přidat účet" +4. Vyplňte: + - UrNetwork email + - UrNetwork heslo + - Přezdívku (např. "Domácí", "Práce") +5. Účet se automaticky ověří +``` + +## 🔄 Rollback (pokud něco nejde) + +### Obnovení zálohy +```bash +# Zastavte aplikaci +pkill -f main.py + +# Obnovte zálohy +cp .env.backup .env +cp transfer_stats.db.backup transfer_stats.db +mv main.py.old main.py + +# Spusťte starou verzi +python3 main.py +``` + +## 📱 Použití Multi-Account + +### Přidání druhého účtu +``` +Příklad: Máte 2 UrNetwork účty + +Účet 1 (Existující): + Email: vlastik.novotny2005@gmail.com + → Automaticky migrován + → Přezdívka: "Hlavní účet" + +Účet 2 (Nový): + Email: druhy.ucet@gmail.com + Heslo: ******** + → Přidán přes GUI + → Přezdívka: "Pracovní účet" +``` + +### Zobrazení kombinovaných dat +``` +Veřejný dashboard: + → Zobrazí součet dat obou účtů + → Individual grafy pro každý účet + → Celkové výdělky ze všech účtů + +Privátní dashboard: + → Toggle: "Kombinovaná statistika" / "Jednotlivé účty" + → Filtr podle účtu v sekci Account & Leaderboard + → Zařízení označená účtem +``` + +## 🎨 CSS Customizace + +### Změna barevného schématu +V `main_enhanced.py`, změňte CSS proměnné: + +```css +:root { + /* Změňte tyto hodnoty pro vlastní barevné schéma */ + --primary: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + --success: #10b981; + --danger: #ef4444; +} +``` + +### Populární barevné schémata: + +**Modrá (Ocean):** +```css +--primary: linear-gradient(135deg, #667eea 0%, #00d4ff 100%); +``` + +**Zelená (Nature):** +```css +--primary: linear-gradient(135deg, #11998e 0%, #38ef7d 100%); +``` + +**Oranžová (Sunset):** +```css +--primary: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); +``` + +## 🐛 Časté Problémy + +### Problém: "Nelze se přihlásit" +**Řešení:** +```bash +# Zkontrolujte ADMIN_PASSWORD v .env +cat .env | grep ADMIN_PASSWORD + +# Pokud chybí, přidejte ho +echo "ADMIN_PASSWORD=your_password" >> .env +``` + +### Problém: "Účet se nepřidává" +**Řešení:** +1. Zkontrolujte UrNetwork credentials +2. Zkuste se přihlásit na bringyour.com +3. Zkontrolujte logy: `tail -f nohup.out` + +### Problém: "Chybí data po migraci" +**Řešení:** +```bash +# Ověřte account_id ve stats tabulce +sqlite3 transfer_stats.db "SELECT COUNT(*) FROM stats WHERE account_id IS NULL;" + +# Pokud je > 0, přiřaďte manuálně +sqlite3 transfer_stats.db "UPDATE stats SET account_id = 1 WHERE account_id IS NULL;" +``` + +### Problém: "Webhook nefunguje" +**Řešení:** +``` +Starý formát: + {"content": "Data: ${total_gb} GB"} + +Nový formát: + {"content": "Account: ${account}, Data: ${total_gb} GB"} + ^^^^^^^^^^^ + Nová proměnná +``` + +## 📈 Performance Tips + +### Pro více než 5 účtů: +```python +# V main_enhanced.py, upravte job interval +@scheduler.task(id="log_stats_job", trigger="cron", minute="0,30") +# místo: minute="0,15,30,45" +# Tím snížíte frekvenci API callů +``` + +### Pro lepší responzivitu: +```bash +# Použijte Gunicorn místo development serveru +pip install gunicorn +gunicorn -w 4 -b 0.0.0.0:90 main_enhanced:app +``` + +## 🎯 Co Dělat Dál + +1. **Přidejte další účty** + - Menu → Správa účtů → Přidat účet + +2. **Nastavte webhooky** + - Menu → Nastavení → Webhook Management + - Použijte `${account}` pro rozlišení účtů + +3. **Customizujte design** + - Změňte CSS proměnné podle svých preferencí + +4. **Monitoring** + - Zkontrolujte logy pravidelně + - Nastavte alerting přes webhooky + +## 📞 Podpora + +- **GitHub Issues**: Pro bug reporty a feature requesty +- **Documentation**: README_CZ.md pro detailní dokumentaci +- **Migration Help**: migrate.py --help pro nápovědu + +--- + +**Enjoy your new multi-account dashboard! 🚀** diff --git a/WEBHOOK_GUIDE.md b/WEBHOOK_GUIDE.md new file mode 100644 index 0000000..2eda464 --- /dev/null +++ b/WEBHOOK_GUIDE.md @@ -0,0 +1,391 @@ +# 🔔 Webhook Průvodce - UrNetwork Stats Dashboard + +## 📋 Dostupné Proměnné + +Když webhook systém posílá notifikace, máš k dispozici tyto proměnné: + +| Proměnná | Popis | Příklad | Formát | +|----------|-------|---------|--------| +| `${account}` | Přezdívka účtu | "Hlavní účet" | Text | +| `${paid_gb}` | Placená data v GB | "123.456" | 3 des. místa | +| `${unpaid_gb}` | Neplacená data v GB | "45.678" | 3 des. místa | +| `${total_gb}` | Celková data v GB | "169.134" | 3 des. místa | +| `${update_time}` | Čas aktualizace | "2024-11-21 12:30:45" | YYYY-MM-DD HH:MM:SS | + +## 🎯 Kdy Se Webhooky Spouštějí? + +- **Každých 15 minut** - Když scheduler načte nová data +- **Pro každý aktivní účet** zvlášť +- Pouze pokud se načtení dat povede + +--- + +## 🔥 Příklady Pro Tebe (Vlastík) + +### 1. Discord - Jednoduchá Notifikace 💬 + +```json +{ + "content": "📊 **${account}** aktualizace!\n💾 Celkem: **${total_gb} GB**\n💰 Placená: ${paid_gb} GB | 📡 Neplacená: ${unpaid_gb} GB\n🕐 ${update_time}" +} +``` + +**Výstup:** +``` +📊 **Hlavní účet** aktualizace! +💾 Celkem: **169.134 GB** +💰 Placená: 123.456 GB | 📡 Neplacená: 45.678 GB +🕐 2024-11-21 12:30:45 +``` + +--- + +### 2. Discord - Bohatý Embed (Český) 🇨🇿 + +```json +{ + "embeds": [{ + "title": "📊 UrNetwork - ${account}", + "description": "Nová data byla načtena!", + "color": 3901635, + "fields": [ + { + "name": "💰 Placená Data", + "value": "${paid_gb} GB", + "inline": true + }, + { + "name": "📡 Neplacená Data", + "value": "${unpaid_gb} GB", + "inline": true + }, + { + "name": "💾 Celkem Poskytnutých Dat", + "value": "**${total_gb} GB**", + "inline": false + } + ], + "footer": { + "text": "Aktualizováno" + }, + "timestamp": "${update_time}" + }] +} +``` + +--- + +### 3. Discord - Minimalistický 🎯 + +```json +{ + "content": "⚡ ${account}: **${total_gb} GB** (${paid_gb} / ${unpaid_gb})" +} +``` + +**Výstup:** +``` +⚡ Hlavní účet: **169.134 GB** (123.456 / 45.678) +``` + +--- + +### 4. Discord - S Progress Barem 📊 + +```json +{ + "embeds": [{ + "title": "⚡ ${account} Stats", + "color": 3447003, + "description": "**Celkem: ${total_gb} GB**\n\n💰 Placená: `${paid_gb} GB`\n📡 Neplacená: `${unpaid_gb} GB`", + "footer": { + "text": "${update_time}" + } + }] +} +``` + +--- + +### 5. Slack - Jednoduchá Zpráva 💼 + +```json +{ + "text": ":bar_chart: *${account}* - Aktualizace\nCelkem: *${total_gb} GB* | Placená: ${paid_gb} GB | Neplacená: ${unpaid_gb} GB\n_${update_time}_" +} +``` + +--- + +### 6. Slack - Formátovaná Zpráva 💼 + +```json +{ + "blocks": [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": "📊 UrNetwork Stats - ${account}" + } + }, + { + "type": "section", + "fields": [ + { + "type": "mrkdwn", + "text": "*Placená Data:*\n${paid_gb} GB" + }, + { + "type": "mrkdwn", + "text": "*Neplacená Data:*\n${unpaid_gb} GB" + }, + { + "type": "mrkdwn", + "text": "*Celkem:*\n${total_gb} GB" + }, + { + "type": "mrkdwn", + "text": "*Čas:*\n${update_time}" + } + ] + } + ] +} +``` + +--- + +### 7. Telegram - Markdown 📱 + +```json +{ + "chat_id": "YOUR_CHAT_ID", + "text": "📊 *${account}* - Aktualizace\n\n💾 *Celkem:* ${total_gb} GB\n💰 Placená: ${paid_gb} GB\n📡 Neplacená: ${unpaid_gb} GB\n\n🕐 _${update_time}_", + "parse_mode": "Markdown" +} +``` + +--- + +### 8. MS Teams - Jednoduchá Karta 🏢 + +```json +{ + "@type": "MessageCard", + "@context": "https://schema.org/extensions", + "summary": "${account} Stats Update", + "themeColor": "0078D7", + "title": "📊 ${account} - UrNetwork Stats", + "sections": [{ + "facts": [ + { + "name": "Placená Data:", + "value": "${paid_gb} GB" + }, + { + "name": "Neplacená Data:", + "value": "${unpaid_gb} GB" + }, + { + "name": "Celkem:", + "value": "${total_gb} GB" + } + ], + "text": "Aktualizováno: ${update_time}" + }] +} +``` + +--- + +### 9. Generic JSON (pro vlastní API) 🔧 + +```json +{ + "event": "urnetwork_update", + "account_name": "${account}", + "data": { + "paid_gb": "${paid_gb}", + "unpaid_gb": "${unpaid_gb}", + "total_gb": "${total_gb}" + }, + "timestamp": "${update_time}" +} +``` + +--- + +### 10. IFTTT Webhook 🔗 + +```json +{ + "value1": "${account}", + "value2": "${total_gb} GB", + "value3": "Placená: ${paid_gb} GB, Neplacená: ${unpaid_gb} GB" +} +``` + +--- + +## 🎨 Doporučené Pro Tebe + +Protože máš UrNetwork setup a pravděpodobně používáš Discord/Telegram, doporučuji: + +### ⭐ Nejlepší Volba - Discord Embed (Český) + +```json +{ + "username": "UrNetwork Bot", + "avatar_url": "https://cdn-icons-png.flaticon.com/512/2920/2920277.png", + "embeds": [{ + "title": "⚡ ${account} - Nová Data!", + "color": 3901635, + "fields": [ + { + "name": "💰 Placená Data", + "value": "`${paid_gb} GB`", + "inline": true + }, + { + "name": "📡 Neplacená Data", + "value": "`${unpaid_gb} GB`", + "inline": true + }, + { + "name": "💾 Celkem", + "value": "**${total_gb} GB**", + "inline": false + } + ], + "footer": { + "text": "UrNetwork Stats Dashboard" + }, + "timestamp": "${update_time}" + }] +} +``` + +### 🚀 Rychlá Varianta - Pro Mobil + +```json +{ + "content": "⚡ **${account}**: ${total_gb} GB\n💰 ${paid_gb} | 📡 ${unpaid_gb}" +} +``` + +--- + +## 📝 Jak Přidat Webhook + +### 1. Discord Webhook URL +``` +1. Jdi do Discord serveru +2. Server Settings → Integrations → Webhooks +3. New Webhook +4. Zkopíruj Webhook URL +5. Vlož do dashboardu v Settings → Webhook Management +``` + +### 2. V Dashboardu +``` +1. Přihlaš se do dashboardu +2. Menu → Settings (Nastavení) +3. Webhook Management +4. Vlož URL: https://discord.com/api/webhooks/... +5. Vlož JSON payload (výše) +6. Přidat Webhook +``` + +--- + +## 🧪 Test Webhooku + +Chceš otestovat, jestli webhook funguje? Spusť manuální fetch: + +```bash +# V dashboardu klikni "Fetch Now" +# nebo z příkazové řádky: +curl -X POST http://localhost:90/trigger +``` + +--- + +## 🔍 Debug + +### Pokud webhook nefunguje: + +1. **Zkontroluj logy:** +```bash +tail -f nohup.out | grep webhook +``` + +2. **Zkontroluj JSON syntax:** +```bash +# Použij online JSON validator +# https://jsonlint.com/ +``` + +3. **Test Discord webhooku:** +```bash +curl -X POST "YOUR_WEBHOOK_URL" \ + -H "Content-Type: application/json" \ + -d '{"content": "Test message"}' +``` + +--- + +## 💡 Pro Tipy + +### Více Webhooků +Můžeš přidat více webhooků - každý dostane notifikaci! +- Discord server 1 +- Discord server 2 +- Slack workspace +- Telegram bot + +### Filtrování Podle Účtu +Chceš notifikace jen pro specifický účet? + +V payloadu použij podmínku (pokud tvůj webhook systém podporuje): +```json +{ + "content": "${account} update: ${total_gb} GB" +} +``` + +A vytvoř samostatný webhook jen když je `${account}` = "Hlavní účet" + +### Rate Limiting +Pozor! Většina služeb má rate limit: +- Discord: 5 zpráv / 2 sekundy per webhook +- Slack: 1 zpráva / sekunda + +Dashboard posílá notifikace každých 15 minut, takže jsi v pohodě! ✅ + +--- + +## 🎯 Tvoje Setup (Doporučení) + +Protože máš pravděpodobně několik účtů, doporučuji: + +```json +{ + "embeds": [{ + "title": "📊 ${account}", + "description": "**${total_gb} GB** poskytnutých dat", + "color": 3901635, + "fields": [ + {"name": "💰 Placená", "value": "${paid_gb} GB", "inline": true}, + {"name": "📡 Neplacená", "value": "${unpaid_gb} GB", "inline": true} + ], + "footer": {"text": "${update_time}"} + }] +} +``` + +Takto dostaneš **jednu zprávu každých 15 minut pro každý účet** s přehledným rozdělením! 🎉 + +--- + +**Potřebuješ pomoct s nastavením? Dej vědět! 🚀** diff --git a/fix_db_path.py b/fix_db_path.py new file mode 100644 index 0000000..80f2c13 --- /dev/null +++ b/fix_db_path.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +""" +Quick fix script for database path issue +Fixes the main.py file to use absolute paths +""" + +import os +import sys + +def fix_main_py(): + """Fix main.py to use absolute database path""" + + main_file = 'main.py' + + if not os.path.exists(main_file): + print("❌ main.py not found in current directory") + print(" Run this script from the urio folder") + sys.exit(1) + + print("🔧 Fixing database path in main.py...") + + # Read the file + with open(main_file, 'r') as f: + content = f.read() + + # Check if already fixed + if 'BASE_DIR = os.path.abspath(os.path.dirname(__file__))' in content: + print("✅ main.py is already fixed!") + return + + # Fix 1: Update Config class + old_config = '''class Config: + """Flask configuration.""" + SCHEDULER_API_ENABLED = True + SQLALCHEMY_DATABASE_URI = os.getenv("DATABASE_URL", "sqlite:///instance/transfer_stats.db")''' + + new_config = '''class Config: + """Flask configuration.""" + SCHEDULER_API_ENABLED = True + # Use absolute path for database + BASE_DIR = os.path.abspath(os.path.dirname(__file__)) + INSTANCE_DIR = os.path.join(BASE_DIR, 'instance') + SQLALCHEMY_DATABASE_URI = os.getenv("DATABASE_URL", f"sqlite:///{os.path.join(INSTANCE_DIR, 'transfer_stats.db')}")''' + + if old_config in content: + content = content.replace(old_config, new_config) + print("✅ Updated Config class with absolute paths") + + # Fix 2: Ensure instance folder creation happens before app init + old_init = '''# --- Flask App Initialization --- + +app = Flask(__name__) +app.config.from_object(Config) + +# Ensure instance folder exists +instance_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'instance') +if not os.path.exists(instance_path): + os.makedirs(instance_path)''' + + new_init = '''# --- Flask App Initialization --- + +# Ensure instance folder exists BEFORE creating the app +instance_path = Config.INSTANCE_DIR +if not os.path.exists(instance_path): + os.makedirs(instance_path) + logging.info(f"Created instance directory: {instance_path}") + +app = Flask(__name__) +app.config.from_object(Config)''' + + if old_init in content: + content = content.replace(old_init, new_init) + print("✅ Updated instance folder creation") + + # Write the fixed content + with open(main_file, 'w') as f: + f.write(content) + + print("\n✅ main.py has been fixed!") + print("\nYou can now run:") + print(" python3 main.py") + +if __name__ == '__main__': + print(""" +╔════════════════════════════════════════════════════════════╗ +║ UrNetwork Stats Dashboard - Database Path Fix ║ +╚════════════════════════════════════════════════════════════╝ + """) + + try: + fix_main_py() + except Exception as e: + print(f"\n❌ Error: {e}") + sys.exit(1) diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..61650eb --- /dev/null +++ b/install.sh @@ -0,0 +1,102 @@ +#!/bin/bash + +# UrNetwork Stats Dashboard - Installation Script v2.1 +# This script automates the installation process + +set -e # Exit on error + +echo "=========================================" +echo "UrNetwork Stats Dashboard v2.1" +echo "Installation Script" +echo "=========================================" +echo "" + +# Check Python version +echo "[1/6] Checking Python version..." +if ! command -v python3 &> /dev/null; then + echo "❌ Python 3 is not installed!" + echo "Please install Python 3.8+ and try again." + exit 1 +fi + +PYTHON_VERSION=$(python3 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))') +echo "✅ Python $PYTHON_VERSION found" +echo "" + +# Check pip +echo "[2/6] Checking pip..." +if ! command -v pip3 &> /dev/null; then + echo "❌ pip3 is not installed!" + echo "Please install pip3 and try again." + exit 1 +fi +echo "✅ pip3 found" +echo "" + +# Install dependencies +echo "[3/6] Installing dependencies..." +echo "This may take a few minutes..." +pip3 install -r requirements.txt +echo "✅ Dependencies installed" +echo "" + +# Create necessary directories +echo "[4/6] Creating directories..." +mkdir -p instance +echo "✅ Directories created" +echo "" + +# Check if already installed +echo "[5/6] Checking existing installation..." +if [ -f ".env" ]; then + echo "⚠️ Found existing .env file" + read -p "Do you want to keep it? (y/n): " keep_env + if [ "$keep_env" != "y" ]; then + mv .env .env.backup + echo "✅ Backed up to .env.backup" + else + echo "✅ Keeping existing configuration" + fi +fi + +if [ -f "instance/transfer_stats.db" ]; then + echo "⚠️ Found existing database" + read -p "Do you want to keep it? (y/n): " keep_db + if [ "$keep_db" != "y" ]; then + mv instance/transfer_stats.db instance/transfer_stats.db.backup + echo "✅ Backed up to transfer_stats.db.backup" + else + echo "✅ Keeping existing database" + fi +fi +echo "" + +# Final instructions +echo "[6/6] Installation complete!" +echo "" +echo "=========================================" +echo "🎉 Ready to start!" +echo "=========================================" +echo "" +echo "To run the dashboard:" +echo " python3 main.py" +echo "" +echo "Then open your browser to:" +echo " http://localhost:90" +echo "" +echo "On first run, you will:" +echo " 1. Set your admin password" +echo " 2. Add your UrNetwork accounts" +echo "" +echo "For production deployment with Gunicorn:" +echo " gunicorn --bind 0.0.0.0:90 --workers 4 main:app" +echo "" +echo "For systemd service setup, see README.md" +echo "" +echo "📖 Documentation:" +echo " - README.md - Full documentation" +echo " - QUICKSTART.md - Quick start guide" +echo " - WEBHOOK_GUIDE.md - Webhook examples" +echo "" +echo "Need help? Check the docs or create an issue!" +echo "=========================================" diff --git a/main_clean.py b/main_clean.py new file mode 100644 index 0000000..e2de183 --- /dev/null +++ b/main_clean.py @@ -0,0 +1,2221 @@ +# ---------------------------------------- +# UrNetwork Stats Dashboard - Enhanced Multi-Account Edition +# ---------------------------------------- +# Enhanced with modern design, multi-account support, and combined statistics +# ---------------------------------------- + +import os +import time +import datetime +import requests +import logging +import json +from functools import wraps +from flask import ( + Flask, request, render_template_string, + redirect, url_for, flash, session, g, jsonify +) +from flask_sqlalchemy import SQLAlchemy +from flask_apscheduler import APScheduler +from dateutil.parser import isoparse +import secrets +from string import Template + +# --- Application Setup & Configuration --- + +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') + +ENV_FILE = ".env" + +def load_env(): + """Load environment variables from .env file.""" + if os.path.exists(ENV_FILE): + with open(ENV_FILE, 'r') as f: + for line in f: + if '=' in line and not line.strip().startswith('#'): + key, value = line.strip().split('=', 1) + os.environ[key] = value + +load_env() + +class Config: + """Flask configuration.""" + SCHEDULER_API_ENABLED = True + # Use absolute path for database + BASE_DIR = os.path.abspath(os.path.dirname(__file__)) + INSTANCE_DIR = os.path.join(BASE_DIR, 'instance') + SQLALCHEMY_DATABASE_URI = os.getenv("DATABASE_URL", f"sqlite:///{os.path.join(INSTANCE_DIR, 'transfer_stats.db')}") + SQLALCHEMY_TRACK_MODIFICATIONS = False + SECRET_KEY = os.getenv("SECRET_KEY", "default-secret-key-for-initial-setup") + UR_API_BASE = "https://api.bringyour.com" + FORCE_HTTPS = os.getenv("FORCE_HTTPS", "False").lower() in ('true', '1', 't') + ENABLE_ACCOUNT_STATS = os.getenv("ENABLE_ACCOUNT_STATS", "True").lower() in ('true', '1', 't') + ENABLE_LEADERBOARD = os.getenv("ENABLE_LEADERBOARD", "True").lower() in ('true', '1', 't') + ENABLE_DEVICE_STATS = os.getenv("ENABLE_DEVICE_STATS", "True").lower() in ('true', '1', 't') + +# --- Flask App Initialization --- + +# Ensure instance folder exists BEFORE creating the app +instance_path = Config.INSTANCE_DIR +if not os.path.exists(instance_path): + os.makedirs(instance_path) + logging.info(f"Created instance directory: {instance_path}") + +app = Flask(__name__) +app.config.from_object(Config) + +db = SQLAlchemy(app) +scheduler = APScheduler() +scheduler.init_app(app) + +# --- Database Models --- + +class Account(db.Model): + """Represents a UrNetwork account.""" + __tablename__ = 'accounts' + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(100), unique=True, nullable=False) + password = db.Column(db.String(200), nullable=False) + nickname = db.Column(db.String(100), nullable=True) + is_active = db.Column(db.Boolean, default=True) + created_at = db.Column(db.DateTime, server_default=db.func.now()) + +class Stats(db.Model): + """Represents a snapshot of paid vs unpaid bytes at a given timestamp.""" + __tablename__ = 'stats' + id = db.Column(db.Integer, primary_key=True) + account_id = db.Column(db.Integer, db.ForeignKey('accounts.id'), nullable=False) + timestamp = db.Column(db.DateTime, server_default=db.func.now()) + paid_bytes = db.Column(db.BigInteger, nullable=False) + paid_gb = db.Column(db.Float, nullable=False) + unpaid_bytes = db.Column(db.BigInteger, nullable=False) + unpaid_gb = db.Column(db.Float, nullable=False) + account = db.relationship('Account', backref='stats') + +class Webhook(db.Model): + """Represents a webhook URL with an optional custom payload.""" + __tablename__ = 'webhook' + id = db.Column(db.Integer, primary_key=True) + url = db.Column(db.String, unique=True, nullable=False) + payload = db.Column(db.Text, nullable=True) + +class Setting(db.Model): + """Represents a key-value setting for the application.""" + __tablename__ = 'settings' + key = db.Column(db.String(50), primary_key=True) + value = db.Column(db.String(100), nullable=False) + +# --- Helper Functions --- + +def get_setting(key, default=None): + """Gets a setting value from the database.""" + setting = Setting.query.get(key) + return setting.value if setting else default + +def get_boolean_setting(key): + """Gets a boolean setting from the database.""" + value = get_setting(key, 'False') + return value.lower() in ('true', '1', 't') + +def is_installed(): + """Check if the application has been configured.""" + return os.getenv("SECRET_KEY") and os.getenv("SECRET_KEY") != "default-secret-key-for-initial-setup" + +def save_env_file(config_data): + """Save configuration data to the .env file.""" + try: + existing_env = {} + if os.path.exists(ENV_FILE): + with open(ENV_FILE, 'r') as f: + for line in f: + if '=' in line and not line.strip().startswith('#'): + key, value = line.strip().split('=', 1) + existing_env[key] = value + + existing_env.update(config_data) + + with open(ENV_FILE, "w") as f: + for key, value in existing_env.items(): + f.write(f"{key}={value}\n") + if "ENABLE_ACCOUNT_STATS" not in existing_env: + f.write("\n# Feature Flags\n") + f.write("ENABLE_ACCOUNT_STATS=True\n") + f.write("ENABLE_LEADERBOARD=True\n") + f.write("ENABLE_DEVICE_STATS=True\n") + if "FORCE_HTTPS" not in existing_env: + f.write("\n# Security Settings\n") + f.write("FORCE_HTTPS=False\n") + load_env() + return True + except IOError as e: + logging.error(f"Failed to write to .env file: {e}") + return False + +def request_with_retry(method, url, retries=3, backoff=5, timeout=30, **kwargs): + """Issue an HTTP request with retries.""" + last_exc = None + for attempt in range(1, retries + 1): + try: + resp = requests.request(method, url, timeout=timeout, **kwargs) + resp.raise_for_status() + return resp + except requests.exceptions.RequestException as e: + last_exc = e + logging.warning(f"[{method.upper()} {url}] attempt {attempt}/{retries} failed: {e}") + if attempt < retries: + time.sleep(backoff) + logging.error(f"All {retries} attempts to {method.upper()} {url} failed: {last_exc}") + return None + +def get_jwt_from_credentials(user, password): + """Fetch a new JWT token using username and password.""" + try: + resp = request_with_retry( + "post", + f"{app.config['UR_API_BASE']}/auth/login-with-password", + headers={"Content-Type": "application/json"}, + json={"user_auth": user, "password": password}, + ) + if not resp: + raise RuntimeError("API request failed after multiple retries.") + data = resp.json() + token = data.get("network", {}).get("by_jwt") + if not token: + err = data.get("message") or data.get("error") or str(data) + raise RuntimeError(f"Login failed: {err}") + return token + except Exception as e: + logging.error(f"Could not get JWT from credentials: {e}") + return None + +def get_valid_jwt(account): + """Gets a valid JWT for API calls for a specific account.""" + if not account: + return None + return get_jwt_from_credentials(account.username, account.password) + +# --- API Fetch Functions --- + +def fetch_transfer_stats(jwt_token): + """Retrieve transfer statistics using the provided JWT.""" + if not jwt_token: return None + resp = request_with_retry("get", f"{app.config['UR_API_BASE']}/transfer/stats", headers={"Authorization": f"Bearer {jwt_token}"}) + if not resp: return None + data = resp.json() + paid = data.get("paid_bytes_provided", 0) + unpaid = data.get("unpaid_bytes_provided", 0) + return { + "paid_bytes": paid, + "paid_gb": paid / 1e9, + "unpaid_bytes": unpaid, + "unpaid_gb": unpaid / 1e9 + } + +def fetch_payment_stats(jwt_token): + """Retrieve account payment statistics using the provided JWT.""" + if not jwt_token: return [] + resp = request_with_retry("get", f"{app.config['UR_API_BASE']}/account/payments", headers={"Authorization": f"Bearer {jwt_token}"}) + if not resp: return [] + return resp.json().get("account_payments", []) + +def fetch_account_details(jwt_token): + """Fetches various account details like points and referrals.""" + if not jwt_token: return {} + headers = {"Authorization": f"Bearer {jwt_token}"} + details = {} + + points_resp = request_with_retry("get", f"{app.config['UR_API_BASE']}/account/points", headers=headers) + if points_resp: + points_data = points_resp.json().get("network_points", []) + details['points'] = sum(p.get('point_value', 0) for p in points_data) + + referral_resp = request_with_retry("get", f"{app.config['UR_API_BASE']}/account/referral-code", headers=headers) + if referral_resp: + details['referrals'] = referral_resp.json() + + ranking_resp = request_with_retry("get", f"{app.config['UR_API_BASE']}/network/ranking", headers=headers) + if ranking_resp: + details['ranking'] = ranking_resp.json().get('network_ranking', {}) + + return details + +def fetch_leaderboard(jwt_token): + """Fetches the global leaderboard.""" + if not jwt_token: return [] + headers = {"Authorization": f"Bearer {jwt_token}"} + resp = request_with_retry("post", f"{app.config['UR_API_BASE']}/stats/leaderboard", headers=headers, json={}) + if not resp: return [] + return resp.json().get("earners", []) + +def fetch_devices(jwt_token): + """Fetches the status of all network clients/devices.""" + if not jwt_token: return [] + headers = {"Authorization": f"Bearer {jwt_token}"} + resp = request_with_retry("get", f"{app.config['UR_API_BASE']}/network/clients", headers=headers) + if not resp: return [] + devices = resp.json().get("clients", []) + provide_mode_map = {-1: "Default", 0: "None", 1: "Network", 2: "Friends & Family", 3: "Public", 4: "Stream"} + for device in devices: + device['provide_mode_str'] = provide_mode_map.get(device.get('provide_mode'), 'Unknown') + return devices + +def remove_device(jwt_token, client_id): + """Removes a device from the network.""" + if not jwt_token: return False, "Authentication token not available." + headers = {"Authorization": f"Bearer {jwt_token}"} + payload = {"client_id": client_id} + resp = request_with_retry("post", f"{app.config['UR_API_BASE']}/network/remove-client", headers=headers, json=payload) + if resp and resp.status_code == 200: + data = resp.json() + if data.get("error"): + return False, data["error"].get("message", "An unknown error occurred.") + return True, "Device removed successfully." + elif resp: + return False, f"API returned status {resp.status_code}." + else: + return False, "Failed to communicate with API." + +def calculate_earnings(payments): + """Calculate total and monthly earnings from a list of payments.""" + total_earnings = 0 + monthly_earnings = 0 + now = datetime.datetime.now(datetime.timezone.utc) + one_month_ago = now - datetime.timedelta(days=30) + + if not payments: + return 0, 0 + + for payment in payments: + if payment.get("completed"): + amount = payment.get("token_amount", 0) + total_earnings += amount + payment_time_str = payment.get("payment_time") + if payment_time_str: + try: + payment_time = isoparse(payment_time_str) + if payment_time > one_month_ago: + monthly_earnings += amount + except (ValueError, TypeError): + logging.warning(f"Could not parse payment_time: {payment_time_str}") + + return total_earnings, monthly_earnings + +def fetch_provider_locations(): + """Retrieve provider locations from the public API.""" + resp = request_with_retry("get", f"{app.config['UR_API_BASE']}/network/provider-locations") + return resp.json() if resp else None + +def send_webhook_notification(stats_data, account_nickname=None): + """Sends a notification to all configured webhooks.""" + with app.app_context(): + webhooks = Webhook.query.all() + if not webhooks: + return + + for webhook in webhooks: + payload_to_send = None + try: + if webhook.payload: + template = Template(webhook.payload) + payload_str = template.safe_substitute( + account=account_nickname or "Unknown", + paid_gb=f"{stats_data['paid_gb']:.3f}", + unpaid_gb=f"{stats_data['unpaid_gb']:.3f}", + total_gb=f"{(stats_data['paid_gb'] + stats_data['unpaid_gb']):.3f}", + update_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ) + payload_to_send = json.loads(payload_str) + else: + payload_to_send = { + "embeds": [{ + "title": f"UrNetwork Stats Update - {account_nickname or 'Account'}", + "description": "New data has been synced from the UrNetwork API.", + "color": 5814783, + "fields": [ + {"name": "Account", "value": account_nickname or "Unknown", "inline": False}, + {"name": "Total Paid Data", "value": f"{stats_data['paid_gb']:.3f} GB", "inline": True}, + {"name": "Total Unpaid Data", "value": f"{stats_data['unpaid_gb']:.3f} GB", "inline": True}, + {"name": "Total Data Provided", "value": f"{(stats_data['paid_gb'] + stats_data['unpaid_gb']):.3f} GB", "inline": True}, + ], + "footer": {"text": f"Update Time: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"} + }] + } + + requests.post(webhook.url, json=payload_to_send, timeout=10) + logging.info(f"Sent webhook notification to {webhook.url}") + except (json.JSONDecodeError, TypeError) as e: + logging.error(f"Failed to parse or substitute custom payload for webhook {webhook.url}: {e}") + except requests.exceptions.RequestException as e: + logging.error(f"Failed to send webhook to {webhook.url}: {e}") + +# --- Authentication Decorator --- +def login_required(f): + """Decorator to ensure a user is logged in.""" + @wraps(f) + def decorated_function(*args, **kwargs): + if not session.get('logged_in'): + flash(g.t['error_login_required'], "error") + next_url = request.url + if app.config['FORCE_HTTPS']: + next_url = next_url.replace('http://', 'https://', 1) + return redirect(url_for('login', next=next_url)) + return f(*args, **kwargs) + return decorated_function + +# --- Scheduled Jobs --- + +@scheduler.task(id="log_stats_job", trigger="cron", minute="0,15,30,45") +def log_stats_job(): + """Scheduled job to fetch and store stats every 15 minutes for all active accounts.""" + with app.app_context(): + if not is_installed(): + logging.warning("log_stats_job skipped: Application is not installed.") + return + + logging.info("Running scheduled stats fetch for all accounts...") + accounts = Account.query.filter_by(is_active=True).all() + + for account in accounts: + try: + jwt = get_valid_jwt(account) + if not jwt: + logging.warning(f"Could not authenticate account {account.username}") + continue + + stats_data = fetch_transfer_stats(jwt) + if not stats_data: + logging.warning(f"Could not fetch stats for account {account.username}") + continue + + entry = Stats( + account_id=account.id, + paid_bytes=stats_data["paid_bytes"], + paid_gb=stats_data["paid_gb"], + unpaid_bytes=stats_data["unpaid_bytes"], + unpaid_gb=stats_data["unpaid_gb"] + ) + db.session.add(entry) + db.session.commit() + logging.info(f"Logged stats for account {account.nickname or account.username} at {entry.timestamp}") + send_webhook_notification(stats_data, account.nickname or account.username) + except Exception as e: + logging.error(f"Failed to fetch stats for account {account.username}: {e}") + +@scheduler.task(id="cleanup_old_stats_job", trigger="cron", hour="3", minute="0") +def cleanup_old_stats_job(): + """Scheduled job to delete stats data older than 7 days, runs daily at 3 AM.""" + with app.app_context(): + if not is_installed(): + logging.warning("cleanup_old_stats_job skipped: Application is not installed.") + return + + logging.info("Running daily stats cleanup job...") + try: + cutoff_date = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(days=7) + num_rows_deleted = db.session.query(Stats).filter(Stats.timestamp < cutoff_date).delete(synchronize_session=False) + db.session.commit() + + if num_rows_deleted > 0: + logging.info(f"Successfully deleted {num_rows_deleted} stats records older than 7 days.") + else: + logging.info("No old stats records found to delete.") + except Exception as e: + logging.error(f"Scheduled job 'cleanup_old_stats_job' failed: {e}") + db.session.rollback() + +# --- Internationalization (i18n) --- + +TEXTS = { + 'cs': { + 'nav_public': 'Veřejný pohled', + 'nav_owner_dashboard': 'Panel vlastníka', + 'nav_overview': 'Přehled', + 'nav_account': 'Účet & Žebříček', + 'nav_devices': 'Zařízení', + 'nav_settings': 'Nastavení', + 'nav_accounts': 'Správa účtů', + 'nav_login': 'Přihlásit se', + 'nav_logout': 'Odhlásit se', + 'title_public_dashboard': 'Veřejný panel', + 'title_owner_dashboard': 'Panel vlastníka', + 'title_account_page': 'Účet & Žebříček', + 'title_devices_page': 'Správa zařízení', + 'title_settings': 'Nastavení', + 'title_accounts_page': 'Správa účtů', + 'title_login': 'Přihlásit se', + 'title_setup': 'Počáteční nastavení', + 'card_paid_data': 'Celkem placených dat', + 'card_unpaid_data': 'Celkem neplacených dat', + 'card_earnings_30d': 'Výdělky (30 dní)', + 'card_total_earnings': 'Celkové výdělky', + 'card_last_update': 'Poslední aktualizace', + 'card_account_points': 'Body účtu', + 'card_your_rank': 'Vaše pozice', + 'card_total_referrals': 'Celkem doporučení', + 'card_active_accounts': 'Aktivní účty', + 'card_total_devices': 'Celkem zařízení', + 'chart_total_data_gb': 'Celková poskytnutá data (GB)', + 'chart_paid_vs_unpaid': 'Placená vs. neplacená data (GB)', + 'chart_delta': 'Změna dat za interval (GB)', + 'chart_paid': 'Placená (GB)', + 'chart_unpaid': 'Neplacená (GB)', + 'chart_no_data': 'Nedostatek dat pro zobrazení grafu.', + 'history_title': 'Detailní historie', + 'history_timestamp': 'Časová značka', + 'history_change': 'Změna (GB)', + 'btn_fetch_now': 'Načíst nyní', + 'btn_clear_data': 'Vymazat všechna data', + 'btn_add_account': 'Přidat účet', + 'confirm_clear_data': 'Jste si jisti? Tímto smažete všechna historická data statistik.', + 'next_fetch': 'Další automatické načtení:', + 'login_header': 'Přihlaste se do svého panelu', + 'login_username': 'Administrátorské heslo', + 'login_password': 'Heslo', + 'login_button': 'Přihlásit se', + 'error_login_required': 'Pro zobrazení této stránky se musíte přihlásit.', + 'error_invalid_credentials': 'Neplatné heslo.', + 'flash_logged_in': 'Byli jste úspěšně přihlášeni.', + 'flash_logged_out': 'Byli jste odhlášeni.', + 'flash_settings_saved': 'Nastavení bylo úspěšně uloženo.', + 'flash_fetch_success': 'Nejnovější statistiky byly úspěšně načteny a uloženy.', + 'flash_fetch_fail': 'Nepodařilo se načíst statistiky: ', + 'flash_clear_success': 'Úspěšně smazáno {count} záznamů z databáze.', + 'flash_clear_fail': 'Při mazání databáze došlo k chybě.', + 'flash_account_added': 'Účet byl úspěšně přidán.', + 'flash_account_removed': 'Účet byl úspěšně odstraněn.', + 'flash_account_updated': 'Účet byl úspěšně aktualizován.', + 'webhook_title': 'Správa webhooků', + 'webhook_desc': 'Přidejte webhooky pro zasílání oznámení.', + 'webhook_url_label': 'URL adresa webhooku', + 'webhook_payload_label': 'Vlastní JSON Payload (volitelné)', + 'webhook_payload_placeholder': 'Např.: {"content": "Účet: ${account}, Data: ${total_gb} GB"}', + 'webhook_add_btn': 'Přidat Webhook', + 'webhook_current': 'Aktuální Webhooky', + 'webhook_delete_btn': 'Smazat', + 'webhook_flash_added': 'Webhook úspěšně přidán.', + 'webhook_flash_deleted': 'Webhook úspěšně smazán.', + 'webhook_flash_invalid_url': 'Je vyžadována platná URL adresa webhooku.', + 'webhook_flash_exists': 'Tato URL adresa webhooku je již zaregistrována.', + 'accounts_username': 'Uživatelské jméno', + 'accounts_nickname': 'Přezdívka', + 'accounts_status': 'Stav', + 'accounts_actions': 'Akce', + 'accounts_active': 'Aktivní', + 'accounts_inactive': 'Neaktivní', + 'accounts_toggle': 'Přepnout', + 'accounts_remove': 'Odebrat', + 'accounts_add_title': 'Přidat nový účet', + 'accounts_add_username': 'UrNetwork uživatelské jméno', + 'accounts_add_password': 'UrNetwork heslo', + 'accounts_add_nickname': 'Přezdívka (volitelné)', + 'combined_stats': 'Kombinovaná statistika', + 'individual_accounts': 'Jednotlivé účty', + 'view_combined': 'Zobrazit kombinovanou statistiku', + 'view_individual': 'Zobrazit jednotlivé účty', + 'map_title': 'Lokace poskytovatelů', + 'map_legend_title': 'Počet poskytovatelů', + 'map_legend_hover': 'Přejeďte myší přes zemi', + 'leaderboard_title': 'Žebříček', + 'leaderboard_rank': 'Pořadí', + 'leaderboard_name': 'Jméno sítě', + 'leaderboard_data': 'Poskytnutá data (MiB)', + 'devices_title': 'Stav a správa zařízení', + 'devices_status': 'Stav', + 'devices_name': 'Jméno zařízení', + 'devices_id': 'ID klienta', + 'devices_mode': 'Režim poskytování', + 'devices_account': 'Účet', + 'devices_remove': 'Odebrat', + 'devices_online': 'Online', + 'devices_offline': 'Offline', + 'devices_confirm_remove': 'Opravdu chcete odebrat toto zařízení?', + 'flash_device_removed': 'Zařízení úspěšně odebráno.', + 'flash_device_remove_fail': 'Nepodařilo se odebrat zařízení: ' + }, + 'en': { + 'nav_public': 'Public View', + 'nav_owner_dashboard': 'Owner Dashboard', + 'nav_overview': 'Overview', + 'nav_account': 'Account & Leaderboard', + 'nav_devices': 'Devices', + 'nav_settings': 'Settings', + 'nav_accounts': 'Account Management', + 'nav_login': 'Login', + 'nav_logout': 'Logout', + 'title_public_dashboard': 'Public Dashboard', + 'title_owner_dashboard': 'Owner Dashboard', + 'title_account_page': 'Account & Leaderboard', + 'title_devices_page': 'Device Management', + 'title_settings': 'Settings', + 'title_accounts_page': 'Account Management', + 'title_login': 'Login', + 'title_setup': 'Initial Setup', + 'card_paid_data': 'Total Paid Data', + 'card_unpaid_data': 'Total Unpaid Data', + 'card_earnings_30d': 'Earnings (30 Days)', + 'card_total_earnings': 'Total Earnings', + 'card_last_update': 'Last Update', + 'card_account_points': 'Account Points', + 'card_your_rank': 'Your Rank', + 'card_total_referrals': 'Total Referrals', + 'card_active_accounts': 'Active Accounts', + 'card_total_devices': 'Total Devices', + 'chart_total_data_gb': 'Total Data Provided (GB)', + 'chart_paid_vs_unpaid': 'Paid vs. Unpaid Data (GB)', + 'chart_delta': 'Data Change per Interval (GB)', + 'chart_paid': 'Paid (GB)', + 'chart_unpaid': 'Unpaid (GB)', + 'chart_no_data': 'Not enough data to display a chart.', + 'history_title': 'Detailed History', + 'history_timestamp': 'Timestamp', + 'history_change': 'Change (GB)', + 'btn_fetch_now': 'Fetch Now', + 'btn_clear_data': 'Clear All Data', + 'btn_add_account': 'Add Account', + 'confirm_clear_data': 'Are you sure? This will delete all historical stats data.', + 'next_fetch': 'Next automatic fetch:', + 'login_header': 'Sign in to your dashboard', + 'login_username': 'Admin Password', + 'login_password': 'Password', + 'login_button': 'Sign in', + 'error_login_required': 'You must be logged in to view this page.', + 'error_invalid_credentials': 'Invalid password.', + 'flash_logged_in': 'You have been logged in successfully.', + 'flash_logged_out': 'You have been logged out.', + 'flash_settings_saved': 'Settings saved successfully.', + 'flash_fetch_success': 'Successfully fetched and saved latest stats.', + 'flash_fetch_fail': 'Failed to fetch stats: ', + 'flash_clear_success': 'Successfully cleared {count} records from the database.', + 'flash_clear_fail': 'An error occurred while clearing the database.', + 'flash_account_added': 'Account added successfully.', + 'flash_account_removed': 'Account removed successfully.', + 'flash_account_updated': 'Account updated successfully.', + 'webhook_title': 'Webhook Management', + 'webhook_desc': 'Add webhooks to send notifications.', + 'webhook_url_label': 'Webhook URL', + 'webhook_payload_label': 'Custom JSON Payload (optional)', + 'webhook_payload_placeholder': 'E.g.: {"content": "Account: ${account}, Data: ${total_gb} GB"}', + 'webhook_add_btn': 'Add Webhook', + 'webhook_current': 'Current Webhooks', + 'webhook_delete_btn': 'Delete', + 'webhook_flash_added': 'Webhook added successfully.', + 'webhook_flash_deleted': 'Webhook deleted successfully.', + 'webhook_flash_invalid_url': 'A valid Webhook URL is required.', + 'webhook_flash_exists': 'This webhook URL is already registered.', + 'accounts_username': 'Username', + 'accounts_nickname': 'Nickname', + 'accounts_status': 'Status', + 'accounts_actions': 'Actions', + 'accounts_active': 'Active', + 'accounts_inactive': 'Inactive', + 'accounts_toggle': 'Toggle', + 'accounts_remove': 'Remove', + 'accounts_add_title': 'Add New Account', + 'accounts_add_username': 'UrNetwork Username', + 'accounts_add_password': 'UrNetwork Password', + 'accounts_add_nickname': 'Nickname (optional)', + 'combined_stats': 'Combined Statistics', + 'individual_accounts': 'Individual Accounts', + 'view_combined': 'View Combined Stats', + 'view_individual': 'View Individual Accounts', + 'map_title': 'Provider Locations', + 'map_legend_title': 'Provider Count', + 'map_legend_hover': 'Hover over a country', + 'leaderboard_title': 'Leaderboard', + 'leaderboard_rank': 'Rank', + 'leaderboard_name': 'Network Name', + 'leaderboard_data': 'Data Provided (MiB)', + 'devices_title': 'Device Status & Management', + 'devices_status': 'Status', + 'devices_name': 'Device Name', + 'devices_id': 'Client ID', + 'devices_mode': 'Provide Mode', + 'devices_account': 'Account', + 'devices_remove': 'Remove', + 'devices_online': 'Online', + 'devices_offline': 'Offline', + 'devices_confirm_remove': 'Are you sure you want to remove this device?', + 'flash_device_removed': 'Device removed successfully.', + 'flash_device_remove_fail': 'Failed to remove device: ' + } +} + +def get_locale(): + """Detects the best language match from request headers.""" + return request.accept_languages.best_match(TEXTS.keys()) or 'en' + +# --- HTML Templates --- + +LAYOUT_TEMPLATE = """ + + +
+ + +Nastavte administrátorské heslo pro váš dashboard.
+ +Poté budete moci přidat své UrNetwork účty v nastavení.
+{{ t.chart_no_data }}
{% endif %} +{{ t.chart_no_data }}
{% endif %} +| {{ t.accounts_nickname }} | +{{ t.accounts_username }} | +{{ t.accounts_status }} | +{{ t.accounts_actions }} | +
|---|---|---|---|
| {{ account.nickname or account.username }} | +{{ account.username }} | ++ + {{ t.accounts_active if account.is_active else t.accounts_inactive }} + + | ++ + + | +
| Zatím nemáte přidané žádné účty. | +|||
{{ t.webhook_desc }}
+ +{{ webhook.url }}
+{{ webhook.payload or 'Default Discord Payload' }}
+
+ Zatím nemáte nakonfigurované žádné webhooky.
+ {% endfor %} +Nastavte administrátorské heslo pro váš dashboard.
+ +Poté budete moci přidat své UrNetwork účty v nastavení.
+{{ t.chart_no_data }}
{% endif %} +{{ t.chart_no_data }}
{% endif %} +| {{ t.accounts_nickname }} | +{{ t.accounts_username }} | +{{ t.accounts_status }} | +{{ t.accounts_actions }} | +
|---|---|---|---|
| {{ account.nickname or account.username }} | +{{ account.username }} | ++ + {{ t.accounts_active if account.is_active else t.accounts_inactive }} + + | ++ + + | +
| Zatím nemáte přidané žádné účty. | +|||
{{ t.webhook_desc }}
+ +{{ webhook.url }}
+{{ webhook.payload or 'Default Discord Payload' }}
+
+ Zatím nemáte nakonfigurované žádné webhooky.
+ {% endfor %} +