Added google and discord oauth2. Fixed menu bugs
This commit is contained in:
parent
8f3f9fa1fb
commit
60693d25da
12 changed files with 555 additions and 47 deletions
132
Moonlight/App/Http/Controllers/Api/Moonlight/OAuth2Controller.cs
Normal file
132
Moonlight/App/Http/Controllers/Api/Moonlight/OAuth2Controller.cs
Normal file
|
@ -0,0 +1,132 @@
|
|||
using Logging.Net;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Moonlight.App.Exceptions;
|
||||
using Moonlight.App.Helpers;
|
||||
using Moonlight.App.Repositories;
|
||||
using Moonlight.App.Services;
|
||||
using Moonlight.App.Services.OAuth2;
|
||||
using Moonlight.App.Services.Sessions;
|
||||
|
||||
namespace Moonlight.App.Http.Controllers.Api.Moonlight;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/moonlight/oauth2")]
|
||||
public class OAuth2Controller : Controller
|
||||
{
|
||||
private readonly GoogleOAuth2Service GoogleOAuth2Service;
|
||||
private readonly DiscordOAuth2Service DiscordOAuth2Service;
|
||||
private readonly UserRepository UserRepository;
|
||||
private readonly UserService UserService;
|
||||
|
||||
public OAuth2Controller(
|
||||
GoogleOAuth2Service googleOAuth2Service,
|
||||
UserRepository userRepository,
|
||||
UserService userService,
|
||||
DiscordOAuth2Service discordOAuth2Service)
|
||||
{
|
||||
GoogleOAuth2Service = googleOAuth2Service;
|
||||
UserRepository = userRepository;
|
||||
UserService = userService;
|
||||
DiscordOAuth2Service = discordOAuth2Service;
|
||||
}
|
||||
|
||||
[HttpGet("google")]
|
||||
public async Task<ActionResult> Google([FromQuery] string code)
|
||||
{
|
||||
try
|
||||
{
|
||||
var userData = await GoogleOAuth2Service.HandleCode(code);
|
||||
|
||||
if (userData == null)
|
||||
return Redirect("/login");
|
||||
|
||||
try
|
||||
{
|
||||
var user = UserRepository.Get().FirstOrDefault(x => x.Email == userData.Email);
|
||||
|
||||
string token;
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
token = await UserService.Register(
|
||||
userData.Email,
|
||||
StringHelper.GenerateString(32),
|
||||
userData.FirstName,
|
||||
userData.LastName
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
token = await UserService.GenerateToken(user);
|
||||
}
|
||||
|
||||
Response.Cookies.Append("token", token, new ()
|
||||
{
|
||||
Expires = new DateTimeOffset(DateTime.UtcNow.AddDays(10))
|
||||
});
|
||||
|
||||
return Redirect("/");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warn(e.Message);
|
||||
return Redirect("/login");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warn(e.Message);
|
||||
return BadRequest();
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("discord")]
|
||||
public async Task<ActionResult> Discord([FromQuery] string code)
|
||||
{
|
||||
try
|
||||
{
|
||||
var userData = await DiscordOAuth2Service.HandleCode(code);
|
||||
|
||||
if (userData == null)
|
||||
return Redirect("/login");
|
||||
|
||||
try
|
||||
{
|
||||
var user = UserRepository.Get().FirstOrDefault(x => x.Email == userData.Email);
|
||||
|
||||
string token;
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
token = await UserService.Register(
|
||||
userData.Email,
|
||||
StringHelper.GenerateString(32),
|
||||
userData.FirstName,
|
||||
userData.LastName
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
token = await UserService.GenerateToken(user);
|
||||
}
|
||||
|
||||
Response.Cookies.Append("token", token, new ()
|
||||
{
|
||||
Expires = new DateTimeOffset(DateTime.UtcNow.AddDays(10))
|
||||
});
|
||||
|
||||
return Redirect("/");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warn(e.Message);
|
||||
return Redirect("/login");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warn(e.Message);
|
||||
return BadRequest();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
using Newtonsoft.Json;
|
||||
|
||||
namespace Moonlight.App.Models.Google.Requests;
|
||||
|
||||
public class GoogleOAuth2CodePayload
|
||||
{
|
||||
[JsonProperty("grant_type")]
|
||||
public string GrantType { get; set; } = "authorization_code";
|
||||
|
||||
[JsonProperty("code")]
|
||||
public string Code { get; set; }
|
||||
|
||||
[JsonProperty("client_id")]
|
||||
public string ClientId { get; set; }
|
||||
|
||||
[JsonProperty("client_secret")]
|
||||
public string ClientSecret { get; set; }
|
||||
|
||||
[JsonProperty("redirect_uri")]
|
||||
public string RedirectUri { get; set; }
|
||||
}
|
127
Moonlight/App/Services/OAuth2/DiscordOAuth2Service.cs
Normal file
127
Moonlight/App/Services/OAuth2/DiscordOAuth2Service.cs
Normal file
|
@ -0,0 +1,127 @@
|
|||
using System.Text;
|
||||
using Logging.Net;
|
||||
using Moonlight.App.Database.Entities;
|
||||
using Moonlight.App.Exceptions;
|
||||
using Moonlight.App.Models.Google.Requests;
|
||||
using RestSharp;
|
||||
|
||||
namespace Moonlight.App.Services.OAuth2;
|
||||
|
||||
public class DiscordOAuth2Service
|
||||
{
|
||||
private readonly bool Enable;
|
||||
private readonly string ClientId;
|
||||
private readonly string ClientSecret;
|
||||
|
||||
private readonly bool EnableOverrideUrl;
|
||||
private readonly string OverrideUrl;
|
||||
private readonly string AppUrl;
|
||||
|
||||
public DiscordOAuth2Service(ConfigService configService)
|
||||
{
|
||||
var config = configService
|
||||
.GetSection("Moonlight")
|
||||
.GetSection("OAuth2");
|
||||
|
||||
Enable = config
|
||||
.GetSection("Discord")
|
||||
.GetValue<bool>("Enable");
|
||||
|
||||
if (Enable)
|
||||
{
|
||||
ClientId = config.GetSection("Discord").GetValue<string>("ClientId");
|
||||
ClientSecret = config.GetSection("Discord").GetValue<string>("ClientSecret");
|
||||
}
|
||||
|
||||
EnableOverrideUrl = config.GetValue<bool>("EnableOverrideUrl");
|
||||
|
||||
if (EnableOverrideUrl)
|
||||
OverrideUrl = config.GetValue<string>("OverrideUrl");
|
||||
|
||||
AppUrl = configService.GetSection("Moonlight").GetValue<string>("AppUrl");
|
||||
}
|
||||
|
||||
public Task<string> GetUrl()
|
||||
{
|
||||
if (!Enable)
|
||||
throw new DisplayException("Discord OAuth2 not enabled");
|
||||
|
||||
string url = $"https://discord.com/api/oauth2/authorize?client_id={ClientId}" +
|
||||
$"&redirect_uri={GetBaseUrl()}/api/moonlight/oauth2/discord" +
|
||||
"&response_type=code&scope=identify%20email";
|
||||
|
||||
return Task.FromResult(
|
||||
url
|
||||
);
|
||||
}
|
||||
|
||||
public async Task<User?> HandleCode(string code)
|
||||
{
|
||||
// Generate access token
|
||||
var endpoint = GetBaseUrl() + "/api/moonlight/oauth2/discord";
|
||||
var discordEndpoint = "https://discordapp.com/api/oauth2/token";
|
||||
|
||||
using var client = new RestClient();
|
||||
var request = new RestRequest(discordEndpoint);
|
||||
|
||||
request.AddParameter("client_id", ClientId);
|
||||
request.AddParameter("client_secret", ClientSecret);
|
||||
request.AddParameter("grant_type", "authorization_code");
|
||||
request.AddParameter("code", code);
|
||||
request.AddParameter("redirect_uri", endpoint);
|
||||
|
||||
var response = await client.ExecutePostAsync(request);
|
||||
|
||||
if (!response.IsSuccessful)
|
||||
{
|
||||
//TODO: Maybe add better error handling
|
||||
Logger.Debug("oAuth2 validate error: " + response.Content!);
|
||||
return null;
|
||||
}
|
||||
|
||||
// parse response
|
||||
|
||||
var data = new ConfigurationBuilder().AddJsonStream(
|
||||
new MemoryStream(Encoding.ASCII.GetBytes(response.Content!))
|
||||
).Build();
|
||||
|
||||
var accessToken = data.GetValue<string>("access_token");
|
||||
|
||||
// Now, we will call the google api with our access token to get the data we need
|
||||
|
||||
var googlePeopleEndpoint = "https://discordapp.com/api/users/@me";
|
||||
|
||||
var getRequest = new RestRequest(googlePeopleEndpoint);
|
||||
getRequest.AddHeader("Authorization", $"Bearer {accessToken}");
|
||||
|
||||
var getResponse = await client.ExecuteGetAsync(getRequest);
|
||||
|
||||
if (!getResponse.IsSuccessful)
|
||||
{
|
||||
//TODO: Maybe add better error handling
|
||||
Logger.Debug("OAuth2 api access error: " + getResponse.Content!);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Parse response
|
||||
|
||||
var getData = new ConfigurationBuilder().AddJsonStream(
|
||||
new MemoryStream(Encoding.ASCII.GetBytes(getResponse.Content!))
|
||||
).Build();
|
||||
|
||||
return new User()
|
||||
{
|
||||
Email = getData.GetValue<string>("email"),
|
||||
FirstName = getData.GetValue<string>("username"),
|
||||
LastName = getData.GetValue<string>("discriminator")
|
||||
};
|
||||
}
|
||||
|
||||
private string GetBaseUrl()
|
||||
{
|
||||
if (EnableOverrideUrl)
|
||||
return OverrideUrl;
|
||||
|
||||
return AppUrl;
|
||||
}
|
||||
}
|
149
Moonlight/App/Services/OAuth2/GoogleOAuth2Service.cs
Normal file
149
Moonlight/App/Services/OAuth2/GoogleOAuth2Service.cs
Normal file
|
@ -0,0 +1,149 @@
|
|||
using System.Text;
|
||||
using Logging.Net;
|
||||
using Moonlight.App.Database.Entities;
|
||||
using Moonlight.App.Exceptions;
|
||||
using Moonlight.App.Models.Google.Requests;
|
||||
using RestSharp;
|
||||
|
||||
namespace Moonlight.App.Services.OAuth2;
|
||||
|
||||
public class GoogleOAuth2Service
|
||||
{
|
||||
private readonly bool EnableGoogle;
|
||||
private readonly string GoogleClientId;
|
||||
private readonly string GoogleClientSecret;
|
||||
|
||||
private readonly bool EnableOverrideUrl;
|
||||
private readonly string OverrideUrl;
|
||||
private readonly string AppUrl;
|
||||
|
||||
public GoogleOAuth2Service(ConfigService configService)
|
||||
{
|
||||
var config = configService
|
||||
.GetSection("Moonlight")
|
||||
.GetSection("OAuth2");
|
||||
|
||||
EnableGoogle = config
|
||||
.GetSection("Google")
|
||||
.GetValue<bool>("Enable");
|
||||
|
||||
if (EnableGoogle)
|
||||
{
|
||||
GoogleClientId = config.GetSection("Google").GetValue<string>("ClientId");
|
||||
GoogleClientSecret = config.GetSection("Google").GetValue<string>("ClientSecret");
|
||||
}
|
||||
|
||||
EnableOverrideUrl = config.GetValue<bool>("EnableOverrideUrl");
|
||||
|
||||
if (EnableOverrideUrl)
|
||||
OverrideUrl = config.GetValue<string>("OverrideUrl");
|
||||
|
||||
AppUrl = configService.GetSection("Moonlight").GetValue<string>("AppUrl");
|
||||
}
|
||||
|
||||
public Task<string> GetUrl()
|
||||
{
|
||||
if (!EnableGoogle)
|
||||
throw new DisplayException("Google OAuth2 not enabled");
|
||||
|
||||
var endpoint = GetBaseUrl() + "/api/moonlight/oauth2/google";
|
||||
var scope = "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email";
|
||||
|
||||
return Task.FromResult(
|
||||
$"https://accounts.google.com/o/oauth2/auth?response_type=code&client_id={GoogleClientId}&redirect_uri={endpoint}&scope={scope}"
|
||||
);
|
||||
}
|
||||
|
||||
public async Task<User?> HandleCode(string code)
|
||||
{
|
||||
// Generate access token
|
||||
var endpoint = GetBaseUrl() + "/api/moonlight/oauth2/google";
|
||||
var googleEndpoint = "https://oauth2.googleapis.com/token";
|
||||
|
||||
// Setup payload
|
||||
var payload = new GoogleOAuth2CodePayload()
|
||||
{
|
||||
Code = code,
|
||||
RedirectUri = endpoint,
|
||||
ClientId = GoogleClientId,
|
||||
ClientSecret = GoogleClientSecret
|
||||
};
|
||||
|
||||
using var client = new RestClient();
|
||||
var request = new RestRequest(googleEndpoint);
|
||||
|
||||
request.AddBody(payload);
|
||||
var response = await client.ExecutePostAsync(request);
|
||||
|
||||
if (!response.IsSuccessful)
|
||||
{
|
||||
//TODO: Maybe add better error handling
|
||||
Logger.Debug("oAuth2 validate error: " + response.Content!);
|
||||
return null;
|
||||
}
|
||||
|
||||
// parse response
|
||||
|
||||
var data = new ConfigurationBuilder().AddJsonStream(
|
||||
new MemoryStream(Encoding.ASCII.GetBytes(response.Content!))
|
||||
).Build();
|
||||
|
||||
var accessToken = data.GetValue<string>("access_token");
|
||||
|
||||
// Now, we will call the google api with our access token to get the data we need
|
||||
|
||||
var googlePeopleEndpoint = "https://people.googleapis.com/v1/people/me";
|
||||
|
||||
var getRequest = new RestRequest(googlePeopleEndpoint);
|
||||
getRequest.AddHeader("Authorization", $"Bearer {accessToken}");
|
||||
getRequest.AddParameter("personFields", "names,emailAddresses");
|
||||
|
||||
var getResponse = await client.ExecuteGetAsync(getRequest);
|
||||
|
||||
if (!getResponse.IsSuccessful)
|
||||
{
|
||||
//TODO: Maybe add better error handling
|
||||
Logger.Debug("OAuth2 api access error: " + getResponse.Content!);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Parse response
|
||||
|
||||
var getData = new ConfigurationBuilder().AddJsonStream(
|
||||
new MemoryStream(Encoding.ASCII.GetBytes(getResponse.Content!))
|
||||
).Build();
|
||||
|
||||
var firstName = getData
|
||||
.GetSection("names")
|
||||
.GetChildren()
|
||||
.First()
|
||||
.GetValue<string>("givenName");
|
||||
|
||||
var lastName = getData
|
||||
.GetSection("names")
|
||||
.GetChildren()
|
||||
.First()
|
||||
.GetValue<string>("familyName");
|
||||
|
||||
var email = getData
|
||||
.GetSection("emailAddresses")
|
||||
.GetChildren()
|
||||
.First()
|
||||
.GetValue<string>("value");
|
||||
|
||||
return new()
|
||||
{
|
||||
Email = email,
|
||||
FirstName = firstName,
|
||||
LastName = lastName
|
||||
};
|
||||
}
|
||||
|
||||
private string GetBaseUrl()
|
||||
{
|
||||
if (EnableOverrideUrl)
|
||||
return OverrideUrl;
|
||||
|
||||
return AppUrl;
|
||||
}
|
||||
}
|
|
@ -68,13 +68,7 @@ public class UserService
|
|||
//var mail = new WelcomeMail(user);
|
||||
//await MailService.Send(mail, user);
|
||||
|
||||
return JwtBuilder.Create()
|
||||
.WithAlgorithm(new HMACSHA256Algorithm())
|
||||
.WithSecret(JwtSecret)
|
||||
.AddClaim("exp", DateTimeOffset.UtcNow.AddDays(10).ToUnixTimeSeconds())
|
||||
.AddClaim("iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds())
|
||||
.AddClaim("userid", user.Id)
|
||||
.Encode();
|
||||
return await GenerateToken(user);
|
||||
}
|
||||
|
||||
public Task<bool> CheckTotp(string email, string password)
|
||||
|
@ -123,13 +117,7 @@ public class UserService
|
|||
{
|
||||
//AuditLogService.Log("login:success", $"{user.Email} has successfully logged in");
|
||||
|
||||
return JwtBuilder.Create()
|
||||
.WithAlgorithm(new HMACSHA256Algorithm())
|
||||
.WithSecret(JwtSecret)
|
||||
.AddClaim("exp", DateTimeOffset.UtcNow.AddDays(10).ToUnixTimeSeconds())
|
||||
.AddClaim("iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds())
|
||||
.AddClaim("userid", user.Id)
|
||||
.Encode();
|
||||
return await GenerateToken(user);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -141,17 +129,11 @@ public class UserService
|
|||
{
|
||||
//AuditLogService.Log("login:success", $"{user.Email} has successfully logged in");
|
||||
|
||||
return JwtBuilder.Create()
|
||||
.WithAlgorithm(new HMACSHA256Algorithm())
|
||||
.WithSecret(JwtSecret)
|
||||
.AddClaim("exp", DateTimeOffset.UtcNow.AddDays(10).ToUnixTimeSeconds())
|
||||
.AddClaim("iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds())
|
||||
.AddClaim("userid", user.Id)
|
||||
.Encode();
|
||||
return await GenerateToken(user!);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ChangePassword(User user, string password)
|
||||
public Task ChangePassword(User user, string password)
|
||||
{
|
||||
user.Password = BCrypt.Net.BCrypt.HashPassword(password);
|
||||
user.TokenValidTime = DateTime.Now;
|
||||
|
@ -161,6 +143,8 @@ public class UserService
|
|||
//await MailService.Send(mail, user);
|
||||
|
||||
//AuditLogService.Log("password:change", "The password has been set to a new one");
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<User> SftpLogin(int id, string password)
|
||||
|
@ -179,4 +163,17 @@ public class UserService
|
|||
//TODO: Log
|
||||
throw new Exception("Invalid userid or password");
|
||||
}
|
||||
|
||||
public Task<string> GenerateToken(User user)
|
||||
{
|
||||
var token = JwtBuilder.Create()
|
||||
.WithAlgorithm(new HMACSHA256Algorithm())
|
||||
.WithSecret(JwtSecret)
|
||||
.AddClaim("exp", DateTimeOffset.UtcNow.AddDays(10).ToUnixTimeSeconds())
|
||||
.AddClaim("iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds())
|
||||
.AddClaim("userid", user.Id)
|
||||
.Encode();
|
||||
|
||||
return Task.FromResult(token);
|
||||
}
|
||||
}
|
|
@ -59,6 +59,7 @@
|
|||
<ItemGroup>
|
||||
<Folder Include="App\Http\Middleware" />
|
||||
<Folder Include="App\Models\AuditLogData" />
|
||||
<Folder Include="App\Models\Google\Resources" />
|
||||
<Folder Include="resources\lang" />
|
||||
<Folder Include="wwwroot\assets\media" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -7,6 +7,7 @@ using Moonlight.App.Repositories;
|
|||
using Moonlight.App.Repositories.Servers;
|
||||
using Moonlight.App.Services;
|
||||
using Moonlight.App.Services.Interop;
|
||||
using Moonlight.App.Services.OAuth2;
|
||||
using Moonlight.App.Services.Sessions;
|
||||
using Moonlight.App.Services.Support;
|
||||
|
||||
|
@ -56,6 +57,8 @@ namespace Moonlight
|
|||
builder.Services.AddSingleton<PaperService>();
|
||||
builder.Services.AddScoped<ClipboardService>();
|
||||
builder.Services.AddSingleton<ResourceService>();
|
||||
builder.Services.AddScoped<GoogleOAuth2Service>();
|
||||
builder.Services.AddScoped<DiscordOAuth2Service>();
|
||||
|
||||
builder.Services.AddScoped<AuditLogService>();
|
||||
builder.Services.AddScoped<SystemAuditLogService>();
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
@using Moonlight.App.Services
|
||||
@using Moonlight.App.Exceptions
|
||||
@using Logging.Net
|
||||
@using Moonlight.App.Services.OAuth2
|
||||
@using Moonlight.App.Services.Sessions
|
||||
|
||||
@inject AlertService AlertService
|
||||
|
@ -16,6 +17,8 @@
|
|||
@inject SmartTranslateService SmartTranslateService
|
||||
@inject CookieService CookieService
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject GoogleOAuth2Service GoogleOAuth2Service
|
||||
@inject DiscordOAuth2Service DiscordOAuth2Service
|
||||
|
||||
<div class="d-flex flex-center">
|
||||
<div class="card rounded-3 w-md-550px">
|
||||
|
@ -35,17 +38,17 @@
|
|||
|
||||
<div class="row g-3 mb-9">
|
||||
<div class="col-md-6">
|
||||
<a href="#" class="btn btn-flex btn-outline btn-text-gray-700 btn-active-color-primary bg-state-light flex-center text-nowrap w-100">
|
||||
<a href="#" @onclick:preventDefault @onclick="DoDiscord" class="btn btn-flex btn-outline btn-text-gray-700 btn-active-color-primary bg-state-light flex-center text-nowrap w-100">
|
||||
<div class="h-15px me-3">
|
||||
<i class="bx bx-md bxl-discord-alt"></i>
|
||||
<i class="mb-1 bx bx-md bxl-discord-alt"></i>
|
||||
</div>
|
||||
<TL>Sign in with Discord</TL>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<a href="#" class="btn btn-flex btn-outline btn-text-gray-700 btn-active-color-primary bg-state-light flex-center text-nowrap w-100">
|
||||
<a href="#" @onclick:preventDefault @onclick="DoGoogle" class="btn btn-flex btn-outline btn-text-gray-700 btn-active-color-primary bg-state-light flex-center text-nowrap w-100">
|
||||
<div class="h-15px me-3">
|
||||
<i class="bx bx-md bxl-google"></i>
|
||||
<i class="mb-1 bx bx-md bxl-google"></i>
|
||||
</div>
|
||||
<TL>Sign in with Google</TL>
|
||||
</a>
|
||||
|
@ -158,4 +161,16 @@
|
|||
Logger.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DoGoogle()
|
||||
{
|
||||
var url = await GoogleOAuth2Service.GetUrl();
|
||||
NavigationManager.NavigateTo(url, true);
|
||||
}
|
||||
|
||||
private async Task DoDiscord()
|
||||
{
|
||||
var url = await DiscordOAuth2Service.GetUrl();
|
||||
NavigationManager.NavigateTo(url, true);
|
||||
}
|
||||
}
|
|
@ -6,8 +6,12 @@
|
|||
*@
|
||||
|
||||
@using Moonlight.App.Services
|
||||
@using Moonlight.App.Services.OAuth2
|
||||
|
||||
@inject SmartTranslateService SmartTranslateService
|
||||
@inject GoogleOAuth2Service GoogleOAuth2Service
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject DiscordOAuth2Service DiscordOAuth2Service
|
||||
|
||||
<div class="d-flex flex-center">
|
||||
<div class="card rounded-3 w-md-550px">
|
||||
|
@ -25,17 +29,17 @@
|
|||
|
||||
<div class="row g-3 mb-9">
|
||||
<div class="col-md-6">
|
||||
<a href="#" class="btn btn-flex btn-outline btn-text-gray-700 btn-active-color-primary bg-state-light flex-center text-nowrap w-100">
|
||||
<a href="#" @onclick:preventDefault @onclick="DoDiscord" class="btn btn-flex btn-outline btn-text-gray-700 btn-active-color-primary bg-state-light flex-center text-nowrap w-100">
|
||||
<div class="h-15px me-3">
|
||||
<i class="bx bx-md bxl-discord-alt"></i>
|
||||
<i class="mb-1 bx bx-md bxl-discord-alt"></i>
|
||||
</div>
|
||||
<TL>Sign up with Discord</TL>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<a href="#" class="btn btn-flex btn-outline btn-text-gray-700 btn-active-color-primary bg-state-light flex-center text-nowrap w-100">
|
||||
<a href="#" @onclick:preventDefault @onclick="DoGoogle" class="btn btn-flex btn-outline btn-text-gray-700 btn-active-color-primary bg-state-light flex-center text-nowrap w-100">
|
||||
<div class="h-15px me-3">
|
||||
<i class="bx bx-md bxl-google"></i>
|
||||
<i class="mb-1 bx bx-md bxl-google"></i>
|
||||
</div>
|
||||
<TL>Sign up with Google</TL>
|
||||
</a>
|
||||
|
@ -85,4 +89,19 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
private async Task DoGoogle()
|
||||
{
|
||||
var url = await GoogleOAuth2Service.GetUrl();
|
||||
NavigationManager.NavigateTo(url, true);
|
||||
}
|
||||
|
||||
private async Task DoDiscord()
|
||||
{
|
||||
var url = await DiscordOAuth2Service.GetUrl();
|
||||
NavigationManager.NavigateTo(url, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
@using Logging.Net
|
||||
@using Moonlight.App.Exceptions
|
||||
@using Moonlight.App.Services
|
||||
@using Moonlight.App.Services.Interop
|
||||
@using Moonlight.App.Services.Sessions
|
||||
|
||||
@inherits ErrorBoundary
|
||||
|
||||
@inject IdentityService IdentityService
|
||||
@inject AlertService AlertService
|
||||
@inject SmartTranslateService SmartTranslateService
|
||||
|
||||
@if (CurrentException is null)
|
||||
{
|
||||
|
@ -60,6 +64,21 @@ else
|
|||
Logger.Error(exception);
|
||||
|
||||
await base.OnErrorAsync(exception);
|
||||
|
||||
if (exception is DisplayException displayException)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await AlertService.Error(
|
||||
SmartTranslateService.Translate("Error"),
|
||||
SmartTranslateService.Translate(displayException.Message)
|
||||
);
|
||||
});
|
||||
|
||||
Recover();
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
public new void Recover()
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
@using Moonlight.App.Services
|
||||
@using Moonlight.App.Services.Interop
|
||||
@using Moonlight.App.Services.Sessions
|
||||
@using Logging.Net
|
||||
|
||||
@layout ThemeInit
|
||||
@implements IDisposable
|
||||
|
@ -31,7 +32,7 @@
|
|||
{
|
||||
if (!string.IsNullOrEmpty(pathPart))
|
||||
{
|
||||
if(pathPart == pathParts.Last())
|
||||
if (pathPart == pathParts.Last())
|
||||
title += $"{pathPart.FirstCharToUpper()} ";
|
||||
else
|
||||
title += $"{pathPart.FirstCharToUpper()} - ";
|
||||
|
@ -128,11 +129,6 @@
|
|||
AddBodyAttribute("data-kt-app-toolbar-enabled", "true");
|
||||
|
||||
AddBodyClass("app-default");
|
||||
|
||||
JsRuntime.InvokeVoidAsync("KTModalUpgradePlan.init");
|
||||
JsRuntime.InvokeVoidAsync("KTCreateApp.init");
|
||||
JsRuntime.InvokeVoidAsync("KTModalUserSearch.init");
|
||||
JsRuntime.InvokeVoidAsync("KTModalNewTarget.init");
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
|
@ -142,9 +138,7 @@
|
|||
try
|
||||
{
|
||||
User = await IdentityService.Get();
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
await Task.Delay(300);
|
||||
|
||||
await JsRuntime.InvokeVoidAsync("document.body.removeAttribute", "data-kt-app-reset-transition");
|
||||
await JsRuntime.InvokeVoidAsync("document.body.removeAttribute", "data-kt-app-page-loading");
|
||||
|
@ -157,19 +151,24 @@
|
|||
NavigationManager.LocationChanged += (sender, args) => { SessionService.Refresh(); };
|
||||
|
||||
MessageService.Subscribe<MainLayout, SupportMessage>(
|
||||
$"support.{User.Id}.message",
|
||||
this,
|
||||
$"support.{User.Id}.message",
|
||||
this,
|
||||
async message =>
|
||||
{
|
||||
if (!NavigationManager.Uri.EndsWith("/support") && (message.IsSupport || message.IsSystem))
|
||||
{
|
||||
await ToastService.Info($"Support: {message.Message}");
|
||||
}
|
||||
});
|
||||
if (!NavigationManager.Uri.EndsWith("/support") && (message.IsSupport || message.IsSystem))
|
||||
{
|
||||
await ToastService.Info($"Support: {message.Message}");
|
||||
}
|
||||
});
|
||||
|
||||
RunDelayedMenu(0);
|
||||
RunDelayedMenu(1);
|
||||
RunDelayedMenu(3);
|
||||
RunDelayedMenu(5);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -193,4 +192,21 @@
|
|||
{
|
||||
JsRuntime.InvokeVoidAsync("document.body.classList.add", className);
|
||||
}
|
||||
|
||||
private void RunDelayedMenu(int seconds)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(seconds));
|
||||
await JsRuntime.InvokeVoidAsync("KTMenu.initHandlers");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warn("Delayed menu error");
|
||||
Logger.Warn(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -2462,8 +2462,17 @@ KTMenu.updateDropdowns = function() {
|
|||
}
|
||||
}
|
||||
|
||||
// Bug fix for menu load initializing
|
||||
KTMenu.hasInit = false;
|
||||
|
||||
// Global handlers
|
||||
KTMenu.initHandlers = function() {
|
||||
|
||||
if(KTMenu.hasInit)
|
||||
return;
|
||||
|
||||
KTMenu.hasInit = true;
|
||||
|
||||
// Dropdown handler
|
||||
document.addEventListener("click", function(e) {
|
||||
var items = document.querySelectorAll('.show.menu-dropdown[data-kt-menu-trigger]');
|
||||
|
|
Loading…
Reference in a new issue