Skip to content

🛡️ VPN Service Platform — Telegram Bot + Web Admin Panel - #3

Open
devin-ai-integration[bot] wants to merge 3 commits into
circleci-project-setupfrom
devin/1779193240-vpnbot-platform
Open

🛡️ VPN Service Platform — Telegram Bot + Web Admin Panel#3
devin-ai-integration[bot] wants to merge 3 commits into
circleci-project-setupfrom
devin/1779193240-vpnbot-platform

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented May 19, 2026

Copy link
Copy Markdown

Summary

Adds a complete VPN service platform built in Python:

  • Telegram bot (aiogram 3.7.0): user registration, subscription purchase (119 ₽/mo), VLESS key delivery, balance top-up, tutorials, admin commands (stats, broadcast, server management)
  • Web admin panel (aiohttp + Jinja2): production-grade dark UI for managing VPN servers, viewing users/subscriptions, adding servers by IP/user/pass with auto country detection and Xray auto-install
  • Xray VLESS management (services/xray_manager.py): SSH into remote servers via sshpass or SSH key, install Xray, generate x25519 keys, deploy VLESS+Reality configs with stats API auto-enabled, set up iptables relay for whitelist servers
  • Payment webhooks: CryptoBot (crypto) and Lolz.live (RUB) with balance crediting and user notification
  • Subscription endpoint (/sub/{token}): serves base64-encoded VLESS links with Subscription-Userinfo header for VPN client apps (profile title: GMBOT)
  • SQLite database: users, servers (with SSH creds, SSH keys + Xray keys), subscriptions, payments, settings, traffic tracking
  • Deployment: deploy.sh installs deps, creates virtualenv, sets up systemd service at /opt/vpnbot/

Updates since last revision

  • Relay display names with country flags — Relay servers now show WHITELIST 🇷🇺 → 🇩🇪 (flag of relay country → flag of target VPN server) instead of the old WHITELIST Россия, Москва text. Updated in both web/app.py (admin panel) and handlers/admin.py (Telegram bot). Uses emoji.flag() to convert country codes to flag emoji.

  • Renamed subscription Profile-Title to GMBOTservices/subscription.py now returns Profile-Title: GMBOT instead of Premium VPN in the Subscription-Userinfo response headers. This controls what VPN client apps display as the profile name.

  • Relay setup supports non-root SSH userssetup_relay() in xray_manager.py now prefixes all iptables, sysctl, and apt-get commands with sudo. This fixes relay setup on Yandex Cloud VMs and similar providers where SSH login is a non-root user (e.g., user) with passwordless sudo.

Previous updates still included

  • Fixed traffic counting end-to-end — Rewrote query_xray_stats() to use json.loads() instead of fragile regex parsing. Added -reset flag so Xray counters reset after each read (incremental collection). Filters stats to only vless-in inbound and user>>> entries, ignoring internal API traffic. Removed dead code in the old parsing loop. Tested on production — Germany and Poland both return real traffic data.

  • Database backup every 3 hours — New send_db_backup() job in scheduler.py. Copies SQLite database and sends it as a Telegram document to all admin users with timestamp caption. Runs via APScheduler alongside existing jobs.

  • Geolocation API reordered — Switched primary API from 2ip.ua to ip-api.com (with lang=ru). The 2ip.ua API returned Singapore for a Polish IP; ip-api.com is more reliable. Fallback chain is now: ip-api.com2ip.uaipwho.is.

  • Improved sync_traffic aggregation — Traffic from all servers is now aggregated first, then distributed evenly to active users (instead of distributing per-server). Added detailed logging: each server's contribution is logged individually, plus the final distribution.

  • SSH key storage in database — Added ssh_key TEXT DEFAULT '' column to servers table. add_server() now accepts and stores an ssh_key parameter. Both relay and VPN server creation paths in web/app.py pass ssh_key through. sync_traffic() fetches the stored ssh_key from each server record and passes it to query_xray_stats().

  • SSH key file upload on Add Server page — File upload button (.zip, .pem, .key). ZIP archives are extracted client-side via JSZip — automatically finds the first file containing PRIVATE KEY. A hidden textarea still holds the key value for form submission and allows manual paste.

  • Traffic display in Telegram botfmt_bytes() helper formats bytes into human-readable strings. /profile and "Мои ключи" both display total traffic, upload, and download.

  • Removed 100GB traffic limitservices/subscription.py queries real traffic stats from the traffic DB table. The Subscription-Userinfo header reports total=0 (unlimited) with real upload/download byte counts.

  • Xray stats API auto-enabled on new serversbuild_xray_config() includes "stats": {}, StatsService API inbound on 127.0.0.1:10085, per-user stats policy, and routing rules. Any server added through the admin panel automatically gets traffic counting.

  • Fix Xray auto-installation — Installs curl + unzip before downloading, parses both key output formats, creates systemd unit.

  • Real-time installation console on Add Server page — SSE streaming of setup logs with color-coded levels.

  • Admin panel full redesign — Dark theme, Lucide SVG icons, sidebar nav, KPI cards, Chart.js. Sunrise palette for help & legal pages.

  • Branding: GMVPN / GMBOT — All logo references updated across help pages, legal pages, <title> tags, and subscription profile title.

