Compare commits

..

2 Commits

Author SHA1 Message Date
9ce31c4dd8 ♻️ Finish centerlizing the data models 2025-09-27 15:14:05 +08:00
e70d8371f8 ♻️ Centralized data models (wip) 2025-09-27 14:09:28 +08:00
262 changed files with 1993 additions and 44869 deletions

View File

@@ -1,6 +1,4 @@
using System.Text.Json; using DysonNetwork.Shared.Models;
using DysonNetwork.Develop.Identity;
using DysonNetwork.Develop.Project;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design; using Microsoft.EntityFrameworkCore.Design;
@@ -11,13 +9,13 @@ public class AppDatabase(
IConfiguration configuration IConfiguration configuration
) : DbContext(options) ) : DbContext(options)
{ {
public DbSet<Developer> Developers { get; set; } = null!; public DbSet<SnDeveloper> Developers { get; set; } = null!;
public DbSet<DevProject> DevProjects { get; set; } = null!; public DbSet<SnDevProject> DevProjects { get; set; } = null!;
public DbSet<CustomApp> CustomApps { get; set; } = null!; public DbSet<SnCustomApp> CustomApps { get; set; } = null!;
public DbSet<CustomAppSecret> CustomAppSecrets { get; set; } = null!; public DbSet<SnCustomAppSecret> CustomAppSecrets { get; set; } = null!;
public DbSet<BotAccount> BotAccounts { get; set; } = null!; public DbSet<SnBotAccount> BotAccounts { get; set; } = null!;
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{ {

View File

@@ -1,6 +1,6 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using DysonNetwork.Develop.Project; using DysonNetwork.Develop.Project;
using DysonNetwork.Shared.Data; using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Proto;
using DysonNetwork.Shared.Registry; using DysonNetwork.Shared.Registry;
using Grpc.Core; using Grpc.Core;
@@ -88,7 +88,7 @@ public class BotAccountController(
return NotFound("Developer not found"); return NotFound("Developer not found");
if (!await developerService.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id), if (!await developerService.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id),
PublisherMemberRole.Viewer)) Shared.Proto.PublisherMemberRole.Viewer))
return StatusCode(403, "You must be an viewer of the developer to list bots"); return StatusCode(403, "You must be an viewer of the developer to list bots");
var project = await projectService.GetProjectAsync(projectId, developer.Id); var project = await projectService.GetProjectAsync(projectId, developer.Id);
@@ -113,7 +113,7 @@ public class BotAccountController(
return NotFound("Developer not found"); return NotFound("Developer not found");
if (!await developerService.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id), if (!await developerService.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id),
PublisherMemberRole.Viewer)) Shared.Proto.PublisherMemberRole.Viewer))
return StatusCode(403, "You must be an viewer of the developer to view bot details"); return StatusCode(403, "You must be an viewer of the developer to view bot details");
var project = await projectService.GetProjectAsync(projectId, developer.Id); var project = await projectService.GetProjectAsync(projectId, developer.Id);
@@ -142,7 +142,7 @@ public class BotAccountController(
return NotFound("Developer not found"); return NotFound("Developer not found");
if (!await developerService.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id), if (!await developerService.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id),
PublisherMemberRole.Editor)) Shared.Proto.PublisherMemberRole.Editor))
return StatusCode(403, "You must be an editor of the developer to create a bot"); return StatusCode(403, "You must be an editor of the developer to create a bot");
var project = await projectService.GetProjectAsync(projectId, developer.Id); var project = await projectService.GetProjectAsync(projectId, developer.Id);
@@ -211,7 +211,7 @@ public class BotAccountController(
return NotFound("Developer not found"); return NotFound("Developer not found");
if (!await developerService.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id), if (!await developerService.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id),
PublisherMemberRole.Editor)) Shared.Proto.PublisherMemberRole.Editor))
return StatusCode(403, "You must be an editor of the developer to update a bot"); return StatusCode(403, "You must be an editor of the developer to update a bot");
var project = await projectService.GetProjectAsync(projectId, developer.Id); var project = await projectService.GetProjectAsync(projectId, developer.Id);
@@ -272,7 +272,7 @@ public class BotAccountController(
return NotFound("Developer not found"); return NotFound("Developer not found");
if (!await developerService.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id), if (!await developerService.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id),
PublisherMemberRole.Editor)) Shared.Proto.PublisherMemberRole.Editor))
return StatusCode(403, "You must be an editor of the developer to delete a bot"); return StatusCode(403, "You must be an editor of the developer to delete a bot");
var project = await projectService.GetProjectAsync(projectId, developer.Id); var project = await projectService.GetProjectAsync(projectId, developer.Id);
@@ -296,7 +296,7 @@ public class BotAccountController(
} }
[HttpGet("{botId:guid}/keys")] [HttpGet("{botId:guid}/keys")]
public async Task<ActionResult<List<ApiKeyReference>>> ListBotKeys( public async Task<ActionResult<List<SnApiKey>>> ListBotKeys(
[FromRoute] string pubName, [FromRoute] string pubName,
[FromRoute] Guid projectId, [FromRoute] Guid projectId,
[FromRoute] Guid botId [FromRoute] Guid botId
@@ -305,7 +305,7 @@ public class BotAccountController(
if (HttpContext.Items["CurrentUser"] is not Account currentUser) if (HttpContext.Items["CurrentUser"] is not Account currentUser)
return Unauthorized(); return Unauthorized();
var (developer, project, bot) = await ValidateBotAccess(pubName, projectId, botId, currentUser, PublisherMemberRole.Viewer); var (developer, project, bot) = await ValidateBotAccess(pubName, projectId, botId, currentUser, Shared.Proto.PublisherMemberRole.Viewer);
if (developer == null) return NotFound("Developer not found"); if (developer == null) return NotFound("Developer not found");
if (project == null) return NotFound("Project not found or you don't have access"); if (project == null) return NotFound("Project not found or you don't have access");
if (bot == null) return NotFound("Bot not found"); if (bot == null) return NotFound("Bot not found");
@@ -314,13 +314,13 @@ public class BotAccountController(
{ {
AutomatedId = bot.Id.ToString() AutomatedId = bot.Id.ToString()
}); });
var data = keys.Data.Select(ApiKeyReference.FromProtoValue).ToList(); var data = keys.Data.Select(SnApiKey.FromProtoValue).ToList();
return Ok(data); return Ok(data);
} }
[HttpGet("{botId:guid}/keys/{keyId:guid}")] [HttpGet("{botId:guid}/keys/{keyId:guid}")]
public async Task<ActionResult<ApiKeyReference>> GetBotKey( public async Task<ActionResult<SnApiKey>> GetBotKey(
[FromRoute] string pubName, [FromRoute] string pubName,
[FromRoute] Guid projectId, [FromRoute] Guid projectId,
[FromRoute] Guid botId, [FromRoute] Guid botId,
@@ -329,7 +329,7 @@ public class BotAccountController(
if (HttpContext.Items["CurrentUser"] is not Account currentUser) if (HttpContext.Items["CurrentUser"] is not Account currentUser)
return Unauthorized(); return Unauthorized();
var (developer, project, bot) = await ValidateBotAccess(pubName, projectId, botId, currentUser, PublisherMemberRole.Viewer); var (developer, project, bot) = await ValidateBotAccess(pubName, projectId, botId, currentUser, Shared.Proto.PublisherMemberRole.Viewer);
if (developer == null) return NotFound("Developer not found"); if (developer == null) return NotFound("Developer not found");
if (project == null) return NotFound("Project not found or you don't have access"); if (project == null) return NotFound("Project not found or you don't have access");
if (bot == null) return NotFound("Bot not found"); if (bot == null) return NotFound("Bot not found");
@@ -338,7 +338,7 @@ public class BotAccountController(
{ {
var key = await accountsReceiver.GetApiKeyAsync(new GetApiKeyRequest { Id = keyId.ToString() }); var key = await accountsReceiver.GetApiKeyAsync(new GetApiKeyRequest { Id = keyId.ToString() });
if (key == null) return NotFound("API key not found"); if (key == null) return NotFound("API key not found");
return Ok(ApiKeyReference.FromProtoValue(key)); return Ok(SnApiKey.FromProtoValue(key));
} }
catch (RpcException ex) when (ex.StatusCode == Grpc.Core.StatusCode.NotFound) catch (RpcException ex) when (ex.StatusCode == Grpc.Core.StatusCode.NotFound)
{ {
@@ -353,7 +353,7 @@ public class BotAccountController(
} }
[HttpPost("{botId:guid}/keys")] [HttpPost("{botId:guid}/keys")]
public async Task<ActionResult<ApiKeyReference>> CreateBotKey( public async Task<ActionResult<SnApiKey>> CreateBotKey(
[FromRoute] string pubName, [FromRoute] string pubName,
[FromRoute] Guid projectId, [FromRoute] Guid projectId,
[FromRoute] Guid botId, [FromRoute] Guid botId,
@@ -362,7 +362,7 @@ public class BotAccountController(
if (HttpContext.Items["CurrentUser"] is not Account currentUser) if (HttpContext.Items["CurrentUser"] is not Account currentUser)
return Unauthorized(); return Unauthorized();
var (developer, project, bot) = await ValidateBotAccess(pubName, projectId, botId, currentUser, PublisherMemberRole.Editor); var (developer, project, bot) = await ValidateBotAccess(pubName, projectId, botId, currentUser, Shared.Proto.PublisherMemberRole.Editor);
if (developer == null) return NotFound("Developer not found"); if (developer == null) return NotFound("Developer not found");
if (project == null) return NotFound("Project not found or you don't have access"); if (project == null) return NotFound("Project not found or you don't have access");
if (bot == null) return NotFound("Bot not found"); if (bot == null) return NotFound("Bot not found");
@@ -376,7 +376,7 @@ public class BotAccountController(
}; };
var createdKey = await accountsReceiver.CreateApiKeyAsync(newKey); var createdKey = await accountsReceiver.CreateApiKeyAsync(newKey);
return Ok(ApiKeyReference.FromProtoValue(createdKey)); return Ok(SnApiKey.FromProtoValue(createdKey));
} }
catch (RpcException ex) when (ex.StatusCode == Grpc.Core.StatusCode.InvalidArgument) catch (RpcException ex) when (ex.StatusCode == Grpc.Core.StatusCode.InvalidArgument)
{ {
@@ -385,7 +385,7 @@ public class BotAccountController(
} }
[HttpPost("{botId:guid}/keys/{keyId:guid}/rotate")] [HttpPost("{botId:guid}/keys/{keyId:guid}/rotate")]
public async Task<ActionResult<ApiKeyReference>> RotateBotKey( public async Task<ActionResult<SnApiKey>> RotateBotKey(
[FromRoute] string pubName, [FromRoute] string pubName,
[FromRoute] Guid projectId, [FromRoute] Guid projectId,
[FromRoute] Guid botId, [FromRoute] Guid botId,
@@ -394,7 +394,7 @@ public class BotAccountController(
if (HttpContext.Items["CurrentUser"] is not Account currentUser) if (HttpContext.Items["CurrentUser"] is not Account currentUser)
return Unauthorized(); return Unauthorized();
var (developer, project, bot) = await ValidateBotAccess(pubName, projectId, botId, currentUser, PublisherMemberRole.Editor); var (developer, project, bot) = await ValidateBotAccess(pubName, projectId, botId, currentUser, Shared.Proto.PublisherMemberRole.Editor);
if (developer == null) return NotFound("Developer not found"); if (developer == null) return NotFound("Developer not found");
if (project == null) return NotFound("Project not found or you don't have access"); if (project == null) return NotFound("Project not found or you don't have access");
if (bot == null) return NotFound("Bot not found"); if (bot == null) return NotFound("Bot not found");
@@ -402,7 +402,7 @@ public class BotAccountController(
try try
{ {
var rotatedKey = await accountsReceiver.RotateApiKeyAsync(new GetApiKeyRequest { Id = keyId.ToString() }); var rotatedKey = await accountsReceiver.RotateApiKeyAsync(new GetApiKeyRequest { Id = keyId.ToString() });
return Ok(ApiKeyReference.FromProtoValue(rotatedKey)); return Ok(SnApiKey.FromProtoValue(rotatedKey));
} }
catch (RpcException ex) when (ex.StatusCode == Grpc.Core.StatusCode.NotFound) catch (RpcException ex) when (ex.StatusCode == Grpc.Core.StatusCode.NotFound)
{ {
@@ -420,7 +420,7 @@ public class BotAccountController(
if (HttpContext.Items["CurrentUser"] is not Account currentUser) if (HttpContext.Items["CurrentUser"] is not Account currentUser)
return Unauthorized(); return Unauthorized();
var (developer, project, bot) = await ValidateBotAccess(pubName, projectId, botId, currentUser, PublisherMemberRole.Editor); var (developer, project, bot) = await ValidateBotAccess(pubName, projectId, botId, currentUser, Shared.Proto.PublisherMemberRole.Editor);
if (developer == null) return NotFound("Developer not found"); if (developer == null) return NotFound("Developer not found");
if (project == null) return NotFound("Project not found or you don't have access"); if (project == null) return NotFound("Project not found or you don't have access");
if (bot == null) return NotFound("Bot not found"); if (bot == null) return NotFound("Bot not found");
@@ -436,12 +436,12 @@ public class BotAccountController(
} }
} }
private async Task<(Developer?, DevProject?, BotAccount?)> ValidateBotAccess( private async Task<(SnDeveloper?, SnDevProject?, SnBotAccount?)> ValidateBotAccess(
string pubName, string pubName,
Guid projectId, Guid projectId,
Guid botId, Guid botId,
Account currentUser, Account currentUser,
PublisherMemberRole requiredRole) Shared.Proto.PublisherMemberRole requiredRole)
{ {
var developer = await developerService.GetDeveloperByName(pubName); var developer = await developerService.GetDeveloperByName(pubName);
if (developer == null) return (null, null, null); if (developer == null) return (null, null, null);

View File

@@ -1,3 +1,4 @@
using DysonNetwork.Shared.Models;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace DysonNetwork.Develop.Identity; namespace DysonNetwork.Develop.Identity;
@@ -7,7 +8,7 @@ namespace DysonNetwork.Develop.Identity;
public class BotAccountPublicController(BotAccountService botService, DeveloperService developerService) : ControllerBase public class BotAccountPublicController(BotAccountService botService, DeveloperService developerService) : ControllerBase
{ {
[HttpGet("{botId:guid}")] [HttpGet("{botId:guid}")]
public async Task<ActionResult<BotAccount>> GetBotTransparentInfo([FromRoute] Guid botId) public async Task<ActionResult<SnBotAccount>> GetBotTransparentInfo([FromRoute] Guid botId)
{ {
var bot = await botService.GetBotByIdAsync(botId); var bot = await botService.GetBotByIdAsync(botId);
if (bot is null) return NotFound("Bot not found"); if (bot is null) return NotFound("Bot not found");
@@ -21,7 +22,7 @@ public class BotAccountPublicController(BotAccountService botService, DeveloperS
} }
[HttpGet("{botId:guid}/developer")] [HttpGet("{botId:guid}/developer")]
public async Task<ActionResult<Developer>> GetBotDeveloper([FromRoute] Guid botId) public async Task<ActionResult<SnDeveloper>> GetBotDeveloper([FromRoute] Guid botId)
{ {
var bot = await botService.GetBotByIdAsync(botId); var bot = await botService.GetBotByIdAsync(botId);
if (bot is null) return NotFound("Bot not found"); if (bot is null) return NotFound("Bot not found");

View File

@@ -1,5 +1,4 @@
using DysonNetwork.Develop.Project; using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Data;
using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Proto;
using DysonNetwork.Shared.Registry; using DysonNetwork.Shared.Registry;
using Grpc.Core; using Grpc.Core;
@@ -14,22 +13,22 @@ public class BotAccountService(
AccountClientHelper accounts AccountClientHelper accounts
) )
{ {
public async Task<BotAccount?> GetBotByIdAsync(Guid id) public async Task<SnBotAccount?> GetBotByIdAsync(Guid id)
{ {
return await db.BotAccounts return await db.BotAccounts
.Include(b => b.Project) .Include(b => b.Project)
.FirstOrDefaultAsync(b => b.Id == id); .FirstOrDefaultAsync(b => b.Id == id);
} }
public async Task<IEnumerable<BotAccount>> GetBotsByProjectAsync(Guid projectId) public async Task<IEnumerable<SnBotAccount>> GetBotsByProjectAsync(Guid projectId)
{ {
return await db.BotAccounts return await db.BotAccounts
.Where(b => b.ProjectId == projectId) .Where(b => b.ProjectId == projectId)
.ToListAsync(); .ToListAsync();
} }
public async Task<BotAccount> CreateBotAsync( public async Task<SnBotAccount> CreateBotAsync(
DevProject project, SnDevProject project,
string slug, string slug,
Account account, Account account,
string? pictureId, string? pictureId,
@@ -58,7 +57,7 @@ public class BotAccountService(
var botAccount = createResponse.Bot; var botAccount = createResponse.Bot;
// Then create the local bot account // Then create the local bot account
var bot = new BotAccount var bot = new SnBotAccount
{ {
Id = automatedId, Id = automatedId,
Slug = slug, Slug = slug,
@@ -89,8 +88,8 @@ public class BotAccountService(
} }
} }
public async Task<BotAccount> UpdateBotAsync( public async Task<SnBotAccount> UpdateBotAsync(
BotAccount bot, SnBotAccount bot,
Account account, Account account,
string? pictureId, string? pictureId,
string? backgroundId string? backgroundId
@@ -130,7 +129,7 @@ public class BotAccountService(
return bot; return bot;
} }
public async Task DeleteBotAsync(BotAccount bot) public async Task DeleteBotAsync(SnBotAccount bot)
{ {
try try
{ {
@@ -153,22 +152,22 @@ public class BotAccountService(
await db.SaveChangesAsync(); await db.SaveChangesAsync();
} }
public async Task<BotAccount?> LoadBotAccountAsync(BotAccount bot) => public async Task<SnBotAccount?> LoadBotAccountAsync(SnBotAccount bot) =>
(await LoadBotsAccountAsync([bot])).FirstOrDefault(); (await LoadBotsAccountAsync([bot])).FirstOrDefault();
public async Task<List<BotAccount>> LoadBotsAccountAsync(IEnumerable<BotAccount> bots) public async Task<List<SnBotAccount>> LoadBotsAccountAsync(IEnumerable<SnBotAccount> bots)
{ {
bots = bots.ToList(); bots = [.. bots];
var automatedIds = bots.Select(b => b.Id).ToList(); var automatedIds = bots.Select(b => b.Id).ToList();
var data = await accounts.GetBotAccountBatch(automatedIds); var data = await accounts.GetBotAccountBatch(automatedIds);
foreach (var bot in bots) foreach (var bot in bots)
{ {
bot.Account = data bot.Account = data
.Select(AccountReference.FromProtoValue) .Select(SnAccount.FromProtoValue)
.FirstOrDefault(e => e.AutomatedId == bot.Id); .FirstOrDefault(e => e.AutomatedId == bot.Id);
} }
return bots as List<BotAccount> ?? []; return bots as List<SnBotAccount> ?? [];
} }
} }

View File

@@ -1,5 +1,6 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using DysonNetwork.Develop.Project; using DysonNetwork.Develop.Project;
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Proto;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@@ -18,9 +19,9 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
[MaxLength(4096)] string? Description, [MaxLength(4096)] string? Description,
string? PictureId, string? PictureId,
string? BackgroundId, string? BackgroundId,
CustomAppStatus? Status, Shared.Models.CustomAppStatus? Status,
CustomAppLinks? Links, SnCustomAppLinks? Links,
CustomAppOauthConfig? OauthConfig SnCustomAppOauthConfig? OauthConfig
); );
public record CreateSecretRequest( public record CreateSecretRequest(
@@ -50,7 +51,7 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
if (developer is null) return NotFound(); if (developer is null) return NotFound();
var accountId = Guid.Parse(currentUser.Id); var accountId = Guid.Parse(currentUser.Id);
if (!await ds.IsMemberWithRole(developer.PublisherId, accountId, PublisherMemberRole.Viewer)) if (!await ds.IsMemberWithRole(developer.PublisherId, accountId, Shared.Proto.PublisherMemberRole.Viewer))
return StatusCode(403, "You must be a viewer of the developer to list custom apps"); return StatusCode(403, "You must be a viewer of the developer to list custom apps");
var project = await projectService.GetProjectAsync(projectId, developer.Id); var project = await projectService.GetProjectAsync(projectId, developer.Id);
@@ -72,7 +73,7 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
if (developer is null) return NotFound(); if (developer is null) return NotFound();
var accountId = Guid.Parse(currentUser.Id); var accountId = Guid.Parse(currentUser.Id);
if (!await ds.IsMemberWithRole(developer.PublisherId, accountId, PublisherMemberRole.Viewer)) if (!await ds.IsMemberWithRole(developer.PublisherId, accountId, Shared.Proto.PublisherMemberRole.Viewer))
return StatusCode(403, "You must be a viewer of the developer to list custom apps"); return StatusCode(403, "You must be a viewer of the developer to list custom apps");
var project = await projectService.GetProjectAsync(projectId, developer.Id); var project = await projectService.GetProjectAsync(projectId, developer.Id);
@@ -99,7 +100,7 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
if (developer is null) if (developer is null)
return NotFound("Developer not found"); return NotFound("Developer not found");
if (!await ds.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id), PublisherMemberRole.Editor)) if (!await ds.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id), Shared.Proto.PublisherMemberRole.Editor))
return StatusCode(403, "You must be an editor of the developer to create a custom app"); return StatusCode(403, "You must be an editor of the developer to create a custom app");
var project = await projectService.GetProjectAsync(projectId, developer.Id); var project = await projectService.GetProjectAsync(projectId, developer.Id);
@@ -143,7 +144,7 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
if (developer is null) if (developer is null)
return NotFound("Developer not found"); return NotFound("Developer not found");
if (!await ds.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id), PublisherMemberRole.Editor)) if (!await ds.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id), Shared.Proto.PublisherMemberRole.Editor))
return StatusCode(403, "You must be an editor of the developer to update a custom app"); return StatusCode(403, "You must be an editor of the developer to update a custom app");
var project = await projectService.GetProjectAsync(projectId, developer.Id); var project = await projectService.GetProjectAsync(projectId, developer.Id);
@@ -180,7 +181,7 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
if (developer is null) if (developer is null)
return NotFound("Developer not found"); return NotFound("Developer not found");
if (!await ds.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id), PublisherMemberRole.Editor)) if (!await ds.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id), Shared.Proto.PublisherMemberRole.Editor))
return StatusCode(403, "You must be an editor of the developer to delete a custom app"); return StatusCode(403, "You must be an editor of the developer to delete a custom app");
var project = await projectService.GetProjectAsync(projectId, developer.Id); var project = await projectService.GetProjectAsync(projectId, developer.Id);
@@ -212,7 +213,7 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
if (developer is null) if (developer is null)
return NotFound("Developer not found"); return NotFound("Developer not found");
if (!await ds.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id), PublisherMemberRole.Editor)) if (!await ds.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id), Shared.Proto.PublisherMemberRole.Editor))
return StatusCode(403, "You must be an editor of the developer to view app secrets"); return StatusCode(403, "You must be an editor of the developer to view app secrets");
var project = await projectService.GetProjectAsync(projectId, developer.Id); var project = await projectService.GetProjectAsync(projectId, developer.Id);
@@ -250,7 +251,7 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
if (developer is null) if (developer is null)
return NotFound("Developer not found"); return NotFound("Developer not found");
if (!await ds.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id), PublisherMemberRole.Editor)) if (!await ds.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id), Shared.Proto.PublisherMemberRole.Editor))
return StatusCode(403, "You must be an editor of the developer to create app secrets"); return StatusCode(403, "You must be an editor of the developer to create app secrets");
var project = await projectService.GetProjectAsync(projectId, developer.Id); var project = await projectService.GetProjectAsync(projectId, developer.Id);
@@ -263,7 +264,7 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
try try
{ {
var secret = await customApps.CreateAppSecretAsync(new CustomAppSecret var secret = await customApps.CreateAppSecretAsync(new SnCustomAppSecret
{ {
AppId = appId, AppId = appId,
Description = request.Description, Description = request.Description,
@@ -309,7 +310,7 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
if (developer is null) if (developer is null)
return NotFound("Developer not found"); return NotFound("Developer not found");
if (!await ds.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id), PublisherMemberRole.Editor)) if (!await ds.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id), Shared.Proto.PublisherMemberRole.Editor))
return StatusCode(403, "You must be an editor of the developer to view app secrets"); return StatusCode(403, "You must be an editor of the developer to view app secrets");
var project = await projectService.GetProjectAsync(projectId, developer.Id); var project = await projectService.GetProjectAsync(projectId, developer.Id);
@@ -350,7 +351,7 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
if (developer is null) if (developer is null)
return NotFound("Developer not found"); return NotFound("Developer not found");
if (!await ds.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id), PublisherMemberRole.Editor)) if (!await ds.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id), Shared.Proto.PublisherMemberRole.Editor))
return StatusCode(403, "You must be an editor of the developer to delete app secrets"); return StatusCode(403, "You must be an editor of the developer to delete app secrets");
var project = await projectService.GetProjectAsync(projectId, developer.Id); var project = await projectService.GetProjectAsync(projectId, developer.Id);
@@ -388,7 +389,7 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
if (developer is null) if (developer is null)
return NotFound("Developer not found"); return NotFound("Developer not found");
if (!await ds.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id), PublisherMemberRole.Editor)) if (!await ds.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id), Shared.Proto.PublisherMemberRole.Editor))
return StatusCode(403, "You must be an editor of the developer to rotate app secrets"); return StatusCode(403, "You must be an editor of the developer to rotate app secrets");
var project = await projectService.GetProjectAsync(projectId, developer.Id); var project = await projectService.GetProjectAsync(projectId, developer.Id);
@@ -401,7 +402,7 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
try try
{ {
var secret = await customApps.RotateAppSecretAsync(new CustomAppSecret var secret = await customApps.RotateAppSecretAsync(new SnCustomAppSecret
{ {
Id = secretId, Id = secretId,
AppId = appId, AppId = appId,

View File

@@ -1,5 +1,4 @@
using DysonNetwork.Develop.Project; using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Data;
using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Proto;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.Security.Cryptography; using System.Security.Cryptography;
@@ -13,7 +12,7 @@ public class CustomAppService(
FileService.FileServiceClient files FileService.FileServiceClient files
) )
{ {
public async Task<CustomApp?> CreateAppAsync( public async Task<SnCustomApp?> CreateAppAsync(
Guid projectId, Guid projectId,
CustomAppController.CustomAppRequest request CustomAppController.CustomAppRequest request
) )
@@ -25,12 +24,12 @@ public class CustomAppService(
if (project == null) if (project == null)
return null; return null;
var app = new CustomApp var app = new SnCustomApp
{ {
Slug = request.Slug!, Slug = request.Slug!,
Name = request.Name!, Name = request.Name!,
Description = request.Description, Description = request.Description,
Status = request.Status ?? CustomAppStatus.Developing, Status = request.Status ?? Shared.Models.CustomAppStatus.Developing,
Links = request.Links, Links = request.Links,
OauthConfig = request.OauthConfig, OauthConfig = request.OauthConfig,
ProjectId = projectId ProjectId = projectId
@@ -46,7 +45,7 @@ public class CustomAppService(
); );
if (picture is null) if (picture is null)
throw new InvalidOperationException("Invalid picture id, unable to find the file on cloud."); throw new InvalidOperationException("Invalid picture id, unable to find the file on cloud.");
app.Picture = CloudFileReferenceObject.FromProtoValue(picture); app.Picture = SnCloudFileReferenceObject.FromProtoValue(picture);
// Create a new reference // Create a new reference
await fileRefs.CreateReferenceAsync( await fileRefs.CreateReferenceAsync(
@@ -65,7 +64,7 @@ public class CustomAppService(
); );
if (background is null) if (background is null)
throw new InvalidOperationException("Invalid picture id, unable to find the file on cloud."); throw new InvalidOperationException("Invalid picture id, unable to find the file on cloud.");
app.Background = CloudFileReferenceObject.FromProtoValue(background); app.Background = SnCloudFileReferenceObject.FromProtoValue(background);
// Create a new reference // Create a new reference
await fileRefs.CreateReferenceAsync( await fileRefs.CreateReferenceAsync(
@@ -84,7 +83,7 @@ public class CustomAppService(
return app; return app;
} }
public async Task<CustomApp?> GetAppAsync(Guid id, Guid? projectId = null) public async Task<SnCustomApp?> GetAppAsync(Guid id, Guid? projectId = null)
{ {
var query = db.CustomApps.AsQueryable(); var query = db.CustomApps.AsQueryable();
@@ -96,7 +95,7 @@ public class CustomAppService(
return await query.FirstOrDefaultAsync(a => a.Id == id); return await query.FirstOrDefaultAsync(a => a.Id == id);
} }
public async Task<List<CustomAppSecret>> GetAppSecretsAsync(Guid appId) public async Task<List<SnCustomAppSecret>> GetAppSecretsAsync(Guid appId)
{ {
return await db.CustomAppSecrets return await db.CustomAppSecrets
.Where(s => s.AppId == appId) .Where(s => s.AppId == appId)
@@ -104,13 +103,13 @@ public class CustomAppService(
.ToListAsync(); .ToListAsync();
} }
public async Task<CustomAppSecret?> GetAppSecretAsync(Guid secretId, Guid appId) public async Task<SnCustomAppSecret?> GetAppSecretAsync(Guid secretId, Guid appId)
{ {
return await db.CustomAppSecrets return await db.CustomAppSecrets
.FirstOrDefaultAsync(s => s.Id == secretId && s.AppId == appId); .FirstOrDefaultAsync(s => s.Id == secretId && s.AppId == appId);
} }
public async Task<CustomAppSecret> CreateAppSecretAsync(CustomAppSecret secret) public async Task<SnCustomAppSecret> CreateAppSecretAsync(SnCustomAppSecret secret)
{ {
if (string.IsNullOrWhiteSpace(secret.Secret)) if (string.IsNullOrWhiteSpace(secret.Secret))
{ {
@@ -141,7 +140,7 @@ public class CustomAppService(
return true; return true;
} }
public async Task<CustomAppSecret> RotateAppSecretAsync(CustomAppSecret secretUpdate) public async Task<SnCustomAppSecret> RotateAppSecretAsync(SnCustomAppSecret secretUpdate)
{ {
var existingSecret = await db.CustomAppSecrets var existingSecret = await db.CustomAppSecrets
.FirstOrDefaultAsync(s => s.Id == secretUpdate.Id && s.AppId == secretUpdate.AppId); .FirstOrDefaultAsync(s => s.Id == secretUpdate.Id && s.AppId == secretUpdate.AppId);
@@ -177,14 +176,14 @@ public class CustomAppService(
return res.ToString(); return res.ToString();
} }
public async Task<List<CustomApp>> GetAppsByProjectAsync(Guid projectId) public async Task<List<SnCustomApp>> GetAppsByProjectAsync(Guid projectId)
{ {
return await db.CustomApps return await db.CustomApps
.Where(a => a.ProjectId == projectId) .Where(a => a.ProjectId == projectId)
.ToListAsync(); .ToListAsync();
} }
public async Task<CustomApp?> UpdateAppAsync(CustomApp app, CustomAppController.CustomAppRequest request) public async Task<SnCustomApp?> UpdateAppAsync(SnCustomApp app, CustomAppController.CustomAppRequest request)
{ {
if (request.Slug is not null) if (request.Slug is not null)
app.Slug = request.Slug; app.Slug = request.Slug;
@@ -209,7 +208,7 @@ public class CustomAppService(
); );
if (picture is null) if (picture is null)
throw new InvalidOperationException("Invalid picture id, unable to find the file on cloud."); throw new InvalidOperationException("Invalid picture id, unable to find the file on cloud.");
app.Picture = CloudFileReferenceObject.FromProtoValue(picture); app.Picture = SnCloudFileReferenceObject.FromProtoValue(picture);
// Create a new reference // Create a new reference
await fileRefs.CreateReferenceAsync( await fileRefs.CreateReferenceAsync(
@@ -228,7 +227,7 @@ public class CustomAppService(
); );
if (background is null) if (background is null)
throw new InvalidOperationException("Invalid picture id, unable to find the file on cloud."); throw new InvalidOperationException("Invalid picture id, unable to find the file on cloud.");
app.Background = CloudFileReferenceObject.FromProtoValue(background); app.Background = SnCloudFileReferenceObject.FromProtoValue(background);
// Create a new reference // Create a new reference
await fileRefs.CreateReferenceAsync( await fileRefs.CreateReferenceAsync(

View File

@@ -1,3 +1,4 @@
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Proto;
using Grpc.Core; using Grpc.Core;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@@ -37,7 +38,7 @@ public class CustomAppServiceGrpc(AppDatabase db) : Shared.Proto.CustomAppServic
if (string.IsNullOrEmpty(request.Secret)) if (string.IsNullOrEmpty(request.Secret))
throw new RpcException(new Status(StatusCode.InvalidArgument, "secret required")); throw new RpcException(new Status(StatusCode.InvalidArgument, "secret required"));
IQueryable<CustomAppSecret> q = db.CustomAppSecrets; IQueryable<SnCustomAppSecret> q = db.CustomAppSecrets;
switch (request.SecretIdentifierCase) switch (request.SecretIdentifierCase)
{ {
case CheckCustomAppSecretRequest.SecretIdentifierOneofCase.SecretId: case CheckCustomAppSecretRequest.SecretIdentifierOneofCase.SecretId:

View File

@@ -1,79 +0,0 @@
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;
using DysonNetwork.Develop.Project;
using DysonNetwork.Shared.Proto;
using DysonNetwork.Shared.Data;
using VerificationMark = DysonNetwork.Shared.Data.VerificationMark;
namespace DysonNetwork.Develop.Identity;
public class Developer
{
public Guid Id { get; set; } = Guid.NewGuid();
public Guid PublisherId { get; set; }
[JsonIgnore] public List<DevProject> Projects { get; set; } = [];
[NotMapped] public PublisherInfo? Publisher { get; set; }
}
public class PublisherInfo
{
public Guid Id { get; set; }
public PublisherType Type { get; set; }
public string Name { get; set; } = string.Empty;
public string Nick { get; set; } = string.Empty;
public string? Bio { get; set; }
public CloudFileReferenceObject? Picture { get; set; }
public CloudFileReferenceObject? Background { get; set; }
public VerificationMark? Verification { get; set; }
public Guid? AccountId { get; set; }
public Guid? RealmId { get; set; }
public static PublisherInfo FromProto(Publisher proto)
{
var info = new PublisherInfo
{
Id = Guid.Parse(proto.Id),
Type = proto.Type == PublisherType.PubIndividual
? PublisherType.PubIndividual
: PublisherType.PubOrganizational,
Name = proto.Name,
Nick = proto.Nick,
Bio = string.IsNullOrEmpty(proto.Bio) ? null : proto.Bio,
Verification = proto.VerificationMark is not null
? VerificationMark.FromProtoValue(proto.VerificationMark)
: null,
AccountId = string.IsNullOrEmpty(proto.AccountId) ? null : Guid.Parse(proto.AccountId),
RealmId = string.IsNullOrEmpty(proto.RealmId) ? null : Guid.Parse(proto.RealmId)
};
if (proto.Picture != null)
{
info.Picture = new CloudFileReferenceObject
{
Id = proto.Picture.Id,
Name = proto.Picture.Name,
MimeType = proto.Picture.MimeType,
Hash = proto.Picture.Hash,
Size = proto.Picture.Size
};
}
if (proto.Background != null)
{
info.Background = new CloudFileReferenceObject
{
Id = proto.Background.Id,
Name = proto.Background.Name,
MimeType = proto.Background.MimeType,
Hash = proto.Background.Hash,
Size = (long)proto.Background.Size
};
}
return info;
}
}

View File

@@ -1,4 +1,5 @@
using DysonNetwork.Shared.Auth; using DysonNetwork.Shared.Auth;
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Proto;
using Grpc.Core; using Grpc.Core;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
@@ -18,7 +19,7 @@ public class DeveloperController(
: ControllerBase : ControllerBase
{ {
[HttpGet("{name}")] [HttpGet("{name}")]
public async Task<ActionResult<Developer>> GetDeveloper(string name) public async Task<ActionResult<SnDeveloper>> GetDeveloper(string name)
{ {
var developer = await ds.GetDeveloperByName(name); var developer = await ds.GetDeveloperByName(name);
if (developer is null) return NotFound(); if (developer is null) return NotFound();
@@ -47,7 +48,7 @@ public class DeveloperController(
[HttpGet] [HttpGet]
[Authorize] [Authorize]
public async Task<ActionResult<List<Developer>>> ListJoinedDevelopers() public async Task<ActionResult<List<SnDeveloper>>> ListJoinedDevelopers()
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
@@ -69,16 +70,16 @@ public class DeveloperController(
[HttpPost("{name}/enroll")] [HttpPost("{name}/enroll")]
[Authorize] [Authorize]
[RequiredPermission("global", "developers.create")] [RequiredPermission("global", "developers.create")]
public async Task<ActionResult<Developer>> EnrollDeveloperProgram(string name) public async Task<ActionResult<SnDeveloper>> EnrollDeveloperProgram(string name)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
var accountId = Guid.Parse(currentUser.Id); var accountId = Guid.Parse(currentUser.Id);
PublisherInfo? pub; SnPublisher? pub;
try try
{ {
var pubResponse = await ps.GetPublisherAsync(new GetPublisherRequest { Name = name }); var pubResponse = await ps.GetPublisherAsync(new GetPublisherRequest { Name = name });
pub = PublisherInfo.FromProto(pubResponse.Publisher); pub = SnPublisher.FromProto(pubResponse.Publisher);
} catch (RpcException ex) } catch (RpcException ex)
{ {
return NotFound(ex.Status.Detail); return NotFound(ex.Status.Detail);
@@ -89,14 +90,14 @@ public class DeveloperController(
{ {
PublisherId = pub.Id.ToString(), PublisherId = pub.Id.ToString(),
AccountId = currentUser.Id, AccountId = currentUser.Id,
Role = PublisherMemberRole.Owner Role = Shared.Proto.PublisherMemberRole.Owner
}); });
if (!permResponse.Valid) return StatusCode(403, "You must be the owner of the publisher to join the developer program"); if (!permResponse.Valid) return StatusCode(403, "You must be the owner of the publisher to join the developer program");
var hasDeveloper = await db.Developers.AnyAsync(d => d.PublisherId == pub.Id); var hasDeveloper = await db.Developers.AnyAsync(d => d.PublisherId == pub.Id);
if (hasDeveloper) return BadRequest("Publisher is already in the developer program"); if (hasDeveloper) return BadRequest("Publisher is already in the developer program");
var developer = new Developer var developer = new SnDeveloper
{ {
Id = Guid.NewGuid(), Id = Guid.NewGuid(),
PublisherId = pub.Id PublisherId = pub.Id

View File

@@ -1,3 +1,4 @@
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Proto;
using Grpc.Core; using Grpc.Core;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@@ -9,22 +10,22 @@ public class DeveloperService(
PublisherService.PublisherServiceClient ps, PublisherService.PublisherServiceClient ps,
ILogger<DeveloperService> logger) ILogger<DeveloperService> logger)
{ {
public async Task<Developer> LoadDeveloperPublisher(Developer developer) public async Task<SnDeveloper> LoadDeveloperPublisher(SnDeveloper developer)
{ {
var pubResponse = await ps.GetPublisherAsync(new GetPublisherRequest { Id = developer.PublisherId.ToString() }); var pubResponse = await ps.GetPublisherAsync(new GetPublisherRequest { Id = developer.PublisherId.ToString() });
developer.Publisher = PublisherInfo.FromProto(pubResponse.Publisher); developer.Publisher = SnPublisher.FromProto(pubResponse.Publisher);
return developer; return developer;
} }
public async Task<IEnumerable<Developer>> LoadDeveloperPublisher(IEnumerable<Developer> developers) public async Task<IEnumerable<SnDeveloper>> LoadDeveloperPublisher(IEnumerable<SnDeveloper> developers)
{ {
var enumerable = developers.ToList(); var enumerable = developers.ToList();
var pubIds = enumerable.Select(d => d.PublisherId).ToList(); var pubIds = enumerable.Select(d => d.PublisherId).ToList();
var pubRequest = new GetPublisherBatchRequest(); var pubRequest = new GetPublisherBatchRequest();
pubIds.ForEach(x => pubRequest.Ids.Add(x.ToString())); pubIds.ForEach(x => pubRequest.Ids.Add(x.ToString()));
var pubResponse = await ps.GetPublisherBatchAsync(pubRequest); var pubResponse = await ps.GetPublisherBatchAsync(pubRequest);
var pubs = pubResponse.Publishers.ToDictionary(p => Guid.Parse(p.Id), PublisherInfo.FromProto); var pubs = pubResponse.Publishers.ToDictionary(p => Guid.Parse(p.Id), SnPublisher.FromProto);
return enumerable.Select(d => return enumerable.Select(d =>
{ {
@@ -33,7 +34,7 @@ public class DeveloperService(
}); });
} }
public async Task<Developer?> GetDeveloperByName(string name) public async Task<SnDeveloper?> GetDeveloperByName(string name)
{ {
try try
{ {
@@ -50,12 +51,12 @@ public class DeveloperService(
} }
} }
public async Task<Developer?> GetDeveloperById(Guid id) public async Task<SnDeveloper?> GetDeveloperById(Guid id)
{ {
return await db.Developers.FirstOrDefaultAsync(d => d.Id == id); return await db.Developers.FirstOrDefaultAsync(d => d.Id == id);
} }
public async Task<bool> IsMemberWithRole(Guid pubId, Guid accountId, PublisherMemberRole role) public async Task<bool> IsMemberWithRole(Guid pubId, Guid accountId, Shared.Proto.PublisherMemberRole role)
{ {
try try
{ {

View File

@@ -1,8 +1,7 @@
// <auto-generated /> // <auto-generated />
using System; using System;
using DysonNetwork.Develop; using DysonNetwork.Develop;
using DysonNetwork.Develop.Identity; using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
@@ -35,7 +34,7 @@ namespace DysonNetwork.Develop.Migrations
.HasColumnType("uuid") .HasColumnType("uuid")
.HasColumnName("id"); .HasColumnName("id");
b.Property<CloudFileReferenceObject>("Background") b.Property<SnCloudFileReferenceObject>("Background")
.HasColumnType("jsonb") .HasColumnType("jsonb")
.HasColumnName("background"); .HasColumnName("background");
@@ -56,7 +55,7 @@ namespace DysonNetwork.Develop.Migrations
.HasColumnType("uuid") .HasColumnType("uuid")
.HasColumnName("developer_id"); .HasColumnName("developer_id");
b.Property<CustomAppLinks>("Links") b.Property<SnCustomAppLinks>("Links")
.HasColumnType("jsonb") .HasColumnType("jsonb")
.HasColumnName("links"); .HasColumnName("links");
@@ -66,11 +65,11 @@ namespace DysonNetwork.Develop.Migrations
.HasColumnType("character varying(1024)") .HasColumnType("character varying(1024)")
.HasColumnName("name"); .HasColumnName("name");
b.Property<CustomAppOauthConfig>("OauthConfig") b.Property<SnCustomAppOauthConfig>("OauthConfig")
.HasColumnType("jsonb") .HasColumnType("jsonb")
.HasColumnName("oauth_config"); .HasColumnName("oauth_config");
b.Property<CloudFileReferenceObject>("Picture") b.Property<SnCloudFileReferenceObject>("Picture")
.HasColumnType("jsonb") .HasColumnType("jsonb")
.HasColumnName("picture"); .HasColumnName("picture");
@@ -88,7 +87,7 @@ namespace DysonNetwork.Develop.Migrations
.HasColumnType("timestamp with time zone") .HasColumnType("timestamp with time zone")
.HasColumnName("updated_at"); .HasColumnName("updated_at");
b.Property<VerificationMark>("Verification") b.Property<SnVerificationMark>("Verification")
.HasColumnType("jsonb") .HasColumnType("jsonb")
.HasColumnName("verification"); .HasColumnName("verification");

View File

@@ -1,6 +1,4 @@
using System; using DysonNetwork.Shared.Models;
using DysonNetwork.Develop.Identity;
using DysonNetwork.Shared.Data;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
using NodaTime; using NodaTime;
@@ -35,11 +33,11 @@ namespace DysonNetwork.Develop.Migrations
name = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: false), name = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: false),
description = table.Column<string>(type: "character varying(4096)", maxLength: 4096, nullable: true), description = table.Column<string>(type: "character varying(4096)", maxLength: 4096, nullable: true),
status = table.Column<int>(type: "integer", nullable: false), status = table.Column<int>(type: "integer", nullable: false),
picture = table.Column<CloudFileReferenceObject>(type: "jsonb", nullable: true), picture = table.Column<SnCloudFileReferenceObject>(type: "jsonb", nullable: true),
background = table.Column<CloudFileReferenceObject>(type: "jsonb", nullable: true), background = table.Column<SnCloudFileReferenceObject>(type: "jsonb", nullable: true),
verification = table.Column<VerificationMark>(type: "jsonb", nullable: true), verification = table.Column<SnVerificationMark>(type: "jsonb", nullable: true),
oauth_config = table.Column<CustomAppOauthConfig>(type: "jsonb", nullable: true), oauth_config = table.Column<SnCustomAppOauthConfig>(type: "jsonb", nullable: true),
links = table.Column<CustomAppLinks>(type: "jsonb", nullable: true), links = table.Column<SnCustomAppLinks>(type: "jsonb", nullable: true),
developer_id = table.Column<Guid>(type: "uuid", nullable: false), developer_id = table.Column<Guid>(type: "uuid", nullable: false),
created_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false), created_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
updated_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false), updated_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),

View File

@@ -1,8 +1,7 @@
// <auto-generated /> // <auto-generated />
using System; using System;
using DysonNetwork.Develop; using DysonNetwork.Develop;
using DysonNetwork.Develop.Identity; using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
@@ -35,7 +34,7 @@ namespace DysonNetwork.Develop.Migrations
.HasColumnType("uuid") .HasColumnType("uuid")
.HasColumnName("id"); .HasColumnName("id");
b.Property<CloudFileReferenceObject>("Background") b.Property<SnCloudFileReferenceObject>("Background")
.HasColumnType("jsonb") .HasColumnType("jsonb")
.HasColumnName("background"); .HasColumnName("background");
@@ -52,7 +51,7 @@ namespace DysonNetwork.Develop.Migrations
.HasColumnType("character varying(4096)") .HasColumnType("character varying(4096)")
.HasColumnName("description"); .HasColumnName("description");
b.Property<CustomAppLinks>("Links") b.Property<SnCustomAppLinks>("Links")
.HasColumnType("jsonb") .HasColumnType("jsonb")
.HasColumnName("links"); .HasColumnName("links");
@@ -62,11 +61,11 @@ namespace DysonNetwork.Develop.Migrations
.HasColumnType("character varying(1024)") .HasColumnType("character varying(1024)")
.HasColumnName("name"); .HasColumnName("name");
b.Property<CustomAppOauthConfig>("OauthConfig") b.Property<SnCustomAppOauthConfig>("OauthConfig")
.HasColumnType("jsonb") .HasColumnType("jsonb")
.HasColumnName("oauth_config"); .HasColumnName("oauth_config");
b.Property<CloudFileReferenceObject>("Picture") b.Property<SnCloudFileReferenceObject>("Picture")
.HasColumnType("jsonb") .HasColumnType("jsonb")
.HasColumnName("picture"); .HasColumnName("picture");
@@ -88,7 +87,7 @@ namespace DysonNetwork.Develop.Migrations
.HasColumnType("timestamp with time zone") .HasColumnType("timestamp with time zone")
.HasColumnName("updated_at"); .HasColumnName("updated_at");
b.Property<VerificationMark>("Verification") b.Property<SnVerificationMark>("Verification")
.HasColumnType("jsonb") .HasColumnType("jsonb")
.HasColumnName("verification"); .HasColumnName("verification");

View File

@@ -1,5 +1,4 @@
using System; using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
using NodaTime; using NodaTime;
#nullable disable #nullable disable

View File

@@ -1,8 +1,7 @@
// <auto-generated /> // <auto-generated />
using System; using System;
using DysonNetwork.Develop; using DysonNetwork.Develop;
using DysonNetwork.Develop.Identity; using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
@@ -77,7 +76,7 @@ namespace DysonNetwork.Develop.Migrations
.HasColumnType("uuid") .HasColumnType("uuid")
.HasColumnName("id"); .HasColumnName("id");
b.Property<CloudFileReferenceObject>("Background") b.Property<SnCloudFileReferenceObject>("Background")
.HasColumnType("jsonb") .HasColumnType("jsonb")
.HasColumnName("background"); .HasColumnName("background");
@@ -94,7 +93,7 @@ namespace DysonNetwork.Develop.Migrations
.HasColumnType("character varying(4096)") .HasColumnType("character varying(4096)")
.HasColumnName("description"); .HasColumnName("description");
b.Property<CustomAppLinks>("Links") b.Property<SnCustomAppLinks>("Links")
.HasColumnType("jsonb") .HasColumnType("jsonb")
.HasColumnName("links"); .HasColumnName("links");
@@ -104,11 +103,11 @@ namespace DysonNetwork.Develop.Migrations
.HasColumnType("character varying(1024)") .HasColumnType("character varying(1024)")
.HasColumnName("name"); .HasColumnName("name");
b.Property<CustomAppOauthConfig>("OauthConfig") b.Property<SnCustomAppOauthConfig>("OauthConfig")
.HasColumnType("jsonb") .HasColumnType("jsonb")
.HasColumnName("oauth_config"); .HasColumnName("oauth_config");
b.Property<CloudFileReferenceObject>("Picture") b.Property<SnCloudFileReferenceObject>("Picture")
.HasColumnType("jsonb") .HasColumnType("jsonb")
.HasColumnName("picture"); .HasColumnName("picture");
@@ -130,7 +129,7 @@ namespace DysonNetwork.Develop.Migrations
.HasColumnType("timestamp with time zone") .HasColumnType("timestamp with time zone")
.HasColumnName("updated_at"); .HasColumnName("updated_at");
b.Property<VerificationMark>("Verification") b.Property<SnVerificationMark>("Verification")
.HasColumnType("jsonb") .HasColumnType("jsonb")
.HasColumnName("verification"); .HasColumnName("verification");

View File

@@ -1,5 +1,4 @@
using System; using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
using NodaTime; using NodaTime;
#nullable disable #nullable disable

View File

@@ -1,8 +1,7 @@
// <auto-generated /> // <auto-generated />
using System; using System;
using DysonNetwork.Develop; using DysonNetwork.Develop;
using DysonNetwork.Develop.Identity; using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
@@ -74,7 +73,7 @@ namespace DysonNetwork.Develop.Migrations
.HasColumnType("uuid") .HasColumnType("uuid")
.HasColumnName("id"); .HasColumnName("id");
b.Property<CloudFileReferenceObject>("Background") b.Property<SnCloudFileReferenceObject>("Background")
.HasColumnType("jsonb") .HasColumnType("jsonb")
.HasColumnName("background"); .HasColumnName("background");
@@ -91,7 +90,7 @@ namespace DysonNetwork.Develop.Migrations
.HasColumnType("character varying(4096)") .HasColumnType("character varying(4096)")
.HasColumnName("description"); .HasColumnName("description");
b.Property<CustomAppLinks>("Links") b.Property<SnCustomAppLinks>("Links")
.HasColumnType("jsonb") .HasColumnType("jsonb")
.HasColumnName("links"); .HasColumnName("links");
@@ -101,11 +100,11 @@ namespace DysonNetwork.Develop.Migrations
.HasColumnType("character varying(1024)") .HasColumnType("character varying(1024)")
.HasColumnName("name"); .HasColumnName("name");
b.Property<CustomAppOauthConfig>("OauthConfig") b.Property<SnCustomAppOauthConfig>("OauthConfig")
.HasColumnType("jsonb") .HasColumnType("jsonb")
.HasColumnName("oauth_config"); .HasColumnName("oauth_config");
b.Property<CloudFileReferenceObject>("Picture") b.Property<SnCloudFileReferenceObject>("Picture")
.HasColumnType("jsonb") .HasColumnType("jsonb")
.HasColumnName("picture"); .HasColumnName("picture");
@@ -127,7 +126,7 @@ namespace DysonNetwork.Develop.Migrations
.HasColumnType("timestamp with time zone") .HasColumnType("timestamp with time zone")
.HasColumnName("updated_at"); .HasColumnName("updated_at");
b.Property<VerificationMark>("Verification") b.Property<SnVerificationMark>("Verification")
.HasColumnType("jsonb") .HasColumnType("jsonb")
.HasColumnName("verification"); .HasColumnName("verification");

View File

@@ -1,6 +1,6 @@
using DysonNetwork.Develop.Identity;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Proto;
using DysonNetwork.Shared.Models;
namespace DysonNetwork.Develop.Project; namespace DysonNetwork.Develop.Project;
@@ -10,12 +10,12 @@ public class DevProjectService(
FileService.FileServiceClient files FileService.FileServiceClient files
) )
{ {
public async Task<DevProject> CreateProjectAsync( public async Task<SnDevProject> CreateProjectAsync(
Developer developer, SnDeveloper developer,
DevProjectController.DevProjectRequest request DevProjectController.DevProjectRequest request
) )
{ {
var project = new DevProject var project = new SnDevProject
{ {
Slug = request.Slug!, Slug = request.Slug!,
Name = request.Name!, Name = request.Name!,
@@ -29,7 +29,7 @@ public class DevProjectService(
return project; return project;
} }
public async Task<DevProject?> GetProjectAsync(Guid id, Guid? developerId = null) public async Task<SnDevProject?> GetProjectAsync(Guid id, Guid? developerId = null)
{ {
var query = db.DevProjects.AsQueryable(); var query = db.DevProjects.AsQueryable();
@@ -41,14 +41,14 @@ public class DevProjectService(
return await query.FirstOrDefaultAsync(p => p.Id == id); return await query.FirstOrDefaultAsync(p => p.Id == id);
} }
public async Task<List<DevProject>> GetProjectsByDeveloperAsync(Guid developerId) public async Task<List<SnDevProject>> GetProjectsByDeveloperAsync(Guid developerId)
{ {
return await db.DevProjects return await db.DevProjects
.Where(p => p.DeveloperId == developerId) .Where(p => p.DeveloperId == developerId)
.ToListAsync(); .ToListAsync();
} }
public async Task<DevProject?> UpdateProjectAsync( public async Task<SnDevProject?> UpdateProjectAsync(
Guid id, Guid id,
Guid developerId, Guid developerId,
DevProjectController.DevProjectRequest request DevProjectController.DevProjectRequest request

View File

@@ -1,9 +1,6 @@
using System.Net;
using DysonNetwork.Develop.Identity; using DysonNetwork.Develop.Identity;
using DysonNetwork.Shared.Auth; using DysonNetwork.Shared.Auth;
using DysonNetwork.Shared.Http; using DysonNetwork.Shared.Http;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.OpenApi.Models;
using Prometheus; using Prometheus;
namespace DysonNetwork.Develop.Startup; namespace DysonNetwork.Develop.Startup;

View File

@@ -1,5 +1,4 @@
using System.Globalization; using System.Globalization;
using Microsoft.OpenApi.Models;
using NodaTime; using NodaTime;
using NodaTime.Serialization.SystemTextJson; using NodaTime.Serialization.SystemTextJson;
using System.Text.Json; using System.Text.Json;
@@ -7,7 +6,6 @@ using System.Text.Json.Serialization;
using DysonNetwork.Develop.Identity; using DysonNetwork.Develop.Identity;
using DysonNetwork.Develop.Project; using DysonNetwork.Develop.Project;
using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Cache;
using StackExchange.Redis;
namespace DysonNetwork.Develop.Startup; namespace DysonNetwork.Develop.Startup;

View File

@@ -1,8 +1,7 @@
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using DysonNetwork.Drive.Billing; using DysonNetwork.Drive.Billing;
using DysonNetwork.Drive.Storage; using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design; using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Query;
@@ -17,11 +16,11 @@ public class AppDatabase(
) : DbContext(options) ) : DbContext(options)
{ {
public DbSet<FilePool> Pools { get; set; } = null!; public DbSet<FilePool> Pools { get; set; } = null!;
public DbSet<FileBundle> Bundles { get; set; } = null!; public DbSet<SnFileBundle> Bundles { get; set; } = null!;
public DbSet<QuotaRecord> QuotaRecords { get; set; } = null!; public DbSet<QuotaRecord> QuotaRecords { get; set; } = null!;
public DbSet<CloudFile> Files { get; set; } = null!; public DbSet<SnCloudFile> Files { get; set; } = null!;
public DbSet<CloudFileReference> FileReferences { get; set; } = null!; public DbSet<CloudFileReference> FileReferences { get; set; } = null!;
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)

View File

@@ -1,4 +1,4 @@
using DysonNetwork.Shared.Data; using DysonNetwork.Shared.Models;
using NodaTime; using NodaTime;
namespace DysonNetwork.Drive.Billing; namespace DysonNetwork.Drive.Billing;

View File

@@ -3,7 +3,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using DysonNetwork.Drive; using DysonNetwork.Drive;
using DysonNetwork.Drive.Storage; using DysonNetwork.Drive.Storage;
using DysonNetwork.Shared.Data; using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;

View File

@@ -1,7 +1,4 @@
using System; using DysonNetwork.Shared.Models;
using System.Collections.Generic;
using DysonNetwork.Drive.Storage;
using DysonNetwork.Shared.Data;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
using NodaTime; using NodaTime;

View File

@@ -2,7 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using DysonNetwork.Drive; using DysonNetwork.Drive;
using DysonNetwork.Shared.Data; using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic; using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable #nullable disable

View File

@@ -2,8 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using DysonNetwork.Drive; using DysonNetwork.Drive;
using DysonNetwork.Drive.Storage; using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;

View File

@@ -1,6 +1,4 @@
using System; using DysonNetwork.Shared.Models;
using System.Collections.Generic;
using DysonNetwork.Drive.Storage;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
using NodaTime; using NodaTime;

View File

@@ -2,8 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using DysonNetwork.Drive; using DysonNetwork.Drive;
using DysonNetwork.Drive.Storage; using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;

View File

@@ -2,8 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using DysonNetwork.Drive; using DysonNetwork.Drive;
using DysonNetwork.Drive.Storage; using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;

View File

@@ -2,8 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using DysonNetwork.Drive; using DysonNetwork.Drive;
using DysonNetwork.Drive.Storage; using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;

View File

@@ -1,5 +1,4 @@
using System; using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
using NodaTime; using NodaTime;
#nullable disable #nullable disable

View File

@@ -2,8 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using DysonNetwork.Drive; using DysonNetwork.Drive;
using DysonNetwork.Drive.Storage; using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;

View File

@@ -1,5 +1,4 @@
using System; using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
using NodaTime; using NodaTime;
#nullable disable #nullable disable

View File

@@ -2,8 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using DysonNetwork.Drive; using DysonNetwork.Drive;
using DysonNetwork.Drive.Storage; using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;

View File

@@ -2,8 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using DysonNetwork.Drive; using DysonNetwork.Drive;
using DysonNetwork.Drive.Storage; using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;

View File

@@ -2,8 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using DysonNetwork.Drive; using DysonNetwork.Drive;
using DysonNetwork.Drive.Storage; using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;

View File

@@ -2,8 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using DysonNetwork.Drive; using DysonNetwork.Drive;
using DysonNetwork.Drive.Storage; using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

View File

@@ -3,11 +3,8 @@ using System.Text.Json.Serialization;
using System.Threading.RateLimiting; using System.Threading.RateLimiting;
using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Cache;
using Microsoft.AspNetCore.RateLimiting; using Microsoft.AspNetCore.RateLimiting;
using Microsoft.OpenApi.Models;
using NodaTime; using NodaTime;
using NodaTime.Serialization.SystemTextJson; using NodaTime.Serialization.SystemTextJson;
using StackExchange.Redis;
using DysonNetwork.Shared.Proto;
using tusdotnet.Stores; using tusdotnet.Stores;
namespace DysonNetwork.Drive.Startup; namespace DysonNetwork.Drive.Startup;

View File

@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Proto;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@@ -22,7 +23,7 @@ public class BundleController(AppDatabase db) : ControllerBase
} }
[HttpGet("{id:guid}")] [HttpGet("{id:guid}")]
public async Task<ActionResult<FileBundle>> GetBundle([FromRoute] Guid id, [FromQuery] string? passcode) public async Task<ActionResult<SnFileBundle>> GetBundle([FromRoute] Guid id, [FromQuery] string? passcode)
{ {
var bundle = await db.Bundles var bundle = await db.Bundles
.Where(e => e.Id == id) .Where(e => e.Id == id)
@@ -36,7 +37,7 @@ public class BundleController(AppDatabase db) : ControllerBase
[HttpGet("me")] [HttpGet("me")]
[Authorize] [Authorize]
public async Task<ActionResult<List<FileBundle>>> ListBundles( public async Task<ActionResult<List<SnFileBundle>>> ListBundles(
[FromQuery] string? term, [FromQuery] string? term,
[FromQuery] int offset = 0, [FromQuery] int offset = 0,
[FromQuery] int take = 20 [FromQuery] int take = 20
@@ -65,7 +66,7 @@ public class BundleController(AppDatabase db) : ControllerBase
[HttpPost] [HttpPost]
[Authorize] [Authorize]
public async Task<ActionResult<FileBundle>> CreateBundle([FromBody] BundleRequest request) public async Task<ActionResult<SnFileBundle>> CreateBundle([FromBody] BundleRequest request)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
var accountId = Guid.Parse(currentUser.Id); var accountId = Guid.Parse(currentUser.Id);
@@ -77,7 +78,7 @@ public class BundleController(AppDatabase db) : ControllerBase
if (string.IsNullOrEmpty(request.Name)) if (string.IsNullOrEmpty(request.Name))
request.Name = "Unnamed Bundle"; request.Name = "Unnamed Bundle";
var bundle = new FileBundle var bundle = new SnFileBundle
{ {
Slug = request.Slug, Slug = request.Slug,
Name = request.Name, Name = request.Name,
@@ -95,7 +96,7 @@ public class BundleController(AppDatabase db) : ControllerBase
[HttpPut("{id:guid}")] [HttpPut("{id:guid}")]
[Authorize] [Authorize]
public async Task<ActionResult<FileBundle>> UpdateBundle([FromRoute] Guid id, [FromBody] BundleRequest request) public async Task<ActionResult<SnFileBundle>> UpdateBundle([FromRoute] Guid id, [FromBody] BundleRequest request)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
var accountId = Guid.Parse(currentUser.Id); var accountId = Guid.Parse(currentUser.Id);

View File

@@ -1,6 +1,6 @@
using DysonNetwork.Drive.Billing; using DysonNetwork.Drive.Billing;
using DysonNetwork.Shared.Auth; using DysonNetwork.Shared.Auth;
using DysonNetwork.Shared.Data; using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Proto;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@@ -165,7 +165,7 @@ public class FileController(
} }
[HttpGet("{id}/info")] [HttpGet("{id}/info")]
public async Task<ActionResult<CloudFile>> GetFileInfo(string id) public async Task<ActionResult<SnCloudFile>> GetFileInfo(string id)
{ {
var file = await fs.GetFileAsync(id); var file = await fs.GetFileAsync(id);
if (file is null) return NotFound("File not found."); if (file is null) return NotFound("File not found.");
@@ -175,7 +175,7 @@ public class FileController(
[Authorize] [Authorize]
[HttpPatch("{id}/name")] [HttpPatch("{id}/name")]
public async Task<ActionResult<CloudFile>> UpdateFileName(string id, [FromBody] string name) public async Task<ActionResult<SnCloudFile>> UpdateFileName(string id, [FromBody] string name)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
var accountId = Guid.Parse(currentUser.Id); var accountId = Guid.Parse(currentUser.Id);
@@ -194,7 +194,7 @@ public class FileController(
[Authorize] [Authorize]
[HttpPut("{id}/marks")] [HttpPut("{id}/marks")]
public async Task<ActionResult<CloudFile>> MarkFile(string id, [FromBody] MarkFileRequest request) public async Task<ActionResult<SnCloudFile>> MarkFile(string id, [FromBody] MarkFileRequest request)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
var accountId = Guid.Parse(currentUser.Id); var accountId = Guid.Parse(currentUser.Id);
@@ -208,7 +208,7 @@ public class FileController(
[Authorize] [Authorize]
[HttpPut("{id}/meta")] [HttpPut("{id}/meta")]
public async Task<ActionResult<CloudFile>> UpdateFileMeta(string id, [FromBody] Dictionary<string, object?> meta) public async Task<ActionResult<SnCloudFile>> UpdateFileMeta(string id, [FromBody] Dictionary<string, object?> meta)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
var accountId = Guid.Parse(currentUser.Id); var accountId = Guid.Parse(currentUser.Id);
@@ -222,7 +222,7 @@ public class FileController(
[Authorize] [Authorize]
[HttpGet("me")] [HttpGet("me")]
public async Task<ActionResult<List<CloudFile>>> GetMyFiles( public async Task<ActionResult<List<SnCloudFile>>> GetMyFiles(
[FromQuery] Guid? pool, [FromQuery] Guid? pool,
[FromQuery] bool recycled = false, [FromQuery] bool recycled = false,
[FromQuery] int offset = 0, [FromQuery] int offset = 0,
@@ -307,7 +307,7 @@ public class FileController(
[Authorize] [Authorize]
[HttpPost("fast")] [HttpPost("fast")]
[RequiredPermission("global", "files.create")] [RequiredPermission("global", "files.create")]
public async Task<ActionResult<CloudFile>> CreateFastFile([FromBody] CreateFastFileRequest request) public async Task<ActionResult<SnCloudFile>> CreateFastFile([FromBody] CreateFastFileRequest request)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
var accountId = Guid.Parse(currentUser.Id); var accountId = Guid.Parse(currentUser.Id);
@@ -368,7 +368,7 @@ public class FileController(
await using var transaction = await db.Database.BeginTransactionAsync(); await using var transaction = await db.Database.BeginTransactionAsync();
try try
{ {
var file = new CloudFile var file = new SnCloudFile
{ {
Name = request.Name, Name = request.Name,
Size = request.Size, Size = request.Size,

View File

@@ -1,3 +1,4 @@
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Proto;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;

View File

@@ -1,4 +1,5 @@
using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Models;
using EFCore.BulkExtensions; using EFCore.BulkExtensions;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NodaTime; using NodaTime;
@@ -347,7 +348,7 @@ public class FileReferenceService(AppDatabase db, FileService fileService, ICach
/// <param name="resourceId">The ID of the resource</param> /// <param name="resourceId">The ID of the resource</param>
/// <param name="usage">Optional filter by usage context</param> /// <param name="usage">Optional filter by usage context</param>
/// <returns>A list of files referenced by the resource</returns> /// <returns>A list of files referenced by the resource</returns>
public async Task<List<CloudFile>> GetResourceFilesAsync(string resourceId, string? usage = null) public async Task<List<SnCloudFile>> GetResourceFilesAsync(string resourceId, string? usage = null)
{ {
var query = db.FileReferences.Where(r => r.ResourceId == resourceId); var query = db.FileReferences.Where(r => r.ResourceId == resourceId);

View File

@@ -12,9 +12,9 @@ using NATS.Client.Core;
using NetVips; using NetVips;
using NodaTime; using NodaTime;
using System.Linq.Expressions; using System.Linq.Expressions;
using DysonNetwork.Shared.Data;
using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Query;
using NATS.Net; using NATS.Net;
using DysonNetwork.Shared.Models;
namespace DysonNetwork.Drive.Storage; namespace DysonNetwork.Drive.Storage;
@@ -28,11 +28,11 @@ public class FileService(
private const string CacheKeyPrefix = "file:"; private const string CacheKeyPrefix = "file:";
private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(15); private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(15);
public async Task<CloudFile?> GetFileAsync(string fileId) public async Task<SnCloudFile?> GetFileAsync(string fileId)
{ {
var cacheKey = $"{CacheKeyPrefix}{fileId}"; var cacheKey = $"{CacheKeyPrefix}{fileId}";
var cachedFile = await cache.GetAsync<CloudFile>(cacheKey); var cachedFile = await cache.GetAsync<SnCloudFile>(cacheKey);
if (cachedFile is not null) if (cachedFile is not null)
return cachedFile; return cachedFile;
@@ -48,15 +48,15 @@ public class FileService(
return file; return file;
} }
public async Task<List<CloudFile>> GetFilesAsync(List<string> fileIds) public async Task<List<SnCloudFile>> GetFilesAsync(List<string> fileIds)
{ {
var cachedFiles = new Dictionary<string, CloudFile>(); var cachedFiles = new Dictionary<string, SnCloudFile>();
var uncachedIds = new List<string>(); var uncachedIds = new List<string>();
foreach (var fileId in fileIds) foreach (var fileId in fileIds)
{ {
var cacheKey = $"{CacheKeyPrefix}{fileId}"; var cacheKey = $"{CacheKeyPrefix}{fileId}";
var cachedFile = await cache.GetAsync<CloudFile>(cacheKey); var cachedFile = await cache.GetAsync<SnCloudFile>(cacheKey);
if (cachedFile != null) if (cachedFile != null)
cachedFiles[fileId] = cachedFile; cachedFiles[fileId] = cachedFile;
@@ -82,11 +82,11 @@ public class FileService(
return fileIds return fileIds
.Select(f => cachedFiles.GetValueOrDefault(f)) .Select(f => cachedFiles.GetValueOrDefault(f))
.Where(f => f != null) .Where(f => f != null)
.Cast<CloudFile>() .Cast<SnCloudFile>()
.ToList(); .ToList();
} }
public async Task<CloudFile> ProcessNewFileAsync( public async Task<SnCloudFile> ProcessNewFileAsync(
Account account, Account account,
string fileId, string fileId,
string filePool, string filePool,
@@ -131,7 +131,7 @@ public class FileService(
var finalContentType = contentType ?? var finalContentType = contentType ??
(!fileName.Contains('.') ? "application/octet-stream" : MimeTypes.GetMimeType(fileName)); (!fileName.Contains('.') ? "application/octet-stream" : MimeTypes.GetMimeType(fileName));
var file = new CloudFile var file = new SnCloudFile
{ {
Id = fileId, Id = fileId,
Name = fileName, Name = fileName,
@@ -190,7 +190,7 @@ public class FileService(
return file; return file;
} }
private async Task ExtractMetadataAsync(CloudFile file, string filePath) private async Task ExtractMetadataAsync(SnCloudFile file, string filePath)
{ {
switch (file.MimeType?.Split('/')[0]) switch (file.MimeType?.Split('/')[0])
{ {
@@ -373,7 +373,7 @@ public class FileService(
); );
} }
public async Task<CloudFile> UpdateFileAsync(CloudFile file, FieldMask updateMask) public async Task<SnCloudFile> UpdateFileAsync(SnCloudFile file, FieldMask updateMask)
{ {
var existingFile = await db.Files.FirstOrDefaultAsync(f => f.Id == file.Id); var existingFile = await db.Files.FirstOrDefaultAsync(f => f.Id == file.Id);
if (existingFile == null) if (existingFile == null)
@@ -414,7 +414,7 @@ public class FileService(
return await db.Files.AsNoTracking().FirstAsync(f => f.Id == file.Id); return await db.Files.AsNoTracking().FirstAsync(f => f.Id == file.Id);
} }
public async Task DeleteFileAsync(CloudFile file) public async Task DeleteFileAsync(SnCloudFile file)
{ {
db.Remove(file); db.Remove(file);
await db.SaveChangesAsync(); await db.SaveChangesAsync();
@@ -423,7 +423,7 @@ public class FileService(
await DeleteFileDataAsync(file); await DeleteFileDataAsync(file);
} }
public async Task DeleteFileDataAsync(CloudFile file, bool force = false) public async Task DeleteFileDataAsync(SnCloudFile file, bool force = false)
{ {
if (!file.PoolId.HasValue) return; if (!file.PoolId.HasValue) return;
@@ -482,7 +482,7 @@ public class FileService(
} }
} }
public async Task DeleteFileDataBatchAsync(List<CloudFile> files) public async Task DeleteFileDataBatchAsync(List<SnCloudFile> files)
{ {
files = files.Where(f => f.PoolId.HasValue).ToList(); files = files.Where(f => f.PoolId.HasValue).ToList();
@@ -512,7 +512,7 @@ public class FileService(
} }
} }
private async Task<FileBundle?> GetBundleAsync(Guid id, Guid accountId) private async Task<SnFileBundle?> GetBundleAsync(Guid id, Guid accountId)
{ {
var bundle = await db.Bundles var bundle = await db.Bundles
.Where(e => e.Id == id) .Where(e => e.Id == id)
@@ -569,15 +569,15 @@ public class FileService(
await Task.WhenAll(tasks); await Task.WhenAll(tasks);
} }
public async Task<List<CloudFile?>> LoadFromReference(List<CloudFileReferenceObject> references) public async Task<List<SnCloudFile?>> LoadFromReference(List<SnCloudFileReferenceObject> references)
{ {
var cachedFiles = new Dictionary<string, CloudFile>(); var cachedFiles = new Dictionary<string, SnCloudFile>();
var uncachedIds = new List<string>(); var uncachedIds = new List<string>();
foreach (var reference in references) foreach (var reference in references)
{ {
var cacheKey = $"{CacheKeyPrefix}{reference.Id}"; var cacheKey = $"{CacheKeyPrefix}{reference.Id}";
var cachedFile = await cache.GetAsync<CloudFile>(cacheKey); var cachedFile = await cache.GetAsync<SnCloudFile>(cacheKey);
if (cachedFile != null) if (cachedFile != null)
{ {
@@ -603,10 +603,9 @@ public class FileService(
} }
} }
return references return [.. references
.Select(r => cachedFiles.GetValueOrDefault(r.Id)) .Select(r => cachedFiles.GetValueOrDefault(r.Id))
.Where(f => f != null) .Where(f => f != null)];
.ToList();
} }
public async Task<int> GetReferenceCountAsync(string fileId) public async Task<int> GetReferenceCountAsync(string fileId)
@@ -685,7 +684,7 @@ public class FileService(
return count; return count;
} }
public async Task<string> CreateFastUploadLinkAsync(CloudFile file) public async Task<string> CreateFastUploadLinkAsync(SnCloudFile file)
{ {
if (file.PoolId is null) throw new InvalidOperationException("Pool ID is null"); if (file.PoolId is null) throw new InvalidOperationException("Pool ID is null");
@@ -707,7 +706,7 @@ public class FileService(
} }
} }
file class UpdatableCloudFile(CloudFile file) file class UpdatableCloudFile(SnCloudFile file)
{ {
public string Name { get; set; } = file.Name; public string Name { get; set; } = file.Name;
public string? Description { get; set; } = file.Description; public string? Description { get; set; } = file.Description;
@@ -715,9 +714,9 @@ file class UpdatableCloudFile(CloudFile file)
public Dictionary<string, object?>? UserMeta { get; set; } = file.UserMeta; public Dictionary<string, object?>? UserMeta { get; set; } = file.UserMeta;
public bool IsMarkedRecycle { get; set; } = file.IsMarkedRecycle; public bool IsMarkedRecycle { get; set; } = file.IsMarkedRecycle;
public Expression<Func<SetPropertyCalls<CloudFile>, SetPropertyCalls<CloudFile>>> ToSetPropertyCalls() public Expression<Func<SetPropertyCalls<SnCloudFile>, SetPropertyCalls<SnCloudFile>>> ToSetPropertyCalls()
{ {
var userMeta = UserMeta ?? new Dictionary<string, object?>(); var userMeta = UserMeta ?? [];
return setter => setter return setter => setter
.SetProperty(f => f.Name, Name) .SetProperty(f => f.Name, Name)
.SetProperty(f => f.Description, Description) .SetProperty(f => f.Description, Description)

View File

@@ -1,4 +1,4 @@
using DysonNetwork.Shared.Data; using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Proto;
using Google.Protobuf.WellKnownTypes; using Google.Protobuf.WellKnownTypes;
using Grpc.Core; using Grpc.Core;
@@ -48,7 +48,7 @@ namespace DysonNetwork.Drive.Storage
{ {
// Assuming CloudFileReferenceObject is a simple class/struct that holds an ID // Assuming CloudFileReferenceObject is a simple class/struct that holds an ID
// You might need to define this or adjust the LoadFromReference method in FileService // You might need to define this or adjust the LoadFromReference method in FileService
var references = request.ReferenceIds.Select(id => new CloudFileReferenceObject { Id = id }).ToList(); var references = request.ReferenceIds.Select(id => new SnCloudFileReferenceObject { Id = id }).ToList();
var files = await fileService.LoadFromReference(references); var files = await fileService.LoadFromReference(references);
var response = new LoadFromReferenceResponse(); var response = new LoadFromReferenceResponse();
response.Files.AddRange(files.Where(f => f != null).Select(f => f!.ToProtoValue())); response.Files.AddRange(files.Where(f => f != null).Select(f => f!.ToProtoValue()));

View File

@@ -1,3 +1,4 @@
using DysonNetwork.Shared.Models;
using NodaTime; using NodaTime;
namespace DysonNetwork.Drive.Storage.Model namespace DysonNetwork.Drive.Storage.Model
@@ -18,7 +19,7 @@ namespace DysonNetwork.Drive.Storage.Model
public class CreateUploadTaskResponse public class CreateUploadTaskResponse
{ {
public bool FileExists { get; set; } public bool FileExists { get; set; }
public CloudFile? File { get; set; } public SnCloudFile? File { get; set; }
public string? TaskId { get; set; } public string? TaskId { get; set; }
public long? ChunkSize { get; set; } public long? ChunkSize { get; set; }
public int? ChunksCount { get; set; } public int? ChunksCount { get; set; }

View File

@@ -1,13 +0,0 @@
{
"lockfileVersion": 1,
"workspaces": {
"": {
"dependencies": {
"highlight.js": "^11.11.1",
},
},
},
"packages": {
"highlight.js": ["highlight.js@11.11.1", "", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="],
}
}

View File

@@ -4,6 +4,7 @@ using DysonNetwork.Pass.Credit;
using DysonNetwork.Pass.Wallet; using DysonNetwork.Pass.Wallet;
using DysonNetwork.Shared.Error; using DysonNetwork.Shared.Error;
using DysonNetwork.Shared.GeoIp; using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Models;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NodaTime; using NodaTime;
@@ -23,9 +24,9 @@ public class AccountController(
) : ControllerBase ) : ControllerBase
{ {
[HttpGet("{name}")] [HttpGet("{name}")]
[ProducesResponseType<Account>(StatusCodes.Status200OK)] [ProducesResponseType<SnAccount>(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<Account?>> GetByName(string name) public async Task<ActionResult<SnAccount?>> GetByName(string name)
{ {
var account = await db.Accounts var account = await db.Accounts
.Include(e => e.Badges) .Include(e => e.Badges)
@@ -42,9 +43,9 @@ public class AccountController(
} }
[HttpGet("{name}/badges")] [HttpGet("{name}/badges")]
[ProducesResponseType<List<AccountBadge>>(StatusCodes.Status200OK)] [ProducesResponseType<List<SnAccountBadge>>(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<List<AccountBadge>>> GetBadgesByName(string name) public async Task<ActionResult<List<SnAccountBadge>>> GetBadgesByName(string name)
{ {
var account = await db.Accounts var account = await db.Accounts
.Include(e => e.Badges) .Include(e => e.Badges)
@@ -103,9 +104,9 @@ public class AccountController(
} }
[HttpPost] [HttpPost]
[ProducesResponseType<Account>(StatusCodes.Status200OK)] [ProducesResponseType<SnAccount>(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<Account>> CreateAccount([FromBody] AccountCreateRequest request) public async Task<ActionResult<SnAccount>> CreateAccount([FromBody] AccountCreateRequest request)
{ {
if (!await auth.ValidateCaptcha(request.CaptchaToken)) if (!await auth.ValidateCaptcha(request.CaptchaToken))
return BadRequest(ApiError.Validation(new Dictionary<string, string[]> return BadRequest(ApiError.Validation(new Dictionary<string, string[]>
@@ -199,7 +200,7 @@ public class AccountController(
} }
[HttpGet("{name}/statuses")] [HttpGet("{name}/statuses")]
public async Task<ActionResult<Status>> GetOtherStatus(string name) public async Task<ActionResult<SnAccountStatus>> GetOtherStatus(string name)
{ {
var account = await db.Accounts.FirstOrDefaultAsync(a => a.Name == name); var account = await db.Accounts.FirstOrDefaultAsync(a => a.Name == name);
if (account is null) if (account is null)
@@ -254,7 +255,7 @@ public class AccountController(
} }
[HttpGet("search")] [HttpGet("search")]
public async Task<List<Account>> Search([FromQuery] string query, [FromQuery] int take = 20) public async Task<List<SnAccount>> Search([FromQuery] string query, [FromQuery] int take = 20)
{ {
if (string.IsNullOrWhiteSpace(query)) if (string.IsNullOrWhiteSpace(query))
return []; return [];

View File

@@ -1,16 +1,15 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using DysonNetwork.Pass.Auth;
using DysonNetwork.Pass.Permission; using DysonNetwork.Pass.Permission;
using DysonNetwork.Pass.Wallet; using DysonNetwork.Pass.Wallet;
using DysonNetwork.Shared.Data;
using DysonNetwork.Shared.Error; using DysonNetwork.Shared.Error;
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Proto;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NodaTime; using NodaTime;
using AuthService = DysonNetwork.Pass.Auth.AuthService; using AuthService = DysonNetwork.Pass.Auth.AuthService;
using AuthSession = DysonNetwork.Pass.Auth.AuthSession; using SnAuthSession = DysonNetwork.Shared.Models.SnAuthSession;
namespace DysonNetwork.Pass.Account; namespace DysonNetwork.Pass.Account;
@@ -29,11 +28,11 @@ public class AccountCurrentController(
) : ControllerBase ) : ControllerBase
{ {
[HttpGet] [HttpGet]
[ProducesResponseType<Account>(StatusCodes.Status200OK)] [ProducesResponseType<SnAccount>(StatusCodes.Status200OK)]
[ProducesResponseType<ApiError>(StatusCodes.Status401Unauthorized)] [ProducesResponseType<ApiError>(StatusCodes.Status401Unauthorized)]
public async Task<ActionResult<Account>> GetCurrentIdentity() public async Task<ActionResult<SnAccount>> GetCurrentIdentity()
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var userId = currentUser.Id; var userId = currentUser.Id;
var account = await db.Accounts var account = await db.Accounts
@@ -56,9 +55,9 @@ public class AccountCurrentController(
} }
[HttpPatch] [HttpPatch]
public async Task<ActionResult<Account>> UpdateBasicInfo([FromBody] BasicInfoRequest request) public async Task<ActionResult<SnAccount>> UpdateBasicInfo([FromBody] BasicInfoRequest request)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var account = await db.Accounts.FirstAsync(a => a.Id == currentUser.Id); var account = await db.Accounts.FirstAsync(a => a.Id == currentUser.Id);
@@ -89,9 +88,9 @@ public class AccountCurrentController(
} }
[HttpPatch("profile")] [HttpPatch("profile")]
public async Task<ActionResult<AccountProfile>> UpdateProfile([FromBody] ProfileRequest request) public async Task<ActionResult<SnAccountProfile>> UpdateProfile([FromBody] ProfileRequest request)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var userId = currentUser.Id; var userId = currentUser.Id;
var profile = await db.AccountProfiles var profile = await db.AccountProfiles
@@ -132,7 +131,7 @@ public class AccountCurrentController(
Usage = "profile.picture" Usage = "profile.picture"
} }
); );
profile.Picture = CloudFileReferenceObject.FromProtoValue(file); profile.Picture = SnCloudFileReferenceObject.FromProtoValue(file);
} }
if (request.BackgroundId is not null) if (request.BackgroundId is not null)
@@ -150,7 +149,7 @@ public class AccountCurrentController(
Usage = "profile.background" Usage = "profile.background"
} }
); );
profile.Background = CloudFileReferenceObject.FromProtoValue(file); profile.Background = SnCloudFileReferenceObject.FromProtoValue(file);
} }
db.Update(profile); db.Update(profile);
@@ -164,7 +163,7 @@ public class AccountCurrentController(
[HttpDelete] [HttpDelete]
public async Task<ActionResult> RequestDeleteAccount() public async Task<ActionResult> RequestDeleteAccount()
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
try try
{ {
@@ -185,18 +184,18 @@ public class AccountCurrentController(
} }
[HttpGet("statuses")] [HttpGet("statuses")]
public async Task<ActionResult<Status>> GetCurrentStatus() public async Task<ActionResult<SnAccountStatus>> GetCurrentStatus()
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var status = await events.GetStatus(currentUser.Id); var status = await events.GetStatus(currentUser.Id);
return Ok(status); return Ok(status);
} }
[HttpPatch("statuses")] [HttpPatch("statuses")]
[RequiredPermission("global", "accounts.statuses.update")] [RequiredPermission("global", "accounts.statuses.update")]
public async Task<ActionResult<Status>> UpdateStatus([FromBody] AccountController.StatusRequest request) public async Task<ActionResult<SnAccountStatus>> UpdateStatus([FromBody] AccountController.StatusRequest request)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
if (request is { IsAutomated: true, AppIdentifier: not null }) if (request is { IsAutomated: true, AppIdentifier: not null })
return BadRequest("Automated status cannot be updated."); return BadRequest("Automated status cannot be updated.");
@@ -228,9 +227,9 @@ public class AccountCurrentController(
[HttpPost("statuses")] [HttpPost("statuses")]
[RequiredPermission("global", "accounts.statuses.create")] [RequiredPermission("global", "accounts.statuses.create")]
public async Task<ActionResult<Status>> CreateStatus([FromBody] AccountController.StatusRequest request) public async Task<ActionResult<SnAccountStatus>> CreateStatus([FromBody] AccountController.StatusRequest request)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
if (request is { IsAutomated: true, AppIdentifier: not null }) if (request is { IsAutomated: true, AppIdentifier: not null })
{ {
@@ -262,7 +261,7 @@ public class AccountCurrentController(
return Ok(existingStatus); // Do not override manually set status with automated ones return Ok(existingStatus); // Do not override manually set status with automated ones
} }
var status = new Status var status = new SnAccountStatus
{ {
AccountId = currentUser.Id, AccountId = currentUser.Id,
Attitude = request.Attitude, Attitude = request.Attitude,
@@ -281,7 +280,7 @@ public class AccountCurrentController(
[HttpDelete("statuses")] [HttpDelete("statuses")]
public async Task<ActionResult> DeleteStatus([FromQuery] string? app) public async Task<ActionResult> DeleteStatus([FromQuery] string? app)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var now = SystemClock.Instance.GetCurrentInstant(); var now = SystemClock.Instance.GetCurrentInstant();
var queryable = db.AccountStatuses var queryable = db.AccountStatuses
@@ -302,9 +301,9 @@ public class AccountCurrentController(
} }
[HttpGet("check-in")] [HttpGet("check-in")]
public async Task<ActionResult<CheckInResult>> GetCheckInResult() public async Task<ActionResult<SnCheckInResult>> GetCheckInResult()
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var userId = currentUser.Id; var userId = currentUser.Id;
var now = SystemClock.Instance.GetCurrentInstant(); var now = SystemClock.Instance.GetCurrentInstant();
@@ -324,12 +323,12 @@ public class AccountCurrentController(
} }
[HttpPost("check-in")] [HttpPost("check-in")]
public async Task<ActionResult<CheckInResult>> DoCheckIn( public async Task<ActionResult<SnCheckInResult>> DoCheckIn(
[FromBody] string? captchaToken, [FromBody] string? captchaToken,
[FromQuery] Instant? backdated = null [FromQuery] Instant? backdated = null
) )
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
if (backdated is null) if (backdated is null)
{ {
@@ -400,7 +399,7 @@ public class AccountCurrentController(
public async Task<ActionResult<List<DailyEventResponse>>> GetEventCalendar([FromQuery] int? month, public async Task<ActionResult<List<DailyEventResponse>>> GetEventCalendar([FromQuery] int? month,
[FromQuery] int? year) [FromQuery] int? year)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var currentDate = SystemClock.Instance.GetCurrentInstant().InUtc().Date; var currentDate = SystemClock.Instance.GetCurrentInstant().InUtc().Date;
month ??= currentDate.Month; month ??= currentDate.Month;
@@ -429,7 +428,7 @@ public class AccountCurrentController(
[FromQuery] int offset = 0 [FromQuery] int offset = 0
) )
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var query = db.ActionLogs var query = db.ActionLogs
.Where(log => log.AccountId == currentUser.Id) .Where(log => log.AccountId == currentUser.Id)
@@ -447,9 +446,9 @@ public class AccountCurrentController(
} }
[HttpGet("factors")] [HttpGet("factors")]
public async Task<ActionResult<List<AccountAuthFactor>>> GetAuthFactors() public async Task<ActionResult<List<SnAccountAuthFactor>>> GetAuthFactors()
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var factors = await db.AccountAuthFactors var factors = await db.AccountAuthFactors
.Include(f => f.Account) .Include(f => f.Account)
@@ -461,15 +460,15 @@ public class AccountCurrentController(
public class AuthFactorRequest public class AuthFactorRequest
{ {
public AccountAuthFactorType Type { get; set; } public Shared.Models.AccountAuthFactorType Type { get; set; }
public string? Secret { get; set; } public string? Secret { get; set; }
} }
[HttpPost("factors")] [HttpPost("factors")]
[Authorize] [Authorize]
public async Task<ActionResult<AccountAuthFactor>> CreateAuthFactor([FromBody] AuthFactorRequest request) public async Task<ActionResult<SnAccountAuthFactor>> CreateAuthFactor([FromBody] AuthFactorRequest request)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
if (await accounts.CheckAuthFactorExists(currentUser, request.Type)) if (await accounts.CheckAuthFactorExists(currentUser, request.Type))
return BadRequest(new ApiError return BadRequest(new ApiError
{ {
@@ -485,9 +484,9 @@ public class AccountCurrentController(
[HttpPost("factors/{id:guid}/enable")] [HttpPost("factors/{id:guid}/enable")]
[Authorize] [Authorize]
public async Task<ActionResult<AccountAuthFactor>> EnableAuthFactor(Guid id, [FromBody] string? code) public async Task<ActionResult<SnAccountAuthFactor>> EnableAuthFactor(Guid id, [FromBody] string? code)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var factor = await db.AccountAuthFactors var factor = await db.AccountAuthFactors
.Where(f => f.AccountId == currentUser.Id && f.Id == id) .Where(f => f.AccountId == currentUser.Id && f.Id == id)
@@ -514,9 +513,9 @@ public class AccountCurrentController(
[HttpPost("factors/{id:guid}/disable")] [HttpPost("factors/{id:guid}/disable")]
[Authorize] [Authorize]
public async Task<ActionResult<AccountAuthFactor>> DisableAuthFactor(Guid id) public async Task<ActionResult<SnAccountAuthFactor>> DisableAuthFactor(Guid id)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var factor = await db.AccountAuthFactors var factor = await db.AccountAuthFactors
.Where(f => f.AccountId == currentUser.Id && f.Id == id) .Where(f => f.AccountId == currentUser.Id && f.Id == id)
@@ -536,9 +535,9 @@ public class AccountCurrentController(
[HttpDelete("factors/{id:guid}")] [HttpDelete("factors/{id:guid}")]
[Authorize] [Authorize]
public async Task<ActionResult<AccountAuthFactor>> DeleteAuthFactor(Guid id) public async Task<ActionResult<SnAccountAuthFactor>> DeleteAuthFactor(Guid id)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var factor = await db.AccountAuthFactors var factor = await db.AccountAuthFactors
.Where(f => f.AccountId == currentUser.Id && f.Id == id) .Where(f => f.AccountId == currentUser.Id && f.Id == id)
@@ -558,10 +557,10 @@ public class AccountCurrentController(
[HttpGet("devices")] [HttpGet("devices")]
[Authorize] [Authorize]
public async Task<ActionResult<List<AuthClientWithChallenge>>> GetDevices() public async Task<ActionResult<List<SnAuthClientWithChallenge>>> GetDevices()
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser || if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser ||
HttpContext.Items["CurrentSession"] is not AuthSession currentSession) return Unauthorized(); HttpContext.Items["CurrentSession"] is not SnAuthSession currentSession) return Unauthorized();
Response.Headers.Append("X-Auth-Session", currentSession.Id.ToString()); Response.Headers.Append("X-Auth-Session", currentSession.Id.ToString());
@@ -569,7 +568,7 @@ public class AccountCurrentController(
.Where(device => device.AccountId == currentUser.Id) .Where(device => device.AccountId == currentUser.Id)
.ToListAsync(); .ToListAsync();
var challengeDevices = devices.Select(AuthClientWithChallenge.FromClient).ToList(); var challengeDevices = devices.Select(SnAuthClientWithChallenge.FromClient).ToList();
var deviceIds = challengeDevices.Select(x => x.Id).ToList(); var deviceIds = challengeDevices.Select(x => x.Id).ToList();
var authChallenges = await db.AuthChallenges var authChallenges = await db.AuthChallenges
@@ -585,13 +584,13 @@ public class AccountCurrentController(
[HttpGet("sessions")] [HttpGet("sessions")]
[Authorize] [Authorize]
public async Task<ActionResult<List<AuthSession>>> GetSessions( public async Task<ActionResult<List<SnAuthSession>>> GetSessions(
[FromQuery] int take = 20, [FromQuery] int take = 20,
[FromQuery] int offset = 0 [FromQuery] int offset = 0
) )
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser || if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser ||
HttpContext.Items["CurrentSession"] is not AuthSession currentSession) return Unauthorized(); HttpContext.Items["CurrentSession"] is not SnAuthSession currentSession) return Unauthorized();
var query = db.AuthSessions var query = db.AuthSessions
.Include(session => session.Account) .Include(session => session.Account)
@@ -613,9 +612,9 @@ public class AccountCurrentController(
[HttpDelete("sessions/{id:guid}")] [HttpDelete("sessions/{id:guid}")]
[Authorize] [Authorize]
public async Task<ActionResult<AuthSession>> DeleteSession(Guid id) public async Task<ActionResult<SnAuthSession>> DeleteSession(Guid id)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
try try
{ {
@@ -630,9 +629,9 @@ public class AccountCurrentController(
[HttpDelete("devices/{deviceId}")] [HttpDelete("devices/{deviceId}")]
[Authorize] [Authorize]
public async Task<ActionResult<AuthSession>> DeleteDevice(string deviceId) public async Task<ActionResult<SnAuthSession>> DeleteDevice(string deviceId)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
try try
{ {
@@ -647,10 +646,10 @@ public class AccountCurrentController(
[HttpDelete("sessions/current")] [HttpDelete("sessions/current")]
[Authorize] [Authorize]
public async Task<ActionResult<AuthSession>> DeleteCurrentSession() public async Task<ActionResult<SnAuthSession>> DeleteCurrentSession()
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser || if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser ||
HttpContext.Items["CurrentSession"] is not AuthSession currentSession) return Unauthorized(); HttpContext.Items["CurrentSession"] is not SnAuthSession currentSession) return Unauthorized();
try try
{ {
@@ -665,9 +664,9 @@ public class AccountCurrentController(
[HttpPatch("devices/{deviceId}/label")] [HttpPatch("devices/{deviceId}/label")]
[Authorize] [Authorize]
public async Task<ActionResult<AuthSession>> UpdateDeviceLabel(string deviceId, [FromBody] string label) public async Task<ActionResult<SnAuthSession>> UpdateDeviceLabel(string deviceId, [FromBody] string label)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
try try
{ {
@@ -682,10 +681,10 @@ public class AccountCurrentController(
[HttpPatch("devices/current/label")] [HttpPatch("devices/current/label")]
[Authorize] [Authorize]
public async Task<ActionResult<AuthSession>> UpdateCurrentDeviceLabel([FromBody] string label) public async Task<ActionResult<SnAuthSession>> UpdateCurrentDeviceLabel([FromBody] string label)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser || if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser ||
HttpContext.Items["CurrentSession"] is not AuthSession currentSession) return Unauthorized(); HttpContext.Items["CurrentSession"] is not SnAuthSession currentSession) return Unauthorized();
var device = await db.AuthClients.FirstOrDefaultAsync(d => d.Id == currentSession.Challenge.ClientId); var device = await db.AuthClients.FirstOrDefaultAsync(d => d.Id == currentSession.Challenge.ClientId);
if (device is null) return NotFound(); if (device is null) return NotFound();
@@ -703,9 +702,9 @@ public class AccountCurrentController(
[HttpGet("contacts")] [HttpGet("contacts")]
[Authorize] [Authorize]
public async Task<ActionResult<List<AccountContact>>> GetContacts() public async Task<ActionResult<List<SnAccountContact>>> GetContacts()
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var contacts = await db.AccountContacts var contacts = await db.AccountContacts
.Where(c => c.AccountId == currentUser.Id) .Where(c => c.AccountId == currentUser.Id)
@@ -716,15 +715,15 @@ public class AccountCurrentController(
public class AccountContactRequest public class AccountContactRequest
{ {
[Required] public AccountContactType Type { get; set; } [Required] public Shared.Models.AccountContactType Type { get; set; }
[Required] public string Content { get; set; } = null!; [Required] public string Content { get; set; } = null!;
} }
[HttpPost("contacts")] [HttpPost("contacts")]
[Authorize] [Authorize]
public async Task<ActionResult<AccountContact>> CreateContact([FromBody] AccountContactRequest request) public async Task<ActionResult<SnAccountContact>> CreateContact([FromBody] AccountContactRequest request)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
try try
{ {
@@ -739,9 +738,9 @@ public class AccountCurrentController(
[HttpPost("contacts/{id:guid}/verify")] [HttpPost("contacts/{id:guid}/verify")]
[Authorize] [Authorize]
public async Task<ActionResult<AccountContact>> VerifyContact(Guid id) public async Task<ActionResult<SnAccountContact>> VerifyContact(Guid id)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var contact = await db.AccountContacts var contact = await db.AccountContacts
.Where(c => c.AccountId == currentUser.Id && c.Id == id) .Where(c => c.AccountId == currentUser.Id && c.Id == id)
@@ -761,9 +760,9 @@ public class AccountCurrentController(
[HttpPost("contacts/{id:guid}/primary")] [HttpPost("contacts/{id:guid}/primary")]
[Authorize] [Authorize]
public async Task<ActionResult<AccountContact>> SetPrimaryContact(Guid id) public async Task<ActionResult<SnAccountContact>> SetPrimaryContact(Guid id)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var contact = await db.AccountContacts var contact = await db.AccountContacts
.Where(c => c.AccountId == currentUser.Id && c.Id == id) .Where(c => c.AccountId == currentUser.Id && c.Id == id)
@@ -783,9 +782,9 @@ public class AccountCurrentController(
[HttpPost("contacts/{id:guid}/public")] [HttpPost("contacts/{id:guid}/public")]
[Authorize] [Authorize]
public async Task<ActionResult<AccountContact>> SetPublicContact(Guid id) public async Task<ActionResult<SnAccountContact>> SetPublicContact(Guid id)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var contact = await db.AccountContacts var contact = await db.AccountContacts
.Where(c => c.AccountId == currentUser.Id && c.Id == id) .Where(c => c.AccountId == currentUser.Id && c.Id == id)
@@ -805,9 +804,9 @@ public class AccountCurrentController(
[HttpDelete("contacts/{id:guid}/public")] [HttpDelete("contacts/{id:guid}/public")]
[Authorize] [Authorize]
public async Task<ActionResult<AccountContact>> UnsetPublicContact(Guid id) public async Task<ActionResult<SnAccountContact>> UnsetPublicContact(Guid id)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var contact = await db.AccountContacts var contact = await db.AccountContacts
.Where(c => c.AccountId == currentUser.Id && c.Id == id) .Where(c => c.AccountId == currentUser.Id && c.Id == id)
@@ -827,9 +826,9 @@ public class AccountCurrentController(
[HttpDelete("contacts/{id:guid}")] [HttpDelete("contacts/{id:guid}")]
[Authorize] [Authorize]
public async Task<ActionResult<AccountContact>> DeleteContact(Guid id) public async Task<ActionResult<SnAccountContact>> DeleteContact(Guid id)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var contact = await db.AccountContacts var contact = await db.AccountContacts
.Where(c => c.AccountId == currentUser.Id && c.Id == id) .Where(c => c.AccountId == currentUser.Id && c.Id == id)
@@ -848,11 +847,11 @@ public class AccountCurrentController(
} }
[HttpGet("badges")] [HttpGet("badges")]
[ProducesResponseType<List<AccountBadge>>(StatusCodes.Status200OK)] [ProducesResponseType<List<SnAccountBadge>>(StatusCodes.Status200OK)]
[Authorize] [Authorize]
public async Task<ActionResult<List<AccountBadge>>> GetBadges() public async Task<ActionResult<List<SnAccountBadge>>> GetBadges()
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var badges = await db.Badges var badges = await db.Badges
.Where(b => b.AccountId == currentUser.Id) .Where(b => b.AccountId == currentUser.Id)
@@ -862,9 +861,9 @@ public class AccountCurrentController(
[HttpPost("badges/{id:guid}/active")] [HttpPost("badges/{id:guid}/active")]
[Authorize] [Authorize]
public async Task<ActionResult<AccountBadge>> ActivateBadge(Guid id) public async Task<ActionResult<SnAccountBadge>> ActivateBadge(Guid id)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
try try
{ {
@@ -879,12 +878,12 @@ public class AccountCurrentController(
[HttpGet("leveling")] [HttpGet("leveling")]
[Authorize] [Authorize]
public async Task<ActionResult<ExperienceRecord>> GetLevelingHistory( public async Task<ActionResult<SnExperienceRecord>> GetLevelingHistory(
[FromQuery] int take = 20, [FromQuery] int take = 20,
[FromQuery] int offset = 0 [FromQuery] int offset = 0
) )
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var queryable = db.ExperienceRecords var queryable = db.ExperienceRecords
.Where(r => r.AccountId == currentUser.Id) .Where(r => r.AccountId == currentUser.Id)
@@ -904,7 +903,7 @@ public class AccountCurrentController(
[HttpGet("credits")] [HttpGet("credits")]
public async Task<ActionResult<bool>> GetSocialCredit() public async Task<ActionResult<bool>> GetSocialCredit()
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var credit = await creditService.GetSocialCredit(currentUser.Id); var credit = await creditService.GetSocialCredit(currentUser.Id);
return Ok(credit); return Ok(credit);
@@ -916,7 +915,7 @@ public class AccountCurrentController(
[FromQuery] int offset = 0 [FromQuery] int offset = 0
) )
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var queryable = db.SocialCreditRecords var queryable = db.SocialCreditRecords
.Where(r => r.AccountId == currentUser.Id) .Where(r => r.AccountId == currentUser.Id)

View File

@@ -1,6 +1,7 @@
using System.Globalization; using System.Globalization;
using DysonNetwork.Pass.Wallet; using DysonNetwork.Pass.Wallet;
using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Proto;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
@@ -36,10 +37,10 @@ public class AccountEventService(
cache.RemoveAsync(cacheKey); cache.RemoveAsync(cacheKey);
} }
public async Task<Status> GetStatus(Guid userId) public async Task<SnAccountStatus> GetStatus(Guid userId)
{ {
var cacheKey = $"{StatusCacheKey}{userId}"; var cacheKey = $"{StatusCacheKey}{userId}";
var cachedStatus = await cache.GetAsync<Status>(cacheKey); var cachedStatus = await cache.GetAsync<SnAccountStatus>(cacheKey);
if (cachedStatus is not null) if (cachedStatus is not null)
{ {
cachedStatus!.IsOnline = !cachedStatus.IsInvisible && await GetAccountIsConnected(userId); cachedStatus!.IsOnline = !cachedStatus.IsInvisible && await GetAccountIsConnected(userId);
@@ -63,9 +64,9 @@ public class AccountEventService(
if (isOnline) if (isOnline)
{ {
return new Status return new SnAccountStatus
{ {
Attitude = StatusAttitude.Neutral, Attitude = Shared.Models.StatusAttitude.Neutral,
IsOnline = true, IsOnline = true,
IsCustomized = false, IsCustomized = false,
Label = "Online", Label = "Online",
@@ -73,9 +74,9 @@ public class AccountEventService(
}; };
} }
return new Status return new SnAccountStatus
{ {
Attitude = StatusAttitude.Neutral, Attitude = Shared.Models.StatusAttitude.Neutral,
IsOnline = false, IsOnline = false,
IsCustomized = false, IsCustomized = false,
Label = "Offline", Label = "Offline",
@@ -83,15 +84,15 @@ public class AccountEventService(
}; };
} }
public async Task<Dictionary<Guid, Status>> GetStatuses(List<Guid> userIds) public async Task<Dictionary<Guid, SnAccountStatus>> GetStatuses(List<Guid> userIds)
{ {
var results = new Dictionary<Guid, Status>(); var results = new Dictionary<Guid, SnAccountStatus>();
var cacheMissUserIds = new List<Guid>(); var cacheMissUserIds = new List<Guid>();
foreach (var userId in userIds) foreach (var userId in userIds)
{ {
var cacheKey = $"{StatusCacheKey}{userId}"; var cacheKey = $"{StatusCacheKey}{userId}";
var cachedStatus = await cache.GetAsync<Status>(cacheKey); var cachedStatus = await cache.GetAsync<SnAccountStatus>(cacheKey);
if (cachedStatus != null) if (cachedStatus != null)
{ {
cachedStatus.IsOnline = !cachedStatus.IsInvisible && await GetAccountIsConnected(userId); cachedStatus.IsOnline = !cachedStatus.IsInvisible && await GetAccountIsConnected(userId);
@@ -131,9 +132,9 @@ public class AccountEventService(
foreach (var userId in usersWithoutStatus) foreach (var userId in usersWithoutStatus)
{ {
var isOnline = await GetAccountIsConnected(userId); var isOnline = await GetAccountIsConnected(userId);
var defaultStatus = new Status var defaultStatus = new SnAccountStatus
{ {
Attitude = StatusAttitude.Neutral, Attitude = Shared.Models.StatusAttitude.Neutral,
IsOnline = isOnline, IsOnline = isOnline,
IsCustomized = false, IsCustomized = false,
Label = isOnline ? "Online" : "Offline", Label = isOnline ? "Online" : "Offline",
@@ -147,7 +148,7 @@ public class AccountEventService(
return results; return results;
} }
public async Task<Status> CreateStatus(Account user, Status status) public async Task<SnAccountStatus> CreateStatus(SnAccount user, SnAccountStatus status)
{ {
var now = SystemClock.Instance.GetCurrentInstant(); var now = SystemClock.Instance.GetCurrentInstant();
await db.AccountStatuses await db.AccountStatuses
@@ -160,7 +161,7 @@ public class AccountEventService(
return status; return status;
} }
public async Task ClearStatus(Account user, Status status) public async Task ClearStatus(SnAccount user, SnAccountStatus status)
{ {
status.ClearedAt = SystemClock.Instance.GetCurrentInstant(); status.ClearedAt = SystemClock.Instance.GetCurrentInstant();
db.Update(status); db.Update(status);
@@ -172,7 +173,7 @@ public class AccountEventService(
private const string CaptchaCacheKey = "checkin:captcha:"; private const string CaptchaCacheKey = "checkin:captcha:";
private const int CaptchaProbabilityPercent = 20; private const int CaptchaProbabilityPercent = 20;
public async Task<bool> CheckInDailyDoAskCaptcha(Account user) public async Task<bool> CheckInDailyDoAskCaptcha(SnAccount user)
{ {
var perkSubscription = await subscriptions.GetPerkSubscriptionAsync(user.Id); var perkSubscription = await subscriptions.GetPerkSubscriptionAsync(user.Id);
if (perkSubscription is not null) return false; if (perkSubscription is not null) return false;
@@ -187,7 +188,7 @@ public class AccountEventService(
return result; return result;
} }
public async Task<bool> CheckInDailyIsAvailable(Account user) public async Task<bool> CheckInDailyIsAvailable(SnAccount user)
{ {
var now = SystemClock.Instance.GetCurrentInstant(); var now = SystemClock.Instance.GetCurrentInstant();
var lastCheckIn = await db.AccountCheckInResults var lastCheckIn = await db.AccountCheckInResults
@@ -204,7 +205,7 @@ public class AccountEventService(
return lastDate < currentDate; return lastDate < currentDate;
} }
public async Task<bool> CheckInBackdatedIsAvailable(Account user, Instant backdated) public async Task<bool> CheckInBackdatedIsAvailable(SnAccount user, Instant backdated)
{ {
var aDay = Duration.FromDays(1); var aDay = Duration.FromDays(1);
var backdatedStart = backdated.ToDateTimeUtc().Date.ToInstant(); var backdatedStart = backdated.ToDateTimeUtc().Date.ToInstant();
@@ -252,7 +253,7 @@ public class AccountEventService(
public const string CheckInLockKey = "checkin:lock:"; public const string CheckInLockKey = "checkin:lock:";
public async Task<CheckInResult> CheckInDaily(Account user, Instant? backdated = null) public async Task<SnCheckInResult> CheckInDaily(SnAccount user, Instant? backdated = null)
{ {
var lockKey = $"{CheckInLockKey}{user.Id}"; var lockKey = $"{CheckInLockKey}{user.Id}";
@@ -270,9 +271,7 @@ public class AccountEventService(
// Now try to acquire the lock properly // Now try to acquire the lock properly
await using var lockObj = await using var lockObj =
await cache.AcquireLockAsync(lockKey, TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(5)); await cache.AcquireLockAsync(lockKey, TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(5)) ?? throw new InvalidOperationException("Check-in was in progress.");
if (lockObj is null) throw new InvalidOperationException("Check-in was in progress.");
var cultureInfo = new CultureInfo(user.Language, false); var cultureInfo = new CultureInfo(user.Language, false);
CultureInfo.CurrentCulture = cultureInfo; CultureInfo.CurrentCulture = cultureInfo;
CultureInfo.CurrentUICulture = cultureInfo; CultureInfo.CurrentUICulture = cultureInfo;
@@ -282,9 +281,10 @@ public class AccountEventService(
.OrderBy(_ => Random.Next()) .OrderBy(_ => Random.Next())
.Take(2) .Take(2)
.ToList(); .ToList();
var tips = positiveIndices.Select(index => new FortuneTip var tips = positiveIndices.Select(index => new CheckInFortuneTip
{ {
IsPositive = true, Title = localizer[$"FortuneTipPositiveTitle_{index}"].Value, IsPositive = true,
Title = localizer[$"FortuneTipPositiveTitle_{index}"].Value,
Content = localizer[$"FortuneTipPositiveContent_{index}"].Value Content = localizer[$"FortuneTipPositiveContent_{index}"].Value
}).ToList(); }).ToList();
@@ -294,9 +294,10 @@ public class AccountEventService(
.OrderBy(_ => Random.Next()) .OrderBy(_ => Random.Next())
.Take(2) .Take(2)
.ToList(); .ToList();
tips.AddRange(negativeIndices.Select(index => new FortuneTip tips.AddRange(negativeIndices.Select(index => new CheckInFortuneTip
{ {
IsPositive = false, Title = localizer[$"FortuneTipNegativeTitle_{index}"].Value, IsPositive = false,
Title = localizer[$"FortuneTipNegativeTitle_{index}"].Value,
Content = localizer[$"FortuneTipNegativeContent_{index}"].Value Content = localizer[$"FortuneTipNegativeContent_{index}"].Value
})); }));
@@ -312,7 +313,7 @@ public class AccountEventService(
if (accountBirthday.HasValue && accountBirthday.Value.InUtc().Date == now) if (accountBirthday.HasValue && accountBirthday.Value.InUtc().Date == now)
checkInLevel = CheckInResultLevel.Special; checkInLevel = CheckInResultLevel.Special;
var result = new CheckInResult var result = new SnCheckInResult
{ {
Tips = tips, Tips = tips,
Level = checkInLevel, Level = checkInLevel,
@@ -353,7 +354,7 @@ public class AccountEventService(
return result; return result;
} }
public async Task<List<DailyEventResponse>> GetEventCalendar(Account user, int month, int year = 0, public async Task<List<DailyEventResponse>> GetEventCalendar(SnAccount user, int month, int year = 0,
bool replaceInvisible = false) bool replaceInvisible = false)
{ {
if (year == 0) if (year == 0)
@@ -367,7 +368,7 @@ public class AccountEventService(
.AsNoTracking() .AsNoTracking()
.TagWith("eventcal:statuses") .TagWith("eventcal:statuses")
.Where(x => x.AccountId == user.Id && x.CreatedAt >= startOfMonth && x.CreatedAt < endOfMonth) .Where(x => x.AccountId == user.Id && x.CreatedAt >= startOfMonth && x.CreatedAt < endOfMonth)
.Select(x => new Status .Select(x => new SnAccountStatus
{ {
Id = x.Id, Id = x.Id,
Attitude = x.Attitude, Attitude = x.Attitude,
@@ -405,7 +406,7 @@ public class AccountEventService(
{ {
Date = date, Date = date,
CheckInResult = checkInByDate.GetValueOrDefault(utcDate), CheckInResult = checkInByDate.GetValueOrDefault(utcDate),
Statuses = statusesByDate.GetValueOrDefault(utcDate, new List<Status>()) Statuses = statusesByDate.GetValueOrDefault(utcDate, new List<SnAccountStatus>())
}; };
}).ToList(); }).ToList();
} }

View File

@@ -1,19 +1,14 @@
using System.Globalization; using System.Globalization;
using System.Text.Json;
using DysonNetwork.Pass.Auth;
using DysonNetwork.Pass.Auth.OpenId; using DysonNetwork.Pass.Auth.OpenId;
using DysonNetwork.Pass.Localization; using DysonNetwork.Pass.Localization;
using DysonNetwork.Pass.Mailer; using DysonNetwork.Pass.Mailer;
using DysonNetwork.Pass.Permission;
using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Data; using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Proto;
using DysonNetwork.Shared.Stream; using DysonNetwork.Shared.Stream;
using EFCore.BulkExtensions;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using NATS.Client.Core; using NATS.Client.Core;
using NATS.Client.JetStream;
using NATS.Net; using NATS.Net;
using NodaTime; using NodaTime;
using OtpNet; using OtpNet;
@@ -36,7 +31,7 @@ public class AccountService(
INatsConnection nats INatsConnection nats
) )
{ {
public static void SetCultureInfo(Account account) public static void SetCultureInfo(SnAccount account)
{ {
SetCultureInfo(account.Language); SetCultureInfo(account.Language);
} }
@@ -50,12 +45,12 @@ public class AccountService(
public const string AccountCachePrefix = "account:"; public const string AccountCachePrefix = "account:";
public async Task PurgeAccountCache(Account account) public async Task PurgeAccountCache(SnAccount account)
{ {
await cache.RemoveGroupAsync($"{AccountCachePrefix}{account.Id}"); await cache.RemoveGroupAsync($"{AccountCachePrefix}{account.Id}");
} }
public async Task<Account?> LookupAccount(string probe) public async Task<SnAccount?> LookupAccount(string probe)
{ {
var account = await db.Accounts.Where(a => a.Name == probe).FirstOrDefaultAsync(); var account = await db.Accounts.Where(a => a.Name == probe).FirstOrDefaultAsync();
if (account is not null) return account; if (account is not null) return account;
@@ -67,7 +62,7 @@ public class AccountService(
return contact?.Account; return contact?.Account;
} }
public async Task<Account?> LookupAccountByConnection(string identifier, string provider) public async Task<SnAccount?> LookupAccountByConnection(string identifier, string provider)
{ {
var connection = await db.AccountConnections var connection = await db.AccountConnections
.Where(c => c.ProvidedIdentifier == identifier && c.Provider == provider) .Where(c => c.ProvidedIdentifier == identifier && c.Provider == provider)
@@ -84,7 +79,7 @@ public class AccountService(
return profile?.Level; return profile?.Level;
} }
public async Task<Account> CreateAccount( public async Task<SnAccount> CreateAccount(
string name, string name,
string nick, string nick,
string email, string email,
@@ -100,39 +95,39 @@ public class AccountService(
throw new InvalidOperationException("Account name has already been taken."); throw new InvalidOperationException("Account name has already been taken.");
var dupeEmailCount = await db.AccountContacts var dupeEmailCount = await db.AccountContacts
.Where(c => c.Content == email && c.Type == AccountContactType.Email .Where(c => c.Content == email && c.Type == Shared.Models.AccountContactType.Email
).CountAsync(); ).CountAsync();
if (dupeEmailCount > 0) if (dupeEmailCount > 0)
throw new InvalidOperationException("Account email has already been used."); throw new InvalidOperationException("Account email has already been used.");
var account = new Account var account = new SnAccount
{ {
Name = name, Name = name,
Nick = nick, Nick = nick,
Language = language, Language = language,
Region = region, Region = region,
Contacts = new List<AccountContact> Contacts =
{ [
new() new()
{ {
Type = AccountContactType.Email, Type = Shared.Models.AccountContactType.Email,
Content = email, Content = email,
VerifiedAt = isEmailVerified ? SystemClock.Instance.GetCurrentInstant() : null, VerifiedAt = isEmailVerified ? SystemClock.Instance.GetCurrentInstant() : null,
IsPrimary = true IsPrimary = true
} }
}, ],
AuthFactors = password is not null AuthFactors = password is not null
? new List<AccountAuthFactor> ? new List<SnAccountAuthFactor>
{ {
new AccountAuthFactor new SnAccountAuthFactor
{ {
Type = AccountAuthFactorType.Password, Type = Shared.Models.AccountAuthFactorType.Password,
Secret = password, Secret = password,
EnabledAt = SystemClock.Instance.GetCurrentInstant() EnabledAt = SystemClock.Instance.GetCurrentInstant()
}.HashSecret() }.HashSecret()
} }
: [], : [],
Profile = new AccountProfile() Profile = new SnAccountProfile()
}; };
if (isActivated) if (isActivated)
@@ -141,7 +136,7 @@ public class AccountService(
var defaultGroup = await db.PermissionGroups.FirstOrDefaultAsync(g => g.Key == "default"); var defaultGroup = await db.PermissionGroups.FirstOrDefaultAsync(g => g.Key == "default");
if (defaultGroup is not null) if (defaultGroup is not null)
{ {
db.PermissionGroupMembers.Add(new PermissionGroupMember db.PermissionGroupMembers.Add(new SnPermissionGroupMember
{ {
Actor = $"user:{account.Id}", Actor = $"user:{account.Id}",
Group = defaultGroup Group = defaultGroup
@@ -167,7 +162,7 @@ public class AccountService(
return account; return account;
} }
public async Task<Account> CreateAccount(OidcUserInfo userInfo) public async Task<SnAccount> CreateAccount(OidcUserInfo userInfo)
{ {
if (string.IsNullOrEmpty(userInfo.Email)) if (string.IsNullOrEmpty(userInfo.Email))
throw new ArgumentException("Email is required for account creation"); throw new ArgumentException("Email is required for account creation");
@@ -191,7 +186,7 @@ public class AccountService(
); );
} }
public async Task<Account> CreateBotAccount(Account account, Guid automatedId, string? pictureId, public async Task<SnAccount> CreateBotAccount(SnAccount account, Guid automatedId, string? pictureId,
string? backgroundId) string? backgroundId)
{ {
var dupeAutomateCount = await db.Accounts.Where(a => a.AutomatedId == automatedId).CountAsync(); var dupeAutomateCount = await db.Accounts.Where(a => a.AutomatedId == automatedId).CountAsync();
@@ -217,7 +212,7 @@ public class AccountService(
Usage = "profile.picture" Usage = "profile.picture"
} }
); );
account.Profile.Picture = CloudFileReferenceObject.FromProtoValue(file); account.Profile.Picture = SnCloudFileReferenceObject.FromProtoValue(file);
} }
if (!string.IsNullOrEmpty(backgroundId)) if (!string.IsNullOrEmpty(backgroundId))
@@ -231,7 +226,7 @@ public class AccountService(
Usage = "profile.background" Usage = "profile.background"
} }
); );
account.Profile.Background = CloudFileReferenceObject.FromProtoValue(file); account.Profile.Background = SnCloudFileReferenceObject.FromProtoValue(file);
} }
db.Accounts.Add(account); db.Accounts.Add(account);
@@ -240,12 +235,12 @@ public class AccountService(
return account; return account;
} }
public async Task<Account?> GetBotAccount(Guid automatedId) public async Task<SnAccount?> GetBotAccount(Guid automatedId)
{ {
return await db.Accounts.FirstOrDefaultAsync(a => a.AutomatedId == automatedId); return await db.Accounts.FirstOrDefaultAsync(a => a.AutomatedId == automatedId);
} }
public async Task RequestAccountDeletion(Account account) public async Task RequestAccountDeletion(SnAccount account)
{ {
var spell = await spells.CreateMagicSpell( var spell = await spells.CreateMagicSpell(
account, account,
@@ -257,7 +252,7 @@ public class AccountService(
await spells.NotifyMagicSpell(spell); await spells.NotifyMagicSpell(spell);
} }
public async Task RequestPasswordReset(Account account) public async Task RequestPasswordReset(SnAccount account)
{ {
var spell = await spells.CreateMagicSpell( var spell = await spells.CreateMagicSpell(
account, account,
@@ -269,7 +264,7 @@ public class AccountService(
await spells.NotifyMagicSpell(spell); await spells.NotifyMagicSpell(spell);
} }
public async Task<bool> CheckAuthFactorExists(Account account, AccountAuthFactorType type) public async Task<bool> CheckAuthFactorExists(SnAccount account, Shared.Models.AccountAuthFactorType type)
{ {
var isExists = await db.AccountAuthFactors var isExists = await db.AccountAuthFactors
.Where(x => x.AccountId == account.Id && x.Type == type) .Where(x => x.AccountId == account.Id && x.Type == type)
@@ -277,45 +272,45 @@ public class AccountService(
return isExists; return isExists;
} }
public async Task<AccountAuthFactor?> CreateAuthFactor(Account account, AccountAuthFactorType type, string? secret) public async Task<SnAccountAuthFactor?> CreateAuthFactor(SnAccount account, Shared.Models.AccountAuthFactorType type, string? secret)
{ {
AccountAuthFactor? factor = null; SnAccountAuthFactor? factor = null;
switch (type) switch (type)
{ {
case AccountAuthFactorType.Password: case Shared.Models.AccountAuthFactorType.Password:
if (string.IsNullOrWhiteSpace(secret)) throw new ArgumentNullException(nameof(secret)); if (string.IsNullOrWhiteSpace(secret)) throw new ArgumentNullException(nameof(secret));
factor = new AccountAuthFactor factor = new SnAccountAuthFactor
{ {
Type = AccountAuthFactorType.Password, Type = Shared.Models.AccountAuthFactorType.Password,
Trustworthy = 1, Trustworthy = 1,
AccountId = account.Id, AccountId = account.Id,
Secret = secret, Secret = secret,
EnabledAt = SystemClock.Instance.GetCurrentInstant(), EnabledAt = SystemClock.Instance.GetCurrentInstant(),
}.HashSecret(); }.HashSecret();
break; break;
case AccountAuthFactorType.EmailCode: case Shared.Models.AccountAuthFactorType.EmailCode:
factor = new AccountAuthFactor factor = new SnAccountAuthFactor
{ {
Type = AccountAuthFactorType.EmailCode, Type = Shared.Models.AccountAuthFactorType.EmailCode,
Trustworthy = 2, Trustworthy = 2,
EnabledAt = SystemClock.Instance.GetCurrentInstant(), EnabledAt = SystemClock.Instance.GetCurrentInstant(),
}; };
break; break;
case AccountAuthFactorType.InAppCode: case Shared.Models.AccountAuthFactorType.InAppCode:
factor = new AccountAuthFactor factor = new SnAccountAuthFactor
{ {
Type = AccountAuthFactorType.InAppCode, Type = Shared.Models.AccountAuthFactorType.InAppCode,
Trustworthy = 1, Trustworthy = 1,
EnabledAt = SystemClock.Instance.GetCurrentInstant() EnabledAt = SystemClock.Instance.GetCurrentInstant()
}; };
break; break;
case AccountAuthFactorType.TimedCode: case Shared.Models.AccountAuthFactorType.TimedCode:
var skOtp = KeyGeneration.GenerateRandomKey(20); var skOtp = KeyGeneration.GenerateRandomKey(20);
var skOtp32 = Base32Encoding.ToString(skOtp); var skOtp32 = Base32Encoding.ToString(skOtp);
factor = new AccountAuthFactor factor = new SnAccountAuthFactor
{ {
Secret = skOtp32, Secret = skOtp32,
Type = AccountAuthFactorType.TimedCode, Type = Shared.Models.AccountAuthFactorType.TimedCode,
Trustworthy = 2, Trustworthy = 2,
EnabledAt = null, // It needs to be tired once to enable EnabledAt = null, // It needs to be tired once to enable
CreatedResponse = new Dictionary<string, object> CreatedResponse = new Dictionary<string, object>
@@ -329,13 +324,13 @@ public class AccountService(
} }
}; };
break; break;
case AccountAuthFactorType.PinCode: case Shared.Models.AccountAuthFactorType.PinCode:
if (string.IsNullOrWhiteSpace(secret)) throw new ArgumentNullException(nameof(secret)); if (string.IsNullOrWhiteSpace(secret)) throw new ArgumentNullException(nameof(secret));
if (!secret.All(char.IsDigit) || secret.Length != 6) if (!secret.All(char.IsDigit) || secret.Length != 6)
throw new ArgumentException("PIN code must be exactly 6 digits"); throw new ArgumentException("PIN code must be exactly 6 digits");
factor = new AccountAuthFactor factor = new SnAccountAuthFactor
{ {
Type = AccountAuthFactorType.PinCode, Type = Shared.Models.AccountAuthFactorType.PinCode,
Trustworthy = 0, // Only for confirming, can't be used for login Trustworthy = 0, // Only for confirming, can't be used for login
Secret = secret, Secret = secret,
EnabledAt = SystemClock.Instance.GetCurrentInstant(), EnabledAt = SystemClock.Instance.GetCurrentInstant(),
@@ -352,10 +347,10 @@ public class AccountService(
return factor; return factor;
} }
public async Task<AccountAuthFactor> EnableAuthFactor(AccountAuthFactor factor, string? code) public async Task<SnAccountAuthFactor> EnableAuthFactor(SnAccountAuthFactor factor, string? code)
{ {
if (factor.EnabledAt is not null) throw new ArgumentException("The factor has been enabled."); if (factor.EnabledAt is not null) throw new ArgumentException("The factor has been enabled.");
if (factor.Type is AccountAuthFactorType.Password or AccountAuthFactorType.TimedCode) if (factor.Type is Shared.Models.AccountAuthFactorType.Password or Shared.Models.AccountAuthFactorType.TimedCode)
{ {
if (code is null || !factor.VerifyPassword(code)) if (code is null || !factor.VerifyPassword(code))
throw new InvalidOperationException( throw new InvalidOperationException(
@@ -370,7 +365,7 @@ public class AccountService(
return factor; return factor;
} }
public async Task<AccountAuthFactor> DisableAuthFactor(AccountAuthFactor factor) public async Task<SnAccountAuthFactor> DisableAuthFactor(SnAccountAuthFactor factor)
{ {
if (factor.EnabledAt is null) throw new ArgumentException("The factor has been disabled."); if (factor.EnabledAt is null) throw new ArgumentException("The factor has been disabled.");
@@ -388,7 +383,7 @@ public class AccountService(
return factor; return factor;
} }
public async Task DeleteAuthFactor(AccountAuthFactor factor) public async Task DeleteAuthFactor(SnAccountAuthFactor factor)
{ {
var count = await db.AccountAuthFactors var count = await db.AccountAuthFactors
.Where(f => f.AccountId == factor.AccountId) .Where(f => f.AccountId == factor.AccountId)
@@ -406,13 +401,13 @@ public class AccountService(
/// </summary> /// </summary>
/// <param name="account">The owner of the auth factor</param> /// <param name="account">The owner of the auth factor</param>
/// <param name="factor">The auth factor needed to send code</param> /// <param name="factor">The auth factor needed to send code</param>
public async Task SendFactorCode(Account account, AccountAuthFactor factor) public async Task SendFactorCode(SnAccount account, SnAccountAuthFactor factor)
{ {
var code = new Random().Next(100000, 999999).ToString("000000"); var code = new Random().Next(100000, 999999).ToString("000000");
switch (factor.Type) switch (factor.Type)
{ {
case AccountAuthFactorType.InAppCode: case Shared.Models.AccountAuthFactorType.InAppCode:
if (await _GetFactorCode(factor) is not null) if (await _GetFactorCode(factor) is not null)
throw new InvalidOperationException("A factor code has been sent and in active duration."); throw new InvalidOperationException("A factor code has been sent and in active duration.");
@@ -431,12 +426,12 @@ public class AccountService(
); );
await _SetFactorCode(factor, code, TimeSpan.FromMinutes(5)); await _SetFactorCode(factor, code, TimeSpan.FromMinutes(5));
break; break;
case AccountAuthFactorType.EmailCode: case Shared.Models.AccountAuthFactorType.EmailCode:
if (await _GetFactorCode(factor) is not null) if (await _GetFactorCode(factor) is not null)
throw new InvalidOperationException("A factor code has been sent and in active duration."); throw new InvalidOperationException("A factor code has been sent and in active duration.");
var contact = await db.AccountContacts var contact = await db.AccountContacts
.Where(c => c.Type == AccountContactType.Email) .Where(c => c.Type == Shared.Models.AccountContactType.Email)
.Where(c => c.VerifiedAt != null) .Where(c => c.VerifiedAt != null)
.Where(c => c.IsPrimary) .Where(c => c.IsPrimary)
.Where(c => c.AccountId == account.Id) .Where(c => c.AccountId == account.Id)
@@ -465,27 +460,27 @@ public class AccountService(
await _SetFactorCode(factor, code, TimeSpan.FromMinutes(30)); await _SetFactorCode(factor, code, TimeSpan.FromMinutes(30));
break; break;
case AccountAuthFactorType.Password: case Shared.Models.AccountAuthFactorType.Password:
case AccountAuthFactorType.TimedCode: case Shared.Models.AccountAuthFactorType.TimedCode:
default: default:
// No need to send, such as password etc... // No need to send, such as password etc...
return; return;
} }
} }
public async Task<bool> VerifyFactorCode(AccountAuthFactor factor, string code) public async Task<bool> VerifyFactorCode(SnAccountAuthFactor factor, string code)
{ {
switch (factor.Type) switch (factor.Type)
{ {
case AccountAuthFactorType.EmailCode: case Shared.Models.AccountAuthFactorType.EmailCode:
case AccountAuthFactorType.InAppCode: case Shared.Models.AccountAuthFactorType.InAppCode:
var correctCode = await _GetFactorCode(factor); var correctCode = await _GetFactorCode(factor);
var isCorrect = correctCode is not null && var isCorrect = correctCode is not null &&
string.Equals(correctCode, code, StringComparison.OrdinalIgnoreCase); string.Equals(correctCode, code, StringComparison.OrdinalIgnoreCase);
await cache.RemoveAsync($"{AuthFactorCachePrefix}{factor.Id}:code"); await cache.RemoveAsync($"{AuthFactorCachePrefix}{factor.Id}:code");
return isCorrect; return isCorrect;
case AccountAuthFactorType.Password: case Shared.Models.AccountAuthFactorType.Password:
case AccountAuthFactorType.TimedCode: case Shared.Models.AccountAuthFactorType.TimedCode:
default: default:
return factor.VerifyPassword(code); return factor.VerifyPassword(code);
} }
@@ -493,7 +488,7 @@ public class AccountService(
private const string AuthFactorCachePrefix = "authfactor:"; private const string AuthFactorCachePrefix = "authfactor:";
private async Task _SetFactorCode(AccountAuthFactor factor, string code, TimeSpan expires) private async Task _SetFactorCode(SnAccountAuthFactor factor, string code, TimeSpan expires)
{ {
await cache.SetAsync( await cache.SetAsync(
$"{AuthFactorCachePrefix}{factor.Id}:code", $"{AuthFactorCachePrefix}{factor.Id}:code",
@@ -502,7 +497,7 @@ public class AccountService(
); );
} }
private async Task<string?> _GetFactorCode(AccountAuthFactor factor) private async Task<string?> _GetFactorCode(SnAccountAuthFactor factor)
{ {
return await cache.GetAsync<string?>( return await cache.GetAsync<string?>(
$"{AuthFactorCachePrefix}{factor.Id}:code" $"{AuthFactorCachePrefix}{factor.Id}:code"
@@ -516,7 +511,7 @@ public class AccountService(
.AnyAsync(s => s.Challenge.ClientId == id); .AnyAsync(s => s.Challenge.ClientId == id);
} }
public async Task<AuthClient> UpdateDeviceName(Account account, string deviceId, string label) public async Task<SnAuthClient> UpdateDeviceName(SnAccount account, string deviceId, string label)
{ {
var device = await db.AuthClients.FirstOrDefaultAsync(c => c.DeviceId == deviceId && c.AccountId == account.Id var device = await db.AuthClients.FirstOrDefaultAsync(c => c.DeviceId == deviceId && c.AccountId == account.Id
); );
@@ -529,7 +524,7 @@ public class AccountService(
return device; return device;
} }
public async Task DeleteSession(Account account, Guid sessionId) public async Task DeleteSession(SnAccount account, Guid sessionId)
{ {
var session = await db.AuthSessions var session = await db.AuthSessions
.Include(s => s.Challenge) .Include(s => s.Challenge)
@@ -555,7 +550,7 @@ public class AccountService(
await cache.RemoveAsync($"{AuthService.AuthCachePrefix}{session.Id}"); await cache.RemoveAsync($"{AuthService.AuthCachePrefix}{session.Id}");
} }
public async Task DeleteDevice(Account account, string deviceId) public async Task DeleteDevice(SnAccount account, string deviceId)
{ {
var device = await db.AuthClients.FirstOrDefaultAsync(c => c.DeviceId == deviceId && c.AccountId == account.Id var device = await db.AuthClients.FirstOrDefaultAsync(c => c.DeviceId == deviceId && c.AccountId == account.Id
); );
@@ -585,7 +580,7 @@ public class AccountService(
await cache.RemoveAsync($"{AuthService.AuthCachePrefix}{item.Id}"); await cache.RemoveAsync($"{AuthService.AuthCachePrefix}{item.Id}");
} }
public async Task<AccountContact> CreateContactMethod(Account account, AccountContactType type, string content) public async Task<SnAccountContact> CreateContactMethod(SnAccount account, Shared.Models.AccountContactType type, string content)
{ {
var isExists = await db.AccountContacts var isExists = await db.AccountContacts
.Where(x => x.AccountId == account.Id && x.Type == type && x.Content == content) .Where(x => x.AccountId == account.Id && x.Type == type && x.Content == content)
@@ -593,7 +588,7 @@ public class AccountService(
if (isExists) if (isExists)
throw new InvalidOperationException("Contact method already exists."); throw new InvalidOperationException("Contact method already exists.");
var contact = new AccountContact var contact = new SnAccountContact
{ {
Type = type, Type = type,
Content = content, Content = content,
@@ -606,7 +601,7 @@ public class AccountService(
return contact; return contact;
} }
public async Task VerifyContactMethod(Account account, AccountContact contact) public async Task VerifyContactMethod(SnAccount account, SnAccountContact contact)
{ {
var spell = await spells.CreateMagicSpell( var spell = await spells.CreateMagicSpell(
account, account,
@@ -618,7 +613,7 @@ public class AccountService(
await spells.NotifyMagicSpell(spell); await spells.NotifyMagicSpell(spell);
} }
public async Task<AccountContact> SetContactMethodPrimary(Account account, AccountContact contact) public async Task<SnAccountContact> SetContactMethodPrimary(SnAccount account, SnAccountContact contact)
{ {
if (contact.AccountId != account.Id) if (contact.AccountId != account.Id)
throw new InvalidOperationException("Contact method does not belong to this account."); throw new InvalidOperationException("Contact method does not belong to this account.");
@@ -647,7 +642,7 @@ public class AccountService(
} }
} }
public async Task<AccountContact> SetContactMethodPublic(Account account, AccountContact contact, bool isPublic) public async Task<SnAccountContact> SetContactMethodPublic(SnAccount account, SnAccountContact contact, bool isPublic)
{ {
contact.IsPublic = isPublic; contact.IsPublic = isPublic;
db.AccountContacts.Update(contact); db.AccountContacts.Update(contact);
@@ -655,7 +650,7 @@ public class AccountService(
return contact; return contact;
} }
public async Task DeleteContactMethod(Account account, AccountContact contact) public async Task DeleteContactMethod(SnAccount account, SnAccountContact contact)
{ {
if (contact.AccountId != account.Id) if (contact.AccountId != account.Id)
throw new InvalidOperationException("Contact method does not belong to this account."); throw new InvalidOperationException("Contact method does not belong to this account.");
@@ -670,7 +665,7 @@ public class AccountService(
/// This method will grant a badge to the account. /// This method will grant a badge to the account.
/// Shouldn't be exposed to normal user and the user itself. /// Shouldn't be exposed to normal user and the user itself.
/// </summary> /// </summary>
public async Task<AccountBadge> GrantBadge(Account account, AccountBadge badge) public async Task<SnAccountBadge> GrantBadge(SnAccount account, SnAccountBadge badge)
{ {
badge.AccountId = account.Id; badge.AccountId = account.Id;
db.Badges.Add(badge); db.Badges.Add(badge);
@@ -682,14 +677,12 @@ public class AccountService(
/// This method will revoke a badge from the account. /// This method will revoke a badge from the account.
/// Shouldn't be exposed to normal user and the user itself. /// Shouldn't be exposed to normal user and the user itself.
/// </summary> /// </summary>
public async Task RevokeBadge(Account account, Guid badgeId) public async Task RevokeBadge(SnAccount account, Guid badgeId)
{ {
var badge = await db.Badges var badge = await db.Badges
.Where(b => b.AccountId == account.Id && b.Id == badgeId) .Where(b => b.AccountId == account.Id && b.Id == badgeId)
.OrderByDescending(b => b.CreatedAt) .OrderByDescending(b => b.CreatedAt)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync() ?? throw new InvalidOperationException("Badge was not found.");
if (badge is null) throw new InvalidOperationException("Badge was not found.");
var profile = await db.AccountProfiles var profile = await db.AccountProfiles
.Where(p => p.AccountId == account.Id) .Where(p => p.AccountId == account.Id)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
@@ -700,7 +693,7 @@ public class AccountService(
await db.SaveChangesAsync(); await db.SaveChangesAsync();
} }
public async Task ActiveBadge(Account account, Guid badgeId) public async Task ActiveBadge(SnAccount account, Guid badgeId)
{ {
await using var transaction = await db.Database.BeginTransactionAsync(); await using var transaction = await db.Database.BeginTransactionAsync();
@@ -734,7 +727,7 @@ public class AccountService(
} }
} }
public async Task DeleteAccount(Account account) public async Task DeleteAccount(SnAccount account)
{ {
await db.AuthSessions await db.AuthSessions
.Where(s => s.AccountId == account.Id) .Where(s => s.AccountId == account.Id)

View File

@@ -236,7 +236,7 @@ public class AccountServiceGrpc(
var relationship = await relationships.GetRelationship( var relationship = await relationships.GetRelationship(
Guid.Parse(request.AccountId), Guid.Parse(request.AccountId),
Guid.Parse(request.RelatedId), Guid.Parse(request.RelatedId),
status: (RelationshipStatus?)request.Status status: (Shared.Models.RelationshipStatus?)request.Status
); );
return new GetRelationshipResponse return new GetRelationshipResponse
{ {
@@ -256,7 +256,7 @@ public class AccountServiceGrpc(
hasRelationship = await relationships.HasRelationshipWithStatus( hasRelationship = await relationships.HasRelationshipWithStatus(
Guid.Parse(request.AccountId), Guid.Parse(request.AccountId),
Guid.Parse(request.RelatedId), Guid.Parse(request.RelatedId),
(RelationshipStatus)request.Status (Shared.Models.RelationshipStatus)request.Status
); );
return new BoolValue { Value = hasRelationship }; return new BoolValue { Value = hasRelationship };
} }

View File

@@ -1,46 +0,0 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;
using DysonNetwork.Shared.Data;
using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Proto;
using NodaTime.Serialization.Protobuf;
using Point = NetTopologySuite.Geometries.Point;
namespace DysonNetwork.Pass.Account;
public class ActionLog : ModelBase
{
public Guid Id { get; set; } = Guid.NewGuid();
[MaxLength(4096)] public string Action { get; set; } = null!;
[Column(TypeName = "jsonb")] public Dictionary<string, object> Meta { get; set; } = new();
[MaxLength(512)] public string? UserAgent { get; set; }
[MaxLength(128)] public string? IpAddress { get; set; }
[Column(TypeName = "jsonb")] public GeoPoint? Location { get; set; }
public Guid AccountId { get; set; }
public Account Account { get; set; } = null!;
public Guid? SessionId { get; set; }
public Shared.Proto.ActionLog ToProtoValue()
{
var protoLog = new Shared.Proto.ActionLog
{
Id = Id.ToString(),
Action = Action,
UserAgent = UserAgent ?? string.Empty,
IpAddress = IpAddress ?? string.Empty,
Location = Location?.ToString() ?? string.Empty,
AccountId = AccountId.ToString(),
CreatedAt = CreatedAt.ToTimestamp()
};
// Convert Meta dictionary to Struct
protoLog.Meta.Add(GrpcTypeHelper.ConvertToValueMap(Meta));
if (SessionId.HasValue)
protoLog.SessionId = SessionId.Value.ToString();
return protoLog;
}
}

View File

@@ -1,5 +1,6 @@
using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.GeoIp; using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Models;
namespace DysonNetwork.Pass.Account; namespace DysonNetwork.Pass.Account;
@@ -7,7 +8,7 @@ public class ActionLogService(GeoIpService geo, FlushBufferService fbs)
{ {
public void CreateActionLog(Guid accountId, string action, Dictionary<string, object?> meta) public void CreateActionLog(Guid accountId, string action, Dictionary<string, object?> meta)
{ {
var log = new ActionLog var log = new SnActionLog
{ {
Action = action, Action = action,
AccountId = accountId, AccountId = accountId,
@@ -18,9 +19,9 @@ public class ActionLogService(GeoIpService geo, FlushBufferService fbs)
} }
public void CreateActionLogFromRequest(string action, Dictionary<string, object> meta, HttpRequest request, public void CreateActionLogFromRequest(string action, Dictionary<string, object> meta, HttpRequest request,
Account? account = null) SnAccount? account = null)
{ {
var log = new ActionLog var log = new SnActionLog
{ {
Action = action, Action = action,
Meta = meta, Meta = meta,
@@ -29,14 +30,14 @@ public class ActionLogService(GeoIpService geo, FlushBufferService fbs)
Location = geo.GetPointFromIp(request.HttpContext.Connection.RemoteIpAddress?.ToString()) Location = geo.GetPointFromIp(request.HttpContext.Connection.RemoteIpAddress?.ToString())
}; };
if (request.HttpContext.Items["CurrentUser"] is Account currentUser) if (request.HttpContext.Items["CurrentUser"] is SnAccount currentUser)
log.AccountId = currentUser.Id; log.AccountId = currentUser.Id;
else if (account != null) else if (account != null)
log.AccountId = account.Id; log.AccountId = account.Id;
else else
throw new ArgumentException("No user context was found"); throw new ArgumentException("No user context was found");
if (request.HttpContext.Items["CurrentSession"] is Auth.AuthSession currentSession) if (request.HttpContext.Items["CurrentSession"] is SnAuthSession currentSession)
log.SessionId = currentSession.Id; log.SessionId = currentSession.Id;
fbs.Enqueue(log); fbs.Enqueue(log);

View File

@@ -1,4 +1,4 @@
using DysonNetwork.Shared.Data; using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Proto;
using Grpc.Core; using Grpc.Core;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@@ -22,7 +22,7 @@ public class BotAccountReceiverGrpc(
ServerCallContext context ServerCallContext context
) )
{ {
var account = Account.FromProtoValue(request.Account); var account = SnAccount.FromProtoValue(request.Account);
account = await accounts.CreateBotAccount( account = await accounts.CreateBotAccount(
account, account,
Guid.Parse(request.AutomatedId), Guid.Parse(request.AutomatedId),
@@ -48,7 +48,7 @@ public class BotAccountReceiverGrpc(
ServerCallContext context ServerCallContext context
) )
{ {
var account = Account.FromProtoValue(request.Account); var account = SnAccount.FromProtoValue(request.Account);
if (request.PictureId is not null) if (request.PictureId is not null)
{ {
@@ -65,7 +65,7 @@ public class BotAccountReceiverGrpc(
Usage = "profile.picture" Usage = "profile.picture"
} }
); );
account.Profile.Picture = CloudFileReferenceObject.FromProtoValue(file); account.Profile.Picture = SnCloudFileReferenceObject.FromProtoValue(file);
} }
if (request.BackgroundId is not null) if (request.BackgroundId is not null)
@@ -83,7 +83,7 @@ public class BotAccountReceiverGrpc(
Usage = "profile.background" Usage = "profile.background"
} }
); );
account.Profile.Background = CloudFileReferenceObject.FromProtoValue(file); account.Profile.Background = SnCloudFileReferenceObject.FromProtoValue(file);
} }
db.Accounts.Update(account); db.Accounts.Update(account);

View File

@@ -50,7 +50,7 @@ public class MagicSpellController(AppDatabase db, MagicSpellService sp) : Contro
return NotFound(); return NotFound();
try try
{ {
if (spell.Type == MagicSpellType.AuthPasswordReset && request?.NewPassword is not null) if (spell.Type == Shared.Models.MagicSpellType.AuthPasswordReset && request?.NewPassword is not null)
await sp.ApplyPasswordReset(spell, request.NewPassword); await sp.ApplyPasswordReset(spell, request.NewPassword);
else else
await sp.ApplyMagicSpell(spell); await sp.ApplyMagicSpell(spell);

View File

@@ -2,8 +2,8 @@ using System.Security.Cryptography;
using System.Text.Json; using System.Text.Json;
using DysonNetwork.Pass.Emails; using DysonNetwork.Pass.Emails;
using DysonNetwork.Pass.Mailer; using DysonNetwork.Pass.Mailer;
using DysonNetwork.Pass.Permission;
using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using NodaTime; using NodaTime;
@@ -20,8 +20,8 @@ public class MagicSpellService(
ICacheService cache ICacheService cache
) )
{ {
public async Task<MagicSpell> CreateMagicSpell( public async Task<SnMagicSpell> CreateMagicSpell(
Account account, SnAccount account,
MagicSpellType type, MagicSpellType type,
Dictionary<string, object> meta, Dictionary<string, object> meta,
Instant? expiredAt = null, Instant? expiredAt = null,
@@ -42,7 +42,7 @@ public class MagicSpellService(
} }
var spellWord = _GenerateRandomString(128); var spellWord = _GenerateRandomString(128);
var spell = new MagicSpell var spell = new SnMagicSpell
{ {
Spell = spellWord, Spell = spellWord,
Type = type, Type = type,
@@ -60,7 +60,7 @@ public class MagicSpellService(
private const string SpellNotifyCacheKeyPrefix = "spells:notify:"; private const string SpellNotifyCacheKeyPrefix = "spells:notify:";
public async Task NotifyMagicSpell(MagicSpell spell, bool bypassVerify = false) public async Task NotifyMagicSpell(SnMagicSpell spell, bool bypassVerify = false)
{ {
var cacheKey = SpellNotifyCacheKeyPrefix + spell.Id; var cacheKey = SpellNotifyCacheKeyPrefix + spell.Id;
var (found, _) = await cache.GetAsyncWithStatus<bool?>(cacheKey); var (found, _) = await cache.GetAsyncWithStatus<bool?>(cacheKey);
@@ -156,7 +156,7 @@ public class MagicSpellService(
} }
} }
public async Task ApplyMagicSpell(MagicSpell spell) public async Task ApplyMagicSpell(SnMagicSpell spell)
{ {
switch (spell.Type) switch (spell.Type)
{ {
@@ -191,7 +191,7 @@ public class MagicSpellService(
var defaultGroup = await db.PermissionGroups.FirstOrDefaultAsync(g => g.Key == "default"); var defaultGroup = await db.PermissionGroups.FirstOrDefaultAsync(g => g.Key == "default");
if (defaultGroup is not null && account is not null) if (defaultGroup is not null && account is not null)
{ {
db.PermissionGroupMembers.Add(new PermissionGroupMember db.PermissionGroupMembers.Add(new SnPermissionGroupMember
{ {
Actor = $"user:{account.Id}", Actor = $"user:{account.Id}",
Group = defaultGroup Group = defaultGroup
@@ -218,7 +218,7 @@ public class MagicSpellService(
await db.SaveChangesAsync(); await db.SaveChangesAsync();
} }
public async Task ApplyPasswordReset(MagicSpell spell, string newPassword) public async Task ApplyPasswordReset(SnMagicSpell spell, string newPassword)
{ {
if (spell.Type != MagicSpellType.AuthPasswordReset) if (spell.Type != MagicSpellType.AuthPasswordReset)
throw new ArgumentException("This spell is not a password reset spell."); throw new ArgumentException("This spell is not a password reset spell.");
@@ -231,7 +231,7 @@ public class MagicSpellService(
{ {
var account = await db.Accounts.FirstOrDefaultAsync(c => c.Id == spell.AccountId); var account = await db.Accounts.FirstOrDefaultAsync(c => c.Id == spell.AccountId);
if (account is null) throw new InvalidOperationException("Both account and auth factor was not found."); if (account is null) throw new InvalidOperationException("Both account and auth factor was not found.");
passwordFactor = new AccountAuthFactor passwordFactor = new SnAccountAuthFactor
{ {
Type = AccountAuthFactorType.Password, Type = AccountAuthFactorType.Password,
Account = account, Account = account,
@@ -257,6 +257,6 @@ public class MagicSpellService(
var base64String = Convert.ToBase64String(randomBytes); var base64String = Convert.ToBase64String(randomBytes);
return base64String.Substring(0, length); return base64String[..length];
} }
} }

View File

@@ -1,3 +1,4 @@
using DysonNetwork.Shared.Models;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@@ -26,7 +27,7 @@ public class NotableDaysController(NotableDaysService days) : ControllerBase
[Authorize] [Authorize]
public async Task<ActionResult<List<NotableDay>>> GetAccountNotableDays(int year) public async Task<ActionResult<List<NotableDay>>> GetAccountNotableDays(int year)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var region = currentUser.Region; var region = currentUser.Region;
if (string.IsNullOrWhiteSpace(region)) region = "us"; if (string.IsNullOrWhiteSpace(region)) region = "us";
@@ -39,7 +40,7 @@ public class NotableDaysController(NotableDaysService days) : ControllerBase
[Authorize] [Authorize]
public async Task<ActionResult<List<NotableDay>>> GetAccountNotableDaysCurrentYear() public async Task<ActionResult<List<NotableDay>>> GetAccountNotableDaysCurrentYear()
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var currentYear = DateTime.Now.Year; var currentYear = DateTime.Now.Year;
var region = currentUser.Region; var region = currentUser.Region;
@@ -64,7 +65,7 @@ public class NotableDaysController(NotableDaysService days) : ControllerBase
[Authorize] [Authorize]
public async Task<ActionResult<NotableDay?>> GetAccountNextHoliday() public async Task<ActionResult<NotableDay?>> GetAccountNextHoliday()
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var region = currentUser.Region; var region = currentUser.Region;
if (string.IsNullOrWhiteSpace(region)) region = "us"; if (string.IsNullOrWhiteSpace(region)) region = "us";

View File

@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using DysonNetwork.Shared.Models;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@@ -12,10 +13,10 @@ public class RelationshipController(AppDatabase db, RelationshipService rels) :
{ {
[HttpGet] [HttpGet]
[Authorize] [Authorize]
public async Task<ActionResult<List<Relationship>>> ListRelationships([FromQuery] int offset = 0, public async Task<ActionResult<List<SnAccountRelationship>>> ListRelationships([FromQuery] int offset = 0,
[FromQuery] int take = 20) [FromQuery] int take = 20)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var userId = currentUser.Id; var userId = currentUser.Id;
var query = db.AccountRelationships.AsQueryable() var query = db.AccountRelationships.AsQueryable()
@@ -44,9 +45,9 @@ public class RelationshipController(AppDatabase db, RelationshipService rels) :
[HttpGet("requests")] [HttpGet("requests")]
[Authorize] [Authorize]
public async Task<ActionResult<List<Relationship>>> ListSentRequests() public async Task<ActionResult<List<SnAccountRelationship>>> ListSentRequests()
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var relationships = await db.AccountRelationships var relationships = await db.AccountRelationships
.Where(r => r.AccountId == currentUser.Id && r.Status == RelationshipStatus.Pending) .Where(r => r.AccountId == currentUser.Id && r.Status == RelationshipStatus.Pending)
@@ -66,10 +67,10 @@ public class RelationshipController(AppDatabase db, RelationshipService rels) :
[HttpPost("{userId:guid}")] [HttpPost("{userId:guid}")]
[Authorize] [Authorize]
public async Task<ActionResult<Relationship>> CreateRelationship(Guid userId, public async Task<ActionResult<SnAccountRelationship>> CreateRelationship(Guid userId,
[FromBody] RelationshipRequest request) [FromBody] RelationshipRequest request)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var relatedUser = await db.Accounts.FindAsync(userId); var relatedUser = await db.Accounts.FindAsync(userId);
if (relatedUser is null) return NotFound("Account was not found."); if (relatedUser is null) return NotFound("Account was not found.");
@@ -89,10 +90,10 @@ public class RelationshipController(AppDatabase db, RelationshipService rels) :
[HttpPatch("{userId:guid}")] [HttpPatch("{userId:guid}")]
[Authorize] [Authorize]
public async Task<ActionResult<Relationship>> UpdateRelationship(Guid userId, public async Task<ActionResult<SnAccountRelationship>> UpdateRelationship(Guid userId,
[FromBody] RelationshipRequest request) [FromBody] RelationshipRequest request)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
try try
{ {
@@ -111,9 +112,9 @@ public class RelationshipController(AppDatabase db, RelationshipService rels) :
[HttpGet("{userId:guid}")] [HttpGet("{userId:guid}")]
[Authorize] [Authorize]
public async Task<ActionResult<Relationship>> GetRelationship(Guid userId) public async Task<ActionResult<SnAccountRelationship>> GetRelationship(Guid userId)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var now = Instant.FromDateTimeUtc(DateTime.UtcNow); var now = Instant.FromDateTimeUtc(DateTime.UtcNow);
var queries = db.AccountRelationships.AsQueryable() var queries = db.AccountRelationships.AsQueryable()
@@ -131,9 +132,9 @@ public class RelationshipController(AppDatabase db, RelationshipService rels) :
[HttpPost("{userId:guid}/friends")] [HttpPost("{userId:guid}/friends")]
[Authorize] [Authorize]
public async Task<ActionResult<Relationship>> SendFriendRequest(Guid userId) public async Task<ActionResult<SnAccountRelationship>> SendFriendRequest(Guid userId)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var relatedUser = await db.Accounts.FindAsync(userId); var relatedUser = await db.Accounts.FindAsync(userId);
if (relatedUser is null) return NotFound("Account was not found."); if (relatedUser is null) return NotFound("Account was not found.");
@@ -158,7 +159,7 @@ public class RelationshipController(AppDatabase db, RelationshipService rels) :
[Authorize] [Authorize]
public async Task<ActionResult> DeleteFriendRequest(Guid userId) public async Task<ActionResult> DeleteFriendRequest(Guid userId)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
try try
{ {
@@ -173,9 +174,9 @@ public class RelationshipController(AppDatabase db, RelationshipService rels) :
[HttpPost("{userId:guid}/friends/accept")] [HttpPost("{userId:guid}/friends/accept")]
[Authorize] [Authorize]
public async Task<ActionResult<Relationship>> AcceptFriendRequest(Guid userId) public async Task<ActionResult<SnAccountRelationship>> AcceptFriendRequest(Guid userId)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var relationship = await rels.GetRelationship(userId, currentUser.Id, RelationshipStatus.Pending); var relationship = await rels.GetRelationship(userId, currentUser.Id, RelationshipStatus.Pending);
if (relationship is null) return NotFound("Friend request was not found."); if (relationship is null) return NotFound("Friend request was not found.");
@@ -193,9 +194,9 @@ public class RelationshipController(AppDatabase db, RelationshipService rels) :
[HttpPost("{userId:guid}/friends/decline")] [HttpPost("{userId:guid}/friends/decline")]
[Authorize] [Authorize]
public async Task<ActionResult<Relationship>> DeclineFriendRequest(Guid userId) public async Task<ActionResult<SnAccountRelationship>> DeclineFriendRequest(Guid userId)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var relationship = await rels.GetRelationship(userId, currentUser.Id, RelationshipStatus.Pending); var relationship = await rels.GetRelationship(userId, currentUser.Id, RelationshipStatus.Pending);
if (relationship is null) return NotFound("Friend request was not found."); if (relationship is null) return NotFound("Friend request was not found.");
@@ -213,9 +214,9 @@ public class RelationshipController(AppDatabase db, RelationshipService rels) :
[HttpPost("{userId:guid}/block")] [HttpPost("{userId:guid}/block")]
[Authorize] [Authorize]
public async Task<ActionResult<Relationship>> BlockUser(Guid userId) public async Task<ActionResult<SnAccountRelationship>> BlockUser(Guid userId)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var relatedUser = await db.Accounts.FindAsync(userId); var relatedUser = await db.Accounts.FindAsync(userId);
if (relatedUser is null) return NotFound("Account was not found."); if (relatedUser is null) return NotFound("Account was not found.");
@@ -233,9 +234,9 @@ public class RelationshipController(AppDatabase db, RelationshipService rels) :
[HttpDelete("{userId:guid}/block")] [HttpDelete("{userId:guid}/block")]
[Authorize] [Authorize]
public async Task<ActionResult<Relationship>> UnblockUser(Guid userId) public async Task<ActionResult<SnAccountRelationship>> UnblockUser(Guid userId)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var relatedUser = await db.Accounts.FindAsync(userId); var relatedUser = await db.Accounts.FindAsync(userId);
if (relatedUser is null) return NotFound("Account was not found."); if (relatedUser is null) return NotFound("Account was not found.");

View File

@@ -1,5 +1,6 @@
using DysonNetwork.Pass.Localization; using DysonNetwork.Pass.Localization;
using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Proto;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
@@ -26,7 +27,7 @@ public class RelationshipService(
return count > 0; return count > 0;
} }
public async Task<Relationship?> GetRelationship( public async Task<SnAccountRelationship?> GetRelationship(
Guid accountId, Guid accountId,
Guid relatedId, Guid relatedId,
RelationshipStatus? status = null, RelationshipStatus? status = null,
@@ -42,7 +43,7 @@ public class RelationshipService(
return relationship; return relationship;
} }
public async Task<Relationship> CreateRelationship(Account sender, Account target, RelationshipStatus status) public async Task<SnAccountRelationship> CreateRelationship(SnAccount sender, SnAccount target, RelationshipStatus status)
{ {
if (status == RelationshipStatus.Pending) if (status == RelationshipStatus.Pending)
throw new InvalidOperationException( throw new InvalidOperationException(
@@ -50,7 +51,7 @@ public class RelationshipService(
if (await HasExistingRelationship(sender.Id, target.Id)) if (await HasExistingRelationship(sender.Id, target.Id))
throw new InvalidOperationException("Found existing relationship between you and target user."); throw new InvalidOperationException("Found existing relationship between you and target user.");
var relationship = new Relationship var relationship = new SnAccountRelationship
{ {
AccountId = sender.Id, AccountId = sender.Id,
RelatedId = target.Id, RelatedId = target.Id,
@@ -65,14 +66,14 @@ public class RelationshipService(
return relationship; return relationship;
} }
public async Task<Relationship> BlockAccount(Account sender, Account target) public async Task<SnAccountRelationship> BlockAccount(SnAccount sender, SnAccount target)
{ {
if (await HasExistingRelationship(sender.Id, target.Id)) if (await HasExistingRelationship(sender.Id, target.Id))
return await UpdateRelationship(sender.Id, target.Id, RelationshipStatus.Blocked); return await UpdateRelationship(sender.Id, target.Id, RelationshipStatus.Blocked);
return await CreateRelationship(sender, target, RelationshipStatus.Blocked); return await CreateRelationship(sender, target, RelationshipStatus.Blocked);
} }
public async Task<Relationship> UnblockAccount(Account sender, Account target) public async Task<SnAccountRelationship> UnblockAccount(SnAccount sender, SnAccount target)
{ {
var relationship = await GetRelationship(sender.Id, target.Id, RelationshipStatus.Blocked); var relationship = await GetRelationship(sender.Id, target.Id, RelationshipStatus.Blocked);
if (relationship is null) throw new ArgumentException("There is no relationship between you and the user."); if (relationship is null) throw new ArgumentException("There is no relationship between you and the user.");
@@ -84,12 +85,12 @@ public class RelationshipService(
return relationship; return relationship;
} }
public async Task<Relationship> SendFriendRequest(Account sender, Account target) public async Task<SnAccountRelationship> SendFriendRequest(SnAccount sender, SnAccount target)
{ {
if (await HasExistingRelationship(sender.Id, target.Id)) if (await HasExistingRelationship(sender.Id, target.Id))
throw new InvalidOperationException("Found existing relationship between you and target user."); throw new InvalidOperationException("Found existing relationship between you and target user.");
var relationship = new Relationship var relationship = new SnAccountRelationship
{ {
AccountId = sender.Id, AccountId = sender.Id,
RelatedId = target.Id, RelatedId = target.Id,
@@ -128,8 +129,8 @@ public class RelationshipService(
await PurgeRelationshipCache(relationship.AccountId, relationship.RelatedId); await PurgeRelationshipCache(relationship.AccountId, relationship.RelatedId);
} }
public async Task<Relationship> AcceptFriendRelationship( public async Task<SnAccountRelationship> AcceptFriendRelationship(
Relationship relationship, SnAccountRelationship relationship,
RelationshipStatus status = RelationshipStatus.Friends RelationshipStatus status = RelationshipStatus.Friends
) )
{ {
@@ -144,7 +145,7 @@ public class RelationshipService(
relationship.ExpiredAt = null; relationship.ExpiredAt = null;
db.Update(relationship); db.Update(relationship);
var relationshipBackward = new Relationship var relationshipBackward = new SnAccountRelationship
{ {
AccountId = relationship.RelatedId, AccountId = relationship.RelatedId,
RelatedId = relationship.AccountId, RelatedId = relationship.AccountId,
@@ -159,7 +160,7 @@ public class RelationshipService(
return relationshipBackward; return relationshipBackward;
} }
public async Task<Relationship> UpdateRelationship(Guid accountId, Guid relatedId, RelationshipStatus status) public async Task<SnAccountRelationship> UpdateRelationship(Guid accountId, Guid relatedId, RelationshipStatus status)
{ {
var relationship = await GetRelationship(accountId, relatedId); var relationship = await GetRelationship(accountId, relatedId);
if (relationship is null) throw new ArgumentException("There is no relationship between you and the user."); if (relationship is null) throw new ArgumentException("There is no relationship between you and the user.");
@@ -173,7 +174,7 @@ public class RelationshipService(
return relationship; return relationship;
} }
public async Task<List<Guid>> ListAccountFriends(Account account) public async Task<List<Guid>> ListAccountFriends(SnAccount account)
{ {
return await ListAccountFriends(account.Id); return await ListAccountFriends(account.Id);
} }
@@ -197,7 +198,7 @@ public class RelationshipService(
return friends ?? []; return friends ?? [];
} }
public async Task<List<Guid>> ListAccountBlocked(Account account) public async Task<List<Guid>> ListAccountBlocked(SnAccount account)
{ {
return await ListAccountBlocked(account.Id); return await ListAccountBlocked(account.Id);
} }

View File

@@ -2,13 +2,8 @@ using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using DysonNetwork.Pass.Account;
using DysonNetwork.Pass.Auth;
using DysonNetwork.Pass.Credit;
using DysonNetwork.Pass.Leveling;
using DysonNetwork.Pass.Permission; using DysonNetwork.Pass.Permission;
using DysonNetwork.Pass.Wallet; using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design; using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Query;
@@ -22,39 +17,39 @@ public class AppDatabase(
IConfiguration configuration IConfiguration configuration
) : DbContext(options) ) : DbContext(options)
{ {
public DbSet<PermissionNode> PermissionNodes { get; set; } = null!; public DbSet<SnPermissionNode> PermissionNodes { get; set; } = null!;
public DbSet<PermissionGroup> PermissionGroups { get; set; } = null!; public DbSet<SnPermissionGroup> PermissionGroups { get; set; } = null!;
public DbSet<PermissionGroupMember> PermissionGroupMembers { get; set; } = null!; public DbSet<SnPermissionGroupMember> PermissionGroupMembers { get; set; } = null!;
public DbSet<MagicSpell> MagicSpells { get; set; } = null!; public DbSet<SnMagicSpell> MagicSpells { get; set; } = null!;
public DbSet<Account.Account> Accounts { get; set; } = null!; public DbSet<SnAccount> Accounts { get; set; } = null!;
public DbSet<AccountConnection> AccountConnections { get; set; } = null!; public DbSet<SnAccountConnection> AccountConnections { get; set; } = null!;
public DbSet<AccountProfile> AccountProfiles { get; set; } = null!; public DbSet<SnAccountProfile> AccountProfiles { get; set; } = null!;
public DbSet<AccountContact> AccountContacts { get; set; } = null!; public DbSet<SnAccountContact> AccountContacts { get; set; } = null!;
public DbSet<AccountAuthFactor> AccountAuthFactors { get; set; } = null!; public DbSet<SnAccountAuthFactor> AccountAuthFactors { get; set; } = null!;
public DbSet<Relationship> AccountRelationships { get; set; } = null!; public DbSet<SnAccountRelationship> AccountRelationships { get; set; } = null!;
public DbSet<Status> AccountStatuses { get; set; } = null!; public DbSet<SnAccountStatus> AccountStatuses { get; set; } = null!;
public DbSet<CheckInResult> AccountCheckInResults { get; set; } = null!; public DbSet<SnCheckInResult> AccountCheckInResults { get; set; } = null!;
public DbSet<AccountBadge> Badges { get; set; } = null!; public DbSet<SnAccountBadge> Badges { get; set; } = null!;
public DbSet<ActionLog> ActionLogs { get; set; } = null!; public DbSet<SnActionLog> ActionLogs { get; set; } = null!;
public DbSet<AbuseReport> AbuseReports { get; set; } = null!; public DbSet<SnAbuseReport> AbuseReports { get; set; } = null!;
public DbSet<AuthSession> AuthSessions { get; set; } = null!; public DbSet<SnAuthSession> AuthSessions { get; set; } = null!;
public DbSet<AuthChallenge> AuthChallenges { get; set; } = null!; public DbSet<SnAuthChallenge> AuthChallenges { get; set; } = null!;
public DbSet<AuthClient> AuthClients { get; set; } = null!; public DbSet<SnAuthClient> AuthClients { get; set; } = null!;
public DbSet<ApiKey> ApiKeys { get; set; } = null!; public DbSet<SnApiKey> ApiKeys { get; set; } = null!;
public DbSet<Wallet.Wallet> Wallets { get; set; } = null!; public DbSet<SnWallet> Wallets { get; set; } = null!;
public DbSet<WalletPocket> WalletPockets { get; set; } = null!; public DbSet<SnWalletPocket> WalletPockets { get; set; } = null!;
public DbSet<Order> PaymentOrders { get; set; } = null!; public DbSet<SnWalletOrder> PaymentOrders { get; set; } = null!;
public DbSet<Transaction> PaymentTransactions { get; set; } = null!; public DbSet<SnWalletTransaction> PaymentTransactions { get; set; } = null!;
public DbSet<Subscription> WalletSubscriptions { get; set; } = null!; public DbSet<SnWalletSubscription> WalletSubscriptions { get; set; } = null!;
public DbSet<Coupon> WalletCoupons { get; set; } = null!; public DbSet<SnWalletCoupon> WalletCoupons { get; set; } = null!;
public DbSet<Punishment> Punishments { get; set; } = null!; public DbSet<SnAccountPunishment> Punishments { get; set; } = null!;
public DbSet<SocialCreditRecord> SocialCreditRecords { get; set; } = null!; public DbSet<SnSocialCreditRecord> SocialCreditRecords { get; set; } = null!;
public DbSet<ExperienceRecord> ExperienceRecords { get; set; } = null!; public DbSet<SnExperienceRecord> ExperienceRecords { get; set; } = null!;
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{ {
@@ -74,11 +69,11 @@ public class AppDatabase(
optionsBuilder.UseAsyncSeeding(async (context, _, cancellationToken) => optionsBuilder.UseAsyncSeeding(async (context, _, cancellationToken) =>
{ {
var defaultPermissionGroup = await context.Set<PermissionGroup>() var defaultPermissionGroup = await context.Set<SnPermissionGroup>()
.FirstOrDefaultAsync(g => g.Key == "default", cancellationToken); .FirstOrDefaultAsync(g => g.Key == "default", cancellationToken);
if (defaultPermissionGroup is null) if (defaultPermissionGroup is null)
{ {
context.Set<PermissionGroup>().Add(new PermissionGroup context.Set<SnPermissionGroup>().Add(new SnPermissionGroup
{ {
Key = "default", Key = "default",
Nodes = new List<string> Nodes = new List<string>
@@ -111,21 +106,21 @@ public class AppDatabase(
{ {
base.OnModelCreating(modelBuilder); base.OnModelCreating(modelBuilder);
modelBuilder.Entity<PermissionGroupMember>() modelBuilder.Entity<SnPermissionGroupMember>()
.HasKey(pg => new { pg.GroupId, pg.Actor }); .HasKey(pg => new { pg.GroupId, pg.Actor });
modelBuilder.Entity<PermissionGroupMember>() modelBuilder.Entity<SnPermissionGroupMember>()
.HasOne(pg => pg.Group) .HasOne(pg => pg.Group)
.WithMany(g => g.Members) .WithMany(g => g.Members)
.HasForeignKey(pg => pg.GroupId) .HasForeignKey(pg => pg.GroupId)
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<Relationship>() modelBuilder.Entity<SnAccountRelationship>()
.HasKey(r => new { FromAccountId = r.AccountId, ToAccountId = r.RelatedId }); .HasKey(r => new { FromAccountId = r.AccountId, ToAccountId = r.RelatedId });
modelBuilder.Entity<Relationship>() modelBuilder.Entity<SnAccountRelationship>()
.HasOne(r => r.Account) .HasOne(r => r.Account)
.WithMany(a => a.OutgoingRelationships) .WithMany(a => a.OutgoingRelationships)
.HasForeignKey(r => r.AccountId); .HasForeignKey(r => r.AccountId);
modelBuilder.Entity<Relationship>() modelBuilder.Entity<SnAccountRelationship>()
.HasOne(r => r.Related) .HasOne(r => r.Related)
.WithMany(a => a.IncomingRelationships) .WithMany(a => a.IncomingRelationships)
.HasForeignKey(r => r.RelatedId); .HasForeignKey(r => r.RelatedId);

View File

@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using DysonNetwork.Shared.Models;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@@ -14,7 +15,7 @@ public class ApiKeyController(AppDatabase db, AuthService auth) : ControllerBase
[Authorize] [Authorize]
public async Task<IActionResult> GetKeys([FromQuery] int offset = 0, [FromQuery] int take = 20) public async Task<IActionResult> GetKeys([FromQuery] int offset = 0, [FromQuery] int take = 20)
{ {
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var query = db.ApiKeys var query = db.ApiKeys
.Where(e => e.AccountId == currentUser.Id) .Where(e => e.AccountId == currentUser.Id)
@@ -34,7 +35,7 @@ public class ApiKeyController(AppDatabase db, AuthService auth) : ControllerBase
[Authorize] [Authorize]
public async Task<IActionResult> GetKey(Guid id) public async Task<IActionResult> GetKey(Guid id)
{ {
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var key = await db.ApiKeys var key = await db.ApiKeys
.Where(e => e.AccountId == currentUser.Id) .Where(e => e.AccountId == currentUser.Id)
@@ -56,7 +57,7 @@ public class ApiKeyController(AppDatabase db, AuthService auth) : ControllerBase
{ {
if (string.IsNullOrWhiteSpace(request.Label)) if (string.IsNullOrWhiteSpace(request.Label))
return BadRequest("Label is required"); return BadRequest("Label is required");
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var key = await auth.CreateApiKey(currentUser.Id, request.Label, request.ExpiredAt); var key = await auth.CreateApiKey(currentUser.Id, request.Label, request.ExpiredAt);
key.Key = await auth.IssueApiKeyToken(key); key.Key = await auth.IssueApiKeyToken(key);
@@ -67,7 +68,7 @@ public class ApiKeyController(AppDatabase db, AuthService auth) : ControllerBase
[Authorize] [Authorize]
public async Task<IActionResult> RotateKey(Guid id) public async Task<IActionResult> RotateKey(Guid id)
{ {
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var key = await auth.GetApiKey(id, currentUser.Id); var key = await auth.GetApiKey(id, currentUser.Id);
if(key is null) return NotFound(); if(key is null) return NotFound();
@@ -80,7 +81,7 @@ public class ApiKeyController(AppDatabase db, AuthService auth) : ControllerBase
[Authorize] [Authorize]
public async Task<IActionResult> DeleteKey(Guid id) public async Task<IActionResult> DeleteKey(Guid id)
{ {
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var key = await auth.GetApiKey(id, currentUser.Id); var key = await auth.GetApiKey(id, currentUser.Id);
if(key is null) return NotFound(); if(key is null) return NotFound();

View File

@@ -1,5 +1,3 @@
using NodaTime;
namespace DysonNetwork.Pass.Auth; namespace DysonNetwork.Pass.Auth;
public static class AuthCacheConstants public static class AuthCacheConstants

View File

@@ -2,15 +2,13 @@ using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using NodaTime; using NodaTime;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using DysonNetwork.Pass.Account;
using DysonNetwork.Pass.Localization; using DysonNetwork.Pass.Localization;
using DysonNetwork.Shared.Data;
using DysonNetwork.Shared.GeoIp; using DysonNetwork.Shared.GeoIp;
using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Proto;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using AccountAuthFactor = DysonNetwork.Pass.Account.AccountAuthFactor;
using AccountService = DysonNetwork.Pass.Account.AccountService; using AccountService = DysonNetwork.Pass.Account.AccountService;
using ActionLogService = DysonNetwork.Pass.Account.ActionLogService; using ActionLogService = DysonNetwork.Pass.Account.ActionLogService;
using DysonNetwork.Shared.Models;
namespace DysonNetwork.Pass.Auth; namespace DysonNetwork.Pass.Auth;
@@ -40,7 +38,7 @@ public class AuthController(
} }
[HttpPost("challenge")] [HttpPost("challenge")]
public async Task<ActionResult<AuthChallenge>> CreateChallenge([FromBody] ChallengeRequest request) public async Task<ActionResult<SnAuthChallenge>> CreateChallenge([FromBody] ChallengeRequest request)
{ {
var account = await accounts.LookupAccount(request.Account); var account = await accounts.LookupAccount(request.Account);
if (account is null) return NotFound("Account was not found."); if (account is null) return NotFound("Account was not found.");
@@ -72,7 +70,7 @@ public class AuthController(
.Where(e => e.UserAgent == userAgent) .Where(e => e.UserAgent == userAgent)
.Where(e => e.StepRemain > 0) .Where(e => e.StepRemain > 0)
.Where(e => e.ExpiredAt != null && now < e.ExpiredAt) .Where(e => e.ExpiredAt != null && now < e.ExpiredAt)
.Where(e => e.Type == ChallengeType.Login) .Where(e => e.Type == Shared.Models.ChallengeType.Login)
.Where(e => e.ClientId == device.Id) .Where(e => e.ClientId == device.Id)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
if (existingChallenge is not null) if (existingChallenge is not null)
@@ -82,7 +80,7 @@ public class AuthController(
if (existingSession is null) return existingChallenge; if (existingSession is null) return existingChallenge;
} }
var challenge = new AuthChallenge var challenge = new SnAuthChallenge
{ {
ExpiredAt = Instant.FromDateTimeUtc(DateTime.UtcNow.AddHours(1)), ExpiredAt = Instant.FromDateTimeUtc(DateTime.UtcNow.AddHours(1)),
StepTotal = await auth.DetectChallengeRisk(Request, account), StepTotal = await auth.DetectChallengeRisk(Request, account),
@@ -106,7 +104,7 @@ public class AuthController(
} }
[HttpGet("challenge/{id:guid}")] [HttpGet("challenge/{id:guid}")]
public async Task<ActionResult<AuthChallenge>> GetChallenge([FromRoute] Guid id) public async Task<ActionResult<SnAuthChallenge>> GetChallenge([FromRoute] Guid id)
{ {
var challenge = await db.AuthChallenges var challenge = await db.AuthChallenges
.Include(e => e.Account) .Include(e => e.Account)
@@ -119,7 +117,7 @@ public class AuthController(
} }
[HttpGet("challenge/{id:guid}/factors")] [HttpGet("challenge/{id:guid}/factors")]
public async Task<ActionResult<List<AccountAuthFactor>>> GetChallengeFactors([FromRoute] Guid id) public async Task<ActionResult<List<SnAccountAuthFactor>>> GetChallengeFactors([FromRoute] Guid id)
{ {
var challenge = await db.AuthChallenges var challenge = await db.AuthChallenges
.Include(e => e.Account) .Include(e => e.Account)
@@ -165,7 +163,7 @@ public class AuthController(
} }
[HttpPatch("challenge/{id:guid}")] [HttpPatch("challenge/{id:guid}")]
public async Task<ActionResult<AuthChallenge>> DoChallenge( public async Task<ActionResult<SnAuthChallenge>> DoChallenge(
[FromRoute] Guid id, [FromRoute] Guid id,
[FromBody] PerformChallengeRequest request [FromBody] PerformChallengeRequest request
) )

View File

@@ -1,8 +1,8 @@
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using DysonNetwork.Pass.Account;
using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NodaTime; using NodaTime;
@@ -13,8 +13,7 @@ public class AuthService(
IConfiguration config, IConfiguration config,
IHttpClientFactory httpClientFactory, IHttpClientFactory httpClientFactory,
IHttpContextAccessor httpContextAccessor, IHttpContextAccessor httpContextAccessor,
ICacheService cache, ICacheService cache
ILogger<AuthService> logger
) )
{ {
private HttpContext HttpContext => httpContextAccessor.HttpContext!; private HttpContext HttpContext => httpContextAccessor.HttpContext!;
@@ -27,7 +26,7 @@ public class AuthService(
/// <param name="request">The request context</param> /// <param name="request">The request context</param>
/// <param name="account">The account to login</param> /// <param name="account">The account to login</param>
/// <returns>The required steps to login</returns> /// <returns>The required steps to login</returns>
public async Task<int> DetectChallengeRisk(HttpRequest request, Account.Account account) public async Task<int> DetectChallengeRisk(HttpRequest request, SnAccount account)
{ {
// 1) Find out how many authentication factors the account has enabled. // 1) Find out how many authentication factors the account has enabled.
var maxSteps = await db.AccountAuthFactors var maxSteps = await db.AccountAuthFactors
@@ -76,10 +75,10 @@ public class AuthService(
return totalRequiredSteps; return totalRequiredSteps;
} }
public async Task<AuthSession> CreateSessionForOidcAsync(Account.Account account, Instant time, public async Task<SnAuthSession> CreateSessionForOidcAsync(SnAccount account, Instant time,
Guid? customAppId = null) Guid? customAppId = null)
{ {
var challenge = new AuthChallenge var challenge = new SnAuthChallenge
{ {
AccountId = account.Id, AccountId = account.Id,
IpAddress = HttpContext.Connection.RemoteIpAddress?.ToString(), IpAddress = HttpContext.Connection.RemoteIpAddress?.ToString(),
@@ -89,7 +88,7 @@ public class AuthService(
Type = customAppId is not null ? ChallengeType.OAuth : ChallengeType.Oidc Type = customAppId is not null ? ChallengeType.OAuth : ChallengeType.Oidc
}; };
var session = new AuthSession var session = new SnAuthSession
{ {
AccountId = account.Id, AccountId = account.Id,
CreatedAt = time, CreatedAt = time,
@@ -105,7 +104,7 @@ public class AuthService(
return session; return session;
} }
public async Task<AuthClient> GetOrCreateDeviceAsync( public async Task<SnAuthClient> GetOrCreateDeviceAsync(
Guid accountId, Guid accountId,
string deviceId, string deviceId,
string? deviceName = null, string? deviceName = null,
@@ -114,7 +113,7 @@ public class AuthService(
{ {
var device = await db.AuthClients.FirstOrDefaultAsync(d => d.DeviceId == deviceId && d.AccountId == accountId); var device = await db.AuthClients.FirstOrDefaultAsync(d => d.DeviceId == deviceId && d.AccountId == accountId);
if (device is not null) return device; if (device is not null) return device;
device = new AuthClient device = new SnAuthClient
{ {
Platform = platform, Platform = platform,
DeviceId = deviceId, DeviceId = deviceId,
@@ -181,7 +180,7 @@ public class AuthService(
} }
} }
public string CreateToken(AuthSession session) public string CreateToken(SnAuthSession session)
{ {
// Load the private key for signing // Load the private key for signing
var privateKeyPem = File.ReadAllText(config["AuthToken:PrivateKeyPath"]!); var privateKeyPem = File.ReadAllText(config["AuthToken:PrivateKeyPath"]!);
@@ -199,7 +198,7 @@ public class AuthService(
/// <param name="challenge">Completed challenge</param> /// <param name="challenge">Completed challenge</param>
/// <returns>Signed compact token</returns> /// <returns>Signed compact token</returns>
/// <exception cref="ArgumentException">If challenge not completed or session already exists</exception> /// <exception cref="ArgumentException">If challenge not completed or session already exists</exception>
public async Task<string> CreateSessionAndIssueToken(AuthChallenge challenge) public async Task<string> CreateSessionAndIssueToken(SnAuthChallenge challenge)
{ {
if (challenge.StepRemain != 0) if (challenge.StepRemain != 0)
throw new ArgumentException("Challenge not yet completed."); throw new ArgumentException("Challenge not yet completed.");
@@ -210,7 +209,7 @@ public class AuthService(
throw new ArgumentException("Session already exists for this challenge."); throw new ArgumentException("Session already exists for this challenge.");
var now = SystemClock.Instance.GetCurrentInstant(); var now = SystemClock.Instance.GetCurrentInstant();
var session = new AuthSession var session = new SnAuthSession
{ {
LastGrantedAt = now, LastGrantedAt = now,
ExpiredAt = now.Plus(Duration.FromDays(7)), ExpiredAt = now.Plus(Duration.FromDays(7)),
@@ -256,7 +255,7 @@ public class AuthService(
return $"{payloadBase64}.{signatureBase64}"; return $"{payloadBase64}.{signatureBase64}";
} }
public async Task<bool> ValidateSudoMode(AuthSession session, string? pinCode) public async Task<bool> ValidateSudoMode(SnAuthSession session, string? pinCode)
{ {
// Check if the session is already in sudo mode (cached) // Check if the session is already in sudo mode (cached)
var sudoModeKey = $"accounts:{session.Id}:sudo"; var sudoModeKey = $"accounts:{session.Id}:sudo";
@@ -319,7 +318,7 @@ public class AuthService(
return factor.VerifyPassword(pinCode); return factor.VerifyPassword(pinCode);
} }
public async Task<ApiKey?> GetApiKey(Guid id, Guid? accountId = null) public async Task<SnApiKey?> GetApiKey(Guid id, Guid? accountId = null)
{ {
var key = await db.ApiKeys var key = await db.ApiKeys
.Include(e => e.Session) .Include(e => e.Session)
@@ -329,13 +328,13 @@ public class AuthService(
return key; return key;
} }
public async Task<ApiKey> CreateApiKey(Guid accountId, string label, Instant? expiredAt = null) public async Task<SnApiKey> CreateApiKey(Guid accountId, string label, Instant? expiredAt = null)
{ {
var key = new ApiKey var key = new SnApiKey
{ {
AccountId = accountId, AccountId = accountId,
Label = label, Label = label,
Session = new AuthSession Session = new SnAuthSession
{ {
AccountId = accountId, AccountId = accountId,
ExpiredAt = expiredAt ExpiredAt = expiredAt
@@ -348,7 +347,7 @@ public class AuthService(
return key; return key;
} }
public async Task<string> IssueApiKeyToken(ApiKey key) public async Task<string> IssueApiKeyToken(SnApiKey key)
{ {
key.Session.LastGrantedAt = SystemClock.Instance.GetCurrentInstant(); key.Session.LastGrantedAt = SystemClock.Instance.GetCurrentInstant();
db.Update(key.Session); db.Update(key.Session);
@@ -357,14 +356,14 @@ public class AuthService(
return tk; return tk;
} }
public async Task RevokeApiKeyToken(ApiKey key) public async Task RevokeApiKeyToken(SnApiKey key)
{ {
db.Remove(key); db.Remove(key);
db.Remove(key.Session); db.Remove(key.Session);
await db.SaveChangesAsync(); await db.SaveChangesAsync();
} }
public async Task<ApiKey> RotateApiKeyToken(ApiKey key) public async Task<SnApiKey> RotateApiKeyToken(SnApiKey key)
{ {
await using var transaction = await db.Database.BeginTransactionAsync(); await using var transaction = await db.Database.BeginTransactionAsync();
try try
@@ -372,7 +371,7 @@ public class AuthService(
var oldSessionId = key.SessionId; var oldSessionId = key.SessionId;
// Create new session // Create new session
var newSession = new AuthSession var newSession = new SnAuthSession
{ {
AccountId = key.AccountId, AccountId = key.AccountId,
ExpiredAt = key.Session?.ExpiredAt ExpiredAt = key.Session?.ExpiredAt

View File

@@ -1,4 +1,5 @@
using System.Security.Cryptography; using System.Security.Cryptography;
using DysonNetwork.Shared.Models;
namespace DysonNetwork.Pass.Auth; namespace DysonNetwork.Pass.Auth;
@@ -7,7 +8,7 @@ public class CompactTokenService(IConfiguration config)
private readonly string _privateKeyPath = config["AuthToken:PrivateKeyPath"] private readonly string _privateKeyPath = config["AuthToken:PrivateKeyPath"]
?? throw new InvalidOperationException("AuthToken:PrivateKeyPath configuration is missing"); ?? throw new InvalidOperationException("AuthToken:PrivateKeyPath configuration is missing");
public string CreateToken(AuthSession session) public string CreateToken(SnAuthSession session)
{ {
// Load the private key for signing // Load the private key for signing
var privateKeyPem = File.ReadAllText(_privateKeyPath); var privateKeyPem = File.ReadAllText(_privateKeyPath);

View File

@@ -6,12 +6,11 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Web; using System.Web;
using DysonNetwork.Pass.Account;
using DysonNetwork.Pass.Auth.OidcProvider.Options; using DysonNetwork.Pass.Auth.OidcProvider.Options;
using DysonNetwork.Shared.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using NodaTime; using NodaTime;
using DysonNetwork.Shared.Models;
namespace DysonNetwork.Pass.Auth.OidcProvider.Controllers; namespace DysonNetwork.Pass.Auth.OidcProvider.Controllers;
@@ -98,9 +97,9 @@ public class OidcProviderController(
var clientInfo = new ClientInfoResponse var clientInfo = new ClientInfoResponse
{ {
ClientId = Guid.Parse(client.Id), ClientId = Guid.Parse(client.Id),
Picture = client.Picture is not null ? CloudFileReferenceObject.FromProtoValue(client.Picture) : null, Picture = client.Picture is not null ? SnCloudFileReferenceObject.FromProtoValue(client.Picture) : null,
Background = client.Background is not null Background = client.Background is not null
? CloudFileReferenceObject.FromProtoValue(client.Background) ? SnCloudFileReferenceObject.FromProtoValue(client.Background)
: null, : null,
ClientName = client.Name, ClientName = client.Name,
HomeUri = client.Links.HomePage, HomeUri = client.Links.HomePage,
@@ -131,7 +130,7 @@ public class OidcProviderController(
[FromForm(Name = "code_challenge_method")] [FromForm(Name = "code_challenge_method")]
string? codeChallengeMethod = null) string? codeChallengeMethod = null)
{ {
if (HttpContext.Items["CurrentUser"] is not Account.Account account) if (HttpContext.Items["CurrentUser"] is not SnAccount account)
return Unauthorized(); return Unauthorized();
// Find the client // Find the client
@@ -303,8 +302,8 @@ public class OidcProviderController(
[Authorize] [Authorize]
public async Task<IActionResult> GetUserInfo() public async Task<IActionResult> GetUserInfo()
{ {
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser || if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser ||
HttpContext.Items["CurrentSession"] is not AuthSession currentSession) return Unauthorized(); HttpContext.Items["CurrentSession"] is not SnAuthSession currentSession) return Unauthorized();
// Get requested scopes from the token // Get requested scopes from the token
var scopes = currentSession.Challenge?.Scopes ?? []; var scopes = currentSession.Challenge?.Scopes ?? [];

View File

@@ -1,5 +1,3 @@
using System;
using System.Collections.Generic;
using NodaTime; using NodaTime;
namespace DysonNetwork.Pass.Auth.OidcProvider.Models; namespace DysonNetwork.Pass.Auth.OidcProvider.Models;

View File

@@ -1,13 +1,12 @@
using System.Text.Json.Serialization; using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Data;
namespace DysonNetwork.Pass.Auth.OidcProvider.Responses; namespace DysonNetwork.Pass.Auth.OidcProvider.Responses;
public class ClientInfoResponse public class ClientInfoResponse
{ {
public Guid ClientId { get; set; } public Guid ClientId { get; set; }
public CloudFileReferenceObject? Picture { get; set; } public SnCloudFileReferenceObject? Picture { get; set; }
public CloudFileReferenceObject? Background { get; set; } public SnCloudFileReferenceObject? Background { get; set; }
public string? ClientName { get; set; } public string? ClientName { get; set; }
public string? HomeUri { get; set; } public string? HomeUri { get; set; }
public string? PolicyUri { get; set; } public string? PolicyUri { get; set; }

View File

@@ -6,12 +6,13 @@ using DysonNetwork.Pass.Auth.OidcProvider.Models;
using DysonNetwork.Pass.Auth.OidcProvider.Options; using DysonNetwork.Pass.Auth.OidcProvider.Options;
using DysonNetwork.Pass.Auth.OidcProvider.Responses; using DysonNetwork.Pass.Auth.OidcProvider.Responses;
using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Proto;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using NodaTime; using NodaTime;
using AccountContactType = DysonNetwork.Pass.Account.AccountContactType; using AccountContactType = DysonNetwork.Shared.Models.AccountContactType;
namespace DysonNetwork.Pass.Auth.OidcProvider.Services; namespace DysonNetwork.Pass.Auth.OidcProvider.Services;
@@ -38,7 +39,7 @@ public class OidcProviderService(
return resp.App ?? null; return resp.App ?? null;
} }
public async Task<AuthSession?> FindValidSessionAsync(Guid accountId, Guid clientId, bool withAccount = false) public async Task<SnAuthSession?> FindValidSessionAsync(Guid accountId, Guid clientId, bool withAccount = false)
{ {
var now = SystemClock.Instance.GetCurrentInstant(); var now = SystemClock.Instance.GetCurrentInstant();
@@ -57,7 +58,7 @@ public class OidcProviderService(
s.AppId == clientId && s.AppId == clientId &&
(s.ExpiredAt == null || s.ExpiredAt > now) && (s.ExpiredAt == null || s.ExpiredAt > now) &&
s.Challenge != null && s.Challenge != null &&
s.Challenge.Type == ChallengeType.OAuth) s.Challenge.Type == Shared.Models.ChallengeType.OAuth)
.OrderByDescending(s => s.CreatedAt) .OrderByDescending(s => s.CreatedAt)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
} }
@@ -80,7 +81,7 @@ public class OidcProviderService(
var client = await FindClientByIdAsync(clientId); var client = await FindClientByIdAsync(clientId);
if (client?.Status != CustomAppStatus.Production) if (client?.Status != Shared.Proto.CustomAppStatus.Production)
return true; return true;
if (client?.OauthConfig?.RedirectUris == null) if (client?.OauthConfig?.RedirectUris == null)
@@ -145,7 +146,7 @@ public class OidcProviderService(
private string GenerateIdToken( private string GenerateIdToken(
CustomApp client, CustomApp client,
AuthSession session, SnAuthSession session,
string? nonce = null, string? nonce = null,
IEnumerable<string>? scopes = null IEnumerable<string>? scopes = null
) )
@@ -224,11 +225,9 @@ public class OidcProviderService(
Guid? sessionId = null Guid? sessionId = null
) )
{ {
var client = await FindClientByIdAsync(clientId); var client = await FindClientByIdAsync(clientId) ?? throw new InvalidOperationException("Client not found");
if (client == null)
throw new InvalidOperationException("Client not found");
AuthSession session; SnAuthSession session;
var clock = SystemClock.Instance; var clock = SystemClock.Instance;
var now = clock.GetCurrentInstant(); var now = clock.GetCurrentInstant();
string? nonce = null; string? nonce = null;
@@ -299,7 +298,7 @@ public class OidcProviderService(
private string GenerateJwtToken( private string GenerateJwtToken(
CustomApp client, CustomApp client,
AuthSession session, SnAuthSession session,
Instant expiresAt, Instant expiresAt,
IEnumerable<string>? scopes = null IEnumerable<string>? scopes = null
) )
@@ -371,7 +370,7 @@ public class OidcProviderService(
} }
} }
public async Task<AuthSession?> FindSessionByIdAsync(Guid sessionId) public async Task<SnAuthSession?> FindSessionByIdAsync(Guid sessionId)
{ {
return await db.AuthSessions return await db.AuthSessions
.Include(s => s.Account) .Include(s => s.Account)
@@ -379,7 +378,7 @@ public class OidcProviderService(
.FirstOrDefaultAsync(s => s.Id == sessionId); .FirstOrDefaultAsync(s => s.Id == sessionId);
} }
private static string GenerateRefreshToken(AuthSession session) private static string GenerateRefreshToken(SnAuthSession session)
{ {
return Convert.ToBase64String(session.Id.ToByteArray()); return Convert.ToBase64String(session.Id.ToByteArray());
} }

View File

@@ -1,6 +1,4 @@
using System.Net.Http.Json;
using System.Text.Json; using System.Text.Json;
using DysonNetwork.Pass;
using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Cache;
namespace DysonNetwork.Pass.Auth.OpenId; namespace DysonNetwork.Pass.Auth.OpenId;

View File

@@ -1,6 +1,5 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
namespace DysonNetwork.Pass.Auth.OpenId; namespace DysonNetwork.Pass.Auth.OpenId;

View File

@@ -3,7 +3,6 @@ using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using DysonNetwork.Pass;
using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Cache;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;

View File

@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Cache;
using NodaTime; using NodaTime;
using DysonNetwork.Shared.Models;
namespace DysonNetwork.Pass.Auth.OpenId; namespace DysonNetwork.Pass.Auth.OpenId;
@@ -23,9 +24,9 @@ public class ConnectionController(
private static readonly TimeSpan StateExpiration = TimeSpan.FromMinutes(15); private static readonly TimeSpan StateExpiration = TimeSpan.FromMinutes(15);
[HttpGet] [HttpGet]
public async Task<ActionResult<List<AccountConnection>>> GetConnections() public async Task<ActionResult<List<SnAccountConnection>>> GetConnections()
{ {
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser)
return Unauthorized(); return Unauthorized();
var connections = await db.AccountConnections var connections = await db.AccountConnections
@@ -48,7 +49,7 @@ public class ConnectionController(
[HttpDelete("{id:guid}")] [HttpDelete("{id:guid}")]
public async Task<ActionResult> RemoveConnection(Guid id) public async Task<ActionResult> RemoveConnection(Guid id)
{ {
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser)
return Unauthorized(); return Unauthorized();
var connection = await db.AccountConnections var connection = await db.AccountConnections
@@ -66,7 +67,7 @@ public class ConnectionController(
[HttpPost("/api/auth/connect/apple/mobile")] [HttpPost("/api/auth/connect/apple/mobile")]
public async Task<ActionResult> ConnectAppleMobile([FromBody] AppleMobileConnectRequest request) public async Task<ActionResult> ConnectAppleMobile([FromBody] AppleMobileConnectRequest request)
{ {
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser)
return Unauthorized(); return Unauthorized();
if (GetOidcService("apple") is not AppleOidcService appleService) if (GetOidcService("apple") is not AppleOidcService appleService)
@@ -99,7 +100,7 @@ public class ConnectionController(
$"This Apple account is already linked to {(existingConnection.AccountId == currentUser.Id ? "your account" : "another user")}."); $"This Apple account is already linked to {(existingConnection.AccountId == currentUser.Id ? "your account" : "another user")}.");
} }
db.AccountConnections.Add(new AccountConnection db.AccountConnections.Add(new SnAccountConnection
{ {
AccountId = currentUser.Id, AccountId = currentUser.Id,
Provider = "apple", Provider = "apple",
@@ -250,7 +251,7 @@ public class ConnectionController(
else else
{ {
// Create new connection // Create new connection
db.AccountConnections.Add(new AccountConnection db.AccountConnections.Add(new SnAccountConnection
{ {
AccountId = accountId, AccountId = accountId,
Provider = provider, Provider = provider,
@@ -324,7 +325,7 @@ public class ConnectionController(
var account = await accounts.LookupAccount(userInfo.Email) ?? await accounts.CreateAccount(userInfo); var account = await accounts.LookupAccount(userInfo.Email) ?? await accounts.CreateAccount(userInfo);
// Create connection for new or existing user // Create connection for new or existing user
var newConnection = new AccountConnection var newConnection = new SnAccountConnection
{ {
Account = account, Account = account,
Provider = provider, Provider = provider,

View File

@@ -1,6 +1,4 @@
using System.Net.Http.Json;
using System.Text.Json; using System.Text.Json;
using DysonNetwork.Pass;
using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Cache;
namespace DysonNetwork.Pass.Auth.OpenId; namespace DysonNetwork.Pass.Auth.OpenId;

View File

@@ -1,6 +1,4 @@
using System.Net.Http.Json;
using System.Text.Json; using System.Text.Json;
using DysonNetwork.Pass;
using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Cache;
namespace DysonNetwork.Pass.Auth.OpenId; namespace DysonNetwork.Pass.Auth.OpenId;

View File

@@ -1,8 +1,4 @@
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using System.Net.Http.Json;
using System.Security.Cryptography;
using System.Text;
using DysonNetwork.Pass;
using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Cache;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;

View File

@@ -1,5 +1,6 @@
using DysonNetwork.Pass.Account; using DysonNetwork.Pass.Account;
using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Models;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
@@ -32,7 +33,7 @@ public class OidcController(
var oidcService = GetOidcService(provider); var oidcService = GetOidcService(provider);
// If the user is already authenticated, treat as an account connection request // If the user is already authenticated, treat as an account connection request
if (HttpContext.Items["CurrentUser"] is Account.Account currentUser) if (HttpContext.Items["CurrentUser"] is SnAccount currentUser)
{ {
var state = Guid.NewGuid().ToString(); var state = Guid.NewGuid().ToString();
var nonce = Guid.NewGuid().ToString(); var nonce = Guid.NewGuid().ToString();
@@ -68,7 +69,7 @@ public class OidcController(
/// Handles Apple authentication directly from mobile apps /// Handles Apple authentication directly from mobile apps
/// </summary> /// </summary>
[HttpPost("apple/mobile")] [HttpPost("apple/mobile")]
public async Task<ActionResult<AuthChallenge>> AppleMobileLogin( public async Task<ActionResult<SnAuthChallenge>> AppleMobileLogin(
[FromBody] AppleMobileSignInRequest request [FromBody] AppleMobileSignInRequest request
) )
{ {
@@ -127,7 +128,7 @@ public class OidcController(
}; };
} }
private async Task<Account.Account> FindOrCreateAccount(OidcUserInfo userInfo, string provider) private async Task<SnAccount> FindOrCreateAccount(OidcUserInfo userInfo, string provider)
{ {
if (string.IsNullOrEmpty(userInfo.Email)) if (string.IsNullOrEmpty(userInfo.Email))
throw new ArgumentException("Email is required for account creation"); throw new ArgumentException("Email is required for account creation");
@@ -156,7 +157,7 @@ public class OidcController(
return existingAccount; return existingAccount;
} }
var connection = new AccountConnection var connection = new SnAccountConnection
{ {
AccountId = existingAccount.Id, AccountId = existingAccount.Id,
Provider = provider, Provider = provider,
@@ -177,7 +178,7 @@ public class OidcController(
var newAccount = await accounts.CreateAccount(userInfo); var newAccount = await accounts.CreateAccount(userInfo);
// Create the provider connection // Create the provider connection
var newConnection = new AccountConnection var newConnection = new SnAccountConnection
{ {
AccountId = newAccount.Id, AccountId = newAccount.Id,
Provider = provider, Provider = provider,

View File

@@ -1,7 +1,7 @@
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using DysonNetwork.Pass.Account;
using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using NodaTime; using NodaTime;
@@ -187,9 +187,9 @@ public abstract class OidcService(
/// Creates a challenge and session for an authenticated user /// Creates a challenge and session for an authenticated user
/// Also creates or updates the account connection /// Also creates or updates the account connection
/// </summary> /// </summary>
public async Task<AuthChallenge> CreateChallengeForUserAsync( public async Task<SnAuthChallenge> CreateChallengeForUserAsync(
OidcUserInfo userInfo, OidcUserInfo userInfo,
Account.Account account, SnAccount account,
HttpContext request, HttpContext request,
string deviceId, string deviceId,
string? deviceName = null string? deviceName = null
@@ -204,7 +204,7 @@ public abstract class OidcService(
if (connection is null) if (connection is null)
{ {
connection = new AccountConnection connection = new SnAccountConnection
{ {
Provider = ProviderName, Provider = ProviderName,
ProvidedIdentifier = userInfo.UserId ?? "", ProvidedIdentifier = userInfo.UserId ?? "",
@@ -219,7 +219,7 @@ public abstract class OidcService(
// Create a challenge that's already completed // Create a challenge that's already completed
var now = SystemClock.Instance.GetCurrentInstant(); var now = SystemClock.Instance.GetCurrentInstant();
var device = await auth.GetOrCreateDeviceAsync(account.Id, deviceId, deviceName, ClientPlatform.Ios); var device = await auth.GetOrCreateDeviceAsync(account.Id, deviceId, deviceName, ClientPlatform.Ios);
var challenge = new AuthChallenge var challenge = new SnAuthChallenge
{ {
ExpiredAt = now.Plus(Duration.FromHours(1)), ExpiredAt = now.Plus(Duration.FromHours(1)),
StepTotal = await auth.DetectChallengeRisk(request.Request, account), StepTotal = await auth.DetectChallengeRisk(request.Request, account),

View File

@@ -1,8 +1,8 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using DysonNetwork.Pass.Wallet; using DysonNetwork.Pass.Wallet;
using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NodaTime; using NodaTime;
@@ -25,7 +25,7 @@ public class TokenAuthService(
/// <param name="token">Incoming token string</param> /// <param name="token">Incoming token string</param>
/// <param name="ipAddress">Client IP address, for logging purposes</param> /// <param name="ipAddress">Client IP address, for logging purposes</param>
/// <returns>(Valid, Session, Message)</returns> /// <returns>(Valid, Session, Message)</returns>
public async Task<(bool Valid, AuthSession? Session, string? Message)> AuthenticateTokenAsync(string token, string? ipAddress = null) public async Task<(bool Valid, SnAuthSession? Session, string? Message)> AuthenticateTokenAsync(string token, string? ipAddress = null)
{ {
try try
{ {
@@ -63,7 +63,7 @@ public class TokenAuthService(
// Try cache first // Try cache first
var cacheKey = $"{AuthCacheConstants.Prefix}{sessionId}"; var cacheKey = $"{AuthCacheConstants.Prefix}{sessionId}";
var session = await cache.GetAsync<AuthSession>(cacheKey); var session = await cache.GetAsync<SnAuthSession>(cacheKey);
if (session is not null) if (session is not null)
{ {
logger.LogDebug("AuthenticateTokenAsync: cache hit for {CacheKey}", cacheKey); logger.LogDebug("AuthenticateTokenAsync: cache hit for {CacheKey}", cacheKey);

View File

@@ -1,4 +1,5 @@
using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace DysonNetwork.Pass.Credit; namespace DysonNetwork.Pass.Credit;
@@ -7,9 +8,9 @@ public class SocialCreditService(AppDatabase db, ICacheService cache)
{ {
private const string CacheKeyPrefix = "account:credits:"; private const string CacheKeyPrefix = "account:credits:";
public async Task<SocialCreditRecord> AddRecord(string reasonType, string reason, double delta, Guid accountId) public async Task<SnSocialCreditRecord> AddRecord(string reasonType, string reason, double delta, Guid accountId)
{ {
var record = new SocialCreditRecord var record = new SnSocialCreditRecord
{ {
ReasonType = reasonType, ReasonType = reasonType,
Reason = reason, Reason = reason,

View File

@@ -1,14 +1,14 @@
using DysonNetwork.Pass.Account;
using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Models;
using EFCore.BulkExtensions; using EFCore.BulkExtensions;
using NodaTime; using NodaTime;
using Quartz; using Quartz;
namespace DysonNetwork.Pass.Handlers; namespace DysonNetwork.Pass.Handlers;
public class ActionLogFlushHandler(IServiceProvider serviceProvider) : IFlushHandler<ActionLog> public class ActionLogFlushHandler(IServiceProvider serviceProvider) : IFlushHandler<SnActionLog>
{ {
public async Task FlushAsync(IReadOnlyList<ActionLog> items) public async Task FlushAsync(IReadOnlyList<SnActionLog> items)
{ {
using var scope = serviceProvider.CreateScope(); using var scope = serviceProvider.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDatabase>(); var db = scope.ServiceProvider.GetRequiredService<AppDatabase>();

View File

@@ -1,4 +1,5 @@
using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NodaTime; using NodaTime;
using Quartz; using Quartz;
@@ -7,8 +8,8 @@ namespace DysonNetwork.Pass.Handlers;
public class LastActiveInfo public class LastActiveInfo
{ {
public Auth.AuthSession Session { get; set; } = null!; public SnAuthSession Session { get; set; } = null!;
public Account.Account Account { get; set; } = null!; public SnAccount Account { get; set; } = null!;
public Instant SeenAt { get; set; } public Instant SeenAt { get; set; }
} }

View File

@@ -1,14 +1,15 @@
using DysonNetwork.Pass.Wallet; using DysonNetwork.Pass.Wallet;
using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace DysonNetwork.Pass.Leveling; namespace DysonNetwork.Pass.Leveling;
public class ExperienceService(AppDatabase db, SubscriptionService subscriptions, ICacheService cache) public class ExperienceService(AppDatabase db, SubscriptionService subscriptions, ICacheService cache)
{ {
public async Task<ExperienceRecord> AddRecord(string reasonType, string reason, long delta, Guid accountId) public async Task<SnExperienceRecord> AddRecord(string reasonType, string reason, long delta, Guid accountId)
{ {
var record = new ExperienceRecord var record = new SnExperienceRecord
{ {
ReasonType = reasonType, ReasonType = reasonType,
Reason = reason, Reason = reason,

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,5 @@
using System; using System.Text.Json;
using System.Collections.Generic; using DysonNetwork.Shared.Models;
using System.Text.Json;
using DysonNetwork.Pass.Account;
using DysonNetwork.Pass.Wallet;
using DysonNetwork.Shared.Data;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
using NetTopologySuite.Geometries; using NetTopologySuite.Geometries;
using NodaTime; using NodaTime;
@@ -49,9 +45,9 @@ namespace DysonNetwork.Pass.Migrations
name = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: false), name = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: false),
description = table.Column<string>(type: "character varying(4096)", maxLength: 4096, nullable: true), description = table.Column<string>(type: "character varying(4096)", maxLength: 4096, nullable: true),
status = table.Column<int>(type: "integer", nullable: false), status = table.Column<int>(type: "integer", nullable: false),
picture = table.Column<CloudFileReferenceObject>(type: "jsonb", nullable: true), picture = table.Column<SnCloudFileReferenceObject>(type: "jsonb", nullable: true),
background = table.Column<CloudFileReferenceObject>(type: "jsonb", nullable: true), background = table.Column<SnCloudFileReferenceObject>(type: "jsonb", nullable: true),
verification = table.Column<VerificationMark>(type: "jsonb", nullable: true), verification = table.Column<SnVerificationMark>(type: "jsonb", nullable: true),
created_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false), created_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
updated_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false), updated_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
deleted_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true) deleted_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true)
@@ -158,7 +154,7 @@ namespace DysonNetwork.Pass.Migrations
level = table.Column<int>(type: "integer", nullable: false), level = table.Column<int>(type: "integer", nullable: false),
reward_points = table.Column<decimal>(type: "numeric", nullable: true), reward_points = table.Column<decimal>(type: "numeric", nullable: true),
reward_experience = table.Column<int>(type: "integer", nullable: true), reward_experience = table.Column<int>(type: "integer", nullable: true),
tips = table.Column<ICollection<FortuneTip>>(type: "jsonb", nullable: false), tips = table.Column<ICollection<CheckInFortuneTip>>(type: "jsonb", nullable: false),
account_id = table.Column<Guid>(type: "uuid", nullable: false), account_id = table.Column<Guid>(type: "uuid", nullable: false),
created_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false), created_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
updated_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false), updated_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
@@ -242,13 +238,13 @@ namespace DysonNetwork.Pass.Migrations
location = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: true), location = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: true),
birthday = table.Column<Instant>(type: "timestamp with time zone", nullable: true), birthday = table.Column<Instant>(type: "timestamp with time zone", nullable: true),
last_seen_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true), last_seen_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true),
verification = table.Column<VerificationMark>(type: "jsonb", nullable: true), verification = table.Column<SnVerificationMark>(type: "jsonb", nullable: true),
active_badge = table.Column<BadgeReferenceObject>(type: "jsonb", nullable: true), active_badge = table.Column<SnAccountBadge>(type: "jsonb", nullable: true),
experience = table.Column<int>(type: "integer", nullable: false), experience = table.Column<int>(type: "integer", nullable: false),
picture_id = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true), picture_id = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
background_id = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true), background_id = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
picture = table.Column<CloudFileReferenceObject>(type: "jsonb", nullable: true), picture = table.Column<SnCloudFileReferenceObject>(type: "jsonb", nullable: true),
background = table.Column<CloudFileReferenceObject>(type: "jsonb", nullable: true), background = table.Column<SnCloudFileReferenceObject>(type: "jsonb", nullable: true),
account_id = table.Column<Guid>(type: "uuid", nullable: false), account_id = table.Column<Guid>(type: "uuid", nullable: false),
created_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false), created_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
updated_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false), updated_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
@@ -594,7 +590,7 @@ namespace DysonNetwork.Pass.Migrations
is_free_trial = table.Column<bool>(type: "boolean", nullable: false), is_free_trial = table.Column<bool>(type: "boolean", nullable: false),
status = table.Column<int>(type: "integer", nullable: false), status = table.Column<int>(type: "integer", nullable: false),
payment_method = table.Column<string>(type: "character varying(4096)", maxLength: 4096, nullable: false), payment_method = table.Column<string>(type: "character varying(4096)", maxLength: 4096, nullable: false),
payment_details = table.Column<PaymentDetails>(type: "jsonb", nullable: false), payment_details = table.Column<SnPaymentDetails>(type: "jsonb", nullable: false),
base_price = table.Column<decimal>(type: "numeric", nullable: false), base_price = table.Column<decimal>(type: "numeric", nullable: false),
coupon_id = table.Column<Guid>(type: "uuid", nullable: true), coupon_id = table.Column<Guid>(type: "uuid", nullable: true),
renewal_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true), renewal_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true),

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,4 @@
using System; using Microsoft.EntityFrameworkCore.Migrations;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
using NodaTime; using NodaTime;
#nullable disable #nullable disable

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,4 @@
using System; using DysonNetwork.Shared.Models;
using System.Collections.Generic;
using DysonNetwork.Shared.Data;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
using NodaTime; using NodaTime;
@@ -70,16 +68,16 @@ namespace DysonNetwork.Pass.Migrations
columns: table => new columns: table => new
{ {
id = table.Column<Guid>(type: "uuid", nullable: false), id = table.Column<Guid>(type: "uuid", nullable: false),
background = table.Column<CloudFileReferenceObject>(type: "jsonb", nullable: true), background = table.Column<SnCloudFileReferenceObject>(type: "jsonb", nullable: true),
created_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false), created_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
deleted_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true), deleted_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true),
description = table.Column<string>(type: "character varying(4096)", maxLength: 4096, nullable: true), description = table.Column<string>(type: "character varying(4096)", maxLength: 4096, nullable: true),
name = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: false), name = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: false),
picture = table.Column<CloudFileReferenceObject>(type: "jsonb", nullable: true), picture = table.Column<SnCloudFileReferenceObject>(type: "jsonb", nullable: true),
slug = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: false), slug = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: false),
status = table.Column<int>(type: "integer", nullable: false), status = table.Column<int>(type: "integer", nullable: false),
updated_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false), updated_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
verification = table.Column<VerificationMark>(type: "jsonb", nullable: true) verification = table.Column<SnVerificationMark>(type: "jsonb", nullable: true)
}, },
constraints: table => constraints: table =>
{ {

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic; using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable #nullable disable

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,4 @@
using System; using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
using NodaTime; using NodaTime;
#nullable disable #nullable disable

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More