Skip to content

Commit

Permalink
feat: login
Browse files Browse the repository at this point in the history
  • Loading branch information
loliGothicK committed Feb 28, 2024
1 parent aea0cf5 commit 8cb2565
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 102 deletions.
2 changes: 1 addition & 1 deletion MitamatchOperations/App.xaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Application
x:Class="MitamatchOperations.App"
x:Class="mitama.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
Expand Down
221 changes: 129 additions & 92 deletions MitamatchOperations/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,117 +1,129 @@
using System.Diagnostics;
using System;
using System;
using System.Diagnostics;
using System.Threading.Channels;
using Google.Cloud.BigQuery.V2;
using JWT.Builder;
using Microsoft.UI.Xaml;
using Microsoft.Windows.AppLifecycle;
using mitama;
using JWT.Builder;
using Newtonsoft.Json;
using mitama.Domain;
using System.Threading.Channels;
using Mitama.Lib;
using Google.Cloud.BigQuery.V2;
using Mitama.Pages.Common;
using Newtonsoft.Json;

// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.

namespace MitamatchOperations
namespace mitama;

/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
public partial class App : Application
{
private readonly Channel<Result<DiscordUser, string>> channel = Channel.CreateUnbounded<Result<DiscordUser, string>>();
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public partial class App : Application
public App()
{
private readonly Channel<Result<DiscordUser, string>> channel = Channel.CreateUnbounded<Result<DiscordUser, string>>();
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("##SyncfusionLicense##");
InitializeComponent();
}
Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("##SyncfusionLicense##");
InitializeComponent();
}

