diff --git a/wg-manager-backend/database/util.py b/wg-manager-backend/database/util.py
index 43ad741..db07c7a 100644
--- a/wg-manager-backend/database/util.py
+++ b/wg-manager-backend/database/util.py
@@ -1,3 +1,4 @@
+import contextlib
import os
import alembic.command
@@ -14,9 +15,11 @@ from loguru import logger
def perform_migrations():
logger.info("Performing migrations...")
alembic_cfg = Config("alembic.ini")
+ alembic_cfg.attributes['configure_logger'] = False
alembic_cfg.set_main_option('script_location', "migrations")
alembic_cfg.set_main_option('sqlalchemy.url', str(engine.url))
+
alembic.command.upgrade(alembic_cfg, 'head')
logger.info("Migrations done!")
diff --git a/wg-manager-backend/db/wireguard.py b/wg-manager-backend/db/wireguard.py
index a995240..29591f5 100644
--- a/wg-manager-backend/db/wireguard.py
+++ b/wg-manager-backend/db/wireguard.py
@@ -11,10 +11,9 @@ import script.wireguard
from sqlalchemy.orm import Session
from database import models
import schemas
-import logging
+from loguru import logger
-_LOGGER = logging.getLogger(__name__)
-_LOGGER.setLevel(logging.DEBUG)
+from util import WGMHTTPException
def start_client(sess: Session, peer: schemas.WGPeer):
@@ -161,8 +160,8 @@ def server_add_on_init(sess: Session):
# Only add if it does not already exists.
server_add(schemas.WGServerAdd(**init_data), sess, start=const.SERVER_INIT_INTERFACE_START)
except Exception as e:
- _LOGGER.warning("Failed to setup initial server interface with exception:")
- _LOGGER.exception(e)
+ logger.warning("Failed to setup initial server interface with exception:")
+ logger.exception(e)
def server_add(server: schemas.WGServerAdd, sess: Session, start=False):
@@ -180,43 +179,54 @@ def server_add(server: schemas.WGServerAdd, sess: Session, start=False):
peers = server.peers if server.peers else []
- # Public/Private key
- try:
+ all_interfaces = sess.query(models.WGServer).all()
+ check_interface_exists = any(map(lambda el: el.interface == server.interface, all_interfaces))
+ check_v4_address_exists = any(map(lambda el: el.address == server.address, all_interfaces))
+ check_v6_address_exists = any(map(lambda el: el.v6_address == server.v6_address, all_interfaces))
+ check_listen_port_exists = any(map(lambda el: el.listen_port == server.listen_port, all_interfaces))
+ if check_interface_exists:
+ raise WGMHTTPException(
+ status_code=400,
+ detail=f"There is already a interface with the name: {server.interface}")
+
+ if check_v4_address_exists:
+ raise WGMHTTPException(
+ status_code=400,
+ detail=f"There is already a interface with the IPv4 address: {server.address}")
+
+ if check_v6_address_exists:
+ raise WGMHTTPException(
+ status_code=400,
+ detail=f"There is already a interface with the IPv6 address: {server.v6_address}")
+
+ if check_listen_port_exists:
+ raise WGMHTTPException(
+ status_code=400,
+ detail=f"There is already a interface listening on port: {server.listen_port}")
+
+ if not server.private_key:
+ keys = script.wireguard.generate_keys()
+ server.private_key = keys["private_key"]
+ server.public_key = keys["public_key"]
- if sess.query(models.WGServer) \
- .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:
- 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.peers = []
- server.sync(sess)
-
- if len(peers) > 0:
- server.from_db(sess)
-
- for schemaPeer in peers:
- schemaPeer.server_id = server.id
- schemaPeer.configuration = script.wireguard.generate_config(dict(
- peer=schemaPeer,
- server=server
- ))
- dbPeer = models.WGPeer(**schemaPeer.dict())
- sess.add(dbPeer)
- sess.commit()
+ server.configuration = script.wireguard.generate_config(server)
+ server.peers = []
+ server.sync(sess)
+ if len(peers) > 0:
server.from_db(sess)
- except ValueError as e:
- raise HTTPException(status_code=400, detail=str(e))
+ for schemaPeer in peers:
+ schemaPeer.server_id = server.id
+ schemaPeer.configuration = script.wireguard.generate_config(dict(
+ peer=schemaPeer,
+ server=server
+ ))
+ dbPeer = models.WGPeer(**schemaPeer.dict())
+ sess.add(dbPeer)
+ sess.commit()
+
+ server.from_db(sess)
if start and not script.wireguard.is_running(server):
script.wireguard.start_interface(server)
diff --git a/wg-manager-backend/logger.py b/wg-manager-backend/logger.py
new file mode 100644
index 0000000..063b203
--- /dev/null
+++ b/wg-manager-backend/logger.py
@@ -0,0 +1,21 @@
+
+def setup_logging():
+ import logging
+ from loguru import logger
+ class InterceptHandler(logging.Handler):
+ def emit(self, record):
+ # Get corresponding Loguru level if it exists
+ try:
+ level = logger.level(record.levelname).name
+ except ValueError:
+ level = record.levelno
+
+ # Find caller from where originated the logged message
+ frame, depth = logging.currentframe(), 2
+ while frame.f_code.co_filename == logging.__file__:
+ frame = frame.f_back
+ depth += 1
+
+ logger.opt(depth=depth, exception=record.exc_info).log(level, record.getMessage())
+
+ logging.basicConfig(handlers=[InterceptHandler()], level=1)
diff --git a/wg-manager-backend/logging.json b/wg-manager-backend/logging.json
new file mode 100644
index 0000000..d56479c
--- /dev/null
+++ b/wg-manager-backend/logging.json
@@ -0,0 +1,11 @@
+{
+ "logger": {
+ "path": "./logs",
+ "filename": "access.log",
+ "level": "info",
+ "rotation": "20 days",
+ "retention": "1 months",
+ "format": "{level: <8} {time:YYYY-MM-DD HH:mm:ss.SSS} {extra[request_id]} - {name}:{function} - {message}"
+
+ }
+}
\ No newline at end of file
diff --git a/wg-manager-backend/main.py b/wg-manager-backend/main.py
index 18d5556..b62e369 100644
--- a/wg-manager-backend/main.py
+++ b/wg-manager-backend/main.py
@@ -1,8 +1,14 @@
+from logger import setup_logging
+setup_logging()
+
import const
+from uvicorn_loguru_integration import run_uvicorn_loguru
+
import time
from starlette.middleware.base import BaseHTTPMiddleware
import middleware
+
from routers.v1 import user, server, peer, wg
import script.wireguard_startup
import pkg_resources
@@ -15,6 +21,7 @@ import database.util
app = FastAPI()
app.add_middleware(BaseHTTPMiddleware, dispatch=middleware.db_session_middleware)
+app.add_middleware(BaseHTTPMiddleware, dispatch=middleware.logging_middleware)
app.include_router(
user.router,
@@ -83,4 +90,13 @@ if __name__ == "__main__":
# Configure wireguard
script.wireguard_startup.setup_on_start()
- uvicorn.run("__main__:app", reload=True)
+ run_uvicorn_loguru(
+ uvicorn.Config(
+ "__main__:app",
+ host="0.0.0.0",
+ port=8000,
+ log_level="warning",
+ reload=True,
+ workers=1
+ )
+ )
diff --git a/wg-manager-backend/middleware.py b/wg-manager-backend/middleware.py
index b49a03e..ce13ca5 100644
--- a/wg-manager-backend/middleware.py
+++ b/wg-manager-backend/middleware.py
@@ -4,6 +4,7 @@ import jwt
from fastapi import Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer
from jwt import PyJWTError
+from loguru import logger
from passlib.context import CryptContext
from sqlalchemy.orm import Session
from starlette import status
@@ -27,6 +28,12 @@ def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
+async def logging_middleware(request: Request, call_next):
+ response = await call_next(request)
+ logger.opt(depth=2).info(f"{request.method} {request.url} - Code: {response.status_code}")
+ return response
+
+
async def db_session_middleware(request: Request, call_next):
response = Response("Internal server error (Database error)", status_code=500)
try:
diff --git a/wg-manager-backend/migrations/env.py b/wg-manager-backend/migrations/env.py
index 5095440..9a72e24 100644
--- a/wg-manager-backend/migrations/env.py
+++ b/wg-manager-backend/migrations/env.py
@@ -14,7 +14,8 @@ config = context.config
# Interpret the config file for Python logging.
# This line sets up loggers basically.
-fileConfig(config.config_file_name)
+if config.attributes.get('configure_logger', True):
+ fileConfig(config.config_file_name)
# add your model's MetaData object here
# for 'autogenerate' support
diff --git a/wg-manager-backend/requirements.txt b/wg-manager-backend/requirements.txt
index 5102d52..970c54a 100644
--- a/wg-manager-backend/requirements.txt
+++ b/wg-manager-backend/requirements.txt
@@ -13,6 +13,7 @@ sqlalchemy_utils
sqlalchemy-migrate
requests
uvicorn
+uvicorn-loguru-integration
uvloop
httptools
qrcode[pil]
diff --git a/wg-manager-backend/util.py b/wg-manager-backend/util.py
index 120fb53..95c2aff 100644
--- a/wg-manager-backend/util.py
+++ b/wg-manager-backend/util.py
@@ -1,2 +1,11 @@
+from loguru import logger
+from fastapi import HTTPException
from jinja2 import Environment, PackageLoader
jinja_env = Environment(loader=PackageLoader(__name__, 'templates'))
+
+
+class WGMHTTPException(HTTPException):
+
+ def __init__(self, status_code: int, detail: str = None):
+ HTTPException.__init__(self, status_code, detail)
+ logger.opt(depth=1).error(detail)
\ No newline at end of file