Browse Source

* Possible fix for peers not updating subnet. This should finish up #11

* Initial Support for IPv6. #20
pull/29/head
Per-Arne Andersen 5 years ago
parent
commit
1eae153572
  1. 12
      README.md
  2. 24
      docker-compose.yaml
  3. 1
      docs/guides/docker_configuration.md
  4. 7
      wg_dashboard_backend/const.py
  5. 2
      wg_dashboard_backend/migrations/versions/004_create_server_subnet.py
  6. 32
      wg_dashboard_backend/migrations/versions/005_create_v6_address.py
  7. 21
      wg_dashboard_backend/migrations/versions/006_create_v6_subnet.py
  8. 3
      wg_dashboard_backend/models.py
  9. 1
      wg_dashboard_backend/requirements.txt
  10. 35
      wg_dashboard_backend/routers/v1/peer.py
  11. 30
      wg_dashboard_backend/routers/v1/server.py
  12. 3
      wg_dashboard_backend/schemas.py
  13. 13
      wg_dashboard_backend/script/wireguard.py
  14. 2
      wg_dashboard_backend/templates/peer.j2
  15. 8
      wg_dashboard_backend/templates/server.j2
  16. 3544
      wg_dashboard_frontend/package-lock.json
  17. 61
      wg_dashboard_frontend/src/app/page/dashboard/add-server/add-server.component.html
  18. 51
      wg_dashboard_frontend/src/app/page/dashboard/add-server/add-server.component.ts
  19. 2
      wg_dashboard_frontend/src/app/page/dashboard/dashboard.module.ts

12
README.md

@ -5,9 +5,11 @@ The wg-manager provides an easy-to-use graphical web interface to import, setup,
The features of wg-manager includes: The features of wg-manager includes:
**Server** **Server**
* IPv4 **and** IPv6 support
* Create/Delete/Modify * Create/Delete/Modify
* Start/Stop/Restart server * Start/Stop/Restart server
* Import existing * Import existing configurations
* Export server config, along with client config as zip.
**Peer** **Peer**
* Create/Delete/Modify * Create/Delete/Modify
@ -40,10 +42,14 @@ The features of wg-manager includes:
## Method #1: Docker-compose ## Method #1: Docker-compose
```yaml ```yaml
version: "2.1"
services:
wireguard: wireguard:
container_name: wg-manager container_name: wg-manager
image: perara/wg-manager image: perara/wg-manager
restart: always restart: always
sysctls:
net.ipv6.conf.all.disable_ipv6: 0 # Required for IPV6
cap_add: cap_add:
- NET_ADMIN - NET_ADMIN
#network_mode: host # Alternatively #network_mode: host # Alternatively
@ -91,6 +97,10 @@ When docker container/server has started, go to http://localhost:8888
| LOG_LEVEL | Logging level of gunicorn/python | info | | LOG_LEVEL | Logging level of gunicorn/python | info |
| ADMIN_USERNAME | Default admin username on database creation | admin | | ADMIN_USERNAME | Default admin username on database creation | admin |
| ADMIN_PASSWORD | Default admin password on database creation | admin | | ADMIN_PASSWORD | Default admin password on database creation | admin |
| POST_UP | The POST_UP Command (version 4) | default |
| POST_DOWN | The POST_DOWN Command (version 4) | default |
| POST_UP_V6 | The POST_UP Command (version 6) | default |
| POST_DOWN_V6 | The POST_DOWN Command (version 6) | default |
# Showcase # Showcase
![Illustration](docs/images/0.png) ![Illustration](docs/images/0.png)

24
docker-compose.yaml

@ -0,0 +1,24 @@
version: "2.1"
services:
server:
container_name: wg-manager
build: .
restart: always
sysctls:
net.ipv6.conf.all.disable_ipv6: 0
cap_add:
- NET_ADMIN
#network_mode: host # Alternatively
ports:
- 11820:11820/udp
- 51800-51900:51800-51900/udp
- 8888:8888
volumes:
- ./wg-manager:/config
environment:
HOST: 0.0.0.0
PORT: 8888
ADMIN_PASSWORD: admin
ADMIN_USERNAME: admin
WEB_CONCURRENCY: 2

1
docs/guides/docker_configuration.md

@ -1,6 +1,7 @@
# Docker Configuration # Docker Configuration
```bash ```bash
docker run -d \ docker run -d \
--sysctl net.ipv6.conf.all.disable_ipv6=0 \
--cap-add NET_ADMIN \ --cap-add NET_ADMIN \
--name wg-manager \ --name wg-manager \
#--net host \ #--net host \

