:drunk: AI trying to fix bugs

This commit is contained in:
2025-07-06 21:15:30 +08:00
parent 3391c08c04
commit 7d1f096e87
70 changed files with 681 additions and 66945 deletions

View File

@ -212,7 +212,7 @@ public class AccountCurrentController(
}
[HttpPost("statuses")]
[DysonNetwork.Sphere.Permission.RequiredPermission("global", "accounts.statuses.create")]
[DysonNetwork.Common.Services.Permission.RequiredPermission("global", "accounts.statuses.create")]
public async Task<ActionResult<Status>> CreateStatus([FromBody] AccountController.StatusRequest request)
{
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();

View File

@ -140,7 +140,7 @@ public class NotificationController(PassDatabase db, NotificationService nty) :
[HttpPost("send")]
[Authorize]
[DysonNetwork.Sphere.Permission.RequiredPermission("global", "notifications.send")]
[DysonNetwork.Common.Services.Permission.RequiredPermission("global", "notifications.send")]
public async Task<ActionResult> SendNotification(
[FromBody] NotificationWithAimRequest request,
[FromQuery] bool save = false

View File

@ -3,7 +3,7 @@ using DysonNetwork.Pass.Features.Auth;
using DysonNetwork.Pass.Features.Auth.OpenId;
using DysonNetwork.Pass.Email;
using DysonNetwork.Pass.Localization;
using DysonNetwork.Sphere.Permission;
// Permission types are now in DysonNetwork.Common.Models
using DysonNetwork.Pass.Storage;
using EFCore.BulkExtensions;
using Microsoft.EntityFrameworkCore;

View File

@ -3,7 +3,7 @@ using DysonNetwork.Pass.Storage;
using Microsoft.EntityFrameworkCore;
using NodaTime;
using DysonNetwork.Common.Models;
using DysonNetwork.Sphere.Permission;
// Permission types are now in DysonNetwork.Common.Models
namespace DysonNetwork.Pass.Features.Account;

View File

@ -6,7 +6,7 @@ namespace DysonNetwork.Pass.Features.Auth.Interfaces;
public interface IOidcService
{
string GetAuthorizationUrl(string state, string nonce);
Task<OidcUserInfo> ProcessCallbackAsync(OidcCallbackData callbackData);
Task<DysonNetwork.Common.Models.OidcUserInfo> ProcessCallbackAsync(OidcCallbackData callbackData);
Task<AuthResult> AuthenticateAsync(string provider, string code, string state);
IEnumerable<string> GetSupportedProviders();
}

View File

@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;
using DysonNetwork.Common.Models;
using NodaTime;
using AccountConnection = DysonNetwork.Common.Models.AccountConnection;
namespace DysonNetwork.Pass.Features.Auth.Models;

View File

@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json;
using DysonNetwork.Common.Models;
using DysonNetwork.Common.Models.Auth;
using NodaTime;
namespace DysonNetwork.Pass.Features.Auth.Models;
@ -34,6 +35,9 @@ public class AccountAuthFactor : ModelBase
[Required]
public bool IsBackup { get; set; }
[Required]
public bool IsEnabled { get; set; } = true;
public Instant? LastUsedAt { get; set; }
public Instant? EnabledAt { get; set; }

View File

@ -1,70 +0,0 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json;
using System.Text.Json.Serialization;
using DysonNetwork.Pass.Models;
using NodaTime;
namespace DysonNetwork.Pass.Features.Auth.Models;
public class AccountConnection : ModelBase
{
[Required]
public Guid AccountId { get; set; }
[ForeignKey(nameof(AccountId))]
[JsonIgnore]
public virtual Account Account { get; set; } = null!;
[Required]
[MaxLength(50)]
public string Provider { get; set; } = string.Empty;
[Required]
[MaxLength(256)]
public string ProviderId { get; set; } = string.Empty;
[MaxLength(256)]
public string? DisplayName { get; set; }
[MaxLength(1000)]
public string? AccessToken { get; set; }
[MaxLength(1000)]
public string? RefreshToken { get; set; }
public Instant? ExpiresAt { get; set; }
[Column(TypeName = "jsonb")]
public JsonDocument? ProfileData { get; set; }
public Instant ConnectedAt { get; set; } = SystemClock.Instance.GetCurrentInstant();
public Instant? LastUsedAt { get; set; }
[Column(TypeName = "jsonb")]
public JsonDocument? Metadata { get; set; }
public bool IsConnected => ExpiresAt == null || ExpiresAt > SystemClock.Instance.GetCurrentInstant();
public void UpdateTokens(string? accessToken, string? refreshToken, Instant? expiresAt)
{
AccessToken = accessToken;
RefreshToken = refreshToken;
ExpiresAt = expiresAt;
LastUsedAt = SystemClock.Instance.GetCurrentInstant();
}
public void Disconnect()
{
AccessToken = null;
RefreshToken = null;
ExpiresAt = null;
ConnectedAt = default; // Set to default value for Instant
}
public void UpdateProfileData(JsonDocument? profileData)
{
ProfileData = profileData;
}
}

View File

@ -6,6 +6,8 @@ using DysonNetwork.Pass.Storage;
using NodaTime;
using DysonNetwork.Pass.Data;
using DysonNetwork.Common.Models;
using Account = DysonNetwork.Common.Models.Account;
using AccountConnection = DysonNetwork.Common.Models.AccountConnection;
namespace DysonNetwork.Pass.Features.Auth.OpenId;
@ -27,21 +29,24 @@ public class ConnectionController(
[HttpGet]
public async Task<ActionResult<List<AccountConnection>>> GetConnections()
{
if (HttpContext.Items["CurrentUser"] is not Models.Account currentUser)
if (HttpContext.Items["CurrentUser"] is not Account currentUser)
return Unauthorized();
var connections = await db.AccountConnections
.Where(c => c.AccountId == currentUser.Id)
.Select(c => new
.Select(c => new AccountConnection
{
c.Id,
c.AccountId,
c.Provider,
c.ProvidedIdentifier,
c.Meta,
c.LastUsedAt,
c.CreatedAt,
c.UpdatedAt,
Id = c.Id,
AccountId = c.AccountId,
Provider = c.Provider,
ProvidedIdentifier = c.ProvidedIdentifier,
DisplayName = c.DisplayName,
AccessToken = c.AccessToken,
RefreshToken = c.RefreshToken,
ExpiresAt = c.ExpiresAt,
LastUsedAt = c.LastUsedAt,
CreatedAt = c.CreatedAt,
UpdatedAt = c.UpdatedAt
})
.ToListAsync();
return Ok(connections);

View File

@ -1,8 +1,8 @@
using System.Net.Http.Json;
using System.Text.Json;
using DysonNetwork.Common.Models;
using DysonNetwork.Common.Services;
using DysonNetwork.Pass.Data;
using DysonNetwork.Sphere;
namespace DysonNetwork.Pass.Features.Auth.OpenId;
@ -47,7 +47,7 @@ public class DiscordOidcService(
})!;
}
public override async Task<OidcUserInfo> ProcessCallbackAsync(OidcCallbackData callbackData)
public override async Task<DysonNetwork.Common.Models.OidcUserInfo> ProcessCallbackAsync(OidcCallbackData callbackData)
{
var tokenResponse = await ExchangeCodeForTokensAsync(callbackData.Code);
if (tokenResponse?.AccessToken == null)
@ -84,7 +84,7 @@ public class DiscordOidcService(
return await response.Content.ReadFromJsonAsync<OidcTokenResponse>();
}
private async Task<OidcUserInfo> GetUserInfoAsync(string accessToken)
private async Task<DysonNetwork.Common.Models.OidcUserInfo> GetUserInfoAsync(string accessToken)
{
var client = HttpClientFactory.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://discord.com/users/@me");
@ -99,7 +99,7 @@ public class DiscordOidcService(
var userId = discordUser.GetProperty("id").GetString() ?? "";
var avatar = discordUser.TryGetProperty("avatar", out var avatarElement) ? avatarElement.GetString() : null;
return new OidcUserInfo
return new DysonNetwork.Common.Models.OidcUserInfo
{
UserId = userId,
Email = (discordUser.TryGetProperty("email", out var emailElement) ? emailElement.GetString() : null) ?? "",

View File

@ -1,6 +1,8 @@
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Serialization;
using DysonNetwork.Common.Models;
using DysonNetwork.Common.Services;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
@ -48,7 +50,7 @@ public abstract class OidcService(
/// <summary>
/// Process the callback from the OIDC provider
/// </summary>
public abstract Task<OidcUserInfo> ProcessCallbackAsync(OidcCallbackData callbackData);
public abstract Task<DysonNetwork.Common.Models.OidcUserInfo> ProcessCallbackAsync(OidcCallbackData callbackData);
/// <summary>
/// Gets the provider configuration
@ -145,7 +147,7 @@ public abstract class OidcService(
/// <summary>
/// Validates and extracts information from an ID token
/// </summary>
protected virtual OidcUserInfo ValidateAndExtractIdToken(string idToken,
protected virtual DysonNetwork.Common.Models.OidcUserInfo ValidateAndExtractIdToken(string idToken,
TokenValidationParameters validationParameters)
{
var handler = new JwtSecurityTokenHandler();
@ -171,18 +173,24 @@ public abstract class OidcService(
username = !string.IsNullOrEmpty(email) ? email.Split('@')[0] : null;
}
return new OidcUserInfo
// Convert the user info to our model
var userInfo = new DysonNetwork.Common.Models.OidcUserInfo
{
UserId = userId,
Email = email,
EmailVerified = emailVerified,
FirstName = givenName ?? "",
LastName = familyName ?? "",
DisplayName = name ?? $"{givenName} {familyName}".Trim(),
PreferredUsername = username ?? "",
ProfilePictureUrl = picture,
Provider = ProviderName
GivenName = givenName,
FamilyName = familyName,
Name = name,
UserId = userId,
Picture = picture,
AccessToken = "",
RefreshToken = "",
Provider = ProviderName,
ExpiresAt = null,
Claims = null
};
return userInfo;
}
/// <summary>
@ -190,7 +198,7 @@ public abstract class OidcService(
/// Also creates or updates the account connection
/// </summary>
public async Task<Challenge> CreateChallengeForUserAsync(
OidcUserInfo userInfo,
DysonNetwork.Common.Models.OidcUserInfo userInfo,
Models.Account account,
HttpContext request,
string deviceId
@ -199,20 +207,22 @@ public abstract class OidcService(
// Create or update the account connection
var connection = await Db.AccountConnections
.FirstOrDefaultAsync(c => c.Provider == ProviderName &&
c.ProvidedIdentifier == userInfo.UserId &&
c.AccountId == account.Id
c.ProvidedIdentifier == userInfo.UserId &&
c.AccountId == account.Id
);
if (connection is null)
{
connection = new AccountConnection
connection = new DysonNetwork.Common.Models.AccountConnection
{
Provider = ProviderName,
ProvidedIdentifier = userInfo.UserId ?? "",
AccessToken = userInfo.AccessToken,
RefreshToken = userInfo.RefreshToken,
LastUsedAt = SystemClock.Instance.GetCurrentInstant(),
AccountId = account.Id
AccountId = account.Id,
CreatedAt = SystemClock.Instance.GetCurrentInstant(),
ProfileData = userInfo.Claims != null ? JsonSerializer.SerializeToDocument(userInfo.Claims) : null
};
await Db.AccountConnections.AddAsync(connection);
}