Skip to content

Commit

Permalink
Merge pull request #693 from b-editor/develop
Browse files Browse the repository at this point in the history
v1.0.0 Preview.2
  • Loading branch information
yuto-trd authored Sep 6, 2023
2 parents 7fe2fd2 + 23cac81 commit 14641ec
Show file tree
Hide file tree
Showing 184 changed files with 8,144 additions and 2,932 deletions.
102 changes: 54 additions & 48 deletions src/Beutl.Api/BeutlApiApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@

using Beutl.Api.Objects;
using Beutl.Api.Services;

using Beutl;
using Beutl.Configuration;

using Reactive.Bindings;
Expand Down Expand Up @@ -56,9 +54,11 @@ public BeutlApiApplication(HttpClient httpClient)
public DiscoverClient Discover { get; }

public LibraryClient Library { get; }

public AppClient App { get; }

public MyAsyncLock Lock { get; } = new();

public IReadOnlyReactiveProperty<AuthorizedUser?> AuthorizedUser => _authorizedUser;

public T GetResource<T>()
Expand Down Expand Up @@ -155,32 +155,35 @@ private async Task<AuthorizedUser> SignInExternalAsync(string provider, Cancella

public async Task<AuthorizedUser> SignInAsync(CancellationToken cancellationToken)
{
string continueUri = $"http://localhost:{GetRandomUnusedPort()}/__/auth/handler";
CreateAuthUriResponse authUriRes = await Account.CreateAuthUriAsync(new CreateAuthUriRequest(continueUri), cancellationToken);
using HttpListener listener = StartListener($"{continueUri}/");
using (await Lock.LockAsync(cancellationToken))
{
string continueUri = $"http://localhost:{GetRandomUnusedPort()}/__/auth/handler";
CreateAuthUriResponse authUriRes = await Account.CreateAuthUriAsync(new CreateAuthUriRequest(continueUri), cancellationToken);
using HttpListener listener = StartListener($"{continueUri}/");

string uri = $"{BaseUrl}/Identity/Account/Login?returnUrl={authUriRes.Auth_uri}";
string uri = $"{BaseUrl}/Identity/Account/Login?returnUrl={authUriRes.Auth_uri}";

Process.Start(new ProcessStartInfo(uri)
{
UseShellExecute = true
});
Process.Start(new ProcessStartInfo(uri)
{
UseShellExecute = true
});

string? code = await GetResponseFromListener(listener, cancellationToken);
if (string.IsNullOrWhiteSpace(code))
{
throw new Exception("The returned code was empty.");
}
string? code = await GetResponseFromListener(listener, cancellationToken);
if (string.IsNullOrWhiteSpace(code))
{
throw new Exception("The returned code was empty.");
}

AuthResponse authResponse = await Account.CodeToJwtAsync(new CodeToJwtRequest(code, authUriRes.Session_id), cancellationToken);
AuthResponse authResponse = await Account.CodeToJwtAsync(new CodeToJwtRequest(code, authUriRes.Session_id), cancellationToken);

_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authResponse.Token);
ProfileResponse profileResponse = await Users.Get2Async(cancellationToken);
var profile = new Profile(profileResponse, this);
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authResponse.Token);
ProfileResponse profileResponse = await Users.Get2Async(cancellationToken);
var profile = new Profile(profileResponse, this);

_authorizedUser.Value = new AuthorizedUser(profile, authResponse, this, _httpClient);
SaveUser();
return _authorizedUser.Value;
_authorizedUser.Value = new AuthorizedUser(profile, authResponse, this, _httpClient);
SaveUser();
return _authorizedUser.Value;
}
}

public static void OpenAccountSettings()
Expand Down Expand Up @@ -214,35 +217,38 @@ public void SaveUser()