7
wg_dashboard_backend/const.py

@ -6,9 +6,10 @@ DATABASE_FILE = "/config/database.db" if IS_DOCKER else "./database.db"
DATABASE_URL = f"sqlite:///{DATABASE_FILE}" DATABASE_URL = f"sqlite:///{DATABASE_FILE}"
os.makedirs("build", exist_ok=True) os.makedirs("build", exist_ok=True)
DEFAULT_POST_UP = os.getenv("POST_UP", "iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;")
DEFAULT_POST_UP = os.getenv("POST_UP", "iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE") DEFAULT_POST_DOWN = os.getenv("POST_DOWN", "iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE;")
DEFAULT_POST_DOWN = os.getenv("POST_DOWN", "iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE") DEFAULT_POST_UP_v6 = os.getenv("POST_UP_V6", "ip6tables -A FORWARD -i %i -j ACCEPT; ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;")
DEFAULT_POST_DOWN_v6 = os.getenv("POST_DOWN_V6", "ip6tables -D FORWARD -i %i -j ACCEPT; ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE;")
SECRET_KEY = ''.join(random.choices(string.ascii_uppercase + string.digits, k=64)) SECRET_KEY = ''.join(random.choices(string.ascii_uppercase + string.digits, k=64))
ALGORITHM = "HS256" ALGORITHM = "HS256"

2
wg_dashboard_backend/migrations/versions/004_create_server_subnet.py

@ -6,7 +6,7 @@ def upgrade(migrate_engine):
try: try:
meta = MetaData(bind=migrate_engine) meta = MetaData(bind=migrate_engine)
server = Table('server', meta, autoload=True) server = Table('server', meta, autoload=True)
subnet = Column('subnet', Text) subnet = Column('subnet', Integer, nullable=False)
subnet.create(server) subnet.create(server)
except: except:
pass pass

32
wg_dashboard_backend/migrations/versions/005_create_v6_address.py

@ -0,0 +1,32 @@
from sqlalchemy import *
from migrate import *
def upgrade(migrate_engine):
try:
meta = MetaData(bind=migrate_engine)
server = Table('server', meta, autoload=True)
v6_address_server = Column('v6_address', VARCHAR, unique=True, nullable=True)
v6_address_server.create(server)
meta = MetaData(bind=migrate_engine)
peer = Table('peer', meta, autoload=True)
v6_address_peer = Column('v6_address', VARCHAR, nullable=True)
v6_address_peer.create(peer)
except:
pass
def downgrade(migrate_engine):
try:
meta = MetaData(bind=migrate_engine)
server = Table('server', meta, autoload=True)
server.c.v6_address.drop()
meta = MetaData(bind=migrate_engine)
peer = Table('peer', meta, autoload=True)
peer.c.v6_address.drop()
except:
pass

21
wg_dashboard_backend/migrations/versions/006_create_v6_subnet.py

@ -0,0 +1,21 @@
from sqlalchemy import *
from migrate import *
def upgrade(migrate_engine):
try:
meta = MetaData(bind=migrate_engine)
peer = Table('server', meta, autoload=True)
v6_subnet = Column('v6_subnet', INTEGER)
v6_subnet.create(peer)
except:
pass
def downgrade(migrate_engine):
try:
meta = MetaData(bind=migrate_engine)
peer = Table('server', meta, autoload=True)
peer.c.v6_subnet.drop()
except:
pass

3
wg_dashboard_backend/models.py

@ -23,6 +23,8 @@ class WGServer(Base):
interface = Column(sqlalchemy.String, unique=True, index=True) interface = Column(sqlalchemy.String, unique=True, index=True)
subnet = Column(sqlalchemy.Integer, nullable=False) subnet = Column(sqlalchemy.Integer, nullable=False)
address = Column(sqlalchemy.String, unique=True) address = Column(sqlalchemy.String, unique=True)
v6_address = Column(sqlalchemy.String, unique=True)
v6_subnet = Column(sqlalchemy.Integer, nullable=False)
listen_port = Column(sqlalchemy.String, unique=True) listen_port = Column(sqlalchemy.String, unique=True)
private_key = Column(sqlalchemy.String) private_key = Column(sqlalchemy.String)
public_key = Column(sqlalchemy.String) public_key = Column(sqlalchemy.String)
@ -43,6 +45,7 @@ class WGPeer(Base):
id = Column(Integer, primary_key=True, index=True) id = Column(Integer, primary_key=True, index=True)
name = Column(sqlalchemy.String, default="Unnamed") name = Column(sqlalchemy.String, default="Unnamed")
address = Column(sqlalchemy.String) address = Column(sqlalchemy.String)
v6_address = Column(sqlalchemy.String)
public_key = Column(sqlalchemy.String) public_key = Column(sqlalchemy.String)
private_key = Column(sqlalchemy.String) private_key = Column(sqlalchemy.String)
shared_key = Column(sqlalchemy.Text) shared_key = Column(sqlalchemy.Text)

