Use new front-end based on Dashmin template
|
@ -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>
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
34
UI/Middlewares/ClientReportingMiddleware.cs
Normal 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>();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
{
|
||||
|
|
51
UI/Middlewares/ErrorHandlingMiddleware.cs
Normal 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>();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
namespace MTWireGuard.Models.Requests
|
||||
{
|
||||
public class DeleteRequest
|
||||
{
|
||||
public int Id { get; set; }
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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
|
@ -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">
|
||||
© <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 author’s credit link/attribution link/backlink. If you'd like to use the template without the footer author’s 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
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
<th>Name</th>
|
||||
<th>Interface</th>
|
||||
<th>Address</th>
|
||||
<th>Traffic</th>
|
||||
<th>Expire</th>
|
||||
<th>Status</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
<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>
|
||||
|
||||
<div class="row mt-2">
|
||||
<div class="col-12">
|
||||
@await Component.InvokeAsync("CreateClientForm", new {
|
||||
Servers = servers
|
||||
})
|
||||
<!-- 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>Allowed Address</th>
|
||||
<th>Traffic</th>
|
||||
<th>Expire</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
</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>
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
21
UI/Pages/Components/Modals/DeleteModal.razor
Normal 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>
|
18
UI/Pages/Components/Modals/DetailsModal.razor
Normal 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 {
|
||||
|
||||
}
|
17
UI/Pages/Components/Modals/QRModal.razor
Normal 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 {
|
||||
|
||||
}
|
95
UI/Pages/Components/Offcanvas/Client/Add.razor
Normal 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="············" 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>
|
38
UI/Pages/Components/Offcanvas/Client/Sync.razor
Normal 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="············" 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>
|
93
UI/Pages/Components/Offcanvas/Client/Update.razor
Normal 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="············" 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>
|
69
UI/Pages/Components/Offcanvas/Server/Add.razor
Normal 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>
|
70
UI/Pages/Components/Offcanvas/Server/Update.razor
Normal 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>
|
|
@ -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>
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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>
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
</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>
|
||||
<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="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-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>
|
||||
<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="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-danger">
|
||||
<i class="bx bx-broadcast bx-sm"></i>
|
||||
</span>
|
||||
</div>
|
||||
</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 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-warning">
|
||||
<i class="bx bx-group bx-sm"></i>
|
||||
</span>
|
||||
</div>
|
||||
</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>
|
||||
<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 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>
|
||||
</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-lg-6">
|
||||
@await Component.InvokeAsync("CreateClientForm", new {
|
||||
Servers = servers
|
||||
})
|
||||
<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>
|
||||
</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>
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<button class="btn btn-primary px-4" type="submit">Login</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</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;
|
||||
}
|
||||
|
||||
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>
|
||||
</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>
|
||||
</html>
|
||||
|
||||
<!-- 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>
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
</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>
|
||||
<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>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</thead>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>
|
||||
<i class="bx bx-hash"></i>
|
||||
</th>
|
||||
<th>Message</th>
|
||||
<th>Topics</th>
|
||||
<th>Time</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script>
|
||||
</script>
|
||||
<script src="js/page-logs.js"></script>
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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
|
@ -0,0 +1,12 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace MTWireGuard.Pages
|
||||
{
|
||||
public class PoolsModel : PageModel
|
||||
{
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
}
|
||||
|
|
|
@ -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);*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 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">
|
||||
<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 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-success" id="ram-box">
|
||||
<div class="card-body">
|
||||
<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 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" id="uptime-box">
|
||||
<div class="card-body">
|
||||
<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">00:00:00</div>
|
||||
<small class="text-medium-emphasis-inverse text-uppercase fw-semibold">UPTime</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Modals {
|
||||
<!-- Toasts -->
|
||||
<component type="typeof(Components.ToastContainer)" render-mode="Static" />
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-6 col-sm-4 col-lg-4">
|
||||
<div class="card text-white bg-info">
|
||||
<div class="card-body">
|
||||
<div class="fs-4 fw-semibold">@info.FreeHDD</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>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6 col-sm-4 col-lg-4">
|
||||
<div class="card text-white bg-warning">
|
||||
<div class="card-body">
|
||||
<div class="fs-4 fw-semibold">@info.FreeRAM</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>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6 col-sm-4 col-lg-4">
|
||||
<div class="card text-white bg-danger">
|
||||
<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>
|
||||
<div class="fs-4 fw-semibold">@info.UPTime</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 {
|
||||
<script src="js/helper.js"></script>
|
||||
<script src="js/page-settings.js"></script>
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
}
|
|
@ -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="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>
|
||||
</div>
|
||||
<ul class="sidebar-nav" data-coreui="navigation" data-simplebar="">
|
||||
<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>
|
||||
</li>
|
||||
<li class="nav-title">Wireguard</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
<!-- 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>
|
||||
<small class="text-medium-emphasis-inverse">0% is using.</small>
|
||||
</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 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>
|
||||
<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>
|
||||
</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">
|
||||
<ul class="navbar-nav w-100" id="sideNav">
|
||||
<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 asp-page="Index" class="nav-link">
|
||||
<i class="bx bx-home-circle me-2"></i>Dashboard
|
||||
</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>
|
||||
<li class="nav-item text-center text-uppercase py-2">
|
||||
<span>Wireguard</span>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a asp-page="Clients" class="nav-link">
|
||||
<i class="bx bx-group me-2"></i>Users
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a asp-page="Servers" class="nav-link">
|
||||
<i class="bx bx-broadcast me-2"></i>Servers
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a asp-page="Pools" class="nav-link">
|
||||
<i class="bx bx-current-location me-2"></i>Pools
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item text-center text-uppercase py-2">
|
||||
<span>Settings</span>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a asp-page="Settings" class="nav-link">
|
||||
<i class="bx bx-cog me-2"></i>Settings
|
||||
</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">
|
||||
</nav>
|
||||
</div>
|
||||
<!-- 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 © <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>
|
||||
|
||||
</html>
|
||||
|
|
|
@ -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>
|
||||
}
|
|
@ -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.Run();
|
||||
app.
|
||||
MapGroup("/api/").
|
||||
MapGeneralApi();
|
||||
|
||||
app.UseCors(options =>
|
||||
{
|
||||
options.AllowAnyHeader();
|
||||
options.AllowAnyMethod();
|
||||
options.AllowAnyOrigin();
|
||||
});
|
||||
|
||||
app.Run();
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 |
Before Width: | Height: | Size: 14 KiB |
5
UI/wwwroot/assets/css/core.css
Normal file
1
UI/wwwroot/assets/css/main.css
Normal 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)}
|
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 14 KiB |
|
@ -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>
|
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 15 KiB |
|
@ -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"
|
||||
}
|
Before Width: | Height: | Size: 8.4 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 9.7 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 17 KiB |