37 Commits

Author SHA1 Message Date
leonidorlov-hash 900a4de5db style(ui): wider connections modal, tighter server-list buttons, slim speed badge
- #userConnsModal .modal max-width 960px (was inline 600px)
- .page--servers .btn-sm padding 6px 12px
- dropped fixed min-width on .speed-badge so it hugs its content
2026-09-13 20:15:49 +03:00
leonidorlov-hash 3c4e1d9c37 Fix peer name source (userData.clientName); move highlight border to CSS class 2026-09-13 20:15:13 +03:00
leonidorlov-hash 110b3e4882 feat(users): unlink-without-delete action + deep links to peers
- POST /api/users/{id}/connections/unlink: detaches a key from the user
  without deleting the peer on the server (admin-only, 404 if not found)
- users page: scissors button per linked connection, server/instance
  names open the server page in a new tab and highlight the peer row
- server page: ?protocol=&highlight= deep link handling, peer row
  gets .peer-highlight CSS class and scrolls into view
- translations for all 5 locales
2026-09-13 19:54:56 +03:00
Public relations & Virtual technology 2c28b886e4 Merge branch 'main' into feat/ui-polish 2026-09-09 12:16:54 +03:00
Public relations & Virtual technology 02ca2ac8fd Merge branch 'main' into feat/local-vendor 2026-09-09 12:15:28 +03:00
Public relations & Virtual technology 7b3dcaf822 Merge pull request #149 from helldweller/feat/exit-nodes
feat(exit): route client traffic through another panel server (exit nodes)
2026-09-09 11:34:24 +03:00
leonidorlov-hash 0eb8713f8d Serve CodeMirror and ReDoc from /static/vendor instead of public CDNs 2026-09-08 22:42:17 +03:00
leonidorlov-hash f8e4b95202 traffic-limit class on the speed badge (both limited and unlimited) 2026-09-08 22:41:39 +03:00
leonidorlov-hash 5fa0da6b67 Match button spinner accent color to the save-spinner (#1c1c30) 2026-09-08 22:13:20 +03:00
leonidorlov-hash af88df3bb9 Bulb peer toggle with in-flight spinner + 180-day static cache
- Replace the blue/grey circle toggle with a bulb emoji; the off state
  is a greyed-out bulb styled in style.css (.peer-toggle-off).
- While the toggle request runs, the bulb becomes a spinner and the
  whole client row is dimmed with all its buttons disabled; on error
  the row is restored.
- Serve /static with Cache-Control: max-age=15552000, immutable. URLs
  already carry ?v=<static mtime>, so a redeploy busts the cache.
2026-09-08 21:50:49 +03:00
leonidorlov-hash 49b06970bd fix(css): keep spinners rotating under prefers-reduced-motion
The global reduced-motion rule froze every animation, including the
busy indicators, so a running operation looked frozen. Spinners signal
progress, not decoration - exempt them
2026-09-08 20:58:42 +03:00
leonidorlov-hash bcff303120 style: drop the 140px cap on .client-actions
Action buttons may wrap freely instead of being squeezed into a fixed
narrow column
2026-09-08 20:54:00 +03:00
hell 69893ba340 feat(exit): route client traffic through another panel server
Today a client leaves to the internet from the same server it connected to.
Exit nodes split the two: clients connect to an *entry* node (reachable,
possibly behind DPI) and egress from an *exit* node (clean IP, other country).
One exit serves many entries and keeps serving its own AWG clients.

    client --AWG--> [entry] amnezia-awg2 --WG/AWG link--> [exit] amnezia-exit --> internet
                    awg0 10.8.1.0/24                     transit0 10.9.0.0/24
                    exit0 10.9.0.N (Table=off)           one [Peer] per entry, /32

Exit side: a new protocol `exit` = container `amnezia-exit`, an AmneziaWG
listener on 55520/udp over the transit subnet 10.9.0.0/24. Peers are panel
nodes, never end users, and live in their own peersTable. ExitManager
subclasses AWGManager and closes every client-facing method.

Entry side: a second interface `exit0` inside the existing AWG container with
`Table = off`, plus policy routing - `from <client subnet> lookup 200`,
`default dev exit0` in table 200, and `blackhole default metric 1000` as a
kill-switch installed *before* the client tunnel comes up, so a container
restart cannot leak. Clients are MASQUERADEd into the entry's transit /32, so
the identical 10.8.1.0/24 of every instance can never collide at the exit.
TCP MSS is clamped, and rules 190/191 keep client<->client and AmneziaDNS
traffic local. State is file-driven: exit0.conf present means linked, and the
same shell block runs at container start and on a live (un)link.

Link crypto is WireGuard; AWG obfuscation is a per-exit-node property for hops
that cross DPI, and S3/S4 are deliberately not written on the transit - they
pad every transport packet and would eat the MTU budget of a tunnel inside a
tunnel. Private keys are generated inside the containers; the panel carries
public keys and a per-peer PSK only.

exit_link_service.py orchestrates linking with per-(entry, protocol) locks,
SSH off the event loop, a 15-second handshake gate that rolls back, and
lifecycle hooks for every way a link can go stale: exit uninstalled, stopped,
deleted, host cleared, entry reinstalled, container gone, backup restored.
Servers get a stable `uid` (already upstream) so reorder and delete cannot
break a link.

IPv4 only for now: while linked, client IPv6 is refused with `unreachable`
rather than leaking out of the entry.

Also included: an egress check that curls a public IP service bound to the
tunnel gateway (the client's exact path) and compares it with the direct one;
optional DNS resolution at the exit node; a default exit node applied to new
AWG installs; and reconciliation after a protocol restore.

244 tests pass. CURRENT_VERSION is untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 23:05:51 +07:00
hell ed08fc0f70 fix: two more v1.6.4 defects found in review
tests/test_server_template_integrity.py calls `node --check` through
subprocess.run() unconditionally. Without node on PATH that raises
FileNotFoundError and fails the suite rather than skipping - verified by
running it with PATH=/usr/bin:/bin. It now carries the same
`skipUnless(shutil.which('node'))` guard the other template-JS check uses.

static/sw.js precached three URLs nobody requests. Both fetch branches match
on the exact URL, and the templates ask for /static/favicon.svg,
/static/js/qrcode.min.js and /static/js/searchable-select.js with no query -
so the `?v=${VERSION}` entries never served a request, and the favicon ended
up stored twice (cache-first misses the versioned entry, fetches the plain
URL, caches that too). style.css keeps its query: the templates request it as
?v={{ static_v }} and the worker is registered with the same value, so that
one does match. Freshness is unaffected either way, since CACHE is named after
VERSION and a new version starts from an empty cache.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 22:23:09 +07:00
Public relations & Virtual technology e70c822503 Merge branch 'main' into feat/protocol-logos 2026-09-08 12:56:28 +03:00
leonidorlov-hash 266f23d743 feat(ui): protocol logos and custom instance names
- WireGuard, AmneziaWG (all variants) and Xray cards show real logos
  (bundled in static/img) instead of emoji, on brand-colored tiles:
  WireGuard #871719, AmneziaWG #303030, Xray #010001
- installed instances can be renamed via a small pencil inside the
  instance name (fades in on hover); the custom name is stored per
  protocol in the server record (custom_name) and shown on the card
  title and in all instance pickers; empty input restores the default
2026-09-05 17:23:59 +03:00
Viktor Baranov 30156c5829 feat(ui): design system and mobile layout overhaul 2026-09-05 16:55:33 +03:00
Viktor Baranov 3770870e32 feat: add ability to restore server backups 2026-09-04 13:39:56 +03:00
hell b34db02ff7 fix(awg3): fall back to userspace on a pre-3.1 kernel module
awg-quick prefers the host kernel module and only falls back to
amneziawg-go when `ip link add ... type amneziawg` fails, i.e. when no
module is installed at all. On a host carrying an older module (the 1.0.x
line the Amnezia PPA still ships) the interface is created, `awg setconf`
then rejects the AWG 3.1 keys with a bare "Invalid argument", and
awg-quick deletes the interface again. The install reported success and
no tunnel existed (issue #113).

The container's amneziawg-go does speak the full 3.1 key set, so awg3 can
run in userspace on such hosts:

- patch awg-quick in the built image with a WG_FORCE_USERSPACE switch;
  without the variable set add_if behaves exactly as before, and a
  grep guard fails the build if upstream changes the line
- probe the module from the awg3 start script (creating a throwaway
  interface also loads a module that is on disk but not loaded yet) and
  force userspace when its major version is below 3; the decision is
  taken on every container start, so a later module upgrade returns the
  tunnel to the kernel datapath on its own
- report the downgrade in the install log, and mark such lines as
  warnings in the UI instead of green ticks
- verify the interface is actually up after the start script, so a
  rejected config surfaces as an error with the container log instead of
  a successful install

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 22:16:18 +07:00
Public relations & Virtual technology 9bf44a75c3 Merge branch 'main' into feat/awg-special-junk-mtu-dns 2026-09-01 16:20:05 +03:00
Public relations & Virtual technology 317e5d7ad6 Merge pull request #105 from Almeonamy/feat/searchable-user-select
Add substring search to the assign-to-user picker
2026-09-01 16:06:38 +03:00
Public relations & Virtual technology 700751307e Merge pull request #100 from leonidorlov-hash/feat/conn-flood-warnings
feat(awg): per-peer connection flood warnings (P2P/torrent detection)
2026-09-01 15:43:56 +03:00
hell 8ae4fcd1e7 Give the QR a quiet zone, and rework the protocol card header
The QR never scanned, before any of this branch's changes: qrcode.js paints
the modules edge to edge with no quiet zone, and against the dark page the
finder patterns sit directly on the background. Simulating a 640x480 capture,
nothing decodes that way -- not the frame series, not the raw config, at any
size. With a 32px white frame around the code all eight frames decode at every
scale tried. So the earlier density work was necessary but not sufficient.

Rounding is dropped from the code itself as well, since a border-radius large
enough to see clips the corner finder patterns.

The card header now carries the protocol name next to its icon, and the
control buttons get a row of their own underneath, where three of them fit
without crowding anything.

Button labels rode high whenever the label opened with a symbol glyph: U+23F9
in the stop button falls back to a font with much taller metrics, which grows
the line box downwards. A fixed line-height keeps the label centred.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 03:01:41 +07:00
hell af87995c87 Fix the protocol card header and make the QR scannable
Two problems with the previous commit's UI.

The header row of a protocol card is a bare space-between flex with no gap,
so the third control button ran straight into the protocol icon on the AWG
cards -- the only ones that get an AWG button. Give the row a gap and let the
button group wrap instead of pushing into the icon.

And the QR: a whole config in one code lands at version 19 raw or 26 wrapped,
while the Android scanner builds ImageAnalysis.Builder() with no
ResolutionSelector and so reads 640x480 frames -- two to three pixels per
module. Emit the frame series ImportController::parseQrCodeChunk reassembles
instead: qint16 magic 1984, quint8 total, quint8 index and the slice as a
QDataStream QByteArray, each frame base64url'd into its own code. At 144 bytes
a frame every code stays under 55x55 modules and decoded at every scale tried
against a simulated VGA capture, including ones where the single code failed.

The WireGuard format stays a single static code, since a raw config is what
AmneziaWG and wg-quick read and it cannot be chunked.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 02:37:45 +07:00
hell 1fbcba5c48 Name the server in the vpn:// key and QR code
A plain WireGuard config cannot carry a name: extractWireGuardConfig() in
the client overwrites description with nextAvailableServerName(), so every
imported key lands as "Server 1". Wrap the config into Amnezia's own server
config format instead, which keeps whatever description it is given, and
name it after the node and the container it came from -- "prx-40 AWG3".

The encoding matches ExportController::generateVpnUrl: base64url of
qCompress'd JSON, the size prefix included. The QR payload carries no
vpn:// prefix, because extractConfigFromQr() feeds the scanned text
straight to QByteArray::fromBase64, which drops ':' and '/' but keeps
'v', 'p' and 'n' and would shift the whole payload by three characters.

AmneziaWG and wg-quick only understand the raw config, so the QR panel
gets a format switch rather than replacing what it shows. The named
payload is about half again as long -- QR version 23 instead of 18 --
so it is drawn a little larger.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 02:09:31 +07:00
Алексей Цыганков 0c1ba5ed82 Set dir="auto" on the trigger, search box and options
Usernames and emails are latin even when the panel runs in Persian (the
only locale that sets dir="rtl"). Inheriting the RTL direction pushes such
a string to the right edge and reorders its trailing punctuation, so
"a.zharkova@example.com (user)" renders with the parens and dots in the
wrong place. dir="auto" lets the browser pick per string: latin entries
lay out LTR, Persian ones stay RTL.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 18:40:35 +07:00
Алексей Цыганков 133dc2bff6 Add substring search to the "assign to user" picker
On a panel with a few dozen users, picking one when creating a connection
means scrolling a plain <select> and reading every entry — there is no way
to type a name.

Adds a small dependency-free searchable select and applies it to the user
picker in the Add Connection modal. It's progressive enhancement: the
native <select> stays in the DOM as the source of truth, so `.value`,
programmatic assignment and `change` listeners keep working exactly as
before (server.html reads `connectionUserId.value` unchanged).

- static/js/searchable-select.js: opt-in via `data-searchable`, with
  case-insensitive substring filtering, keyboard navigation
  (arrows/Enter/Escape/Tab), an empty state, and ARIA combobox roles
- styles built entirely from existing CSS variables, so light/dark themes
  and RTL follow the rest of the panel
- labels come from `data-search-*` attributes, so strings stay in the
  templates; new keys added to all five locales

No jQuery/select2: the panel ships no jQuery today, and pulling in two
libraries for one dropdown seemed a poor trade against ~300 lines of
vanilla JS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 18:20:15 +07:00
leonidorlov-hash e981c28a4a refactor: drop client-ip part (shipped as a separate PR) 2026-08-28 00:11:37 +03:00
leonidorlov-hash d6c6d50ffe feat(awg): per-peer connection flood warnings (torrent detection)
- Count conntrack entries per peer IP from /proc/net/nf_conntrack (no extra
  packages on the host), matching against the instance subnet
- Record a warning when a peer exceeds 600 simultaneous connections,
  at most one per hour per peer, keeping the last 5 events; persisted in
  conn_warnings.json inside the container data dir (volume-backed)
- Render warnings under the client meta row (.conn-warnings/.conn-warning,
  no inline styles), localized via new conn_warning_text key (en/ru/fr/fa/zh)
- Show peer internal IP next to the client name (.client-ip)
2026-08-28 00:10:01 +03:00
leonidorlov-hash 964b6b778e feat(ui): show peer internal IP next to the client name
Uses clientIp/allowedIps already present in the API response. Also
fixes the rename editor picking up the IP text together with the name
(saving produced 'Name 10.x.x.x 10.x.x.x').
2026-08-28 00:08:36 +03:00
hell bbe2a6e95b Add MTU, DNS and I1-I5 fields to the UI
The install dialog gains an AmneziaWG section, and installed AWG
protocols gain an "AWG" button next to the raw config editor that opens
the same fields for editing. A "Default" button fills I1 with the packet
the desktop client uses, so the common case is one click.

AWG Legacy predates special junk packets, so it only gets MTU and DNS.

The dialog says outright that changes apply to configs generated from
now on: a client that already imported a config keeps its old values,
because the values live in the config it holds.

fr/zh/fa strings are machine-written and want a native reader.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 14:03:48 +07:00
leonidorlov-hash 0e6ceb0751 feat(ui): live peer speed badge, compact fixed-width handshake, online pulse (wg-easy style) 2026-08-21 20:50:57 +03:00
Public relations & Virtual technology dd8bda3ac1 Version 1.5.0
Added Cloudflare WARP support
Added Market Templates
Added Multi-protocol support
Fixed SOCKS5 management
Added NGINX + Let's Encrypt integration
Updated built-in configuration editor
Added Backup / Migrate protocols (Alpha)
Added Admin Telegram bot role
2026-07-09 17:53:38 +03:00
Public relations & Virtual technology 29def95df8 v1.4.3
Add SOCKS5 support
Add AdGuard Home integration
Add node status and latency tracking
Add drag-and-drop server management
Upgrade Xray from v1.8 to v26.3.27 with backward compatibility
Fix Telemt dependency installation
Extend Telegram bot to support all protocols
Improve server cleanup process
Enhance API structure (system routes moved to a separate group)
Add JWT token generation for external integrations
2026-05-04 16:01:06 +03:00
Public relations & Virtual technology 1fd100e4e9 Update v1.4.0
1) Added WireGuard support
2) Xray improved link generation after config updates
3) Added: version display, new version check, download
2026-04-15 17:32:35 +03:00
Public relations & Virtual technology 5b40445442 v1.3.0
* Added support for AmneziaDNS
* Added traffic reset functionality
* Fixed hideConnectionsSection is not defined error and false “Connection Error” when clearing the server
* Added basic management features (restart, delete, poll, clear)
* Fixed AWG 2.0 connection generation
* Optimized Check Server polling speed (now queries all protocols and services in parallel over a single SSH connection; ~5–6x faster)
2026-04-02 13:43:59 +03:00
Public relations & Virtual technology 814feeda06 Simple Enjoy 2026-03-16 15:39:35 +03:00