Use new front-end based on Dashmin template

This commit is contained in:
Tech Garage 2024-01-25 20:40:43 +03:30
parent c118242c5b
commit dbd7fafb34
4391 changed files with 228401 additions and 73494 deletions

View file

@ -1,26 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>943131a5-93e0-4ec4-91aa-e26e825730c4</UserSecretsId>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerfileContext>.</DockerfileContext>
<DockerfileFile>../Dockerfile</DockerfileFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="12.0.1" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.5">
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.17.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.12" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.6" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Razor.Templating.Core" Version="1.8.0" />
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
<PackageReference Include="NuGet.Protocol" Version="6.8.0" />
<PackageReference Include="Razor.Templating.Core" Version="1.9.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.1" />
</ItemGroup>
<ItemGroup>

View file

@ -1,62 +0,0 @@
using AutoMapper;
using MTWireGuard.Application.Models;
using MTWireGuard.Application.Models.Mikrotik;
using MTWireGuard.Models.Requests;
using MTWireGuard.Models.Responses;
namespace MTWireGuard.Mapper
{
public class RequestProfile : Profile
{
public RequestProfile()
{
// Peer Request
CreateMap<CreateClientRequest, UserCreateModel>()
.ForMember(dest => dest.Disabled,
opt => opt.MapFrom(src => !src.Enabled))
.ForMember(dest => dest.EndpointAddress,
opt => opt.MapFrom(src => src.Endpoint))
.ForMember(dest => dest.PersistentKeepalive,
opt => opt.MapFrom(src => src.KeepAlive.ToString()))
.ForMember(dest => dest.Expire,
opt => opt.MapFrom(src => Convert.ToDateTime(src.Expire)));
CreateMap<SyncUserRequest, UserSyncModel>();
CreateMap<UpdateClientRequest, UserUpdateModel>()
.ForMember(dest => dest.EndpointAddress,
opt => opt.MapFrom(src => src.Endpoint))
.ForMember(dest => dest.PersistentKeepalive,
opt => opt.MapFrom(src => src.KeepAlive))
.ForMember(dest => dest.Expire,
opt => opt.MapFrom(src => Convert.ToDateTime(src.Expire)));
// Server Request
CreateMap<CreateServerRequest, ServerCreateModel>()
.ForMember(dest => dest.ListenPort,
opt => opt.MapFrom(src => src.Port));
CreateMap<UpdateServerRequest, ServerUpdateModel>()
.ForMember(dest => dest.ListenPort,
opt => opt.MapFrom(src => src.Port));
// Item Creation
CreateMap<MikrotikAPI.Models.CreationStatus, CreationResult>()
.ForMember(dest => dest.Code,
opt => opt.MapFrom(src => (src.Success) ? "200" : src.Code.ToString()))
.ForMember(dest => dest.Title,
opt => opt.MapFrom(src => (src.Success) ? "Done" : src.Message))
.ForMember(dest => dest.Description,
opt => opt.MapFrom(src => (src.Success) ? "Item created/updated successfully." : src.Detail));
// Toast Result
CreateMap<CreationResult, ToastMessage>()
.ForMember(dest => dest.Title,
opt => opt.MapFrom(src => src.Code == "200" ? src.Title : $"[{src.Code}] {src.Title}"))
.ForMember(dest => dest.Body,
opt => opt.MapFrom(src => src.Description))
.ForMember(dest => dest.Background,
opt => opt.MapFrom(src => src.Code == "200" ? "success" : "danger"));
}
}
}

View file

@ -0,0 +1,34 @@
using MTWireGuard.Application;
using MTWireGuard.Application.Repositories;
namespace MTWireGuard.Middlewares
{
public class ClientReportingMiddleware(RequestDelegate next)
{
public async Task Invoke(HttpContext context, IMikrotikRepository api)
{
if ((context.Request.Path.Value == "/" || context.Request.Path.Value == "/Index") && (!context.User.Identity.IsAuthenticated))
{
var ip = context.Connection.RemoteIpAddress;
var users = await api.GetUsersAsync();
var user = users.Find(x => x.Address == $"{ip}/32");
if (user != null)
{
context.Session.Set("user", user);
context.Response.Redirect("/Client", true);
}
}
await next(context);
}
}
public static class ClientReportingMiddlewareExtensions
{
public static IApplicationBuilder UseClientReporting(this IApplicationBuilder builder)
{
return builder.UseMiddleware<ClientReportingMiddleware>();
}
}
}

View file

