diff --git a/Foxnouns.Backend/Controllers/Authentication/DiscordAuthController.cs b/Foxnouns.Backend/Controllers/Authentication/DiscordAuthController.cs index 118caa8..ee22804 100644 --- a/Foxnouns.Backend/Controllers/Authentication/DiscordAuthController.cs +++ b/Foxnouns.Backend/Controllers/Authentication/DiscordAuthController.cs @@ -104,9 +104,21 @@ public class DiscordAuthController( { CheckRequirements(); - var state = await remoteAuthService.ValidateAddAccountRequestAsync( - CurrentUser!.Id, - AuthType.Discord + var existingAccounts = await db + .AuthMethods.Where(m => m.UserId == CurrentUser!.Id && m.AuthType == AuthType.Discord) + .CountAsync(); + if (existingAccounts > AuthUtils.MaxAuthMethodsPerType) + { + throw new ApiError.BadRequest( + "Too many linked Discord accounts, maximum of 3 per account." + ); + } + + var state = HttpUtility.UrlEncode( + await keyCacheService.GenerateAddExtraAccountStateAsync( + AuthType.Discord, + CurrentUser!.Id + ) ); var url = @@ -126,11 +138,12 @@ public class DiscordAuthController( { CheckRequirements(); - await remoteAuthService.ValidateAddAccountStateAsync( - req.State, - CurrentUser!.Id, - AuthType.Discord - ); + var accountState = await keyCacheService.GetAddExtraAccountStateAsync(req.State); + if ( + accountState is not { AuthType: AuthType.Discord } + || accountState.UserId != CurrentUser!.Id + ) + throw new ApiError.BadRequest("Invalid state", "state", req.State); var remoteUser = await remoteAuthService.RequestDiscordTokenAsync(req.Code); try diff --git a/Foxnouns.Backend/Controllers/Authentication/FediverseAuthController.cs b/Foxnouns.Backend/Controllers/Authentication/FediverseAuthController.cs index 103061b..7cb52c8 100644 --- a/Foxnouns.Backend/Controllers/Authentication/FediverseAuthController.cs +++ b/Foxnouns.Backend/Controllers/Authentication/FediverseAuthController.cs @@ -1,8 +1,5 @@ -using System.Net; -using EntityFramework.Exceptions.Common; using Foxnouns.Backend.Database; using Foxnouns.Backend.Database.Models; -using Foxnouns.Backend.Middleware; using Foxnouns.Backend.Services; using Foxnouns.Backend.Services.Auth; using Foxnouns.Backend.Utils; @@ -18,14 +15,13 @@ public class FediverseAuthController( DatabaseContext db, FediverseAuthService fediverseAuthService, AuthService authService, - RemoteAuthService remoteAuthService, KeyCacheService keyCacheService ) : ApiControllerBase { private readonly ILogger _logger = logger.ForContext(); [HttpGet] - [ProducesResponseType(statusCode: StatusCodes.Status200OK)] + [ProducesResponseType(statusCode: StatusCodes.Status200OK)] public async Task GetFediverseUrlAsync( [FromQuery] string instance, [FromQuery] bool forceRefresh = false @@ -35,7 +31,7 @@ public class FediverseAuthController( throw new ApiError.BadRequest("Not a valid domain.", "instance", instance); var url = await fediverseAuthService.GenerateAuthUrlAsync(instance, forceRefresh); - return Ok(new AuthController.SingleUrlResponse(url)); + return Ok(new FediverseUrlResponse(url)); } [HttpPost("callback")] @@ -122,75 +118,10 @@ public class FediverseAuthController( return Ok(await authService.GenerateUserTokenAsync(user)); } - [HttpGet("add-account")] - [Authorize("*")] - public async Task AddFediverseAccountAsync( - [FromQuery] string instance, - [FromQuery] bool forceRefresh = false - ) - { - if (instance.Any(c => c is '@' or ':' or '/') || !instance.Contains('.')) - throw new ApiError.BadRequest("Not a valid domain.", "instance", instance); - - var state = await remoteAuthService.ValidateAddAccountRequestAsync( - CurrentUser!.Id, - AuthType.Fediverse, - instance - ); - - var url = await fediverseAuthService.GenerateAuthUrlAsync(instance, forceRefresh, state); - return Ok(new AuthController.SingleUrlResponse(url)); - } - - [HttpPost("add-account/callback")] - [Authorize("*")] - public async Task AddAccountCallbackAsync([FromBody] CallbackRequest req) - { - await remoteAuthService.ValidateAddAccountStateAsync( - req.State, - CurrentUser!.Id, - AuthType.Fediverse, - req.Instance - ); - - var app = await fediverseAuthService.GetApplicationAsync(req.Instance); - var remoteUser = await fediverseAuthService.GetRemoteFediverseUserAsync(app, req.Code); - try - { - var authMethod = await authService.AddAuthMethodAsync( - CurrentUser.Id, - AuthType.Fediverse, - remoteUser.Id, - remoteUser.Username, - app - ); - _logger.Debug( - "Added new Fediverse auth method {AuthMethodId} to user {UserId}", - authMethod.Id, - CurrentUser.Id - ); - - return Ok( - new AuthController.AddOauthAccountResponse( - authMethod.Id, - AuthType.Fediverse, - authMethod.RemoteId, - $"{authMethod.RemoteUsername}@{app.Domain}" - ) - ); - } - catch (UniqueConstraintException) - { - throw new ApiError( - "That account is already linked.", - HttpStatusCode.BadRequest, - ErrorCode.AccountAlreadyLinked - ); - } - } - public record CallbackRequest(string Instance, string Code, string State); + private record FediverseUrlResponse(string Url); + private record FediverseTicketData( Snowflake ApplicationId, FediverseAuthService.FediverseUser User diff --git a/Foxnouns.Backend/Controllers/FlagsController.cs b/Foxnouns.Backend/Controllers/FlagsController.cs index e7a17e9..d414dba 100644 --- a/Foxnouns.Backend/Controllers/FlagsController.cs +++ b/Foxnouns.Backend/Controllers/FlagsController.cs @@ -1,5 +1,6 @@ using Coravel.Queuing.Interfaces; using Foxnouns.Backend.Database; +using Foxnouns.Backend.Database.Models; using Foxnouns.Backend.Extensions; using Foxnouns.Backend.Jobs; using Foxnouns.Backend.Middleware; diff --git a/Foxnouns.Backend/Controllers/UsersController.cs b/Foxnouns.Backend/Controllers/UsersController.cs index b133954..aa8d02d 100644 --- a/Foxnouns.Backend/Controllers/UsersController.cs +++ b/Foxnouns.Backend/Controllers/UsersController.cs @@ -1,4 +1,5 @@ using System.Diagnostics.CodeAnalysis; +using Coravel.Mailer.Mail.Helpers; using Coravel.Queuing.Interfaces; using EntityFramework.Exceptions.Common; using Foxnouns.Backend.Database; diff --git a/Foxnouns.Backend/Database/Models/DataExport.cs b/Foxnouns.Backend/Database/Models/DataExport.cs index 582ffd8..6e5a719 100644 --- a/Foxnouns.Backend/Database/Models/DataExport.cs +++ b/Foxnouns.Backend/Database/Models/DataExport.cs @@ -1,3 +1,4 @@ +using System.ComponentModel.DataAnnotations.Schema; using NodaTime; namespace Foxnouns.Backend.Database.Models; @@ -8,5 +9,6 @@ public class DataExport : BaseModel public User User { get; init; } = null!; public required string Filename { get; init; } + [NotMapped] public static readonly Duration Expiration = Duration.FromDays(15); } diff --git a/Foxnouns.Backend/Database/Models/FediverseApplication.cs b/Foxnouns.Backend/Database/Models/FediverseApplication.cs index 6dc813d..882a377 100644 --- a/Foxnouns.Backend/Database/Models/FediverseApplication.cs +++ b/Foxnouns.Backend/Database/Models/FediverseApplication.cs @@ -1,3 +1,5 @@ +using NodaTime; + namespace Foxnouns.Backend.Database.Models; public class FediverseApplication : BaseModel diff --git a/Foxnouns.Backend/Database/Models/User.cs b/Foxnouns.Backend/Database/Models/User.cs index f75acde..7eda12d 100644 --- a/Foxnouns.Backend/Database/Models/User.cs +++ b/Foxnouns.Backend/Database/Models/User.cs @@ -55,7 +55,10 @@ public class User : BaseModel public PreferenceSize Size { get; set; } } + [NotMapped] public static readonly Duration DeleteAfter = Duration.FromDays(30); + + [NotMapped] public static readonly Duration DeleteSuspendedAfter = Duration.FromDays(180); } diff --git a/Foxnouns.Backend/Extensions/KeyCacheExtensions.cs b/Foxnouns.Backend/Extensions/KeyCacheExtensions.cs index f3e1467..522c8d6 100644 --- a/Foxnouns.Backend/Extensions/KeyCacheExtensions.cs +++ b/Foxnouns.Backend/Extensions/KeyCacheExtensions.cs @@ -63,14 +63,13 @@ public static class KeyCacheExtensions this KeyCacheService keyCacheService, AuthType authType, Snowflake userId, - string? instance = null, CancellationToken ct = default ) { var state = AuthUtils.RandomToken(); await keyCacheService.SetKeyAsync( $"add_account:{state}", - new AddExtraAccountState(authType, userId, instance), + new AddExtraAccountState(authType, userId), Duration.FromDays(1), ct ); @@ -94,4 +93,4 @@ public record RegisterEmailState( [property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] Snowflake? ExistingUserId ); -public record AddExtraAccountState(AuthType AuthType, Snowflake UserId, string? Instance = null); +public record AddExtraAccountState(AuthType AuthType, Snowflake UserId); diff --git a/Foxnouns.Backend/Foxnouns.Backend.csproj b/Foxnouns.Backend/Foxnouns.Backend.csproj index d604012..dbc46d3 100644 --- a/Foxnouns.Backend/Foxnouns.Backend.csproj +++ b/Foxnouns.Backend/Foxnouns.Backend.csproj @@ -12,7 +12,6 @@ - diff --git a/Foxnouns.Backend/Jobs/MemberAvatarUpdateInvocable.cs b/Foxnouns.Backend/Jobs/MemberAvatarUpdateInvocable.cs index d2fd9ec..d97e1a7 100644 --- a/Foxnouns.Backend/Jobs/MemberAvatarUpdateInvocable.cs +++ b/Foxnouns.Backend/Jobs/MemberAvatarUpdateInvocable.cs @@ -1,3 +1,4 @@ +using System.Security.Cryptography; using Coravel.Invocable; using Foxnouns.Backend.Database; using Foxnouns.Backend.Extensions; diff --git a/Foxnouns.Backend/Jobs/UserAvatarUpdateInvocable.cs b/Foxnouns.Backend/Jobs/UserAvatarUpdateInvocable.cs index f212cc3..8147424 100644 --- a/Foxnouns.Backend/Jobs/UserAvatarUpdateInvocable.cs +++ b/Foxnouns.Backend/Jobs/UserAvatarUpdateInvocable.cs @@ -1,3 +1,4 @@ +using System.Security.Cryptography; using Coravel.Invocable; using Foxnouns.Backend.Database; using Foxnouns.Backend.Extensions; diff --git a/Foxnouns.Backend/Middleware/ErrorHandlerMiddleware.cs b/Foxnouns.Backend/Middleware/ErrorHandlerMiddleware.cs index 4c14e5f..70fa6ff 100644 --- a/Foxnouns.Backend/Middleware/ErrorHandlerMiddleware.cs +++ b/Foxnouns.Backend/Middleware/ErrorHandlerMiddleware.cs @@ -1,5 +1,6 @@ using System.Net; using Foxnouns.Backend.Utils; +using Microsoft.AspNetCore.Mvc.ModelBinding; using Newtonsoft.Json; namespace Foxnouns.Backend.Middleware; diff --git a/Foxnouns.Backend/Services/Auth/AuthService.cs b/Foxnouns.Backend/Services/Auth/AuthService.cs index e3ec4c4..4eca66e 100644 --- a/Foxnouns.Backend/Services/Auth/AuthService.cs +++ b/Foxnouns.Backend/Services/Auth/AuthService.cs @@ -218,11 +218,10 @@ public class AuthService( AuthType authType, string remoteId, string? remoteUsername = null, - FediverseApplication? app = null, CancellationToken ct = default ) { - AssertValidAuthType(authType, app); + AssertValidAuthType(authType, null); // This is already checked when var currentCount = await db @@ -238,7 +237,6 @@ public class AuthService( Id = snowflakeGenerator.GenerateSnowflake(), AuthType = authType, RemoteId = remoteId, - FediverseApplicationId = app?.Id, RemoteUsername = remoteUsername, UserId = userId, }; diff --git a/Foxnouns.Backend/Services/Auth/FediverseAuthService.Mastodon.cs b/Foxnouns.Backend/Services/Auth/FediverseAuthService.Mastodon.cs index ba232bf..97e411a 100644 --- a/Foxnouns.Backend/Services/Auth/FediverseAuthService.Mastodon.cs +++ b/Foxnouns.Backend/Services/Auth/FediverseAuthService.Mastodon.cs @@ -69,11 +69,10 @@ public partial class FediverseAuthService private async Task GetMastodonUserAsync( FediverseApplication app, string code, - string? state = null + string state ) { - if (state != null) - await _keyCacheService.ValidateAuthStateAsync(state); + await _keyCacheService.ValidateAuthStateAsync(state); var tokenResp = await _client.PostAsync( MastodonTokenUri(app.Domain), @@ -121,8 +120,7 @@ public partial class FediverseAuthService private async Task GenerateMastodonAuthUrlAsync( FediverseApplication app, - bool forceRefresh, - string? state = null + bool forceRefresh ) { if (forceRefresh) @@ -134,7 +132,7 @@ public partial class FediverseAuthService app = await CreateMastodonApplicationAsync(app.Domain, existingAppId: app.Id); } - state ??= HttpUtility.UrlEncode(await _keyCacheService.GenerateAuthStateAsync()); + var state = HttpUtility.UrlEncode(await _keyCacheService.GenerateAuthStateAsync()); return $"https://{app.Domain}/oauth/authorize?response_type=code" + $"&client_id={app.ClientId}" diff --git a/Foxnouns.Backend/Services/Auth/FediverseAuthService.cs b/Foxnouns.Backend/Services/Auth/FediverseAuthService.cs index 224c0a3..f78fbde 100644 --- a/Foxnouns.Backend/Services/Auth/FediverseAuthService.cs +++ b/Foxnouns.Backend/Services/Auth/FediverseAuthService.cs @@ -37,14 +37,10 @@ public partial class FediverseAuthService _client.DefaultRequestHeaders.Add("Accept", "application/json"); } - public async Task GenerateAuthUrlAsync( - string instance, - bool forceRefresh, - string? state = null - ) + public async Task GenerateAuthUrlAsync(string instance, bool forceRefresh) { var app = await GetApplicationAsync(instance); - return await GenerateAuthUrlAsync(app, forceRefresh, state); + return await GenerateAuthUrlAsync(app, forceRefresh); } // thank you, gargron and syuilo, for agreeing on a name for *once* in your lives, @@ -100,17 +96,12 @@ public partial class FediverseAuthService ); } - private async Task GenerateAuthUrlAsync( - FediverseApplication app, - bool forceRefresh, - string? state = null - ) => + private async Task GenerateAuthUrlAsync(FediverseApplication app, bool forceRefresh) => app.InstanceType switch { FediverseInstanceType.MastodonApi => await GenerateMastodonAuthUrlAsync( app, - forceRefresh, - state + forceRefresh ), FediverseInstanceType.MisskeyApi => throw new NotImplementedException(), _ => throw new ArgumentOutOfRangeException(nameof(app), app.InstanceType, null), @@ -119,7 +110,7 @@ public partial class FediverseAuthService public async Task GetRemoteFediverseUserAsync( FediverseApplication app, string code, - string? state = null + string state ) => app.InstanceType switch { diff --git a/Foxnouns.Backend/Services/Auth/RemoteAuthService.cs b/Foxnouns.Backend/Services/RemoteAuthService.cs similarity index 52% rename from Foxnouns.Backend/Services/Auth/RemoteAuthService.cs rename to Foxnouns.Backend/Services/RemoteAuthService.cs index 9b62a70..91a2dc5 100644 --- a/Foxnouns.Backend/Services/Auth/RemoteAuthService.cs +++ b/Foxnouns.Backend/Services/RemoteAuthService.cs @@ -1,21 +1,9 @@ using System.Diagnostics.CodeAnalysis; -using System.Web; -using Foxnouns.Backend.Database; -using Foxnouns.Backend.Database.Models; -using Foxnouns.Backend.Extensions; -using Foxnouns.Backend.Utils; -using Humanizer; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore; -namespace Foxnouns.Backend.Services.Auth; +namespace Foxnouns.Backend.Services; -public class RemoteAuthService( - Config config, - ILogger logger, - DatabaseContext db, - KeyCacheService keyCacheService -) +public class RemoteAuthService(Config config, ILogger logger) { private readonly ILogger _logger = logger.ForContext(); private readonly HttpClient _httpClient = new(); @@ -88,56 +76,4 @@ public class RemoteAuthService( private record DiscordUserResponse(string id, string username); public record RemoteUser(string Id, string Username); - - /// - /// Validates whether a user can still add a new account of the given AuthType, and throws an error if they can't. - /// - /// The user to check. - /// The auth type to check. - /// The optional fediverse instance to generate a state for. - /// A state for the given auth type and user ID. - /// The given user can't add another account of this type. - /// This exception should not be caught by controller code. - public async Task ValidateAddAccountRequestAsync( - Snowflake userId, - AuthType authType, - string? instance = null - ) - { - var existingAccounts = await db - .AuthMethods.Where(m => m.UserId == userId && m.AuthType == authType) - .CountAsync(); - if (existingAccounts > AuthUtils.MaxAuthMethodsPerType) - { - throw new ApiError.BadRequest( - $"Too many linked {authType.Humanize()} accounts, maximum of {AuthUtils.MaxAuthMethodsPerType} per account." - ); - } - - return HttpUtility.UrlEncode( - await keyCacheService.GenerateAddExtraAccountStateAsync(authType, userId, instance) - ); - } - - /// - /// Checks whether the given state is correct for the given user/auth type combination. - /// - /// The state doesn't match. - /// This exception should not be caught by controller code. - public async Task ValidateAddAccountStateAsync( - string state, - Snowflake userId, - AuthType authType, - string? instance = null - ) - { - var accountState = await keyCacheService.GetAddExtraAccountStateAsync(state); - if ( - accountState == null - || accountState.AuthType != authType - || accountState.UserId != userId - || (instance != null && accountState.Instance != instance) - ) - throw new ApiError.BadRequest("Invalid state", "state", state); - } } diff --git a/Foxnouns.Backend/Services/UserRendererService.cs b/Foxnouns.Backend/Services/UserRendererService.cs index cb728d1..f073cda 100644 --- a/Foxnouns.Backend/Services/UserRendererService.cs +++ b/Foxnouns.Backend/Services/UserRendererService.cs @@ -4,6 +4,7 @@ using Foxnouns.Backend.Utils; using Microsoft.EntityFrameworkCore; using Newtonsoft.Json; using NodaTime; +using Org.BouncyCastle.Ocsp; namespace Foxnouns.Backend.Services; diff --git a/Foxnouns.Backend/Utils/ValidationUtils.Fields.cs b/Foxnouns.Backend/Utils/ValidationUtils.Fields.cs index 4271459..1ed083c 100644 --- a/Foxnouns.Backend/Utils/ValidationUtils.Fields.cs +++ b/Foxnouns.Backend/Utils/ValidationUtils.Fields.cs @@ -127,9 +127,6 @@ public static partial class ValidationUtils if (entries.Length > Limits.FieldEntriesLimit + 50) return errors; - var customPreferenceIds = - customPreferences?.Keys.Select(id => id.ToString()).ToArray() ?? []; - foreach (var (entry, entryIdx) in entries.Select((entry, entryIdx) => (entry, entryIdx))) { switch (entry.Value.Length) @@ -162,6 +159,8 @@ public static partial class ValidationUtils break; } + var customPreferenceIds = customPreferences?.Keys.Select(id => id.ToString()) ?? []; + if ( !DefaultStatusOptions.Contains(entry.Status) && !customPreferenceIds.Contains(entry.Status) @@ -204,9 +203,6 @@ public static partial class ValidationUtils if (entries.Length > Limits.FieldEntriesLimit + 50) return errors; - var customPreferenceIds = - customPreferences?.Keys.Select(id => id.ToString()).ToList() ?? []; - foreach (var (entry, entryIdx) in entries.Select((entry, entryIdx) => (entry, entryIdx))) { switch (entry.Value.Length) @@ -272,6 +268,8 @@ public static partial class ValidationUtils } } + var customPreferenceIds = customPreferences?.Keys.Select(id => id.ToString()) ?? []; + if ( !DefaultStatusOptions.Contains(entry.Status) && !customPreferenceIds.Contains(entry.Status) diff --git a/Foxnouns.Backend/packages.lock.json b/Foxnouns.Backend/packages.lock.json index 5a5a32b..02ca7ca 100644 --- a/Foxnouns.Backend/packages.lock.json +++ b/Foxnouns.Backend/packages.lock.json @@ -45,12 +45,6 @@ "Npgsql": "8.0.1" } }, - "Humanizer.Core": { - "type": "Direct", - "requested": "[2.14.1, )", - "resolved": "2.14.1", - "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" - }, "JetBrains.Annotations": { "type": "Direct", "requested": "[2024.2.0, )", @@ -297,6 +291,11 @@ "Microsoft.EntityFrameworkCore.Relational": "8.0.0" } }, + "Humanizer.Core": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" + }, "MailKit": { "type": "Transitive", "resolved": "2.5.1", diff --git a/Foxnouns.Frontend/package.json b/Foxnouns.Frontend/package.json index 0e74736..3fc70d1 100644 --- a/Foxnouns.Frontend/package.json +++ b/Foxnouns.Frontend/package.json @@ -42,7 +42,6 @@ "bootstrap-icons": "^1.11.3", "luxon": "^3.5.0", "markdown-it": "^14.1.0", - "minidenticons": "^4.2.1", "pretty-bytes": "^6.1.1", "sanitize-html": "^2.13.1", "svelte-tippy": "^1.3.2", diff --git a/Foxnouns.Frontend/pnpm-lock.yaml b/Foxnouns.Frontend/pnpm-lock.yaml index bb1a839..9b78513 100644 --- a/Foxnouns.Frontend/pnpm-lock.yaml +++ b/Foxnouns.Frontend/pnpm-lock.yaml @@ -23,9 +23,6 @@ importers: markdown-it: specifier: ^14.1.0 version: 14.1.0 - minidenticons: - specifier: ^4.2.1 - version: 4.2.1 pretty-bytes: specifier: ^6.1.1 version: 6.1.1 @@ -1113,10 +1110,6 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} - minidenticons@4.2.1: - resolution: {integrity: sha512-oWfFivA0lOx/V/bO/YIJbthB26lV8JXYvhnv9zM2hNd3fzsHTXQ6c6bWZPcvhD3nnOB+lQk/D9lF43BXixrN8g==} - engines: {node: '>=15.14.0'} - minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -2376,8 +2369,6 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 - minidenticons@4.2.1: {} - minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 diff --git a/Foxnouns.Frontend/src/lib/components/Avatar.svelte b/Foxnouns.Frontend/src/lib/components/Avatar.svelte index d9f4eec..99dd8f3 100644 --- a/Foxnouns.Frontend/src/lib/components/Avatar.svelte +++ b/Foxnouns.Frontend/src/lib/components/Avatar.svelte @@ -1,22 +1,16 @@ diff --git a/Foxnouns.Frontend/src/lib/components/editor/AvatarEditor.svelte b/Foxnouns.Frontend/src/lib/components/editor/AvatarEditor.svelte index e18c6b6..05a9a62 100644 --- a/Foxnouns.Frontend/src/lib/components/editor/AvatarEditor.svelte +++ b/Foxnouns.Frontend/src/lib/components/editor/AvatarEditor.svelte @@ -7,13 +7,12 @@ import ShortNoscriptWarning from "./ShortNoscriptWarning.svelte"; type Props = { - name: string; current: string | null; alt: string; update: (avatar: string) => Promise; updated: boolean; }; - let { name, current, alt, update: onclick, updated }: Props = $props(); + let { current, alt, update: onclick, updated }: Props = $props(); const MAX_AVATAR_BYTES = 1_000_000; @@ -41,7 +40,7 @@

- +

diff --git a/Foxnouns.Frontend/src/lib/components/profile/ProfileHeader.svelte b/Foxnouns.Frontend/src/lib/components/profile/ProfileHeader.svelte index 0fd1960..d28a001 100644 --- a/Foxnouns.Frontend/src/lib/components/profile/ProfileHeader.svelte +++ b/Foxnouns.Frontend/src/lib/components/profile/ProfileHeader.svelte @@ -22,7 +22,6 @@
{ + const { meUser } = await parent(); + if (meUser) redirect(303, `/@${meUser.username}`); + const code = url.searchParams.get("code") as string | null; const state = url.searchParams.get("state") as string | null; if (!code || !state) throw new ApiError(undefined, ErrorCode.BadRequest).obj; - const { meUser } = await parent(); - if (meUser) { - try { - const resp = await apiRequest( - "POST", - "/auth/fediverse/add-account/callback", - { - isInternal: true, - body: { code, state, instance: params.instance }, - fetch, - cookies, - }, - ); + const resp = await apiRequest("POST", "/auth/fediverse/callback", { + body: { code, state, instance: params.instance }, + isInternal: true, + fetch, + }); - return { hasAccount: true, isLinkRequest: true, newAuthMethod: resp }; - } catch (e) { - if (e instanceof ApiError) return { isLinkRequest: true, error: e.obj }; - log.error("error linking new fediverse account to user %s:", meUser.id, e); - throw e; - } + if (resp.has_account) { + setToken(cookies, resp.token!); + redirect(303, `/@${resp.user!.username}`); } - try { - const resp = await apiRequest("POST", "/auth/fediverse/callback", { - body: { code, state, instance: params.instance }, - isInternal: true, - fetch, - }); - - if (resp.has_account) { - setToken(cookies, resp.token!); - redirect(303, `/@${resp.user!.username}`); - } - - return { - hasAccount: false, - isLinkRequest: false, - ticket: resp.ticket!, - remoteUser: resp.remote_username!, - }; - } catch (e) { - if (isRedirect(e)) throw e; - if (e instanceof ApiError) return { isLinkRequest: false, error: e.obj }; - log.error("error while requesting fediverse callback:", e); - throw e; - } + return { + hasAccount: false, + instance: params.instance, + ticket: resp.ticket!, + remoteUser: resp.remote_username!, + }; }; export const actions = { diff --git a/Foxnouns.Frontend/src/routes/auth/callback/mastodon/[instance]/+page.svelte b/Foxnouns.Frontend/src/routes/auth/callback/mastodon/[instance]/+page.svelte index 99bd00c..5d02eeb 100644 --- a/Foxnouns.Frontend/src/routes/auth/callback/mastodon/[instance]/+page.svelte +++ b/Foxnouns.Frontend/src/routes/auth/callback/mastodon/[instance]/+page.svelte @@ -1,9 +1,7 @@ - -

Link a new Fediverse account

- -
- - - - -

- {$t("auth.log-in-with-fediverse-error-blurb")} - -

-
diff --git a/Foxnouns.Frontend/src/routes/settings/export/+page.server.ts b/Foxnouns.Frontend/src/routes/settings/export/+page.server.ts deleted file mode 100644 index 136af01..0000000 --- a/Foxnouns.Frontend/src/routes/settings/export/+page.server.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { apiRequest, fastRequest } from "$api"; -import ApiError from "$api/error.js"; -import log from "$lib/log.js"; -import { DateTime, Duration } from "luxon"; - -type Export = { url: string | null; expires_at: string | null }; - -export const load = async ({ fetch, cookies }) => { - const resp = await apiRequest("GET", "/data-exports", { - fetch, - cookies, - isInternal: true, - }); - - let canExport = true; - if (resp.expires_at) { - const created = DateTime.fromISO(resp.expires_at).minus(Duration.fromObject({ days: 15 })); - canExport = DateTime.now().diff(created, "seconds").seconds >= 86400; - } - - return { url: resp.url, expiresAt: resp.expires_at, canExport }; -}; - -export const actions = { - default: async ({ fetch, cookies }) => { - try { - fastRequest("POST", "/data-exports", { fetch, cookies, isInternal: true }); - return { ok: true, error: null }; - } catch (e) { - if (e instanceof ApiError) return { ok: false, error: e.obj }; - log.error("Error requesting data export:", e); - throw e; - } - }, -}; diff --git a/Foxnouns.Frontend/src/routes/settings/export/+page.svelte b/Foxnouns.Frontend/src/routes/settings/export/+page.svelte deleted file mode 100644 index 874f8e8..0000000 --- a/Foxnouns.Frontend/src/routes/settings/export/+page.svelte +++ /dev/null @@ -1,49 +0,0 @@ - - -
diff --git a/Foxnouns.Frontend/src/routes/settings/members/+page.svelte b/Foxnouns.Frontend/src/routes/settings/members/+page.svelte index 6963c0c..563cf90 100644 --- a/Foxnouns.Frontend/src/routes/settings/members/+page.svelte +++ b/Foxnouns.Frontend/src/routes/settings/members/+page.svelte @@ -30,7 +30,6 @@ {#each data.members as member (member.id)}

{$t("settings.avatar")}

{$t("edit-profile.member-name")}

-
+

{$t("edit-profile.display-name")}

- +

{$t("edit-profile.profile-options-header")}

- +

{$t("edit-profile.bio-tab")}

- +
diff --git a/Foxnouns.Frontend/src/routes/settings/members/new/+page.svelte b/Foxnouns.Frontend/src/routes/settings/members/new/+page.svelte index 2c1fc0b..7999be2 100644 --- a/Foxnouns.Frontend/src/routes/settings/members/new/+page.svelte +++ b/Foxnouns.Frontend/src/routes/settings/members/new/+page.svelte @@ -1,5 +1,4 @@