Review & Testing Checklist for Human

  • Traffic attribution is per-server total, not per-usersync_traffic() divides total VPN traffic equally among all active users. If 3 users share a server and one uses 90% of the bandwidth, all 3 get credited with 33% each. Verify this matches intended behavior or if per-UUID tracking is needed (the email field exists in Xray configs but is not currently used for attribution).
  • -reset flag means lost data on failurequery_xray_stats() uses -reset so Xray zeroes counters after each read. If the sync job reads stats but crashes before writing to the DB, that traffic data is permanently lost. There is no retry or WAL mechanism.
  • total=0 in Subscription-Userinfo — Some VPN clients (v2rayNG, Clash) may interpret total=0 as "0 bytes quota" rather than "unlimited". Test with target clients. Consider using a very large value (e.g. 999999999999999).
  • Security: SSH passwords/keys in plaintextservers table stores ssh_password and ssh_key in plain text in SQLite. deduct_balance() has a TOCTOU race. Admin panel has no CSRF protection. All pages served over HTTP. DB backup is sent as an unencrypted file via Telegram.
  • Relay display name edge case — If the relay target server has no country_code set, the display will be WHITELIST 🇷🇺 → with a trailing arrow and no target flag. Verify all target VPN servers have country_code populated.
  • JSZip loaded from CDNadd_server.html loads JSZip from cdnjs.cloudflare.com on first ZIP upload. If CDN is blocked, ZIP extraction will silently fail.
  • _setup_logs memory leak — Setup logs stored in global dict. If client disconnects mid-stream or never connects, logs accumulate indefinitely.
  • No automated tests — Entire platform has zero automated tests.
  • Test plan — Deploy and verify:
    1. Admin panel → Servers → Add Server → enter IP/pass → submit → verify real-time console streams setup logs → verify Xray installs and stats API starts on 127.0.0.1:10085
    2. Add a relay server → verify display name shows WHITELIST 🇷🇺 → 🇩🇪 (appropriate flags)
    3. Upload or paste SSH key for a key-auth-only server with non-root user → verify relay setup completes (sudo commands succeed)
    4. Connect VPN client, generate traffic, wait ~40 seconds → check /sub/{token} header → verify upload/download values are non-zero and profile title is GMBOT
    5. In Telegram bot: /profile → verify traffic stats displayed (Total / Upload / Download)
    6. In Telegram bot: "Мои ключи" → verify traffic stats alongside subscription info
    7. Wait 3 hours (or temporarily reduce interval) → verify admin receives DB backup file in Telegram

Notes

  • Admin sessions stored in-memory; invalidated on restart.
  • Traffic sync runs every 40 seconds via APScheduler. First sync happens 40 seconds after bot startup.
  • DB backup runs every 3 hours via APScheduler. Backup file is copied to /tmp, sent, then deleted.
  • help/style.css and legal/style.css import Inter from fonts.googleapis.com. If blocked in Russia, pages fall back to system fonts.
  • Geo API fallback chain: ip-api.com2ip.uaipwho.is.