1
wg_dashboard_backend/requirements.txt

@ -12,3 +12,4 @@ jinja2
sqlalchemy_utils sqlalchemy_utils
sqlalchemy-migrate sqlalchemy-migrate
requests requests
uvicorn

35
wg_dashboard_backend/routers/v1/peer.py

@ -1,5 +1,5 @@
import ipaddress import ipaddress
import itertools
from fastapi import APIRouter, Depends, HTTPException from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
@ -13,20 +13,18 @@ import script.wireguard
router = APIRouter() router = APIRouter()
@router.post("/add", response_model=schemas.WGPeer) def generate_ip_address(server: schemas.WGServer, v6):
def add_peer( if v6:
peer_add: schemas.WGPeerAdd, address_space = set(
sess: Session = Depends(middleware.get_db) itertools.islice(ipaddress.ip_network("fd42:42:42::1/64", strict=False).hosts(), 1, 1024)
): )
server = schemas.WGServer(interface=peer_add.server_interface).from_db(sess) else:
peer = schemas.WGPeer(server_id=server.id)
address_space = set(ipaddress.ip_network(f"{server.address}/{server.subnet}", strict=False).hosts()) address_space = set(ipaddress.ip_network(f"{server.address}/{server.subnet}", strict=False).hosts())
occupied_space = set() occupied_space = set()
# Try add server IP to list. # Try add server IP to list.
try: try:
occupied_space.add(ipaddress.ip_address(server.address.split("/")[0])) occupied_space.add(ipaddress.ip_address(server.v6_address if v6 else server.address))
except ValueError: except ValueError:
pass pass
@ -34,14 +32,27 @@ def add_peer(
# Try add peer ip to list. # Try add peer ip to list.
try: try:
occupied_space.add(ipaddress.ip_address(p.address.split("/")[0])) occupied_space.add(ipaddress.ip_address(p.v6_address if v6 else p.address))
except ValueError as e: except ValueError as e:
pass # Ignore invalid addresses. These are out of address_space pass # Ignore invalid addresses. These are out of address_space
address_space -= occupied_space address_space -= occupied_space
# Select first available address # Select first available address
peer.address = str(list(sorted(address_space)).pop(0)) return str(list(sorted(address_space)).pop(0))
@router.post("/add", response_model=schemas.WGPeer)
def add_peer(
peer_add: schemas.WGPeerAdd,
sess: Session = Depends(middleware.get_db)
):
server = schemas.WGServer(interface=peer_add.server_interface).from_db(sess)
peer = schemas.WGPeer(server_id=server.id)
if server.v6_address:
peer.v6_address = generate_ip_address(server, v6=True)
peer.address = generate_ip_address(server, v6=False)
# Private public key generation # Private public key generation
keys = script.wireguard.generate_keys() keys = script.wireguard.generate_keys()

30
wg_dashboard_backend/routers/v1/server.py

@ -32,15 +32,30 @@ def add_interface(
server: schemas.WGServerAdd, server: schemas.WGServerAdd,
sess: Session = Depends(middleware.get_db) sess: Session = Depends(middleware.get_db)
): ):
server.post_up = server.post_up if server.post_up != "" else const.DEFAULT_POST_UP
server.post_down = server.post_up if server.post_up != "" else const.DEFAULT_POST_DOWN # Configure POST UP with defaults if not manually set.
if server.post_up == "":
server.post_up = const.DEFAULT_POST_UP
if server.v6_address is not None:
server.post_up += const.DEFAULT_POST_UP_v6
# Configure POST DOWN with defaults if not manually set.
if server.post_down == "":
server.post_down = const.DEFAULT_POST_DOWN
if server.v6_address is not None:
server.post_down += const.DEFAULT_POST_DOWN_v6
peers = server.peers if server.peers else [] peers = server.peers if server.peers else []
# Public/Private key # Public/Private key
try: try:
if server.filter_query(sess).count() != 0: if sess.query(models.WGServer)\
raise HTTPException(status_code=400, detail="The server interface %s already exists in the database" % server.interface) .filter(
(models.WGServer.interface == server.interface) |
(models.WGServer.address == server.address) |
(models.WGServer.v6_address == server.v6_address)).count() != 0:
raise HTTPException(status_code=400, detail="The server interface or ip %s already exists in the database" % server.interface)
if not server.private_key: if not server.private_key:
keys = script.wireguard.generate_keys() keys = script.wireguard.generate_keys()
@ -153,11 +168,14 @@ def edit_server(
peer=peer, peer=peer,
server=server server=server
)) ))
peer.sync(sess)
db_peer = models.WGPeer(**peer.dict())
sess.merge(db_peer)
sess.commit()
script.wireguard.start_interface(server) script.wireguard.start_interface(server)
server.is_running = script.wireguard.is_running(server) server.is_running = script.wireguard.is_running(server)
server.sync(sess) server.sync(sess) # TODO - fix this sync mess.
server.from_db(sess) server.from_db(sess)
return server return server