public async Task RestoreUserAsync()
{
string fileName = Path.Combine(Helper.AppRoot, "user.json");
if (File.Exists(fileName))
using (await Lock.LockAsync())
{
JsonNode? node;
using (StreamReader reader = File.OpenText(fileName))
{
string json = await reader.ReadToEndAsync();
node = JsonNode.Parse(json);
}

if (node != null)
string fileName = Path.Combine(Helper.AppRoot, "user.json");
if (File.Exists(fileName))
{
ProfileResponse? profile = JsonSerializer.Deserialize<ProfileResponse>(node["profile"]);
string? token = (string?)node["token"];
string? refreshToken = (string?)node["refresh_token"];
var expiration = (DateTimeOffset?)node["expiration"];

if (profile != null
&& token != null
&& refreshToken != null
&& expiration.HasValue)
JsonNode? node;
using (StreamReader reader = File.OpenText(fileName))
{
var user = new AuthorizedUser(new Profile(profile, this), new AuthResponse(expiration.Value, refreshToken, token), this, _httpClient);
await user.RefreshAsync();
string json = await reader.ReadToEndAsync();
node = JsonNode.Parse(json);
}

_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", user.Token);
await user.Profile.RefreshAsync();
_authorizedUser.Value = user;
SaveUser();
if (node != null)
{
ProfileResponse? profile = JsonSerializer.Deserialize<ProfileResponse>(node["profile"]);
string? token = (string?)node["token"];
string? refreshToken = (string?)node["refresh_token"];
var expiration = (DateTimeOffset?)node["expiration"];

if (profile != null
&& token != null
&& refreshToken != null
&& expiration.HasValue)
{
var user = new AuthorizedUser(new Profile(profile, this), new AuthResponse(expiration.Value, refreshToken, token), this, _httpClient);
await user.RefreshAsync();

_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", user.Token);
await user.Profile.RefreshAsync();
_authorizedUser.Value = user;
SaveUser();
}
}
}
}
Expand Down
65 changes: 65 additions & 0 deletions src/Beutl.Api/MyAsyncLock.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// 以下のような再入場を調べるための、デバッグ用のコードです。

// using (await lock.LockAsync())
// {
// await Second();
// }

// async Task Second()
// {
// using (await lock.LockAsync())
// {
// }
// }

#if !DEBUG
global using MyAsyncLock = Nito.AsyncEx.AsyncLock;
#endif

namespace Beutl.Api;

#if DEBUG
public sealed class MyAsyncLock
{
private readonly SemaphoreSlim _semaphore = new(1, 1);
private readonly Task<IDisposable> _releaser;

public MyAsyncLock()
{
_releaser = Task.FromResult<IDisposable>(new Releaser(this));
}

public Task<IDisposable> LockAsync(CancellationToken cancellationToken = default)
{
Task wait = _semaphore.WaitAsync(cancellationToken);
if (wait.IsCompleted)
{
return _releaser;
}
else
{
return wait.ContinueWith(
continuationFunction: (_, state) => (IDisposable)state!,
state: _releaser.Result,
cancellationToken: cancellationToken,
continuationOptions: TaskContinuationOptions.ExecuteSynchronously,
scheduler: TaskScheduler.Default);
}
}

private sealed class Releaser : IDisposable
{
private readonly MyAsyncLock _mutex;

public Releaser(MyAsyncLock toRelease)
{
_mutex = toRelease;
}

public void Dispose()
{
_mutex._semaphore.Release();
}
}
}
#endif
4 changes: 4 additions & 0 deletions src/Beutl.Api/Objects/Asset.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Reactive.Linq;

using Nito.AsyncEx;

using Reactive.Bindings;

namespace Beutl.Api.Objects;
Expand Down Expand Up @@ -52,6 +54,8 @@ public Asset(Profile profile, AssetMetadataResponse response, BeutlApiApplicatio

public IReadOnlyReactiveProperty<bool> IsPublic { get; }

public MyAsyncLock Lock => _clients.Lock;

public async Task RefreshAsync()
{
_response.Value = await _clients.Assets.GetAssetAsync(Owner.Name, Name);
Expand Down
20 changes: 9 additions & 11 deletions src/Beutl.Api/Objects/AuthorizedUser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ namespace Beutl.Api.Objects;

public class AuthorizedUser
{
private readonly AsyncLock _mutex = new();
private readonly BeutlApiApplication _clients;
private readonly HttpClient _httpClient;
private AuthResponse _response;
Expand All @@ -30,20 +29,19 @@ public AuthorizedUser(Profile profile, AuthResponse response, BeutlApiApplicatio

public bool IsExpired => Expiration < DateTimeOffset.UtcNow;

public MyAsyncLock Lock => _clients.Lock;

public async ValueTask RefreshAsync(bool force = false)
{
using (await _mutex.LockAsync())
if (force || IsExpired)
{
if (force || IsExpired)
_response = await _clients.Account.RefreshAsync(new RefeshTokenRequest(RefreshToken, Token))
.ConfigureAwait(false);
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token);

if (_clients.AuthorizedUser.Value == this)
{
_response = await _clients.Account.RefreshAsync(new RefeshTokenRequest(RefreshToken, Token))
.ConfigureAwait(false);
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token);

if (_clients.AuthorizedUser.Value == this)
{
_clients.SaveUser();
}
_clients.SaveUser();
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/Beutl.Api/Objects/Package.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Reactive.Linq;

using Nito.AsyncEx;

using Reactive.Bindings;

namespace Beutl.Api.Objects;
Expand Down Expand Up @@ -57,6 +59,8 @@ public Package(Profile profile, PackageResponse response, BeutlApiApplication cl

public IReadOnlyReactiveProperty<bool> IsDeleted => _isDeleted;

public MyAsyncLock Lock => _clients.Lock;

public async Task RefreshAsync()
{
_response.Value = await _clients.Packages.GetPackageAsync(Name);
Expand Down
4 changes: 4 additions & 0 deletions src/Beutl.Api/Objects/Profile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using System.Reactive.Linq;
using System.Text;

using Nito.AsyncEx;

using Reactive.Bindings;

namespace Beutl.Api.Objects;
Expand Down Expand Up @@ -54,6 +56,8 @@ public Profile(ProfileResponse response, BeutlApiApplication clients)

public IReadOnlyReactiveProperty<int> PublicPackages { get; }

public MyAsyncLock Lock => _clients.Lock;

public async Task RefreshAsync()
{
_response.Value = await _clients.Users.GetUserAsync(Name);
Expand Down
4 changes: 4 additions & 0 deletions src/Beutl.Api/Services/DiscoverService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
using System.Threading.Tasks;
using Beutl.Api.Objects;

using Nito.AsyncEx;

namespace Beutl.Api.Services;

public class DiscoverService : IBeutlApiResource
Expand All @@ -17,6 +19,8 @@ public DiscoverService(BeutlApiApplication clients)
_clients = clients;
}

public MyAsyncLock Lock => _clients.Lock;

public async Task<Package> GetPackage(string name)
{
PackageResponse package = await _clients.Packages.GetPackageAsync(name);
Expand Down
15 changes: 13 additions & 2 deletions src/Beutl.Api/Services/ExtensionProvider.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Beutl.Collections;
using System.Collections.Concurrent;

using Beutl.Collections;
using Beutl.Configuration;
using Beutl.Extensibility;

Expand All @@ -8,7 +10,7 @@ namespace Beutl.Api.Services;

public sealed class ExtensionProvider : IBeutlApiResource
{
internal readonly Dictionary<int, Extension[]> _allExtensions = new();
private readonly ConcurrentDictionary<int, Extension[]> _allExtensions = new();
private readonly ExtensionConfig _config = GlobalConfiguration.Instance.ExtensionConfig;
private readonly Dictionary<Type, Array> _cache = new();
private bool _cacheInvalidated;
Expand Down Expand Up @@ -107,6 +109,15 @@ public IEnumerable<ProjectItemExtension> MatchProjectItemExtensions(string file)
}
}

public void AddExtensions(int id, Extension[] extensions)
{
if (!_allExtensions.TryAdd(id, extensions))
{
throw new Exception("");
}
InvalidateCache();
}

public void InvalidateCache()
{
_cacheInvalidated = true;
Expand Down
9 changes: 5 additions & 4 deletions src/Beutl.Api/Services/LocalPackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ namespace Beutl.Api.Services;

public class LocalPackage
{
internal static int s_nextId;
// LoadPrimitiveExtensionTask
internal const int Reserved0 = 0;

internal static int s_nextId = 2;

public LocalPackage()
{
LocalId = s_nextId++;
LocalId = Interlocked.Increment(ref s_nextId);
}

public LocalPackage(Package package)
Expand Down Expand Up @@ -44,8 +47,6 @@ public LocalPackage(NuspecReader nuspecReader)
ShortDescription = nuspecReader.GetDescription();
//Logo = nuspecReader.GetIcon();
Tags = nuspecReader.GetTags().Split(' ', ';').ToList();

LocalId = -1;
}

public string Name { get; set; } = string.Empty;
Expand Down
Loading

0 comments on commit 14641ec

Please sign in to comment.