♻️ Finish centerlizing the data models
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
using System.Text.Json;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
|
@@ -1,6 +1,5 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using DysonNetwork.Develop.Project;
|
||||
using DysonNetwork.Shared.Data;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using DysonNetwork.Shared.Registry;
|
||||
@@ -89,7 +88,7 @@ public class BotAccountController(
|
||||
return NotFound("Developer not found");
|
||||
|
||||
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");
|
||||
|
||||
var project = await projectService.GetProjectAsync(projectId, developer.Id);
|
||||
@@ -114,7 +113,7 @@ public class BotAccountController(
|
||||
return NotFound("Developer not found");
|
||||
|
||||
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");
|
||||
|
||||
var project = await projectService.GetProjectAsync(projectId, developer.Id);
|
||||
@@ -143,7 +142,7 @@ public class BotAccountController(
|
||||
return NotFound("Developer not found");
|
||||
|
||||
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");
|
||||
|
||||
var project = await projectService.GetProjectAsync(projectId, developer.Id);
|
||||
@@ -212,7 +211,7 @@ public class BotAccountController(
|
||||
return NotFound("Developer not found");
|
||||
|
||||
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");
|
||||
|
||||
var project = await projectService.GetProjectAsync(projectId, developer.Id);
|
||||
@@ -273,7 +272,7 @@ public class BotAccountController(
|
||||
return NotFound("Developer not found");
|
||||
|
||||
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");
|
||||
|
||||
var project = await projectService.GetProjectAsync(projectId, developer.Id);
|
||||
@@ -297,7 +296,7 @@ public class BotAccountController(
|
||||
}
|
||||
|
||||
[HttpGet("{botId:guid}/keys")]
|
||||
public async Task<ActionResult<List<ApiKeyReference>>> ListBotKeys(
|
||||
public async Task<ActionResult<List<SnApiKey>>> ListBotKeys(
|
||||
[FromRoute] string pubName,
|
||||
[FromRoute] Guid projectId,
|
||||
[FromRoute] Guid botId
|
||||
@@ -306,7 +305,7 @@ public class BotAccountController(
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser)
|
||||
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 (project == null) return NotFound("Project not found or you don't have access");
|
||||
if (bot == null) return NotFound("Bot not found");
|
||||
@@ -315,13 +314,13 @@ public class BotAccountController(
|
||||
{
|
||||
AutomatedId = bot.Id.ToString()
|
||||
});
|
||||
var data = keys.Data.Select(ApiKeyReference.FromProtoValue).ToList();
|
||||
var data = keys.Data.Select(SnApiKey.FromProtoValue).ToList();
|
||||
|
||||
return Ok(data);
|
||||
}
|
||||
|
||||
[HttpGet("{botId:guid}/keys/{keyId:guid}")]
|
||||
public async Task<ActionResult<ApiKeyReference>> GetBotKey(
|
||||
public async Task<ActionResult<SnApiKey>> GetBotKey(
|
||||
[FromRoute] string pubName,
|
||||
[FromRoute] Guid projectId,
|
||||
[FromRoute] Guid botId,
|
||||
@@ -330,7 +329,7 @@ public class BotAccountController(
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser)
|
||||
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 (project == null) return NotFound("Project not found or you don't have access");
|
||||
if (bot == null) return NotFound("Bot not found");
|
||||
@@ -339,7 +338,7 @@ public class BotAccountController(
|
||||
{
|
||||
var key = await accountsReceiver.GetApiKeyAsync(new GetApiKeyRequest { Id = keyId.ToString() });
|
||||
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)
|
||||
{
|
||||
@@ -354,7 +353,7 @@ public class BotAccountController(
|
||||
}
|
||||
|
||||
[HttpPost("{botId:guid}/keys")]
|
||||
public async Task<ActionResult<ApiKeyReference>> CreateBotKey(
|
||||
public async Task<ActionResult<SnApiKey>> CreateBotKey(
|
||||
[FromRoute] string pubName,
|
||||
[FromRoute] Guid projectId,
|
||||
[FromRoute] Guid botId,
|
||||
@@ -363,7 +362,7 @@ public class BotAccountController(
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser)
|
||||
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 (project == null) return NotFound("Project not found or you don't have access");
|
||||
if (bot == null) return NotFound("Bot not found");
|
||||
@@ -377,7 +376,7 @@ public class BotAccountController(
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -386,7 +385,7 @@ public class BotAccountController(
|
||||
}
|
||||
|
||||
[HttpPost("{botId:guid}/keys/{keyId:guid}/rotate")]
|
||||
public async Task<ActionResult<ApiKeyReference>> RotateBotKey(
|
||||
public async Task<ActionResult<SnApiKey>> RotateBotKey(
|
||||
[FromRoute] string pubName,
|
||||
[FromRoute] Guid projectId,
|
||||
[FromRoute] Guid botId,
|
||||
@@ -395,7 +394,7 @@ public class BotAccountController(
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser)
|
||||
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 (project == null) return NotFound("Project not found or you don't have access");
|
||||
if (bot == null) return NotFound("Bot not found");
|
||||
@@ -403,7 +402,7 @@ public class BotAccountController(
|
||||
try
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -421,7 +420,7 @@ public class BotAccountController(
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser)
|
||||
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 (project == null) return NotFound("Project not found or you don't have access");
|
||||
if (bot == null) return NotFound("Bot not found");
|
||||
@@ -437,12 +436,12 @@ public class BotAccountController(
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<(SnDeveloper?, SnDevProject?, BotAccount?)> ValidateBotAccess(
|
||||
private async Task<(SnDeveloper?, SnDevProject?, SnBotAccount?)> ValidateBotAccess(
|
||||
string pubName,
|
||||
Guid projectId,
|
||||
Guid botId,
|
||||
Account currentUser,
|
||||
PublisherMemberRole requiredRole)
|
||||
Shared.Proto.PublisherMemberRole requiredRole)
|
||||
{
|
||||
var developer = await developerService.GetDeveloperByName(pubName);
|
||||
if (developer == null) return (null, null, null);
|
||||
|
@@ -1,4 +1,3 @@
|
||||
using DysonNetwork.Shared.Data;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using DysonNetwork.Shared.Registry;
|
||||
@@ -14,21 +13,21 @@ public class BotAccountService(
|
||||
AccountClientHelper accounts
|
||||
)
|
||||
{
|
||||
public async Task<BotAccount?> GetBotByIdAsync(Guid id)
|
||||
public async Task<SnBotAccount?> GetBotByIdAsync(Guid id)
|
||||
{
|
||||
return await db.BotAccounts
|
||||
.Include(b => b.Project)
|
||||
.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
|
||||
.Where(b => b.ProjectId == projectId)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<BotAccount> CreateBotAsync(
|
||||
public async Task<SnBotAccount> CreateBotAsync(
|
||||
SnDevProject project,
|
||||
string slug,
|
||||
Account account,
|
||||
@@ -58,7 +57,7 @@ public class BotAccountService(
|
||||
var botAccount = createResponse.Bot;
|
||||
|
||||
// Then create the local bot account
|
||||
var bot = new BotAccount
|
||||
var bot = new SnBotAccount
|
||||
{
|
||||
Id = automatedId,
|
||||
Slug = slug,
|
||||
@@ -89,8 +88,8 @@ public class BotAccountService(
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<BotAccount> UpdateBotAsync(
|
||||
BotAccount bot,
|
||||
public async Task<SnBotAccount> UpdateBotAsync(
|
||||
SnBotAccount bot,
|
||||
Account account,
|
||||
string? pictureId,
|
||||
string? backgroundId
|
||||
@@ -130,7 +129,7 @@ public class BotAccountService(
|
||||
return bot;
|
||||
}
|
||||
|
||||
public async Task DeleteBotAsync(BotAccount bot)
|
||||
public async Task DeleteBotAsync(SnBotAccount bot)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -153,12 +152,12 @@ public class BotAccountService(
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<BotAccount?> LoadBotAccountAsync(BotAccount bot) =>
|
||||
public async Task<SnBotAccount?> LoadBotAccountAsync(SnBotAccount bot) =>
|
||||
(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 data = await accounts.GetBotAccountBatch(automatedIds);
|
||||
|
||||
@@ -169,6 +168,6 @@ public class BotAccountService(
|
||||
.FirstOrDefault(e => e.AutomatedId == bot.Id);
|
||||
}
|
||||
|
||||
return bots as List<BotAccount> ?? [];
|
||||
return bots as List<SnBotAccount> ?? [];
|
||||
}
|
||||
}
|
@@ -19,9 +19,9 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
|
||||
[MaxLength(4096)] string? Description,
|
||||
string? PictureId,
|
||||
string? BackgroundId,
|
||||
CustomAppStatus? Status,
|
||||
CustomAppLinks? Links,
|
||||
CustomAppOauthConfig? OauthConfig
|
||||
Shared.Models.CustomAppStatus? Status,
|
||||
SnCustomAppLinks? Links,
|
||||
SnCustomAppOauthConfig? OauthConfig
|
||||
);
|
||||
|
||||
public record CreateSecretRequest(
|
||||
@@ -51,7 +51,7 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
|
||||
if (developer is null) return NotFound();
|
||||
|
||||
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");
|
||||
|
||||
var project = await projectService.GetProjectAsync(projectId, developer.Id);
|
||||
@@ -73,7 +73,7 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
|
||||
if (developer is null) return NotFound();
|
||||
|
||||
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");
|
||||
|
||||
var project = await projectService.GetProjectAsync(projectId, developer.Id);
|
||||
@@ -100,7 +100,7 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
|
||||
if (developer is null)
|
||||
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");
|
||||
|
||||
var project = await projectService.GetProjectAsync(projectId, developer.Id);
|
||||
@@ -144,7 +144,7 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
|
||||
if (developer is null)
|
||||
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");
|
||||
|
||||
var project = await projectService.GetProjectAsync(projectId, developer.Id);
|
||||
@@ -181,7 +181,7 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
|
||||
if (developer is null)
|
||||
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");
|
||||
|
||||
var project = await projectService.GetProjectAsync(projectId, developer.Id);
|
||||
@@ -213,7 +213,7 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
|
||||
if (developer is null)
|
||||
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");
|
||||
|
||||
var project = await projectService.GetProjectAsync(projectId, developer.Id);
|
||||
@@ -251,7 +251,7 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
|
||||
if (developer is null)
|
||||
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");
|
||||
|
||||
var project = await projectService.GetProjectAsync(projectId, developer.Id);
|
||||
@@ -264,7 +264,7 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
|
||||
|
||||
try
|
||||
{
|
||||
var secret = await customApps.CreateAppSecretAsync(new CustomAppSecret
|
||||
var secret = await customApps.CreateAppSecretAsync(new SnCustomAppSecret
|
||||
{
|
||||
AppId = appId,
|
||||
Description = request.Description,
|
||||
@@ -310,7 +310,7 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
|
||||
if (developer is null)
|
||||
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");
|
||||
|
||||
var project = await projectService.GetProjectAsync(projectId, developer.Id);
|
||||
@@ -351,7 +351,7 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
|
||||
if (developer is null)
|
||||
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");
|
||||
|
||||
var project = await projectService.GetProjectAsync(projectId, developer.Id);
|
||||
@@ -389,7 +389,7 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
|
||||
if (developer is null)
|
||||
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");
|
||||
|
||||
var project = await projectService.GetProjectAsync(projectId, developer.Id);
|
||||
@@ -402,7 +402,7 @@ public class CustomAppController(CustomAppService customApps, DeveloperService d
|
||||
|
||||
try
|
||||
{
|
||||
var secret = await customApps.RotateAppSecretAsync(new CustomAppSecret
|
||||
var secret = await customApps.RotateAppSecretAsync(new SnCustomAppSecret
|
||||
{
|
||||
Id = secretId,
|
||||
AppId = appId,
|
||||
|
@@ -1,4 +1,3 @@
|
||||
using DysonNetwork.Develop.Project;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -13,7 +12,7 @@ public class CustomAppService(
|
||||
FileService.FileServiceClient files
|
||||
)
|
||||
{
|
||||
public async Task<CustomApp?> CreateAppAsync(
|
||||
public async Task<SnCustomApp?> CreateAppAsync(
|
||||
Guid projectId,
|
||||
CustomAppController.CustomAppRequest request
|
||||
)
|
||||
@@ -25,12 +24,12 @@ public class CustomAppService(
|
||||
if (project == null)
|
||||
return null;
|
||||
|
||||
var app = new CustomApp
|
||||
var app = new SnCustomApp
|
||||
{
|
||||
Slug = request.Slug!,
|
||||
Name = request.Name!,
|
||||
Description = request.Description,
|
||||
Status = request.Status ?? CustomAppStatus.Developing,
|
||||
Status = request.Status ?? Shared.Models.CustomAppStatus.Developing,
|
||||
Links = request.Links,
|
||||
OauthConfig = request.OauthConfig,
|
||||
ProjectId = projectId
|
||||
@@ -84,7 +83,7 @@ public class CustomAppService(
|
||||
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();
|
||||
|
||||
@@ -96,7 +95,7 @@ public class CustomAppService(
|
||||
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
|
||||
.Where(s => s.AppId == appId)
|
||||
@@ -104,13 +103,13 @@ public class CustomAppService(
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<CustomAppSecret?> GetAppSecretAsync(Guid secretId, Guid appId)
|
||||
public async Task<SnCustomAppSecret?> GetAppSecretAsync(Guid secretId, Guid appId)
|
||||
{
|
||||
return await db.CustomAppSecrets
|
||||
.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))
|
||||
{
|
||||
@@ -141,7 +140,7 @@ public class CustomAppService(
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<CustomAppSecret> RotateAppSecretAsync(CustomAppSecret secretUpdate)
|
||||
public async Task<SnCustomAppSecret> RotateAppSecretAsync(SnCustomAppSecret secretUpdate)
|
||||
{
|
||||
var existingSecret = await db.CustomAppSecrets
|
||||
.FirstOrDefaultAsync(s => s.Id == secretUpdate.Id && s.AppId == secretUpdate.AppId);
|
||||
@@ -177,14 +176,14 @@ public class CustomAppService(
|
||||
return res.ToString();
|
||||
}
|
||||
|
||||
public async Task<List<CustomApp>> GetAppsByProjectAsync(Guid projectId)
|
||||
public async Task<List<SnCustomApp>> GetAppsByProjectAsync(Guid projectId)
|
||||
{
|
||||
return await db.CustomApps
|
||||
.Where(a => a.ProjectId == projectId)
|
||||
.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)
|
||||
app.Slug = request.Slug;
|
||||
|
@@ -38,7 +38,7 @@ public class CustomAppServiceGrpc(AppDatabase db) : Shared.Proto.CustomAppServic
|
||||
if (string.IsNullOrEmpty(request.Secret))
|
||||
throw new RpcException(new Status(StatusCode.InvalidArgument, "secret required"));
|
||||
|
||||
IQueryable<CustomAppSecret> q = db.CustomAppSecrets;
|
||||
IQueryable<SnCustomAppSecret> q = db.CustomAppSecrets;
|
||||
switch (request.SecretIdentifierCase)
|
||||
{
|
||||
case CheckCustomAppSecretRequest.SecretIdentifierOneofCase.SecretId:
|
||||
|
@@ -75,11 +75,11 @@ public class DeveloperController(
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
var accountId = Guid.Parse(currentUser.Id);
|
||||
|
||||
PublisherInfo? pub;
|
||||
SnPublisher? pub;
|
||||
try
|
||||
{
|
||||
var pubResponse = await ps.GetPublisherAsync(new GetPublisherRequest { Name = name });
|
||||
pub = PublisherInfo.FromProto(pubResponse.Publisher);
|
||||
pub = SnPublisher.FromProto(pubResponse.Publisher);
|
||||
} catch (RpcException ex)
|
||||
{
|
||||
return NotFound(ex.Status.Detail);
|
||||
@@ -90,7 +90,7 @@ public class DeveloperController(
|
||||
{
|
||||
PublisherId = pub.Id.ToString(),
|
||||
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");
|
||||
|
||||
|
@@ -13,7 +13,7 @@ public class DeveloperService(
|
||||
public async Task<SnDeveloper> LoadDeveloperPublisher(SnDeveloper developer)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ public class DeveloperService(
|
||||
var pubRequest = new GetPublisherBatchRequest();
|
||||
pubIds.ForEach(x => pubRequest.Ids.Add(x.ToString()));
|
||||
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 =>
|
||||
{
|
||||
@@ -56,7 +56,7 @@ public class DeveloperService(
|
||||
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
|
||||
{
|
||||
|
@@ -55,7 +55,7 @@ namespace DysonNetwork.Develop.Migrations
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("developer_id");
|
||||
|
||||
b.Property<CustomAppLinks>("Links")
|
||||
b.Property<SnCustomAppLinks>("Links")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("links");
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace DysonNetwork.Develop.Migrations
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<CustomAppOauthConfig>("OauthConfig")
|
||||
b.Property<SnCustomAppOauthConfig>("OauthConfig")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("oauth_config");
|
||||
|
||||
|
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using NodaTime;
|
||||
|
||||
@@ -37,8 +36,8 @@ namespace DysonNetwork.Develop.Migrations
|
||||
picture = table.Column<SnCloudFileReferenceObject>(type: "jsonb", nullable: true),
|
||||
background = table.Column<SnCloudFileReferenceObject>(type: "jsonb", nullable: true),
|
||||
verification = table.Column<SnVerificationMark>(type: "jsonb", nullable: true),
|
||||
oauth_config = table.Column<CustomAppOauthConfig>(type: "jsonb", nullable: true),
|
||||
links = table.Column<CustomAppLinks>(type: "jsonb", nullable: true),
|
||||
oauth_config = table.Column<SnCustomAppOauthConfig>(type: "jsonb", nullable: true),
|
||||
links = table.Column<SnCustomAppLinks>(type: "jsonb", nullable: true),
|
||||
developer_id = table.Column<Guid>(type: "uuid", 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),
|
||||
|
@@ -51,7 +51,7 @@ namespace DysonNetwork.Develop.Migrations
|
||||
.HasColumnType("character varying(4096)")
|
||||
.HasColumnName("description");
|
||||
|
||||
b.Property<CustomAppLinks>("Links")
|
||||
b.Property<SnCustomAppLinks>("Links")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("links");
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace DysonNetwork.Develop.Migrations
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<CustomAppOauthConfig>("OauthConfig")
|
||||
b.Property<SnCustomAppOauthConfig>("OauthConfig")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("oauth_config");
|
||||
|
||||
|
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using NodaTime;
|
||||
|
||||
#nullable disable
|
||||
|
@@ -93,7 +93,7 @@ namespace DysonNetwork.Develop.Migrations
|
||||
.HasColumnType("character varying(4096)")
|
||||
.HasColumnName("description");
|
||||
|
||||
b.Property<CustomAppLinks>("Links")
|
||||
b.Property<SnCustomAppLinks>("Links")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("links");
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace DysonNetwork.Develop.Migrations
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<CustomAppOauthConfig>("OauthConfig")
|
||||
b.Property<SnCustomAppOauthConfig>("OauthConfig")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("oauth_config");
|
||||
|
||||
|
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using NodaTime;
|
||||
|
||||
#nullable disable
|
||||
|
@@ -90,7 +90,7 @@ namespace DysonNetwork.Develop.Migrations
|
||||
.HasColumnType("character varying(4096)")
|
||||
.HasColumnName("description");
|
||||
|
||||
b.Property<CustomAppLinks>("Links")
|
||||
b.Property<SnCustomAppLinks>("Links")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("links");
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace DysonNetwork.Develop.Migrations
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<CustomAppOauthConfig>("OauthConfig")
|
||||
b.Property<SnCustomAppOauthConfig>("OauthConfig")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("oauth_config");
|
||||
|
||||
|
@@ -1,9 +1,6 @@
|
||||
using System.Net;
|
||||
using DysonNetwork.Develop.Identity;
|
||||
using DysonNetwork.Shared.Auth;
|
||||
using DysonNetwork.Shared.Http;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Prometheus;
|
||||
|
||||
namespace DysonNetwork.Develop.Startup;
|
||||
|
@@ -1,5 +1,4 @@
|
||||
using System.Globalization;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using NodaTime;
|
||||
using NodaTime.Serialization.SystemTextJson;
|
||||
using System.Text.Json;
|
||||
@@ -7,7 +6,6 @@ using System.Text.Json.Serialization;
|
||||
using DysonNetwork.Develop.Identity;
|
||||
using DysonNetwork.Develop.Project;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using StackExchange.Redis;
|
||||
|
||||
namespace DysonNetwork.Develop.Startup;
|
||||
|
||||
|
@@ -1,7 +1,6 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using DysonNetwork.Drive.Billing;
|
||||
using DysonNetwork.Shared.Data;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
|
@@ -1,4 +1,3 @@
|
||||
using DysonNetwork.Shared.Data;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using NodaTime;
|
||||
|
||||
|
@@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using DysonNetwork.Drive.Storage;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using NodaTime;
|
||||
|
||||
|
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
|
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using NodaTime;
|
||||
|
||||
|
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using NodaTime;
|
||||
|
||||
#nullable disable
|
||||
|
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using NodaTime;
|
||||
|
||||
#nullable disable
|
||||
|
@@ -3,11 +3,8 @@ using System.Text.Json.Serialization;
|
||||
using System.Threading.RateLimiting;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using Microsoft.AspNetCore.RateLimiting;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using NodaTime;
|
||||
using NodaTime.Serialization.SystemTextJson;
|
||||
using StackExchange.Redis;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using tusdotnet.Stores;
|
||||
|
||||
namespace DysonNetwork.Drive.Startup;
|
||||
|
@@ -165,7 +165,7 @@ public class FileController(
|
||||
}
|
||||
|
||||
[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);
|
||||
if (file is null) return NotFound("File not found.");
|
||||
@@ -175,7 +175,7 @@ public class FileController(
|
||||
|
||||
[Authorize]
|
||||
[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();
|
||||
var accountId = Guid.Parse(currentUser.Id);
|
||||
@@ -194,7 +194,7 @@ public class FileController(
|
||||
|
||||
[Authorize]
|
||||
[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();
|
||||
var accountId = Guid.Parse(currentUser.Id);
|
||||
@@ -208,7 +208,7 @@ public class FileController(
|
||||
|
||||
[Authorize]
|
||||
[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();
|
||||
var accountId = Guid.Parse(currentUser.Id);
|
||||
@@ -222,7 +222,7 @@ public class FileController(
|
||||
|
||||
[Authorize]
|
||||
[HttpGet("me")]
|
||||
public async Task<ActionResult<List<CloudFile>>> GetMyFiles(
|
||||
public async Task<ActionResult<List<SnCloudFile>>> GetMyFiles(
|
||||
[FromQuery] Guid? pool,
|
||||
[FromQuery] bool recycled = false,
|
||||
[FromQuery] int offset = 0,
|
||||
@@ -307,7 +307,7 @@ public class FileController(
|
||||
[Authorize]
|
||||
[HttpPost("fast")]
|
||||
[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();
|
||||
var accountId = Guid.Parse(currentUser.Id);
|
||||
@@ -368,7 +368,7 @@ public class FileController(
|
||||
await using var transaction = await db.Database.BeginTransactionAsync();
|
||||
try
|
||||
{
|
||||
var file = new CloudFile
|
||||
var file = new SnCloudFile
|
||||
{
|
||||
Name = request.Name,
|
||||
Size = request.Size,
|
||||
|
@@ -28,11 +28,11 @@ public class FileService(
|
||||
private const string CacheKeyPrefix = "file:";
|
||||
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 cachedFile = await cache.GetAsync<CloudFile>(cacheKey);
|
||||
var cachedFile = await cache.GetAsync<SnCloudFile>(cacheKey);
|
||||
if (cachedFile is not null)
|
||||
return cachedFile;
|
||||
|
||||
@@ -48,15 +48,15 @@ public class FileService(
|
||||
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>();
|
||||
|
||||
foreach (var fileId in fileIds)
|
||||
{
|
||||
var cacheKey = $"{CacheKeyPrefix}{fileId}";
|
||||
var cachedFile = await cache.GetAsync<CloudFile>(cacheKey);
|
||||
var cachedFile = await cache.GetAsync<SnCloudFile>(cacheKey);
|
||||
|
||||
if (cachedFile != null)
|
||||
cachedFiles[fileId] = cachedFile;
|
||||
@@ -82,11 +82,11 @@ public class FileService(
|
||||
return fileIds
|
||||
.Select(f => cachedFiles.GetValueOrDefault(f))
|
||||
.Where(f => f != null)
|
||||
.Cast<CloudFile>()
|
||||
.Cast<SnCloudFile>()
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public async Task<CloudFile> ProcessNewFileAsync(
|
||||
public async Task<SnCloudFile> ProcessNewFileAsync(
|
||||
Account account,
|
||||
string fileId,
|
||||
string filePool,
|
||||
@@ -131,7 +131,7 @@ public class FileService(
|
||||
var finalContentType = contentType ??
|
||||
(!fileName.Contains('.') ? "application/octet-stream" : MimeTypes.GetMimeType(fileName));
|
||||
|
||||
var file = new CloudFile
|
||||
var file = new SnCloudFile
|
||||
{
|
||||
Id = fileId,
|
||||
Name = fileName,
|
||||
@@ -190,7 +190,7 @@ public class FileService(
|
||||
return file;
|
||||
}
|
||||
|
||||
private async Task ExtractMetadataAsync(CloudFile file, string filePath)
|
||||
private async Task ExtractMetadataAsync(SnCloudFile file, string filePath)
|
||||
{
|
||||
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);
|
||||
if (existingFile == null)
|
||||
@@ -414,7 +414,7 @@ public class FileService(
|
||||
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);
|
||||
await db.SaveChangesAsync();
|
||||
@@ -423,7 +423,7 @@ public class FileService(
|
||||
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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -569,15 +569,15 @@ public class FileService(
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
public async Task<List<CloudFile?>> LoadFromReference(List<SnCloudFileReferenceObject> 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>();
|
||||
|
||||
foreach (var reference in references)
|
||||
{
|
||||
var cacheKey = $"{CacheKeyPrefix}{reference.Id}";
|
||||
var cachedFile = await cache.GetAsync<CloudFile>(cacheKey);
|
||||
var cachedFile = await cache.GetAsync<SnCloudFile>(cacheKey);
|
||||
|
||||
if (cachedFile != null)
|
||||
{
|
||||
@@ -603,10 +603,9 @@ public class FileService(
|
||||
}
|
||||
}
|
||||
|
||||
return references
|
||||
return [.. references
|
||||
.Select(r => cachedFiles.GetValueOrDefault(r.Id))
|
||||
.Where(f => f != null)
|
||||
.ToList();
|
||||
.Where(f => f != null)];
|
||||
}
|
||||
|
||||
public async Task<int> GetReferenceCountAsync(string fileId)
|
||||
@@ -685,7 +684,7 @@ public class FileService(
|
||||
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");
|
||||
|
||||
@@ -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? Description { get; set; } = file.Description;
|
||||
@@ -715,9 +714,9 @@ file class UpdatableCloudFile(CloudFile file)
|
||||
public Dictionary<string, object?>? UserMeta { get; set; } = file.UserMeta;
|
||||
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
|
||||
.SetProperty(f => f.Name, Name)
|
||||
.SetProperty(f => f.Description, Description)
|
||||
|
@@ -1,4 +1,4 @@
|
||||
using DysonNetwork.Shared.Data;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using Grpc.Core;
|
||||
@@ -48,7 +48,7 @@ namespace DysonNetwork.Drive.Storage
|
||||
{
|
||||
// Assuming CloudFileReferenceObject is a simple class/struct that holds an ID
|
||||
// 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 response = new LoadFromReferenceResponse();
|
||||
response.Files.AddRange(files.Where(f => f != null).Select(f => f!.ToProtoValue()));
|
||||
|
@@ -4,6 +4,7 @@ using DysonNetwork.Pass.Credit;
|
||||
using DysonNetwork.Pass.Wallet;
|
||||
using DysonNetwork.Shared.Error;
|
||||
using DysonNetwork.Shared.GeoIp;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NodaTime;
|
||||
@@ -23,9 +24,9 @@ public class AccountController(
|
||||
) : ControllerBase
|
||||
{
|
||||
[HttpGet("{name}")]
|
||||
[ProducesResponseType<Account>(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType<SnAccount>(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<Account?>> GetByName(string name)
|
||||
public async Task<ActionResult<SnAccount?>> GetByName(string name)
|
||||
{
|
||||
var account = await db.Accounts
|
||||
.Include(e => e.Badges)
|
||||
@@ -42,9 +43,9 @@ public class AccountController(
|
||||
}
|
||||
|
||||
[HttpGet("{name}/badges")]
|
||||
[ProducesResponseType<List<AccountBadge>>(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType<List<SnAccountBadge>>(StatusCodes.Status200OK)]
|
||||
[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
|
||||
.Include(e => e.Badges)
|
||||
@@ -103,9 +104,9 @@ public class AccountController(
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ProducesResponseType<Account>(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType<SnAccount>(StatusCodes.Status200OK)]
|
||||
[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))
|
||||
return BadRequest(ApiError.Validation(new Dictionary<string, string[]>
|
||||
@@ -199,7 +200,7 @@ public class AccountController(
|
||||
}
|
||||
|
||||
[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);
|
||||
if (account is null)
|
||||
@@ -254,7 +255,7 @@ public class AccountController(
|
||||
}
|
||||
|
||||
[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))
|
||||
return [];
|
||||
|
@@ -28,11 +28,11 @@ public class AccountCurrentController(
|
||||
) : ControllerBase
|
||||
{
|
||||
[HttpGet]
|
||||
[ProducesResponseType<Account>(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType<SnAccount>(StatusCodes.Status200OK)]
|
||||
[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 account = await db.Accounts
|
||||
@@ -55,9 +55,9 @@ public class AccountCurrentController(
|
||||
}
|
||||
|
||||
[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);
|
||||
|
||||
@@ -88,9 +88,9 @@ public class AccountCurrentController(
|
||||
}
|
||||
|
||||
[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 profile = await db.AccountProfiles
|
||||
@@ -163,7 +163,7 @@ public class AccountCurrentController(
|
||||
[HttpDelete]
|
||||
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
|
||||
{
|
||||
@@ -184,18 +184,18 @@ public class AccountCurrentController(
|
||||
}
|
||||
|
||||
[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);
|
||||
return Ok(status);
|
||||
}
|
||||
|
||||
[HttpPatch("statuses")]
|
||||
[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 })
|
||||
return BadRequest("Automated status cannot be updated.");
|
||||
|
||||
@@ -227,9 +227,9 @@ public class AccountCurrentController(
|
||||
|
||||
[HttpPost("statuses")]
|
||||
[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 })
|
||||
{
|
||||
@@ -261,7 +261,7 @@ public class AccountCurrentController(
|
||||
return Ok(existingStatus); // Do not override manually set status with automated ones
|
||||
}
|
||||
|
||||
var status = new Status
|
||||
var status = new SnAccountStatus
|
||||
{
|
||||
AccountId = currentUser.Id,
|
||||
Attitude = request.Attitude,
|
||||
@@ -280,7 +280,7 @@ public class AccountCurrentController(
|
||||
[HttpDelete("statuses")]
|
||||
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 queryable = db.AccountStatuses
|
||||
@@ -301,9 +301,9 @@ public class AccountCurrentController(
|
||||
}
|
||||
|
||||
[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 now = SystemClock.Instance.GetCurrentInstant();
|
||||
@@ -323,12 +323,12 @@ public class AccountCurrentController(
|
||||
}
|
||||
|
||||
[HttpPost("check-in")]
|
||||
public async Task<ActionResult<CheckInResult>> DoCheckIn(
|
||||
public async Task<ActionResult<SnCheckInResult>> DoCheckIn(
|
||||
[FromBody] string? captchaToken,
|
||||
[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)
|
||||
{
|
||||
@@ -399,7 +399,7 @@ public class AccountCurrentController(
|
||||
public async Task<ActionResult<List<DailyEventResponse>>> GetEventCalendar([FromQuery] int? month,
|
||||
[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;
|
||||
month ??= currentDate.Month;
|
||||
@@ -428,7 +428,7 @@ public class AccountCurrentController(
|
||||
[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
|
||||
.Where(log => log.AccountId == currentUser.Id)
|
||||
@@ -446,9 +446,9 @@ public class AccountCurrentController(
|
||||
}
|
||||
|
||||
[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
|
||||
.Include(f => f.Account)
|
||||
@@ -460,15 +460,15 @@ public class AccountCurrentController(
|
||||
|
||||
public class AuthFactorRequest
|
||||
{
|
||||
public AccountAuthFactorType Type { get; set; }
|
||||
public Shared.Models.AccountAuthFactorType Type { get; set; }
|
||||
public string? Secret { get; set; }
|
||||
}
|
||||
|
||||
[HttpPost("factors")]
|
||||
[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))
|
||||
return BadRequest(new ApiError
|
||||
{
|
||||
@@ -484,9 +484,9 @@ public class AccountCurrentController(
|
||||
|
||||
[HttpPost("factors/{id:guid}/enable")]
|
||||
[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
|
||||
.Where(f => f.AccountId == currentUser.Id && f.Id == id)
|
||||
@@ -513,9 +513,9 @@ public class AccountCurrentController(
|
||||
|
||||
[HttpPost("factors/{id:guid}/disable")]
|
||||
[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
|
||||
.Where(f => f.AccountId == currentUser.Id && f.Id == id)
|
||||
@@ -535,9 +535,9 @@ public class AccountCurrentController(
|
||||
|
||||
[HttpDelete("factors/{id:guid}")]
|
||||
[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
|
||||
.Where(f => f.AccountId == currentUser.Id && f.Id == id)
|
||||
@@ -559,7 +559,7 @@ public class AccountCurrentController(
|
||||
[Authorize]
|
||||
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 SnAuthSession currentSession) return Unauthorized();
|
||||
|
||||
Response.Headers.Append("X-Auth-Session", currentSession.Id.ToString());
|
||||
@@ -589,7 +589,7 @@ public class AccountCurrentController(
|
||||
[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 SnAuthSession currentSession) return Unauthorized();
|
||||
|
||||
var query = db.AuthSessions
|
||||
@@ -614,7 +614,7 @@ public class AccountCurrentController(
|
||||
[Authorize]
|
||||
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
|
||||
{
|
||||
@@ -631,7 +631,7 @@ public class AccountCurrentController(
|
||||
[Authorize]
|
||||
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
|
||||
{
|
||||
@@ -648,7 +648,7 @@ public class AccountCurrentController(
|
||||
[Authorize]
|
||||
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 SnAuthSession currentSession) return Unauthorized();
|
||||
|
||||
try
|
||||
@@ -666,7 +666,7 @@ public class AccountCurrentController(
|
||||
[Authorize]
|
||||
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
|
||||
{
|
||||
@@ -683,7 +683,7 @@ public class AccountCurrentController(
|
||||
[Authorize]
|
||||
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 SnAuthSession currentSession) return Unauthorized();
|
||||
|
||||
var device = await db.AuthClients.FirstOrDefaultAsync(d => d.Id == currentSession.Challenge.ClientId);
|
||||
@@ -702,9 +702,9 @@ public class AccountCurrentController(
|
||||
|
||||
[HttpGet("contacts")]
|
||||
[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
|
||||
.Where(c => c.AccountId == currentUser.Id)
|
||||
@@ -715,15 +715,15 @@ public class AccountCurrentController(
|
||||
|
||||
public class AccountContactRequest
|
||||
{
|
||||
[Required] public AccountContactType Type { get; set; }
|
||||
[Required] public Shared.Models.AccountContactType Type { get; set; }
|
||||
[Required] public string Content { get; set; } = null!;
|
||||
}
|
||||
|
||||
[HttpPost("contacts")]
|
||||
[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
|
||||
{
|
||||
@@ -738,9 +738,9 @@ public class AccountCurrentController(
|
||||
|
||||
[HttpPost("contacts/{id:guid}/verify")]
|
||||
[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
|
||||
.Where(c => c.AccountId == currentUser.Id && c.Id == id)
|
||||
@@ -760,9 +760,9 @@ public class AccountCurrentController(
|
||||
|
||||
[HttpPost("contacts/{id:guid}/primary")]
|
||||
[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
|
||||
.Where(c => c.AccountId == currentUser.Id && c.Id == id)
|
||||
@@ -782,9 +782,9 @@ public class AccountCurrentController(
|
||||
|
||||
[HttpPost("contacts/{id:guid}/public")]
|
||||
[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
|
||||
.Where(c => c.AccountId == currentUser.Id && c.Id == id)
|
||||
@@ -804,9 +804,9 @@ public class AccountCurrentController(
|
||||
|
||||
[HttpDelete("contacts/{id:guid}/public")]
|
||||
[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
|
||||
.Where(c => c.AccountId == currentUser.Id && c.Id == id)
|
||||
@@ -826,9 +826,9 @@ public class AccountCurrentController(
|
||||
|
||||
[HttpDelete("contacts/{id:guid}")]
|
||||
[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
|
||||
.Where(c => c.AccountId == currentUser.Id && c.Id == id)
|
||||
@@ -847,11 +847,11 @@ public class AccountCurrentController(
|
||||
}
|
||||
|
||||
[HttpGet("badges")]
|
||||
[ProducesResponseType<List<AccountBadge>>(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType<List<SnAccountBadge>>(StatusCodes.Status200OK)]
|
||||
[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
|
||||
.Where(b => b.AccountId == currentUser.Id)
|
||||
@@ -861,9 +861,9 @@ public class AccountCurrentController(
|
||||
|
||||
[HttpPost("badges/{id:guid}/active")]
|
||||
[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
|
||||
{
|
||||
@@ -878,12 +878,12 @@ public class AccountCurrentController(
|
||||
|
||||
[HttpGet("leveling")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<ExperienceRecord>> GetLevelingHistory(
|
||||
public async Task<ActionResult<SnExperienceRecord>> GetLevelingHistory(
|
||||
[FromQuery] int take = 20,
|
||||
[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
|
||||
.Where(r => r.AccountId == currentUser.Id)
|
||||
@@ -903,7 +903,7 @@ public class AccountCurrentController(
|
||||
[HttpGet("credits")]
|
||||
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);
|
||||
return Ok(credit);
|
||||
@@ -915,7 +915,7 @@ public class AccountCurrentController(
|
||||
[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
|
||||
.Where(r => r.AccountId == currentUser.Id)
|
||||
|
@@ -37,10 +37,10 @@ public class AccountEventService(
|
||||
cache.RemoveAsync(cacheKey);
|
||||
}
|
||||
|
||||
public async Task<Status> GetStatus(Guid userId)
|
||||
public async Task<SnAccountStatus> GetStatus(Guid userId)
|
||||
{
|
||||
var cacheKey = $"{StatusCacheKey}{userId}";
|
||||
var cachedStatus = await cache.GetAsync<Status>(cacheKey);
|
||||
var cachedStatus = await cache.GetAsync<SnAccountStatus>(cacheKey);
|
||||
if (cachedStatus is not null)
|
||||
{
|
||||
cachedStatus!.IsOnline = !cachedStatus.IsInvisible && await GetAccountIsConnected(userId);
|
||||
@@ -64,9 +64,9 @@ public class AccountEventService(
|
||||
|
||||
if (isOnline)
|
||||
{
|
||||
return new Status
|
||||
return new SnAccountStatus
|
||||
{
|
||||
Attitude = StatusAttitude.Neutral,
|
||||
Attitude = Shared.Models.StatusAttitude.Neutral,
|
||||
IsOnline = true,
|
||||
IsCustomized = false,
|
||||
Label = "Online",
|
||||
@@ -74,9 +74,9 @@ public class AccountEventService(
|
||||
};
|
||||
}
|
||||
|
||||
return new Status
|
||||
return new SnAccountStatus
|
||||
{
|
||||
Attitude = StatusAttitude.Neutral,
|
||||
Attitude = Shared.Models.StatusAttitude.Neutral,
|
||||
IsOnline = false,
|
||||
IsCustomized = false,
|
||||
Label = "Offline",
|
||||
@@ -84,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>();
|
||||
|
||||
foreach (var userId in userIds)
|
||||
{
|
||||
var cacheKey = $"{StatusCacheKey}{userId}";
|
||||
var cachedStatus = await cache.GetAsync<Status>(cacheKey);
|
||||
var cachedStatus = await cache.GetAsync<SnAccountStatus>(cacheKey);
|
||||
if (cachedStatus != null)
|
||||
{
|
||||
cachedStatus.IsOnline = !cachedStatus.IsInvisible && await GetAccountIsConnected(userId);
|
||||
@@ -132,9 +132,9 @@ public class AccountEventService(
|
||||
foreach (var userId in usersWithoutStatus)
|
||||
{
|
||||
var isOnline = await GetAccountIsConnected(userId);
|
||||
var defaultStatus = new Status
|
||||
var defaultStatus = new SnAccountStatus
|
||||
{
|
||||
Attitude = StatusAttitude.Neutral,
|
||||
Attitude = Shared.Models.StatusAttitude.Neutral,
|
||||
IsOnline = isOnline,
|
||||
IsCustomized = false,
|
||||
Label = isOnline ? "Online" : "Offline",
|
||||
@@ -148,7 +148,7 @@ public class AccountEventService(
|
||||
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();
|
||||
await db.AccountStatuses
|
||||
@@ -161,7 +161,7 @@ public class AccountEventService(
|
||||
return status;
|
||||
}
|
||||
|
||||
public async Task ClearStatus(Account user, Status status)
|
||||
public async Task ClearStatus(SnAccount user, SnAccountStatus status)
|
||||
{
|
||||
status.ClearedAt = SystemClock.Instance.GetCurrentInstant();
|
||||
db.Update(status);
|
||||
@@ -173,7 +173,7 @@ public class AccountEventService(
|
||||
private const string CaptchaCacheKey = "checkin:captcha:";
|
||||
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);
|
||||
if (perkSubscription is not null) return false;
|
||||
@@ -188,7 +188,7 @@ public class AccountEventService(
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<bool> CheckInDailyIsAvailable(Account user)
|
||||
public async Task<bool> CheckInDailyIsAvailable(SnAccount user)
|
||||
{
|
||||
var now = SystemClock.Instance.GetCurrentInstant();
|
||||
var lastCheckIn = await db.AccountCheckInResults
|
||||
@@ -205,7 +205,7 @@ public class AccountEventService(
|
||||
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 backdatedStart = backdated.ToDateTimeUtc().Date.ToInstant();
|
||||
@@ -253,7 +253,7 @@ public class AccountEventService(
|
||||
|
||||
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}";
|
||||
|
||||
@@ -271,9 +271,7 @@ public class AccountEventService(
|
||||
|
||||
// Now try to acquire the lock properly
|
||||
await using var lockObj =
|
||||
await cache.AcquireLockAsync(lockKey, TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(5));
|
||||
if (lockObj is null) throw new InvalidOperationException("Check-in was in progress.");
|
||||
|
||||
await cache.AcquireLockAsync(lockKey, TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(5)) ?? throw new InvalidOperationException("Check-in was in progress.");
|
||||
var cultureInfo = new CultureInfo(user.Language, false);
|
||||
CultureInfo.CurrentCulture = cultureInfo;
|
||||
CultureInfo.CurrentUICulture = cultureInfo;
|
||||
@@ -283,9 +281,10 @@ public class AccountEventService(
|
||||
.OrderBy(_ => Random.Next())
|
||||
.Take(2)
|
||||
.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
|
||||
}).ToList();
|
||||
|
||||
@@ -295,9 +294,10 @@ public class AccountEventService(
|
||||
.OrderBy(_ => Random.Next())
|
||||
.Take(2)
|
||||
.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
|
||||
}));
|
||||
|
||||
@@ -313,7 +313,7 @@ public class AccountEventService(
|
||||
if (accountBirthday.HasValue && accountBirthday.Value.InUtc().Date == now)
|
||||
checkInLevel = CheckInResultLevel.Special;
|
||||
|
||||
var result = new CheckInResult
|
||||
var result = new SnCheckInResult
|
||||
{
|
||||
Tips = tips,
|
||||
Level = checkInLevel,
|
||||
@@ -323,7 +323,7 @@ public class AccountEventService(
|
||||
BackdatedFrom = backdated.HasValue ? SystemClock.Instance.GetCurrentInstant() : null,
|
||||
CreatedAt = backdated ?? SystemClock.Instance.GetCurrentInstant(),
|
||||
};
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
if (result.RewardPoints.HasValue)
|
||||
@@ -354,7 +354,7 @@ public class AccountEventService(
|
||||
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)
|
||||
{
|
||||
if (year == 0)
|
||||
@@ -368,7 +368,7 @@ public class AccountEventService(
|
||||
.AsNoTracking()
|
||||
.TagWith("eventcal:statuses")
|
||||
.Where(x => x.AccountId == user.Id && x.CreatedAt >= startOfMonth && x.CreatedAt < endOfMonth)
|
||||
.Select(x => new Status
|
||||
.Select(x => new SnAccountStatus
|
||||
{
|
||||
Id = x.Id,
|
||||
Attitude = x.Attitude,
|
||||
@@ -406,7 +406,7 @@ public class AccountEventService(
|
||||
{
|
||||
Date = date,
|
||||
CheckInResult = checkInByDate.GetValueOrDefault(utcDate),
|
||||
Statuses = statusesByDate.GetValueOrDefault(utcDate, new List<Status>())
|
||||
Statuses = statusesByDate.GetValueOrDefault(utcDate, new List<SnAccountStatus>())
|
||||
};
|
||||
}).ToList();
|
||||
}
|
||||
|
@@ -1,18 +1,14 @@
|
||||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
using DysonNetwork.Pass.Auth.OpenId;
|
||||
using DysonNetwork.Pass.Localization;
|
||||
using DysonNetwork.Pass.Mailer;
|
||||
using DysonNetwork.Pass.Permission;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using DysonNetwork.Shared.Stream;
|
||||
using EFCore.BulkExtensions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using NATS.Client.Core;
|
||||
using NATS.Client.JetStream;
|
||||
using NATS.Net;
|
||||
using NodaTime;
|
||||
using OtpNet;
|
||||
@@ -35,7 +31,7 @@ public class AccountService(
|
||||
INatsConnection nats
|
||||
)
|
||||
{
|
||||
public static void SetCultureInfo(Account account)
|
||||
public static void SetCultureInfo(SnAccount account)
|
||||
{
|
||||
SetCultureInfo(account.Language);
|
||||
}
|
||||
@@ -49,12 +45,12 @@ public class AccountService(
|
||||
|
||||
public const string AccountCachePrefix = "account:";
|
||||
|
||||
public async Task PurgeAccountCache(Account account)
|
||||
public async Task PurgeAccountCache(SnAccount account)
|
||||
{
|
||||
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();
|
||||
if (account is not null) return account;
|
||||
@@ -66,7 +62,7 @@ public class AccountService(
|
||||
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
|
||||
.Where(c => c.ProvidedIdentifier == identifier && c.Provider == provider)
|
||||
@@ -83,7 +79,7 @@ public class AccountService(
|
||||
return profile?.Level;
|
||||
}
|
||||
|
||||
public async Task<Account> CreateAccount(
|
||||
public async Task<SnAccount> CreateAccount(
|
||||
string name,
|
||||
string nick,
|
||||
string email,
|
||||
@@ -99,39 +95,39 @@ public class AccountService(
|
||||
throw new InvalidOperationException("Account name has already been taken.");
|
||||
|
||||
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();
|
||||
if (dupeEmailCount > 0)
|
||||
throw new InvalidOperationException("Account email has already been used.");
|
||||
|
||||
var account = new Account
|
||||
var account = new SnAccount
|
||||
{
|
||||
Name = name,
|
||||
Nick = nick,
|
||||
Language = language,
|
||||
Region = region,
|
||||
Contacts = new List<AccountContact>
|
||||
{
|
||||
Contacts =
|
||||
[
|
||||
new()
|
||||
{
|
||||
Type = AccountContactType.Email,
|
||||
Type = Shared.Models.AccountContactType.Email,
|
||||
Content = email,
|
||||
VerifiedAt = isEmailVerified ? SystemClock.Instance.GetCurrentInstant() : null,
|
||||
IsPrimary = true
|
||||
}
|
||||
},
|
||||
],
|
||||
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,
|
||||
EnabledAt = SystemClock.Instance.GetCurrentInstant()
|
||||
}.HashSecret()
|
||||
}
|
||||
: [],
|
||||
Profile = new AccountProfile()
|
||||
Profile = new SnAccountProfile()
|
||||
};
|
||||
|
||||
if (isActivated)
|
||||
@@ -166,7 +162,7 @@ public class AccountService(
|
||||
return account;
|
||||
}
|
||||
|
||||
public async Task<Account> CreateAccount(OidcUserInfo userInfo)
|
||||
public async Task<SnAccount> CreateAccount(OidcUserInfo userInfo)
|
||||
{
|
||||
if (string.IsNullOrEmpty(userInfo.Email))
|
||||
throw new ArgumentException("Email is required for account creation");
|
||||
@@ -190,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)
|
||||
{
|
||||
var dupeAutomateCount = await db.Accounts.Where(a => a.AutomatedId == automatedId).CountAsync();
|
||||
@@ -239,12 +235,12 @@ public class AccountService(
|
||||
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);
|
||||
}
|
||||
|
||||
public async Task RequestAccountDeletion(Account account)
|
||||
public async Task RequestAccountDeletion(SnAccount account)
|
||||
{
|
||||
var spell = await spells.CreateMagicSpell(
|
||||
account,
|
||||
@@ -256,7 +252,7 @@ public class AccountService(
|
||||
await spells.NotifyMagicSpell(spell);
|
||||
}
|
||||
|
||||
public async Task RequestPasswordReset(Account account)
|
||||
public async Task RequestPasswordReset(SnAccount account)
|
||||
{
|
||||
var spell = await spells.CreateMagicSpell(
|
||||
account,
|
||||
@@ -268,7 +264,7 @@ public class AccountService(
|
||||
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
|
||||
.Where(x => x.AccountId == account.Id && x.Type == type)
|
||||
@@ -276,45 +272,45 @@ public class AccountService(
|
||||
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)
|
||||
{
|
||||
case AccountAuthFactorType.Password:
|
||||
case Shared.Models.AccountAuthFactorType.Password:
|
||||
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,
|
||||
AccountId = account.Id,
|
||||
Secret = secret,
|
||||
EnabledAt = SystemClock.Instance.GetCurrentInstant(),
|
||||
}.HashSecret();
|
||||
break;
|
||||
case AccountAuthFactorType.EmailCode:
|
||||
factor = new AccountAuthFactor
|
||||
case Shared.Models.AccountAuthFactorType.EmailCode:
|
||||
factor = new SnAccountAuthFactor
|
||||
{
|
||||
Type = AccountAuthFactorType.EmailCode,
|
||||
Type = Shared.Models.AccountAuthFactorType.EmailCode,
|
||||
Trustworthy = 2,
|
||||
EnabledAt = SystemClock.Instance.GetCurrentInstant(),
|
||||
};
|
||||
break;
|
||||
case AccountAuthFactorType.InAppCode:
|
||||
factor = new AccountAuthFactor
|
||||
case Shared.Models.AccountAuthFactorType.InAppCode:
|
||||
factor = new SnAccountAuthFactor
|
||||
{
|
||||
Type = AccountAuthFactorType.InAppCode,
|
||||
Type = Shared.Models.AccountAuthFactorType.InAppCode,
|
||||
Trustworthy = 1,
|
||||
EnabledAt = SystemClock.Instance.GetCurrentInstant()
|
||||
};
|
||||
break;
|
||||
case AccountAuthFactorType.TimedCode:
|
||||
case Shared.Models.AccountAuthFactorType.TimedCode:
|
||||
var skOtp = KeyGeneration.GenerateRandomKey(20);
|
||||
var skOtp32 = Base32Encoding.ToString(skOtp);
|
||||
factor = new AccountAuthFactor
|
||||
factor = new SnAccountAuthFactor
|
||||
{
|
||||
Secret = skOtp32,
|
||||
Type = AccountAuthFactorType.TimedCode,
|
||||
Type = Shared.Models.AccountAuthFactorType.TimedCode,
|
||||
Trustworthy = 2,
|
||||
EnabledAt = null, // It needs to be tired once to enable
|
||||
CreatedResponse = new Dictionary<string, object>
|
||||
@@ -328,13 +324,13 @@ public class AccountService(
|
||||
}
|
||||
};
|
||||
break;
|
||||
case AccountAuthFactorType.PinCode:
|
||||
case Shared.Models.AccountAuthFactorType.PinCode:
|
||||
if (string.IsNullOrWhiteSpace(secret)) throw new ArgumentNullException(nameof(secret));
|
||||
if (!secret.All(char.IsDigit) || secret.Length != 6)
|
||||
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
|
||||
Secret = secret,
|
||||
EnabledAt = SystemClock.Instance.GetCurrentInstant(),
|
||||
@@ -351,10 +347,10 @@ public class AccountService(
|
||||
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.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))
|
||||
throw new InvalidOperationException(
|
||||
@@ -369,7 +365,7 @@ public class AccountService(
|
||||
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.");
|
||||
|
||||
@@ -387,7 +383,7 @@ public class AccountService(
|
||||
return factor;
|
||||
}
|
||||
|
||||
public async Task DeleteAuthFactor(AccountAuthFactor factor)
|
||||
public async Task DeleteAuthFactor(SnAccountAuthFactor factor)
|
||||
{
|
||||
var count = await db.AccountAuthFactors
|
||||
.Where(f => f.AccountId == factor.AccountId)
|
||||
@@ -405,13 +401,13 @@ public class AccountService(
|
||||
/// </summary>
|
||||
/// <param name="account">The owner of the auth factor</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");
|
||||
|
||||
switch (factor.Type)
|
||||
{
|
||||
case AccountAuthFactorType.InAppCode:
|
||||
case Shared.Models.AccountAuthFactorType.InAppCode:
|
||||
if (await _GetFactorCode(factor) is not null)
|
||||
throw new InvalidOperationException("A factor code has been sent and in active duration.");
|
||||
|
||||
@@ -430,12 +426,12 @@ public class AccountService(
|
||||
);
|
||||
await _SetFactorCode(factor, code, TimeSpan.FromMinutes(5));
|
||||
break;
|
||||
case AccountAuthFactorType.EmailCode:
|
||||
case Shared.Models.AccountAuthFactorType.EmailCode:
|
||||
if (await _GetFactorCode(factor) is not null)
|
||||
throw new InvalidOperationException("A factor code has been sent and in active duration.");
|
||||
|
||||
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.IsPrimary)
|
||||
.Where(c => c.AccountId == account.Id)
|
||||
@@ -464,27 +460,27 @@ public class AccountService(
|
||||
|
||||
await _SetFactorCode(factor, code, TimeSpan.FromMinutes(30));
|
||||
break;
|
||||
case AccountAuthFactorType.Password:
|
||||
case AccountAuthFactorType.TimedCode:
|
||||
case Shared.Models.AccountAuthFactorType.Password:
|
||||
case Shared.Models.AccountAuthFactorType.TimedCode:
|
||||
default:
|
||||
// No need to send, such as password etc...
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> VerifyFactorCode(AccountAuthFactor factor, string code)
|
||||
public async Task<bool> VerifyFactorCode(SnAccountAuthFactor factor, string code)
|
||||
{
|
||||
switch (factor.Type)
|
||||
{
|
||||
case AccountAuthFactorType.EmailCode:
|
||||
case AccountAuthFactorType.InAppCode:
|
||||
case Shared.Models.AccountAuthFactorType.EmailCode:
|
||||
case Shared.Models.AccountAuthFactorType.InAppCode:
|
||||
var correctCode = await _GetFactorCode(factor);
|
||||
var isCorrect = correctCode is not null &&
|
||||
string.Equals(correctCode, code, StringComparison.OrdinalIgnoreCase);
|
||||
await cache.RemoveAsync($"{AuthFactorCachePrefix}{factor.Id}:code");
|
||||
return isCorrect;
|
||||
case AccountAuthFactorType.Password:
|
||||
case AccountAuthFactorType.TimedCode:
|
||||
case Shared.Models.AccountAuthFactorType.Password:
|
||||
case Shared.Models.AccountAuthFactorType.TimedCode:
|
||||
default:
|
||||
return factor.VerifyPassword(code);
|
||||
}
|
||||
@@ -492,7 +488,7 @@ public class AccountService(
|
||||
|
||||
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(
|
||||
$"{AuthFactorCachePrefix}{factor.Id}:code",
|
||||
@@ -501,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?>(
|
||||
$"{AuthFactorCachePrefix}{factor.Id}:code"
|
||||
@@ -515,7 +511,7 @@ public class AccountService(
|
||||
.AnyAsync(s => s.Challenge.ClientId == id);
|
||||
}
|
||||
|
||||
public async Task<SnAuthClient> 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
|
||||
);
|
||||
@@ -528,7 +524,7 @@ public class AccountService(
|
||||
return device;
|
||||
}
|
||||
|
||||
public async Task DeleteSession(Account account, Guid sessionId)
|
||||
public async Task DeleteSession(SnAccount account, Guid sessionId)
|
||||
{
|
||||
var session = await db.AuthSessions
|
||||
.Include(s => s.Challenge)
|
||||
@@ -554,7 +550,7 @@ public class AccountService(
|
||||
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
|
||||
);
|
||||
@@ -584,7 +580,7 @@ public class AccountService(
|
||||
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
|
||||
.Where(x => x.AccountId == account.Id && x.Type == type && x.Content == content)
|
||||
@@ -592,7 +588,7 @@ public class AccountService(
|
||||
if (isExists)
|
||||
throw new InvalidOperationException("Contact method already exists.");
|
||||
|
||||
var contact = new AccountContact
|
||||
var contact = new SnAccountContact
|
||||
{
|
||||
Type = type,
|
||||
Content = content,
|
||||
@@ -605,7 +601,7 @@ public class AccountService(
|
||||
return contact;
|
||||
}
|
||||
|
||||
public async Task VerifyContactMethod(Account account, AccountContact contact)
|
||||
public async Task VerifyContactMethod(SnAccount account, SnAccountContact contact)
|
||||
{
|
||||
var spell = await spells.CreateMagicSpell(
|
||||
account,
|
||||
@@ -617,7 +613,7 @@ public class AccountService(
|
||||
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)
|
||||
throw new InvalidOperationException("Contact method does not belong to this account.");
|
||||
@@ -646,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;
|
||||
db.AccountContacts.Update(contact);
|
||||
@@ -654,7 +650,7 @@ public class AccountService(
|
||||
return contact;
|
||||
}
|
||||
|
||||
public async Task DeleteContactMethod(Account account, AccountContact contact)
|
||||
public async Task DeleteContactMethod(SnAccount account, SnAccountContact contact)
|
||||
{
|
||||
if (contact.AccountId != account.Id)
|
||||
throw new InvalidOperationException("Contact method does not belong to this account.");
|
||||
@@ -669,7 +665,7 @@ public class AccountService(
|
||||
/// This method will grant a badge to the account.
|
||||
/// Shouldn't be exposed to normal user and the user itself.
|
||||
/// </summary>
|
||||
public async Task<AccountBadge> GrantBadge(Account account, AccountBadge badge)
|
||||
public async Task<SnAccountBadge> GrantBadge(SnAccount account, SnAccountBadge badge)
|
||||
{
|
||||
badge.AccountId = account.Id;
|
||||
db.Badges.Add(badge);
|
||||
@@ -681,14 +677,12 @@ public class AccountService(
|
||||
/// This method will revoke a badge from the account.
|
||||
/// Shouldn't be exposed to normal user and the user itself.
|
||||
/// </summary>
|
||||
public async Task RevokeBadge(Account account, Guid badgeId)
|
||||
public async Task RevokeBadge(SnAccount account, Guid badgeId)
|
||||
{
|
||||
var badge = await db.Badges
|
||||
.Where(b => b.AccountId == account.Id && b.Id == badgeId)
|
||||
.OrderByDescending(b => b.CreatedAt)
|
||||
.FirstOrDefaultAsync();
|
||||
if (badge is null) throw new InvalidOperationException("Badge was not found.");
|
||||
|
||||
.FirstOrDefaultAsync() ?? throw new InvalidOperationException("Badge was not found.");
|
||||
var profile = await db.AccountProfiles
|
||||
.Where(p => p.AccountId == account.Id)
|
||||
.FirstOrDefaultAsync();
|
||||
@@ -699,7 +693,7 @@ public class AccountService(
|
||||
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();
|
||||
|
||||
@@ -733,7 +727,7 @@ public class AccountService(
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteAccount(Account account)
|
||||
public async Task DeleteAccount(SnAccount account)
|
||||
{
|
||||
await db.AuthSessions
|
||||
.Where(s => s.AccountId == account.Id)
|
||||
|
@@ -236,7 +236,7 @@ public class AccountServiceGrpc(
|
||||
var relationship = await relationships.GetRelationship(
|
||||
Guid.Parse(request.AccountId),
|
||||
Guid.Parse(request.RelatedId),
|
||||
status: (RelationshipStatus?)request.Status
|
||||
status: (Shared.Models.RelationshipStatus?)request.Status
|
||||
);
|
||||
return new GetRelationshipResponse
|
||||
{
|
||||
@@ -256,7 +256,7 @@ public class AccountServiceGrpc(
|
||||
hasRelationship = await relationships.HasRelationshipWithStatus(
|
||||
Guid.Parse(request.AccountId),
|
||||
Guid.Parse(request.RelatedId),
|
||||
(RelationshipStatus)request.Status
|
||||
(Shared.Models.RelationshipStatus)request.Status
|
||||
);
|
||||
return new BoolValue { Value = hasRelationship };
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ public class ActionLogService(GeoIpService geo, FlushBufferService fbs)
|
||||
{
|
||||
public void CreateActionLog(Guid accountId, string action, Dictionary<string, object?> meta)
|
||||
{
|
||||
var log = new ActionLog
|
||||
var log = new SnActionLog
|
||||
{
|
||||
Action = action,
|
||||
AccountId = accountId,
|
||||
@@ -19,9 +19,9 @@ public class ActionLogService(GeoIpService geo, FlushBufferService fbs)
|
||||
}
|
||||
|
||||
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,
|
||||
Meta = meta,
|
||||
@@ -30,7 +30,7 @@ public class ActionLogService(GeoIpService geo, FlushBufferService fbs)
|
||||
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;
|
||||
else if (account != null)
|
||||
log.AccountId = account.Id;
|
||||
|
@@ -22,7 +22,7 @@ public class BotAccountReceiverGrpc(
|
||||
ServerCallContext context
|
||||
)
|
||||
{
|
||||
var account = Account.FromProtoValue(request.Account);
|
||||
var account = SnAccount.FromProtoValue(request.Account);
|
||||
account = await accounts.CreateBotAccount(
|
||||
account,
|
||||
Guid.Parse(request.AutomatedId),
|
||||
@@ -48,7 +48,7 @@ public class BotAccountReceiverGrpc(
|
||||
ServerCallContext context
|
||||
)
|
||||
{
|
||||
var account = Account.FromProtoValue(request.Account);
|
||||
var account = SnAccount.FromProtoValue(request.Account);
|
||||
|
||||
if (request.PictureId is not null)
|
||||
{
|
||||
|
@@ -50,7 +50,7 @@ public class MagicSpellController(AppDatabase db, MagicSpellService sp) : Contro
|
||||
return NotFound();
|
||||
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);
|
||||
else
|
||||
await sp.ApplyMagicSpell(spell);
|
||||
|
@@ -2,8 +2,8 @@ using System.Security.Cryptography;
|
||||
using System.Text.Json;
|
||||
using DysonNetwork.Pass.Emails;
|
||||
using DysonNetwork.Pass.Mailer;
|
||||
using DysonNetwork.Pass.Permission;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using NodaTime;
|
||||
@@ -20,8 +20,8 @@ public class MagicSpellService(
|
||||
ICacheService cache
|
||||
)
|
||||
{
|
||||
public async Task<MagicSpell> CreateMagicSpell(
|
||||
Account account,
|
||||
public async Task<SnMagicSpell> CreateMagicSpell(
|
||||
SnAccount account,
|
||||
MagicSpellType type,
|
||||
Dictionary<string, object> meta,
|
||||
Instant? expiredAt = null,
|
||||
@@ -42,7 +42,7 @@ public class MagicSpellService(
|
||||
}
|
||||
|
||||
var spellWord = _GenerateRandomString(128);
|
||||
var spell = new MagicSpell
|
||||
var spell = new SnMagicSpell
|
||||
{
|
||||
Spell = spellWord,
|
||||
Type = type,
|
||||
@@ -60,7 +60,7 @@ public class MagicSpellService(
|
||||
|
||||
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 (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)
|
||||
{
|
||||
@@ -191,7 +191,7 @@ public class MagicSpellService(
|
||||
var defaultGroup = await db.PermissionGroups.FirstOrDefaultAsync(g => g.Key == "default");
|
||||
if (defaultGroup is not null && account is not null)
|
||||
{
|
||||
db.PermissionGroupMembers.Add(new PermissionGroupMember
|
||||
db.PermissionGroupMembers.Add(new SnPermissionGroupMember
|
||||
{
|
||||
Actor = $"user:{account.Id}",
|
||||
Group = defaultGroup
|
||||
@@ -218,7 +218,7 @@ public class MagicSpellService(
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task ApplyPasswordReset(MagicSpell spell, string newPassword)
|
||||
public async Task ApplyPasswordReset(SnMagicSpell spell, string newPassword)
|
||||
{
|
||||
if (spell.Type != MagicSpellType.AuthPasswordReset)
|
||||
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);
|
||||
if (account is null) throw new InvalidOperationException("Both account and auth factor was not found.");
|
||||
passwordFactor = new AccountAuthFactor
|
||||
passwordFactor = new SnAccountAuthFactor
|
||||
{
|
||||
Type = AccountAuthFactorType.Password,
|
||||
Account = account,
|
||||
@@ -257,6 +257,6 @@ public class MagicSpellService(
|
||||
|
||||
var base64String = Convert.ToBase64String(randomBytes);
|
||||
|
||||
return base64String.Substring(0, length);
|
||||
return base64String[..length];
|
||||
}
|
||||
}
|
@@ -1,3 +1,4 @@
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
@@ -26,7 +27,7 @@ public class NotableDaysController(NotableDaysService days) : ControllerBase
|
||||
[Authorize]
|
||||
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;
|
||||
if (string.IsNullOrWhiteSpace(region)) region = "us";
|
||||
@@ -39,7 +40,7 @@ public class NotableDaysController(NotableDaysService days) : ControllerBase
|
||||
[Authorize]
|
||||
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 region = currentUser.Region;
|
||||
@@ -64,7 +65,7 @@ public class NotableDaysController(NotableDaysService days) : ControllerBase
|
||||
[Authorize]
|
||||
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;
|
||||
if (string.IsNullOrWhiteSpace(region)) region = "us";
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -12,10 +13,10 @@ public class RelationshipController(AppDatabase db, RelationshipService rels) :
|
||||
{
|
||||
[HttpGet]
|
||||
[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)
|
||||
{
|
||||
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 query = db.AccountRelationships.AsQueryable()
|
||||
@@ -44,9 +45,9 @@ public class RelationshipController(AppDatabase db, RelationshipService rels) :
|
||||
|
||||
[HttpGet("requests")]
|
||||
[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
|
||||
.Where(r => r.AccountId == currentUser.Id && r.Status == RelationshipStatus.Pending)
|
||||
@@ -66,10 +67,10 @@ public class RelationshipController(AppDatabase db, RelationshipService rels) :
|
||||
|
||||
[HttpPost("{userId:guid}")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<Relationship>> CreateRelationship(Guid userId,
|
||||
public async Task<ActionResult<SnAccountRelationship>> CreateRelationship(Guid userId,
|
||||
[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);
|
||||
if (relatedUser is null) return NotFound("Account was not found.");
|
||||
@@ -89,10 +90,10 @@ public class RelationshipController(AppDatabase db, RelationshipService rels) :
|
||||
|
||||
[HttpPatch("{userId:guid}")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<Relationship>> UpdateRelationship(Guid userId,
|
||||
public async Task<ActionResult<SnAccountRelationship>> UpdateRelationship(Guid userId,
|
||||
[FromBody] RelationshipRequest request)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -111,9 +112,9 @@ public class RelationshipController(AppDatabase db, RelationshipService rels) :
|
||||
|
||||
[HttpGet("{userId:guid}")]
|
||||
[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 queries = db.AccountRelationships.AsQueryable()
|
||||
@@ -131,9 +132,9 @@ public class RelationshipController(AppDatabase db, RelationshipService rels) :
|
||||
|
||||
[HttpPost("{userId:guid}/friends")]
|
||||
[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);
|
||||
if (relatedUser is null) return NotFound("Account was not found.");
|
||||
@@ -158,7 +159,7 @@ public class RelationshipController(AppDatabase db, RelationshipService rels) :
|
||||
[Authorize]
|
||||
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
|
||||
{
|
||||
@@ -173,9 +174,9 @@ public class RelationshipController(AppDatabase db, RelationshipService rels) :
|
||||
|
||||
[HttpPost("{userId:guid}/friends/accept")]
|
||||
[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);
|
||||
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")]
|
||||
[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);
|
||||
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")]
|
||||
[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);
|
||||
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")]
|
||||
[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);
|
||||
if (relatedUser is null) return NotFound("Account was not found.");
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using DysonNetwork.Pass.Localization;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Localization;
|
||||
@@ -26,7 +27,7 @@ public class RelationshipService(
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
public async Task<Relationship?> GetRelationship(
|
||||
public async Task<SnAccountRelationship?> GetRelationship(
|
||||
Guid accountId,
|
||||
Guid relatedId,
|
||||
RelationshipStatus? status = null,
|
||||
@@ -42,7 +43,7 @@ public class RelationshipService(
|
||||
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)
|
||||
throw new InvalidOperationException(
|
||||
@@ -50,7 +51,7 @@ public class RelationshipService(
|
||||
if (await HasExistingRelationship(sender.Id, target.Id))
|
||||
throw new InvalidOperationException("Found existing relationship between you and target user.");
|
||||
|
||||
var relationship = new Relationship
|
||||
var relationship = new SnAccountRelationship
|
||||
{
|
||||
AccountId = sender.Id,
|
||||
RelatedId = target.Id,
|
||||
@@ -65,14 +66,14 @@ public class RelationshipService(
|
||||
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))
|
||||
return await UpdateRelationship(sender.Id, target.Id, 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);
|
||||
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;
|
||||
}
|
||||
|
||||
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))
|
||||
throw new InvalidOperationException("Found existing relationship between you and target user.");
|
||||
|
||||
var relationship = new Relationship
|
||||
var relationship = new SnAccountRelationship
|
||||
{
|
||||
AccountId = sender.Id,
|
||||
RelatedId = target.Id,
|
||||
@@ -128,8 +129,8 @@ public class RelationshipService(
|
||||
await PurgeRelationshipCache(relationship.AccountId, relationship.RelatedId);
|
||||
}
|
||||
|
||||
public async Task<Relationship> AcceptFriendRelationship(
|
||||
Relationship relationship,
|
||||
public async Task<SnAccountRelationship> AcceptFriendRelationship(
|
||||
SnAccountRelationship relationship,
|
||||
RelationshipStatus status = RelationshipStatus.Friends
|
||||
)
|
||||
{
|
||||
@@ -144,7 +145,7 @@ public class RelationshipService(
|
||||
relationship.ExpiredAt = null;
|
||||
db.Update(relationship);
|
||||
|
||||
var relationshipBackward = new Relationship
|
||||
var relationshipBackward = new SnAccountRelationship
|
||||
{
|
||||
AccountId = relationship.RelatedId,
|
||||
RelatedId = relationship.AccountId,
|
||||
@@ -159,7 +160,7 @@ public class RelationshipService(
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
public async Task<List<Guid>> ListAccountFriends(Account account)
|
||||
public async Task<List<Guid>> ListAccountFriends(SnAccount account)
|
||||
{
|
||||
return await ListAccountFriends(account.Id);
|
||||
}
|
||||
@@ -197,7 +198,7 @@ public class RelationshipService(
|
||||
return friends ?? [];
|
||||
}
|
||||
|
||||
public async Task<List<Guid>> ListAccountBlocked(Account account)
|
||||
public async Task<List<Guid>> ListAccountBlocked(SnAccount account)
|
||||
{
|
||||
return await ListAccountBlocked(account.Id);
|
||||
}
|
||||
|
@@ -2,9 +2,7 @@ using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using DysonNetwork.Pass.Account;
|
||||
using DysonNetwork.Pass.Permission;
|
||||
using DysonNetwork.Shared.Data;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
@@ -24,16 +22,16 @@ public class AppDatabase(
|
||||
public DbSet<SnPermissionGroupMember> PermissionGroupMembers { get; set; } = null!;
|
||||
|
||||
public DbSet<SnMagicSpell> MagicSpells { get; set; } = null!;
|
||||
public DbSet<Account.Account> Accounts { get; set; } = null!;
|
||||
public DbSet<AccountConnection> AccountConnections { get; set; } = null!;
|
||||
public DbSet<SnAccount> Accounts { get; set; } = null!;
|
||||
public DbSet<SnAccountConnection> AccountConnections { get; set; } = null!;
|
||||
public DbSet<SnAccountProfile> AccountProfiles { get; set; } = null!;
|
||||
public DbSet<AccountContact> AccountContacts { get; set; } = null!;
|
||||
public DbSet<AccountAuthFactor> AccountAuthFactors { get; set; } = null!;
|
||||
public DbSet<SnAccountContact> AccountContacts { get; set; } = null!;
|
||||
public DbSet<SnAccountAuthFactor> AccountAuthFactors { get; set; } = null!;
|
||||
public DbSet<SnAccountRelationship> AccountRelationships { get; set; } = null!;
|
||||
public DbSet<SnAccountStatus> AccountStatuses { get; set; } = null!;
|
||||
public DbSet<SnCheckInResult> AccountCheckInResults { 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<SnAbuseReport> AbuseReports { get; set; } = null!;
|
||||
|
||||
public DbSet<SnAuthSession> AuthSessions { get; set; } = null!;
|
||||
@@ -41,17 +39,17 @@ public class AppDatabase(
|
||||
public DbSet<SnAuthClient> AuthClients { get; set; } = null!;
|
||||
public DbSet<SnApiKey> ApiKeys { get; set; } = null!;
|
||||
|
||||
public DbSet<Shared.Models.SnWallet> Wallets { get; set; } = null!;
|
||||
public DbSet<SnWallet> Wallets { get; set; } = null!;
|
||||
public DbSet<SnWalletPocket> WalletPockets { get; set; } = null!;
|
||||
public DbSet<SnWalletOrder> PaymentOrders { get; set; } = null!;
|
||||
public DbSet<SnWalletTransaction> PaymentTransactions { get; set; } = null!;
|
||||
public DbSet<SnSubscription> WalletSubscriptions { get; set; } = null!;
|
||||
public DbSet<Coupon> WalletCoupons { get; set; } = null!;
|
||||
public DbSet<SnWalletSubscription> WalletSubscriptions { get; set; } = null!;
|
||||
public DbSet<SnWalletCoupon> WalletCoupons { get; set; } = null!;
|
||||
|
||||
public DbSet<SnAccountPunishment> Punishments { get; set; } = null!;
|
||||
|
||||
public DbSet<SocialCreditRecord> SocialCreditRecords { get; set; } = null!;
|
||||
public DbSet<ExperienceRecord> ExperienceRecords { get; set; } = null!;
|
||||
public DbSet<SnSocialCreditRecord> SocialCreditRecords { get; set; } = null!;
|
||||
public DbSet<SnExperienceRecord> ExperienceRecords { get; set; } = null!;
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -14,7 +15,7 @@ public class ApiKeyController(AppDatabase db, AuthService auth) : ControllerBase
|
||||
[Authorize]
|
||||
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
|
||||
.Where(e => e.AccountId == currentUser.Id)
|
||||
@@ -34,7 +35,7 @@ public class ApiKeyController(AppDatabase db, AuthService auth) : ControllerBase
|
||||
[Authorize]
|
||||
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
|
||||
.Where(e => e.AccountId == currentUser.Id)
|
||||
@@ -56,7 +57,7 @@ public class ApiKeyController(AppDatabase db, AuthService auth) : ControllerBase
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request.Label))
|
||||
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);
|
||||
key.Key = await auth.IssueApiKeyToken(key);
|
||||
@@ -67,7 +68,7 @@ public class ApiKeyController(AppDatabase db, AuthService auth) : ControllerBase
|
||||
[Authorize]
|
||||
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);
|
||||
if(key is null) return NotFound();
|
||||
@@ -80,7 +81,7 @@ public class ApiKeyController(AppDatabase db, AuthService auth) : ControllerBase
|
||||
[Authorize]
|
||||
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);
|
||||
if(key is null) return NotFound();
|
||||
|
@@ -1,5 +1,3 @@
|
||||
using NodaTime;
|
||||
|
||||
namespace DysonNetwork.Pass.Auth;
|
||||
|
||||
public static class AuthCacheConstants
|
||||
|
@@ -2,13 +2,10 @@ using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NodaTime;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using DysonNetwork.Pass.Account;
|
||||
using DysonNetwork.Pass.Localization;
|
||||
using DysonNetwork.Shared.Data;
|
||||
using DysonNetwork.Shared.GeoIp;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using AccountAuthFactor = DysonNetwork.Pass.Account.AccountAuthFactor;
|
||||
using AccountService = DysonNetwork.Pass.Account.AccountService;
|
||||
using ActionLogService = DysonNetwork.Pass.Account.ActionLogService;
|
||||
using DysonNetwork.Shared.Models;
|
||||
@@ -41,7 +38,7 @@ public class AuthController(
|
||||
}
|
||||
|
||||
[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);
|
||||
if (account is null) return NotFound("Account was not found.");
|
||||
@@ -73,7 +70,7 @@ public class AuthController(
|
||||
.Where(e => e.UserAgent == userAgent)
|
||||
.Where(e => e.StepRemain > 0)
|
||||
.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)
|
||||
.FirstOrDefaultAsync();
|
||||
if (existingChallenge is not null)
|
||||
@@ -83,7 +80,7 @@ public class AuthController(
|
||||
if (existingSession is null) return existingChallenge;
|
||||
}
|
||||
|
||||
var challenge = new AuthChallenge
|
||||
var challenge = new SnAuthChallenge
|
||||
{
|
||||
ExpiredAt = Instant.FromDateTimeUtc(DateTime.UtcNow.AddHours(1)),
|
||||
StepTotal = await auth.DetectChallengeRisk(Request, account),
|
||||
@@ -107,7 +104,7 @@ public class AuthController(
|
||||
}
|
||||
|
||||
[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
|
||||
.Include(e => e.Account)
|
||||
@@ -120,7 +117,7 @@ public class AuthController(
|
||||
}
|
||||
|
||||
[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
|
||||
.Include(e => e.Account)
|
||||
@@ -166,7 +163,7 @@ public class AuthController(
|
||||
}
|
||||
|
||||
[HttpPatch("challenge/{id:guid}")]
|
||||
public async Task<ActionResult<AuthChallenge>> DoChallenge(
|
||||
public async Task<ActionResult<SnAuthChallenge>> DoChallenge(
|
||||
[FromRoute] Guid id,
|
||||
[FromBody] PerformChallengeRequest request
|
||||
)
|
||||
|
@@ -1,7 +1,6 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using DysonNetwork.Pass.Account;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -14,8 +13,7 @@ public class AuthService(
|
||||
IConfiguration config,
|
||||
IHttpClientFactory httpClientFactory,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ICacheService cache,
|
||||
ILogger<AuthService> logger
|
||||
ICacheService cache
|
||||
)
|
||||
{
|
||||
private HttpContext HttpContext => httpContextAccessor.HttpContext!;
|
||||
@@ -28,7 +26,7 @@ public class AuthService(
|
||||
/// <param name="request">The request context</param>
|
||||
/// <param name="account">The account to login</param>
|
||||
/// <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.
|
||||
var maxSteps = await db.AccountAuthFactors
|
||||
@@ -77,7 +75,7 @@ public class AuthService(
|
||||
return totalRequiredSteps;
|
||||
}
|
||||
|
||||
public async Task<SnAuthSession> CreateSessionForOidcAsync(Account.Account account, Instant time,
|
||||
public async Task<SnAuthSession> CreateSessionForOidcAsync(SnAccount account, Instant time,
|
||||
Guid? customAppId = null)
|
||||
{
|
||||
var challenge = new SnAuthChallenge
|
||||
|
@@ -6,7 +6,6 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Web;
|
||||
using DysonNetwork.Pass.Account;
|
||||
using DysonNetwork.Pass.Auth.OidcProvider.Options;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
@@ -131,7 +130,7 @@ public class OidcProviderController(
|
||||
[FromForm(Name = "code_challenge_method")]
|
||||
string? codeChallengeMethod = null)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account account)
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount account)
|
||||
return Unauthorized();
|
||||
|
||||
// Find the client
|
||||
@@ -303,7 +302,7 @@ public class OidcProviderController(
|
||||
[Authorize]
|
||||
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 SnAuthSession currentSession) return Unauthorized();
|
||||
|
||||
// Get requested scopes from the token
|
||||
|
@@ -1,5 +1,3 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NodaTime;
|
||||
|
||||
namespace DysonNetwork.Pass.Auth.OidcProvider.Models;
|
||||
|
@@ -1,4 +1,3 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using DysonNetwork.Shared.Models;
|
||||
|
||||
namespace DysonNetwork.Pass.Auth.OidcProvider.Responses;
|
||||
|
@@ -12,7 +12,7 @@ using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using NodaTime;
|
||||
using AccountContactType = DysonNetwork.Pass.Account.AccountContactType;
|
||||
using AccountContactType = DysonNetwork.Shared.Models.AccountContactType;
|
||||
|
||||
namespace DysonNetwork.Pass.Auth.OidcProvider.Services;
|
||||
|
||||
@@ -39,7 +39,7 @@ public class OidcProviderService(
|
||||
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();
|
||||
|
||||
@@ -58,7 +58,7 @@ public class OidcProviderService(
|
||||
s.AppId == clientId &&
|
||||
(s.ExpiredAt == null || s.ExpiredAt > now) &&
|
||||
s.Challenge != null &&
|
||||
s.Challenge.Type == ChallengeType.OAuth)
|
||||
s.Challenge.Type == Shared.Models.ChallengeType.OAuth)
|
||||
.OrderByDescending(s => s.CreatedAt)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
@@ -81,7 +81,7 @@ public class OidcProviderService(
|
||||
|
||||
|
||||
var client = await FindClientByIdAsync(clientId);
|
||||
if (client?.Status != CustomAppStatus.Production)
|
||||
if (client?.Status != Shared.Proto.CustomAppStatus.Production)
|
||||
return true;
|
||||
|
||||
if (client?.OauthConfig?.RedirectUris == null)
|
||||
@@ -146,7 +146,7 @@ public class OidcProviderService(
|
||||
|
||||
private string GenerateIdToken(
|
||||
CustomApp client,
|
||||
AuthSession session,
|
||||
SnAuthSession session,
|
||||
string? nonce = null,
|
||||
IEnumerable<string>? scopes = null
|
||||
)
|
||||
@@ -225,11 +225,9 @@ public class OidcProviderService(
|
||||
Guid? sessionId = null
|
||||
)
|
||||
{
|
||||
var client = await FindClientByIdAsync(clientId);
|
||||
if (client == null)
|
||||
throw new InvalidOperationException("Client not found");
|
||||
var client = await FindClientByIdAsync(clientId) ?? throw new InvalidOperationException("Client not found");
|
||||
|
||||
AuthSession session;
|
||||
SnAuthSession session;
|
||||
var clock = SystemClock.Instance;
|
||||
var now = clock.GetCurrentInstant();
|
||||
string? nonce = null;
|
||||
@@ -300,7 +298,7 @@ public class OidcProviderService(
|
||||
|
||||
private string GenerateJwtToken(
|
||||
CustomApp client,
|
||||
AuthSession session,
|
||||
SnAuthSession session,
|
||||
Instant expiresAt,
|
||||
IEnumerable<string>? scopes = null
|
||||
)
|
||||
@@ -372,7 +370,7 @@ public class OidcProviderService(
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<AuthSession?> FindSessionByIdAsync(Guid sessionId)
|
||||
public async Task<SnAuthSession?> FindSessionByIdAsync(Guid sessionId)
|
||||
{
|
||||
return await db.AuthSessions
|
||||
.Include(s => s.Account)
|
||||
@@ -380,7 +378,7 @@ public class OidcProviderService(
|
||||
.FirstOrDefaultAsync(s => s.Id == sessionId);
|
||||
}
|
||||
|
||||
private static string GenerateRefreshToken(AuthSession session)
|
||||
private static string GenerateRefreshToken(SnAuthSession session)
|
||||
{
|
||||
return Convert.ToBase64String(session.Id.ToByteArray());
|
||||
}
|
||||
|
@@ -1,6 +1,4 @@
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using DysonNetwork.Pass;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
|
||||
namespace DysonNetwork.Pass.Auth.OpenId;
|
||||
|
@@ -1,6 +1,5 @@
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace DysonNetwork.Pass.Auth.OpenId;
|
||||
|
||||
|
@@ -3,7 +3,6 @@ using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using DysonNetwork.Pass;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
|
@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using NodaTime;
|
||||
using DysonNetwork.Shared.Models;
|
||||
|
||||
namespace DysonNetwork.Pass.Auth.OpenId;
|
||||
|
||||
@@ -23,9 +24,9 @@ public class ConnectionController(
|
||||
private static readonly TimeSpan StateExpiration = TimeSpan.FromMinutes(15);
|
||||
|
||||
[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();
|
||||
|
||||
var connections = await db.AccountConnections
|
||||
@@ -48,7 +49,7 @@ public class ConnectionController(
|
||||
[HttpDelete("{id:guid}")]
|
||||
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();
|
||||
|
||||
var connection = await db.AccountConnections
|
||||
@@ -66,7 +67,7 @@ public class ConnectionController(
|
||||
[HttpPost("/api/auth/connect/apple/mobile")]
|
||||
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();
|
||||
|
||||
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")}.");
|
||||
}
|
||||
|
||||
db.AccountConnections.Add(new AccountConnection
|
||||
db.AccountConnections.Add(new SnAccountConnection
|
||||
{
|
||||
AccountId = currentUser.Id,
|
||||
Provider = "apple",
|
||||
@@ -250,7 +251,7 @@ public class ConnectionController(
|
||||
else
|
||||
{
|
||||
// Create new connection
|
||||
db.AccountConnections.Add(new AccountConnection
|
||||
db.AccountConnections.Add(new SnAccountConnection
|
||||
{
|
||||
AccountId = accountId,
|
||||
Provider = provider,
|
||||
@@ -324,7 +325,7 @@ public class ConnectionController(
|
||||
var account = await accounts.LookupAccount(userInfo.Email) ?? await accounts.CreateAccount(userInfo);
|
||||
|
||||
// Create connection for new or existing user
|
||||
var newConnection = new AccountConnection
|
||||
var newConnection = new SnAccountConnection
|
||||
{
|
||||
Account = account,
|
||||
Provider = provider,
|
||||
|
@@ -1,6 +1,4 @@
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using DysonNetwork.Pass;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
|
||||
namespace DysonNetwork.Pass.Auth.OpenId;
|
||||
|
@@ -1,6 +1,4 @@
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using DysonNetwork.Pass;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
|
||||
namespace DysonNetwork.Pass.Auth.OpenId;
|
||||
|
@@ -1,8 +1,4 @@
|
||||
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 Microsoft.IdentityModel.Tokens;
|
||||
|
||||
|
@@ -33,7 +33,7 @@ public class OidcController(
|
||||
var oidcService = GetOidcService(provider);
|
||||
|
||||
// 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 nonce = Guid.NewGuid().ToString();
|
||||
@@ -128,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))
|
||||
throw new ArgumentException("Email is required for account creation");
|
||||
@@ -157,7 +157,7 @@ public class OidcController(
|
||||
return existingAccount;
|
||||
}
|
||||
|
||||
var connection = new AccountConnection
|
||||
var connection = new SnAccountConnection
|
||||
{
|
||||
AccountId = existingAccount.Id,
|
||||
Provider = provider,
|
||||
@@ -178,7 +178,7 @@ public class OidcController(
|
||||
var newAccount = await accounts.CreateAccount(userInfo);
|
||||
|
||||
// Create the provider connection
|
||||
var newConnection = new AccountConnection
|
||||
var newConnection = new SnAccountConnection
|
||||
{
|
||||
AccountId = newAccount.Id,
|
||||
Provider = provider,
|
||||
|
@@ -1,6 +1,5 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Text.Json.Serialization;
|
||||
using DysonNetwork.Pass.Account;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -190,7 +189,7 @@ public abstract class OidcService(
|
||||
/// </summary>
|
||||
public async Task<SnAuthChallenge> CreateChallengeForUserAsync(
|
||||
OidcUserInfo userInfo,
|
||||
Account.Account account,
|
||||
SnAccount account,
|
||||
HttpContext request,
|
||||
string deviceId,
|
||||
string? deviceName = null
|
||||
@@ -205,7 +204,7 @@ public abstract class OidcService(
|
||||
|
||||
if (connection is null)
|
||||
{
|
||||
connection = new AccountConnection
|
||||
connection = new SnAccountConnection
|
||||
{
|
||||
Provider = ProviderName,
|
||||
ProvidedIdentifier = userInfo.UserId ?? "",
|
||||
|
@@ -1,4 +1,3 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using DysonNetwork.Pass.Wallet;
|
||||
|
@@ -8,9 +8,9 @@ public class SocialCreditService(AppDatabase db, ICacheService cache)
|
||||
{
|
||||
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,
|
||||
Reason = reason,
|
||||
|
@@ -1,14 +1,14 @@
|
||||
using DysonNetwork.Pass.Account;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using EFCore.BulkExtensions;
|
||||
using NodaTime;
|
||||
using Quartz;
|
||||
|
||||
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();
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDatabase>();
|
||||
|
@@ -9,7 +9,7 @@ namespace DysonNetwork.Pass.Handlers;
|
||||
public class LastActiveInfo
|
||||
{
|
||||
public SnAuthSession Session { get; set; } = null!;
|
||||
public Account.Account Account { get; set; } = null!;
|
||||
public SnAccount Account { get; set; } = null!;
|
||||
public Instant SeenAt { get; set; }
|
||||
}
|
||||
|
||||
|
@@ -7,9 +7,9 @@ namespace DysonNetwork.Pass.Leveling;
|
||||
|
||||
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,
|
||||
Reason = reason,
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using DysonNetwork.Pass.Account;
|
||||
using System.Text.Json;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using NetTopologySuite.Geometries;
|
||||
@@ -157,7 +154,7 @@ namespace DysonNetwork.Pass.Migrations
|
||||
level = table.Column<int>(type: "integer", nullable: false),
|
||||
reward_points = table.Column<decimal>(type: "numeric", 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),
|
||||
created_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
|
||||
updated_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
|
||||
@@ -242,7 +239,7 @@ namespace DysonNetwork.Pass.Migrations
|
||||
birthday = 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<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),
|
||||
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),
|
||||
@@ -593,7 +590,7 @@ namespace DysonNetwork.Pass.Migrations
|
||||
is_free_trial = table.Column<bool>(type: "boolean", nullable: false),
|
||||
status = table.Column<int>(type: "integer", 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),
|
||||
coupon_id = table.Column<Guid>(type: "uuid", 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
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using NodaTime;
|
||||
|
||||
#nullable disable
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using NodaTime;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using NodaTime;
|
||||
|
||||
#nullable disable
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using NodaTime;
|
||||
|
||||
#nullable disable
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using NodaTime;
|
||||
|
||||
#nullable disable
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
|
@@ -6,6 +6,7 @@ using DysonNetwork.Pass;
|
||||
using DysonNetwork.Pass.Account;
|
||||
using DysonNetwork.Shared.GeoIp;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
@@ -395,7 +396,7 @@ namespace DysonNetwork.Pass.Migrations
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("account_id");
|
||||
|
||||
b.Property<BadgeReferenceObject>("ActiveBadge")
|
||||
b.Property<SnAccountBadge>("ActiveBadge")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("active_badge");
|
||||
|
||||
@@ -589,7 +590,7 @@ namespace DysonNetwork.Pass.Migrations
|
||||
.HasColumnType("numeric")
|
||||
.HasColumnName("reward_points");
|
||||
|
||||
b.Property<ICollection<FortuneTip>>("Tips")
|
||||
b.Property<ICollection<CheckInFortuneTip>>("Tips")
|
||||
.IsRequired()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("tips");
|
||||
@@ -1481,7 +1482,7 @@ namespace DysonNetwork.Pass.Migrations
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_free_trial");
|
||||
|
||||
b.Property<PaymentDetails>("PaymentDetails")
|
||||
b.Property<SnPaymentDetails>("PaymentDetails")
|
||||
.IsRequired()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("payment_details");
|
||||
|
@@ -1,6 +1,7 @@
|
||||
namespace DysonNetwork.Pass.Permission;
|
||||
|
||||
using System;
|
||||
using DysonNetwork.Shared.Models;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class RequiredPermissionAttribute(string area, string key) : Attribute
|
||||
@@ -21,7 +22,7 @@ public class PermissionMiddleware(RequestDelegate next)
|
||||
|
||||
if (attr != null)
|
||||
{
|
||||
if (httpContext.Items["CurrentUser"] is not Account.Account currentUser)
|
||||
if (httpContext.Items["CurrentUser"] is not SnAccount currentUser)
|
||||
{
|
||||
httpContext.Response.StatusCode = StatusCodes.Status403Forbidden;
|
||||
await httpContext.Response.WriteAsync("Unauthorized");
|
||||
|
@@ -1,7 +1,6 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NodaTime;
|
||||
using System.Text.Json;
|
||||
using DysonNetwork.Pass;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using DysonNetwork.Shared.Models;
|
||||
|
||||
|
@@ -4,7 +4,6 @@ using DysonNetwork.Shared.Proto;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using System.Text.Json;
|
||||
using NodaTime.Serialization.Protobuf;
|
||||
using DysonNetwork.Shared.Models;
|
||||
|
||||
namespace DysonNetwork.Pass.Permission;
|
||||
|
||||
@@ -76,22 +75,3 @@ public class PermissionServiceGrpc(
|
||||
return new RemovePermissionNodeFromGroupResponse { Success = true };
|
||||
}
|
||||
}
|
||||
|
||||
public static class PermissionExtensions
|
||||
{
|
||||
public static DysonNetwork.Shared.Proto.PermissionNode ToProtoValue(this PermissionNode node)
|
||||
{
|
||||
return new DysonNetwork.Shared.Proto.PermissionNode
|
||||
{
|
||||
Id = node.Id.ToString(),
|
||||
Actor = node.Actor,
|
||||
Area = node.Area,
|
||||
Key = node.Key,
|
||||
Value = Value.Parser.ParseJson(node.Value.RootElement.GetRawText()),
|
||||
ExpiredAt = node.ExpiredAt?.ToTimestamp(),
|
||||
AffectedAt = node.AffectedAt?.ToTimestamp(),
|
||||
GroupId = node.GroupId?.ToString() ?? string.Empty
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using DysonNetwork.Pass.Account;
|
||||
using DysonNetwork.Pass.Permission;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace DysonNetwork.Pass.Safety;
|
||||
|
||||
[ApiController]
|
||||
[Route("/api/safety/reports")]
|
||||
public class AbuseReportController(
|
||||
public class SnAbuseReportController(
|
||||
SafetyService safety
|
||||
) : ControllerBase
|
||||
{
|
||||
@@ -26,11 +26,11 @@ public class AbuseReportController(
|
||||
|
||||
[HttpPost("")]
|
||||
[Authorize]
|
||||
[ProducesResponseType<AbuseReport>(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType<SnAbuseReport>(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
public async Task<ActionResult<AbuseReport>> CreateReport([FromBody] CreateReportRequest request)
|
||||
public async Task<ActionResult<SnAbuseReport>> CreateReport([FromBody] CreateReportRequest request)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -52,8 +52,8 @@ public class AbuseReportController(
|
||||
[HttpGet("")]
|
||||
[Authorize]
|
||||
[RequiredPermission("safety", "reports.view")]
|
||||
[ProducesResponseType<List<AbuseReport>>(StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<List<AbuseReport>>> GetReports(
|
||||
[ProducesResponseType<List<SnAbuseReport>>(StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<List<SnAbuseReport>>> GetReports(
|
||||
[FromQuery] int offset = 0,
|
||||
[FromQuery] int take = 20,
|
||||
[FromQuery] bool includeResolved = false
|
||||
@@ -67,15 +67,15 @@ public class AbuseReportController(
|
||||
|
||||
[HttpGet("me")]
|
||||
[Authorize]
|
||||
[ProducesResponseType<List<AbuseReport>>(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType<List<SnAbuseReport>>(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
public async Task<ActionResult<List<AbuseReport>>> GetMyReports(
|
||||
public async Task<ActionResult<List<SnAbuseReport>>> GetMyReports(
|
||||
[FromQuery] int offset = 0,
|
||||
[FromQuery] int take = 20,
|
||||
[FromQuery] bool includeResolved = false
|
||||
)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||
|
||||
var totalCount = await safety.CountUserReports(currentUser.Id, includeResolved);
|
||||
var reports = await safety.GetUserReports(currentUser.Id, offset, take, includeResolved);
|
||||
@@ -86,9 +86,9 @@ public class AbuseReportController(
|
||||
[HttpGet("{id}")]
|
||||
[Authorize]
|
||||
[RequiredPermission("safety", "reports.view")]
|
||||
[ProducesResponseType<AbuseReport>(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType<SnAbuseReport>(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<AbuseReport>> GetReportById(Guid id)
|
||||
public async Task<ActionResult<SnAbuseReport>> GetReportById(Guid id)
|
||||
{
|
||||
var report = await safety.GetReportById(id);
|
||||
return report == null ? NotFound() : Ok(report);
|
||||
@@ -96,12 +96,12 @@ public class AbuseReportController(
|
||||
|
||||
[HttpGet("me/{id}")]
|
||||
[Authorize]
|
||||
[ProducesResponseType<AbuseReport>(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType<SnAbuseReport>(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
public async Task<ActionResult<AbuseReport>> GetMyReportById(Guid id)
|
||||
public async Task<ActionResult<SnAbuseReport>> GetMyReportById(Guid id)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||
|
||||
var report = await safety.GetReportById(id);
|
||||
if (report == null) return NotFound();
|
||||
@@ -123,9 +123,9 @@ public class AbuseReportController(
|
||||
[HttpPost("{id}/resolve")]
|
||||
[Authorize]
|
||||
[RequiredPermission("safety", "reports.resolve")]
|
||||
[ProducesResponseType<AbuseReport>(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType<SnAbuseReport>(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<AbuseReport>> ResolveReport(Guid id, [FromBody] ResolveReportRequest request)
|
||||
public async Task<ActionResult<SnAbuseReport>> ResolveReport(Guid id, [FromBody] ResolveReportRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@@ -1,4 +1,4 @@
|
||||
using DysonNetwork.Pass.Account;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NodaTime;
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace DysonNetwork.Pass.Safety;
|
||||
|
||||
public class SafetyService(AppDatabase db, ILogger<SafetyService> logger)
|
||||
{
|
||||
public async Task<AbuseReport> CreateReport(string resourceIdentifier, AbuseReportType type, string reason, Guid accountId)
|
||||
public async Task<SnAbuseReport> CreateReport(string resourceIdentifier, AbuseReportType type, string reason, Guid accountId)
|
||||
{
|
||||
// Check if a similar report already exists from this user
|
||||
var existingReport = await db.AbuseReports
|
||||
@@ -20,7 +20,7 @@ public class SafetyService(AppDatabase db, ILogger<SafetyService> logger)
|
||||
throw new InvalidOperationException("You have already reported this content.");
|
||||
}
|
||||
|
||||
var report = new AbuseReport
|
||||
var report = new SnAbuseReport
|
||||
{
|
||||
ResourceIdentifier = resourceIdentifier,
|
||||
Type = type,
|
||||
@@ -52,7 +52,7 @@ public class SafetyService(AppDatabase db, ILogger<SafetyService> logger)
|
||||
.CountAsync();
|
||||
}
|
||||
|
||||
public async Task<List<AbuseReport>> GetReports(int offset = 0, int take = 20, bool includeResolved = false)
|
||||
public async Task<List<SnAbuseReport>> GetReports(int offset = 0, int take = 20, bool includeResolved = false)
|
||||
{
|
||||
return await db.AbuseReports
|
||||
.Where(r => includeResolved || r.ResolvedAt == null)
|
||||
@@ -63,7 +63,7 @@ public class SafetyService(AppDatabase db, ILogger<SafetyService> logger)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<List<AbuseReport>> GetUserReports(Guid accountId, int offset = 0, int take = 20, bool includeResolved = false)
|
||||
public async Task<List<SnAbuseReport>> GetUserReports(Guid accountId, int offset = 0, int take = 20, bool includeResolved = false)
|
||||
{
|
||||
return await db.AbuseReports
|
||||
.Where(r => r.AccountId == accountId)
|
||||
@@ -74,21 +74,16 @@ public class SafetyService(AppDatabase db, ILogger<SafetyService> logger)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<AbuseReport?> GetReportById(Guid id)
|
||||
public async Task<SnAbuseReport?> GetReportById(Guid id)
|
||||
{
|
||||
return await db.AbuseReports
|
||||
.Include(r => r.Account)
|
||||
.FirstOrDefaultAsync(r => r.Id == id);
|
||||
}
|
||||
|
||||
public async Task<AbuseReport> ResolveReport(Guid id, string resolution)
|
||||
public async Task<SnAbuseReport> ResolveReport(Guid id, string resolution)
|
||||
{
|
||||
var report = await db.AbuseReports.FindAsync(id);
|
||||
if (report == null)
|
||||
{
|
||||
throw new KeyNotFoundException("Report not found");
|
||||
}
|
||||
|
||||
var report = await db.AbuseReports.FindAsync(id) ?? throw new KeyNotFoundException("Report not found");
|
||||
report.ResolvedAt = SystemClock.Instance.GetCurrentInstant();
|
||||
report.Resolution = resolution;
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user