After Width: | Height: | Size: 119 KiB |
Before Width: | Height: | Size: 190 KiB After Width: | Height: | Size: 94 KiB |
Before Width: | Height: | Size: 205 KiB After Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 260 KiB After Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 192 KiB After Width: | Height: | Size: 187 KiB |
After Width: | Height: | Size: 194 KiB |
After Width: | Height: | Size: 213 KiB |
After Width: | Height: | Size: 220 KiB |
After Width: | Height: | Size: 74 KiB |
@ -0,0 +1,25 @@ |
|||||
|
{ |
||||
|
"requires": true, |
||||
|
"lockfileVersion": 1, |
||||
|
"dependencies": { |
||||
|
"@angular/cdk": { |
||||
|
"version": "9.2.0", |
||||
|
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-9.2.0.tgz", |
||||
|
"integrity": "sha512-jeeznvNDpR9POuxzz8Y0zFvMynG9HCJo3ZPTqOjlOq8Lj8876+rLsHDvKEMeLdwlkdi1EweYJW1CLQzI+TwqDA==", |
||||
|
"requires": { |
||||
|
"parse5": "^5.0.0" |
||||
|
} |
||||
|
}, |
||||
|
"@angular/flex-layout": { |
||||
|
"version": "9.0.0-beta.29", |
||||
|
"resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-9.0.0-beta.29.tgz", |
||||
|
"integrity": "sha512-93sxR+kYfYMOdnlWL0Q77FZ428gg8XnBu0YZm6GsCdkw/vLggIT/G1ZAqHlCPIODt6pxmCJ5KXh4ShvniIYDsA==" |
||||
|
}, |
||||
|
"parse5": { |
||||
|
"version": "5.1.1", |
||||
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", |
||||
|
"integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", |
||||
|
"optional": true |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
import sqlalchemy |
||||
|
from sqlalchemy.ext.declarative import declarative_base |
||||
|
from sqlalchemy.orm import sessionmaker |
||||
|
import const |
||||
|
|
||||
|
engine = sqlalchemy.create_engine( |
||||
|
const.DATABASE_URL, connect_args={"check_same_thread": False} |
||||
|
) |
||||
|
|
||||
|
|
||||
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) |
||||
|
|
||||
|
Base = declarative_base() |
@ -0,0 +1,81 @@ |
|||||
|
from datetime import timedelta, datetime |
||||
|
|
||||
|
import jwt |
||||
|
from fastapi import Depends, HTTPException |
||||
|
from fastapi.security import OAuth2PasswordBearer |
||||
|
from jwt import PyJWTError |
||||
|
from passlib.context import CryptContext |
||||
|
from sqlalchemy.orm import Session |
||||
|
from starlette import status |
||||
|
from starlette.requests import Request |
||||
|
from starlette.responses import Response |
||||
|
|
||||
|
import const |
||||
|
import schemas |
||||
|
from database import SessionLocal |
||||
|
import db.user |
||||
|
|
||||
|
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/login", auto_error=False) |
||||
|
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") |
||||
|
|
||||
|
|
||||
|
def get_password_hash(password): |
||||
|
return pwd_context.hash(password) |
||||
|
|
||||
|
|
||||
|
def verify_password(plain_password, hashed_password): |
||||
|
return pwd_context.verify(plain_password, hashed_password) |
||||
|
|
||||
|
|
||||
|
async def db_session_middleware(request: Request, call_next): |
||||
|
response = Response("Internal server error (Database error)", status_code=500) |
||||
|
try: |
||||
|
request.state.db = SessionLocal() |
||||
|
response = await call_next(request) |
||||
|
finally: |
||||
|
request.state.db.close() |
||||
|
return response |
||||
|
|
||||
|
|
||||
|
# NON MIDDLEWARE MIDDLEWARISH THING |
||||
|
|
||||
|
|
||||
|
# Dependency |
||||
|
def get_db(request: Request): |
||||
|
return request.state.db |
||||
|
|
||||
|
|
||||
|
def create_access_token(*, data: dict, expires_delta: timedelta = None): |
||||
|
to_encode = data.copy() |
||||
|
if expires_delta: |
||||
|
expire = datetime.utcnow() + expires_delta |
||||
|
else: |
||||
|
expire = datetime.utcnow() + timedelta(minutes=15) |
||||
|
to_encode.update({"exp": expire}) |
||||
|
encoded_jwt = jwt.encode(to_encode, const.SECRET_KEY, algorithm=const.ALGORITHM) |
||||
|
return encoded_jwt |
||||
|
|
||||
|
|
||||
|
def auth(token: str = Depends(oauth2_scheme), sess: Session = Depends(get_db)): |
||||
|
|
||||
|
credentials_exception = HTTPException( |
||||
|
status_code=status.HTTP_401_UNAUTHORIZED, |
||||
|
detail="Could not validate credentials", |
||||
|
headers={"WWW-Authenticate": "Bearer"}, |
||||
|
) |
||||
|
|
||||
|
try: |
||||
|
payload = jwt.decode(token, const.SECRET_KEY, algorithms=[const.ALGORITHM]) |
||||
|
username: str = payload.get("sub") |
||||
|
if username is None: |
||||
|
raise credentials_exception |
||||
|
|
||||
|
except PyJWTError: |
||||
|
raise credentials_exception |
||||
|
user = schemas.User.from_orm( |
||||
|
schemas.UserInDB(username=username, password="").from_db(sess) |
||||
|
) |
||||
|
if user is None: |
||||
|
raise credentials_exception |
||||
|
return user |
||||
|
|
@ -0,0 +1,95 @@ |
|||||
|
import ipaddress |
||||
|
|
||||
|
from fastapi import APIRouter, Depends, HTTPException |
||||
|
from sqlalchemy.orm import Session |
||||
|
|
||||
|
import const |
||||
|
import models |
||||
|
import schemas |
||||
|
import middleware |
||||
|
import db.wireguard |
||||
|
import script.wireguard |
||||
|
|
||||
|
router = APIRouter() |
||||
|
|
||||
|
|
||||
|
@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) |
||||
|
|
||||
|
address_space = set(ipaddress.ip_network(server.address, strict=False).hosts()) |
||||
|
occupied_space = set() |
||||
|
for p in server.peers: |
||||
|
try: |
||||
|
occupied_space.add(ipaddress.ip_address(p.address.split("/")[0])) |
||||
|
except ValueError as e: |
||||
|
pass # Ignore invalid addresses. These are out of address_space |
||||
|
|
||||
|
address_space -= occupied_space |
||||
|
|
||||
|
# Select first available address |
||||
|
peer.address = str(list(sorted(address_space)).pop(0)) + "/32" |
||||
|
|
||||
|
# Private public key generation |
||||
|
keys = script.wireguard.generate_keys() |
||||
|
peer.private_key = keys["private_key"] |
||||
|
peer.public_key = keys["public_key"] |
||||
|
|
||||
|
# Set 0.0.0.0/0, ::/0 as default allowed ips |
||||
|
peer.allowed_ips = ', '.join(const.PEER_DEFAULT_ALLOWED_IPS) |
||||
|
|
||||
|
# Set unnamed |
||||
|
peer.name = "Unnamed" |
||||
|
|
||||
|
peer.dns = server.endpoint |
||||
|
|
||||
|
peer.configuration = script.wireguard.generate_config(dict( |
||||
|
peer=peer, |
||||
|
server=server |
||||
|
)) |
||||
|
|
||||
|
peer.sync(sess) |
||||
|
|
||||
|
# If server is running. Add peer |
||||
|
if script.wireguard.is_running(server): |
||||
|
script.wireguard.add_peer(server, peer) |
||||
|
|
||||
|
return peer |
||||
|
|
||||
|
|
||||
|
@router.post("/delete", response_model=schemas.WGPeer) |
||||
|
def delete_peer( |
||||
|
peer: schemas.WGPeer, |
||||
|
sess: Session = Depends(middleware.get_db) |
||||
|
): |
||||
|
peer.from_db(sess) # Sync full object |
||||
|
|
||||
|
if not db.wireguard.peer_remove(sess, peer): |
||||
|
raise HTTPException(400, detail="Were not able to delete peer %s (%s)" % (peer.name, peer.public_key)) |
||||
|
|
||||
|
server = schemas.WGServer(interface=peer.server_id) |
||||
|
if script.wireguard.is_running(server): |
||||
|
script.wireguard.remove_peer(server, peer) |
||||
|
|
||||
|
return peer |
||||
|
|
||||
|
|
||||
|
@router.post("/edit", response_model=schemas.WGPeer) |
||||
|
def edit_peer( |
||||
|
peer: schemas.WGPeer, |
||||
|
sess: Session = Depends(middleware.get_db) |
||||
|
): |
||||
|
server = schemas.WGServer(interface="")\ |
||||
|
.from_orm(sess.query(models.WGServer).filter_by(id=peer.server_id).one()) |
||||
|
|
||||
|
peer.configuration = script.wireguard.generate_config(dict( |
||||
|
peer=peer, |
||||
|
server=server |
||||
|
)) |
||||
|
peer.sync(sess) |
||||
|
|
||||
|
return peer |
@ -0,0 +1,138 @@ |
|||||
|
import tempfile |
||||
|
from os.path import exists |
||||
|
|
||||
|
from fastapi import APIRouter, Depends, HTTPException |
||||
|
from sqlalchemy.orm import Session |
||||
|
from starlette.responses import JSONResponse |
||||
|
|
||||
|
import const |
||||
|
import schemas |
||||
|
import middleware |
||||
|
import db.wireguard |
||||
|
import script.wireguard |
||||
|
import typing |
||||
|
|
||||
|
router = APIRouter() |
||||
|
|
||||
|
|
||||
|
@router.get("/all", response_model=typing.List[schemas.WGServer]) |
||||
|
def servers_all( |
||||
|
sess: Session = Depends(middleware.get_db) |
||||
|
): |
||||
|
interfaces = db.wireguard.server_get_all(sess) |
||||
|
for iface in interfaces: |
||||
|
iface.is_running = script.wireguard.is_running(iface) |
||||
|
|
||||
|
return interfaces |
||||
|
|
||||
|
|
||||
|
@router.post("/add", response_model=schemas.WGServer) |
||||
|
def add_interface( |
||||
|
server: schemas.WGServerAdd, |
||||
|
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 |
||||
|
|
||||
|
# Public/Private key |
||||
|
try: |
||||
|
|
||||
|
if server.filter_query(sess).count() != 0: |
||||
|
raise HTTPException(status_code=400, detail="The server interface %s already exists in the database" % server.interface) |
||||
|
|
||||
|
keys = script.wireguard.generate_keys() |
||||
|
server.private_key = keys["private_key"] |
||||
|
server.public_key = keys["public_key"] |
||||
|
server.configuration = script.wireguard.generate_config(server) |
||||
|
|
||||
|
server.sync(sess) |
||||
|
except ValueError as e: |
||||
|
raise HTTPException(status_code=400, detail=str(e)) |
||||
|
|
||||
|
return server |
||||
|
|
||||
|
|
||||
|
@router.post("/stop", response_model=schemas.WGServer) |
||||
|
def start_server( |
||||
|
form_data: schemas.WGServer |
||||
|
): |
||||
|
script.wireguard.stop_interface(form_data) |
||||
|
form_data.is_running = script.wireguard.is_running(form_data) |
||||
|
return form_data |
||||
|
|
||||
|
|
||||
|
@router.post("/start", response_model=schemas.WGServer) |
||||
|
def start_server( |
||||
|
server: schemas.WGServer, |
||||
|
sess: Session = Depends(middleware.get_db) |
||||
|
): |
||||
|
script.wireguard.start_interface(server) |
||||
|
server.is_running = script.wireguard.is_running(server) |
||||
|
server.sync(sess) |
||||
|
return server |
||||
|
|
||||
|
|
||||
|
@router.post("/restart", response_model=schemas.WGServer) |
||||
|
def restart_server( |
||||
|
server: schemas.WGServer, |
||||
|
sess: Session = Depends(middleware.get_db) |
||||
|
): |
||||
|
script.wireguard.restart_interface(server) |
||||
|
server.is_running = script.wireguard.is_running(server) |
||||
|
server.sync(sess) |
||||
|
|
||||
|
return server |
||||
|
|
||||
|
|
||||
|
@router.post("/delete", response_model=schemas.WGServer) |
||||
|
def delete_server( |
||||
|
form_data: schemas.WGServer, |
||||
|
sess: Session = Depends(middleware.get_db) |
||||
|
): |
||||
|
# Stop if running |
||||
|
if script.wireguard.is_running(form_data): |
||||
|
script.wireguard.stop_interface(form_data) |
||||
|
|
||||
|
if not db.wireguard.server_remove(sess, form_data): |
||||
|
raise HTTPException(400, detail="Were not able to delete %s" % form_data.interface) |
||||
|
return form_data |
||||
|
|
||||
|
|
||||
|
@router.post("/stats", dependencies=[Depends(middleware.auth)]) |
||||
|
def stats_server(server: schemas.WGServer): |
||||
|
stats = script.wireguard.get_stats(server) |
||||
|
return JSONResponse(content=stats) |
||||
|
|
||||
|
|
||||
|
@router.post("/edit", response_model=schemas.WGServer) |
||||
|
def edit_server( |
||||
|
data: dict, sess: Session = Depends(middleware.get_db) |
||||
|
): |
||||
|
interface = data["interface"] |
||||
|
old = schemas.WGServer(interface=interface).from_db(sess) |
||||
|
|
||||
|
# Stop if running |
||||
|
if script.wireguard.is_running(old): |
||||
|
script.wireguard.stop_interface(old) |
||||
|
|
||||
|
# Update server |
||||
|
server = schemas.WGServer(**data["server"]) |
||||
|
server.configuration = script.wireguard.generate_config(server) |
||||
|
server = old.update(sess, new=server) |
||||
|
|
||||
|
# Update peers |
||||
|
for peer_data in server.peers: |
||||
|
peer = schemas.WGPeer(**peer_data) |
||||
|
peer.configuration = script.wireguard.generate_config(dict( |
||||
|
peer=peer, |
||||
|
server=server |
||||
|
)) |
||||
|
peer.sync(sess) |
||||
|
|
||||
|
script.wireguard.start_interface(server) |
||||
|
server.is_running = script.wireguard.is_running(server) |
||||
|
server.sync(sess) |
||||
|
server.from_db(sess) |
||||
|
|
||||
|
return server |
||||
|
|
@ -0,0 +1,86 @@ |
|||||
|
from datetime import timedelta |
||||
|
|
||||
|
from fastapi import APIRouter, HTTPException, Depends, Form |
||||
|
from sqlalchemy.orm import Session |
||||
|
from starlette import status |
||||
|
|
||||
|
import const |
||||
|
import db.user |
||||
|
import middleware |
||||
|
import models |
||||
|
import schemas |
||||
|
|
||||
|
router = APIRouter() |
||||
|
|
||||
|
|
||||
|
@router.get("/logout") |
||||
|
def logout(user: schemas.User = Depends(middleware.auth)): |
||||
|
return dict(message="ok") |
||||
|
|
||||
|
|
||||
|
@router.post("/user/edit", response_model=schemas.User) |
||||
|
def edit(form_data: schemas.UserInDB, |
||||
|
user: schemas.UserInDB = Depends(middleware.auth), |
||||
|
sess: Session = Depends(middleware.get_db) |
||||
|
): |
||||
|
form_data.password = middleware.get_password_hash(form_data.password) |
||||
|
form_data.sync(sess) |
||||
|
return form_data |
||||
|
|
||||
|
|
||||
|
@router.post("/login", response_model=schemas.Token) |
||||
|
def login(*, username: str = Form(...), password: str = Form(...), sess: Session = Depends(middleware.get_db)): |
||||
|
user: schemas.UserInDB = schemas.UserInDB(username=username, password="").from_db(sess) |
||||
|
|
||||
|
# Verify password |
||||
|
if not user or not middleware.verify_password(password, user.password): |
||||
|
raise HTTPException( |
||||
|
status_code=status.HTTP_401_UNAUTHORIZED, |
||||
|
detail="Incorrect username or password", |
||||
|
headers={"WWW-Authenticate": "Bearer"}, |
||||
|
) |
||||
|
|
||||
|
# Create token |
||||
|
access_token_expires = timedelta(minutes=const.ACCESS_TOKEN_EXPIRE_MINUTES) |
||||
|
access_token = middleware.create_access_token( |
||||
|
data={"sub": user.username}, expires_delta=access_token_expires |
||||
|
) |
||||
|
|
||||
|
return schemas.Token( |
||||
|
access_token=access_token, |
||||
|
token_type="bearer", |
||||
|
user=schemas.User(**user.dict()) |
||||
|
) |
||||
|
|
||||
|
|
||||
|
@router.post("/users/create/") |
||||
|
def create_user( |
||||
|
form_data: schemas.UserInDB, |
||||
|
sess: Session = Depends(middleware.get_db), |
||||
|
user: schemas.User = Depends(middleware.auth) |
||||
|
): |
||||
|
user = db.user.get_user_by_name(sess, form_data.username) |
||||
|
|
||||
|
# User already exists |
||||
|
if user: |
||||
|
if not db.user.authenticate_user(sess, form_data.username, form_data.password): |
||||
|
raise HTTPException(status_code=401, detail="Incorrect password") |
||||
|
|
||||
|
else: |
||||
|
|
||||
|
# Create the user |
||||
|
if not db.user.create_user(sess, models.User( |
||||
|
username=form_data.username, |
||||
|
password=form_data.password, |
||||
|
full_name=form_data.full_name, |
||||
|
email=form_data.email, |
||||
|
role=form_data.role, |
||||
|
)): |
||||
|
raise HTTPException(status_code=400, detail="Could not create user") |
||||
|
|
||||
|
return login_for_access_token(OAuth2PasswordRequestForm( |
||||
|
username=form_data.username, |
||||
|
password=form_data.password, |
||||
|
scope="" |
||||
|
), sess) |
||||
|
|
@ -0,0 +1,25 @@ |
|||||
|
from fastapi import APIRouter |
||||
|
|
||||
|
import middleware |
||||
|
import schemas |
||||
|
import script.wireguard |
||||
|
|
||||
|
router = APIRouter() |
||||
|
|
||||
|
|
||||
|
@router.get("/generate_psk", response_model=schemas.PSK) |
||||
|
def generate_psk(): |
||||
|
return schemas.PSK( |
||||
|
psk=script.wireguard.generate_psk() |
||||
|
) |
||||
|
|
||||
|
|
||||
|
@router.get("/generate_keypair", response_model=schemas.KeyPair) |
||||
|
def generate_key_pair(): |
||||
|
keys = script.wireguard.generate_keys() |
||||
|
private_key = keys["private_key"] |
||||
|
public_key = keys["public_key"] |
||||
|
return schemas.KeyPair( |
||||
|
private_key=private_key, |
||||
|
public_key=public_key |
||||
|
) |
@ -1,12 +1,12 @@ |
|||||
[Interface] |
[Interface] |
||||
Address = {{ data.address.replace("/32", "/24") }} |
Address = {{ data.peer.address.replace("/32", "/24") }} |
||||
PrivateKey = {{ data.private_key }} |
PrivateKey = {{ data.peer.private_key }} |
||||
DNS = {{ data.dns }} |
DNS = {{ data.peer.dns }} |
||||
|
|
||||
[Peer] |
[Peer] |
||||
PublicKey = {{ data.server_ref.public_key }} |
PublicKey = {{ data.server.public_key }} |
||||
AllowedIPs = {{ data.allowed_ips }} |
AllowedIPs = {{ data.peer.allowed_ips }} |
||||
Endpoint = {{ data.server_ref.endpoint }}:{{ data.server_ref.listen_port }} |
Endpoint = {{ data.server.endpoint }}:{{ data.server.listen_port }} |
||||
{% if data.preshared_key %} |
{% if data.server.shared_key %} |
||||
PresharedKey = {{ data.server_ref.preshared_key }} |
PresharedKey = {{ data.server.shared_key }} |
||||
{% endif %} |
{% endif %} |
||||
|
@ -0,0 +1,80 @@ |
|||||
|
import warnings |
||||
|
|
||||
|
import schemas |
||||
|
from database import SessionLocal |
||||
|
|
||||
|
with warnings.catch_warnings(): |
||||
|
warnings.filterwarnings("ignore",category=DeprecationWarning) |
||||
|
|
||||
|
|
||||
|
from main import app |
||||
|
from fastapi.testclient import TestClient |
||||
|
|
||||
|
|
||||
|
client = TestClient(app) |
||||
|
|
||||
|
sess = SessionLocal() |
||||
|
|
||||
|
username = "admin" |
||||
|
password = "admin" |
||||
|
token_headers = {} |
||||
|
|
||||
|
def test_logout_without_auth(): |
||||
|
response = client.get("/api/logout") |
||||
|
assert response.status_code == 401 |
||||
|
#assert response.json() == dict(message="ok") |
||||
|
|
||||
|
|
||||
|
def test_login_missing_username(): |
||||
|
response = client.post("/api/login", json=dict( |
||||
|
password=password |
||||
|
)) |
||||
|
assert response.status_code == 422 |
||||
|
|
||||
|
|
||||
|
def test_login_missing_password(): |
||||
|
|
||||
|
response = client.post("/api/login", json=dict( |
||||
|
password=password |
||||
|
)) |
||||
|
assert response.status_code == 422 |
||||
|
|
||||
|
|
||||
|
def test_login(): |
||||
|
|
||||
|
response = client.post("/api/login", json=dict( |
||||
|
username=username, |
||||
|
password=password |
||||
|
) |
||||
|
) |
||||
|
assert response.status_code == 200 # Must have status code 200 |
||||
|
assert "user" in response.json() |
||||
|
assert "token_type" in response.json() |
||||
|
assert "access_token" in response.json() |
||||
|
token_headers["Authorization"] = response.json()["token_type"] + " " + response.json()["access_token"] |
||||
|
return response |
||||
|
|
||||
|
|
||||
|
def test_logout_with_auth(): |
||||
|
response = client.get("/api/logout", headers=token_headers) |
||||
|
assert response.status_code == 200 |
||||
|
|
||||
|
|
||||
|
def test_user_edit(): |
||||
|
|
||||
|
user = schemas.UserInDB( |
||||
|
username="test", |
||||
|
password="test", |
||||
|
full_name="test", |
||||
|
email="test", |
||||
|
role="test" |
||||
|
) |
||||
|
|
||||
|
user.sync(sess=sess) |
||||
|
|
||||
|
db_user = user.from_db(sess) |
||||
|
#print(db_user.username) |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
@ -1,12 +0,0 @@ |
|||||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. |
|
||||
# For additional information regarding the format and rule options, please see: |
|
||||
# https://github.com/browserslist/browserslist#queries |
|
||||
|
|
||||
# You can see what browsers were selected by your queries by running: |
|
||||
# npx browserslist |
|
||||
|
|
||||
> 0.5% |
|
||||
last 2 versions |
|
||||
Firefox ESR |
|
||||
not dead |
|
||||
not IE 9-11 # For IE 9-11 support, remove 'not'. |
|
@ -1,15 +1,68 @@ |
|||||
import { Component } from '@angular/core'; |
import {Component, HostBinding} from '@angular/core'; |
||||
import {AuthService} from "@services/*"; |
import { AuthService } from '@services/*'; |
||||
|
import {OverlayContainer} from "@angular/cdk/overlay"; |
||||
|
import {DataService} from "./services/data.service"; |
||||
|
import {CookieService} from "ngx-cookie-service"; |
||||
|
|
||||
|
const THEME_DARKNESS_SUFFIX = `-dark`; |
||||
|
|
||||
@Component({ |
@Component({ |
||||
selector: 'app-root', |
selector: 'app-root', |
||||
template: `<router-outlet></router-outlet>`, |
template: `<router-outlet></router-outlet>`, |
||||
}) |
}) |
||||
export class AppComponent { |
export class AppComponent { |
||||
|
@HostBinding('class') activeThemeCssClass: string; |
||||
|
isThemeDark = false; |
||||
|
activeTheme: string; |
||||
|
|
||||
|
constructor( |
||||
|
private auth: |
||||
|
AuthService, |
||||
|
private overlayContainer: OverlayContainer, |
||||
|
private comm: DataService, |
||||
|
private cookieService: CookieService |
||||
|
) { |
||||
|
auth.init(); |
||||
|
|
||||
|
this.comm.on("changeTheme").subscribe( (data: { |
||||
|
theme: any, |
||||
|
darkMode: boolean |
||||
|
}) => { |
||||
|
this.setActiveTheme(data.theme.theme, /* darkness: */ data.darkMode) |
||||
|
}); |
||||
|
|
||||
|
if(this.cookieService.check("currentTheme")){ |
||||
|
this.setActiveTheme( |
||||
|
JSON.parse(this.cookieService.get("currentTheme")).theme, |
||||
|
(this.cookieService.get("darkMode") === 'true') |
||||
|
); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
constructor(private auth: AuthService) { |
|
||||
auth.init() |
|
||||
} |
} |
||||
|
|
||||
|
setActiveTheme(theme: string, darkness: boolean = null) { |
||||
|
if (darkness === null) |
||||
|
darkness = this.isThemeDark; |
||||
|
else if (this.isThemeDark === darkness) { |
||||
|
if (this.activeTheme === theme) return |
||||
|
} else |
||||
|
this.isThemeDark = darkness; |
||||
|
|
||||
|
this.activeTheme = theme; |
||||
|
|
||||
|
const cssClass = darkness === true ? theme + THEME_DARKNESS_SUFFIX : theme; |
||||
|
|
||||
|
const classList = this.overlayContainer.getContainerElement().classList; |
||||
|
if (classList.contains(this.activeThemeCssClass)) |
||||
|
classList.replace(this.activeThemeCssClass, cssClass); |
||||
|
else |
||||
|
classList.add(cssClass); |
||||
|
|
||||
|
this.activeThemeCssClass = cssClass |
||||
|
} |
||||
} |
} |
||||
|
@ -1,80 +0,0 @@ |
|||||
@import '~theme/helpers'; |
|
||||
|
|
||||
.blank-layout-card { |
|
||||
margin: auto; |
|
||||
|
|
||||
.mdl-button { |
|
||||
font-weight: 500; |
|
||||
} |
|
||||
|
|
||||
font-family: Roboto, Helvetica, sans-serif; |
|
||||
font-size: 1rem; |
|
||||
|
|
||||
.mdl-card__blank-layout-card.mdl-card { |
|
||||
max-width: 450px; |
|
||||
margin: auto; |
|
||||
|
|
||||
.mdl-card__supporting-text { |
|
||||
min-height: inherit; |
|
||||
width: 100%; |
|
||||
padding: 32px 24px; |
|
||||
box-sizing: border-box; |
|
||||
|
|
||||
.mdl-card__title-text { |
|
||||
font-size: 17px; |
|
||||
font-weight: bold; |
|
||||
} |
|
||||
|
|
||||
.blank-layout-card-name { |
|
||||
font-size: 24px; |
|
||||
display: block; |
|
||||
padding: 0 0 8px 0; |
|
||||
} |
|
||||
|
|
||||
.blank-layout-card-link { |
|
||||
padding: 12px 0; |
|
||||
} |
|
||||
|
|
||||
.blank-layout-card-link, |
|
||||
.blank-layout-card-link * { |
|
||||
display: inline-block; |
|
||||
font-size: 1rem; |
|
||||
font-weight: inherit; |
|
||||
color: $color-alto; |
|
||||
} |
|
||||
|
|
||||
.underlined { |
|
||||
display: inline-block; |
|
||||
border-bottom: 1px solid $color-light-blue; |
|
||||
} |
|
||||
|
|
||||
.checkbox--inline { |
|
||||
display: inline; |
|
||||
padding-top: 4px; |
|
||||
padding-left: 35px; |
|
||||
} |
|
||||
|
|
||||
.submit-cell { |
|
||||
display: flex; |
|
||||
} |
|
||||
|
|
||||
.text--huge { |
|
||||
font-size: 120px; |
|
||||
font-weight: bold; |
|
||||
display: inline-block; |
|
||||
padding: 100px 0 40px 0; |
|
||||
} |
|
||||
|
|
||||
.text--sorry { |
|
||||
font-size: 28px; |
|
||||
font-weight: 300; |
|
||||
} |
|
||||
|
|
||||
.alignment--bottom-right { |
|
||||
position: absolute; |
|
||||
bottom: 39px; |
|
||||
right: 46px; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,10 +0,0 @@ |
|||||
import { Component, HostBinding } from '@angular/core'; |
|
||||
|
|
||||
import { UpgradableComponent } from 'theme/components/upgradable'; |
|
||||
|
|
||||
@Component({ |
|
||||
template: '', |
|
||||
}) |
|
||||
export class BlankLayoutCardComponent extends UpgradableComponent { |
|
||||
@HostBinding('class.blank-layout-card') protected readonly blankLayoutCard = true; |
|
||||
} |
|
@ -1 +0,0 @@ |
|||||
export { BlankLayoutCardComponent } from './blank-layout-card.component'; |
|
@ -1,2 +0,0 @@ |
|||||
export { MessageMenuComponent } from './message-menu.component'; |
|
||||
export { MessageMenuService } from './message-menu.service'; |
|
@ -1,27 +0,0 @@ |
|||||
<div class="material-icons mdl-badge mdl-badge--overlap mdl-button--icon message" id="inbox" [attr.data-badge]="messages.length"> |
|
||||
mail_outline |
|
||||
</div> |
|
||||
<ul |
|
||||
class="mdl-menu mdl-list mdl-js-menu mdl-js-ripple-effect mdl-menu--bottom-right mdl-shadow--2dp messages-dropdown" |
|
||||
for="inbox"> |
|
||||
<li class="mdl-list__item"> |
|
||||
You have {{ messages.length }} new messages! |
|
||||
</li> |
|
||||
<li *ngFor="let item of messages" class="mdl-menu__item mdl-list__item mdl-list__item--two-line list__item--border-top"> |
|
||||
<span class="mdl-list__item-primary-content"> |
|
||||
<span class="mdl-list__item-avatar" |
|
||||
ngClass="background-color--{{ item.color }}"> |
|
||||
<span class="text">{{ item.icon }}</span> |
|
||||
</span> |
|
||||
<span>{{ item.name }}</span> |
|
||||
<span |
|
||||
class="mdl-list__item-sub-title">{{ item.type }}</span> |
|
||||
</span> |
|
||||
<span class="mdl-list__item-secondary-content"> |
|
||||
<span class="label label--transparent">{{ item.time }}</span> |
|
||||
</span> |
|
||||
</li> |
|
||||
<li class="mdl-list__item list__item--border-top"> |
|
||||
<button href="#" class="mdl-button mdl-js-button mdl-js-ripple-effect">SHOW ALL MESSAGES</button> |
|
||||
</li> |
|
||||
</ul> |
|
@ -1,54 +0,0 @@ |
|||||
@import '~theme/helpers'; |
|
||||
|
|
||||
.message-menu { |
|
||||
position: relative; |
|
||||
} |
|
||||
|
|
||||
.messages-dropdown { |
|
||||
&.mdl-menu { |
|
||||
width: 310px; |
|
||||
} |
|
||||
|
|
||||
.label { |
|
||||
color: $messages-dropdown-label-text-color; |
|
||||
} |
|
||||
|
|
||||
.mdl-list__item-primary-content { |
|
||||
font-weight: 400; |
|
||||
line-height: 18px; |
|
||||
|
|
||||
.mdl-list__item-avatar { |
|
||||
padding: ($list-avatar-size - $list-icon-size); |
|
||||
text-align: center; |
|
||||
|
|
||||
.material-icons { |
|
||||
vertical-align: top; |
|
||||
} |
|
||||
|
|
||||
.text { |
|
||||
font-size: 19px; |
|
||||
vertical-align: middle; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.mdl-list__item-sub-title { |
|
||||
font-weight: 100; |
|
||||
font-size: 12px; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
&.mdl-list { |
|
||||
.mdl-list__item { |
|
||||
@include typo-dropdown-menu-li; |
|
||||
|
|
||||
&:first-child { |
|
||||
color: $dropdown-menu-header-font-color; |
|
||||
} |
|
||||
|
|
||||
&:last-child { |
|
||||
padding-top: $list-min-padding/2; |
|
||||
padding-bottom: 0; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,19 +0,0 @@ |
|||||
import { Component, HostBinding } from '@angular/core'; |
|
||||
|
|
||||
import { MessageMenuService } from './message-menu.service'; |
|
||||
|
|
||||
@Component({ |
|
||||
selector: 'app-message-menu', |
|
||||
styleUrls: ['./message-menu.component.scss'], |
|
||||
templateUrl: './message-menu.component.html', |
|
||||
providers: [MessageMenuService], |
|
||||
}) |
|
||||
export class MessageMenuComponent { |
|
||||
@HostBinding('class.message-menu') private readonly messageMenu = true; |
|
||||
|
|
||||
public messages: object[]; |
|
||||
|
|
||||
constructor(messageMenuService: MessageMenuService) { |
|
||||
this.messages = messageMenuService.getMessages(); |
|
||||
} |
|
||||
} |
|
@ -1,44 +0,0 @@ |
|||||
import { Injectable } from '@angular/core'; |
|
||||
|
|
||||
@Injectable() |
|
||||
export class MessageMenuService { |
|
||||
public getMessages(): object[] { |
|
||||
return [ |
|
||||
{ |
|
||||
name: 'Alice', |
|
||||
type: 'Birthday Party', |
|
||||
time: 'just now', |
|
||||
icon: 'A', |
|
||||
color: 'primary', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'Vladimir', |
|
||||
type: 'Deployment', |
|
||||
time: 'just now', |
|
||||
icon: 'V', |
|
||||
color: 'primary', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'Mike', |
|
||||
type: 'No theme', |
|
||||
time: '5 min', |
|
||||
icon: 'M', |
|
||||
color: 'baby-blue', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'Darth', |
|
||||
type: 'Suggestion', |
|
||||
time: '23 hours', |
|
||||
icon: 'D', |
|
||||
color: 'cerulean', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'Don McDuket', |
|
||||
type: 'NEWS', |
|
||||
time: '30 Nov', |
|
||||
icon: 'D', |
|
||||
color: 'mint', |
|
||||
}, |
|
||||
]; |
|
||||
} |
|
||||
} |
|
@ -1,2 +0,0 @@ |
|||||
export { NotificationMenuComponent } from './notification-menu.component'; |
|
||||
export { NotificationMenuService } from './notification-menu.service'; |
|
@ -1,27 +0,0 @@ |
|||||
<div class="material-icons mdl-badge mdl-badge--overlap mdl-button--icon notification" id="notification" |
|
||||
[attr.data-badge]="notifications.length"> |
|
||||
notifications_none |
|
||||
</div> |
|
||||
<ul |
|
||||
class="mdl-menu mdl-list mdl-js-menu mdl-js-ripple-effect mdl-menu--bottom-right mdl-shadow--2dp notifications-dropdown" |
|
||||
for="notification"> |
|
||||
<li class="mdl-list__item"> |
|
||||
You have {{ notifications.length }} new notifications! |
|
||||
</li> |
|
||||
<li class="mdl-menu__item mdl-list__item list__item--border-top" |
|
||||
*ngFor="let item of notifications"> |
|
||||
<span class="mdl-list__item-primary-content"> |
|
||||
<span class="mdl-list__item-avatar" ngClass="background-color--{{ item.color }}"> |
|
||||
<i class="material-icons">{{ item.icon }}</i> |
|
||||
</span> |
|
||||
<span>{{ item.text }}</span> |
|
||||
</span> |
|
||||
<span class="mdl-list__item-secondary-content"> |
|
||||
<span class="label">{{ item.time }}</span> |
|
||||
</span> |
|
||||
</li> |
|
||||
|
|
||||
<li class="mdl-list__item list__item--border-top"> |
|
||||
<button href="#" class="mdl-button mdl-js-button mdl-js-ripple-effect">ALL NOTIFICATIONS</button> |
|
||||
</li> |
|
||||
</ul> |
|
@ -1,31 +0,0 @@ |
|||||
@import '~theme/helpers'; |
|
||||
|
|
||||
.notification-menu { |
|
||||
position: relative; |
|
||||
} |
|
||||
|
|
||||
.notifications-dropdown.mdl-list { |
|
||||
width: 310px; |
|
||||
|
|
||||
.mdl-list__item { |
|
||||
@include typo-dropdown-menu-li; |
|
||||
|
|
||||
&:first-child { |
|
||||
color: $dropdown-menu-header-font-color; |
|
||||
} |
|
||||
|
|
||||
.mdl-list__item-avatar { |
|
||||
padding: ($list-avatar-size - $list-icon-size); |
|
||||
text-align: center; |
|
||||
|
|
||||
.material-icons { |
|
||||
vertical-align: top; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
&:last-child { |
|
||||
padding-top: $list-min-padding/2; |
|
||||
padding-bottom: 0; |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,19 +0,0 @@ |
|||||
import { Component, HostBinding } from '@angular/core'; |
|
||||
|
|
||||
import { NotificationMenuService } from './notification-menu.service'; |
|
||||
|
|
||||
@Component({ |
|
||||
selector: 'app-notification-menu', |
|
||||
styleUrls: ['./notification-menu.component.scss'], |
|
||||
templateUrl: './notification-menu.component.html', |
|
||||
providers: [NotificationMenuService], |
|
||||
}) |
|
||||
export class NotificationMenuComponent { |
|
||||
@HostBinding('class.notification-menu') private readonly notificationMenu = true; |
|
||||
|
|
||||
public notifications: object[]; |
|
||||
|
|
||||
constructor(notificationMenuService: NotificationMenuService) { |
|
||||
this.notifications = notificationMenuService.getNotifications(); |
|
||||
} |
|
||||
} |
|
@ -1,30 +0,0 @@ |
|||||
import { Injectable } from '@angular/core'; |
|
||||
|
|
||||
@Injectable() |
|
||||
export class NotificationMenuService { |
|
||||
public getNotifications(): object[] { |
|
||||
return [ |
|
||||
{ |
|
||||
text: 'You have 3 new orders.', |
|
||||
time: 'just now', |
|
||||
icon: 'plus_one', |
|
||||
color: 'primary', |
|
||||
}, { |
|
||||
text: 'Database error', |
|
||||
time: '1 min', |
|
||||
icon: 'error_outline', |
|
||||
color: 'secondary', |
|
||||
}, { |
|
||||
text: 'The Death Star is built!', |
|
||||
time: '2 hours', |
|
||||
icon: 'new_releases', |
|
||||
color: 'primary', |
|
||||
}, { |
|
||||
text: 'You have 4 new mails.', |
|
||||
time: '5 days', |
|
||||
icon: 'mail_outline', |
|
||||
color: 'primary', |
|
||||
}, |
|
||||
]; |
|
||||
} |
|
||||
} |
|
@ -1 +0,0 @@ |
|||||
export { SidebarComponent } from './sidebar.component'; |
|
@ -1,3 +0,0 @@ |
|||||
.mdl-navigation base-menu-item:nth-child(2) i.material-icons { |
|
||||
transform: rotate(180deg); |
|
||||
} |
|
@ -1,15 +0,0 @@ |
|||||
import { Component, Input } from '@angular/core'; |
|
||||
|
|
||||
import { SidebarComponent as BaseSidebarComponent } from 'theme/components/sidebar'; |
|
||||
|
|
||||
@Component({ |
|
||||
selector: 'app-sidebar', |
|
||||
styleUrls: ['../../../theme/components/sidebar/sidebar.component.scss', './sidebar.component.scss'], |
|
||||
templateUrl: '../../../theme/components/sidebar/sidebar.component.html', |
|
||||
}) |
|
||||
export class SidebarComponent extends BaseSidebarComponent { |
|
||||
public title = 'Wireguard'; |
|
||||
public menu = [ |
|
||||
{ name: 'Dashboard', link: '/app/dashboard', icon: 'dashboard' }, |
|
||||
]; |
|
||||
} |
|
@ -1,11 +1,11 @@ |
|||||
import {Peer} from "./peer"; |
import { Peer } from './peer'; |
||||
|
|
||||
export interface User { |
export interface User { |
||||
full_name: string; |
full_name: string; |
||||
email: string; |
email: string; |
||||
role: string; |
role: string; |
||||
username: string; |
username: string; |
||||
access_token: string, |
access_token: string; |
||||
token_type: string, |
token_type: string; |
||||
|
|
||||
} |
} |
||||
|
@ -1,29 +1,78 @@ |
|||||
import { Component, OnInit } from '@angular/core'; |
import { Component, OnInit } from '@angular/core'; |
||||
import {Observable} from "rxjs"; |
import { Observable } from 'rxjs'; |
||||
import {BreakpointObserver, Breakpoints} from "@angular/cdk/layout"; |
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; |
||||
import {map, shareReplay} from "rxjs/operators"; |
import { map, shareReplay } from 'rxjs/operators'; |
||||
import {ConfigService} from "../../services/config.service"; |
import { ConfigService } from '../../services/config.service'; |
||||
|
import { AuthService } from '@services/*'; |
||||
|
import {OverlayContainer} from "@angular/cdk/overlay"; |
||||
|
import {DataService} from "../../services/data.service"; |
||||
|
import {CookieService} from "ngx-cookie-service"; |
||||
|
|
||||
@Component({ |
@Component({ |
||||
selector: 'app-layout', |
selector: 'app-layout', |
||||
templateUrl: './layout.component.html', |
templateUrl: './layout.component.html', |
||||
styleUrls: ['./layout.component.scss'] |
styleUrls: ['./layout.component.scss'], |
||||
}) |
}) |
||||
export class LayoutComponent implements OnInit { |
export class LayoutComponent implements OnInit { |
||||
|
|
||||
isHandset$: Observable<boolean> = this.breakpointObserver.observe(Breakpoints.Handset) |
isHandset$: Observable<boolean> = this.breakpointObserver.observe(Breakpoints.Handset) |
||||
.pipe( |
.pipe( |
||||
map(result => result.matches), |
map(result => result.matches), |
||||
shareReplay() |
shareReplay(), |
||||
); |
); |
||||
|
|
||||
menu: Array<{link: Array<string>, icon: string, text: string}> = [ |
menu: {link: string[], icon: string, text: string}[] = [ |
||||
{ link: ["/page/dashboard"], icon: "home", text: "Dashboard"} |
{ link: ['/page/dashboard'], icon: 'home', text: 'Dashboard' }, |
||||
]; |
]; |
||||
|
|
||||
constructor(private breakpointObserver: BreakpointObserver, public config: ConfigService) {} |
themes = [ |
||||
|
{theme: "indigo-pink", name: "Blue"}, |
||||
|
{theme: "deeppurple-amber", name: "Purple"}, |
||||
|
{theme: "pink-bluegrey", name: "Pink"}, |
||||
|
{theme: "purple-green", name: "Purple-Green"}, |
||||
|
]; |
||||
|
currentTheme = null; |
||||
|
darkMode = false; |
||||
|
|
||||
|
constructor( |
||||
|
private breakpointObserver: BreakpointObserver, |
||||
|
public config: ConfigService, |
||||
|
public auth: AuthService, |
||||
|
private comm: DataService, |
||||
|
private cookieService: CookieService |
||||
|
) {} |
||||
ngOnInit(): void { |
ngOnInit(): void { |
||||
console.log("Layout") |
console.log('Layout'); |
||||
|
|
||||
|
if(this.cookieService.check("currentTheme")){ |
||||
|
this.currentTheme = JSON.parse(this.cookieService.get("currentTheme")); |
||||
|
this.darkMode = (this.cookieService.get("darkMode") === 'true'); |
||||
|
}else { |
||||
|
this.currentTheme = { ... this.themes[0]} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
toggleDarkMode($event){ |
||||
|
$event.stopPropagation(); |
||||
|
this.darkMode = !this.darkMode; |
||||
|
this.cookieService.set("darkMode", String(this.darkMode)); |
||||
|
this.sendData(); |
||||
|
} |
||||
|
|
||||
|
setCurrentTheme(theme){ |
||||
|
this.cookieService.set("currentTheme", JSON.stringify(theme)); |
||||
|
this.currentTheme = theme; |
||||
|
this.sendData(); |
||||
|
} |
||||
|
|
||||
|
sendData(){ |
||||
|
const send = { |
||||
|
theme: this.currentTheme, |
||||
|
darkMode: this.darkMode |
||||
|
}; |
||||
|
|
||||
|
this.comm.emit('changeTheme', send); |
||||
} |
} |
||||
|
|
||||
} |
} |
||||
|
@ -1,30 +1,10 @@ |
|||||
import { Component, HostBinding } from '@angular/core'; |
import { Component } from '@angular/core'; |
||||
|
|
||||
|
|
||||
@Component({ |
@Component({ |
||||
selector: 'app-components', |
selector: 'app-components', |
||||
templateUrl: './components.component.html', |
template: '', |
||||
styleUrls: ['./components.component.scss'], |
styles: [''], |
||||
}) |
}) |
||||
export class ComponentsComponent { |
export class ComponentsComponent { |
||||
@HostBinding('class.mdl-grid') private readonly mdlGrid = true; |
|
||||
@HostBinding('class.ui-components') private readonly uiComponents = true; |
|
||||
|
|
||||
public data = [ |
|
||||
{ |
|
||||
name: 'Nathan Fillion', |
|
||||
description: 'Malcolm “Mal” Reynolds', |
|
||||
image: 'nathan-fillion.png', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'Gina Torres', |
|
||||
description: 'Zoe Alleyne Washburne', |
|
||||
image: 'gina-torres.png', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'Alan Tudyk', |
|
||||
description: 'Hoban “Wash” Washburne', |
|
||||
image: 'tudyk.png', |
|
||||
}, |
|
||||
]; |
|
||||
} |
} |
||||
|
@ -1,36 +1,51 @@ |
|||||
<ng-template #content let-modal> |
|
||||
<div class="modal-header"> |
|
||||
<h4 class="modal-title" id="modal-basic-title">{{title || "No 'title' defined" }}</h4> |
|
||||
|
|
||||
|
|
||||
<button type="button" class="close" aria-label="Close" (click)="modal.close('cancel')"> |
<div *ngIf="shown" fxLayout="row" |
||||
<span aria-hidden="true">×</span> |
fxLayout.xs="column" |
||||
</button> |
fxFlexFill |
||||
|
fxLayoutAlign="center center"> |
||||
|
<mat-card style="position: absolute; z-index: 10; left: 50%; top: 0; width: 50%;"> |
||||
|
<mat-card-header> |
||||
|
|
||||
</div> |
<mat-card-title class="card-container-left"> |
||||
<div class="modal-body"> |
{{title || "No 'title' defined" }} |
||||
<textarea *ngIf="area" readonly class=" |
</mat-card-title> |
||||
mdl-textfield--full-width |
<mat-card-title class="card-container-right"> |
||||
mdl-layout__header" style="min-height: 250px; height: 100%;">{{text || "No Text Defined" }}</textarea> |
|
||||
|
<mat-icon matTooltip="Close" (click)="cancel($event)" |
||||
|
class="app-material-icon-valign" style="cursor: pointer" |
||||
|
>close</mat-icon> |
||||
|
|
||||
|
|
||||
|
</mat-card-title> |
||||
|
|
||||
|
</mat-card-header> |
||||
|
|
||||
|
<mat-card-content> |
||||
|
|
||||
|
<textarea *ngIf="area" readonly class="full-width" style="min-height: 250px; height: 100%;">{{text || "No Text Defined" }}</textarea> |
||||
<div class="form-group" *ngIf="!area">{{text || "No Text Defined" }}</div> |
<div class="form-group" *ngIf="!area">{{text || "No Text Defined" }}</div> |
||||
<div style="text-align: center;" *ngIf="qrCode"> |
<div style="text-align: center;" *ngIf="qrCode"> |
||||
|
<hr> |
||||
<qrcode [qrdata]="text" [width]="256" [errorCorrectionLevel]="'M'"></qrcode> |
<qrcode [qrdata]="text" [width]="256" [errorCorrectionLevel]="'M'"></qrcode> |
||||
</div> |
</div> |
||||
|
|
||||
|
</mat-card-content> |
||||
|
|
||||
|
<mat-card-actions align="end"> |
||||
|
<div class="button-row"> |
||||
|
<button mat-flat-button color="primary" type="button" (click)="confirm($event)">Confirm</button> |
||||
|
<button mat-flat-button color="warn" type="button" (click)="cancel($event)">Cancel</button> |
||||
</div> |
</div> |
||||
<div class="modal-footer"> |
</mat-card-actions> |
||||
<button type="button" class="btn btn-dark" (click)="modal.close('confirm')">Confirm</button> |
|
||||
<button type="button" class="btn btn-dark" (click)="modal.close('cancel')">Cancel</button> |
|
||||
</div> |
|
||||
</ng-template> |
|
||||
|
|
||||
|
|
||||
|
</mat-card> |
||||
|
</div> |
||||
|
|
||||
<button |
<button |
||||
class="mdl-button mdl-button--icon mdl-js-button mdl-js-ripple-effect" |
mat-icon-button |
||||
data-placement="bottom" |
color="primary" |
||||
[title]="hover" |
[matTooltip]="hover" |
||||
(click)="open($event, content)"> |
(click)="open($event)"> |
||||
<i class="material-icons">{{icon}}</i> |
<i class="material-icons">{{icon}}</i> |
||||
</button> |
</button> |
||||
|
@ -0,0 +1 @@ |
|||||
|
|
@ -1,9 +1,15 @@ |
|||||
<div flex fxFill fxLayout="row" fxLayoutAlign="space-between" > |
<div |
||||
|
fxFlexFill |
||||
|
fxLayout="row" |
||||
|
fxLayout.lt-lg="column" |
||||
|
|
||||
|
style="padding: 10px;" fxLayoutGap="20px"> |
||||
|
|
||||
<div fxFlex="65"> |
<div fxFlex="65"> |
||||
<app-server [(server)]="servers[idx]" [(servers)]="servers" *ngFor="let server of servers; let idx = index"></app-server> |
<app-server [(server)]="servers[idx]" [(servers)]="servers" *ngFor="let server of servers; let idx = index"></app-server> |
||||
</div> |
</div> |
||||
|
|
||||
<div fxFlex="34"> |
<div fxFlex="35"> |
||||
<app-add-server [(servers)]="servers"></app-add-server> |
<app-add-server [(servers)]="servers"></app-add-server> |
||||
</div> |
</div> |
||||
|
|
@ -0,0 +1,38 @@ |
|||||
|
import { Component, OnInit } from '@angular/core'; |
||||
|
import { BreakpointObserver } from '@angular/cdk/layout'; |
||||
|
import { Server } from '../../interfaces/server'; |
||||
|
import { ServerService } from '../../services/server.service'; |
||||
|
import { Peer } from '../../interfaces/peer'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'dashboard', |
||||
|
templateUrl: './dashboard.component.html', |
||||
|
styleUrls: ['./dashboard.component.css'], |
||||
|
}) |
||||
|
export class DashboardComponent implements OnInit { |
||||
|
servers: Server[] = []; |
||||
|
|
||||
|
constructor(private breakpointObserver: BreakpointObserver, private serverAPI: ServerService) { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
ngOnInit(): void { |
||||
|
this.serverAPI.getServers() |
||||
|
.subscribe((servers: Server[]) => { |
||||
|
this.servers.push(...servers); |
||||
|
servers.forEach((server) => { |
||||
|
|
||||
|
this.serverAPI.serverStats(server).subscribe((stats: Peer[]) => { |
||||
|
stats.forEach(item => { |
||||
|
const peer = server.peers.find(x => x.public_key == item.public_key); |
||||
|
peer._stats = item; |
||||
|
}); |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,49 @@ |
|||||
|
import { NgModule } from '@angular/core'; |
||||
|
import { CommonModule } from '@angular/common'; |
||||
|
import { DashboardComponent } from './dashboard.component'; |
||||
|
import { MatGridListModule } from '@angular/material/grid-list'; |
||||
|
import { MatCardModule } from '@angular/material/card'; |
||||
|
import { MatMenuModule } from '@angular/material/menu'; |
||||
|
import { MatIconModule } from '@angular/material/icon'; |
||||
|
import { MatButtonModule } from '@angular/material/button'; |
||||
|
import { ServerComponent } from './server/server.component'; |
||||
|
import { MatExpansionModule } from '@angular/material/expansion'; |
||||
|
import { AddServerComponent } from './add-server/add-server.component'; |
||||
|
import { MatFormFieldModule } from '@angular/material/form-field'; |
||||
|
import { MatInputModule } from '@angular/material/input'; |
||||
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; |
||||
|
import { ComponentsModule } from '../components'; |
||||
|
import { FlexModule } from '@angular/flex-layout'; |
||||
|
import { MatTableModule } from '@angular/material/table'; |
||||
|
import { PeerComponent } from './peer/peer.component'; |
||||
|
import { QRCodeModule } from 'angularx-qrcode'; |
||||
|
import {MatTooltipModule} from "@angular/material/tooltip"; |
||||
|
|
||||
|
@NgModule({ |
||||
|
declarations: [ |
||||
|
DashboardComponent, |
||||
|
ServerComponent, |
||||
|
AddServerComponent, |
||||
|
PeerComponent, |
||||
|
], |
||||
|
imports: [ |
||||
|
CommonModule, |
||||
|
MatGridListModule, |
||||
|
MatCardModule, |
||||
|
MatMenuModule, |
||||
|
MatIconModule, |
||||
|
MatButtonModule, |
||||
|
MatExpansionModule, |
||||
|
MatFormFieldModule, |
||||
|
MatInputModule, |
||||
|
ReactiveFormsModule, |
||||
|
ComponentsModule, |
||||
|
FlexModule, |
||||
|
MatTableModule, |
||||
|
FormsModule, |
||||
|
QRCodeModule, |
||||
|
MatTooltipModule, |
||||
|
|
||||
|
], |
||||
|
}) |
||||
|
export class DashboardModule { } |
@ -0,0 +1,70 @@ |
|||||
|
import { Component, EventEmitter, Input, OnInit, ViewEncapsulation } from '@angular/core'; |
||||
|
import { ServerService } from '../../../services/server.service'; |
||||
|
import { Peer } from '../../../interfaces/peer'; |
||||
|
import { Server } from '../../../interfaces/server'; |
||||
|
import { FormControl, FormGroup } from '@angular/forms'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'app-peer', |
||||
|
templateUrl: './peer.component.html', |
||||
|
encapsulation: ViewEncapsulation.None, |
||||
|
styleUrls: ['./peer.component.scss'], |
||||
|
}) |
||||
|
export class PeerComponent implements OnInit { |
||||
|
|
||||
|
@Input('peer') peer: Peer; |
||||
|
@Input('server') server: Server; |
||||
|
@Input('selectedPeer') selectedPeer: Peer; |
||||
|
@Input('onEvent') editPeerEmitter: EventEmitter<any> = new EventEmitter<any>(); |
||||
|
|
||||
|
constructor(public serverAPI: ServerService) { } |
||||
|
|
||||
|
ngOnInit(): void { |
||||
|
|
||||
|
this.editPeerEmitter.subscribe((msg) => { |
||||
|
if (msg.peer !== this.peer) { |
||||
|
return; |
||||
|
} |
||||
|
if (msg.type === 'edit') { |
||||
|
this.edit(); |
||||
|
|
||||
|
} else if (msg.type == 'delete') { |
||||
|
this.delete(); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
edit() { |
||||
|
if (this.peer._edit) { |
||||
|
|
||||
|
// Submit the edit (True -> False)
|
||||
|
const idx = this.server.peers.indexOf(this.peer); |
||||
|
this.serverAPI.editPeer(this.peer).subscribe((newPeer) => { |
||||
|
Object.keys(newPeer).forEach(k => { |
||||
|
this.server.peers[idx][k] = newPeer[k]; |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
} else if (!this.peer._edit) { |
||||
|
this.peer._expand = true; |
||||
|
|
||||
|
// Open for edit. aka do nothing (False -> True
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
this.peer._edit = !this.peer._edit; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
delete() { |
||||
|
const idx = this.server.peers.indexOf(this.peer); |
||||
|
this.serverAPI.deletePeer(this.peer).subscribe((apiServer) => { |
||||
|
this.server.peers.splice(idx, 1); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,148 @@ |
|||||
|
|
||||
|
<mat-card class="dashboard-card"> |
||||
|
|
||||
|
<mat-card-header> |
||||
|
<mat-card-title class="card-container-left"> |
||||
|
|
||||
|
<mat-icon |
||||
|
class="app-material-icon-valign" |
||||
|
[class]="{'green': server.is_running, 'red': !server.is_running}" |
||||
|
matTooltip="Indicates if the server is online or offline" |
||||
|
>check_circle</mat-icon> |
||||
|
{{server.interface}} |
||||
|
|
||||
|
</mat-card-title> |
||||
|
<mat-card-title class="card-container-right"> |
||||
|
|
||||
|
<app-modal-confirm |
||||
|
[qrCode]="true" |
||||
|
[noConfirm]="false" |
||||
|
area="true" |
||||
|
icon="settings" |
||||
|
title="Configuration" |
||||
|
[text]="server.configuration" |
||||
|
hover="Show config for {{server.interface}}"> |
||||
|
</app-modal-confirm> |
||||
|
|
||||
|
<app-modal-confirm |
||||
|
[noConfirm]="true" |
||||
|
(onConfirm)="addPeer()" |
||||
|
icon="person_add" |
||||
|
hover="Add peer to {{server.interface}}"> |
||||
|
</app-modal-confirm> |
||||
|
|
||||
|
<app-modal-confirm |
||||
|
*ngIf="!server.is_running" |
||||
|
[noConfirm]="true" |
||||
|
(onConfirm)="start()" |
||||
|
icon="play_arrow" |
||||
|
hover="Start {{server.interface}}"> |
||||
|
</app-modal-confirm> |
||||
|
|
||||
|
<app-modal-confirm |
||||
|
*ngIf="server.is_running" |
||||
|
[noConfirm]="false" |
||||
|
(onConfirm)="stop()" |
||||
|
title="Stop server {{server.interface}}?" |
||||
|
text="Are you sure you want to stop this server? This may cause you or your clients to lose connection to the server." |
||||
|
icon="stop" |
||||
|
hover="Stop {{server.interface}}"> |
||||
|
</app-modal-confirm> |
||||
|
|
||||
|
<app-modal-confirm |
||||
|
[noConfirm]="false" |
||||
|
(onConfirm)="restart()" |
||||
|
title="Restart server {{server.interface}}?" |
||||
|
text="Are you sure you want to restart this server? This may cause you or your clients to lose connection to the server." |
||||
|
icon="autorenew" |
||||
|
hover="Restart {{server.interface}}"> |
||||
|
</app-modal-confirm> |
||||
|
|
||||
|
<app-modal-confirm |
||||
|
[noConfirm]="true" |
||||
|
(onConfirm)="edit()" |
||||
|
icon="edit" |
||||
|
hover="Edit {{server.interface}}"> |
||||
|
</app-modal-confirm> |
||||
|
|
||||
|
<app-modal-confirm |
||||
|
(onConfirm)="delete()" |
||||
|
title="Delete {{server.interface}}" |
||||
|
text="Are you sure you want to delete {{server.interface}}" |
||||
|
icon="delete" |
||||
|
hover="Delete {{server.interface}}"> |
||||
|
</app-modal-confirm> |
||||
|
|
||||
|
</mat-card-title> |
||||
|
|
||||
|
<mat-card-subtitle style="margin-top: 2px;">Endpoint: <b>{{server.endpoint}}:{{server.listen_port}}</b> - Address Space: <b>{{server.address}}</b></mat-card-subtitle> |
||||
|
</mat-card-header> |
||||
|
|
||||
|
<mat-card-content class="dashboard-card-content"> |
||||
|
|
||||
|
|
||||
|
<table class="table"> |
||||
|
<thead> |
||||
|
<tr> |
||||
|
<th>Name</th> |
||||
|
<th>Address</th> |
||||
|
<th>Public-Key</th> |
||||
|
<th>Total tx/rx</th> |
||||
|
<th>Handshake</th> |
||||
|
<th>Manage</th> |
||||
|
</tr> |
||||
|
</thead> |
||||
|
|
||||
|
|
||||
|
<tbody> |
||||
|
<ng-container *ngFor="let peer of server.peers; let idx = index;" (click)="selectedPeer = (selectedPeer != peer)? peer : null"> |
||||
|
|
||||
|
<tr (click)="openPeer(peer)"> |
||||
|
<td> |
||||
|
<i *ngIf="{ a: (peer._stats && peer._stats.handshake && (peer._stats.handshake.split(' ')[1] === 'seconds' || pInt(peer._stats.handshake.split(' ')[0]) < 3))}; let isRunning"class="material-icons table-icon app-material-icon-valign" [ngClass]="{'green': isRunning.a, 'red': !isRunning.a}">check_circle</i> |
||||
|
{{peer.name}}</td> |
||||
|
<td>{{peer.address}}</td> |
||||
|
<td>{{peer.public_key}}</td> |
||||
|
<td>{{peer._stats?.tx || '0'}}/{{peer._stats?.rx || '0'}}</td> |
||||
|
<td> {{peer._stats?.handshake || 'N/A'}}</td> |
||||
|
<td> |
||||
|
|
||||
|
<!-- Edit buttons --> |
||||
|
<app-modal-confirm |
||||
|
[noConfirm]="true" |
||||
|
(onConfirm)="this.editPeerEmitter.emit({type: 'edit', peer: peer}); selectedPeer=peer" |
||||
|
icon="edit" |
||||
|
hover="Edit {{peer.name}}"> |
||||
|
</app-modal-confirm> |
||||
|
|
||||
|
<app-modal-confirm |
||||
|
[noConfirm]="false" |
||||
|
(onConfirm)="this.editPeerEmitter.emit({type: 'delete', peer: peer});" |
||||
|
text="Are you sure you want to delete {{peer.name}} ({{peer.public_key}})?" |
||||
|
title="Delete {{peer.name}}" |
||||
|
icon="delete" |
||||
|
hover="Delete {{peer.name}} ({{peer.public_key}})"> |
||||
|
</app-modal-confirm> |
||||
|
|
||||
|
|
||||
|
|
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr [hidden]="peer !== selectedPeer"> |
||||
|
<td colspan="6"> |
||||
|
<app-peer [onEvent]="this.editPeerEmitter" [(peer)]="server.peers[idx]" [(server)]="server"></app-peer> |
||||
|
</td> |
||||
|
</tr> |
||||
|
|
||||
|
|
||||
|
</ng-container> |
||||
|
</tbody> |
||||
|
</table> |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
</mat-card-content> |
||||
|
<mat-card-actions> |
||||
|
</mat-card-actions> |
||||
|
</mat-card> |
@ -0,0 +1,13 @@ |
|||||
|
|
||||
|
table { |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
:host { |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.table-icon{ |
||||
|
font-size: 20px; |
||||
|
} |
||||
|
|
@ -0,0 +1,75 @@ |
|||||
|
import { Component, EventEmitter, Input, OnInit, ViewEncapsulation } from '@angular/core'; |
||||
|
import { Server } from '../../../interfaces/server'; |
||||
|
import { ServerService } from '../../../services/server.service'; |
||||
|
import { DataService } from '../../../services/data.service'; |
||||
|
import { Peer } from '../../../interfaces/peer'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'app-server', |
||||
|
templateUrl: './server.component.html', |
||||
|
|
||||
|
styleUrls: ['./server.component.scss', '../dashboard.component.css'], |
||||
|
}) |
||||
|
export class ServerComponent implements OnInit { |
||||
|
@Input() server: Server; |
||||
|
@Input() servers: Server[]; |
||||
|
public editPeerEmitter: EventEmitter<any> = new EventEmitter<any>(); |
||||
|
|
||||
|
selectedPeer: Peer | null; |
||||
|
|
||||
|
constructor(private serverAPI: ServerService, private comm: DataService) { } |
||||
|
|
||||
|
ngOnInit(): void { |
||||
|
console.log('Server'); |
||||
|
} |
||||
|
|
||||
|
edit() { |
||||
|
|
||||
|
this.comm.emit('server-edit', this.server); |
||||
|
} |
||||
|
|
||||
|
stop() { |
||||
|
this.serverAPI.stopServer(this.server).subscribe((apiServer) => { |
||||
|
this.server.is_running = apiServer.is_running; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
start() { |
||||
|
this.serverAPI.startServer(this.server).subscribe((apiServer) => { |
||||
|
this.server.is_running = apiServer.is_running; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
addPeer() { |
||||
|
this.serverAPI.addPeer({ |
||||
|
server_interface: this.server.interface |
||||
|
}).subscribe((peer) => { |
||||
|
this.server.peers.push(peer); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
restart() { |
||||
|
this.serverAPI.restartServer(this.server).subscribe((apiServer) => { |
||||
|
this.server.is_running = apiServer.is_running; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
delete() { |
||||
|
const index = this.servers.indexOf(this.server); |
||||
|
this.serverAPI.deleteServer(this.server).subscribe((apiServer) => { |
||||
|
this.servers.splice(index, 1); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
openPeer(peer: Peer) { |
||||
|
if (this.selectedPeer == peer) { |
||||
|
this.selectedPeer = null; |
||||
|
return; |
||||
|
} |
||||
|
this.selectedPeer = peer; |
||||
|
this.editPeerEmitter.emit({ type: 'open', peer }); |
||||
|
} |
||||
|
pInt(string: string) { |
||||
|
return parseInt(string); |
||||
|
} |
||||
|
} |
@ -1,18 +0,0 @@ |
|||||
.grid-container { |
|
||||
margin: 20px; |
|
||||
} |
|
||||
|
|
||||
.dashboard-card { |
|
||||
position: absolute; |
|
||||
top: 15px; |
|
||||
left: 15px; |
|
||||
right: 15px; |
|
||||
bottom: 15px; |
|
||||
} |
|
||||
|
|
||||
.more-button { |
|
||||
position: absolute; |
|
||||
top: 5px; |
|
||||
right: 10px; |
|
||||
} |
|
||||
|
|
@ -1,40 +0,0 @@ |
|||||
import { LayoutModule } from '@angular/cdk/layout'; |
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations'; |
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; |
|
||||
import { MatButtonModule } from '@angular/material/button'; |
|
||||
import { MatCardModule } from '@angular/material/card'; |
|
||||
import { MatGridListModule } from '@angular/material/grid-list'; |
|
||||
import { MatIconModule } from '@angular/material/icon'; |
|
||||
import { MatMenuModule } from '@angular/material/menu'; |
|
||||
|
|
||||
import { Dashboard2Component } from './dashboard2.component'; |
|
||||
|
|
||||
describe('Dashboard2Component', () => { |
|
||||
let component: Dashboard2Component; |
|
||||
let fixture: ComponentFixture<Dashboard2Component>; |
|
||||
|
|
||||
beforeEach(async(() => { |
|
||||
TestBed.configureTestingModule({ |
|
||||
declarations: [Dashboard2Component], |
|
||||
imports: [ |
|
||||
NoopAnimationsModule, |
|
||||
LayoutModule, |
|
||||
MatButtonModule, |
|
||||
MatCardModule, |
|
||||
MatGridListModule, |
|
||||
MatIconModule, |
|
||||
MatMenuModule, |
|
||||
] |
|
||||
}).compileComponents(); |
|
||||
})); |
|
||||
|
|
||||
beforeEach(() => { |
|
||||
fixture = TestBed.createComponent(Dashboard2Component); |
|
||||
component = fixture.componentInstance; |
|
||||
fixture.detectChanges(); |
|
||||
}); |
|
||||
|
|
||||
it('should compile', () => { |
|
||||
expect(component).toBeTruthy(); |
|
||||
}); |
|
||||
}); |
|
@ -1,44 +0,0 @@ |
|||||
import {Component, OnInit} from '@angular/core'; |
|
||||
import { map } from 'rxjs/operators'; |
|
||||
import { Breakpoints, BreakpointObserver } from '@angular/cdk/layout'; |
|
||||
import {Server} from "../../interfaces/server"; |
|
||||
import {ServerService} from "../../services/server.service"; |
|
||||
import {Peer} from "../../interfaces/peer"; |
|
||||
|
|
||||
@Component({ |
|
||||
selector: 'dashboard2', |
|
||||
templateUrl: './dashboard2.component.html', |
|
||||
styleUrls: ['./dashboard2.component.css'] |
|
||||
}) |
|
||||
export class Dashboard2Component implements OnInit |
|
||||
{ |
|
||||
servers: Array<Server> = []; |
|
||||
|
|
||||
constructor(private breakpointObserver: BreakpointObserver, private serverAPI: ServerService) { |
|
||||
|
|
||||
} |
|
||||
|
|
||||
|
|
||||
ngOnInit(): void { |
|
||||
this.serverAPI.getServers() |
|
||||
.subscribe( (servers: Array<Server>) => { |
|
||||
this.servers.push(...servers); |
|
||||
servers.forEach((server) => { |
|
||||
|
|
||||
this.serverAPI.serverStats(server).subscribe((stats: Peer[]) => { |
|
||||
stats.forEach( item => { |
|
||||
const peer = server.peers.find(x => x.public_key == item.public_key); |
|
||||
peer._stats = item |
|
||||
}); |
|
||||
|
|
||||
|
|
||||
}); |
|
||||
|
|
||||
|
|
||||
}); |
|
||||
|
|
||||
|
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
} |
|
@ -1,49 +0,0 @@ |
|||||
import { NgModule } from '@angular/core'; |
|
||||
import { CommonModule } from '@angular/common'; |
|
||||
import {Dashboard2Component} from "./dashboard2.component"; |
|
||||
import {MatGridListModule} from "@angular/material/grid-list"; |
|
||||
import {MatCardModule} from "@angular/material/card"; |
|
||||
import {MatMenuModule} from "@angular/material/menu"; |
|
||||
import {MatIconModule} from "@angular/material/icon"; |
|
||||
import {MatButtonModule} from "@angular/material/button"; |
|
||||
import {ServerComponent} from "./server/server.component"; |
|
||||
import {MatExpansionModule} from "@angular/material/expansion"; |
|
||||
import {AddServerComponent} from "./add-server/add-server.component"; |
|
||||
import {MatFormFieldModule} from "@angular/material/form-field"; |
|
||||
import {MatInputModule} from "@angular/material/input"; |
|
||||
import {FormsModule, ReactiveFormsModule} from "@angular/forms"; |
|
||||
import {ComponentsModule} from "../components"; |
|
||||
import {FlexModule} from "@angular/flex-layout"; |
|
||||
import {MatTableModule} from "@angular/material/table"; |
|
||||
import {PeerComponent} from "./peer/peer.component"; |
|
||||
import {QRCodeModule} from "angularx-qrcode"; |
|
||||
|
|
||||
|
|
||||
|
|
||||
@NgModule({ |
|
||||
declarations: [ |
|
||||
Dashboard2Component, |
|
||||
ServerComponent, |
|
||||
AddServerComponent, |
|
||||
PeerComponent |
|
||||
], |
|
||||
imports: [ |
|
||||
CommonModule, |
|
||||
MatGridListModule, |
|
||||
MatCardModule, |
|
||||
MatMenuModule, |
|
||||
MatIconModule, |
|
||||
MatButtonModule, |
|
||||
MatExpansionModule, |
|
||||
MatFormFieldModule, |
|
||||
MatInputModule, |
|
||||
ReactiveFormsModule, |
|
||||
ComponentsModule, |
|
||||
FlexModule, |
|
||||
MatTableModule, |
|
||||
FormsModule, |
|
||||
QRCodeModule, |
|
||||
|
|
||||
] |
|
||||
}) |
|
||||
export class Dashboard2Module { } |
|
@ -1,84 +0,0 @@ |
|||||
import {Component, EventEmitter, Input, OnInit, ViewEncapsulation} from '@angular/core'; |
|
||||
import {ServerService} from "../../../services/server.service"; |
|
||||
import {Peer} from "../../../interfaces/peer"; |
|
||||
import {Server} from "../../../interfaces/server"; |
|
||||
import {FormControl, FormGroup} from "@angular/forms"; |
|
||||
|
|
||||
@Component({ |
|
||||
selector: 'app-peer', |
|
||||
templateUrl: './peer.component.html', |
|
||||
encapsulation: ViewEncapsulation.None, |
|
||||
styleUrls: ['./peer.component.scss'], |
|
||||
}) |
|
||||
export class PeerComponent implements OnInit { |
|
||||
|
|
||||
@Input("peer") peer: Peer; |
|
||||
@Input("server") server: Server; |
|
||||
@Input("selectedPeer") selectedPeer: Peer; |
|
||||
@Input("onEvent") editPeerEmitter: EventEmitter<any> = new EventEmitter<any>(); |
|
||||
|
|
||||
|
|
||||
|
|
||||
config: string = "Loading..."; |
|
||||
|
|
||||
|
|
||||
constructor(public serverAPI: ServerService) { } |
|
||||
|
|
||||
ngOnInit(): void { |
|
||||
|
|
||||
this.editPeerEmitter.subscribe( (msg) => { |
|
||||
if(msg.peer !== this.peer){ |
|
||||
return; |
|
||||
} |
|
||||
if(msg.type === "edit"){ |
|
||||
this.edit(); |
|
||||
|
|
||||
}else if(msg.type == "delete"){ |
|
||||
this.delete(); |
|
||||
}else if(msg.type == "open"){ |
|
||||
this.fetchConfig(); |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
} |
|
||||
|
|
||||
edit(){ |
|
||||
if(this.peer._edit) { |
|
||||
|
|
||||
// Submit the edit (True -> False)
|
|
||||
const idx = this.server.peers.indexOf(this.peer); |
|
||||
this.serverAPI.editPeer(this.peer).subscribe((newPeer) => { |
|
||||
Object.keys(newPeer).forEach(k => { |
|
||||
this.server.peers[idx][k] = newPeer[k]; |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
} else if(!this.peer._edit) { |
|
||||
this.peer._expand = true; |
|
||||
|
|
||||
// Open for edit. aka do nothing (False -> True
|
|
||||
|
|
||||
} |
|
||||
|
|
||||
this.peer._edit = !this.peer._edit; |
|
||||
|
|
||||
|
|
||||
} |
|
||||
|
|
||||
delete(){ |
|
||||
const idx = this.server.peers.indexOf(this.peer); |
|
||||
this.serverAPI.deletePeer(this.peer).subscribe((apiServer) => { |
|
||||
this.server.peers.splice(idx, 1); |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
fetchConfig() { |
|
||||
this.serverAPI.peerConfig(this.peer).subscribe((config: any) => { |
|
||||
this.config = config.config |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
pInt(string: string) { |
|
||||
return parseInt(string) |
|
||||
} |
|
||||
} |
|
@ -1,248 +0,0 @@ |
|||||
|
|
||||
<mat-card class="dashboard-card"> |
|
||||
<mat-card-header class="server-card-header"> |
|
||||
<mat-card-title> |
|
||||
<span>{{server.interface}}</span> |
|
||||
|
|
||||
<!-- This fills the remaining space of the current row --> |
|
||||
<span class="fill-remaining-space"></span> |
|
||||
|
|
||||
<i class="material-icons" [ngClass]="{'text-success': server.is_running, 'text-danger': !server.is_running}">check_circle</i> |
|
||||
|
|
||||
<app-modal-confirm |
|
||||
[qrCode]="true" |
|
||||
[noConfirm]="false" |
|
||||
area="true" |
|
||||
icon="settings" |
|
||||
title="Configuration" |
|
||||
[text]="serverConfig" |
|
||||
hover="Show config for {{server.interface}}"> |
|
||||
</app-modal-confirm> |
|
||||
|
|
||||
<span> |
|
||||
|
|
||||
<app-modal-confirm |
|
||||
[noConfirm]="true" |
|
||||
(onConfirm)="addPeer()" |
|
||||
icon="person_add" |
|
||||
hover="Add peer to {{server.interface}}"> |
|
||||
</app-modal-confirm> |
|
||||
|
|
||||
<app-modal-confirm |
|
||||
*ngIf="!server.is_running" |
|
||||
[noConfirm]="true" |
|
||||
(onConfirm)="start()" |
|
||||
icon="play_arrow" |
|
||||
hover="Start {{server.interface}}"> |
|
||||
</app-modal-confirm> |
|
||||
|
|
||||
<app-modal-confirm |
|
||||
*ngIf="server.is_running" |
|
||||
[noConfirm]="false" |
|
||||
(onConfirm)="stop()" |
|
||||
title="Stop server {{server.interface}}?" |
|
||||
text="Are you sure you want to stop this server? This may cause you or your clients to lose connection to the server." |
|
||||
icon="stop" |
|
||||
hover="Stop {{server.interface}}"> |
|
||||
</app-modal-confirm> |
|
||||
|
|
||||
<app-modal-confirm |
|
||||
[noConfirm]="false" |
|
||||
(onConfirm)="restart()" |
|
||||
title="Restart server {{server.interface}}?" |
|
||||
text="Are you sure you want to restart this server? This may cause you or your clients to lose connection to the server." |
|
||||
icon="autorenew" |
|
||||
hover="Restart {{server.interface}}"> |
|
||||
</app-modal-confirm> |
|
||||
|
|
||||
<app-modal-confirm |
|
||||
[noConfirm]="true" |
|
||||
(onConfirm)="edit()" |
|
||||
icon="edit" |
|
||||
hover="Edit {{server.interface}}"> |
|
||||
</app-modal-confirm> |
|
||||
|
|
||||
<app-modal-confirm |
|
||||
(onConfirm)="delete()" |
|
||||
title="Delete {{server.interface}}" |
|
||||
text="Are you sure you want to delete {{server.interface}}" |
|
||||
icon="delete" |
|
||||
hover="Delete {{server.interface}}"> |
|
||||
</app-modal-confirm> |
|
||||
</span> |
|
||||
|
|
||||
|
|
||||
|
|
||||
</mat-card-title> |
|
||||
<mat-card-subtitle>{{server.address}} @ {{server.endpoint}}</mat-card-subtitle> |
|
||||
</mat-card-header> |
|
||||
|
|
||||
<mat-card-content class="dashboard-card-content"> |
|
||||
|
|
||||
|
|
||||
<table class="table"> |
|
||||
<thead> |
|
||||
<tr> |
|
||||
<th>Name</th> |
|
||||
<th>Address</th> |
|
||||
<th>Public-Key</th> |
|
||||
<th>Total tx/rx</th> |
|
||||
<th>Handshake</th> |
|
||||
<th>Manage</th> |
|
||||
</tr> |
|
||||
</thead> |
|
||||
|
|
||||
|
|
||||
<tbody> |
|
||||
<ng-container *ngFor="let peer of server.peers; let idx = index;" (click)="selectedPeer = (selectedPeer != peer)? peer : null"> |
|
||||
|
|
||||
<tr (click)="openPeer(peer)"> |
|
||||
<td>{{peer.name}}</td> |
|
||||
<td>{{peer.address}}</td> |
|
||||
<td>{{peer.public_key}}</td> |
|
||||
<td>{{peer._stats?.tx || '0'}}/{{peer._stats?.rx || '0'}}</td> |
|
||||
<td>{{peer._stats?.handshake || 'N/A'}}</td> |
|
||||
<td> |
|
||||
|
|
||||
<!-- Edit buttons --> |
|
||||
<app-modal-confirm |
|
||||
[noConfirm]="true" |
|
||||
(onConfirm)="this.editPeerEmitter.emit({type: 'edit', peer: peer})" |
|
||||
icon="edit" |
|
||||
hover="Edit {{peer.name}}"> |
|
||||
</app-modal-confirm> |
|
||||
|
|
||||
<app-modal-confirm |
|
||||
[noConfirm]="false" |
|
||||
(onConfirm)="this.editPeerEmitter.emit({type: 'delete', peer: peer});" |
|
||||
text="Are you sure you want to delete {{peer.name}} ({{peer.public_key}})?" |
|
||||
title="Delete {{peer.name}}" |
|
||||
icon="delete" |
|
||||
hover="Delete {{peer.name}} ({{peer.public_key}})"> |
|
||||
</app-modal-confirm> |
|
||||
|
|
||||
|
|
||||
|
|
||||
</td> |
|
||||
</tr> |
|
||||
<tr [hidden]="peer !== selectedPeer"> |
|
||||
<td colspan="6"> |
|
||||
<app-peer [onEvent]="this.editPeerEmitter" [(peer)]="server.peers[idx]" [(server)]="server"></app-peer> |
|
||||
</td> |
|
||||
</tr> |
|
||||
|
|
||||
|
|
||||
</ng-container> |
|
||||
</tbody> |
|
||||
</table> |
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
</mat-card-content> |
|
||||
<mat-card-actions> |
|
||||
</mat-card-actions> |
|
||||
</mat-card> |
|
||||
|
|
||||
|
|
||||
|
|
||||
<!-- |
|
||||
<mat-card class="dashboard-card"> |
|
||||
<mat-card-content class="dashboard-card-content"> |
|
||||
*Server* |
|
||||
<ng-container > |
|
||||
|
|
||||
|
|
||||
</ng-container> |
|
||||
</mat-card-content> |
|
||||
</mat-card> |
|
||||
|
|
||||
|
|
||||
--> |
|
||||
|
|
||||
<!-- |
|
||||
<div class=" mdl-card mdl-shadow--2dp"> |
|
||||
<div class="mdl-card__title mdl-card--border"> |
|
||||
<h2 class="mdl-card__title-text">{{server.interface}}</h2> |
|
||||
<span style="width:20px;"></span> |
|
||||
|
|
||||
|
|
||||
|
|
||||
</div> |
|
||||
|
|
||||
<div class="mdl-card__actions"> |
|
||||
|
|
||||
<div class="mdl-grid peer-item-header"> |
|
||||
<div class="mdl-cell--2-col mdl-cell--12-col-phone">Name</div> |
|
||||
<div class="mdl-cell--2-col mdl-cell--12-col-phone">Address</div> |
|
||||
<div class="mdl-cell--3-col mdl-cell--12-col-phone">Public-Key</div> |
|
||||
<div class="mdl-cell--2-col mdl-cell--12-col-phone">Total tx/rx</div> |
|
||||
<div class="mdl-cell--2-col mdl-cell--12-col-phone">Handshake</div> |
|
||||
<div class="mdl-cell--2-col mdl-cell--12-col-phone">Manage</div> |
|
||||
</div> |
|
||||
|
|
||||
|
|
||||
<div style="cursor: pointer;" *ngFor="let peer of server.peers; let idx = index;" > |
|
||||
<app-peer [(peer)]="server.peers[idx]" [(server)]="server"></app-peer> |
|
||||
</div> |
|
||||
|
|
||||
</div> |
|
||||
|
|
||||
<div class="mdl-card__supporting-text"> |
|
||||
</div> |
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
<div class="mdl-card__menu"> |
|
||||
|
|
||||
<app-modal-confirm |
|
||||
[noConfirm]="true" |
|
||||
(onConfirm)="addPeer()" |
|
||||
icon="person_add" |
|
||||
hover="Add peer to {{server.interface}}"> |
|
||||
</app-modal-confirm> |
|
||||
|
|
||||
<app-modal-confirm |
|
||||
*ngIf="!server.is_running" |
|
||||
[noConfirm]="true" |
|
||||
(onConfirm)="start()" |
|
||||
icon="play_arrow" |
|
||||
hover="Start {{server.interface}}"> |
|
||||
</app-modal-confirm> |
|
||||
|
|
||||
<app-modal-confirm |
|
||||
*ngIf="server.is_running" |
|
||||
[noConfirm]="false" |
|
||||
(onConfirm)="stop()" |
|
||||
title="Stop server {{server.interface}}?" |
|
||||
text="Are you sure you want to stop this server? This may cause you or your clients to lose connection to the server." |
|
||||
icon="stop" |
|
||||
hover="Stop {{server.interface}}"> |
|
||||
</app-modal-confirm> |
|
||||
|
|
||||
<app-modal-confirm |
|
||||
[noConfirm]="false" |
|
||||
(onConfirm)="restart()" |
|
||||
title="Restart server {{server.interface}}?" |
|
||||
text="Are you sure you want to restart this server? This may cause you or your clients to lose connection to the server." |
|
||||
icon="autorenew" |
|
||||
hover="Restart {{server.interface}}"> |
|
||||
</app-modal-confirm> |
|
||||
|
|
||||
<app-modal-confirm |
|
||||
[noConfirm]="true" |
|
||||
(onConfirm)="edit()" |
|
||||
icon="edit" |
|
||||
hover="Edit {{server.interface}}"> |
|
||||
</app-modal-confirm> |
|
||||
|
|
||||
<app-modal-confirm |
|
||||
(onConfirm)="delete()" |
|
||||
title="Delete {{server.interface}}" |
|
||||
text="Are you sure you want to delete {{server.interface}}" |
|
||||
icon="delete" |
|
||||
hover="Delete {{server.interface}}"> |
|
||||
</app-modal-confirm> |
|
||||
</div> |
|
||||
</div>--> |
|
@ -1,28 +0,0 @@ |
|||||
|
|
||||
table { |
|
||||
width: 100%; |
|
||||
} |
|
||||
|
|
||||
tr.example-detail-row { |
|
||||
height: 0 !important; |
|
||||
} |
|
||||
|
|
||||
tr.example-element-row:not(.example-expanded-row):hover { |
|
||||
background: whitesmoke; |
|
||||
} |
|
||||
|
|
||||
tr.example-element-row:not(.example-expanded-row):active { |
|
||||
background: #efefef; |
|
||||
} |
|
||||
|
|
||||
.example-element-row td { |
|
||||
border-bottom-width: 0; |
|
||||
} |
|
||||
|
|
||||
.example-element-detail { |
|
||||
overflow: hidden; |
|
||||
display: flex; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
|
|
@ -1,77 +0,0 @@ |
|||||
import {Component, EventEmitter, Input, OnInit, ViewEncapsulation} from '@angular/core'; |
|
||||
import {Server} from "../../../interfaces/server"; |
|
||||
import {ServerService} from "../../../services/server.service"; |
|
||||
import {DataService} from "../../../services/data.service"; |
|
||||
import {Peer} from "../../../interfaces/peer"; |
|
||||
|
|
||||
|
|
||||
@Component({ |
|
||||
selector: 'app-server', |
|
||||
templateUrl: './server.component.html', |
|
||||
encapsulation: ViewEncapsulation.None, |
|
||||
styleUrls: ['./server.component.scss', '../dashboard2.component.css'], |
|
||||
}) |
|
||||
export class ServerComponent implements OnInit { |
|
||||
@Input() server: Server; |
|
||||
@Input() servers: Array<Server>; |
|
||||
public editPeerEmitter: EventEmitter<any> = new EventEmitter<any>(); |
|
||||
|
|
||||
serverConfig: string; |
|
||||
|
|
||||
selectedPeer: Peer | null; |
|
||||
|
|
||||
constructor(private serverAPI: ServerService, private comm: DataService) { } |
|
||||
|
|
||||
ngOnInit(): void { |
|
||||
console.log("Server"); |
|
||||
|
|
||||
this.serverAPI.serverConfig(this.server).subscribe((x: any) => this.serverConfig = x.config) |
|
||||
|
|
||||
} |
|
||||
|
|
||||
edit(){ |
|
||||
|
|
||||
this.comm.emit('server-edit', this.server); |
|
||||
} |
|
||||
|
|
||||
stop() { |
|
||||
this.serverAPI.stopServer(this.server).subscribe((apiServer) => { |
|
||||
this.server.is_running = apiServer.is_running |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
start() { |
|
||||
this.serverAPI.startServer(this.server).subscribe((apiServer) => { |
|
||||
this.server.is_running = apiServer.is_running |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
addPeer() { |
|
||||
this.serverAPI.addPeer(this.server).subscribe((peer) => { |
|
||||
this.server.peers.push(peer) |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
restart() { |
|
||||
this.serverAPI.restartServer(this.server).subscribe((apiServer) => { |
|
||||
this.server.is_running = apiServer.is_running |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
|
|
||||
delete() { |
|
||||
const index = this.servers.indexOf(this.server); |
|
||||
this.serverAPI.deleteServer(this.server).subscribe((apiServer) => { |
|
||||
this.servers.splice(index, 1); |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
openPeer(peer: Peer) { |
|
||||
if(this.selectedPeer == peer){ |
|
||||
this.selectedPeer = null; |
|
||||
return |
|
||||
} |
|
||||
this.selectedPeer = peer; |
|
||||
this.editPeerEmitter.emit({type: 'open', peer: peer}); |
|
||||
} |
|
||||
} |
|
@ -1,32 +1,29 @@ |
|||||
import { NgModule } from '@angular/core'; |
import { NgModule } from '@angular/core'; |
||||
import { Routes, RouterModule } from '@angular/router'; |
import { Routes, RouterModule } from '@angular/router'; |
||||
import {Dashboard2Component} from "./dashboard2/dashboard2.component"; |
import { DashboardComponent } from './dashboard/dashboard.component'; |
||||
import {LayoutComponent} from "../layout/layout/layout.component"; |
import { LayoutComponent } from '../layout/layout/layout.component'; |
||||
import {ErrorComponent} from "./error"; |
import { ErrorComponent } from './error'; |
||||
import {LoginComponent} from "./user/login/login.component"; |
import { LoginComponent } from './user/login/login.component'; |
||||
import {AuthGuard} from "@services/*"; |
import { AuthGuard } from '@services/*'; |
||||
|
import { EditComponent } from './user/edit/edit.component'; |
||||
|
|
||||
|
|
||||
|
|
||||
const routes: Routes = [ |
const routes: Routes = [ |
||||
{ path: '', component: LayoutComponent, children: |
{ path: '', component: LayoutComponent, children: |
||||
[ |
[ |
||||
//{ path: 'dashboard', component: DashboardComponent, pathMatch: 'full', canActivate: [AuthGuard]},
|
{ path: 'dashboard', component: DashboardComponent, pathMatch: 'full', canActivate: [AuthGuard] }, |
||||
{ path: 'dashboard', component: Dashboard2Component, pathMatch: 'full', canActivate: [AuthGuard]}, |
|
||||
{ path: '404', component: ErrorComponent, pathMatch: 'full' }, |
{ path: '404', component: ErrorComponent, pathMatch: 'full' }, |
||||
] |
], |
||||
}, |
}, |
||||
{ path: 'user', component: LayoutComponent, children: |
{ path: 'user', component: LayoutComponent, children: |
||||
[ |
[ |
||||
//{ path: 'dashboard', component: DashboardComponent, pathMatch: 'full', canActivate: [AuthGuard]},
|
{ path: 'edit', component: EditComponent, pathMatch: 'full' }, |
||||
{ path: 'login', component: LoginComponent, pathMatch: 'full' }, |
{ path: 'login', component: LoginComponent, pathMatch: 'full' }, |
||||
] |
], |
||||
}, |
}, |
||||
]; |
]; |
||||
|
|
||||
@NgModule({ |
@NgModule({ |
||||
imports: [RouterModule.forChild(routes)], |
imports: [RouterModule.forChild(routes)], |
||||
exports: [RouterModule] |
exports: [RouterModule], |
||||
}) |
}) |
||||
export class PageRoutingModule { } |
export class PageRoutingModule { } |
||||
|
@ -1,48 +1,55 @@ |
|||||
<div class="container"> |
<div flex fxFill fxLayout="row" fxLayoutAlign="center center" > |
||||
|
<div fxFlex="33"> |
||||
|
|
||||
<base-card> |
<mat-card> |
||||
<base-card-title> |
<mat-card-title> |
||||
<h2 class="mdl-card__title-text">Edit User</h2> |
Edit User |
||||
</base-card-title> |
</mat-card-title> |
||||
<base-card-body> |
|
||||
|
|
||||
|
<mat-card-content> |
||||
<form [formGroup]="editForm" (ngSubmit)="editForm.valid && edit()" class="form"> |
<form [formGroup]="editForm" (ngSubmit)="editForm.valid && edit()" class="form"> |
||||
|
|
||||
<div class="mdl-grid"> |
<p> |
||||
<div class="mdl-cell mdl-cell--6-col mdl-textfield mdl-js-textfield mdl-textfield--floating-label"> |
<mat-form-field class="full-width"> |
||||
<input formControlName="full_name" class="mdl-textfield__input" type="text" id="full_name" value=""/> |
<mat-label>Full Name</mat-label> |
||||
<label class="mdl-textfield__label" for="full_name">Full Name</label> |
<input type="text" id="full_name" formControlName="full_name" matInput> |
||||
</div> |
</mat-form-field> |
||||
|
</p> |
||||
|
|
||||
<div class="mdl-cell mdl-cell--6-col mdl-textfield mdl-js-textfield mdl-textfield--floating-label"> |
<p> |
||||
<input formControlName="username" class="mdl-textfield__input" type="text" id="username" /> |
<mat-form-field class="full-width"> |
||||
<label class="mdl-textfield__label" for="username">Username</label> |
<mat-label>Username</mat-label> |
||||
</div> |
<input type="text" id="username" formControlName="username" matInput> |
||||
|
</mat-form-field> |
||||
<div class="mdl-cell mdl-cell--6-col mdl-textfield mdl-js-textfield mdl-textfield--floating-label"> |
</p> |
||||
<input formControlName="password" class="mdl-textfield__input" type="text" id="password"/> |
|
||||
<label class="mdl-textfield__label" for="password">Password</label> |
<p> |
||||
</div> |
<mat-form-field class="full-width"> |
||||
|
<mat-label>E-Mail</mat-label> |
||||
<div class="mdl-cell mdl-cell--6-col mdl-textfield mdl-js-textfield mdl-textfield--floating-label"> |
<input type="text" id="email" formControlName="email" matInput> |
||||
<input formControlName="email" class="mdl-textfield__input" type="text" id="email"/> |
</mat-form-field> |
||||
<label class="mdl-textfield__label" for="email">Email</label> |
</p> |
||||
</div> |
|
||||
|
<p> |
||||
</div> |
<mat-form-field class="full-width"> |
||||
|
<mat-label>Password</mat-label> |
||||
|
<input type="password" id="password" formControlName="password" matInput> |
||||
<button [disabled]="!editForm.valid" type="submit" class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect button--colored-light-blue"> |
</mat-form-field> |
||||
|
</p> |
||||
|
|
||||
|
<button mat-raised-button color="primary" [disabled]="!editForm.valid" type="submit"> |
||||
Edit User |
Edit User |
||||
</button> |
</button> |
||||
|
|
||||
|
|
||||
|
|
||||
</form> |
</form> |
||||
|
</mat-card-content> |
||||
|
</mat-card> |
||||
|
</div> |
||||
|
|
||||
|
|
||||
|
|
||||
</base-card-body> |
|
||||
</base-card> |
|
||||
|
|
||||
</div> |
</div> |
||||
|
|
||||
|
|
||||
|
@ -1,28 +1,26 @@ |
|||||
import { EventEmitter, Injectable } from '@angular/core'; |
import { EventEmitter, Injectable } from '@angular/core'; |
||||
import {Observable} from "rxjs"; |
import { Observable } from 'rxjs'; |
||||
|
|
||||
|
|
||||
@Injectable({ |
@Injectable({ |
||||
providedIn: 'root' |
providedIn: 'root', |
||||
}) |
}) |
||||
export class DataService { |
export class DataService { |
||||
|
|
||||
_observables: any = {}; |
_observables: any = {}; |
||||
constructor() {} |
constructor() {} |
||||
|
|
||||
|
|
||||
emit(event: string, value: any): void { |
emit(event: string, value: any): void { |
||||
if (this._observables.hasOwnProperty(event)) { |
if (this._observables.hasOwnProperty(event)) { |
||||
this._observables[event].emit(value) |
this._observables[event].emit(value); |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
on(event: string): Observable<any> { |
on(event: string): Observable<any> { |
||||
if (!this._observables.hasOwnProperty(event)) { |
if (!this._observables.hasOwnProperty(event)) { |
||||
this._observables[event] = new EventEmitter<any>() |
this._observables[event] = new EventEmitter<any>(); |
||||
} |
} |
||||
|
|
||||
return this._observables[event].asObservable() |
return this._observables[event].asObservable(); |
||||
} |
} |
||||
|
|
||||
} |
} |
||||
|
@ -1,93 +1,93 @@ |
|||||
import { Injectable } from '@angular/core'; |
import { Injectable } from '@angular/core'; |
||||
import {ConfigService} from "./config.service"; |
import { ConfigService } from './config.service'; |
||||
import {HttpClient} from "@angular/common/http"; |
import { HttpClient } from '@angular/common/http'; |
||||
|
|
||||
import {catchError} from "rxjs/operators"; |
import { catchError } from 'rxjs/operators'; |
||||
import {Server} from "../interfaces/server"; |
import { Server } from '../interfaces/server'; |
||||
import {Peer} from "../interfaces/peer"; |
import { Peer } from '../interfaces/peer'; |
||||
import {Observable, Subscribable} from "rxjs"; |
import { Observable, Subscribable } from 'rxjs'; |
||||
|
|
||||
@Injectable({ |
@Injectable({ |
||||
providedIn: 'root' |
providedIn: 'root', |
||||
}) |
}) |
||||
export class ServerService { |
export class ServerService { |
||||
public_url_wg: string = "/api/wg"; |
public base = '/api/v1/'; |
||||
public url: string = this.public_url_wg + "/server"; |
public serverURL = this.base + "server"; |
||||
constructor(private config: ConfigService, private http: HttpClient) { |
public peerURL = this.base + "peer"; |
||||
|
public wgURL = this.base + "wg"; |
||||
|
|
||||
|
|
||||
|
constructor(private config: ConfigService, private http: HttpClient) { |
||||
|
|
||||
} |
} |
||||
|
|
||||
public deletePeer(peer: Peer): Subscribable<Peer> { |
public deletePeer(peer: Peer): Subscribable<Peer> { |
||||
return this.http.post(this.url + "/peer/delete", peer) |
return this.http.post(this.peerURL + '/delete', peer); |
||||
} |
} |
||||
|
|
||||
|
|
||||
public serverPerformAction(action: string, item: any): Subscribable<Server> { |
public serverPerformAction(action: string, item: any): Subscribable<Server> { |
||||
return this.http.post(this.url + "/" + action, item) |
return this.http.post(this.serverURL + '/' + action, item) |
||||
.pipe(catchError(this.config.handleError.bind(this))) |
.pipe(catchError(this.config.handleError.bind(this))); |
||||
} |
} |
||||
|
|
||||
public addPeer(server: Server): Subscribable<Peer>{ |
public addPeer(server_interface: any): Subscribable<Peer> { |
||||
return this.http.post(this.url + "/peer/add", server) |
return this.http.post(this.peerURL + '/add', server_interface); |
||||
} |
} |
||||
|
|
||||
public editPeer(peer: Peer): Subscribable<Peer> { |
public editPeer(peer: Peer): Subscribable<Peer> { |
||||
return this.http.post(this.url + "/peer/edit", peer) |
return this.http.post(this.peerURL + '/edit', peer); |
||||
} |
} |
||||
|
|
||||
public getServers(): Observable<Array<Server>>{ |
public getServers(): Observable<Server[]> { |
||||
return this.http.get<Array<Server>>(this.url + "/all") |
return this.http.get<Server[]>(this.serverURL + '/all') |
||||
.pipe(catchError(this.config.handleError.bind(this))) |
.pipe(catchError(this.config.handleError.bind(this))); |
||||
} |
} |
||||
|
|
||||
|
|
||||
public addServer(item: Server): Subscribable<Server> { |
public addServer(item: Server): Subscribable<Server> { |
||||
return this.http.post(this.url + "/add", item) |
return this.http.post(this.serverURL + '/add', item) |
||||
.pipe(catchError(this.config.handleError.bind(this))) |
.pipe(catchError(this.config.handleError.bind(this))); |
||||
} |
} |
||||
|
|
||||
public startServer(item: Server): Subscribable<Server> { |
public startServer(item: Server): Subscribable<Server> { |
||||
return this.serverPerformAction("start", item) |
return this.serverPerformAction('start', item); |
||||
} |
} |
||||
|
|
||||
public stopServer(item: Server): Subscribable<Server> { |
public stopServer(item: Server): Subscribable<Server> { |
||||
return this.serverPerformAction("stop", item) |
return this.serverPerformAction('stop', item); |
||||
} |
} |
||||
|
|
||||
public restartServer(item: Server): Subscribable<Server> { |
public restartServer(item: Server): Subscribable<Server> { |
||||
return this.serverPerformAction("restart", item) |
return this.serverPerformAction('restart', item); |
||||
} |
} |
||||
|
|
||||
public deleteServer(item: Server): Subscribable<Server> { |
public deleteServer(item: Server): Subscribable<Server> { |
||||
return this.serverPerformAction("delete", item) |
return this.serverPerformAction('delete', item); |
||||
} |
} |
||||
|
|
||||
public editServer(oldServer: Server, newServer: Server): Subscribable<Server> { |
public editServer(oldServer: Server, newServer: Server): Subscribable<Server> { |
||||
return this.serverPerformAction("edit", { |
return this.serverPerformAction('edit', { |
||||
"interface": oldServer.interface, |
interface: oldServer.interface, |
||||
"server": newServer |
server: newServer, |
||||
}) |
}); |
||||
} |
} |
||||
|
|
||||
public getKeyPair() { |
public getKeyPair() { |
||||
return this.http.get(this.public_url_wg + "/generate_keypair") |
return this.http.get(this.wgURL + '/generate_keypair'); |
||||
} |
} |
||||
|
|
||||
public getPSK() { |
public getPSK() { |
||||
return this.http.get(this.public_url_wg + "/generate_psk") |
return this.http.get(this.wgURL + '/generate_psk'); |
||||
} |
} |
||||
|
|
||||
public peerConfig(peer: Peer) { |
public peerConfig(peer: Peer) { |
||||
return this.http.post(this.public_url_wg + "/server/peer/config", peer) |
return this.http.post(this.peerURL + '/config', peer); |
||||
} |
} |
||||
|
|
||||
public serverConfig(server: Server) { |
public serverConfig(server: Server) { |
||||
return this.http.post(this.url + "/config", server) |
return this.http.post(this.serverURL + '/config', server); |
||||
} |
} |
||||
|
|
||||
public serverStats(server: Server) { |
public serverStats(server: Server) { |
||||
return this.http.post(this.url + "/stats", server) |
return this.http.post(this.serverURL + '/stats', server); |
||||
} |
} |
||||
} |
} |
||||
|