@ -1,22 +1,19 @@
using MTWireGuard.Pages;
using MTWireGuard.Application.Repositories;
using MTWireGuard.Application.Repositories;
using MTWireGuard.Pages;
using Razor.Templating.Core;
using System.Globalization;
namespace MTWireGuard.Middlewares
{
public class DependencyCheckMiddleware
{
private readonly RequestDelegate _next;
private readonly IMikrotikRepository API;
public DependencyCheckMiddleware(RequestDelegate next, IMikrotikRepository mikrotik)
public DependencyCheckMiddleware(RequestDelegate next)
{
_next = next;
API = mikrotik;
}
public async Task InvokeAsync(HttpContext context)
public async Task InvokeAsync(HttpContext context, IMikrotikRepository API)
{
bool Error = false;
@ -26,7 +23,7 @@ namespace MTWireGuard.Middlewares
string? PUBLICIP = Environment.GetEnvironmentVariable("MT_PUBLIC_IP");
ErrorModel errorModel = new();
Dictionary<string, object> ViewBag = new();
Dictionary<string, object> ViewBag = [];
if (string.IsNullOrEmpty(IP) || string.IsNullOrEmpty(USER) || string.IsNullOrEmpty(PUBLICIP))
{

View file

@ -0,0 +1,51 @@
using Microsoft.AspNetCore.Mvc;
using MTWireGuard.Application;
using Newtonsoft.Json;
namespace MTWireGuard.Middlewares
{
public class ErrorHandlingMiddleware(RequestDelegate next)
{
public async Task Invoke(HttpContext context)
{
try
{
await next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
string exceptionType = exception.GetType().Name,
message = exception.Message,
stackTrace = exception.StackTrace,
details = JsonConvert.SerializeObject(exception)!;
var viewResult = new ViewResult
{
ViewName = "Error",
StatusCode = 500
};
viewResult.ViewData["Title"] = message;
viewResult.ViewData["Message"] = stackTrace;
viewResult.ViewData["Details"] = details;
var html = viewResult.ToHtml(context);
return context.Response.WriteAsync(html);
}
}
public static class ExceptionHandlerMiddlewareExtensions
{
public static IApplicationBuilder UseExceptionHandling(this IApplicationBuilder builder)
{
return builder.UseMiddleware<ErrorHandlingMiddleware>();
}
}
}

View file

@ -1,44 +0,0 @@
using System.Net;
namespace MTWireGuard.Middlewares
{
public class ExceptionHandlerMiddleware
{
private readonly RequestDelegate _next;
//public (HttpStatusCode code, string message) GetResponse(Exception exception);
public ExceptionHandlerMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception exception)
{
// log the error
//Logger.Error(exception, "error during executing {Context}", context.Request.Path.Value);
Console.WriteLine(exception);
var response = context.Response;
response.ContentType = "application/json";
// get the response code and message
//var (status, message) = GetResponse(exception);
response.StatusCode = (int)HttpStatusCode.OK;
await response.WriteAsync(exception.Message);
}
}
}
public static class ExceptionHandlerMiddlewareExtensions
{
public static IApplicationBuilder UseExceptionHandling(this IApplicationBuilder builder)
{
return builder.UseMiddleware<ExceptionHandlerMiddleware>();
}
}
}

View file

@ -1,13 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel.DataAnnotations;
namespace MTWireGuard.Models.Requests
{
public class ChangeStateRequest
{
[FromQuery(Name = "ID"), Required]
public int Id { get; set; }
[FromQuery(Name = "IsEnabled"), Required]
public bool Enabled { get; set; }
}
}

View file

@ -1,17 +0,0 @@
namespace MTWireGuard.Models.Requests
{
public class CreateClientRequest
{
public string Name { get; set; }
public string Endpoint { get; set; }
public ushort EndpointPort { get; set; }
public string AllowedAddress { get; set; }
public string PresharedKey { get; set; }
public string PrivateKey { get; set; }
public string PublicKey { get; set; }
public string Interface { get; set; }
public int KeepAlive { get; set; }
public bool Enabled { get; set; }
public string Expire { get; set; }
}
}

View file

@ -1,15 +0,0 @@
namespace MTWireGuard.Models.Requests
{
public class CreateServerRequest
{
public string Name { get; set; }
public ushort Port { get; set; } = 13231;
public ushort MTU { get; set; } = 1420;
public string PrivateKey { get; set; }
public bool Enabled { get; set; }
}
}

View file

@ -1,7 +0,0 @@
namespace MTWireGuard.Models.Requests
{
public class DeleteRequest
{
public int Id { get; set; }
}
}

View file

@ -1,10 +0,0 @@
namespace MTWireGuard.Models.Requests
{
public class SyncUserRequest
{
public int ID { get; set; }
public string Name { get; set; }
public string PrivateKey { get; set; }
public string PublicKey { get; set; }
}
}

View file

@ -1,17 +0,0 @@
namespace MTWireGuard.Models.Requests
{
public class UpdateClientRequest
{
public int ID { get; set; }
public string Name { get; set; }
public string Endpoint { get; set; }
public ushort EndpointPort { get; set; }
public string AllowedAddress { get; set; }
public string PresharedKey { get; set; }
public string PrivateKey { get; set; }
public string PublicKey { get; set; }
public string Interface { get; set; }
public int KeepAlive { get; set; }
public string Expire { get; set; }
}
}

View file

@ -1,15 +0,0 @@
namespace MTWireGuard.Models.Requests
{
public class UpdateServerRequest
{
public int Id { get; set; }
public string Name { get; set; }
public ushort Port { get; set; }
public ushort MTU { get; set; }
public string PrivateKey { get; set; }
}
}

View file

@ -1,9 +0,0 @@
namespace MTWireGuard.Models.Responses
{
public class ToastMessage
{
public string Title { get; set; }
public string Body { get; set; }
public string Background { get; set; }
}
}

View file

@ -1,22 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
namespace MTWireGuard.Models.Responses
{
public class ToastResult : IActionResult
{
private readonly ToastMessage _result;
public ToastResult(ToastMessage message)
{
_result = message;
}
public async Task ExecuteResultAsync(ActionContext context)
{
var objectResult = new ObjectResult(_result);
await objectResult.ExecuteResultAsync(context);
}
}
}

138
UI/Pages/Client.cshtml Normal file
View file

@ -0,0 +1,138 @@
@page
@model MTWireGuard.Pages.ClientModel
@using MTWireGuard.Application
@using MTWireGuard.Application.Models.Mikrotik
@inject MTWireGuard.Application.Repositories.IMikrotikRepository api
@{
Layout = null;
var ip = HttpContext.Connection.RemoteIpAddress;
var user = HttpContext.Session.Get<WGPeerViewModel>("user");
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>DASHMIN - Bootstrap Admin Template</title>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<meta content="" name="keywords">
<meta content="" name="description">
<!-- Favicon -->
<link href="img/favicon.ico" rel="icon">
<!-- Google Web Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Heebo:wght@400;500;600;700&display=swap" rel="stylesheet">
<!-- Icon Font Stylesheet -->
<link href="assets/lib/boxicons/css/boxicons.min.css" rel="stylesheet">
<link href="assets/libs/fortawesome/fontawesome-free/css/all.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.4.1/font/bootstrap-icons.css" rel="stylesheet">
<!-- Template Stylesheet -->
<link href="assets/css/core.css" rel="stylesheet">
<!-- Theme Style Switcher-->
<script src="assets/js/themeSwitcher.js"></script>
</head>
<body>
<div class="container-fluid position-relative d-flex flex-column" style="height: 100vh;">
<div class="row justify-content-center align-items-center d-flex flex-column h-100">
<div class="col-sm-12 col-md-6">
<img class="rounded mx-auto d-block" src="img/logo.png" alt="MTWireguard Logo" style="width: 50%;">
</div>
<div class="col-sm-12 col-md-5">
<div class="card">
<div class="card-header">
<h3 class="card-title text-center">@user.Name's Information</h3>
</div>
<div class="card-body">
<table class="table table-striped">
<tbody>
<tr>
<th>Name</th>
<td>@user.Name</td>
</tr>
<tr>
<th>Contact</th>
<td></td>
</tr>
<tr>
<th>Expiration</th>
<td>
<span class="badge bg-indigo">@user.Expire</span>
</td>
</tr>
<tr>
<th>Traffic Usage</th>
<td>
<span class="badge bg-warning">@user.TrafficUsed/@user.Traffic</span>
</td>
</tr>
<tr>
<th>IP</th>
<td>@user.Address</td>
</tr>
<tr>
<th>DNS</th>
<td>@user.DNSAddress</td>
</tr>
</tbody>
</table>
</div>
<div class="card-footer">
<div class="row gy-2">
<div class="col-md-4 d-flex justify-content-center">
<button class="btn btn-labeled bg-orange text-dark fw-medium rounded-3">
<span class="btn-label d-flex align-items-center justify-content-center">
<i class="bx bx-qr-scan"></i> QR
</span>
</button>
</div>
<div class="col-md-4 d-flex justify-content-center">
<button class="btn btn-labeled bg-blue text-dark fw-medium rounded-3">
<span class="btn-label d-flex align-items-center justify-content-center">
<i class="bx bx-download"></i> Download
</span>
</button>
</div>
<div class="col-md-4 d-flex justify-content-center">
<button class="btn btn-labeled bg-teal text-dark fw-medium rounded-3">
<span class="btn-label d-flex align-items-center justify-content-center">
<i class="bx bxs-key"></i> Change Password
</span>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Footer Start -->
<footer class="container-fluid sticky-bottom px-4">
<div class="rounded-top p-4">
<div class="row">
<div class="col-12 col-sm-6 text-center text-sm-start">
&copy; <a href="#">MTWireguard</a>, All Right Reserved.
</div>
<div class="col-12 col-sm-6 text-center text-sm-end">
<!--/*** This template is free as long as you keep the footer authors credit link/attribution link/backlink. If you'd like to use the template without the footer authors credit link/attribution link/backlink, you can purchase the Credit Removal License from "https://htmlcodex.com/credit-removal". Thank you for your support. ***/-->
Designed By <a href="https://htmlcodex.com">HTML Codex</a>
</div>
</div>
</div>
</footer>
<!-- Footer End -->
</div>
<!-- JavaScript Libraries -->
<script src="assets/libs/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="assets/lib/jquery/js/jquery.min.js"></script>
<script src="assets/libs/jquery-ui/jquery-ui.min.js"></script>
</body>
</html>

39
UI/Pages/Client.cshtml.cs Normal file
View file

@ -0,0 +1,39 @@
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using MTWireGuard.Application.Repositories;
using Microsoft.AspNetCore.Authorization;
using MTWireGuard.Application.Models.Requests;
using MTWireGuard.Application.Models.Mikrotik;
using MTWireGuard.Application;
namespace MTWireGuard.Pages
{
[AllowAnonymous]
[IgnoreAntiforgeryToken(Order = 1001)]
public class ClientModel(IMikrotikRepository api) : PageModel
{
public async Task<IActionResult> OnGetAsync()
{
var user = HttpContext.Session.Get<WGPeerViewModel>("user");
if (user == null)
{
return RedirectPermanent("/Login");
}
return Page();
}
public async Task<IActionResult> OnPostAsync([FromBody] LoginRequest login)
{
var users = await api.GetUsersAsync();
var user = users.Find(x => x.Name.Equals(login.Username, StringComparison.CurrentCultureIgnoreCase));
if (user != null)
{
HttpContext.Session.Set("user", user);
return Page();
}
return Unauthorized();
}
}
}

View file

@ -1,70 +1,133 @@
@page "{handler?}"
@page
@using MTWireGuard.Application.Models.Mikrotik
@model MTWireGuard.Pages.ClientsModel
@{
ViewData["Title"] = "Clients";
ViewData["Breadcrumb"] = "Users";
var servers = (List<WGServerViewModel>)ViewData["servers"];
}
<div class="row">
<div class="table-responsive">
<table class="table border mb-0">
<thead class="table-light fw-semibold">
<tr class="align-middle">
<th class="text-center">
<svg class="icon">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-people"></use>
</svg>
</th>
<div class="row g-4 mb-4">
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span>Total Users</span>
<div class="d-flex align-items-end mt-2">
<h4 class="mb-0 me-2" id="users-total">0</h4>
</div>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-primary">
<i class="bx bx-user bx-sm"></i>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span>Active Users</span>
<div class="d-flex align-items-end mt-2">
<h4 class="mb-0 me-2" id="users-active">0</h4>
</div>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-danger">
<i class="bx bx-user-check bx-sm"></i>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span>Online Users</span>
<div class="d-flex align-items-end mt-2">
<h4 class="mb-0 me-2" id="users-online">0</h4>
</div>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-success">
<i class="bx bx-group bx-sm"></i>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span>Pending Users</span>
<div class="d-flex align-items-end mt-2">
<h4 class="mb-0 me-2" id="users-pending">0</h4>
</div>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-warning">
<i class="bx bx-user-voice bx-sm"></i>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Users List Table -->
<div class="card">
<div class="card-header border-bottom">
<h5 class="card-title">WG Peers</h5>
</div>
<div class="card-body">
<div class="card-datatable table-responsive pt-0">
<table class="datatables-basic table table-striped table-hover border-top">
<thead>
<tr>
<th></th>
<th></th>
<th>Name</th>
<th>Interface</th>
<th>Address</th>
<th>Allowed Address</th>
<th>Traffic</th>
<th>Expire</th>
<th>Status</th>
<th></th>
<th>Action</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
<div class="row mt-2">
<div class="col-12">
@await Component.InvokeAsync("CreateClientForm", new {
Servers = servers
})
</div>
<!-- Offcanvas to add user -->
<component type="typeof(Components.Offcanvas.Client.Add)" render-mode="Static" />
<!-- Offcanvas to add user -->
<!-- Offcanvas to edit user -->
<component type="typeof(Components.Offcanvas.Client.Update)" render-mode="Static" />
<!-- Offcanvas to edit user -->
<!-- Offcanvas to sync user -->
<component type="typeof(Components.Offcanvas.Client.Sync)" render-mode="Static" />
<!-- Offcanvas to sync user -->
</div>
<!-- QR Modal -->
<component type="typeof(Modals.QRModal)" render-mode="Static" />
<!-- Delete Modal -->
@await Component.InvokeAsync("DeleteModal", new {
IsServer = false
})
<!-- Edit Modal -->
@await Component.InvokeAsync("UpdateClientModal", new {
Servers = servers
})
<!-- Sync Modal -->
@await Component.InvokeAsync("SyncUserModal")
@section Modals {
<!-- QR Modal -->
<component type="typeof(Components.Modals.QRModal)" render-mode="Static" />
<!-- Remove Modal -->
<component type="typeof(Components.Modals.DeleteModal)" render-mode="Static" />
<!-- Details Modal -->
<component type="typeof(Components.Modals.DetailsModal)" render-mode="Static" />
<!-- Toasts -->
<component type="typeof(Components.ToastContainer)" render-mode="Static" />
}
@section Scripts {
<script src="js/wireguard.js"></script>
<script src="js/wgelements.js"></script>
<script>
document.getElementById("QRModal").addEventListener('shown.coreui.modal', (event) => {
let Id = event.relatedTarget.closest('tr').getAttribute('data-id');
const xhttp = new XMLHttpRequest();
xhttp.onload = function () {
document.querySelector('#QRModal img.img-thumbnail').setAttribute("src", "data:image/png;base64, " + this.responseText);
}
xhttp.open("GET", "/Clients/QR?id=" + Id, true);
xhttp.send();
});
</script>
<script src="js/page-users.js"></script>
}

View file

@ -4,93 +4,14 @@ using Microsoft.AspNetCore.Mvc.RazorPages;
using MTWireGuard.Application.Models;
using MTWireGuard.Application.Models.Mikrotik;
using MTWireGuard.Application.Repositories;
using MTWireGuard.Models.Requests;
using MTWireGuard.Models.Responses;
using Newtonsoft.Json;
namespace MTWireGuard.Pages
{
public class ClientsModel : PageModel
{
private readonly IMikrotikRepository API;
private readonly IMapper mapper;
public ClientsModel(IMikrotikRepository mikrotik, IMapper mapper)
public void OnGet()
{
API = mikrotik;
this.mapper = mapper;
}
public async Task OnGetAsync()
{
var servers = await API.GetServersAsync();
ViewData["servers"] = servers;
}
public async Task<PartialViewResult> OnGetGetAll()
{
var users = await API.GetUsersAsync();
return Partial("_ClientsTable", users);
}
public async Task<IActionResult> OnGetQRAsync(int id)
{
string config = await API.GetQRCodeBase64(id);
return Content(config);
}
public async Task<FileResult> OnGetDownloadTunnelAsync(int id)
{
string config = await API.GetUserTunnelConfig(id);
byte[] bytesInStream = System.Text.Encoding.UTF8.GetBytes(config);
var user = await API.GetUser(id);
string filename = string.IsNullOrWhiteSpace(user.Name) ? user.Interface : user.Name;
Response.Headers.Add("content-disposition", $"attachment; filename={filename}.conf");
return File(bytesInStream, "text/plain");
}
public async Task<IActionResult> OnPostCreateAsync(CreateClientRequest request)
{
var model = mapper.Map<UserCreateModel>(request);
var make = await API.CreateUser(model);
var message = mapper.Map<ToastMessage>(make);
return new ToastResult(message);
}
public async Task<IActionResult> OnPostDelete(DeleteRequest request)
{
var delete = await API.DeleteUser(request.Id);
var message = mapper.Map<ToastMessage>(delete);
return new ToastResult(message);
}
public async Task<IActionResult> OnPostUpdate(UpdateClientRequest request)
{
var model = mapper.Map<UserUpdateModel>(request);
var update = await API.UpdateUser(model);
var message = mapper.Map<ToastMessage>(update);
return new ToastResult(message);
}
public async Task<IActionResult> OnPostSyncAsync(SyncUserRequest request)
{
var model = mapper.Map<UserSyncModel>(request);
var update = await API.SyncUser(model);
var message = mapper.Map<ToastMessage>(update);
return new ToastResult(message);
}
public async Task<IActionResult> OnGetEnableAsync(ChangeStateRequest request)
{
CreationResult result;
if (!request.Enabled)
result = await API.EnableUser(request.Id);
else
result = await API.DisableUser(request.Id);
var message = mapper.Map<ToastMessage>(result);
return new ToastResult(message);
}
}
}

View file

@ -1,99 +0,0 @@
@using MTWireGuard.Application.Models.Mikrotik
@using MTWireGuard.Models.Requests
@model CreateClientRequest
@{
List<WGServerViewModel> Servers = ViewData["Servers"] as List<WGServerViewModel>;
}
<div class="card mb-4">
<div class="card-header">Quick add peer</div>
<div class="card-body">
<form asp-page="Clients" asp-page-handler="Create" method="POST">
<div class="mb-3 row">
<label for="WGPName" class="col-sm-3 col-form-label user-select-none">Name</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="WGPName" name="Name">
</div>
</div>
<div class="mb-3 row">
<label for="WGPInterface" class="col-sm-3 col-form-label user-select-none">Interface</label>
<div class="col-sm-9">
<select class="form-select" name="Interface" id="WGPInterface" aria-label="Default select example">
@foreach (var server in Servers)
{
<option value="@server.Name">@server.Name</option>
}
</select>
</div>
</div>
<div class="mb-3 row">
<label for="WGPPrivKey" class="col-sm-3 col-form-label user-select-none">Private Key</label>
<div class="col-sm-9">
<div class="input-group">
<input type="text" class="form-control" id="WGPPrivKey" name="PrivateKey" aria-label="Generate" aria-describedby="PKeygenBTN" autocomplete="off">
<button type="button" class="input-group-text" id="PKeygenBTN" style="cursor: pointer;" onclick="generateKeys(this)">
<svg class="icon icon-sm text-dark my-1">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-reload"></use>
</svg>
</button>
</div>
</div>
</div>
<div class="mb-3 row">
<label for="WGPPubKey" class="col-sm-3 col-form-label user-select-none">Public Key</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="WGPPubKey" name="PublicKey" autocomplete="off">
</div>
</div>
<div class="mb-3 row">
<label for="WGPPSKey" class="col-sm-3 col-form-label user-select-none">Preshared Key</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="WGPPSKey" name="PresharedKey" autocomplete="off">
</div>
</div>
<div class="mb-3 row">
<label for="WGPAddress" class="col-sm-3 col-form-label user-select-none">Allowed Address</label>
<div class="col-sm-9">
<input class="form-control" id="WGPAddress" asp-for="AllowedAddress">
</div>
</div>
<div class="mb-3 row">
<label for="WGPEndpoint" class="col-sm-3 col-form-label user-select-none">Endpoint</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="WGPEndpoint" name="Endpoint">
</div>
</div>
<div class="mb-3 row">
<label for="WGPPort" class="col-sm-3 col-form-label user-select-none">Endpoint Port</label>
<div class="col-sm-9">
<input type="number" class="form-control" id="WGPPort" name="EndpointPort">
</div>
</div>
<div class="mb-3 row">
<label for="WGPKeepAlive" class="col-sm-3 col-form-label user-select-none">Persistent Keepalive</label>
<div class="col-sm-9">
<input type="number" class="form-control" id="WGPKeepAlive" name="KeepAlive">
</div>
</div>
<div class="mb-3 row">
<label for="WGPExpire" class="col-sm-3 col-form-label user-select-none">Expire</label>
<div class="input-group w-75 col-sm-9 date-input" id="WGPExpire" data-td-target-input="nearest" data-td-target-toggle="nearest">
<input id="WGPExpire" type="text" name="Expire" class="form-control" data-td-target="#WGPExpire" />
<span class="input-group-text" data-td-target="#WGPExpire" data-td-toggle="datetimepicker">
<i class="fas fa-calendar"></i>
</span>
</div>
</div>
<div class="mb-5 row">
<label for="WGSEnabled" class="col-sm-3 form-check-label user-select-none">Enabled</label>
<div class="col-sm-9">
<input type="checkbox" class="form-check-input" id="WGSEnabled" asp-for="Enabled" checked>
</div>
</div>
<div class="col-12">
<button class="btn btn-primary" type="submit">Create</button>
<button class="btn btn-danger" type="reset">Clear</button>
</div>
@Html.AntiForgeryToken()
</form>
</div>
</div>

View file

@ -1,12 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace MTWireGuard.Pages.Components.CreateClientForm
{
public class CreateClientFormModel : PageModel
{
public void OnGet()
{
}
}
}

View file

@ -1,64 +0,0 @@
@using MTWireGuard.Models.Requests
@model CreateServerRequest
<div class="card mb-4">
<div class="card-header">Quick add server</div>
<div class="card-body">
<form asp-page="Servers" asp-page-handler="Create" method="POST">
<div class="mb-3 row">
<label for="WGSName" class="col-sm-3 col-form-label user-select-none">Name</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="WGSName" name="Name" placeholder="wireguard1" required>
</div>
</div>
<div class="mb-3 row">
<label for="WGSPort" class="col-sm-3 col-form-label user-select-none">Listening Port</label>
<div class="col-sm-9">
<input type="number" class="form-control" id="WGSPort" name="Port" placeholder="13231" min="1" max="65535">
</div>
</div>
<div class="mb-3 row">
<label for="WGSMTU" class="col-sm-3 col-form-label user-select-none">MTU</label>
<div class="col-sm-9">
<input type="number" class="form-control" id="WGSMTU" name="MTU" placeholder="1420" min="64" max="65535">
</div>
</div>
<div class="mb-3 row">
<label for="WGSPrivKey" class="col-sm-3 col-form-label user-select-none">Private Key</label>
<div class="col-sm-9">
<div class="input-group">
<input type="text" class="form-control" id="WGSPrivKey" name="PrivateKey" aria-label="Generate" aria-describedby="SKeygenBTN" autocomplete="off" required>
<button type="button" class="input-group-text" id="SKeygenBTN" style="cursor: pointer;" onclick="generateKeys(this)">
<svg class="icon icon-sm text-dark my-1">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-reload"></use>
</svg>
</button>
</div>
</div>
</div>
<div class="mb-3 row">
<label for="WGSPubKey" class="col-sm-3 col-form-label user-select-none">Public Key</label>
<div class="col-sm-9">
<div class="input-group">
<input type="text" class="form-control" id="WGSPubKey" aria-label="Generate" aria-describedby="SUnlockBTN" autocomplete="off" disabled>
<button type="button" class="input-group-text" id="SUnlockBTN" style="cursor: pointer;" onclick="changeIcon(this)">
<svg class="icon icon-sm text-dark my-1">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-lock-unlocked"></use>
</svg>
</button>
</div>
</div>
</div>
<div class="mb-5 row">
<label for="WGSEnabled" class="col-sm-3 form-check-label user-select-none">Enabled</label>
<div class="col-sm-9">
<input type="checkbox" class="form-check-input" id="WGSEnabled" asp-for="Enabled" checked>
</div>
</div>
<div class="col-12">
<button class="btn btn-primary" type="submit">Create</button>
<button class="btn btn-danger" type="reset">Clear</button>
</div>
@Html.AntiForgeryToken()
</form>
</div>
</div>

View file

@ -1,12 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace MTWireGuard.Pages.Components.CreateServerForm
{
public class CreateServerFormModel : PageModel
{
public void OnGet()
{
}
}
}

View file

@ -1,31 +0,0 @@
@using MTWireGuard.Models.Requests
@model DeleteRequest
@{
bool IsServer = (bool)ViewData["IsServer"];
}
<div class="modal fade" id="DeleteModal" tabindex="-1" aria-labelledby="DeleteModalLabel" aria-hidden="true">
<div class="modal-dialog">
@{
string FormAction = IsServer ? "Servers/Delete" : "Clients/Delete";
string Type = IsServer ? "server interface" : "client account";
}
<form class="modal-content" action="@FormAction" method="POST">
<div class="modal-header">
<h5 class="modal-title" id="DeleteModalLabel">Are you sure ?</h5>
<button type="button" class="btn-close" data-coreui-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>
Are you sure to delete this @Type ?<br />
This can't be undone!
</p>
<input type="hidden" asp-for="Id" />
</div>
<div class="modal-footer">
<button type="reset" class="btn btn-secondary" data-coreui-dismiss="modal">Close</button>
<button type="submit" class="btn btn-danger">Delete</button>
</div>
@Html.AntiForgeryToken()
</form>
</div>
</div>

View file

@ -1,12 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace MTWireGuard.Pages.Components.DeleteModal
{
public class DeleteModalModel : PageModel
{
public void OnGet()
{
}
}
}

View file

@ -0,0 +1,21 @@
<div class="modal fade" id="RemoveModal" tabindex="-1" aria-labelledby="RemoveModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<form class="modal-content" id="removeForm" onsubmit="return false">
<div class="modal-header">
<h5 class="modal-title" id="RemoveModalLabel">Are you sure ?</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>
Are you sure to delete this item ?<br />
This can't be undone!
</p>
<input type="hidden" name="ID">
</div>
<div class="modal-footer">
<button type="reset" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="submit" class="btn btn-danger">Delete</button>
</div>
</form>
</div>
</div>

View file

@ -0,0 +1,18 @@
<div class="modal fade dtr-bs-modal" id="DetailsModal" tabindex="-1" aria-labelledby="DetailsModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="DetailsModalLabel">Details of N/A</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body p-1"></div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
@code {
}

View file

@ -0,0 +1,17 @@
<div class="modal modal-sm fade" id="QRModal" tabindex="-1" aria-labelledby="QRModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="QRModalLabel">user's QR Code</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body text-center m-0 p-0">
<img id="qr-src" src="" alt="QR" width="256" height="256">
</div>
</div>
</div>
</div>
@code {
}

View file

@ -0,0 +1,95 @@
<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasAddUser" aria-labelledby="offcanvasAddUserLabel">
<div class="offcanvas-header">
<h5 id="offcanvasAddUserLabel" class="offcanvas-title">Add User</h5>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0">
<form class="add-new-user pt-0" id="addUserForm" onsubmit="return false">
<div class="mb-3">
<label class="form-label" for="add-username">Username</label>
<input type="text" class="form-control" id="add-username" name="Username" aria-label="Username" />
</div>
<div class="form-password-toggle mb-3">
<label class="form-label" for="add-password">Password</label>
<div class="input-group input-group-merge">
<input type="password" class="form-control" id="add-password" name="Password" placeholder="&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;" aria-describedby="add-password-eye" />
<span id="add-password-eye" class="input-group-text password-eye"><i class="bx bx-hide"></i></span>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="add-interface">Interface</label>
<select id="add-interface" name="Interface" class="form-select"></select>
</div>
<div class="mb-3">
<label class="form-label" for="add-allowed-address">Allowed Address</label>
<div class="input-group">
<div class="input-group-text">
<input class="form-check-input mt-0 me-1" type="checkbox" id="add-ip-dynamic" name="InheritIP" aria-label="IP from pool" checked>
<label class="form-check-label" for="add-ip-dynamic">Dynamic</label>
</div>
<input id="add-allowed-address" name="AllowedAddress" type="text" class="form-control" aria-label="Allowed Address" aria-describedby="add-ip-cidr" disabled>
<span class="input-group-text" id="add-ip-cidr">/32</span>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="add-dns-server">DNS Server</label>
<div class="input-group">
<div class="input-group-text">
<input class="form-check-input mt-0 me-1" type="checkbox" id="add-dns-dynamic" name="InheritDNS" aria-label="DNS from interface" checked>
<label class="form-check-label" for="add-dns-dynamic">Inherit</label>
</div>
<input type="text" class="form-control" id="add-dns-server" name="DNSAddress" aria-label="DNS Address" disabled>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="add-private-key">Private Key</label>
<div class="input-group input-group-merge">
<input type="text" id="add-private-key" name="PrivateKey" class="form-control" aria-label="Private Key" aria-describedby="add-keygen" />
<span id="add-keygen" class="input-group-text keygen-button"><i class="bx bx-refresh"></i></span>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="add-public-key">Public Key</label>
<div class="input-group input-group-merge">
<input type="text" id="add-public-key" name="PublicKey" class="form-control" aria-label="Public Key" aria-describedby="add-key-reset" />
<span id="add-key-reset" class="input-group-text key-reset-button"><i class="bx bx-reset"></i></span>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="add-preshared-key">Preshared Key</label>
<input type="text" id="add-preshared-key" name="PresharedKey" class="form-control" aria-label="Preshared Key" />
</div>
<div class="mb-3">
<label class="form-label" for="add-endpoint">Endpoint</label>
<input type="text" id="add-endpoint" name="Endpoint" class="form-control" aria-label="Endpoint" />
</div>
<div class="mb-3">
<label class="form-label" for="add-endpoint-port">Endpoint Port</label>
<input type="number" id="add-endpoint-port" name="EndpointPort" class="form-control" aria-label="Endpoint Port" />
</div>
<div class="mb-3">
<label class="form-label" for="add-keepalive">Keepalive</label>
<input type="number" id="add-keepalive" name="KeepAlive" class="form-control" aria-label="Keepalive" placeholder="seconds" />
</div>
<div class="mb-3">
<label class="form-label" for="add-expire">Expire</label>
<input type="datetime" id="add-expire" name="Expire" class="form-control" aria-label="Expire" />
</div>
<div class="mb-3">
<label class="form-label" for="add-traffic">Traffic</label>
<div class="input-group">
<input id="add-traffic" name="Traffic" type="number" class="form-control" aria-label="Traffic" aria-describedby="add-gigabyte">
<span class="input-group-text" id="add-gigabyte">GB</span>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="add-enabled">Enabled</label>
<button type="button" class="btn btn-toggle active" id="add-enabled" name="Enabled" data-bs-toggle="button" aria-pressed="true" autocomplete="off">
<div class="handle"></div>
</button>
</div>
<button type="submit" class="btn btn-success me-sm-3 me-1 data-submit">Submit</button>
<button type="reset" class="btn btn-danger" data-bs-dismiss="offcanvas">Cancel</button>
</form>
</div>
</div>

View file

@ -0,0 +1,38 @@
<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasSyncUser" aria-labelledby="offcanvasSyncUserLabel">
<div class="offcanvas-header">
<h5 id="offcanvasSyncUserLabel" class="offcanvas-title">Sync User</h5>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0">
<form class="add-new-user pt-0" id="syncUserForm" onsubmit="return false">
<input type="hidden" id="sync-id" name="ID">
<div class="mb-3">
<label class="form-label" for="sync-username">Username</label>
<input type="text" class="form-control" id="sync-username" name="Username" aria-label="Username" />
</div>
<div class="form-password-toggle mb-3">
<label class="form-label" for="sync-password">Password</label>
<div class="input-group input-group-merge">
<input type="password" class="form-control" id="sync-password" name="Password" placeholder="&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;" aria-describedby="sync-password-eye" />
<span id="sync-password-eye" class="input-group-text password-eye"><i class="bx bx-hide"></i></span>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="sync-private-key">Private Key</label>
<div class="input-group input-group-merge">
<input type="text" id="sync-private-key" name="PrivateKey" class="form-control" aria-label="Private Key" aria-describedby="sync-keygen" />
<span id="sync-keygen" class="input-group-text keygen-button"><i class="bx bx-refresh"></i></span>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="sync-public-key">Public Key</label>
<div class="input-group input-group-merge">
<input type="text" id="sync-public-key" name="PublicKey" class="form-control" aria-label="Public Key" aria-describedby="sync-key-reset" />
<span id="sync-key-reset" class="input-group-text key-reset-button"><i class="bx bx-reset"></i></span>
</div>
</div>
<button type="submit" class="btn btn-primary me-sm-3 me-1 data-submit">Submit</button>
<button type="reset" class="btn btn-label-secondary" data-bs-dismiss="offcanvas">Cancel</button>
</form>
</div>
</div>

View file

@ -0,0 +1,93 @@
<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasEditUser" aria-labelledby="offcanvasEditUserLabel">
<div class="offcanvas-header">
<h5 id="offcanvasEditUserLabel" class="offcanvas-title">Edit User</h5>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0 pt-0">
<div class="text-center shadow rounded-pill border-bottom mb-3">
<small class="text-truncate text-info user-select-none">Empty fields will remain unchanged.</small>
</div>
<form class="add-new-user pt-0" id="editUserForm" onsubmit="return false">
<input type="hidden" id="edit-id" name="ID">
<div class="mb-3">
<label class="form-label" for="edit-username">Username</label>
<input type="text" class="form-control" id="edit-username" name="Username" aria-label="Username" />
</div>
<div class="form-password-toggle mb-3">
<label class="form-label" for="edit-password">Password</label>
<div class="input-group input-group-merge">
<input type="password" class="form-control" id="edit-password" name="Password" placeholder="&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;" aria-describedby="edit-password-eye" />
<span id="edit-password-eye" class="input-group-text password-eye"><i class="bx bx-hide"></i></span>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="edit-interface">Interface</label>
<select class="form-select" id="edit-interface" name="Interface"></select>
</div>
<div class="mb-3">
<label class="form-label" for="edit-allowed-address">Allowed Address</label>
<div class="input-group">
<div class="input-group-text">
<input class="form-check-input mt-0 me-1" type="checkbox" id="edit-ip-dynamic" name="InheritIP" aria-label="IP from pool" checked>
<label class="form-check-label" for="edit-ip-dynamic">Dynamic</label>
</div>
<input id="edit-allowed-address" name="AllowedAddress" type="text" class="form-control" aria-label="Allowed Address" aria-describedby="edit-ip-cidr" disabled>
<span class="input-group-text" id="edit-ip-cidr">/32</span>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="edit-dns-address">DNS Server</label>
<div class="input-group">
<div class="input-group-text">
<input class="form-check-input mt-0 me-1" type="checkbox" id="edit-dns-dynamic" name="InheritDNS" aria-label="DNS from interface" checked>
<label class="form-check-label" for="edit-dns-dynamic">Inherit</label>
</div>
<input type="text" class="form-control" id="edit-dns-address" name="DNSAddress" aria-label="DNS Address" disabled>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="edit-private-key">Private Key</label>
<div class="input-group input-group-merge">
<input type="text" id="edit-private-key" name="PrivateKey" class="form-control" aria-label="Private Key" aria-describedby="edit-keygen" />
<span id="edit-keygen" class="input-group-text keygen-button"><i class="bx bx-refresh"></i></span>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="edit-public-key">Public Key</label>
<div class="input-group input-group-merge">
<input type="text" id="edit-public-key" name="PublicKey" class="form-control" aria-label="Public Key" aria-describedby="edit-key-reset" />
<span id="edit-key-reset" class="input-group-text key-reset-button"><i class="bx bx-reset"></i></span>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="edit-preshared-key">Preshared Key</label>
<input type="text" id="edit-preshared-key" name="PresharedKey" class="form-control" aria-label="Public Key" />
</div>
<div class="mb-3">
<label class="form-label" for="edit-endpoint">Endpoint</label>
<input type="text" id="edit-endpoint" name="Endpoint" class="form-control" aria-label="Endpoint" />
</div>
<div class="mb-3">
<label class="form-label" for="edit-endpoint-port">Endpoint Port</label>
<input type="number" id="edit-endpoint-port" name="EndpointPort" class="form-control" aria-label="Endpoint Port" />
</div>
<div class="mb-3">
<label class="form-label" for="edit-keepalive">Keepalive</label>
<input type="number" id="edit-keepalive" name="KeepAlive" class="form-control" aria-label="Keepalive" placeholder="seconds" />
</div>
<div class="mb-3">
<label class="form-label" for="edit-expire">Expire</label>
<input type="datetime" id="edit-expire" name="Expire" class="form-control" aria-label="Expire" />
</div>
<div class="mb-3">
<label class="form-label" for="edit-traffic">Traffic</label>
<div class="input-group">
<input id="edit-traffic" name="Traffic" type="number" class="form-control" aria-label="Traffic" aria-describedby="edit-gigabyte">
<span class="input-group-text" id="edit-gigabyte">GB</span>
</div>
</div>
<button type="submit" class="btn btn-success me-sm-3 me-1 data-submit">Submit</button>
<button type="reset" class="btn btn-danger" data-bs-dismiss="offcanvas">Cancel</button>
</form>
</div>
</div>

View file

@ -0,0 +1,69 @@
<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasAddServer" aria-labelledby="offcanvasAddServerLabel">
<div class="offcanvas-header">
<h5 id="offcanvasAddServerLabel" class="offcanvas-title">Add Server</h5>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0">
<form class="add-new-server pt-0" id="addServerForm" onsubmit="return false">
<div class="mb-3">
<label class="form-label" for="add-name">Name</label>
<input type="text" class="form-control" id="add-name" name="Name" aria-label="Name" />
</div>
<div class="mb-3">
<label class="form-label" for="add-ip-address">IP Address</label>
<div class="input-group">
<input id="add-ip-address" name="IPAddress" type="text" class="form-control" aria-label="IP Address" aria-describedby="add-ip-cidr">
<select id="add-ip-cidr" name="IPCidr" class="input-group-text form-select-sm">
<option value="8">8</option>
<option value="16">16</option>
<option value="24" selected>24</option>
</select>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="add-pool">IP Pool</label>
<div class="input-group">
<div class="input-group-text">
<input class="form-check-input mt-0 me-1" type="checkbox" id="add-static-addressing" name="UseIPPool" aria-label="Use IP pool" checked>
<label class="form-check-label" for="add-static-addressing">Static</label>
</div>
<select class="form-select" id="add-pool" name="IPPoolId" aria-label="IP Pool" disabled></select>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="add-dns-server">DNS Server</label>
<div class="input-group">
<div class="input-group-text">
<input class="form-check-input mt-0 me-1" type="checkbox" id="add-dns-dynamic" name="InheritDNS" aria-label="DNS from interface" checked>
<label class="form-check-label" for="add-dns-dynamic">Inherit</label>
</div>
<input type="text" class="form-control" id="add-dns-server" name="DNSAddress" aria-label="DNS Address" disabled>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="add-private-key">Private Key</label>
<div class="input-group input-group-merge">
<input type="text" id="add-private-key" name="PrivateKey" class="form-control" aria-label="Private Key" aria-describedby="add-keygen" />
<span id="add-keygen" class="input-group-text keygen-button"><i class="bx bx-refresh"></i></span>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="add-public-key">Public Key</label>
<div class="input-group input-group-merge">
<input type="text" id="add-public-key" name="PublicKey" class="form-control" aria-label="Public Key" aria-describedby="add-key-reset" />
<span id="add-key-reset" class="input-group-text key-reset-button"><i class="bx bx-reset"></i></span>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="add-port">Listen Port</label>
<input type="number" id="add-port" name="Port" class="form-control" aria-label="Listen Port" />
</div>
<div class="mb-3">
<label class="form-label" for="add-mtu">MTU</label>
<input type="number" id="add-mtu" name="MTU" class="form-control" aria-label="MTU" />
</div>
<button type="submit" class="btn btn-success me-sm-3 me-1 data-submit">Submit</button>
<button type="reset" class="btn btn-danger" data-bs-dismiss="offcanvas">Cancel</button>
</form>
</div>
</div>

View file

@ -0,0 +1,70 @@
<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasEditServer" aria-labelledby="offcanvasEditServerLabel">
<div class="offcanvas-header">
<h5 id="offcanvasEditServerLabel" class="offcanvas-title">Edit Server</h5>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0">
<form class="edit-new-server pt-0" id="editServerForm" onsubmit="return false">
<input type="hidden" id="edit-id" name="ID">
<div class="mb-3">
<label class="form-label" for="edit-name">Name</label>
<input type="text" class="form-control" id="edit-name" name="Name" aria-label="Name" />
</div>
<div class="mb-3">
<label class="form-label" for="edit-ip-address">IP Address</label>
<div class="input-group">
<input id="edit-ip-address" name="IPAddress" type="text" class="form-control" aria-label="IP Address" aria-describedby="edit-ip-cidr">
<select id="edit-ip-cidr" name="IPCidr" class="input-group-text form-select-sm">
<option value="8">8</option>
<option value="16">16</option>
<option value="24" selected>24</option>
</select>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="edit-pool">IP Pool</label>
<div class="input-group">
<div class="input-group-text">
<input class="form-check-input mt-0 me-1" type="checkbox" id="edit-static-addressing" name="UseIPPool" aria-label="Use IP pool" checked>
<label class="form-check-label" for="edit-static-addressing">Static</label>
</div>
<select class="form-select" id="edit-pool" name="IPPoolId" aria-label="IP Pool" disabled></select>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="edit-dns-server">DNS Server</label>
<div class="input-group">
<div class="input-group-text">
<input class="form-check-input mt-0 me-1" type="checkbox" id="edit-dns-dynamic" name="InheritDNS" aria-label="DNS from interface" checked>
<label class="form-check-label" for="edit-dns-dynamic">Inherit</label>
</div>
<input type="text" class="form-control" id="edit-dns-server" name="DNSAddress" aria-label="DNS Address" disabled>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="edit-private-key">Private Key</label>
<div class="input-group input-group-merge">
<input type="text" id="edit-private-key" name="PrivateKey" class="form-control" aria-label="Private Key" aria-describedby="edit-keygen" />
<span id="edit-keygen" class="input-group-text keygen-button"><i class="bx bx-refresh"></i></span>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="edit-public-key">Public Key</label>
<div class="input-group input-group-merge">
<input type="text" id="edit-public-key" name="PublicKey" class="form-control" aria-label="Public Key" aria-describedby="edit-key-reset" />
<span id="edit-key-reset" class="input-group-text key-reset-button"><i class="bx bx-reset"></i></span>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="edit-port">Listen Port</label>
<input type="number" id="edit-port" name="Port" class="form-control" aria-label="Listen Port" />
</div>
<div class="mb-3">
<label class="form-label" for="edit-mtu">MTU</label>
<input type="number" id="edit-mtu" name="MTU" class="form-control" aria-label="MTU" />
</div>
<button type="submit" class="btn btn-success me-sm-3 me-1 data-submit">Submit</button>
<button type="reset" class="btn btn-danger" data-bs-dismiss="offcanvas">Cancel</button>
</form>
</div>
</div>

View file

@ -1,50 +0,0 @@
@using MTWireGuard.Models.Requests
@model SyncUserRequest
<div class="modal fade" id="SyncModal" tabindex="-1" aria-labelledby="SyncModalLabel" aria-hidden="true">
<div class="modal-dialog">
<form class="modal-content" asp-page="Clients" asp-page-handler="Sync" method="POST">
<div class="modal-header">
<h5 class="modal-title" id="SyncModalLabel">Are you sure ?</h5>
<button type="button" class="btn-close" data-coreui-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>
By syncing, the values of unsynced properties will be changed to the value stored in the database.<br />
Peer's Private Key is required to create client tunnel file or QR code.<br />
<strong>All keys (such as private key and public key) are only stored in your docker machine and no any information is sent to outside your network.</strong>
</p>
<div class="mb-3 row">
<label for="WGPName" class="col-sm-3 col-form-label user-select-none">Name</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="WGPName" name="Name">
</div>
</div>
<div class="mb-3 row">
<label for="WGPPrivKey" class="col-sm-3 col-form-label user-select-none">Private Key</label>
<div class="col-sm-9">
<div class="input-group">
<input type="text" class="form-control" id="WGUPrivKey" name="PrivateKey" aria-label="Generate" aria-describedby="PKeygenBTN">
<button type="button" class="input-group-text" id="PKeygenBTN" style="cursor: pointer;" onclick="generateKeys(this)">
<svg class="icon icon-sm text-dark my-1">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-reload"></use>
</svg>
</button>
</div>
</div>
</div>
<div class="mb-3 row">
<label for="WGPPubKey" class="col-sm-3 col-form-label user-select-none">Public Key</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="WGUPubKey" name="PublicKey">
</div>
</div>
<input type="hidden" asp-for="ID" />
</div>
<div class="modal-footer">
<button type="reset" class="btn btn-danger" data-coreui-dismiss="modal">Close</button>
<button type="submit" class="btn btn-success">Sync</button>
</div>
@Html.AntiForgeryToken()
</form>
</div>
</div>

View file

@ -1,12 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace MTWireGuard.Pages.Components.SyncUserModal
{
public class SyncUserModalModel : PageModel
{
public void OnGet()
{
}
}
}

View file

@ -1,6 +1,4 @@
<div class="toast-container position-fixed bottom-0 end-0 p-3" id="toastContainer">
</div>
<div class="toast-container position-fixed bottom-0 end-0 p-3"></div>
@code {

View file

@ -1,107 +0,0 @@
@using MTWireGuard.Application.Models.Mikrotik
@using MTWireGuard.Models.Requests
@model UpdateClientRequest
@{
List<WGServerViewModel> Servers = ViewData["Servers"] as List<WGServerViewModel>;
}
<div class="modal fade" id="EditModal" tabindex="-1" aria-labelledby="EditModalLabel" aria-hidden="true">
<div class="modal-dialog">
<form class="modal-content" action="Clients/Update" method="POST">
<div class="modal-header">
<h5 class="modal-title" id="EditModalLabel">Edit user details</h5>
<button type="button" class="btn-close" data-coreui-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3 row">
<label for="WGPUName" class="col-sm-3 col-form-label user-select-none">Name</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="WGPUName" name="Name">
</div>
</div>
<div class="mb-3 row">
<label for="WGPUInterface" class="col-sm-3 col-form-label user-select-none">Interface</label>
<div class="col-sm-9">
<select class="form-select" id="WGPUInterface" name="Interface">
@foreach (var server in Servers)
{
@if (server.IsEnabled)
{
<option value="@server.Name">@server.Name</option>
}
else
{
<option class="fst-italic" value="@server.Name">@server.Name</option>
}
}
</select>
</div>
</div>
<div class="mb-3 row">
<label for="WGPUPrivKey" class="col-sm-3 col-form-label user-select-none">Private Key</label>
<div class="col-sm-9">
<div class="input-group">
<input type="text" class="form-control" id="WGPUPrivKey" name="PrivateKey" aria-label="Generate" aria-describedby="PKeygenBTN" autocomplete="off">
<button type="button" class="input-group-text" id="PKeygenBTN" style="cursor: pointer;" onclick="generateKeys(this)">
<svg class="icon icon-sm text-dark my-1">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-reload"></use>
</svg>
</button>
</div>
</div>
</div>
<div class="mb-3 row">
<label for="WGPUPubKey" class="col-sm-3 col-form-label user-select-none">Public Key</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="WGPUPubKey" name="PublicKey" autocomplete="off">
</div>
</div>
<div class="mb-3 row">
<label for="WGPUPSKey" class="col-sm-3 col-form-label user-select-none">Preshared Key</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="WGPUPSKey" name="PresharedKey" autocomplete="off">
</div>
</div>
<div class="mb-3 row">
<label for="WGPUAddress" class="col-sm-3 col-form-label user-select-none">Allowed Address</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="WGPUAddress" name="AllowedAddress">
</div>
</div>
<div class="mb-3 row">
<label for="WGPUEndpoint" class="col-sm-3 col-form-label user-select-none">Endpoint</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="WGPUEndpoint" name="Endpoint">
</div>
</div>
<div class="mb-3 row">
<label for="WGPUPort" class="col-sm-3 col-form-label user-select-none">Endpoint Port</label>
<div class="col-sm-9">
<input type="number" class="form-control" id="WGPUPort" name="EndpointPort">
</div>
</div>
<div class="mb-3 row">
<label for="WGPUKeepAlive" class="col-sm-3 col-form-label user-select-none">Persistent Keepalive</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="WGPUKeepAlive" name="KeepAlive" placeholder="Seconds">
</div>
</div>
<div class="mb-3 row">
<label for="WGPUExpireInput" class="col-sm-3 col-form-label user-select-none">Expire</label>
<div class="input-group w-75 col-sm-9 date-input" id="WGPUExpire" data-td-target-input="nearest" data-td-target-toggle="nearest">
<input id="WGPUExpireInput" type="text" class="form-control" data-td-target="#WGPUExpire" />
<span class="input-group-text" data-td-target="#WGPUExpire" data-td-toggle="datetimepicker">
<i class="fas fa-calendar"></i>
</span>
</div>
</div>
<input type="hidden" asp-for="ID" />
</div>
<div class="modal-footer">
<button type="reset" class="btn btn-secondary" data-coreui-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Update</button>
</div>
@Html.AntiForgeryToken()
</form>
</div>
</div>

View file

@ -1,12 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace MTWireGuard.Pages.Components.UpdateClientModal
{
public class UpdateClientModalModel : PageModel
{
public void OnGet()
{
}
}
}

View file

@ -1,65 +0,0 @@
@using MTWireGuard.Models.Requests
@model UpdateServerRequest
<div class="modal fade" id="EditModal" tabindex="-1" aria-labelledby="EditModalLabel" aria-hidden="true">
<div class="modal-dialog">
<form class="modal-content" action="Servers/Update" method="POST">
<div class="modal-header">
<h5 class="modal-title" id="EditModalLabel">Edit server details</h5>
<button type="button" class="btn-close" data-coreui-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3 row">
<label for="WGSName" class="col-sm-3 col-form-label user-select-none">Name</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="WGSName" name="Name" placeholder="wireguard1">
</div>
</div>
<div class="mb-3 row">
<label for="WGSPort" class="col-sm-3 col-form-label user-select-none">Listening Port</label>
<div class="col-sm-9">
<input type="number" class="form-control" id="WGSPort" name="Port" placeholder="13231" min="1" max="65535">
</div>
</div>
<div class="mb-3 row">
<label for="WGSMTU" class="col-sm-3 col-form-label user-select-none">MTU</label>
<div class="col-sm-9">
<input type="number" class="form-control" id="WGSMTU" name="MTU" placeholder="1420" min="64" max="65535">
</div>
</div>
<div class="mb-3 row">
<label for="WGSPrivKey" class="col-sm-3 col-form-label user-select-none">Private Key</label>
<div class="col-sm-9">
<div class="input-group">
<input type="text" class="form-control" id="WGSPrivKey" name="PrivateKey" aria-label="Generate" aria-describedby="SKeygenBTN" autocomplete="off">
<button type="button" class="input-group-text" id="SKeygenBTN" style="cursor: pointer;" onclick="generateKeys(this)">
<svg class="icon icon-sm text-dark my-1">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-reload"></use>
</svg>
</button>
</div>
</div>
</div>
<div class="mb-3 row">
<label for="WGSPubKey" class="col-sm-3 col-form-label user-select-none">Public Key</label>
<div class="col-sm-9">
<div class="input-group">
<input type="text" class="form-control" id="WGSPubKey" aria-label="Generate" aria-describedby="SUnlockBTN" autocomplete="off">
<button type="button" class="input-group-text" id="SUnlockBTN" style="cursor: pointer;" onclick="changeIcon(this);">
<svg class="icon icon-sm text-dark my-1">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-lock-unlocked"></use>
</svg>
</button>
</div>
</div>
</div>
<input type="hidden" name="ID" />
</div>
<div class="modal-footer">
<button type="reset" class="btn btn-secondary" data-coreui-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Update</button>
</div>
@Html.AntiForgeryToken()
</form>
</div>
</div>

View file

@ -1,12 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace MTWireGuard.Pages.Components.UpdateServerModal
{
public class UpdateServerModalModel : PageModel
{
public void OnGet()
{
}
}
}

View file

@ -7,52 +7,51 @@
<!DOCTYPE html>
<html lang="en">
<head>
<base href="./">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<meta name="description" content="CoreUI - Open Source Bootstrap Admin Template">
<meta name="author" content="Łukasz Holeczek">
<meta name="keyword" content="Bootstrap,Admin,Template,Open,Source,jQuery,CSS,HTML,RWD,Dashboard">
<meta name="description" content="Mikrotik Wireguard management panel">
<meta name="author" content="Kazem Ma79">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="keywords" content="Mikrotik, RouterOS, Wireguard, MTWireguard">
<title>Error</title>
<link rel="apple-touch-icon" sizes="57x57" href="assets/favicon/apple-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="assets/favicon/apple-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="assets/favicon/apple-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="assets/favicon/apple-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="assets/favicon/apple-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="assets/favicon/apple-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="assets/favicon/apple-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="assets/favicon/apple-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="assets/favicon/apple-icon-180x180.png">
<link rel="icon" type="image/png" sizes="192x192" href="assets/favicon/android-icon-192x192.png">
<link rel="icon" type="image/png" sizes="32x32" href="assets/favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="assets/favicon/favicon-96x96.png">
<link rel="icon" type="image/png" sizes="16x16" href="assets/favicon/favicon-16x16.png">
<link rel="manifest" href="assets/favicon/manifest.json">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="assets/favicon/ms-icon-144x144.png">
<meta name="theme-color" content="#ffffff">
<!-- Vendors styles-->
<link rel="stylesheet" href="vendors/simplebar/css/simplebar.css">
<link rel="stylesheet" href="css/vendors/simplebar.css">
<!-- Main styles for this application-->
<link href="css/style.css" rel="stylesheet">
<!-- Favicon -->
<link href="img/favicon.ico" rel="icon">
<!-- Google Web Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Heebo:wght@400;500;600;700&display=swap" rel="stylesheet">
<!-- Icon Font Stylesheet -->
<link href="assets/lib/boxicons/css/boxicons.min.css" rel="stylesheet">
<link href="assets/libs/fortawesome/fontawesome-free/css/all.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.4.1/font/bootstrap-icons.css" rel="stylesheet">
<!-- Template Stylesheet -->
<link href="assets/css/core.css" rel="stylesheet">
<!-- Theme Style Switcher-->
<script src="assets/js/themeSwitcher.js"></script>
</head>
<body>
<div class="bg-light min-vh-100 d-flex flex-row align-items-center">
<div class="min-vh-100 d-flex flex-row align-items-center">
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="clearfix">
<h1 class="float-start display-3 me-4">500</h1>
<h4 class="pt-3">@ViewBag.Title</h4>
<p class="text-medium-emphasis">@Html.Raw(ViewBag.Message)</p>
<h6>@ViewBag.Message</h6>
<p class="text-medium-emphasis">@Html.Raw(ViewBag.Details)</p>
</div>
</div>
</div>
</div>
</div>
<script src="vendors/coreui/coreui/js/coreui.bundle.min.js"></script>
<script src="vendors/simplebar/js/simplebar.min.js"></script>
<!-- JavaScript Libraries -->
<script src="assets/libs/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="assets/lib/jquery/js/jquery.min.js"></script>
<script src="assets/libs/jquery-ui/jquery-ui.min.js"></script>
</body>
</html>

View file

@ -9,24 +9,9 @@ namespace MTWireGuard.Pages
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
public string? RequestId { get; set; }
public string? ExceptionMessage { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public ErrorModel()
{
}
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
var exceptionHandlerPathFeature =
HttpContext.Features.Get<IExceptionHandlerPathFeature>();
ViewData["Title"] = exceptionHandlerPathFeature.Error.Message;
ViewData["Message"] = exceptionHandlerPathFeature.Error.Source;
}
}
}

View file

@ -3,97 +3,201 @@
@model IndexModel
@{
ViewData["Title"] = "Dashboard";
ViewData["Breadcrumb"] = "Home";
var users = (List<WGPeerViewModel>)ViewData["users"];
var servers = (List<WGServerViewModel>)ViewData["servers"];
var uptr = ViewData["uptr"];
var downtr = ViewData["downtr"];
}
<div class="row">
<div class="col-sm-6 col-lg-4">
<div class="card mb-4" style="--cui-card-cap-bg: #88171a">
<div class="card-header position-relative d-flex justify-content-center align-items-center">
<svg class="icon icon-3xl text-white my-4">
<use xlink:href="vendors/coreui/icons/svg/brand.svg#cib-wireguard"></use>
</svg>
<div class="chart-wrapper position-absolute top-0 start-0 w-100 h-100">
<canvas id="social-box-chart-1" height="90"></canvas>
<h3>Server/Clients Brief</h3>
<div class="row g-4 mb-4">
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span>Total Servers</span>
<div class="d-flex align-items-end mt-2">
<h4 class="mb-0 me-2" id="servers-total">0</h4>
</div>
</div>
<div class="card-body row text-center">
<div class="col">
<div class="fs-5 fw-semibold">@servers.Count</div>
<div class="text-uppercase text-medium-emphasis small">Servers</div>
</div>
<div class="vr"></div>
<div class="col">
<div class="fs-5 fw-semibold">@servers.Where(x => x.Running).Count()</div>
<div class="text-uppercase text-medium-emphasis small">Active</div>
<div class="avatar">
<span class="avatar-initial rounded bg-primary">
<i class="bx bx-radar bx-sm"></i>
</span>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-lg-4">
<div class="card mb-4" style="--cui-card-cap-bg: #0d6efd">
<div class="card-header position-relative d-flex justify-content-center align-items-center">
<svg class="icon icon-3xl text-white my-4">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-lan"></use>
</svg>
<div class="chart-wrapper position-absolute top-0 start-0 w-100 h-100">
<canvas id="social-box-chart-1" height="90"></canvas>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span>Active Servers</span>
<div class="d-flex align-items-end mt-2">
<h4 class="mb-0 me-2" id="servers-active">0</h4>
</div>
</div>
<div class="card-body row text-center">
<div class="col">
<div class="fs-5 fw-semibold">@users.Count</div>
<div class="text-uppercase text-medium-emphasis small">Users</div>
</div>
<div class="vr"></div>
<div class="col">
<div class="fs-5 fw-semibold">@users.Where(x => !x.CurrentAddress.EndsWith(":0")).Count()</div>
<div class="text-uppercase text-medium-emphasis small">Online</div>
<div class="avatar">
<span class="avatar-initial rounded bg-danger">
<i class="bx bx-broadcast bx-sm"></i>
</span>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-lg-4">
<div class="card mb-4" style="--cui-card-cap-bg: #0dcaf0">
<div class="card-header position-relative d-flex justify-content-center align-items-center">
<svg class="icon icon-3xl text-white my-4">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-graph"></use>
</svg>
<div class="chart-wrapper position-absolute top-0 start-0 w-100 h-100">
<canvas id="social-box-chart-1" height="90"></canvas>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span>Total Users</span>
<div class="d-flex align-items-end mt-2">
<h4 class="mb-0 me-2" id="users-total">0</h4>
</div>
</div>
<div class="card-body row text-center">
<div class="col">
<div class="fs-5 fw-semibold">@uptr</div>
<div class="text-uppercase text-medium-emphasis small">Upload</div>
<div class="avatar">
<span class="avatar-initial rounded bg-warning">
<i class="bx bx-group bx-sm"></i>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span>Online Users</span>
<div class="d-flex align-items-end mt-2">
<h4 class="mb-0 me-2" id="users-online">0</h4>
</div>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-success">
<i class="bx bx-user-voice bx-sm"></i>
</span>
</div>
<div class="vr"></div>
<div class="col">
<div class="fs-5 fw-semibold">@downtr</div>
<div class="text-uppercase text-medium-emphasis small">Download</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12 col-lg-6">
@await Component.InvokeAsync("CreateServerForm")
<div class="row g-4">
<div class="col-sm-12 col-md-6">
<div class="card">
<div class="card-header">
<h5 class="card-title">Mikrotik Statistics</h5>
</div>
<div class="card-body">
<table class="table table-responsive">
<tbody>
<tr>
<th>Identity</th>
<td id="info-identity"></td>
</tr>
<tr>
<th>Device</th>
<td id="info-device"></td>
</tr>
<tr>
<th>OS Version</th>
<td id="info-version"></td>
</tr>
<tr>
<th>Public IPv4</th>
<td id="info-ip"></td>
</tr>
<tr>
<th>DNS Servers</th>
<td id="info-dns"></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-sm-12 col-md-3">
<div class="card">
<div class="card-header text-center">
<h5 class="card-title">Recently Online Users</h5>
</div>
<div class="card-body">
<div class="card-datatable table-responsive pt-0">
<table class="datatables-basic table table-striped table-hover table-bordered">
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Last Seen</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
<div class="col-sm-12 col-md-3">
<div class="card">
<div class="card-header text-center">
<h5 class="card-title">MTWireguard</h5>
</div>
<div class="card-body text-center">
<img class="w-50" src="img/logo.png" alt="" srcset="">
<small class="d-block mb-1">v@(Application.Helper.GetProjectVersion())</small>
<ul class="list-group list-unstyled border border-2 rounded-3">
<li>
<a class="list-group-item d-flex align-items-center bg-gradient-green" href="https://MTWireguard.techgarage.ir/">
<i class='bx bxs-book ms-2 me-4'></i>
<span>Documentation</span>
</a>
</li>
<li>
<a class="list-group-item d-flex align-items-center bg-gradient-yellow" href="https://hub.docker.com/r/techgarageir/mtwireguard/">
<i class='bx bxl-docker ms-2 me-4'></i>
<span>Docker</span>
</a>
</li>
<li>
<a class="list-group-item d-flex align-items-center bg-gradient-red" href="https://github.com/techgarage-ir/MTWireGuard/">
<i class='bx bxl-github ms-2 me-4'></i>
<span>Github</span>
</a>
</li>
<li>
<a class="list-group-item d-flex align-items-center bg-gradient-blue" href="https://t.me/MTWireguard">
<i class='bx bxl-telegram ms-2 me-4'></i>
<span>Telegram Channel</span>
</a>
</li>
<li>
<button class="list-group-item d-flex align-items-center bg-gradient-purple w-100" data-bs-toggle="modal" data-bs-target="#updateModal">
<i class='bx bx-revision ms-2 me-4'></i>
<span>Check For Updates</span>
</button>
</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Update Checking Modal -->
<div class="modal fade" id="updateModal" tabindex="-1" aria-labelledby="updateModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="updateModalLabel">Not available</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>This feature will come in next versions.</p>
</div>
</div>
<div class="col-sm-12 col-lg-6">
@await Component.InvokeAsync("CreateClientForm", new {
Servers = servers
})
</div>
</div>
@section Scripts {
<script src="vendors/coreui/utils/js/coreui-utils.js"></script>
<script src="js/wireguard.js"></script>
<script src="js/wgelements.js"></script>
<script src="js/page-home.js"></script>
}

View file

@ -12,32 +12,8 @@ namespace MTWireGuard.Pages
[Authorize]
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
private readonly IMikrotikRepository API;
public IndexModel(ILogger<IndexModel> logger, IMikrotikRepository mikrotik)
public void OnGet()
{
_logger = logger;
API = mikrotik;
}
public async Task OnGetAsync()
{
var users = await API.GetUsersAsync();
var servers = await API.GetServersAsync();
var traffics = await API.GetServersTraffic();
var wgTraffics = traffics.Where(s => s.Type == "wg");
long up = 0;
long down = 0;
foreach (var item in wgTraffics)
{
up += item.UploadBytes;
down += item.DownloadBytes;
}
ViewData["users"] = users;
ViewData["servers"] = servers;
ViewData["uptr"] = Helper.ConvertByteSize(up, 2);
ViewData["downtr"] = Helper.ConvertByteSize(down, 2);
}
}
}

View file

@ -2,87 +2,131 @@
@model MTWireGuard.Pages.LoginModel
@{
Layout = null;
bool invalid = false;
if (ViewData["Invalid"] != null) invalid = (bool)ViewData["Invalid"];
string invalidClass = invalid ? "is-invalid" : "", username = "";
if (ViewData["Username"] != null) username = ViewData["Username"].ToString();
}
<!DOCTYPE html>
<html lang="en">
<head>
<base href="./">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<meta name="description" content="CoreUI - Open Source Bootstrap Admin Template">
<meta name="author" content="Łukasz Holeczek">
<meta name="keyword" content="Bootstrap,Admin,Template,Open,Source,jQuery,CSS,HTML,RWD,Dashboard">
<title>Login</title>
<link rel="apple-touch-icon" sizes="57x57" href="assets/favicon/apple-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="assets/favicon/apple-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="assets/favicon/apple-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="assets/favicon/apple-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="assets/favicon/apple-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="assets/favicon/apple-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="assets/favicon/apple-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="assets/favicon/apple-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="assets/favicon/apple-icon-180x180.png">
<link rel="icon" type="image/png" sizes="192x192" href="assets/favicon/android-icon-192x192.png">
<link rel="icon" type="image/png" sizes="32x32" href="assets/favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="assets/favicon/favicon-96x96.png">
<link rel="icon" type="image/png" sizes="16x16" href="assets/favicon/favicon-16x16.png">
<link rel="manifest" href="assets/favicon/manifest.json">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="assets/favicon/ms-icon-144x144.png">
<meta name="theme-color" content="#ffffff">
<!-- Vendors styles-->
<link rel="stylesheet" href="vendors/simplebar/css/simplebar.css">
<link rel="stylesheet" href="css/vendors/simplebar.css">
<!-- Main styles for this application-->
<link href="css/style.css" rel="stylesheet">
</head>
<body>
<div class="bg-light min-vh-100 d-flex flex-row align-items-center">
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card-group d-block d-md-flex row">
<div class="card p-4 mb-0">
<form class="card-body" method="POST">
<h1>Login</h1>
<p class="text-medium-emphasis mb-4">Sign In to your router</p>
<div class="input-group mb-3"><span class="input-group-text">
<svg class="icon">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-user"></use>
</svg></span>
<input type="text" name="username" class="form-control @invalidClass" placeholder="Username" value="@username">
</div>
<div class="input-group mb-4"><span class="input-group-text">
<svg class="icon">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-lock-locked"></use>
</svg></span>
<input type="password" name="password" class="form-control @invalidClass" placeholder="Password">
@if (invalid == true) {
<div class="invalid-feedback">
Invalid username or password!
</div>
<meta name="description" content="Mikrotik Wireguard management panel">
<meta name="author" content="Kazem Ma79">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="keywords" content="Mikrotik, RouterOS, Wireguard, MTWireguard">
<title>MTWireguard - Login</title>
<!-- Favicon -->
<link href="img/favicon.ico" rel="icon">
<!-- Google Web Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Heebo:wght@400;500;600;700&display=swap" rel="stylesheet">
<!-- Icon Font Stylesheet -->
<link href="assets/lib/boxicons/css/boxicons.min.css" rel="stylesheet">
<link href="assets/libs/fortawesome/fontawesome-free/css/all.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css" rel="stylesheet">
<!-- Template Stylesheet -->
<link href="assets/css/core.css" rel="stylesheet">
<style>
body {
background-image: url(img/background.jpg);
background-attachment: fixed;
}
</div>
<div class="row">
<div class="col-6">
<button class="btn btn-primary px-4" type="submit">Login</button>
body > div:first-of-type {
backdrop-filter: blur(5px);
}
</style>
<!-- Theme Style Switcher-->
<script src="assets/js/themeSwitcher.js"></script>
</head>
<body>
<div class="container-fluid position-relative d-flex p-0">
<!-- Spinner Start -->
<div id="spinner" class="show bg-white position-fixed translate-middle w-100 vh-100 top-50 start-50 d-flex align-items-center justify-content-center">
<div class="spinner-border text-primary" style="width: 3rem; height: 3rem;" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>
<!-- Spinner End -->
<!-- Sign In Start -->
<div class="container-fluid">
<div class="row h-100 align-items-center justify-content-center" style="min-height: 100vh;">
<div class="col-12 col-sm-8 col-md-6 col-lg-5 col-xl-4">
<div class="card card-transparent rounded-3">
<div class="card-header">
<h3 class="card-title">
<i class="fa fa-hashtag me-2"></i> MTWireguard
</h3>
</div>
<div class="card-body">
<div class="d-flex justify-content-center">
<img src="/img/logo.png" class="mx-auto" style="max-width: 50%; text-align: center;">
</div>
<form id="loginForm" action="#" method="post" onsubmit="return false;">
<div class="alert alert-danger" role="alert" id="loginAlert" style="display: none;">Invalid username or password !</div>
<div class="form-floating mb-3">
<input type="text" class="form-control" id="loginName" name="username" placeholder="admin">
<label for="loginName">Username</label>
</div>
<div class="form-floating">
<input type="password" class="form-control" id="loginPassword" name="password" placeholder="Password">
<label for="loginPassword">Password</label>
</div>
<div class="d-flex align-items-center justify-content-between my-4">
<div class="form-check">
<input type="checkbox" id="loginRemember" name="remember">
<label class="form-check-label" for="loginRemember">Remember me</label>
</div>
<a href="#" data-bs-toggle="modal" data-bs-target="#ForgotModal">Forgot Password</a>
</div>
<div class="input-group align-items-center mb-3">
<i class="bx bx-sm bx-terminal color-green mx-2"></i>
<input class="mt-0 me-1" type="checkbox" id="isAdmin" name="isAdmin" aria-label="Login as system admin">
<label class="form-check-label" for="isAdmin">Login as system admin</label>
</div>
<button type="submit" class="btn btn-primary text-uppercase py-3 w-100 mb-4">Sign in</button>
<p class="text-center mb-0">Don't have an Account? <a asp-page="Signup">Sign Up</a></p>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- Sign In End -->
</div>
<!-- CoreUI and necessary plugins-->
<script src="vendors/coreui/coreui/js/coreui.bundle.min.js"></script>
<script src="vendors/simplebar/js/simplebar.min.js"></script>
</body>
<!-- Forgot Modal -->
<div class="modal fade" id="ForgotModal" tabindex="-1" aria-labelledby="ForgotModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="ForgotModalLabel">Forgot Password ?</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Contact service owner to reset your password.<br />If you're system administrator, change the mikrotik user's password.'</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- JavaScript Libraries -->
<script src="js/APIClient.js"></script>
<script src="assets/libs/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="assets/lib/jquery/js/jquery.min.js"></script>
<script src="assets/libs/jquery-ui/jquery-ui.min.js"></script>
<!-- Template Javascript -->
<script src="js/page-login.js"></script>
</body>
</html>

View file

@ -5,76 +5,29 @@ using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Net;
using System.Security.Claims;
using System.Text;
using Microsoft.AspNetCore.Authorization;
using MTWireGuard.Application.Repositories;
using MTWireGuard.Application.Models.Requests;
using MTWireGuard.Application.Models.Mikrotik;
using MTWireGuard.Application;
namespace MTWireGuard.Pages
{
[IgnoreAntiforgeryToken(Order = 1001)]
[AllowAnonymous]
public class LoginModel : PageModel
{
public IActionResult OnGet()
public IActionResult OnGet(string ReturnUrl = "Index")
{
var user = HttpContext.Session.Get<WGPeerViewModel>("user");
if (user != null)
{
return RedirectPermanent("/Client");
}
if (HttpContext.User.Identity.IsAuthenticated)
{
return RedirectToPage("Index");
}
return Page();
}
public async Task<IActionResult> OnPostAsync(string username, string password)
{
try
{
string MT_IP = Environment.GetEnvironmentVariable("MT_IP");
string MT_USER = Environment.GetEnvironmentVariable("MT_USER");
string MT_PASS = Environment.GetEnvironmentVariable("MT_PASS");
if (username == MT_USER && password == MT_PASS)
{
HttpClientHandler handler = new()
{
ServerCertificateCustomValidationCallback = delegate { return true; }
};
using HttpClient httpClient = new(handler);
using var request = new HttpRequestMessage(new HttpMethod("GET"), $"https://{MT_IP}/rest/");
string base64authorization = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{MT_USER}:{MT_PASS}"));
request.Headers.TryAddWithoutValidation("Authorization", $"Basic {base64authorization}");
HttpResponseMessage response = await httpClient.SendAsync(request);
var resp = await response.Content.ReadAsStringAsync();
var claims = new List<Claim>
{
new Claim(ClaimTypes.Role, "Administrator"),
};
var claimsIdentity = new ClaimsIdentity(
claims, CookieAuthenticationDefaults.AuthenticationScheme);
var authProperties = new AuthenticationProperties
{
AllowRefresh = true,
IsPersistent = true
};
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
claimsPrincipal,
authProperties);
HttpContext.User = claimsPrincipal;
return RedirectToPage("Index");
}
else
{
ViewData["Username"] = username;
ViewData["Invalid"] = true;
}
}
catch (Exception ex)
{
ViewData["body"] = ex.Message;
Console.WriteLine(ex.Message);
ReturnUrl = ReturnUrl == "/" ? "Index" : ReturnUrl;
return RedirectToPage(ReturnUrl);
}
return Page();
}

View file

@ -6,24 +6,15 @@ using MTWireGuard.Application.Repositories;
namespace MTWireGuard.Pages
{
public class LogoutModel : PageModel
public class LogoutModel(IMikrotikRepository api) : PageModel
{
private readonly IMikrotikRepository API;
public LogoutModel(IMikrotikRepository mikrotik)
{
API = mikrotik;
}
public async Task<IActionResult> OnGetAsync(string returnUrl = "Login")
{
// Clear the existing external cookie
await HttpContext.SignOutAsync(
CookieAuthenticationDefaults.AuthenticationScheme);
/*
var sessionId = await MTAPIHandler.GetCurrentSessionID();
var remove = await MTAPIHandler.KillJob(sessionId);*/
var sessionId = await API.GetCurrentSessionID();
var kill = await API.KillJob(sessionId);
var sessionId = await api.GetCurrentSessionID();
var kill = await api.KillJob(sessionId);
return RedirectToPage(returnUrl);
}
}

View file

@ -1,75 +1,121 @@
@page
@using MTWireGuard.Application.Models.Mikrotik
@model MTWireGuard.Pages.LogsModel
@{
ViewData["Title"] = "Event Logs";
ViewData["Breadcrumb"] = "Logs";
var Logs = (List<LogViewModel>)ViewData["Logs"];
}
<div class="row">
<div class="">
<table class="table table-bordered border-secondary logs-table">
<thead class="table-dark fw-semibold">
<tr class="align-middle">
<th class="text-center">
<svg class="icon">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-people"></use>
</svg>
<div class="row g-4 mb-4">
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span>Total Logs</span>
<div class="d-flex align-items-end mt-2">
<h4 class="mb-0 me-2" id="logs-total">1,000</h4>
</div>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-success">
<i class="bi bi-calendar-event bx-sm"></i>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span>Critical Logs</span>
<div class="d-flex align-items-end mt-2">
<h4 class="mb-0 me-2" id="logs-critical">69</h4>
</div>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-danger">
<i class="bi bi-x-octagon bx-sm"></i>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span>Wireguard Logs</span>
<div class="d-flex align-items-end mt-2">
<h4 class="mb-0 me-2" id="logs-wireguard">731</h4>
</div>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-warning">
<i class="bi bi-exclamation-triangle bx-sm"></i>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span>Info Logs</span>
<div class="d-flex align-items-end mt-2">
<h4 class="mb-0 me-2" id="logs-info">200</h4>
</div>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-info">
<i class="bi bi-exclamation-octagon bx-sm"></i>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Logs List Table -->
<div class="card">
<div class="card-header border-bottom">
<h5 class="card-title">Event Logs</h5>
</div>
<div class="card-body">
<div class="card-datatable table-responsive pt-0">
<table class="datatables-basic table table-striped table-hover border-top">
<thead>
<tr>
<th></th>
<th>
<i class="bx bx-hash"></i>
</th>
<th>Message</th>
<th>Topics</th>
<th>Time</th>
</tr>
</thead>
<tbody>
@foreach (var log in Logs)
{
<tr class="align-middle">
<td class="text-center">
<span class="text-medium-emphasis">#@log.Id</span>
</td>
<td>@log.Message</td>
<td>
@foreach (var topic in log.Topics)
{
switch (topic.ToLower())
{
case "system":
<span class="badge text-bg-dark mx-1">@topic</span>
break;
case "info":
<span class="badge text-bg-info mx-1">@topic</span>
break;
case "error":
case "critical":
<span class="badge text-bg-danger mx-1">@topic</span>
break;
case "account":
<span class="badge text-bg-secondary mx-1">@topic</span>
break;
case "dhcp":
case "ppp":
case "l2tp":
case "pptp":
case "sstp":
<span class="badge text-bg-light border border-secondary mx-1">@topic</span>
break;
default:
<span class="badge text-bg-light border border-secondary mx-1">@topic</span>
break;
}
}
</td>
<td>@log.Time</td>
<tfoot>
<tr>
<th></th>
<th>
<i class="bx bx-hash"></i>
</th>
<th>Message</th>
<th>Topics</th>
<th>Time</th>
</tr>
}
</tbody>
</tfoot>
</table>
</div>
</div>
</div>
@section Scripts {
<script>
</script>
<script src="js/page-logs.js"></script>
}

View file

@ -6,16 +6,8 @@ namespace MTWireGuard.Pages
{
public class LogsModel : PageModel
{
private readonly IMikrotikRepository API;
public LogsModel(IMikrotikRepository mikrotik)
public void OnGet()
{
API = mikrotik;
}
public async Task OnGetAsync()
{
ViewData["Logs"] = await API.GetLogsAsync();
}
}
}

View file

@ -1,16 +0,0 @@
<div class="modal fade" id="QRModal" tabindex="-1" aria-labelledby="QRModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="QRModalLabel">Scan This</h5>
</div>
<div class="modal-body">
<img id="QRImage" src="" class="img-thumbnail" />
</div>
</div>
</div>
</div>
@code {
}

123
UI/Pages/Pools.cshtml Normal file
View file

@ -0,0 +1,123 @@
@page
@model MTWireGuard.Pages.PoolsModel
@{
ViewData["Title"] = "IP Pools";
}
<!-- Pools List Table -->
<div class="card">
<div class="card-header border-bottom">
<h5 class="card-title">IP Pools</h5>
</div>
<div class="card-body">
<div class="card-datatable table-responsive pt-0">
<table class="datatables-basic table table-striped table-hover border-top">
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Address</th>
<th>Next Pool</th>
<th>Action</th>
</tr>
</thead>
</table>
</div>
</div>
<!-- Offcanvas to add pool -->
<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasAddPool" aria-labelledby="offcanvasAddPoolLabel">
<div class="offcanvas-header">
<h5 id="offcanvasAddPoolLabel" class="offcanvas-title">Add Pool</h5>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0">
<form class="add-new-pool pt-0" id="addPoolForm" onsubmit="return false">
<div class="mb-3">
<label class="form-label" for="add-name">Name</label>
<input type="text" class="form-control" id="add-name" name="Name" aria-label="Name" />
</div>
<div class="mb-3">
<label class="form-label d-flex align-items-center justify-content-between" for="add-addresses">
Ranges
<button type="button" id="addRangeBtn" class="btn btn-success rounded-1">
<i class="bx bx-plus"></i>
</button>
</label>
<div class="d-flex justify-content-end mt-2"></div>
<div class="form-group" id="add-addresses">
<div class="input-group my-1">
<input type="text" name="Ranges[]" class="form-control" aria-label="Range" aria-describedby="removeField1">
<span class="input-group-text text-danger" id="removeField1" role="button" onclick="removeRangeInputGroup(this)">
<i class="bx bx-minus"></i>
</span>
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="add-next">Next Pool</label>
<select id="add-next" name="NextPool" class="form-select">
<option value="">None</option>
</select>
</div>
<button type="submit" class="btn btn-success me-sm-3 me-1 data-submit">Submit</button>
<button type="reset" class="btn btn-danger" data-bs-dismiss="offcanvas">Cancel</button>
</form>
</div>
</div>
<!-- Offcanvas to add pool -->
<!-- Offcanvas to edit pool -->
<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasEditPool" aria-labelledby="offcanvasEditPoolLabel">
<div class="offcanvas-header">
<h5 id="offcanvasEditPoolLabel" class="offcanvas-title">Edit Pool</h5>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0">
<form class="edit-new-pool pt-0" id="editPoolForm" onsubmit="return false">
<input type="hidden" id="edit-id" name="ID">
<div class="mb-3">
<label class="form-label" for="edit-name">Name</label>
<input type="text" class="form-control" id="edit-name" name="Name" aria-label="Name" />
</div>
<div class="mb-3">
<label class="form-label d-flex align-items-center justify-content-between" for="edit-addresses">
Ranges
<button type="button" id="editRangeBtn" class="btn btn-success rounded-1">
<i class="bx bx-plus"></i>
</button>
</label>
<div class="d-flex justify-content-end mt-2">
</div>
<div class="form-group" id="edit-addresses">
<div class="input-group my-1">
<input type="text" name="Ranges[]" class="form-control" aria-label="Range" aria-describedby="removeField1">
<span class="input-group-text text-danger" id="removeField1" role="button" onclick="removeRangeInputGroup(this)">
<i class="bx bx-minus"></i>
</span>
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="edit-next">Next Pool</label>
<select id="edit-next" name="NextPool" class="form-select">
<option value="none">None</option>
</select>
</div>
<button type="submit" class="btn btn-success me-sm-3 me-1 data-submit">Submit</button>
<button type="reset" class="btn btn-danger" data-bs-dismiss="offcanvas">Cancel</button>
</form>
</div>
</div>
<!-- Offcanvas to edit pool -->
</div>
@section Modals {
<!-- Details Modal -->
<component type="typeof(Components.Modals.DetailsModal)" render-mode="Static" />
<!-- Toasts -->
<component type="typeof(Components.ToastContainer)" render-mode="Static" />
}
@section Scripts {
<script src="js/helper.js"></script>
<script src="js/page-pools.js"></script>
}

12
UI/Pages/Pools.cshtml.cs Normal file
View file

@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace MTWireGuard.Pages
{
public class PoolsModel : PageModel
{
public void OnGet()
{
}
}
}

View file

@ -1,8 +0,0 @@
@page
@model PrivacyModel
@{
ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>
<p>Use this page to detail your site's privacy policy.</p>

View file

@ -1,19 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace MTWireGuard.Pages
{
public class PrivacyModel : PageModel
{
private readonly ILogger<PrivacyModel> _logger;
public PrivacyModel(ILogger<PrivacyModel> logger)
{
_logger = logger;
}
public void OnGet()
{
}
}
}

View file

@ -1,49 +1,128 @@
@page "{handler?}"
@page
@using MTWireGuard.Application.Models.Mikrotik
@model MTWireGuard.Pages.ServersModel
@{
ViewData["Title"] = "Servers";
ViewData["Breadcrumb"] = "Interfaces";
}
<div class="table-responsive">
<table class="table border mb-0">
<thead class="table-light fw-semibold">
<tr class="align-middle">
<th class="text-center">
<svg class="icon">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-people"></use>
</svg>
</th>
<th>Name</th>
<th>Listen Port</th>
<th>MTU</th>
<th>Public-Key</th>
<th>Traffic</th>
<th>Status</th>
<th></th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<div class="row mt-2">
<div class="col-12">
@await Component.InvokeAsync("CreateServerForm")
<div class="row g-4 mb-4">
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span>Total Servers</span>
<div class="d-flex align-items-end mt-2">
<h4 class="mb-0 me-2" id="servers-total">0</h4>
</div>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-primary">
<i class="bx bx-radar bx-sm"></i>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span>Active Servers</span>
<div class="d-flex align-items-end mt-2">
<h4 class="mb-0 me-2" id="servers-active">0</h4>
</div>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-warning">
<i class="bx bx-broadcast bx-sm"></i>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span>Running Servers</span>
<div class="d-flex align-items-end mt-2">
<h4 class="mb-0 me-2" id="servers-running">0</h4>
</div>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-success">
<i class="bx bx-station bx-sm"></i>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span>Not Running</span>
<div class="d-flex align-items-end mt-2">
<h4 class="mb-0 me-2" id="servers-not-running">0</h4>
</div>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-danger">
<i class="bx bx-user-voice bx-sm"></i>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Servers List Table -->
<div class="card">
<div class="card-header border-bottom">
<h5 class="card-title">WG Servers</h5>
</div>
<div class="card-body">
<div class="card-datatable table-responsive pt-0">
<table class="datatables-basic table table-striped table-hover border-top">
<thead>
<tr>
<th></th>
<th></th>
<th>Name</th>
<th>IP Address</th>
<th>Port</th>
<th>DHCP</th>
<th>DNS</th>
<th>Action</th>
</tr>
</thead>
</table>
</div>
</div>
<!-- Offcanvas to add server -->
<component type="typeof(Components.Offcanvas.Server.Add)" render-mode="Static" />
<!-- Offcanvas to add server -->
<!-- Offcanvas to edit server -->
<component type="typeof(Components.Offcanvas.Server.Update)" render-mode="Static" />
<!-- Offcanvas to edit server -->
</div>
<!-- Delete Modal -->
@await Component.InvokeAsync("DeleteModal", new {
IsServer = true
})
<!-- Edit Modal -->
@await Component.InvokeAsync("UpdateServerModal")
@section Modals {
<!-- Remove Modal -->
<component type="typeof(Components.Modals.DeleteModal)" render-mode="Static" />
<!-- Details Modal -->
<component type="typeof(Components.Modals.DetailsModal)" render-mode="Static" />
<!-- Toasts -->
<component type="typeof(Components.ToastContainer)" render-mode="Static" />
}
@section Scripts {
<script src="js/wireguard.js"></script>
<script src="js/wgelements.js"></script>
<script src="js/page-servers.js"></script>
}

View file

@ -3,8 +3,6 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using MTWireGuard.Application.Models;
using MTWireGuard.Application.Models.Mikrotik;
using MTWireGuard.Models.Requests;
using MTWireGuard.Models.Responses;
using MTWireGuard.Application.Repositories;
using Newtonsoft.Json;
@ -12,74 +10,8 @@ namespace MTWireGuard.Pages
{
public class ServersModel : PageModel
{
private readonly IMikrotikRepository API;
private readonly IMapper mapper;
public ServersModel(IMikrotikRepository mikrotik, IMapper mapper)
public void OnGet()
{
API = mikrotik;
this.mapper = mapper;
}
public async Task OnGetAsync()
{
}
public async Task<PartialViewResult> OnGetGetAll()
{
var servers = await API.GetServersAsync();
var traffics = await API.GetServersTraffic();
return Partial("_ServersTable", (servers, traffics));
}
public async Task<IActionResult> OnPostCreateAsync(CreateServerRequest request)
{
var model = mapper.Map<ServerCreateModel>(request);
var make = await API.CreateServer(model);
var message = mapper.Map<ToastMessage>(make);
return new ToastResult(message);
/*
string status = make.Code == "200" ? "success" : "danger";
string title = make.Code == "200" ? make.Title : $"[{make.Code}] {make.Title}";
return new ToastResult(title, make.Description, status);*/
}
public async Task<IActionResult> OnPostDelete(DeleteRequest request)
{
var delete = await API.DeleteServer(request.Id);
var message = mapper.Map<ToastMessage>(delete);
return new ToastResult(message);
/*
string status = delete.Code == "200" ? "success" : "danger";
string title = delete.Code == "200" ? delete.Title : $"[{delete.Code}] {delete.Title}";
return new ToastResult(title, delete.Description, status);*/
}
public async Task<IActionResult> OnPostUpdate(UpdateServerRequest request)
{
var model = mapper.Map<ServerUpdateModel>(request);
var update = await API.UpdateServer(model);
var message = mapper.Map<ToastMessage>(update);
return new ToastResult(message);
/*
string status = update.Code == "200" ? "success" : "danger";
string title = update.Code == "200" ? update.Title : $"[{update.Code}] {update.Title}";
return new ToastResult(title, update.Description, status);*/
}
public async Task<IActionResult> OnGetEnableAsync(ChangeStateRequest request)
{
CreationResult result;
if (!request.Enabled)
result = await API.EnableServer(request.Id);
else
result = await API.DisableServer(request.Id);
var message = mapper.Map<ToastMessage>(result);
return new ToastResult(message);
/*
string status = result.Code == "200" ? "success" : "danger";
string title = result.Code == "200" ? result.Title : $"[{result.Code}] {result.Title}";
return new ToastResult(title, result.Description, status);*/
}
}
}

View file

@ -1,145 +1,153 @@
@page "{handler?}"
@page
@using MTWireGuard.Application.Models.Mikrotik
@model SettingsModel
@{
ViewData["Title"] = "System Settings";
ViewData["Breadcrumb"] = "Settings";
var servers = (List<WGServerViewModel>)ViewData["servers"];
var info = (MTInfoViewModel)ViewData["info"];
var HDDPercent = (info.UsedHDDBytes * 100) / info.TotalHDDBytes;
var RAMPercent = (info.UsedRAMBytes * 100) / info.TotalRAMBytes;
var name = ViewData["name"].ToString();
}
<div class="row">
<div class="col-xs-6 col-sm-4 col-lg-4">
<div class="card text-white bg-info">
<div class="row g-2">
<div class="col-sm-12 col-md-6">
<div class="d-flex flex-column">
<!-- DNS Settings -->
<div class="card mb-2" style="max-height: 400px;">
<div class="card-header">
<h5 class="card-title"><i class="bi bi-globe"></i> DNS</h5>
</div>
<div class="card-body overflow-x-auto">
<form id="dnsForm" onsubmit="return false">
<div class="row mb-3">
<div class="col-md-3">
<label class="form-label d-flex align-items-center justify-content-between" for="servers">
Servers
<button type="button" id="addServerBtn" class="btn btn-success rounded-1">
<i class="bx bx-plus"></i>
</button>
</label>
</div>
<div class="col-md-9">
<div class="form-group" id="serversGroup"></div>
</div>
</div>
<div class="row">
<div class="col-md-3">
<label class="mt-2" for="dynamicServersGroup">Dynamic Servers</label>
</div>
<div class="col-md-9">
<div class="form-group" id="dynamicServersGroup"></div>
</div>
</div>
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-primary rounded-2">Submit</button>
</div>
</form>
</div>
</div>
<!-- Settings -->
<div class="card">
<div class="card-header">
<h5 class="card-title"><i class="bi bi-ui-radios-grid"></i> Client Panel</h5>
</div>
<div class="card-body">
<div class="fs-4 fw-semibold">@info.FreeHDD</div>
<form onsubmit="return false">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="user-register-request" disabled>
<label class="form-check-label" for="user-register-request">Registration Request</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="user-get-config" disabled>
<label class="form-check-label" for="user-get-config">Get Config (QR-code or .conf file)</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="user-view-usage" disabled>
<label class="form-check-label" for="user-view-usage">View Traffic Usage</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="user-change-password" disabled>
<label class="form-check-label" for="user-change-password">Can Change Password</label>
</div>
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-primary rounded-2">Save</button>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="col-sm-12 col-md-6">
<div class="d-flex flex-column">
<!-- Identity Settings -->
<div class="card mb-2">
<div class="card-header">
<h5 class="card-title"><i class="bi bi-person-vcard"></i> Identity</h5>
</div>
<div class="card-body">
<form id="identityForm" onsubmit="return false">
<div class="form-floating mb-3">
<input type="text" class="form-control" id="identityName" name="Name" placeholder="srv-main">
<label for="identityName">Identity</label>
</div>
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-primary rounded-2">Save</button>
</div>
</form>
</div>
</div>
<!-- Status -->
<div class="card">
<div class="card-header">
<h5 class="card-title"><i class="bi bi-info-lg"></i> Status</h5>
</div>
<div class="card-body">
<div class="row gy-2">
<div class="col-xs-6 col-sm-4 col-lg-4">
<div class="card text-white bg-primary" id="hdd-box">
<div class="card-body">
<div class="fs-4 fw-semibold">0MB</div>
<div>Free Disk Space</div>
<div class="progress progress-white progress-thin my-2">
<div class="progress-bar" role="progressbar" style="width: @HDDPercent%" aria-valuenow="@HDDPercent" aria-valuemin="0" aria-valuemax="100"></div>
</div><small class="text-medium-emphasis-inverse">Used @info.UsedHDD of @info.TotalHDD</small>
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<small class="text-medium-emphasis-inverse">Used 0MB of 0MB</small>
</div>
</div>
</div>
<div class="col-xs-6 col-sm-4 col-lg-4">
<div class="card text-white bg-warning">
<div class="card text-white bg-success" id="ram-box">
<div class="card-body">
<div class="fs-4 fw-semibold">@info.FreeRAM</div>
<div class="fs-4 fw-semibold">0MB</div>
<div>Free RAM Space</div>
<div class="progress progress-white progress-thin my-2">
<div class="progress-bar" role="progressbar" style="width: @RAMPercent%" aria-valuenow="@RAMPercent" aria-valuemin="0" aria-valuemax="100"></div>
</div><small class="text-medium-emphasis-inverse">Used @info.UsedRAM of @info.TotalRAM</small>
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<small class="text-medium-emphasis-inverse">Used 0MB of 0MB</small>
</div>
</div>
</div>
<div class="col-xs-6 col-sm-4 col-lg-4">
<div class="card text-white bg-danger">
<div class="card text-white bg-danger" id="uptime-box">
<div class="card-body">
<div class="text-medium-emphasis-inverse text-end mb-2">
<svg class="icon icon-xxl">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-speedometer"></use>
</svg>
<div class="text-medium-emphasis-inverse fs-5 mb-1">
<i class="bi bi-speedometer"></i>
<div class="vr"></div>
<small class="fs-5 fw-lighter">CPU: 0%</small>
</div>
<div class="fs-4 fw-semibold">@info.UPTime</div>
<div class="fs-4 fw-semibold">00:00:00</div>
<small class="text-medium-emphasis-inverse text-uppercase fw-semibold">UPTime</small>
</div>
</div>
</div>
</div>
<div class="row my-2">
<div class="col-sm-6 col-lg-4">
<div class="card mb-4" style="--cui-card-cap-bg: #7D50B9">
<div class="card-header position-relative d-flex justify-content-center align-items-center">
<svg class="icon icon-3xl text-white my-4">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-router"></use>
</svg>
<div class="chart-wrapper position-absolute top-0 start-0 w-100 h-100">
<canvas id="social-box-chart-1" height="90"></canvas>
</div>
</div>
<div class="card-body row text-center">
<div class="col">
<div class="fs-5 fw-semibold">@info.Architecture</div>
<div class="text-uppercase text-medium-emphasis small">Arch</div>
</div>
<div class="vr"></div>
<div class="col">
<div class="fs-5 fw-semibold">@info.Platform</div>
<div class="text-uppercase text-medium-emphasis small">Platform</div>
</div>
<div class="vr"></div>
<div class="col">
<div class="fs-5 fw-semibold">@info.BoardName</div>
<div class="text-uppercase text-medium-emphasis small">Board</div>
</div>
</div>
<div class="card-footer row bg-light px-3 py-2 m-0">
<a class="btn-block text-medium-emphasis d-flex justify-content-center align-items-center text-decoration-none col" href="#">
<span class="small fw-semibold">Identity: @name</span>
</a>
<div class="vr"></div>
<a class="btn-block text-medium-emphasis d-flex justify-content-center align-items-center text-decoration-none col" href="#">
<span class="small fw-semibold">Version: @info.Version</span>
</a>
</div>
</div>
</div>
<div class="col-sm-6 col-lg-4">
<div class="card mb-4" style="--cui-card-cap-bg: #4FA3A5">
<div class="card-header position-relative d-flex justify-content-center align-items-center">
<svg class="icon icon-3xl text-white my-4">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-aperture"></use>
</svg>
<div class="chart-wrapper position-absolute top-0 start-0 w-100 h-100">
<canvas id="social-box-chart-1" height="90"></canvas>
</div>
</div>
<div class="card-body row text-center">
<div class="col">
<div class="fs-5 fw-semibold">@info.CPU</div>
<div class="text-uppercase text-medium-emphasis small">CPU</div>
</div>
<div class="vr"></div>
<div class="col">
<div class="fs-5 fw-semibold">@info.CPUCount</div>
<div class="text-uppercase text-medium-emphasis small">Cores</div>
</div>
<div class="vr"></div>
<div class="col">
<div class="fs-5 fw-semibold">@info.CPUFrequency</div>
<div class="text-uppercase text-medium-emphasis small">Frequency</div>
</div>
<div class="vr"></div>
<div class="col">
<div class="fs-5 fw-semibold" id="cpuLoad">@info.CPULoad %</div>
<div class="text-uppercase text-medium-emphasis small">Usage</div>
</div>
</div>
</div>
</div>
<!--<div class="col-sm-6 col-lg-4">
<div class="card text-bg-dark">
<div class="card-header">
Fix MSS
</div>
<div class="card-body">
<h5 class="card-title">Fix problems loading apps like Instagram</h5>
<p class="card-text">This will fix problems with loading some applications such as Instagram by changing TCP MSS.</p>
<select class="form-select" aria-label="Default select example">
@foreach (var server in servers)
{
<option value="@server.Id">@server.Name</option>
}
</select>
<button type="button" class="btn btn-info mt-2">
<span class="cil-contrast btn-icon mr-2"></span> Fix
</button>
</div>
</div>
</div>-->
</div>
@section Scripts {
@section Modals {
<!-- Toasts -->
<component type="typeof(Components.ToastContainer)" render-mode="Static" />
}
@section Scripts {
<script src="js/helper.js"></script>
<script src="js/page-settings.js"></script>
}

View file

@ -8,54 +8,8 @@ namespace MTWireGuard.Pages
{
public class SettingsModel : PageModel
{
private readonly IMikrotikRepository API;
public SettingsModel(IMikrotikRepository mikrotik)
public void OnGet()
{
API = mikrotik;
}
public async Task OnGetAsync()
{
ViewData["servers"] = await API.GetServersAsync();
ViewData["info"] = await API.GetInfo();
var identity = await API.GetName();
ViewData["name"] = identity.Name;
}
public async Task<IActionResult> OnGetGetInfo()
{
var info = await API.GetInfo();
var ramUsed = 100 - info.FreeRAMPercentage;
var hddUsed = 100 - info.FreeHDDPercentage;
string cpuColor, ramColor, hddColor;
if (info.CPULoad <= 25) cpuColor = "bg-info-gradient";
else if (info.CPULoad <= 75) cpuColor = "bg-warning-gradient";
else cpuColor = "bg-danger-gradient";
if (hddUsed <= 25) hddColor = "bg-info-gradient";
else if (hddUsed <= 75) hddColor = "bg-warning-gradient";
else hddColor = "bg-danger-gradient";
if (ramUsed <= 25) ramColor = "bg-info-gradient";
else if (ramUsed <= 75) ramColor = "bg-warning-gradient";
else ramColor = "bg-danger-gradient";
var result = new SidebarInfo()
{
CPUBgColor = cpuColor,
HDDBgColor = hddColor,
RAMBgColor = ramColor,
CPUUsedPercentage = info.CPULoad,
HDDUsedPercentage = hddUsed,
RAMUsedPercentage = ramUsed,
HDDUsed = info.UsedHDD,
RAMUsed = info.UsedRAM,
TotalHDD = info.TotalHDD,
TotalRAM = info.TotalRAM,
};
return new JsonResult(result);
}
}
}

View file

@ -1,97 +0,0 @@
@using MTWireGuard.Application.Models.Mikrotik
@model List<WGPeerViewModel>
@foreach (var user in Model)
{
<tr class="align-middle" data-id="@user.Id">
<td class="text-center">
<span class="text-medium-emphasis">#@user.Id</span>
</td>
<td>
<div>@user.Name</div>
</td>
<td>
@if (user.Interface.StartsWith('*'))
{
<div class="text-danger fst-italic">@user.Interface</div>
}
else
{
<div>@user.Interface</div>
}
</td>
<td>
<div>@user.Address</div>
@if (!user.CurrentAddress.EndsWith(":0"))
{
<div class="small text-medium-emphasis">@user.CurrentAddress</div>
}
</td>
<td>
<div>
<svg class="icon">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-data-transfer-up"></use>
</svg>
@user.Upload / @user.Download
<svg class="icon">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-data-transfer-down"></use>
</svg>
</div>
</td>
<td>
<div>@user.Expire</div>
</td>
<td>
<div class="row">
@if (!user.IsEnabled)
{
<span class="col badge rounded-pill text-bg-danger">Disabled</span>
}
else
{
<span class="col badge rounded-pill text-bg-success">Enabled</span>
}
@if (user.IsDifferent)
{
<span class="col badge rounded-pill text-bg-danger mx-md-1">Not Synced</span>
}
else
{
<span class="col badge rounded-pill text-bg-success mx-md-1">Synced</span>
}
@if (user.CurrentAddress.EndsWith(":0"))
{
<span class="col badge rounded-pill text-bg-danger">Offline</span>
}
else
{
<span class="col badge rounded-pill text-bg-success">Online</span>
}
</div>
</td>
<td>
<div class="dropdown position-static">
<button class="btn btn-transparent p-0" type="button" data-coreui-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<svg class="icon">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-options"></use>
</svg>
</button>
<div class="dropdown-menu dropdown-menu-end">
@if (user.IsDifferent)
{
<button class="dropdown-item sync-btn" data-coreui-toggle="modal" data-coreui-target="#SyncModal" onclick="syncBtn(event)">Sync</button>
}
<button class="dropdown-item" data-coreui-toggle="modal" data-coreui-target="#QRModal">QR</button>
<a class="dropdown-item" asp-page="Clients" asp-page-handler="DownloadTunnel" asp-route-id="@user.Id">Download</a>
<button class="dropdown-item update-btn" data-coreui-toggle="modal" data-coreui-target="#EditModal" onclick="updateClientBtn(event)">Edit</button>
@{
var enabled = user.IsEnabled;
string enableORdisable = enabled ? "Disable" : "Enable";
}
<a class="dropdown-item" asp-page="Clients" asp-page-handler="Enable" asp-route-ID="@user.Id" asp-route-IsEnabled="@enabled" data-target="_self" onclick="dropdownBtnClick(event);">@enableORdisable</a>
<button class="dropdown-item delete-btn text-danger" data-coreui-toggle="modal" data-coreui-target="#DeleteModal" onclick="deleteBtn(event)">Delete</button>
</div>
</div>
</td>
</tr>
}

View file

@ -1,207 +1,218 @@
@using MTWireGuard.Application.Repositories
@inject IMikrotikRepository API;
@{
var logs = await API.GetLogsAsync();
@{
string username = Environment.GetEnvironmentVariable("MT_USER");
}
<!DOCTYPE html>
<html lang="en">
<head>
<base href="./">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<meta name="description" content="Mikrotik Wireguard management panel">
<meta name="author" content="Kazem Ma79">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="keywords" content="Mikrotik, RouterOS, Wireguard, MTWireguard">
<title>@ViewData["Title"]</title>
<link rel="apple-touch-icon" sizes="180x180" href="assets/favicon/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="192x192" href="assets/favicon/android-icon-36x36.png">
<link rel="icon" type="image/png" sizes="192x192" href="assets/favicon/android-icon-48x48.png">
<link rel="icon" type="image/png" sizes="192x192" href="assets/favicon/android-icon-72x72.png">
<link rel="icon" type="image/png" sizes="192x192" href="assets/favicon/android-icon-96x96.png">
<link rel="icon" type="image/png" sizes="192x192" href="assets/favicon/android-icon-144x144.png">
<link rel="icon" type="image/png" sizes="192x192" href="assets/favicon/android-icon-192x192.png">
<link rel="icon" type="image/png" sizes="192x192" href="assets/favicon/android-icon-256x256.png">
<link rel="icon" type="image/png" sizes="192x192" href="assets/favicon/android-icon-384x384.png">
<link rel="icon" type="image/png" sizes="192x192" href="assets/favicon/android-icon-512x512.png">
<link rel="icon" type="image/png" sizes="16x16" href="assets/favicon/favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="assets/favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="assets/favicon/favicon-96x96.png">
<link rel="manifest" href="assets/favicon/manifest.json">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="assets/favicon/mstile-150x150.png">
<meta name="theme-color" content="#ffffff">
<!-- Simplebar styles-->
<link rel="stylesheet" href="vendors/simplebar/css/simplebar.css">
<link rel="stylesheet" href="css/vendors/simplebar.css">
<!-- Calendarify -->
<link href="vendors/calendarify/calendarify.min.css" rel="stylesheet">
<link href="vendors/tempus/css/tempus-dominus.min.css" rel="stylesheet">
<!-- Main styles-->
<link href="css/style.css" rel="stylesheet">
<link href="css/gradients.css" rel="stylesheet">
<link href="css/custom.css" rel="stylesheet">
<!-- Page Styles (if any)-->
<!-- Favicon -->
<link href="img/favicon.ico" rel="icon">
<!-- Google Web Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Heebo:wght@400;500;600;700&display=swap" rel="stylesheet">
<!-- Icon Font Stylesheet -->
<link href="assets/lib/boxicons/css/boxicons.min.css" rel="stylesheet">
<link href="assets/libs/fortawesome/fontawesome-free/css/all.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.4.1/font/bootstrap-icons.css" rel="stylesheet">
<!-- Template Stylesheet -->
<link href="assets/css/core.css" rel="stylesheet">
<!-- Theme Style Switcher-->
<script src="assets/js/themeSwitcher.js"></script>
<!-- Libraries Stylesheet -->
<link href="assets/lib/owl.carousel/css/owl.carousel.min.css" rel="stylesheet">
<link href="lib/tempusdominus/css/tempusdominus-bootstrap-4.min.css" rel="stylesheet">
<link href="assets/libs/flatpickr/flatpickr.min.css" rel="stylesheet">
<link href="https://cdn.datatables.net/v/bs5/jszip-3.10.1/dt-1.13.7/b-2.4.2/b-html5-2.4.2/fc-4.3.0/fh-3.4.0/r-2.5.0/sp-2.2.0/sl-1.7.0/datatables.min.css" rel="stylesheet">
@await RenderSectionAsync("Styles", required: false)
</head>
<body>
<div class="sidebar sidebar-dark sidebar-fixed" id="sidebar">
<div class="sidebar-brand d-none d-md-flex">
<svg class="sidebar-brand-full" width="118" height="46" alt="Wireguard Logo">
<image xlink:href="assets/brand/wireguard.svg" width="118" height="46" />
</svg>
<svg class="sidebar-brand-narrow" width="46" height="46" alt="Wireguard Logo">
<image xlink:href="assets/brand/wireguard-mini.svg" width="46" height="46"></image>
</svg>
<div class="container-fluid position-relative d-flex p-0">
<!-- Spinner Start -->
<div id="spinner" class="show bg-white position-fixed translate-middle w-100 vh-100 top-50 start-50 d-flex align-items-center justify-content-center">
<div class="spinner-border text-primary" style="width: 3rem; height: 3rem;" role="status">
<span class="sr-only">Loading...</span>
</div>
<ul class="sidebar-nav" data-coreui="navigation" data-simplebar="">
</div>
<!-- Spinner End -->
<!-- Sidebar Start -->
<div class="sidebar pb-3">
<nav class="navbar navbar-light pt-0">
<div class="navbar-brand d-flex justify-content-center align-items-center m-0 p-3 w-100">
<a href="index.html">
<h3 class="text-primary">
MTWireguard
</h3>
</a>
</div>
<div class="d-flex align-items-center ms-4 my-4">
<div class="position-relative">
<img class="rounded-circle" src="img/user.png" alt="" style="width: 45px; height: 45px;">
</div>
<div class="ms-3">
<h6 class="mb-0">@username</h6>
<span>Admin</span>
</div>
</div>
<ul class="navbar-nav w-100" id="sideNav">
<li class="nav-item">
<a class="nav-link" asp-page="/">
<svg class="nav-icon">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-speedometer"></use>
</svg> Dashboard
<a asp-page="Index" class="nav-link">
<i class="bx bx-home-circle me-2"></i>Dashboard
</a>
</li>
<li class="nav-title">Wireguard</li>
<li class="nav-item text-center text-uppercase py-2">
<span>Wireguard</span>
</li>
<li class="nav-item">
<a class="nav-link" asp-page="/Servers">
<svg class="nav-icon">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-3d"></use>
</svg> Servers
<a asp-page="Clients" class="nav-link">
<i class="bx bx-group me-2"></i>Users
</a>
</li>
<li class="nav-item">
<a class="nav-link" asp-page="/Clients">
<svg class="nav-icon">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-devices"></use>
</svg> Clients
<a asp-page="Servers" class="nav-link">
<i class="bx bx-broadcast me-2"></i>Servers
</a>
</li>
<li class="nav-title">System</li>
<li class="nav-item">
<a class="nav-link" asp-page="/Settings">
<svg class="nav-icon">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-apps"></use>
</svg> Settings
<a asp-page="Pools" class="nav-link">
<i class="bx bx-current-location me-2"></i>Pools
</a>
</li>
<li class="nav-title mt-auto">System Utilization</li>
<li class="nav-item px-3 d-narrow-none" id="cpuUsage">
<div class="text-uppercase mb-1"><small><b>CPU Usage</b></small></div>
<div class="progress progress-thin">
<div class="progress-bar" role="progressbar" style="width: 0%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<small class="text-medium-emphasis-inverse">0% is using.</small>
<li class="nav-item text-center text-uppercase py-2">
<span>Settings</span>
</li>
<li class="nav-item px-3 d-narrow-none" id="ramUsage">
<div class="text-uppercase mb-1"><small><b>Memory Usage</b></small></div>
<div class="progress progress-thin">
<div class="progress-bar" role="progressbar" style="width: 0%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<small class="text-medium-emphasis-inverse">0/0</small>
</li>
<li class="nav-item px-3 mb-3 d-narrow-none" id="hddUsage">
<div class="text-uppercase mb-1"><small><b>Disk Usage</b></small></div>
<div class="progress progress-thin">
<div class="progress-bar" role="progressbar" style="width: 0%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<small class="text-medium-emphasis-inverse">0/0</small>
<li class="nav-item">
<a asp-page="Settings" class="nav-link">
<i class="bx bx-cog me-2"></i>Settings
</a>
</li>
</ul>
<button class="sidebar-toggler" type="button" data-coreui-toggle="unfoldable"></button>
</div>
<div class="wrapper d-flex flex-column min-vh-100 bg-light">
<header class="header header-sticky mb-4">
<div class="container-fluid">
<button class="header-toggler px-md-0 me-md-3" type="button" onclick="coreui.Sidebar.getInstance(document.querySelector('#sidebar')).toggle()">
<svg class="icon icon-lg">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-menu"></use>
</svg>
</button>
<a class="header-brand d-md-none" href="#">
<svg width="118" height="46" alt="Wireguard Logo">
<use xlink:href="assets/brand/wireguard.svg"></use>
</svg>
</a>
<ul class="header-nav d-none d-md-flex">
<li class="nav-item">
<a class="nav-link" asp-page="Index">Dashboard</a>
</li>
<li class="nav-item">
<a class="nav-link" asp-page="Clients">Users</a>
</li>
<li class="nav-item">
<a class="nav-link" asp-page="Settings">Settings</a>
</li>
</ul>
<ul class="header-nav ms-auto">
<li class="nav-item dropdown d-md-down-none">
<a class="nav-link" data-coreui-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">
<span class="d-inline-block my-1 mx-2 position-relative">
<svg class="icon icon-lg">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-list-rich"></use>
</svg>
<span class="position-absolute top-0 start-100 translate-middle p-1 bg-danger rounded-circle">
<span class="visually-hidden">New logs</span>
</span>
</span>
</a>
<div class="dropdown-menu dropdown-menu-end dropdown-menu-lg py-0" style="max-width: 30vw; max-height: 70vh; overflow-y: auto;">
<component type="typeof(Components.LogsView)" render-mode="Static" param-logs="@logs" />
<a class="dropdown-item text-center border-top" asp-page="Logs"><strong>View all messages</strong></a>
</div>
</li>
</ul>
<ul class="header-nav ms-3">
<li class="nav-item dropdown">
<a class="nav-link py-0" data-coreui-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">
<div class="avatar avatar-md"><img class="avatar-img" src="assets/img/avatars/admin.png" alt="kazem_ma79@yahoo.com"></div>
</a>
<div class="dropdown-menu dropdown-menu-end py-0 mb-0">
<div class="dropdown-header bg-light py-2">
<div class="fw-semibold">Account</div>
</div>
<a class="dropdown-item" asp-page="Logout">
<svg class="icon">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-account-logout"></use>
</svg> Logout
</a>
</div>
</li>
</ul>
</div>
<div class="header-divider"></div>
<div class="container-fluid">
<nav aria-label="breadcrumb">
<ol class="breadcrumb my-0 ms-2">
<li class="breadcrumb-item">
<span>Dashboard</span>
</li>
<li class="breadcrumb-item active">
<span>@ViewData["Breadcrumb"]</span>
</li>
</ol>
</nav>
</div>
</header>
<div class="body flex-grow-1 px-3">
<div class="m-1">
<!-- Sidebar End -->
<!-- Content Start -->
<div class="content">
<!-- Navbar Start -->
<nav class="navbar navbar-expand navbar-light sticky-top px-4 py-0">
<a href="index.html" class="navbar-brand d-flex d-lg-none me-4">
<h2 class="text-primary mb-0"><i class="fa fa-hashtag"></i></h2>
</a>
<a href="#" class="sidebar-toggler flex-shrink-0">
<i class="fa fa-bars"></i>
</a>
<form class="col-4 d-none d-md-flex ms-4">
<input class="form-control border-1 shadow-sm" type="search" placeholder="Search">
</form>
<div class="navbar-nav align-items-center ms-auto">
<div class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" data-bs-toggle="dropdown">
<i class="bx bx-sm bx-bell"></i>
</a>
<ul id="logs" class="dropdown-menu dropdown-menu-end border-1 rounded-0 rounded-bottom m-0"></ul>
</div>
<div class="nav-item dropdown">
<a href="#" class="nav-link" data-bs-toggle="dropdown">
<i class="bx bx-sm bx-palette"></i>
</a>
<ul class="dropdown-menu p-0">
<li>
<a class="dropdown-item" href="javascript:void(0);" data-bs-theme-value="light">
<span class="d-flex align-items-center"><i class="bx bx-sun me-2"></i>Light</span>
</a>
</li>
<li>
<a class="dropdown-item" href="javascript:void(0);" data-bs-theme-value="dark">
<span class="d-flex align-items-center"><i class="bx bx-moon me-2"></i>Dark</span>
</a>
</li>
<li>
<a class="dropdown-item" href="javascript:void(0);" data-bs-theme-value="auto">
<span class="d-flex align-items-center"><i class="bx bx-desktop me-2"></i>System</span>
</a>
</li>
</ul>
</div>
<div class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" data-bs-toggle="dropdown">
<img class="rounded-circle me-lg-2" src="img/user.png" alt="" style="width: 40px; height: 40px;">
<span class="d-none d-lg-inline-flex">@username</span>
</a>
<div class="dropdown-menu dropdown-menu-end border-1 rounded-0 rounded-bottom m-0">
<a asp-page="Logout" class="dropdown-item">Log Out</a>
</div>
</div>
</div>
</nav>
<!-- Navbar End -->
<div class="container-fluid p-4">
@RenderBody()
</div>
</div>
<footer class="footer">
<div><a href="https://mtwg.techgarage.ir/">Mikrotik WireGuard Dashboard</a> © 2023 Techgarage.</div>
<div class="ms-md-auto">Powered by <a href="https://coreui.io/">CoreUI</a></div>
</footer>
</div>
<component type="typeof(Components.ToastContainer)" render-mode="Static" />
<!-- CoreUI and necessary plugins-->
<script src="vendors/coreui/coreui/js/coreui.bundle.min.js"></script>
<script src="vendors/simplebar/js/simplebar.min.js"></script>
<script src="vendors/calendarify/calendarify.iife.js"></script>
<script src="https://unpkg.com/@@popperjs/core@@^2.0.0"></script>
<script src="vendors/tempus/js/tempus-dominus.min.js"></script>
<script src="js/script.js"></script>
<!-- Footer Start -->
<footer class="container-fluid px-4">
<div class="rounded-top p-4">
<div class="row">
<div class="col-12 col-sm-6 text-center text-sm-start">
2022 - 2024 &copy; <a href="#">MTWireguard</a>, All Right Reserved.
</div>
<div class="col-12 col-sm-6 text-center text-sm-end">
Designed By <a href="https://htmlcodex.com">HTML Codex</a>
</div>
</div>
</div>
</footer>
<!-- Footer End -->
</div>
<!-- Content End -->
@await RenderSectionAsync("Modals", required: false)
<!-- Back to Top -->
<a href="#" class="btn btn-lg btn-primary btn-lg-square back-to-top" style="display: none;"><i class="bi bi-arrow-up"></i></a>
</div>
<!-- JavaScript Libraries -->
<script src="js/APIClient.js"></script>
<script src="assets/js/main.js"></script>
<script src="js/wireguard.js"></script>
<script src="assets/libs/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="assets/lib/jquery/js/jquery.min.js"></script>
<script src="assets/libs/jquery-ui/jquery-ui.min.js"></script>
<script src="lib/chart/chart.min.js"></script>
<script src="lib/easing/easing.min.js"></script>
<script src="lib/waypoints/waypoints.min.js"></script>
<script src="assets/lib/owl.carousel/js/owl.carousel.min.js"></script>
<script src="lib/tempusdominus/js/moment.min.js"></script>
<script src="lib/tempusdominus/js/moment-timezone.min.js"></script>
<script src="lib/tempusdominus/js/tempusdominus-bootstrap-4.min.js"></script>
<script src="assets/libs/flatpickr/flatpickr.min.js"></script>
<script src="https://cdn.datatables.net/v/bs5/jszip-3.10.1/dt-1.13.7/b-2.4.2/b-html5-2.4.2/fc-4.3.0/fh-3.4.0/r-2.5.0/sp-2.2.0/sl-1.7.0/datatables.min.js"></script>
<script>
$.ajaxSetup({
async: true
});
let here = location.pathname;
$(`#sideNav .nav-item .nav-link[href="${here}"]`).addClass('active');
</script>
<!-- Template Javascript -->
<script src="js/helper.js"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>

View file

@ -1,79 +0,0 @@
@using MTWireGuard.Application.Models.Mikrotik
@model (List<WGServerViewModel>, List<ServerTrafficViewModel>)
@{
var servers = Model.Item1;
var traffic = Model.Item2;
}
@foreach (var server in servers)
{
<tr class="align-middle" data-id="@server.Id">
<td class="text-center">
<span class="text-medium-emphasis">#@server.Id</span>
</td>
<td>
<div>@server.Name</div>
</td>
<td>
<div>@server.ListenPort</div>
</td>
<td>
<div>@server.MTU</div>
</td>
<td>
<div>@server.PublicKey</div>
</td>
<td>
@{
var enabled = server.IsEnabled;
string enableORdisable = enabled ? "Disable" : "Enable";
var currentServerTraffic = traffic.Find(s => s.Name == server.Name);
var up = currentServerTraffic.Upload;
var down = currentServerTraffic.Download;
}
<div>
<svg class="icon">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-data-transfer-up"></use>
</svg>
@up / @down
<svg class="icon">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-data-transfer-down"></use>
</svg>
</div>
</td>
<td>
@switch (enabled)
{
case true:
<span class="badge rounded-pill text-bg-success">Enabled</span>
break;
case false:
<span class="badge rounded-pill text-bg-danger">Disabled</span>
break;
}
@switch (server.Running)
{
case true:
<span class="badge rounded-pill text-bg-success">Running</span>
break;
case false:
<span class="badge rounded-pill text-bg-secondary">Not Running</span>
break;
}
</td>
<td>
<div class="dropdown position-static">
<button class="btn btn-transparent p-0" type="button" data-coreui-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<svg class="icon">
<use xlink:href="vendors/coreui/icons/svg/free.svg#cil-options"></use>
</svg>
</button>
<div class="dropdown-menu dropdown-menu-end">
<a class="dropdown-item" asp-page="Servers" asp-page-handler="Enable" asp-route-ID="@server.Id" asp-route-IsEnabled="@enabled" data-target="_self" onclick="dropdownBtnClick(event);">@enableORdisable</a>
<button class="dropdown-item update-btn" data-coreui-toggle="modal" data-coreui-target="#EditModal" onclick="updateServerBtn(event)">Edit</button>
<button class="dropdown-item delete-btn text-danger" data-coreui-toggle="modal" data-coreui-target="#DeleteModal" data-id="@server.Id" onclick="deleteBtn(event)">Delete</button>
</div>
</div>
</td>
</tr>
}

View file

@ -1,51 +1,13 @@
using Microsoft.AspNetCore.Authentication.Cookies;
using MTWireGuard.Middlewares;
using MTWireGuard.Application;
using MTWireGuard.Mapper;
using Microsoft.Extensions.Caching.Memory;
using MTWireGuard.Application.MinimalAPI;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages().AddRazorPagesOptions(o =>
{
//o.Conventions.ConfigureFilter(new IgnoreAntiforgeryTokenAttribute());
o.Conventions.AuthorizeFolder("/");
o.Conventions.AllowAnonymousToPage("/Login");
});
builder.Services.AddAntiforgery(o =>
{
o.HeaderName = "XSRF-TOKEN";
o.FormFieldName = "XSRF-Validation-Token";
o.Cookie.Name = "XSRF-Validation";
});
builder.Services.AddAutoMapper(typeof(RequestProfile));
builder.Services.AddApplicationServices();
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
{
options.ExpireTimeSpan = TimeSpan.FromMinutes(15);
options.SlidingExpiration = true;
options.LoginPath = "/Login";
options.AccessDeniedPath = "/Forbidden";
options.Cookie.Name = "Authentication";
});
builder.Services.AddAuthorization(configure =>
{
configure.AddPolicy("Administrator", authBuilder =>
{
authBuilder.RequireRole("Administrator");
});
});
builder.Services.ConfigureApplicationCookie(configure =>
{
configure.Cookie.Name = "MTWireguard";
});
var app = builder.Build();
// Configure the HTTP request pipeline.
@ -57,9 +19,11 @@ if (!app.Environment.IsDevelopment())
app.UseHttpsRedirection();
// Ensure Database Exists
var serviceScope = app.Services.CreateScope().ServiceProvider;
//serviceScope.GetService<DBContext>().Database.EnsureCreated();
// Validate Prerequisite
var validator = new SetupValidator(serviceScope);
await validator.Validate();
if (!app.Environment.IsDevelopment())
app.UseStaticFiles();
@ -68,13 +32,14 @@ else
{
OnPrepareResponse = context =>
{
context.Context.Response.Headers.Add("Cache-Control", "no-cache, no-store");
context.Context.Response.Headers.Add("Expires", "-1");
context.Context.Response.Headers.Append("Cache-Control", "no-cache, no-store");
context.Context.Response.Headers.Append("Expires", "-1");
}
});
app.UseDependencyCheck();
//app.UseExceptionHandling();
app.UseClientReporting();
app.UseExceptionHandling();
//app.UseAntiForgery();
app.UseRouting();
@ -82,6 +47,19 @@ app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.MapRazorPages();
app.
MapGroup("/api/").
MapGeneralApi();
app.UseCors(options =>
{
options.AllowAnyHeader();
options.AllowAnyMethod();
options.AllowAnyOrigin();
});
app.Run();

View file

@ -3,7 +3,6 @@
"MTWireGuard": {
"commandName": "Project",
"launchBrowser": true,
"hotReloadEnabled": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"MT_IP": "192.168.0.96",
@ -11,8 +10,10 @@
"MT_PASS": "",
"MT_PUBLIC_IP": "192.168.0.96"
},
"hotReloadEnabled": true,
"dotnetRunMessages": true,
"applicationUrl": "https://localhost:7220;http://localhost:5220"
"applicationUrl": "https://192.168.0.5:7220;http://192.168.0.5:5220",
"nativeDebugging": true
},
"IIS Express": {
"commandName": "IISExpress",
@ -25,14 +26,14 @@
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
"publishAllPorts": true,
"useSSL": true,
"environmentVariables": {
"MT_IP": "192.168.0.96",
"MT_USER": "panel",
"MT_PASS": "",
"MT_PUBLIC_IP": "192.168.0.96"
}
},
"publishAllPorts": true,
"useSSL": true
}
},
"iisSettings": {

View file

@ -1,15 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using MTWireGuard.Models.Requests;
using System.Xml.Linq;
namespace MTWireGuard.ViewComponents
{
[ViewComponent(Name = "CreateClientForm")]
public class CreateClientFormViewComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync()
{
return View("CreateClientForm", new CreateClientRequest());
}
}
}

View file

@ -1,14 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using MTWireGuard.Models.Requests;
namespace MTWireGuard.ViewComponents
{
[ViewComponent(Name = "CreateServerForm")]
public class CreateServerFormViewComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync()
{
return View("CreateServerForm", new CreateServerRequest());
}
}
}

View file

@ -1,15 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using MTWireGuard.Models.Requests;
namespace MTWireGuard.ViewComponents
{
[ViewComponent(Name = "DeleteModal")]
public class DeleteModalViewComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(bool IsServer)
{
ViewData["IsServer"] = IsServer;
return View("DeleteModal", new DeleteRequest());
}
}
}