Link to Devin session: https://app.devin.ai/sessions/6d47698e17e145bcb8b015918ff3ff4e
Requested by: @promi1

- Telegram bot (aiogram 3.7.0) with subscription management
- Web admin panel with dark theme UI
- Xray VLESS auto-configuration via SSH
- CryptoBot + Lolz.live payment integration
- Whitelist relay support for Russian IPs
- Subscription endpoint with base64-encoded VLESS links
- Country auto-detection via IP geolocation
- Custom Telegram emoji support
- Background scheduler for subscription expiry
- Deployment script with systemd service

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

promi1 and others added 2 commits May 19, 2026 12:29
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Author

Bot E2E Test Results

Tested: May 19, 2026 via Telegram Web
Account: +79951014739 (ID: 7922916137, admin)
Bot: @Bglazbogaoboglzobot

All 20 tests passed

Main Menu Flow (Tests 1-9)
Test Result
/start — welcome message, 7 buttons, no emoji PASS
Мой профиль — ID, balance, VPN status PASS
Купить VPN — price, servers with flags, buttons PASS
Insufficient balance — error message PASS
Пополнить — payment methods, CryptoBot only PASS
CryptoBot not configured — alert popup PASS
Туториалы — 4 tutorials + web link PASS
Tutorial detail (Happ) — full text PASS
Помощь / FAQ — help text PASS
Мои ключи (no sub) — correct message PASS
Admin Flow (Tests 10-16)
Test Result
/admin — stats, panel link, password spoiler, 8 buttons PASS
Статистика — detailed stats PASS
Серверы — server list with flags PASS
Пароль админки (show) — password in tg-spoiler PASS
Password change — FSM works, message deleted PASS
Verify on web panel — login with new password PASS
Restore password — FSM works multiple times PASS
Пользователи — 2 users listed PASS
Подписки — 0 active (correct) PASS
Web Pages & Navigation (Tests 17-20)
Test Result
Правила сервиса link → telegra.ph PASS
/help/ tutorials page PASS
/legal/terms.html PASS
Назад navigation throughout PASS

Emoji compliance

All button labels verified emoji-free. Country flags (🇵🇱) in server names correctly preserved.

No bugs found

Devin session

@devin-ai-integration

Copy link
Copy Markdown
Author

Test Report: SSH Key Storage, Traffic Sync & Display

Date: May 19, 2026 | Environment: Production 2.27.12.148:8080 | Bot: @Bglazbogaoboglzobot

Results: 8/10 passed, 2 untested

Test Result
Admin Dashboard — dark theme, KPI cards PASSED
Servers page — DE flag, Frankfurt, Xray OK PASSED
Add Server — SSH key upload button (.zip/.pem) PASSED
Traffic sync — runs every 40s, no errors PASSED
Xray Stats API — responds on 127.0.0.1:10085 PASSED
/profile — traffic display UNTESTED (test account has no subscription)
"Мои ключи" — traffic display UNTESTED (test account has no subscription)
Help pages — sunrise palette, GMVPN PASSED
Legal/Terms — sunrise palette PASSED
Subscription-Userinfo header — upload/download/total PASSED

Escalations

  1. Traffic display in bot not visually verified — Test account (7922916137) has no subscription, so traffic section doesn't render. User (1006207917) can verify by sending /profile in bot.
  2. No real VPN traffic flowing — Stats API returns 0 bytes because no client is connected. Sync works without errors.
  3. total=0 in Subscription-Userinfo — Some clients may interpret as "0 quota" instead of "unlimited". Consider using 999999999999999.
Shell evidence

Traffic sync logs (no errors):

May 19 20:25:16 - sync_traffic executed successfully
May 19 20:25:56 - sync_traffic executed successfully  
May 19 20:26:36 - sync_traffic executed successfully

Subscription endpoint headers:

HTTP/1.1 200 OK
Subscription-Userinfo: upload=0; download=0; total=0; expire=1781797008

Xray stats API response:

{"stat": [{"name": "inbound>>>vless-in>>>traffic>>>downlink"}, {"name": "inbound>>>api>>>traffic>>>uplink", "value": 431}]}

Devin session

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant