fix: align backend APIs and upload flow
This commit is contained in:
@@ -4,6 +4,7 @@ using IdentityService.WebApi.Applications.Dtos;
|
||||
using IdentityService.WebApi.Applications.Dtos.Common;
|
||||
using IM.Commons;
|
||||
using IM.Jwt;
|
||||
using IM.InitCommon.Management;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Security.Claims;
|
||||
|
||||
@@ -16,10 +17,11 @@ namespace IdentityService.WebApi.Applications.Auth
|
||||
private readonly IOptions<JwtOptions> jwtOptions;
|
||||
private readonly IdDomainService idDomainService;
|
||||
private readonly IMapper mapper;
|
||||
private readonly RuntimePolicy runtime;
|
||||
|
||||
public AuthService(ITokenService tokenService, IIdRepository idRepository,
|
||||
IOptions<JwtOptions> options, IdDomainService idDomainService,
|
||||
IMapper mapper
|
||||
IMapper mapper, RuntimePolicy runtime
|
||||
)
|
||||
{
|
||||
this.tokenService = tokenService;
|
||||
@@ -27,6 +29,7 @@ namespace IdentityService.WebApi.Applications.Auth
|
||||
jwtOptions = options;
|
||||
this.idDomainService = idDomainService;
|
||||
this.mapper = mapper;
|
||||
this.runtime = runtime;
|
||||
}
|
||||
|
||||
public async Task<Result<LoginResponse>> LoginAsync(string username, string password)
|
||||
@@ -37,17 +40,20 @@ namespace IdentityService.WebApi.Applications.Auth
|
||||
{
|
||||
return Result<LoginResponse>.Fail(ResultCode.USER_NOT_FOUND);
|
||||
}
|
||||
if (user.Status != UserState.Normal || user.IsDeleted) return Result<LoginResponse>.Fail(ResultCode.AUTH_FAILED);
|
||||
var idResult = await idRepository.CheckForSignInAsync(user, password, true);
|
||||
if (!idResult.Succeeded)
|
||||
{
|
||||
return Result<LoginResponse>.Fail(ResultCode.PASSWORD_ERROR);
|
||||
}
|
||||
var token = await BuildTokenAsync(user);
|
||||
var refreshToken = await tokenService.CreateRefreshTokenAsync(user.Id);
|
||||
var refreshToken = await tokenService.CreateRefreshTokenAsync(user.Id, stamp: user.SecurityStamp, days: runtime.Current.ClientRefreshDays);
|
||||
return Result<LoginResponse>.Success(new LoginResponse(user.Id, token, refreshToken, null, user.UserName, user.NickName,user.Avatar, user.CreationTime));
|
||||
}
|
||||
public async Task<Result<UserResponse?>> RegisterAsync(string userName, string password, string nickName)
|
||||
{
|
||||
if (!runtime.Current.RegistrationEnabled) return Result<UserResponse?>.Fail(ResultCode.PERMISSION_DENIED, "平台暂未开放注册");
|
||||
if (password.Length < runtime.Current.PasswordMinLength) return Result<UserResponse?>.Fail(ResultCode.PARAMETER_ERROR, "密码不符合当前最小长度要求");
|
||||
var userResult = await idDomainService.CreateUserAsync(userName, password, nickName);
|
||||
if (!userResult.Succeeded)
|
||||
return Result<UserResponse?>.Fail(userResult);
|
||||
@@ -78,20 +84,24 @@ namespace IdentityService.WebApi.Applications.Auth
|
||||
return Result<LoginResponse>.Fail(ResultCode.USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (user.Status != UserState.Normal || user.IsDeleted || validateRes.stamp != user.SecurityStamp) return Result<LoginResponse>.Fail(ResultCode.AUTH_FAILED);
|
||||
var token = await BuildTokenAsync(user);
|
||||
|
||||
return Result<LoginResponse>.Success(new LoginResponse(user.Id, token, refreshToken, null, user.UserName, user.NickName, user.Avatar, user.CreationTime));
|
||||
await tokenService.RevokeRefreshTokenAsync(refreshToken);
|
||||
var nextRefresh = await tokenService.CreateRefreshTokenAsync(user.Id, stamp: user.SecurityStamp, days: runtime.Current.ClientRefreshDays);
|
||||
return Result<LoginResponse>.Success(new LoginResponse(user.Id, token, nextRefresh, null, user.UserName, user.NickName, user.Avatar, user.CreationTime));
|
||||
}
|
||||
private async Task<string> BuildTokenAsync(Domain.Entities.User user)
|
||||
{
|
||||
var roles = await idRepository.GetRolesAsync(user);
|
||||
List<Claim> claims = new List<Claim>();
|
||||
claims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
|
||||
claims.Add(new Claim("session_stamp", user.SecurityStamp ?? ""));
|
||||
foreach (string role in roles)
|
||||
{
|
||||
claims.Add(new Claim(ClaimTypes.Role, role));
|
||||
}
|
||||
return tokenService.GetToken(claims, jwtOptions.Value);
|
||||
var original = jwtOptions.Value;
|
||||
return tokenService.GetToken(claims, new JwtOptions { Key = original.Key, Issuer = original.Issuer, Audience = original.Audience, RefreshTokenDays = original.RefreshTokenDays, AccessTokenMinutes = runtime.Current.ClientAccessMinutes > 0 ? runtime.Current.ClientAccessMinutes : original.AccessTokenMinutes });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
using IdentityService.Infrastructure;
|
||||
using IM.InitCommon.Management;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace IdentityService.WebApi.Controllers;
|
||||
[ApiController, Route("internal/management")]
|
||||
public sealed class ManagementController(UserDbContext db, InternalClient client) : ControllerBase
|
||||
{
|
||||
[HttpGet("summary")] public async Task<object> Summary() => new { total = await db.Users.CountAsync() };
|
||||
[HttpGet("access/{id:guid}")] public async Task<UserAccess> Access(Guid id) { var u = await db.Users.AsNoTracking().SingleOrDefaultAsync(x => x.Id == id); return new(u is not null && u.Status == Domain.UserState.Normal && !u.IsDeleted, u?.SecurityStamp ?? ""); }
|
||||
[HttpGet("list")] public async Task<object> List(string? q, string? status, int page = 1, int size = 8)
|
||||
{
|
||||
page = Math.Max(1, page); size = Math.Clamp(size, 1, 100);
|
||||
var query = db.Users.AsNoTracking().Where(x => q == null || x.NickName.Contains(q) || x.UserName!.Contains(q) || x.Id.ToString() == q);
|
||||
if (!string.IsNullOrEmpty(status)) { var state = status == "封禁" ? Domain.UserState.Banned : status == "正常" ? Domain.UserState.Normal : Domain.UserState.Inactive; query = query.Where(x => x.Status == state); }
|
||||
return new { items = await query.OrderByDescending(x => x.CreationTime).Skip((page - 1) * size).Take(size).Select(x => new { x.Id, name = x.NickName, account = x.UserName, x.Region, status = x.Status == Domain.UserState.Banned ? "封禁" : x.Status == Domain.UserState.Normal ? "正常" : "未激活", createdAt = x.CreationTime }).ToListAsync(), total = await query.CountAsync(), page, size };
|
||||
}
|
||||
[HttpGet("detail/{id:guid}")] public async Task<IActionResult> Detail(Guid id)
|
||||
{
|
||||
var u = await db.Users.AsNoTracking().SingleOrDefaultAsync(x => x.Id == id); if (u is null) return NotFound();
|
||||
var groups = await client.Send<System.Text.Json.JsonElement>("group", $"/internal/management/user/{id}/groups");
|
||||
return Ok(new { u.Id, name = u.NickName, account = u.UserName, u.Region, u.Description, createdAt = u.CreationTime, status = u.Status == Domain.UserState.Banned ? "封禁" : u.Status == Domain.UserState.Normal ? "正常" : "未激活", groups });
|
||||
}
|
||||
[HttpPost("action")] public async Task<IActionResult> Action(InternalAction command, CancellationToken ct)
|
||||
{
|
||||
if (command.Action is not "封禁" and not "解封") return BadRequest();
|
||||
var receipt = await ReceiptStore.Execute(db, command, async () => {
|
||||
var u = await db.Users.SingleOrDefaultAsync(x => x.Id == command.TargetId, ct) ?? throw new InvalidOperationException("用户不存在");
|
||||
var before = u.Status == Domain.UserState.Banned ? "封禁" : "正常";
|
||||
if (command.Action == "封禁") u.Ban(command.Reason); else u.Unban();
|
||||
u.SecurityStamp = Guid.NewGuid().ToString("N");
|
||||
return new ActionReceipt(u.NickName, before, command.Action == "封禁" ? "封禁" : "正常");
|
||||
}, ct);
|
||||
if (command.Action == "封禁") await client.Send<object>("connector", $"/internal/management/disconnect/{command.TargetId}", new { }, ct);
|
||||
return Ok(receipt);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
|
||||
using IM.InitCommon.Management;
|
||||
|
||||
using IdentityService.Domain.Entities;
|
||||
using IdentityService.Infrastructure;
|
||||
using IM.InitCommon;
|
||||
@@ -38,6 +39,7 @@ namespace IdentityService.WebApi
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
var app = builder.Build();
|
||||
if (app.ApplyMigrationsIfRequested(args)) return;
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
@@ -47,6 +49,7 @@ namespace IdentityService.WebApi
|
||||
}
|
||||
|
||||
app.UseAppDefault();
|
||||
app.MapManagementHealth();
|
||||
|
||||
app.MapControllers();
|
||||
app.MapAllGrpcServer();
|
||||
|
||||
Reference in New Issue
Block a user