diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index d5b1a86..1e2b3b3 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -2,16 +2,16 @@ "version": 1, "isRoot": true, "tools": { - "nvika": { - "version": "3.0.0", + "jetbrains.resharper.globaltools": { + "version": "2023.3.1", "commands": [ - "nvika" + "jb" ] }, - "jetbrains.resharper.globaltools": { - "version": "2022.1.2", + "nvika": { + "version": "3.0.0", "commands": [ - "jb" + "nvika" ] } } diff --git a/.github/workflows/codequality.yaml b/.github/workflows/codequality.yaml index 8e80d6a..5c8441b 100644 --- a/.github/workflows/codequality.yaml +++ b/.github/workflows/codequality.yaml @@ -1,23 +1,26 @@ name: Code Quality + on: - pull_request: { } + pull_request: + branches: master push: - branches: - - master + branches: master + +env: + DOTNET_NOLOGO: true + DOTNET_CLI_TELEMETRY_OPTOUT: false jobs: quality: runs-on: ubuntu-latest + steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Install .NET - uses: actions/setup-dotnet@v1 + - uses: actions/checkout@v4 + - uses: actions/setup-dotnet@v4 with: dotnet-version: | 6.0.x - 7.0.x + 8.0.x - name: Restore Tools run: dotnet tool restore @@ -25,8 +28,8 @@ jobs: - name: Restore Packages run: dotnet restore - - name: Run InspectCode - run: dotnet jb inspectcode ${{github.workspace}}/DragonFruit.Sakura.sln --exclude="**/wwwroot/**.*" --exclude="**/*.razor" --output=${{github.workspace}}/inspectcodereport.xml --cachesDir=${{github.workspace}}/inspectcode --verbosity=WARN --no-build + - name: InspectCode + run: dotnet jb inspectcode DragonFruit.Sakura.sln --exclude="**/wwwroot/**.*" --exclude="**/*.razor" --output=inspectcodereport.xml --cachesDir=inspectcode --verbosity=WARN --no-build - - name: Run NVika - run: dotnet nvika parsereport "${{github.workspace}}/inspectcodereport.xml" + - name: NVika + run: dotnet nvika parsereport inspectcodereport.xml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 0b14d92..0c6de1c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -4,9 +4,6 @@ on: release: types: [ published ] -permissions: - contents: write - env: build-output: sakura-publish @@ -19,7 +16,7 @@ jobs: url: https://dragonfruit.network/ steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 @@ -33,59 +30,33 @@ jobs: ignore_missing: true version: ${{ github.ref_name }} - linux-container: + website-package: runs-on: ubuntu-latest - - needs: - - sentry-release - - environment: - name: production - url: https://dragonfruit.network/ + + permissions: + packages: write steps: - - uses: actions/checkout@v3 - - uses: actions/setup-dotnet@v3 - with: - dotnet-version: '7.0.x' - - - uses: docker/setup-qemu-action@v3 - - uses: docker/setup-buildx-action@v3 - - uses: docker/login-action@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-dotnet@v4 with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Create build output folder - run: mkdir ${{ env.build-output }} - - - name: Restore NuGet Packages - run: dotnet restore - - - name: Build Project - run: dotnet publish -c Release -o ${{ env.build-output }} -p:Version=${{ github.ref_name }} DragonFruit.Sakura.Host/DragonFruit.Sakura.Host.csproj - - - id: meta - name: Create metadata - uses: docker/metadata-action@v4 - with: - images: dragonfruitdotnet/website - tags: | - type=raw,value=latest - type=ref,event=tag - - - name: Dockerise - uses: docker/build-push-action@v3 - with: - push: true - context: ${{ env.build-output }} - platforms: linux/arm64,linux/amd64 - tags: ${{ steps.meta.outputs.tags }} - file: DragonFruit.Sakura.Host/Dockerfile + dotnet-version: '8.0.x' + + - name: Build Program + run: dotnet publish -c Release -p:Version=${{ github.event.release.tag_name }} DragonFruit.Sakura + + - name: Package Output + run: dotnet pack -c Release -p:Version=${{ github.event.inputs.version }} -o ./packages --no-build DragonFruit.Sakura + + - name: Upload Package + run: dotnet nuget push -s https://nuget.pkg.github.com/dragonfruitnetwork/index.json -k ${{ github.token }} ./packages/*.nupkg windows-iis: runs-on: windows-latest + permissions: + contents: write + needs: - sentry-release @@ -94,14 +65,18 @@ jobs: url: https://dragonfruit.network/ steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + - name: Build Program - run: dotnet publish -c Release -p:Version=${{ github.ref_name }} -p:PublishProfile=Web.pubxml DragonFruit.Sakura.Host + run: dotnet publish -c Release -p:Version=${{ github.event.release.tag_name }} -p:PublishProfile=Web.pubxml DragonFruit.Sakura.Host - name: Archive Output - run: Compress-Archive -Path .\DragonFruit.Sakura.Host\bin\publish\* -DestinationPath Sakura-${{ github.ref_name }}.zip + run: Compress-Archive -Path .\DragonFruit.Sakura.Host\bin\publish\* -DestinationPath Sakura-${{ github.event.release.tag_name }}.zip - name: Upload Deploy Package uses: softprops/action-gh-release@v1 with: - files: Sakura-${{ github.ref_name }}.zip \ No newline at end of file + files: Sakura-${{ github.event.release.tag_name }}.zip \ No newline at end of file diff --git a/DragonFruit.Sakura.Host/Dockerfile b/DragonFruit.Sakura.Host/Dockerfile deleted file mode 100644 index bbb7c93..0000000 --- a/DragonFruit.Sakura.Host/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -FROM mcr.microsoft.com/dotnet/aspnet:7.0-alpine -WORKDIR /app - -ADD . . -EXPOSE 80 -ENV ASPNETCORE_URLS http://+:80 - -ENTRYPOINT ["dotnet", "DragonFruit.Sakura.Host.dll"] \ No newline at end of file diff --git a/DragonFruit.Sakura.Host/DragonFruit.Sakura.Host.csproj b/DragonFruit.Sakura.Host/DragonFruit.Sakura.Host.csproj index 705094f..8ea374d 100644 --- a/DragonFruit.Sakura.Host/DragonFruit.Sakura.Host.csproj +++ b/DragonFruit.Sakura.Host/DragonFruit.Sakura.Host.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 Linux InProcess @@ -11,9 +11,9 @@ - - - + + + diff --git a/DragonFruit.Sakura.Host/Host.cshtml b/DragonFruit.Sakura.Host/Host.cshtml index c519d26..5ce7c0e 100644 --- a/DragonFruit.Sakura.Host/Host.cshtml +++ b/DragonFruit.Sakura.Host/Host.cshtml @@ -31,9 +31,9 @@ - - - + + + diff --git a/DragonFruit.Sakura.Host/Program.cs b/DragonFruit.Sakura.Host/Program.cs index 701c162..89d9d82 100644 --- a/DragonFruit.Sakura.Host/Program.cs +++ b/DragonFruit.Sakura.Host/Program.cs @@ -8,15 +8,13 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using MudBlazor.Services; -using Serilog; -using Serilog.Events; -using Serilog.Sinks.SystemConsole.Themes; +using Sentry; namespace DragonFruit.Sakura.Host { public static class Program { - public static async Task Main(string[] args) + public static Task Main(string[] args) { var builder = WebApplication.CreateBuilder(args); @@ -27,21 +25,24 @@ public static async Task Main(string[] args) // enable _content files builder.WebHost.UseStaticWebAssets(); - builder.Logging.ClearProviders(); - builder.Logging.AddSerilog(new LoggerConfiguration() - .MinimumLevel.Debug() - .WriteTo.Console(theme: AnsiConsoleTheme.Literate) - .WriteTo.Sentry(o => - { - o.Dsn = builder.Configuration["Sentry:Dsn"]; + builder.Logging.ClearProviders() + .AddSimpleConsole(o => + { + o.SingleLine = true; + o.IncludeScopes = false; + o.TimestampFormat = "[dd/MM/yyyy hh:mm:ss] "; + }) + .AddSentry(o => + { + o.Dsn = builder.Configuration["Sentry:Dsn"]; + o.Release = Assembly.GetExecutingAssembly().GetName().Version!.ToString(3); - o.MaxBreadcrumbs = 50; - o.MinimumEventLevel = LogEventLevel.Error; - o.MinimumBreadcrumbLevel = LogEventLevel.Debug; + o.MaxBreadcrumbs = 50; + o.MinimumEventLevel = LogLevel.Error; + o.MinimumBreadcrumbLevel = LogLevel.Debug; - var version = Assembly.GetExecutingAssembly().GetName().Version; - o.Release = version?.ToString(version.Build > 0 ? 3 : 2); - }).CreateLogger(), true); + o.DisableUnobservedTaskExceptionCapture(); + }); builder.Services.AddApiAuthorization(); builder.Services.AddHttpContextAccessor(); @@ -62,7 +63,7 @@ public static async Task Main(string[] args) app.MapFallbackToController("Host", "Blazor"); app.MapFallbackToController("/changelogs/{app}/{version}", "Host", "Blazor"); - await app.RunAsync().ConfigureAwait(false); + return app.RunAsync(); } } } diff --git a/DragonFruit.Sakura.sln.DotSettings b/DragonFruit.Sakura.sln.DotSettings index 3d7ffed..dec38fa 100644 --- a/DragonFruit.Sakura.sln.DotSettings +++ b/DragonFruit.Sakura.sln.DotSettings @@ -364,6 +364,7 @@ Licensed under GNU AGPLv3. Refer to the LICENSE file for more info MD5 MMR False + True True True True diff --git a/DragonFruit.Sakura/Administration/Index.razor b/DragonFruit.Sakura/Administration/Index.razor index f190e25..3616078 100644 --- a/DragonFruit.Sakura/Administration/Index.razor +++ b/DragonFruit.Sakura/Administration/Index.razor @@ -2,6 +2,7 @@ @using DragonFruit.Sakura.Network @layout AdminLayout +Admin · DragonFruit Network diff --git a/DragonFruit.Sakura/DragonFruit.Sakura.csproj b/DragonFruit.Sakura/DragonFruit.Sakura.csproj index 0162fa7..3450408 100644 --- a/DragonFruit.Sakura/DragonFruit.Sakura.csproj +++ b/DragonFruit.Sakura/DragonFruit.Sakura.csproj @@ -1,11 +1,18 @@ - net7.0 + net8.0 enable true + + DragonFruit Sakura + DragonFruit Network + Copyright (c) DragonFruit Network + The face of DragonFruit Network + + icon.png DragonFruit.Sakura @@ -15,16 +22,13 @@ - - - - - - - - - - + + + + + + + diff --git a/DragonFruit.Sakura/Network/Requests/AdminApiAppsListingRequest.cs b/DragonFruit.Sakura/Network/Requests/AdminApiAppsListingRequest.cs index a3cc7b4..3a02b7e 100644 --- a/DragonFruit.Sakura/Network/Requests/AdminApiAppsListingRequest.cs +++ b/DragonFruit.Sakura/Network/Requests/AdminApiAppsListingRequest.cs @@ -3,9 +3,10 @@ namespace DragonFruit.Sakura.Network.Requests { - public class AdminApiAppsListingRequest : YunaApiRequest + public partial class AdminApiAppsListingRequest : YunaApiRequest { protected override string Stub => "/apps"; - protected override bool RequireAuth => true; + + protected internal override bool RequiresAuthentication => true; } } diff --git a/DragonFruit.Sakura/Network/Requests/AdminApiChangelogModificationCreationRequest.cs b/DragonFruit.Sakura/Network/Requests/AdminApiChangelogModificationCreationRequest.cs index 38a0047..dd1ec34 100644 --- a/DragonFruit.Sakura/Network/Requests/AdminApiChangelogModificationCreationRequest.cs +++ b/DragonFruit.Sakura/Network/Requests/AdminApiChangelogModificationCreationRequest.cs @@ -1,30 +1,21 @@ // DragonFruit Sakura Copyright (c) DragonFruit Network // Licensed under GNU AGPLv3. Refer to the LICENSE file for more info -using DragonFruit.Data; -using DragonFruit.Data.Parameters; +using DragonFruit.Data.Requests; namespace DragonFruit.Sakura.Network.Requests { - public class AdminApiChangelogModificationCreationRequest : YunaApiRequest + public partial class AdminApiChangelogModificationCreationRequest(string appId, string version, ApiChangelogModification modification) : YunaApiRequest { + public override HttpMethod RequestMethod => HttpMethod.Post; protected override string Stub => $"/{AppId}/changelogs/{Version}/modifications"; - protected override bool RequireAuth => true; - protected override Methods Method => Methods.Post; - protected override BodyType BodyType => BodyType.SerializedProperty; + protected internal override bool RequiresAuthentication => true; - public AdminApiChangelogModificationCreationRequest(string appId, string version, ApiChangelogModification modification) - { - AppId = appId; - Version = version; - Modification = modification; - } - - public string AppId { get; } - public string Version { get; } + public string AppId { get; } = appId; + public string Version { get; } = version; [RequestBody] - public ApiChangelogModification Modification { get; } + public ApiChangelogModification Modification { get; } = modification; } } diff --git a/DragonFruit.Sakura/Network/Requests/AdminApiChangelogModificationDeletionRequest.cs b/DragonFruit.Sakura/Network/Requests/AdminApiChangelogModificationDeletionRequest.cs index 96568dd..9a586b1 100644 --- a/DragonFruit.Sakura/Network/Requests/AdminApiChangelogModificationDeletionRequest.cs +++ b/DragonFruit.Sakura/Network/Requests/AdminApiChangelogModificationDeletionRequest.cs @@ -1,26 +1,17 @@ // DragonFruit Sakura Copyright (c) DragonFruit Network // Licensed under GNU AGPLv3. Refer to the LICENSE file for more info -using DragonFruit.Data; - namespace DragonFruit.Sakura.Network.Requests { - public class AdminApiChangelogModificationDeletionRequest : YunaApiRequest + public partial class AdminApiChangelogModificationDeletionRequest(string appId, string version, int modificationId) : YunaApiRequest { + public override HttpMethod RequestMethod => HttpMethod.Delete; protected override string Stub => $"/{AppId}/changelogs/{Version}/modifications/{ModificationId}"; - protected override bool RequireAuth => true; - - protected override Methods Method => Methods.Delete; - public AdminApiChangelogModificationDeletionRequest(string appId, string version, int modificationId) - { - AppId = appId; - Version = version; - ModificationId = modificationId; - } + protected internal override bool RequiresAuthentication => true; - public string AppId { get; } - public string Version { get; } - public int ModificationId { get; } + public string AppId { get; } = appId; + public string Version { get; } = version; + public int ModificationId { get; } = modificationId; } } diff --git a/DragonFruit.Sakura/Network/Requests/AdminApiChangelogModificationEditRequest.cs b/DragonFruit.Sakura/Network/Requests/AdminApiChangelogModificationEditRequest.cs index 4567a06..26d85df 100644 --- a/DragonFruit.Sakura/Network/Requests/AdminApiChangelogModificationEditRequest.cs +++ b/DragonFruit.Sakura/Network/Requests/AdminApiChangelogModificationEditRequest.cs @@ -1,18 +1,16 @@ // DragonFruit Sakura Copyright (c) DragonFruit Network // Licensed under GNU AGPLv3. Refer to the LICENSE file for more info -using DragonFruit.Data; -using DragonFruit.Data.Parameters; +using DragonFruit.Data.Requests; namespace DragonFruit.Sakura.Network.Requests { - public class AdminApiChangelogModificationEditRequest : YunaApiRequest + public partial class AdminApiChangelogModificationEditRequest : YunaApiRequest { + public override HttpMethod RequestMethod => HttpMethod.Patch; protected override string Stub => $"/{AppId}/changelogs/{Version}/modifications/{Modification.Id}"; - protected override bool RequireAuth => true; - protected override Methods Method => Methods.Patch; - protected override BodyType BodyType => BodyType.SerializedProperty; + protected internal override bool RequiresAuthentication => true; public AdminApiChangelogModificationEditRequest(string appId, string version, ApiChangelogModification modification) { diff --git a/DragonFruit.Sakura/Network/Requests/AdminApiChangelogVersionListingRequest.cs b/DragonFruit.Sakura/Network/Requests/AdminApiChangelogVersionListingRequest.cs index a64fc1c..2a120f8 100644 --- a/DragonFruit.Sakura/Network/Requests/AdminApiChangelogVersionListingRequest.cs +++ b/DragonFruit.Sakura/Network/Requests/AdminApiChangelogVersionListingRequest.cs @@ -3,10 +3,9 @@ namespace DragonFruit.Sakura.Network.Requests { - public class AdminApiChangelogVersionListingRequest : YunaApiRequest + public partial class AdminApiChangelogVersionListingRequest : YunaApiRequest { protected override string Stub => $"/{AppId}/changelogs/list"; - protected override bool RequireAuth => true; public AdminApiChangelogVersionListingRequest(string appId) { diff --git a/DragonFruit.Sakura/Network/Requests/AdminApiChangelogsCreationRequest.cs b/DragonFruit.Sakura/Network/Requests/AdminApiChangelogsCreationRequest.cs index 839cfe3..031d1d8 100644 --- a/DragonFruit.Sakura/Network/Requests/AdminApiChangelogsCreationRequest.cs +++ b/DragonFruit.Sakura/Network/Requests/AdminApiChangelogsCreationRequest.cs @@ -1,28 +1,20 @@ // DragonFruit Sakura Copyright (c) DragonFruit Network // Licensed under GNU AGPLv3. Refer to the LICENSE file for more info -using DragonFruit.Data; -using DragonFruit.Data.Parameters; +using DragonFruit.Data.Requests; namespace DragonFruit.Sakura.Network.Requests { - public class AdminApiChangelogsCreationRequest : YunaApiRequest + public partial class AdminApiChangelogsCreationRequest(string appId, ApiChangelogRelease release) : YunaApiRequest { + public override HttpMethod RequestMethod => HttpMethod.Post; protected override string Stub => $"/{AppId}/changelogs"; - protected override bool RequireAuth => true; - protected override Methods Method => Methods.Post; - protected override BodyType BodyType => BodyType.SerializedProperty; + protected internal override bool RequiresAuthentication => true; - public AdminApiChangelogsCreationRequest(string appId, ApiChangelogRelease release) - { - AppId = appId; - Release = release; - } - - public string AppId { get; } + public string AppId { get; } = appId; [RequestBody] - public ApiChangelogRelease Release { get; } + public ApiChangelogRelease Release { get; } = release; } } diff --git a/DragonFruit.Sakura/Network/Requests/AdminApiChangelogsDeletionRequest.cs b/DragonFruit.Sakura/Network/Requests/AdminApiChangelogsDeletionRequest.cs index 8e417d8..6b22a64 100644 --- a/DragonFruit.Sakura/Network/Requests/AdminApiChangelogsDeletionRequest.cs +++ b/DragonFruit.Sakura/Network/Requests/AdminApiChangelogsDeletionRequest.cs @@ -1,24 +1,16 @@ // DragonFruit Sakura Copyright (c) DragonFruit Network // Licensed under GNU AGPLv3. Refer to the LICENSE file for more info -using DragonFruit.Data; - namespace DragonFruit.Sakura.Network.Requests { - public class AdminApiChangelogsDeletionRequest : YunaApiRequest + public partial class AdminApiChangelogsDeletionRequest(string appId, string versionName) : YunaApiRequest { + public override HttpMethod RequestMethod => HttpMethod.Delete; protected override string Stub => $"/{AppId}/changelogs/{VersionName}"; - protected override bool RequireAuth => true; - - protected override Methods Method => Methods.Delete; - public AdminApiChangelogsDeletionRequest(string appId, string versionName) - { - AppId = appId; - VersionName = versionName; - } + protected internal override bool RequiresAuthentication => true; - public string AppId { get; } - public string VersionName { get; } + public string AppId { get; } = appId; + public string VersionName { get; } = versionName; } } diff --git a/DragonFruit.Sakura/Network/Requests/AdminApiDistributionActiveReleaseRequest.cs b/DragonFruit.Sakura/Network/Requests/AdminApiDistributionActiveReleaseRequest.cs index 23c1a97..46db028 100644 --- a/DragonFruit.Sakura/Network/Requests/AdminApiDistributionActiveReleaseRequest.cs +++ b/DragonFruit.Sakura/Network/Requests/AdminApiDistributionActiveReleaseRequest.cs @@ -1,17 +1,16 @@ // DragonFruit Sakura Copyright (c) DragonFruit Network // Licensed under GNU AGPLv3. Refer to the LICENSE file for more info -using DragonFruit.Data; -using DragonFruit.Data.Parameters; +using DragonFruit.Data.Requests; namespace DragonFruit.Sakura.Network.Requests { - public class AdminApiDistributionActiveReleaseRequest : YunaApiRequest + public partial class AdminApiDistributionActiveReleaseRequest : YunaApiRequest { + public override HttpMethod RequestMethod => HttpMethod.Put; protected override string Stub => $"/{AppId}/dist/{BranchName}/releases/active"; - protected override bool RequireAuth => true; - protected override Methods Method => Methods.Put; + protected internal override bool RequiresAuthentication => true; public AdminApiDistributionActiveReleaseRequest(string appId, string branchName, int activeReleaseId) { @@ -23,10 +22,10 @@ public AdminApiDistributionActiveReleaseRequest(string appId, string branchName, public string AppId { get; } public string BranchName { get; } - [FormParameter("release_id")] + [RequestParameter(ParameterType.Form, "release_id")] public int ActiveReleaseId { get; } - [FormParameter("force")] + [RequestParameter(ParameterType.Form, "force")] protected bool Force => true; } } diff --git a/DragonFruit.Sakura/Network/Requests/AdminApiDistributionBranchCreationRequest.cs b/DragonFruit.Sakura/Network/Requests/AdminApiDistributionBranchCreationRequest.cs index 7f29697..608eab0 100644 --- a/DragonFruit.Sakura/Network/Requests/AdminApiDistributionBranchCreationRequest.cs +++ b/DragonFruit.Sakura/Network/Requests/AdminApiDistributionBranchCreationRequest.cs @@ -1,27 +1,20 @@ // DragonFruit Sakura Copyright (c) DragonFruit Network // Licensed under GNU AGPLv3. Refer to the LICENSE file for more info -using DragonFruit.Data; -using DragonFruit.Data.Parameters; +using DragonFruit.Data.Requests; namespace DragonFruit.Sakura.Network.Requests { - public class AdminApiDistributionBranchCreationRequest : YunaApiRequest + public partial class AdminApiDistributionBranchCreationRequest(string appId, string branchName) : YunaApiRequest { + public override HttpMethod RequestMethod => HttpMethod.Post; protected override string Stub => $"/{AppId}/dist/branches"; - protected override bool RequireAuth => true; - protected override Methods Method => Methods.Post; + protected internal override bool RequiresAuthentication => true; - public AdminApiDistributionBranchCreationRequest(string appId, string branchName) - { - AppId = appId; - BranchName = branchName; - } + public string AppId { get; } = appId; - public string AppId { get; } - - [FormParameter("name")] - public string BranchName { get; } + [RequestParameter(ParameterType.Form, "name")] + public string BranchName { get; } = branchName; } } diff --git a/DragonFruit.Sakura/Network/Requests/AdminApiDistributionBranchDeletionRequest.cs b/DragonFruit.Sakura/Network/Requests/AdminApiDistributionBranchDeletionRequest.cs index 142e2fb..7b16b28 100644 --- a/DragonFruit.Sakura/Network/Requests/AdminApiDistributionBranchDeletionRequest.cs +++ b/DragonFruit.Sakura/Network/Requests/AdminApiDistributionBranchDeletionRequest.cs @@ -1,16 +1,14 @@ // DragonFruit Sakura Copyright (c) DragonFruit Network // Licensed under GNU AGPLv3. Refer to the LICENSE file for more info -using DragonFruit.Data; - namespace DragonFruit.Sakura.Network.Requests { - public class AdminApiDistributionBranchDeletionRequest : YunaApiRequest + public partial class AdminApiDistributionBranchDeletionRequest : YunaApiRequest { + public override HttpMethod RequestMethod => HttpMethod.Delete; protected override string Stub => $"/{AppId}/dist/branches/{BranchName}"; - protected override bool RequireAuth => true; - protected override Methods Method => Methods.Delete; + protected internal override bool RequiresAuthentication => true; public AdminApiDistributionBranchDeletionRequest(string appId, string branchName) { diff --git a/DragonFruit.Sakura/Network/Requests/AdminApiDistributionBranchRequest.cs b/DragonFruit.Sakura/Network/Requests/AdminApiDistributionBranchRequest.cs index 066cf82..323b07f 100644 --- a/DragonFruit.Sakura/Network/Requests/AdminApiDistributionBranchRequest.cs +++ b/DragonFruit.Sakura/Network/Requests/AdminApiDistributionBranchRequest.cs @@ -3,10 +3,11 @@ namespace DragonFruit.Sakura.Network.Requests { - public class AdminApiDistributionBranchRequest : YunaApiRequest + public partial class AdminApiDistributionBranchRequest : YunaApiRequest { protected override string Stub => $"/{AppId}/dist/{BranchName}"; - protected override bool RequireAuth => true; + + protected internal override bool RequiresAuthentication => true; public AdminApiDistributionBranchRequest(string appId, string branchName) { diff --git a/DragonFruit.Sakura/Network/Requests/AdminApiDistributionBranchesRequest.cs b/DragonFruit.Sakura/Network/Requests/AdminApiDistributionBranchesRequest.cs index 30d934a..3fbb2fc 100644 --- a/DragonFruit.Sakura/Network/Requests/AdminApiDistributionBranchesRequest.cs +++ b/DragonFruit.Sakura/Network/Requests/AdminApiDistributionBranchesRequest.cs @@ -3,10 +3,11 @@ namespace DragonFruit.Sakura.Network.Requests { - public class AdminApiDistributionBranchesRequest : YunaApiRequest + public partial class AdminApiDistributionBranchesRequest : YunaApiRequest { protected override string Stub => $"/{AppId}/dist/branches"; - protected override bool RequireAuth => true; + + protected internal override bool RequiresAuthentication => true; public AdminApiDistributionBranchesRequest(string appId) { diff --git a/DragonFruit.Sakura/Network/Requests/AdminApiDistributionReleaseDeletionRequest.cs b/DragonFruit.Sakura/Network/Requests/AdminApiDistributionReleaseDeletionRequest.cs index 0667e43..4f436e9 100644 --- a/DragonFruit.Sakura/Network/Requests/AdminApiDistributionReleaseDeletionRequest.cs +++ b/DragonFruit.Sakura/Network/Requests/AdminApiDistributionReleaseDeletionRequest.cs @@ -1,16 +1,14 @@ // DragonFruit Sakura Copyright (c) DragonFruit Network // Licensed under GNU AGPLv3. Refer to the LICENSE file for more info -using DragonFruit.Data; - namespace DragonFruit.Sakura.Network.Requests { - public class AdminApiDistributionReleaseDeletionRequest : YunaApiRequest + public partial class AdminApiDistributionReleaseDeletionRequest : YunaApiRequest { + public override HttpMethod RequestMethod => HttpMethod.Delete; protected override string Stub => $"/{AppId}/dist/{BranchName}/releases/{ReleaseId}"; - protected override bool RequireAuth => true; - protected override Methods Method => Methods.Delete; + protected internal override bool RequiresAuthentication => true; public AdminApiDistributionReleaseDeletionRequest(string appId, string branchName, int releaseId) { diff --git a/DragonFruit.Sakura/Network/Requests/AdminApiKeychainAdditionRequest.cs b/DragonFruit.Sakura/Network/Requests/AdminApiKeychainAdditionRequest.cs index 6ea5d95..8d17efc 100644 --- a/DragonFruit.Sakura/Network/Requests/AdminApiKeychainAdditionRequest.cs +++ b/DragonFruit.Sakura/Network/Requests/AdminApiKeychainAdditionRequest.cs @@ -1,17 +1,17 @@ // DragonFruit Sakura Copyright (c) DragonFruit Network // Licensed under GNU AGPLv3. Refer to the LICENSE file for more info -using DragonFruit.Data; +using DragonFruit.Data.Requests; namespace DragonFruit.Sakura.Network.Requests { - public class AdminApiKeychainAdditionRequest : YunaApiRequest + [FormBodyType(FormBodyType.Multipart)] + public partial class AdminApiKeychainAdditionRequest : YunaApiRequest { + public override HttpMethod RequestMethod => HttpMethod.Post; protected override string Stub => "/users/me/keychain"; - protected override bool RequireAuth => true; - protected override Methods Method => Methods.Post; - protected override BodyType BodyType => BodyType.Custom; + protected internal override bool RequiresAuthentication => true; public AdminApiKeychainAdditionRequest(Stream pemFile, DateTime? expiry) { @@ -19,23 +19,10 @@ public AdminApiKeychainAdditionRequest(Stream pemFile, DateTime? expiry) Expiry = expiry; } + [RequestParameter(ParameterType.Form, "pem")] public Stream PublicKey { get; } - public DateTime? Expiry { get; } - - protected override HttpContent BodyContent - { - get - { - var multipartBody = new MultipartFormDataContent(); - multipartBody.Add(new StreamContent(PublicKey), "pem", "file.pem"); - if (Expiry.HasValue) - { - multipartBody.Add(new StringContent(Expiry.Value.ToString("O")), "expiry"); - } - - return multipartBody; - } - } + [RequestParameter(ParameterType.Form, "expiry")] + public DateTime? Expiry { get; } } } diff --git a/DragonFruit.Sakura/Network/Requests/AdminApiKeychainDeletionRequest.cs b/DragonFruit.Sakura/Network/Requests/AdminApiKeychainDeletionRequest.cs index 6793965..baee85f 100644 --- a/DragonFruit.Sakura/Network/Requests/AdminApiKeychainDeletionRequest.cs +++ b/DragonFruit.Sakura/Network/Requests/AdminApiKeychainDeletionRequest.cs @@ -1,16 +1,14 @@ // DragonFruit Sakura Copyright (c) DragonFruit Network // Licensed under GNU AGPLv3. Refer to the LICENSE file for more info -using DragonFruit.Data; - namespace DragonFruit.Sakura.Network.Requests { - public class AdminApiKeychainDeletionRequest : YunaApiRequest + public partial class AdminApiKeychainDeletionRequest : YunaApiRequest { + public override HttpMethod RequestMethod => HttpMethod.Delete; protected override string Stub => $"/users/me/keychain/{KeyId}"; - protected override bool RequireAuth => true; - protected override Methods Method => Methods.Delete; + protected internal override bool RequiresAuthentication => true; public AdminApiKeychainDeletionRequest(string keyId) { diff --git a/DragonFruit.Sakura/Network/Requests/AdminApiKeychainListingRequest.cs b/DragonFruit.Sakura/Network/Requests/AdminApiKeychainListingRequest.cs index b2b8468..288c843 100644 --- a/DragonFruit.Sakura/Network/Requests/AdminApiKeychainListingRequest.cs +++ b/DragonFruit.Sakura/Network/Requests/AdminApiKeychainListingRequest.cs @@ -1,19 +1,19 @@ // DragonFruit Sakura Copyright (c) DragonFruit Network // Licensed under GNU AGPLv3. Refer to the LICENSE file for more info -using DragonFruit.Data.Parameters; +using DragonFruit.Data.Requests; namespace DragonFruit.Sakura.Network.Requests { - public class AdminApiKeychainListingRequest : YunaApiRequest + public partial class AdminApiKeychainListingRequest : YunaApiRequest { protected override string Stub => "/users/me/keychain"; - protected override bool RequireAuth => true; + protected internal override bool RequiresAuthentication => true; - [QueryParameter("offset")] + [RequestParameter(ParameterType.Query, "offset")] public int? Offset { get; set; } - [QueryParameter("limit")] + [RequestParameter(ParameterType.Query, "limit")] public int? Limit { get; set; } } } diff --git a/DragonFruit.Sakura/Network/Requests/ApiChangelogsRequest.cs b/DragonFruit.Sakura/Network/Requests/ApiChangelogsRequest.cs index fb8e578..ced2f87 100644 --- a/DragonFruit.Sakura/Network/Requests/ApiChangelogsRequest.cs +++ b/DragonFruit.Sakura/Network/Requests/ApiChangelogsRequest.cs @@ -3,17 +3,11 @@ namespace DragonFruit.Sakura.Network.Requests { - public class ApiChangelogsRequest : YunaApiRequest + public partial class ApiChangelogsRequest(string appId, string versionName) : YunaApiRequest { protected override string Stub => $"/{AppId}/changelogs/{VersionName}"; - public ApiChangelogsRequest(string appId, string versionName) - { - AppId = appId; - VersionName = versionName ?? "latest"; - } - - public string AppId { get; } - public string VersionName { get; } + public string AppId { get; } = appId; + public string VersionName { get; } = versionName ?? "latest"; } } diff --git a/DragonFruit.Sakura/Network/Requests/ApiDefaultChangelogsRequest.cs b/DragonFruit.Sakura/Network/Requests/ApiDefaultChangelogsRequest.cs index 1016373..0a2b88e 100644 --- a/DragonFruit.Sakura/Network/Requests/ApiDefaultChangelogsRequest.cs +++ b/DragonFruit.Sakura/Network/Requests/ApiDefaultChangelogsRequest.cs @@ -3,7 +3,7 @@ namespace DragonFruit.Sakura.Network.Requests { - public class ApiDefaultChangelogsRequest : YunaApiRequest + public partial class ApiDefaultChangelogsRequest : YunaApiRequest { protected override string Stub => "/changelogs/latest"; } diff --git a/DragonFruit.Sakura/Network/Requests/ApiGeolocationInfoRequest.cs b/DragonFruit.Sakura/Network/Requests/ApiGeolocationInfoRequest.cs index 8579a44..1d52cac 100644 --- a/DragonFruit.Sakura/Network/Requests/ApiGeolocationInfoRequest.cs +++ b/DragonFruit.Sakura/Network/Requests/ApiGeolocationInfoRequest.cs @@ -3,7 +3,7 @@ namespace DragonFruit.Sakura.Network.Requests { - public class ApiGeolocationInfoRequest : YunaApiRequest + public partial class ApiGeolocationInfoRequest : YunaApiRequest { protected override string Stub => "/onionfruit/status"; } diff --git a/DragonFruit.Sakura/Network/Requests/ApiWikiPageRequest.cs b/DragonFruit.Sakura/Network/Requests/ApiWikiPageRequest.cs index fa87bbe..a5de850 100644 --- a/DragonFruit.Sakura/Network/Requests/ApiWikiPageRequest.cs +++ b/DragonFruit.Sakura/Network/Requests/ApiWikiPageRequest.cs @@ -3,15 +3,10 @@ namespace DragonFruit.Sakura.Network.Requests { - public class ApiWikiPageRequest : YunaApiRequest + public partial class ApiWikiPageRequest(string pagePath) : YunaApiRequest { protected override string Stub => $"/wiki/en/{PagePath?.TrimStart('/')}"; - public ApiWikiPageRequest(string pagePath) - { - PagePath = pagePath; - } - - public string PagePath { get; } + public string PagePath { get; } = pagePath; } } diff --git a/DragonFruit.Sakura/Network/Requests/OnionCountryInfoRequest.cs b/DragonFruit.Sakura/Network/Requests/OnionCountryInfoRequest.cs index af66aee..9cdf150 100644 --- a/DragonFruit.Sakura/Network/Requests/OnionCountryInfoRequest.cs +++ b/DragonFruit.Sakura/Network/Requests/OnionCountryInfoRequest.cs @@ -5,8 +5,8 @@ namespace DragonFruit.Sakura.Network.Requests { - public class OnionCountryInfoRequest : ApiRequest + public partial class OnionCountryInfoRequest : ApiRequest { - public override string Path => "https://onioncdn.dragonfruit.network/country-details.json"; + public override string RequestPath => "https://onioncdn.dragonfruit.network/country-details.json"; } } diff --git a/DragonFruit.Sakura/Network/Requests/YunaApiRequest.cs b/DragonFruit.Sakura/Network/Requests/YunaApiRequest.cs index 3211f0c..cb87ea3 100644 --- a/DragonFruit.Sakura/Network/Requests/YunaApiRequest.cs +++ b/DragonFruit.Sakura/Network/Requests/YunaApiRequest.cs @@ -2,23 +2,35 @@ // Licensed under GNU AGPLv3. Refer to the LICENSE file for more info using DragonFruit.Data; +using DragonFruit.Data.Requests; namespace DragonFruit.Sakura.Network.Requests { - public abstract class YunaApiRequest : ApiRequest + public abstract class YunaApiRequest : ApiRequest, IAsyncRequestExecutingCallback { - public override string Path => $"{BaseUrlOverride.TrimEnd('/')}/{Stub.TrimStart('/')}"; + public override string RequestPath => $"https://api.dragonfruit.network/{Stub.TrimStart('/')}"; /// - /// The base url to override. This will be set by the requesting client if not set. + /// The request path. /// - internal string BaseUrlOverride { get; set; } + protected abstract string Stub { get; } /// - /// The request path. + /// Whether the request requires authentication. /// - protected abstract string Stub { get; } + protected internal virtual bool RequiresAuthentication => false; + + [RequestParameter(ParameterType.Header, "Authorization")] + protected internal string AuthenticationToken { get; set; } + + public ValueTask OnRequestExecuting(ApiClient client) + { + if (client is SakuraWasmClient wasmClient) + { + return wasmClient.EnsureAuthenticatedAsync(this); + } - internal bool RequiresInteractiveToken => RequireAuth; + return ValueTask.CompletedTask; + } } } diff --git a/DragonFruit.Sakura/Network/SakuraClient.cs b/DragonFruit.Sakura/Network/SakuraClient.cs index 716cdf7..49c6e33 100644 --- a/DragonFruit.Sakura/Network/SakuraClient.cs +++ b/DragonFruit.Sakura/Network/SakuraClient.cs @@ -2,25 +2,14 @@ // Licensed under GNU AGPLv3. Refer to the LICENSE file for more info using DragonFruit.Data; -using DragonFruit.Data.Serializers.SystemJson; -using DragonFruit.Sakura.Network.Requests; +using DragonFruit.Data.Serializers; namespace DragonFruit.Sakura.Network { - public abstract class SakuraClient : ApiClient + public abstract class SakuraClient : ApiClient { public abstract bool IsServerSide { get; } - protected virtual string ApiBaseUrl => "https://api.dragonfruit.network"; - - protected override Task ValidateRequest(ApiRequest request) - { - if (request is YunaApiRequest yunaApiRequest && yunaApiRequest.BaseUrlOverride == null) - { - yunaApiRequest.BaseUrlOverride = ApiBaseUrl; - } - - return base.ValidateRequest(request); - } + protected internal virtual string ApiBaseUrl => "https://api.dragonfruit.network"; } } diff --git a/DragonFruit.Sakura/Network/SakuraWasmClient.cs b/DragonFruit.Sakura/Network/SakuraWasmClient.cs index 2c46687..af658ee 100644 --- a/DragonFruit.Sakura/Network/SakuraWasmClient.cs +++ b/DragonFruit.Sakura/Network/SakuraWasmClient.cs @@ -1,32 +1,26 @@ // DragonFruit Sakura Copyright (c) DragonFruit Network // Licensed under GNU AGPLv3. Refer to the LICENSE file for more info -using DragonFruit.Data; -using DragonFruit.Data.Extensions; using DragonFruit.Sakura.Network.Requests; using Microsoft.AspNetCore.Components.WebAssembly.Authentication; namespace DragonFruit.Sakura.Network { - public class SakuraWasmClient : SakuraClient + public class SakuraWasmClient(IAccessTokenProvider token) : SakuraClient { - private readonly IAccessTokenProvider _token; private AccessToken _lastToken; - public SakuraWasmClient(IAccessTokenProvider token) - { - _token = token; - } - public override bool IsServerSide => false; - protected override async Task ValidateRequest(ApiRequest request) + protected override HttpClient CreateClient() => new(); + + internal async ValueTask EnsureAuthenticatedAsync(YunaApiRequest request) { - if (request is YunaApiRequest { RequiresInteractiveToken: true }) + if (request.RequiresAuthentication) { if (_lastToken == null || _lastToken.Expires.Subtract(DateTimeOffset.Now) < TimeSpan.FromMinutes(5)) { - var newToken = await _token.RequestAccessToken().ConfigureAwait(false); + var newToken = await token.RequestAccessToken().ConfigureAwait(false); if (!newToken.TryGetToken(out _lastToken)) { @@ -34,10 +28,8 @@ protected override async Task ValidateRequest(ApiRequest request) } } - request.WithAuthHeader($"Bearer {_lastToken.Value}"); + request.AuthenticationToken = $"Bearer {_lastToken.Value}"; } - - await base.ValidateRequest(request).ConfigureAwait(false); } } } diff --git a/DragonFruit.Sakura/Program.cs b/DragonFruit.Sakura/Program.cs index c7219e0..5b7042b 100644 --- a/DragonFruit.Sakura/Program.cs +++ b/DragonFruit.Sakura/Program.cs @@ -7,8 +7,6 @@ using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using MudBlazor; using MudBlazor.Services; -using Serilog; -using Serilog.Events; namespace DragonFruit.Sakura { @@ -25,25 +23,20 @@ public static class Program } }; - public static async Task Main(string[] args) + public static Task Main(string[] args) { var builder = WebAssemblyHostBuilder.CreateDefault(args); - var loggingConfig = new LoggerConfiguration() - .MinimumLevel.Debug() - .Enrich.WithProperty("InstanceId", Guid.NewGuid().ToString("D")) - .WriteTo.BrowserConsole(restrictedToMinimumLevel: LogEventLevel.Information) - .WriteTo.Sentry(o => - { - var version = Assembly.GetExecutingAssembly().GetName().Version; - o.MaxBreadcrumbs = 50; - o.MinimumEventLevel = LogEventLevel.Error; - o.MinimumBreadcrumbLevel = LogEventLevel.Debug; - o.Release = version?.ToString(version.Build > 0 ? 3 : 2); - o.Dsn = "https://d42ddda84a6f4ffb8d9d66cb7d1a6d9b@o97031.ingest.sentry.io/6542291"; - }); + builder.Logging.AddSentry(o => + { + o.MaxBreadcrumbs = 50; + o.MinimumEventLevel = LogLevel.Error; + o.MinimumBreadcrumbLevel = LogLevel.Debug; + + o.Dsn = "https://d42ddda84a6f4ffb8d9d66cb7d1a6d9b@o97031.ingest.sentry.io/6542291"; + o.Release = Assembly.GetExecutingAssembly().GetName().Version!.ToString(3); + }); - builder.Logging.AddSerilog(loggingConfig.CreateLogger(), true); builder.Services.AddOidcAuthentication(o => { o.ProviderOptions.Authority = "https://id.dragonfruit.network/connect/authorize"; @@ -70,7 +63,7 @@ public static async Task Main(string[] args) builder.Services.AddScoped(); builder.Services.AddScoped(); - await builder.Build().RunAsync(); + return builder.Build().RunAsync(); } } } diff --git a/DragonFruit.Sakura/Wiki/Wiki.razor b/DragonFruit.Sakura/Wiki/Wiki.razor index 0b5edf3..93d4316 100644 --- a/DragonFruit.Sakura/Wiki/Wiki.razor +++ b/DragonFruit.Sakura/Wiki/Wiki.razor @@ -19,7 +19,7 @@ else - + @@ -70,13 +70,13 @@ else }
- @PageContent.Content + @PageContent?.Content
@if (PageMetadata != null) { - + @@ -88,7 +88,9 @@ else @foreach (var author in PageMetadata.Authors) { - @author.Username[0] + + @(string.IsNullOrEmpty(author.AvatarUrl) ? char.ToUpperInvariant(author.Username[0]) : null) + } diff --git a/DragonFruit.Sakura/Wiki/Wiki.razor.cs b/DragonFruit.Sakura/Wiki/Wiki.razor.cs index 9703108..1e0311b 100644 --- a/DragonFruit.Sakura/Wiki/Wiki.razor.cs +++ b/DragonFruit.Sakura/Wiki/Wiki.razor.cs @@ -94,12 +94,12 @@ private List GenerateBreadcrumbs() if (pathSegments is null || !pathSegments.Any()) { - return new List { homeSegment }; + return [homeSegment]; } // split the url into a set of segments representing the location of each page (i.e. the topmost page has to include all the parents) var segmentArrays = Enumerable.Range(1, pathSegments.Length).Select(i => new ArraySegment(pathSegments, 0, i)); - return segmentArrays.Select(x => new BreadcrumbItem(x.Last(), $"/wiki/{string.Join("/", x)}")).Prepend(homeSegment).ToList(); + return segmentArrays.Select(x => new BreadcrumbItem(x.Last(), $"/{string.Join("/", x)}")).ToList(); } } } diff --git a/DragonFruit.Sakura/wwwroot/styles/global.css b/DragonFruit.Sakura/wwwroot/styles/global.css index 4e08357..dfce80c 100644 --- a/DragonFruit.Sakura/wwwroot/styles/global.css +++ b/DragonFruit.Sakura/wwwroot/styles/global.css @@ -11,6 +11,8 @@ iframe { .sakura-hero, .sakura-full-height-section { min-height: 100vh; + background-size: cover; + background-repeat: no-repeat; } .sakura-full-height-section { diff --git a/DragonFruit.Sakura/wwwroot/styles/overrides.css b/DragonFruit.Sakura/wwwroot/styles/overrides.css index a8bf744..faa5422 100644 --- a/DragonFruit.Sakura/wwwroot/styles/overrides.css +++ b/DragonFruit.Sakura/wwwroot/styles/overrides.css @@ -1,3 +1,7 @@ .mud-button { text-transform: none !important; +} + +.mud-avatar { + align-items: unset !important; } \ No newline at end of file