1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Use real backend

This commit is contained in:
Robert 2023-07-14 09:21:09 +02:00
parent 428bbd73e3
commit 1c07bef3cf
8 changed files with 43 additions and 30 deletions

View File

@ -5,6 +5,7 @@ using System.Reactive.Disposables;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.WebClient.Workshop;
using Artemis.WebClient.Workshop.Services; using Artemis.WebClient.Workshop.Services;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Flurl.Http; using Flurl.Http;
@ -94,7 +95,7 @@ public class CurrentUserViewModel : ActivatableViewModelBase
{ {
try try
{ {
Avatar = new Bitmap(await $"{IAuthenticationService.AUTHORITY}/user/avatar/{userId}".GetStreamAsync()); Avatar = new Bitmap(await $"{WorkshopConstants.AUTHORITY_URL}/user/avatar/{userId}".GetStreamAsync());
} }
catch (Exception) catch (Exception)
{ {

View File

@ -5,7 +5,7 @@
"strawberryShake": { "strawberryShake": {
"name": "WorkshopClient", "name": "WorkshopClient",
"namespace": "Artemis.WebClient.Workshop", "namespace": "Artemis.WebClient.Workshop",
"url": "https://localhost:7281/graphql/", "url": "https://workshop.artemis-rgb.com/graphql/",
"emitGeneratedCode": false, "emitGeneratedCode": false,
"records": { "records": {
"inputs": false, "inputs": false,

View File

@ -13,6 +13,7 @@
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
<PackageReference Include="StrawberryShake.Server" Version="13.0.5" /> <PackageReference Include="StrawberryShake.Server" Version="13.0.5" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.31.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -22,12 +22,12 @@ public static class ContainerExtensions
serviceCollection serviceCollection
.AddHttpClient() .AddHttpClient()
.AddWorkshopClient() .AddWorkshopClient()
.ConfigureHttpClient(client => client.BaseAddress = new Uri("https://localhost:7281/graphql")); .ConfigureHttpClient(client => client.BaseAddress = new Uri(WorkshopConstants.WORKSHOP_URL + "/graphql"));
serviceCollection.AddSingleton<IDiscoveryCache>(r => serviceCollection.AddSingleton<IDiscoveryCache>(r =>
{ {
IHttpClientFactory factory = r.GetRequiredService<IHttpClientFactory>(); IHttpClientFactory factory = r.GetRequiredService<IHttpClientFactory>();
return new DiscoveryCache(IAuthenticationService.AUTHORITY, () => factory.CreateClient()); return new DiscoveryCache(WorkshopConstants.AUTHORITY_URL, () => factory.CreateClient());
}); });
container.WithDependencyInjectionAdapter(serviceCollection); container.WithDependencyInjectionAdapter(serviceCollection);

View File

@ -1,5 +1,6 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics; using System.Diagnostics;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims; using System.Security.Claims;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
@ -11,8 +12,6 @@ namespace Artemis.WebClient.Workshop.Services;
public interface IAuthenticationService : IProtectedArtemisService public interface IAuthenticationService : IProtectedArtemisService
{ {
public const string AUTHORITY = "https://localhost:5001";
bool IsLoggedIn { get; } bool IsLoggedIn { get; }
string? UserCode { get; } string? UserCode { get; }
ReadOnlyObservableCollection<Claim> Claims { get; } ReadOnlyObservableCollection<Claim> Claims { get; }
@ -25,7 +24,7 @@ public interface IAuthenticationService : IProtectedArtemisService
internal class AuthenticationService : CorePropertyChanged, IAuthenticationService internal class AuthenticationService : CorePropertyChanged, IAuthenticationService
{ {
internal const string CLIENT_ID = "artemis.desktop"; private const string CLIENT_ID = "artemis.desktop";
private readonly IDiscoveryCache _discoveryCache; private readonly IDiscoveryCache _discoveryCache;
private readonly IAuthenticationRepository _authenticationRepository; private readonly IAuthenticationRepository _authenticationRepository;
@ -109,7 +108,8 @@ internal class AuthenticationService : CorePropertyChanged, IAuthenticationServi
DeviceAuthorizationResponse response = await client.RequestDeviceAuthorizationAsync(new DeviceAuthorizationRequest DeviceAuthorizationResponse response = await client.RequestDeviceAuthorizationAsync(new DeviceAuthorizationRequest
{ {
Address = disco.DeviceAuthorizationEndpoint, Address = disco.DeviceAuthorizationEndpoint,
ClientId = CLIENT_ID ClientId = CLIENT_ID,
Scope = "openid profile email offline_access api"
}); });
if (response.IsError) if (response.IsError)
throw new ArtemisWebClientException("Failed to request device authorization: " + response.Error); throw new ArtemisWebClientException("Failed to request device authorization: " + response.Error);
@ -168,33 +168,25 @@ internal class AuthenticationService : CorePropertyChanged, IAuthenticationServi
throw new ArtemisWebClientException("Failed to request device token: " + response.Error); throw new ArtemisWebClientException("Failed to request device token: " + response.Error);
} }
await SetCurrentUser(client, response); SetCurrentUser(response);
return true; return true;
} }
private async Task SetCurrentUser(HttpClient client, TokenResponse response) private void SetCurrentUser(TokenResponse response)
{ {
_token = new AuthenticationToken(response); _token = new AuthenticationToken(response);
_authenticationRepository.SetRefreshToken(_token.RefreshToken); _authenticationRepository.SetRefreshToken(_token.RefreshToken);
await GetUserInfo(client, _token.AccessToken); JwtSecurityTokenHandler handler = new();
IsLoggedIn = true; JwtSecurityToken? token = handler.ReadJwtToken(response.IdentityToken);
} if (token == null)
throw new ArtemisWebClientException("Failed to read JWT token");
private async Task GetUserInfo(HttpClient client, string accessToken)
{
DiscoveryDocumentResponse disco = await GetDiscovery();
UserInfoResponse response = await client.GetUserInfoAsync(new UserInfoRequest()
{
Address = disco.UserInfoEndpoint,
Token = accessToken
});
if (response.IsError)
throw new ArtemisWebClientException("Failed to retrieve user info: " + response.Error);
_claims.Clear(); _claims.Clear();
foreach (Claim responseClaim in response.Claims) foreach (Claim responseClaim in token.Claims)
_claims.Add(responseClaim); _claims.Add(responseClaim);
IsLoggedIn = true;
} }
private async Task<bool> UseRefreshToken(string refreshToken) private async Task<bool> UseRefreshToken(string refreshToken)
@ -216,7 +208,7 @@ internal class AuthenticationService : CorePropertyChanged, IAuthenticationServi
throw new ArtemisWebClientException("Failed to request refresh token: " + response.Error); throw new ArtemisWebClientException("Failed to request refresh token: " + response.Error);
} }
await SetCurrentUser(client, response); SetCurrentUser(response);
return false; return false;
} }
} }

View File

@ -0,0 +1,7 @@
namespace Artemis.WebClient.Workshop;
public static class WorkshopConstants
{
public const string AUTHORITY_URL = "https://identity.artemis-rgb.com";
public const string WORKSHOP_URL = "https://workshop.artemis-rgb.com";
}

View File

@ -2,7 +2,7 @@ schema: schema.graphql
extensions: extensions:
endpoints: endpoints:
Default GraphQL Endpoint: Default GraphQL Endpoint:
url: https://localhost:7281/graphql url: https://workshop.artemis-rgb.com/graphql
headers: headers:
user-agent: JS GraphQL user-agent: JS GraphQL
introspect: true introspect: true

View File

@ -32,7 +32,8 @@ type EntriesEdge {
} }
type Entry { type Entry {
author: UUID! author: String!
authorId: UUID!
categories: [Category!]! categories: [Category!]!
createdAt: DateTime! createdAt: DateTime!
description: String! description: String!
@ -83,6 +84,7 @@ type Query {
where: EntryFilterInput where: EntryFilterInput
): EntriesConnection ): EntriesConnection
entry(id: UUID!): Entry entry(id: UUID!): Entry
searchEntries(input: String!, order: [EntrySortInput!], type: EntryType, where: EntryFilterInput): [Entry!]!
} }
type Release { type Release {
@ -90,6 +92,7 @@ type Release {
downloadSize: Long! downloadSize: Long!
downloads: Long! downloads: Long!
entry: Entry! entry: Entry!
entryId: UUID!
id: UUID! id: UUID!
md5Hash: String md5Hash: String
version: String! version: String!
@ -100,6 +103,12 @@ type Tag {
name: String! name: String!
} }
enum ApplyPolicy {
AFTER_RESOLVER
BEFORE_RESOLVER
VALIDATION
}
enum EntryType { enum EntryType {
LAYOUT LAYOUT
PLUGIN PLUGIN
@ -150,7 +159,8 @@ input DateTimeOperationFilterInput {
input EntryFilterInput { input EntryFilterInput {
and: [EntryFilterInput!] and: [EntryFilterInput!]
author: UuidOperationFilterInput author: StringOperationFilterInput
authorId: UuidOperationFilterInput
categories: ListFilterInputTypeOfCategoryFilterInput categories: ListFilterInputTypeOfCategoryFilterInput
createdAt: DateTimeOperationFilterInput createdAt: DateTimeOperationFilterInput
description: StringOperationFilterInput description: StringOperationFilterInput
@ -175,6 +185,7 @@ input EntryInput {
input EntrySortInput { input EntrySortInput {
author: SortEnumType author: SortEnumType
authorId: SortEnumType
createdAt: SortEnumType createdAt: SortEnumType
description: SortEnumType description: SortEnumType
downloads: SortEnumType downloads: SortEnumType
@ -268,6 +279,7 @@ input ReleaseFilterInput {
downloadSize: LongOperationFilterInput downloadSize: LongOperationFilterInput
downloads: LongOperationFilterInput downloads: LongOperationFilterInput
entry: EntryFilterInput entry: EntryFilterInput
entryId: UuidOperationFilterInput
id: UuidOperationFilterInput id: UuidOperationFilterInput
md5Hash: StringOperationFilterInput md5Hash: StringOperationFilterInput
or: [ReleaseFilterInput!] or: [ReleaseFilterInput!]