3
wg_dashboard_backend/schemas.py

@ -114,6 +114,7 @@ class WGPeer(GenericModel):
id: int = None id: int = None
name: str = None name: str = None
address: str = None address: str = None
v6_address: str = None
private_key: str = None private_key: str = None
public_key: str = None public_key: str = None
shared_key: str = None shared_key: str = None
@ -144,7 +145,9 @@ class PSK(GenericModel):
class WGServer(GenericModel): class WGServer(GenericModel):
id: int = None id: int = None
address: str = None address: str = None
v6_address: str = None
subnet: int = None subnet: int = None
v6_subnet: int = None
interface: str interface: str
listen_port: int = None listen_port: int = None
endpoint: str = None endpoint: str = None

13
wg_dashboard_backend/script/wireguard.py

@ -27,6 +27,9 @@ class WGPermissionsError(Exception):
pass pass
class WGPortAlreadyInUse(Exception):
pass
class TempServerFile(): class TempServerFile():
def __init__(self, server: schemas.WGServer): def __init__(self, server: schemas.WGServer):
self.server = server self.server = server
@ -82,8 +85,11 @@ def start_interface(server: schemas.WGServer):
output = subprocess.check_output(const.CMD_WG_QUICK + ["up", server_file], stderr=subprocess.STDOUT) output = subprocess.check_output(const.CMD_WG_QUICK + ["up", server_file], stderr=subprocess.STDOUT)
return output return output
except Exception as e: except Exception as e:
print(e.output)
if b'already exists' in e.output: if b'already exists' in e.output:
raise WGAlreadyStartedError("The wireguard device %s is already started." % server.interface) raise WGAlreadyStartedError("The wireguard device %s is already started." % server.interface)
elif b'Address already in use' in e.output:
raise WGPortAlreadyInUse("The port %s is already used by another application." % server.listen_port)
def stop_interface(server: schemas.WGServer): def stop_interface(server: schemas.WGServer):
@ -92,7 +98,6 @@ def stop_interface(server: schemas.WGServer):
output = subprocess.check_output(const.CMD_WG_QUICK + ["down", server_file], stderr=subprocess.STDOUT) output = subprocess.check_output(const.CMD_WG_QUICK + ["down", server_file], stderr=subprocess.STDOUT)
return output return output
except Exception as e: except Exception as e:
if b'is not a WireGuard interface' in e.output: if b'is not a WireGuard interface' in e.output:
raise WGAlreadyStoppedError("The wireguard device %s is already stopped." % server.interface) raise WGAlreadyStoppedError("The wireguard device %s is already stopped." % server.interface)
@ -111,6 +116,7 @@ def is_running(server: schemas.WGServer):
if output is None: if output is None:
return False return False
except Exception as e: except Exception as e:
print(e.output)
if b'No such device' in e.output: if b'No such device' in e.output:
return False return False
return True return True
@ -197,13 +203,16 @@ def move_server_dir(interface, interface1):
def generate_config(obj: typing.Union[typing.Dict[schemas.WGPeer, schemas.WGServer], schemas.WGServer]): def generate_config(obj: typing.Union[typing.Dict[schemas.WGPeer, schemas.WGServer], schemas.WGServer]):
if isinstance(obj, dict) and "server" in obj and "peer" in obj: if isinstance(obj, dict) and "server" in obj and "peer" in obj:
template = "peer.j2" template = "peer.j2"
is_ipv6 = obj["server"].v6_address is not None
elif isinstance(obj, schemas.WGServer) or isinstance(obj, models.WGServer): elif isinstance(obj, schemas.WGServer) or isinstance(obj, models.WGServer):
template = "server.j2" template = "server.j2"
is_ipv6 = obj.v6_address is not None
else: else:
raise ValueError("Incorrect input type. Should be WGPeer or WGServer") raise ValueError("Incorrect input type. Should be WGPeer or WGServer")
result = util.jinja_env.get_template(template).render( result = util.jinja_env.get_template(template).render(
data=obj data=obj,
is_ipv6=is_ipv6
) )
return result return result