/// <summary>
/// Invoked when the application is launched.
/// </summary>
/// <param name="args">Details about the launch request and process.</param>
protected override async void OnLaunched(LaunchActivatedEventArgs args)
{
// Get the activation args
var appArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
/// <summary>
/// Invoked when the application is launched.
/// </summary>
/// <param name="args">Details about the launch request and process.</param>
protected override async void OnLaunched(LaunchActivatedEventArgs args)
{
// Get the activation args
var appArgs = AppInstance.GetCurrent().GetActivatedEventArgs();

// Get or register the main instance
var mainInstance = AppInstance.FindOrRegisterForKey("main");
// Get or register the main instance
var mainInstance = AppInstance.FindOrRegisterForKey("main");

// If the main instance isn't this current instance
if (!mainInstance.IsCurrent)
{
// Redirect activation to that instance
await mainInstance.RedirectActivationToAsync(appArgs);
// If the main instance isn't this current instance
if (!mainInstance.IsCurrent)
{
// Redirect activation to that instance
await mainInstance.RedirectActivationToAsync(appArgs);

// And exit our instance and stop
Process.GetCurrentProcess().Kill();
return;
}
else
{
On_Activated(null, appArgs);
}
// And exit our instance and stop
Process.GetCurrentProcess().Kill();
return;
}
else
{
On_Activated(null, appArgs);
}

// Otherwise, register for activation redirection
AppInstance.GetCurrent().Activated += On_Activated;
// Otherwise, register for activation redirection
AppInstance.GetCurrent().Activated += On_Activated;

var splash = new SplashScreen(typeof(MainWindow), async () =>
var splash = new SplashScreen(typeof(MainWindow), async () =>
await channel.Reader.ReadAsync() switch
{
var item = await channel.Reader.ReadAsync();
switch (item)
{
case Ok<DiscordUser, string>(var user):
{
Upsert(user);
return true;
}
case Err<DiscordUser, string>:
{
return false;
}
};
return false;
Ok<DiscordUser, string> => true,
_ => false,
});
splash.Completed += (_, e) => m_window = e;
}
splash.Completed += (_, e) => m_window = e;
}

private async void On_Activated(object _, AppActivationArguments args)
private async void On_Activated(object _, AppActivationArguments args)
{
if (args.Kind == ExtendedActivationKind.Protocol)
{
if (args.Kind == ExtendedActivationKind.Protocol)
var eventArgs = args.Data as Windows.ApplicationModel.Activation.ProtocolActivatedEventArgs;
var query = eventArgs.Uri.Query;
var queryDictionary = System.Web.HttpUtility.ParseQueryString(query);
var jwtToken = queryDictionary["token"];
// verify JWT token
switch (DecodeJwt(jwtToken))
{
var eventArgs = args.Data as Windows.ApplicationModel.Activation.ProtocolActivatedEventArgs;
var query = eventArgs.Uri.Query;
var queryDictionary = System.Web.HttpUtility.ParseQueryString(query);
var jwtToken = queryDictionary["token"];
// verify JWT token
try
{
var json = JwtBuilder
.Create()
.WithAlgorithm(new JWT.Algorithms.HMACSHA256Algorithm())
.WithSecret("secret")
.MustVerifySignature()
.Decode(jwtToken);
// JSON から MyToken オブジェクトへのデシリアライズ
var user = JsonConvert.DeserializeObject<DiscordUser>(json);
await channel.Writer.WriteAsync(new Ok<DiscordUser, string>(user));
}
catch (Exception ex)
{
await channel.Writer.WriteAsync(new Err<DiscordUser, string>(ex.Message));
}
case Ok<string, string>(var json):
{
var user = JsonConvert.DeserializeObject<DiscordUser>(json);
Upsert(user);
// Cache に JWT token を保存
var cache = Director.ReadCache() with { JWT = jwtToken };
Director.CacheWrite(cache.ToJsonBytes());
await channel.Writer.WriteAsync(new Ok<DiscordUser, string>(user));
break;
}
case Err<string, string>(var msg):
{
await channel.Writer.WriteAsync(new Err<DiscordUser, string>(msg));
break;
}
}
}
}

public static Result<string, string> DecodeJwt(string token)
{
try
{
var json = JwtBuilder
.Create()
.WithAlgorithm(new JWT.Algorithms.HMACSHA256Algorithm())
.WithSecret("##MITAMA_AUTH_JWT_SECRET##")
.MustVerifySignature()
.Decode(token);
return new Ok<string, string>(json);
}
catch (Exception ex)
{
return new Err<string, string>(ex.Message);
}
}

private static void Upsert(DiscordUser user)
private static void Upsert(DiscordUser user)
{
if (Director.ReadCache().JWT is null)
{
var json = new {
var json = new
{
type = "service_account",
project_id = "assaultlily",
private_key_id = "13b3e809d5e493489d67018ac1d69d5c2e2eaa04",
Expand All @@ -124,12 +136,9 @@ private static void Upsert(DiscordUser user)
client_x509_cert_url = "https://www.googleapis.com/robot/v1/metadata/x509/mitamatch%40assaultlily.iam.gserviceaccount.com",
universe_domain = "googleapis.com"
};

var cred = Google.Apis.Auth.OAuth2.GoogleCredential.FromJson(JsonConvert.SerializeObject(json));

BigQueryClient client = BigQueryClient.Create("asaultlily", cred);
var client = BigQueryClient.Create("asaultlily", cred);
var table = client.GetTable("assaultlily", "mitamatch", "users");

_ = table.InsertRows(new BigQueryInsertRow()
{
{ "id", user.id },
Expand All @@ -138,7 +147,35 @@ private static void Upsert(DiscordUser user)
{ "avatar", user.avatar },
});
}
}

private Window m_window;
// DO NOT CALL THIS METHOD
internal static void Update(DiscordUser user)
{
var json = new
{
type = "service_account",
project_id = "assaultlily",
private_key_id = "13b3e809d5e493489d67018ac1d69d5c2e2eaa04",
private_key = "##GOOGLE_CLOUD_PRIVATE_KEY##",
client_email = "[email protected]",
client_id = "116107053801726389433",
auth_uri = "https://accounts.google.com/o/oauth2/auth",
token_uri = "https://oauth2.googleapis.com/token",
auth_provider_x509_cert_url = "https://www.googleapis.com/oauth2/v1/certs",
client_x509_cert_url = "https://www.googleapis.com/robot/v1/metadata/x509/mitamatch%40assaultlily.iam.gserviceaccount.com",
universe_domain = "googleapis.com"
};

var cred = Google.Apis.Auth.OAuth2.GoogleCredential.FromJson(JsonConvert.SerializeObject(json));

BigQueryClient client = BigQueryClient.Create("asaultlily", cred);
var avatar = user.avatar is null ? $"'{user.avatar}'" : "null";
string queryUpdate = $"UPDATE `assaultlily.mitamatch.users` SET avatar = {avatar}, updated_at = CURRENT_DATETIME WHERE id = '{user.id}'";

// クエリを実行して更新
client.ExecuteQuery(queryUpdate, parameters: null);
}

private Window m_window;
}
4 changes: 2 additions & 2 deletions MitamatchOperations/MitamatchOperations.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,9 @@
<HasPackageAndPublishMenu>true</HasPackageAndPublishMenu>
</PropertyGroup>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command="cd &quot;$(ProjectDir)&quot; &#xD;&#xA;SyncfusionLicenseRegister.bat PreBuild App.xaml.cs" />
<Exec Command="cd &quot;$(ProjectDir)&quot;&#xD;&#xA;RegisterSecrets.bat PreBuild App.xaml.cs" />
</Target>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="cd &quot;$(ProjectDir)&quot; &#xD;&#xA;SyncfusionLicenseRegister.bat PostBuild App.xaml.cs" />
<Exec Command="cd &quot;$(ProjectDir)&quot; &#xD;&#xA;RegisterSecrets.bat PostBuild App.xaml.cs" />
</Target>
</Project>
2 changes: 1 addition & 1 deletion MitamatchOperations/Pages/Common/Cache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Mitama.Pages.Common;

internal record struct Cache(string Legion, string User = null)
internal record struct Cache(string Legion, string User = null, string JWT = null)
{
internal static Cache FromJson(string json) => JsonSerializer.Deserialize<Cache>(json);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,19 @@
)
powershell -Command "cat %sourceFile%"
)