View file

@ -1,15 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using MTWireGuard.Models.Requests;
using System.Xml.Linq;
namespace MTWireGuard.ViewComponents
{
[ViewComponent(Name = "SyncUserModal")]
public class SyncUserModalViewComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync()
{
return View("SyncUserModal", new SyncUserRequest());
}
}
}

View file

@ -1,16 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using MTWireGuard.Application.Models.Mikrotik;
using MTWireGuard.Models.Requests;
namespace MTWireGuard.ViewComponents
{
[ViewComponent(Name = "UpdateClientModal")]
public class UpdateClientModalViewComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(List<WGServerViewModel> Servers)
{
ViewData["Servers"] = Servers;
return View("UpdateClientModal", new UpdateClientRequest());
}
}
}

View file

@ -1,14 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using MTWireGuard.Models.Requests;
namespace MTWireGuard.ViewComponents
{
[ViewComponent(Name = "UpdateServerModal")]
public class UpdateServerModalViewComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync()
{
return View("UpdateServerModal", new UpdateServerRequest());
}
}
}

View file

@ -1,9 +0,0 @@
<svg width="300" height="300" viewport="0 0 46 46" xmlns="http://www.w3.org/2000/svg" version="1.1">
<title>wireguard</title>
<g>
<title>Layer 1</title>
<path id="svg_37" fill="#88171a" d="m299.7406,145.56s6.9396,-145.56 -153.04,-145.56c-141.48,0 -145.9,139.63 -145.9,139.63s-20.811,160.37 149.16,160.37c163.02,0 149.78,-154.44 149.78,-154.44zm-197.8,-50.863c30.017,-18.364 68.366,-7.1401 82.735,20.476c2.7233,5.2338 3.0694,13.291 1.3447,18.782c-5.9546,18.956 -20.014,29.587 -39.312,34.103c5.6892,-4.8707 10.218,-10.394 11.659,-18.025a26.402,26.402 0 0 0 -4.5425,-20.956a26.76,26.76 0 0 0 -30.811,-9.3892c-11.881,4.5111 -18.389,15.354 -17.216,28.683c1.0898,12.381 10.484,20.405 28.061,23.453c-2.627,1.3904 -4.6503,2.4144 -6.6299,3.5172a63.918,63.918 0 0 0 -20.544,17.868c-1.7839,2.4084 -3.0104,2.6024 -5.727,0.94116c-35.338,-21.61 -37.609,-75.844 0.98226,-99.453l0.00044,-0.00016zm-26.449,133.53c-5.6769,1.441 -11.178,3.5742 -16.981,5.4775c2.8385,-19.151 25.265,-36.788 44.23,-34.776a48.881,48.881 0 0 0 -9.242,25.893c-6.302,1.1606 -12.241,1.9414 -18.007,3.405l0,0.0005zm120.79,-186.98c5.6099,0.20612 11.23,0.12091 16.844,0.25378a29.052,29.052 0 0 1 4.1674,0.58069a40.607,40.607 0 0 1 -4.2357,5.4332c-2.007,1.8701 -4.2745,3.6986 -7.1661,0.856c-0.6955,-0.68372 -2.3386,-0.52679 -3.5487,-0.54272c-5.5823,-0.07336 -11.172,-0.25177 -16.746,-0.04132a104.04,104.04 0 0 0 -14.425,1.473c-0.89368,0.16046 -2.2299,3.1315 -1.8191,4.227c0.9693,2.5853 2.3833,5.4363 4.4779,7.0898c7.7403,6.11 15.972,11.596 23.748,17.664c7.556,5.8966 14.589,12.358 18.875,21.253c5.5843,11.59 5.747,23.743 3.3388,35.95c-4.0203,20.378 -14.333,37.261 -31.032,49.524c-6.7288,4.941 -15.06,7.7451 -22.767,11.295c-6.778,3.1225 -13.755,5.8115 -20.549,8.9008c-12.249,5.5695 -19.133,18.865 -17.108,32.688c1.8585,12.685 12.987,23.271 25.735,25.456c15.292,2.6216 31.071,-7.3163 34.812,-22.86c4.2067,-17.478 -5.2898,-33.083 -23.065,-37.813c-0.78271,-0.20831 -1.5684,-0.40552 -3.2012,-0.8269c4.7549,-2.1245 8.8614,-3.6381 12.653,-5.7244q9.9213,-5.4594 19.481,-11.562c1.8742,-1.199 2.8868,-1.1996 4.4852,0.18225c12.225,10.57 19.518,23.718 21.563,39.839c3.3845,26.684 -9.2471,51.198 -33.072,63.762c-36.86,19.439 -81.965,-2.6864 -90.106,-43.552c-6.9738,-35.003 17.73,-66.754 47.462,-72.884c12.787,-2.6364 24.48,-7.9596 33.57,-17.807c5.8652,-6.3541 8.7084,-11.806 9.6772,-14.266a39.565,39.565 0 0 0 2.7211,-14.469a33.867,33.867 0 0 0 -2.9654,-12.398c-3.104,-7.075 -14.995,-18.33 -17.939,-20.704l-28,-21.921c-0.98761,-0.81256 -2.0994,-0.75366 -4.5079,-0.59045c-2.8611,0.19391 -10.175,0.59888 -13.331,-0.22815c2.553,-1.9321 9.5132,-4.7451 12.502,-7.007c-9.0734,-6.1297 -19.43,-3.9158 -28.941,-5.7461c2.1992,-4.0959 13.081,-10.39 19.27,-11.091a91.533,91.533 0 0 0 -1.6876,-10.281c-0.37781,-1.3917 -1.9312,-2.7408 -3.2864,-3.5355c-3.286,-1.9267 -6.7694,-3.5167 -10.549,-5.4327a21.936,21.936 0 0 1 11.332,-3.5055a42.316,42.316 0 0 1 11.348,1.1056c6.7422,1.5405 12.124,0.53491 17.488,-4.048c-4.222,-1.7002 -8.4435,-3.2535 -12.538,-5.0907a123.04,123.04 0 0 1 -11.779,-6.1583c10.622,1.4755 20.896,5.4585 31.757,4.0034q0.1387,-0.74048 0.27728,-1.4809c-8.1194,-1.8899 -16.239,-3.7798 -25.229,-5.8724c15.04,-1.3769 29.042,-1.604 42.301,4.8541c3.731,1.8173 7.6348,3.3215 11.211,5.3972c1.7443,1.0124 2.9186,3.0078 4.3496,4.5594c1.1366,1.2325 2.0495,2.8837 3.446,3.6264c5.3,2.8184 11.134,2.9291 17.078,2.7879c0.04443,-0.67694 0.08606,-1.3114 0.1308,-1.9933c5.9821,1.8693 12.715,8.7679 12.704,13.806c-9.6911,0 -19.374,-0.037 -29.056,0.05389c-1.0348,0.0097 -2.0626,0.76563 -3.0936,1.1754c0.97986,0.57067 1.9428,1.5994 2.9423,1.6362l-0.00388,-0.00067z" class="a"/>
<path id="svg_38" fill="#88171a" d="m183.7806,26.906a1.4806,1.4806 0 0 0 -0.18927,2.3686a2.2326,2.2326 0 0 0 3.0724,0.8219c0.9328,-0.47052 1.8478,-0.97137 2.975,-1.5665c-0.9079,-0.775 -1.6362,-1.4148 -2.3857,-2.0324c-1.318,-1.086 -2.411,-0.40386 -3.4724,0.40833l-0.00003,0.00007z" class="a"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.8 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
.btn.btn-primary{color:#fff}.progress .progress-bar{width:0;transition:2s}.testimonial-carousel .owl-dots{margin-top:24px;display:flex;align-items:flex-end;justify-content:center}.testimonial-carousel .owl-dot{position:relative;display:inline-block;margin:0 5px;width:15px;height:15px;border:5px solid var(--primary);border-radius:15px;transition:.5s}.testimonial-carousel .owl-dot.active{background:var(--dark);border-color:var(--primary)}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<TileColor>#da532c</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View file

@ -1,54 +0,0 @@
{
"name": "Mikrotik Wireguard Manager",
"short_name": "MTWG",
"icons": [
{
"src": "/android-icon-36x36.png",
"sizes": "36x36",
"type": "image/png"
},
{
"src": "/android-icon-48x48.png",
"sizes": "48x48",
"type": "image/png"
},
{
"src": "/android-icon-72x72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "/android-icon-96x96.png",
"sizes": "96x96",
"type": "image/png"
},
{
"src": "/android-icon-144x144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "/android-icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-icon-256x256.png",
"sizes": "256x256",
"type": "image/png"
},
{
"src": "/android-icon-384x384.png",
"sizes": "384x384",
"type": "image/png"
},
{
"src": "/android-icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Some files were not shown because too many files have changed in this diff Show more