2
wg_dashboard_backend/templates/peer.j2

@ -1,5 +1,5 @@
[Interface] [Interface]
Address = {{ data.peer.address }}/{{ data.server.subnet }} Address = {{ data.peer.address }}/{{ data.server.subnet }}{%- if is_ipv6 -%},{{ data.peer.v6_address }}/{{ data.server.v6_subnet }}{%- endif %}
PrivateKey = {{ data.peer.private_key }} PrivateKey = {{ data.peer.private_key }}
DNS = {{ data.peer.dns }} DNS = {{ data.peer.dns }}

8
wg_dashboard_backend/templates/server.j2

@ -1,10 +1,10 @@
[Interface] [Interface]
Address = {{ data.address }}/{{ data.subnet }} Address = {{ data.address }}/{{ data.subnet }}{%- if is_ipv6 -%},{{ data.v6_address }}/{{ data.v6_subnet }}{%- endif %}
ListenPort = {{ data.listen_port }} ListenPort = {{ data.listen_port }}
PrivateKey = {{ data.private_key }} PrivateKey = {{ data.private_key }}
PostUp = {{ data.post_up }} PostUp = {{ data.post_up }}{%- if is_ipv6 -%} {{ data.v6_post_up }}{%- endif %}
PostDown = {{ data.post_down }} PostDown = {{ data.post_down }}{%- if is_ipv6 -%} {{ data.v6_post_down }}{%- endif %}
{% for peer in data.peers %} {% for peer in data.peers %}
[Peer] [Peer]
@ -13,5 +13,5 @@ PublicKey = {{ peer.public_key }}
{%- if peer.shared_key %} {%- if peer.shared_key %}
PresharedKey = {{ peer.shared_key }} PresharedKey = {{ peer.shared_key }}
{%- endif %} {%- endif %}
AllowedIPs = {{ peer.address }}/32 AllowedIPs = {{ peer.address }}/32{%- if is_ipv6 -%},{{ peer.v6_address }}/128{%- endif %}
{% endfor %} {% endfor %}

3544
wg_dashboard_frontend/package-lock.json

File diff suppressed because it is too large

61
wg_dashboard_frontend/src/app/page/dashboard/add-server/add-server.component.html