::Replacement string
set DummyJwtSecret=##MITAMA_AUTH_JWT_SECRET##
set JwtSecret=%MITAMA_AUTH_JWT_SECRET%

::Replacement statement
if NOT "%JwtSecret%" == "" (

if "%buildType%" == "PostBuild" (
powershell -Command "(gc %sourceFile%) -Replace '%JwtSecret%','%DummyJwtSecret%'|SC %sourceFile%"
)
if "%buildType%" == "PreBuild" (
powershell -Command "(gc %sourceFile%) -Replace '%DummyJwtSecret%','%JwtSecret%'|SC %sourceFile%"
)
powershell -Command "cat %sourceFile%"
)
16 changes: 10 additions & 6 deletions MitamatchOperations/SplashScreen.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
using System;
using System.Threading.Tasks;
using mitama.Domain;
using Mitama.Lib;
using Mitama.Pages.Common;
using Newtonsoft.Json;

namespace mitama;

Expand All @@ -22,15 +26,15 @@ public SplashScreen(Type window, Func<Task<bool>> IO) : base(window)

protected override async Task OnLoading()
{
while (!await PerformLogin())
var jwt = Director.ReadCache().JWT;
if (jwt is not null && App.DecodeJwt(jwt) is Ok<string, string> ok)
{
await Task.Delay(1000);
return;
}
for (int i = 0; i < 100; i += 5)

while (!await PerformLogin())
{
Status.Text = $"Loading {i}%...";
ProgressBar.Value = i;
await Task.Delay(50);
await Task.Delay(1000);
}
}
}

0 comments on commit 8cb2565

Please sign in to comment.