@ -20,52 +20,91 @@
<form [formGroup]="serverForm" class="add-server-form"> <form [formGroup]="serverForm" class="add-server-form">
<p><b>Essentials</b></p> <p><b>Essentials</b></p>
<table class="add-server-full-width" cellspacing="0"><tr> <table class="add-server-full-width" cellspacing="0"><tr>
<td> <td>
<mat-form-field class="add-server-full-width"> <mat-form-field class="add-server-full-width">
<mat-label>Interface</mat-label> <mat-label>Interface</mat-label>
<input formControlName="interface" matInput placeholder="wg0"> <input formControlName="interface" matInput [placeholder]="defaultInterface">
</mat-form-field> </mat-form-field>
</td> </td>
<td>
<mat-form-field class="add-server-full-width">
<mat-label>Endpoint</mat-label>
<input formControlName="endpoint" matInput placeholder="my-address.com">
</mat-form-field>
</td>
<td>
<mat-form-field class="add-server-full-width">
<mat-label>Port</mat-label>
<input formControlName="listen_port" matInput [placeholder]="defaultListenPort">
</mat-form-field>
</td>
</tr></table>
<table class="add-server-full-width" cellspacing="0"><tr>
<td> <td>
<mat-form-field class="add-server-full-width"> <mat-form-field class="add-server-full-width">
<mat-label>Address</mat-label> <mat-label>IPv4 Address</mat-label>
<input formControlName="address" matInput placeholder="10.0.200.1"> <input formControlName="address" matInput [placeholder]="defaultIPv4Address">
</mat-form-field> </mat-form-field>
</td> </td>
<td> <td>
<mat-form-field matLine class="add-server-full-width"> <mat-form-field matLine class="add-server-full-width">
<mat-label>Subnet</mat-label> <mat-label>Subnet</mat-label>
<select [(ngModel)]='selectedSubnet' matNativeControl formControlName="subnet"> <select matNativeControl formControlName="subnet">
<option *ngFor="let subnet of subnets" [value]="subnet">/{{subnet}}</option> <option *ngFor="let v4Subnet of v4Subnets" [value]="v4Subnet">/{{v4Subnet}}</option>
</select> </select>
</mat-form-field> </mat-form-field>
</td> </td>
</tr></table> </tr></table>
<table class="add-server-full-width" cellspacing="0"><tr> <table class="add-server-full-width" cellspacing="0"><tr>
<td>
<mat-checkbox [checked]="true" #hasIPV6Support (change)="ipv6SupportChanged($event)">IPv6 Support</mat-checkbox>
</td>
</tr></table>
<table *ngIf="hasIPV6Support.checked" class="add-server-full-width" cellspacing="0"><tr>
<td> <td>
<mat-form-field class="add-server-full-width"> <mat-form-field class="add-server-full-width">
<mat-label>Endpoint</mat-label> <mat-label>IPv6 Address</mat-label>
<input formControlName="endpoint" matInput placeholder="my-address.com"> <input formControlName="v6_address" matInput [placeholder]="defaultIPv6Address">
</mat-form-field> </mat-form-field>
</td> </td>
<td> <td>
<mat-form-field class="add-server-full-width"> <mat-form-field matLine class="add-server-full-width">
<mat-label>Port</mat-label> <mat-label>Subnet</mat-label>
<input formControlName="listen_port" matInput placeholder="51820"> <select matNativeControl formControlName="v6_subnet">
<option *ngFor="let v6Subnet of v6Subnets" [value]="v6Subnet">/{{v6Subnet}}</option>
</select>
</mat-form-field> </mat-form-field>
</td> </td>
</tr></table> </tr></table>
<table class="add-server-full-width" cellspacing="0"><tr> <table class="add-server-full-width" cellspacing="0"><tr>
<td> <td>
<mat-form-field class="add-server-full-width"> <mat-form-field class="add-server-full-width">
<mat-label>Default DNS</mat-label> <mat-label>Default DNS</mat-label>
<input formControlName="dns" matInput placeholder="8.8.8.8"> <input formControlName="dns" matInput [placeholder]="defaultIPv4Address">
</mat-form-field> </mat-form-field>
</td> </td>
</tr></table> </tr></table>

51
wg_dashboard_frontend/src/app/page/dashboard/add-server/add-server.component.ts

@ -1,4 +1,4 @@
import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core'; import {Component, Input, OnInit, ViewChild, ViewEncapsulation} from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms'; import { FormControl, FormGroup, Validators } from '@angular/forms';
import { IPValidator } from '../../../validators/ip-address.validator'; import { IPValidator } from '../../../validators/ip-address.validator';
import { NumberValidator } from '../../../validators/number.validator'; import { NumberValidator } from '../../../validators/number.validator';
@ -10,6 +10,7 @@ import {Peer} from "../../../interfaces/peer";
import {forkJoin, from} from "rxjs"; import {forkJoin, from} from "rxjs";
import {map, mergeMap} from "rxjs/operators"; import {map, mergeMap} from "rxjs/operators";
import {NotifierService} from "angular-notifier"; import {NotifierService} from "angular-notifier";
import {MatCheckboxChange} from "@angular/material/checkbox";
@Component({ @Component({
selector: 'app-add-server', selector: 'app-add-server',
templateUrl: './add-server.component.html', templateUrl: './add-server.component.html',
@ -34,8 +35,16 @@ export class AddServerComponent implements OnInit {
"DNS": "dns" "DNS": "dns"
} }
subnets = []; v4Subnets = [];
selectedSubnet = 24; v6Subnets = [];
defaultListenPort = "51820"
defaultInterface = "wg0"
defaultIPv4Subnet = 24;
defaultIPv6Subnet = 64;
defaultIPv4Address = "10.0.200.1"
defaultDNS = this.defaultIPv4Address + ",8.8.8.8"
defaultIPv6Address = "fd42:42:42::1"
serverForm: FormGroup = null; serverForm: FormGroup = null;
isEdit = false; isEdit = false;
@ -43,12 +52,14 @@ export class AddServerComponent implements OnInit {
initForm(){ initForm(){
this.serverForm = new FormGroup({ this.serverForm = new FormGroup({
address: new FormControl('', [Validators.required, IPValidator.isIPAddress]), address: new FormControl(this.defaultIPv4Address, [Validators.required, IPValidator.isIPAddress]),
subnet: new FormControl('', [Validators.required, Validators.min(1), Validators.max(32)]), v6_address: new FormControl(this.defaultIPv6Address, [Validators.required, IPValidator.isIPAddress]),
interface: new FormControl('', [Validators.required, Validators.minLength(3)]), subnet: new FormControl(this.defaultIPv4Subnet, [Validators.required, Validators.min(1), Validators.max(32)]),
listen_port: new FormControl('', [Validators.required, NumberValidator.stringIsNumber]), v6_subnet: new FormControl(this.defaultIPv6Subnet, [Validators.required, Validators.min(1), Validators.max(64)]),
interface: new FormControl(this.defaultInterface, [Validators.required, Validators.minLength(3)]),
listen_port: new FormControl(this.defaultListenPort, [Validators.required, NumberValidator.stringIsNumber]),
endpoint: new FormControl('', Validators.required), endpoint: new FormControl('', Validators.required),
dns: new FormControl(''), dns: new FormControl(this.defaultDNS),
private_key: new FormControl('' ), private_key: new FormControl('' ),
public_key: new FormControl('' ), public_key: new FormControl('' ),
post_up: new FormControl(''), post_up: new FormControl(''),
@ -61,12 +72,25 @@ export class AddServerComponent implements OnInit {
}); });
} }
ipv6SupportChanged($event: MatCheckboxChange){
let v6AddressControl = this.serverForm.get("v6_address");
let v6SubnetControl = this.serverForm.get("v6_subnet");
if($event.checked){
v6AddressControl.enable()
v6SubnetControl.enable()
}else {
v6AddressControl.disable()
v6SubnetControl.disable()
}
}
constructor(private serverAPI: ServerService, private comm: DataService, private notify: NotifierService) { constructor(private serverAPI: ServerService, private comm: DataService, private notify: NotifierService) {
} }
ngOnInit(): void { ngOnInit(): void {
this.subnets = Array(32).fill(1).map((x,i)=>i+1); this.v4Subnets = Array(32).fill(1).map((x,i)=>i+1);
this.v6Subnets = Array(64).fill(1).map((x,i)=>i+1);
this.initForm(); this.initForm();
this.comm.on('server-edit').subscribe((data: Server) => { this.comm.on('server-edit').subscribe((data: Server) => {
@ -208,10 +232,9 @@ export class AddServerComponent implements OnInit {
}); });
} }
this.isEdit = false;
this.editServer = null; this.resetForm();
this.serverForm.reset();
this.serverForm.clearValidators();
} }
getKeyPair() { getKeyPair() {
@ -228,7 +251,7 @@ export class AddServerComponent implements OnInit {
resetForm() { resetForm() {
this.isEdit = false; this.isEdit = false;
this.editServer = null; this.editServer = null;
this.serverForm.clearValidators();
this.initForm() this.initForm()
} }
} }

2
wg_dashboard_frontend/src/app/page/dashboard/dashboard.module.ts

@ -19,6 +19,7 @@ import { PeerComponent } from './peer/peer.component';
import { QRCodeModule } from 'angularx-qrcode'; import { QRCodeModule } from 'angularx-qrcode';
import {MatTooltipModule} from "@angular/material/tooltip"; import {MatTooltipModule} from "@angular/material/tooltip";
import {MatSelectModule} from "@angular/material/select"; import {MatSelectModule} from "@angular/material/select";
import {MatCheckboxModule} from "@angular/material/checkbox";
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -45,6 +46,7 @@ import {MatSelectModule} from "@angular/material/select";
QRCodeModule, QRCodeModule,
MatTooltipModule, MatTooltipModule,
MatSelectModule, MatSelectModule,
MatCheckboxModule,
], ],
}) })

Loading…
Cancel
Save