refactor: 统一命名空间为Foxel.Api,移除不必要的using语句,优化代码结构

This commit is contained in:
shiyu
2025-06-22 17:07:43 +08:00
parent 9612a69f9d
commit 49c84658b5
50 changed files with 112 additions and 210 deletions
+3 -3
View File
@@ -1,11 +1,11 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Foxel.Models; using Foxel.Models;
using Foxel.Models.Request.Album; using Foxel.Models.Request.Album;
using Foxel.Models.Response.Album; using Foxel.Models.Response.Album;
using Foxel.Services.Media; using Foxel.Services.Media;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Foxel.Controllers; namespace Foxel.Api;
[Authorize] [Authorize]
[Route("api/album")] [Route("api/album")]
+15 -15
View File
@@ -1,12 +1,12 @@
using Foxel.Models; using Foxel.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Foxel.Models.Request.Auth; using Foxel.Models.Request.Auth;
using Foxel.Models.Response.Auth; using Foxel.Models.Response.Auth;
using Foxel.Services.Auth; using Foxel.Services.Auth;
using Foxel.Services.Configuration; using Foxel.Services.Configuration;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Foxel.Controllers; namespace Foxel.Api;
[Route("api/auth")] [Route("api/auth")]
public class AuthController(IAuthService authService, IConfigService configuration) : BaseApiController public class AuthController(IAuthService authService, IConfigService configuration) : BaseApiController
@@ -119,15 +119,15 @@ public class AuthController(IAuthService authService, IConfigService configurati
switch (result) switch (result)
{ {
case GitHubAuthResult.Success: case OAuthResult.Success:
return Redirect($"/login?token={Uri.EscapeDataString(data!)}"); return Redirect($"/login?token={Uri.EscapeDataString(data!)}");
case GitHubAuthResult.UserNotBound: case OAuthResult.UserNotBound:
return Redirect($"/bind?githubId={data}"); return Redirect($"/bind?githubId={data}");
case GitHubAuthResult.InvalidCode: case OAuthResult.InvalidCode:
case GitHubAuthResult.TokenRequestFailed: case OAuthResult.TokenRequestFailed:
case GitHubAuthResult.UserInfoFailed: case OAuthResult.UserInfoFailed:
case GitHubAuthResult.InvalidUserId: case OAuthResult.InvalidUserId:
default: default:
return Redirect($"/login?error=github_auth_failed&message={Uri.EscapeDataString(message)}"); return Redirect($"/login?error=github_auth_failed&message={Uri.EscapeDataString(message)}");
} }
@@ -147,15 +147,15 @@ public class AuthController(IAuthService authService, IConfigService configurati
switch (result) switch (result)
{ {
case LinuxDoAuthResult.Success: case OAuthResult.Success:
return Redirect($"/login?token={Uri.EscapeDataString(data!)}"); return Redirect($"/login?token={Uri.EscapeDataString(data!)}");
case LinuxDoAuthResult.UserNotBound: case OAuthResult.UserNotBound:
return Redirect($"/bind?linuxdoId={data}"); return Redirect($"/bind?linuxdoId={data}");
case LinuxDoAuthResult.InvalidCode: case OAuthResult.InvalidCode:
case LinuxDoAuthResult.TokenRequestFailed: case OAuthResult.TokenRequestFailed:
case LinuxDoAuthResult.UserInfoFailed: case OAuthResult.UserInfoFailed:
case LinuxDoAuthResult.InvalidUserId: case OAuthResult.InvalidUserId:
default: default:
return Redirect($"/login?error=linuxdo_auth_failed&message={Uri.EscapeDataString(message)}"); return Redirect($"/login?error=linuxdo_auth_failed&message={Uri.EscapeDataString(message)}");
} }
+3 -3
View File
@@ -1,9 +1,9 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Foxel.Models; using Foxel.Models;
using Foxel.Services.Background; using Foxel.Services.Background;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Foxel.Controllers; namespace Foxel.Api;
[Authorize] [Authorize]
[Route("api/background-tasks")] [Route("api/background-tasks")]
+4 -6
View File
@@ -1,11 +1,9 @@
using Microsoft.AspNetCore.Mvc;
using Foxel.Models;
using System.Security.Claims;
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens; using System.Security.Claims;
using System.Text; using Foxel.Models;
using Microsoft.AspNetCore.Mvc;
namespace Foxel.Controllers namespace Foxel.Api
{ {
[ApiController] [ApiController]
[Route("api/[controller]")] [Route("api/[controller]")]
+3 -3
View File
@@ -1,11 +1,11 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Foxel.Models; using Foxel.Models;
using Foxel.Models.DataBase; using Foxel.Models.DataBase;
using Foxel.Models.Request.Config; using Foxel.Models.Request.Config;
using Foxel.Services.Configuration; using Foxel.Services.Configuration;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Foxel.Controllers; namespace Foxel.Api;
[Authorize(Roles = "Administrator")] [Authorize(Roles = "Administrator")]
[Route("api/config")] [Route("api/config")]
+1 -1
View File
@@ -1,7 +1,7 @@
using Foxel.Controllers;
using Foxel.Models; using Foxel.Models;
using Foxel.Models.Response.Face; using Foxel.Models.Response.Face;
using Foxel.Models.Response.Picture; using Foxel.Models.Response.Picture;
using Foxel.Services.AI;
using Foxel.Services.Management; using Foxel.Services.Management;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@@ -1,4 +1,3 @@
using Foxel.Controllers;
using Foxel.Models; using Foxel.Models;
using Foxel.Models.Request.Album; using Foxel.Models.Request.Album;
using Foxel.Models.Response.Album; using Foxel.Models.Response.Album;
+1 -1
View File
@@ -1,7 +1,7 @@
using Foxel.Controllers;
using Foxel.Models; using Foxel.Models;
using Foxel.Models.Response.Face; using Foxel.Models.Response.Face;
using Foxel.Models.Response.Picture; using Foxel.Models.Response.Picture;
using Foxel.Services.AI;
using Foxel.Services.Management; using Foxel.Services.Management;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@@ -3,7 +3,6 @@ using Microsoft.AspNetCore.Mvc;
using Foxel.Models; using Foxel.Models;
using Foxel.Models.Response.Log; using Foxel.Models.Response.Log;
using Foxel.Models.Request.Log; using Foxel.Models.Request.Log;
using Foxel.Controllers;
using Foxel.Services.Management; using Foxel.Services.Management;
namespace Foxel.Api.Management; namespace Foxel.Api.Management;
@@ -2,7 +2,6 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Foxel.Models; using Foxel.Models;
using Foxel.Models.Response.Picture; using Foxel.Models.Response.Picture;
using Foxel.Controllers;
using Foxel.Services.Management; using Foxel.Services.Management;
namespace Foxel.Api.Management; namespace Foxel.Api.Management;
@@ -1,9 +1,8 @@
using Foxel.Controllers;
using Foxel.Models; using Foxel.Models;
using Foxel.Models.Request.Storage; using Foxel.Models.Request.Storage;
using Foxel.Models.Response.Storage; using Foxel.Models.Response.Storage;
using Foxel.Services.Attributes;
using Foxel.Services.Management; using Foxel.Services.Management;
using Foxel.Services.Storage;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
+1 -2
View File
@@ -1,8 +1,7 @@
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Foxel.Controllers;
using Foxel.Models; using Foxel.Models;
using Foxel.Services.VectorDB; using Foxel.Services.VectorDb;
namespace Foxel.Api.Management; namespace Foxel.Api.Management;
@@ -3,7 +3,6 @@ using Microsoft.AspNetCore.Mvc;
using Foxel.Models; using Foxel.Models;
using Foxel.Models.Request.User; using Foxel.Models.Request.User;
using Foxel.Models.Response.User; using Foxel.Models.Response.User;
using Foxel.Controllers;
using Foxel.Services.Management; using Foxel.Services.Management;
namespace Foxel.Api.Management; namespace Foxel.Api.Management;
-1
View File
@@ -1,4 +1,3 @@
using Foxel.Controllers;
using Foxel.Models; using Foxel.Models;
using Foxel.Models.DataBase; using Foxel.Models.DataBase;
using Foxel.Models.Request.Picture; using Foxel.Models.Request.Picture;
+3 -3
View File
@@ -1,11 +1,11 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Foxel.Models; using Foxel.Models;
using Foxel.Models.Request.Tag; using Foxel.Models.Request.Tag;
using Foxel.Models.Response.Tag; using Foxel.Models.Response.Tag;
using Foxel.Services.Media; using Foxel.Services.Media;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Foxel.Controllers; namespace Foxel.Api;
[Route("api/tag")] [Route("api/tag")]
public class TagController(ITagService tagService) : BaseApiController public class TagController(ITagService tagService) : BaseApiController
+1 -1
View File
@@ -12,9 +12,9 @@ using Foxel.Services.Initializer;
using Foxel.Services.Management; using Foxel.Services.Management;
using Foxel.Services.Media; using Foxel.Services.Media;
using Foxel.Services.Storage; using Foxel.Services.Storage;
using Foxel.Services.VectorDB;
using Foxel.Services.Background.Processors; using Foxel.Services.Background.Processors;
using Foxel.Services.Mapping; using Foxel.Services.Mapping;
using Foxel.Services.VectorDb;
namespace Foxel.Extensions; namespace Foxel.Extensions;
-1
View File
@@ -1,7 +1,6 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json; using System.Text.Json;
using Foxel.Services.Attributes;
namespace Foxel.Models.DataBase; namespace Foxel.Models.DataBase;
+1 -1
View File
@@ -1,6 +1,6 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using Foxel.Services.Attributes; using Foxel.Services.Storage;
namespace Foxel.Models.DataBase; namespace Foxel.Models.DataBase;
+3 -8
View File
@@ -2,12 +2,7 @@ namespace Foxel.Models;
public record PaginatedResult<T> : BaseResult<List<T>> public record PaginatedResult<T> : BaseResult<List<T>>
{ {
public int Page { get; init; } = 1;
public int PageSize { get; init; } = 10;
public int Page { get; set; } = 1; public int TotalCount { get; init; }
public int PageSize { get; set; } = 10;
public int TotalCount { get; set; }
public int TotalPages => (int)Math.Ceiling(TotalCount / (double)PageSize);
public bool HasPreviousPage => Page > 1;
public bool HasNextPage => Page < TotalPages;
} }
@@ -1,5 +1,7 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using Foxel.Services.Attributes; // For StorageType enum using Foxel.Services.Storage;
// For StorageType enum
namespace Foxel.Models.Request.Storage; namespace Foxel.Models.Request.Storage;
@@ -1,5 +1,7 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using Foxel.Services.Attributes; // For StorageType enum using Foxel.Services.Storage;
// For StorageType enum
namespace Foxel.Models.Request.Storage; namespace Foxel.Models.Request.Storage;
@@ -1,4 +1,6 @@
using Foxel.Services.Attributes; // For StorageType enum using Foxel.Services.Storage;
// For StorageType enum
namespace Foxel.Models.Response.Storage; namespace Foxel.Models.Response.Storage;
+1 -1
View File
@@ -1,6 +1,6 @@
using Foxel.Extensions; using Foxel.Extensions;
using Foxel.Services.Initializer; using Foxel.Services.Initializer;
using Foxel.Services.VectorDB; using Foxel.Services.VectorDb;
using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.HttpOverrides;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
+2
View File
@@ -1,5 +1,7 @@
using Foxel.Models.DataBase; using Foxel.Models.DataBase;
namespace Foxel.Services.AI;
public interface IFaceClusteringService public interface IFaceClusteringService
{ {
/// <summary> /// <summary>
+21 -18
View File
@@ -10,7 +10,10 @@ using static Foxel.Utils.AuthHelper;
namespace Foxel.Services.Auth; namespace Foxel.Services.Auth;
public class AuthService(IDbContextFactory<MyDbContext> dbContextFactory, IConfigService configuration, ILogger<AuthService> logger) public class AuthService(
IDbContextFactory<MyDbContext> dbContextFactory,
IConfigService configuration,
ILogger<AuthService> logger)
: IAuthService : IAuthService
{ {
public async Task<(bool success, string message, User? user)> RegisterUserAsync(RegisterRequest request) public async Task<(bool success, string message, User? user)> RegisterUserAsync(RegisterRequest request)
@@ -196,11 +199,11 @@ public class AuthService(IDbContextFactory<MyDbContext> dbContextFactory, IConfi
$"https://github.com/login/oauth/authorize?client_id={Uri.EscapeDataString(githubClientId)}&redirect_uri={Uri.EscapeDataString(githubCallback)}"; $"https://github.com/login/oauth/authorize?client_id={Uri.EscapeDataString(githubClientId)}&redirect_uri={Uri.EscapeDataString(githubCallback)}";
} }
public async Task<(GitHubAuthResult result, string message, string? data)> ProcessGitHubCallbackAsync(string code) public async Task<(OAuthResult result, string message, string? data)> ProcessGitHubCallbackAsync(string code)
{ {
if (string.IsNullOrEmpty(code)) if (string.IsNullOrEmpty(code))
{ {
return (GitHubAuthResult.InvalidCode, "GitHub授权码无效", null); return (OAuthResult.InvalidCode, "GitHub授权码无效", null);
} }
string githubClientId = configuration["Authentication:GitHubClientId"]; string githubClientId = configuration["Authentication:GitHubClientId"];
@@ -219,7 +222,7 @@ public class AuthService(IDbContextFactory<MyDbContext> dbContextFactory, IConfi
{ {
var errorContent = await tokenResponse.Content.ReadAsStringAsync(); var errorContent = await tokenResponse.Content.ReadAsStringAsync();
logger.LogError("获取GitHub访问令牌失败: {StatusCode}, {ErrorContent}", tokenResponse.StatusCode, errorContent); logger.LogError("获取GitHub访问令牌失败: {StatusCode}, {ErrorContent}", tokenResponse.StatusCode, errorContent);
return (GitHubAuthResult.TokenRequestFailed, $"获取GitHub访问令牌失败: {errorContent}", null); return (OAuthResult.TokenRequestFailed, $"获取GitHub访问令牌失败: {errorContent}", null);
} }
var tokenResponseContent = await tokenResponse.Content.ReadAsStringAsync(); var tokenResponseContent = await tokenResponse.Content.ReadAsStringAsync();
@@ -229,7 +232,7 @@ public class AuthService(IDbContextFactory<MyDbContext> dbContextFactory, IConfi
accessTokenElement.GetString() == null) accessTokenElement.GetString() == null)
{ {
logger.LogError("GitHub响应中未找到access_token: {TokenResponseContent}", tokenResponseContent); logger.LogError("GitHub响应中未找到access_token: {TokenResponseContent}", tokenResponseContent);
return (GitHubAuthResult.TokenRequestFailed, "获取GitHub访问令牌失败,响应中未包含令牌。", null); return (OAuthResult.TokenRequestFailed, "获取GitHub访问令牌失败,响应中未包含令牌。", null);
} }
var accessToken = accessTokenElement.GetString(); var accessToken = accessTokenElement.GetString();
@@ -242,7 +245,7 @@ public class AuthService(IDbContextFactory<MyDbContext> dbContextFactory, IConfi
{ {
var errorContent = await userResponse.Content.ReadAsStringAsync(); var errorContent = await userResponse.Content.ReadAsStringAsync();
logger.LogError("获取GitHub用户信息失败: {StatusCode}, {ErrorContent}", userResponse.StatusCode, errorContent); logger.LogError("获取GitHub用户信息失败: {StatusCode}, {ErrorContent}", userResponse.StatusCode, errorContent);
return (GitHubAuthResult.UserInfoFailed, $"获取GitHub用户信息失败: {errorContent}", null); return (OAuthResult.UserInfoFailed, $"获取GitHub用户信息失败: {errorContent}", null);
} }
var userContent = await userResponse.Content.ReadAsStringAsync(); var userContent = await userResponse.Content.ReadAsStringAsync();
@@ -275,34 +278,34 @@ public class AuthService(IDbContextFactory<MyDbContext> dbContextFactory, IConfi
if (string.IsNullOrEmpty(githubUserId)) if (string.IsNullOrEmpty(githubUserId))
{ {
return (GitHubAuthResult.InvalidUserId, "无法从GitHub获取用户ID", null); return (OAuthResult.InvalidUserId, "无法从GitHub获取用户ID", null);
} }
var (isSuccess, message, user) = await FindGitHubUserAsync(githubUserId); var (isSuccess, message, user) = await FindGitHubUserAsync(githubUserId);
if (!isSuccess || user == null) if (!isSuccess || user == null)
{ {
return (GitHubAuthResult.UserNotBound, "GitHub用户未绑定到系统账户", githubUserId); return (OAuthResult.UserNotBound, "GitHub用户未绑定到系统账户", githubUserId);
} }
var jwtToken = await GenerateJwtTokenAsync(user); var jwtToken = await GenerateJwtTokenAsync(user);
return (GitHubAuthResult.Success, "GitHub授权成功", jwtToken); return (OAuthResult.Success, "GitHub授权成功", jwtToken);
} }
public string GetLinuxDoLoginUrl() public string GetLinuxDoLoginUrl()
{ {
string linuxdoClientId = configuration["Authentication:LinuxDoClientId"]; string linuxdoClientId = configuration["Authentication:LinuxDoClientId"];
string linuxdoCallback = configuration["Authentication:LinuxDoCallbackUrl"]; string linuxdoCallback = configuration["Authentication:LinuxDoCallbackUrl"];
string state = Guid.NewGuid().ToString(); string state = Guid.NewGuid().ToString();
return return
$"https://connect.linux.do/oauth2/authorize?response_type=code&client_id={Uri.EscapeDataString(linuxdoClientId)}&redirect_uri={Uri.EscapeDataString(linuxdoCallback)}&state={Uri.EscapeDataString(state)}"; $"https://connect.linux.do/oauth2/authorize?response_type=code&client_id={Uri.EscapeDataString(linuxdoClientId)}&redirect_uri={Uri.EscapeDataString(linuxdoCallback)}&state={Uri.EscapeDataString(state)}";
} }
public async Task<(LinuxDoAuthResult result, string message, string? data)> ProcessLinuxDoCallbackAsync(string code) public async Task<(OAuthResult result, string message, string? data)> ProcessLinuxDoCallbackAsync(string code)
{ {
if (string.IsNullOrEmpty(code)) if (string.IsNullOrEmpty(code))
{ {
return (LinuxDoAuthResult.InvalidCode, "LinuxDo授权码无效", null); return (OAuthResult.InvalidCode, "LinuxDo授权码无效", null);
} }
string linuxdoClientId = configuration["Authentication:LinuxDoClientId"]; string linuxdoClientId = configuration["Authentication:LinuxDoClientId"];
@@ -331,7 +334,7 @@ public class AuthService(IDbContextFactory<MyDbContext> dbContextFactory, IConfi
{ {
var errorContent = await tokenResponse.Content.ReadAsStringAsync(); var errorContent = await tokenResponse.Content.ReadAsStringAsync();
logger.LogError("获取LinuxDo访问令牌失败: {StatusCode}, {ErrorContent}", tokenResponse.StatusCode, errorContent); logger.LogError("获取LinuxDo访问令牌失败: {StatusCode}, {ErrorContent}", tokenResponse.StatusCode, errorContent);
return (LinuxDoAuthResult.TokenRequestFailed, $"获取LinuxDo访问令牌失败: {errorContent}", null); return (OAuthResult.TokenRequestFailed, $"获取LinuxDo访问令牌失败: {errorContent}", null);
} }
var tokenResponseContent = await tokenResponse.Content.ReadAsStringAsync(); var tokenResponseContent = await tokenResponse.Content.ReadAsStringAsync();
@@ -341,7 +344,7 @@ public class AuthService(IDbContextFactory<MyDbContext> dbContextFactory, IConfi
accessTokenElement.GetString() == null) accessTokenElement.GetString() == null)
{ {
logger.LogError("LinuxDo响应中未找到access_token: {TokenResponseContent}", tokenResponseContent); logger.LogError("LinuxDo响应中未找到access_token: {TokenResponseContent}", tokenResponseContent);
return (LinuxDoAuthResult.TokenRequestFailed, "获取LinuxDo访问令牌失败,响应中未包含令牌。", null); return (OAuthResult.TokenRequestFailed, "获取LinuxDo访问令牌失败,响应中未包含令牌。", null);
} }
var accessToken = accessTokenElement.GetString(); var accessToken = accessTokenElement.GetString();
@@ -355,7 +358,7 @@ public class AuthService(IDbContextFactory<MyDbContext> dbContextFactory, IConfi
{ {
var errorContent = await userResponse.Content.ReadAsStringAsync(); var errorContent = await userResponse.Content.ReadAsStringAsync();
logger.LogError("获取LinuxDo用户信息失败: {StatusCode}, {ErrorContent}", userResponse.StatusCode, errorContent); logger.LogError("获取LinuxDo用户信息失败: {StatusCode}, {ErrorContent}", userResponse.StatusCode, errorContent);
return (LinuxDoAuthResult.UserInfoFailed, $"获取LinuxDo用户信息失败: {errorContent}", null); return (OAuthResult.UserInfoFailed, $"获取LinuxDo用户信息失败: {errorContent}", null);
} }
var userContent = await userResponse.Content.ReadAsStringAsync(); var userContent = await userResponse.Content.ReadAsStringAsync();
@@ -382,18 +385,18 @@ public class AuthService(IDbContextFactory<MyDbContext> dbContextFactory, IConfi
if (string.IsNullOrEmpty(linuxdoUserId)) if (string.IsNullOrEmpty(linuxdoUserId))
{ {
return (LinuxDoAuthResult.InvalidUserId, "无法从LinuxDo获取用户ID", null); return (OAuthResult.InvalidUserId, "无法从LinuxDo获取用户ID", null);
} }
var (isSuccess, message, user) = await FindLinuxDoUserAsync(linuxdoUserId); var (isSuccess, message, user) = await FindLinuxDoUserAsync(linuxdoUserId);
if (!isSuccess || user == null) if (!isSuccess || user == null)
{ {
return (LinuxDoAuthResult.UserNotBound, "LinuxDo用户未绑定到系统账户", linuxdoUserId); return (OAuthResult.UserNotBound, "LinuxDo用户未绑定到系统账户", linuxdoUserId);
} }
var jwtToken = await GenerateJwtTokenAsync(user); var jwtToken = await GenerateJwtTokenAsync(user);
return (LinuxDoAuthResult.Success, "LinuxDo授权成功", jwtToken); return (OAuthResult.Success, "LinuxDo授权成功", jwtToken);
} }
public async Task<(bool success, string message, User? user)> BindAccountAsync(BindAccountRequest request) public async Task<(bool success, string message, User? user)> BindAccountAsync(BindAccountRequest request)
-11
View File
@@ -1,11 +0,0 @@
namespace Foxel.Services.Auth;
public enum GitHubAuthResult
{
Success, // 授权成功并找到绑定用户
UserNotBound, // 授权成功但用户未绑定
InvalidCode, // 授权码无效
TokenRequestFailed, // 获取访问令牌失败
UserInfoFailed, // 获取用户信息失败
InvalidUserId // 无法获取GitHub用户ID
}
+13 -3
View File
@@ -3,6 +3,16 @@ using Foxel.Models.Request.Auth;
namespace Foxel.Services.Auth; namespace Foxel.Services.Auth;
public enum OAuthResult
{
Success,
UserNotBound,
InvalidCode,
TokenRequestFailed,
UserInfoFailed,
InvalidUserId
}
public interface IAuthService public interface IAuthService
{ {
Task<(bool success, string message, User? user)> RegisterUserAsync(RegisterRequest request); Task<(bool success, string message, User? user)> RegisterUserAsync(RegisterRequest request);
@@ -14,7 +24,7 @@ public interface IAuthService
Task<(bool success, string message, User? user)> UpdateUserInfoAsync(int userId, UpdateUserRequest request); Task<(bool success, string message, User? user)> UpdateUserInfoAsync(int userId, UpdateUserRequest request);
string GetGitHubLoginUrl(); string GetGitHubLoginUrl();
string GetLinuxDoLoginUrl(); string GetLinuxDoLoginUrl();
Task<(GitHubAuthResult result, string message, string? data)> ProcessGitHubCallbackAsync(string code); Task<(OAuthResult result, string message, string? data)> ProcessGitHubCallbackAsync(string code);
Task<(LinuxDoAuthResult result, string message, string? data)> ProcessLinuxDoCallbackAsync(string code); Task<(OAuthResult result, string message, string? data)> ProcessLinuxDoCallbackAsync(string code);
Task<(bool success, string message, User? user)> BindAccountAsync(BindAccountRequest request); Task<(bool success, string message, User? user)> BindAccountAsync(BindAccountRequest request);
} }
-11
View File
@@ -1,11 +0,0 @@
namespace Foxel.Services.Auth;
public enum LinuxDoAuthResult
{
Success, // 授权成功并找到绑定用户
UserNotBound, // 授权成功但用户未绑定
InvalidCode, // 授权码无效
TokenRequestFailed, // 获取访问令牌失败
UserInfoFailed, // 获取用户信息失败
InvalidUserId // 无法获取LinuxDo用户ID
}
@@ -85,7 +85,7 @@ namespace Foxel.Services.Background.Processors
using var scope = serviceProvider.CreateScope(); using var scope = serviceProvider.CreateScope();
var storageService = scope.ServiceProvider.GetRequiredService<IStorageService>(); var storageService = scope.ServiceProvider.GetRequiredService<IStorageService>();
if (picture.StorageMode.StorageType == Attributes.StorageType.Local) if (picture.StorageMode.StorageType == StorageType.Local)
{ {
logger.LogInformation( logger.LogInformation(
"Picture {PictureId} is Local. Attempting to download via StorageService for consistency.", "Picture {PictureId} is Local. Attempting to download via StorageService for consistency.",
@@ -1,10 +1,10 @@
using Foxel.Models.DataBase; using Foxel.Models.DataBase;
using Foxel.Services.AI; using Foxel.Services.AI;
using Foxel.Services.Storage; using Foxel.Services.Storage;
using Foxel.Services.VectorDB;
using Foxel.Utils; using Foxel.Utils;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.Text.Json; using System.Text.Json;
using Foxel.Services.VectorDb;
// using Foxel.Services.Attributes; // StorageType enum might not be directly needed here anymore // using Foxel.Services.Attributes; // StorageType enum might not be directly needed here anymore
using Microsoft.Extensions.DependencyInjection; // For CreateScope using Microsoft.Extensions.DependencyInjection; // For CreateScope
using Microsoft.AspNetCore.Hosting; // For IWebHostEnvironment using Microsoft.AspNetCore.Hosting; // For IWebHostEnvironment
@@ -117,7 +117,7 @@ namespace Foxel.Services.Background.Processors
string actualThumbnailPathForAI; string actualThumbnailPathForAI;
// Check the StorageType of the associated StorageMode // Check the StorageType of the associated StorageMode
if (picture.StorageMode.StorageType == Attributes.StorageType.Local) if (picture.StorageMode.StorageType == StorageType.Local)
{ {
// As with PictureTaskProcessor, safer to use DownloadFileAsync for consistency // As with PictureTaskProcessor, safer to use DownloadFileAsync for consistency
_logger.LogInformation("Picture {PictureId} thumbnail is Local. Attempting to download via StorageService for AI.", pictureId); _logger.LogInformation("Picture {PictureId} thumbnail is Local. Attempting to download via StorageService for AI.", pictureId);
+1 -1
View File
@@ -1,7 +1,7 @@
using Foxel.Models.DataBase; using Foxel.Models.DataBase;
using Foxel.Services.Attributes;
using Foxel.Services.Configuration; using Foxel.Services.Configuration;
using Foxel.Services.Logging; using Foxel.Services.Logging;
using Foxel.Services.Storage;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace Foxel.Services.Initializer; namespace Foxel.Services.Initializer;
@@ -1,7 +1,9 @@
using Foxel.Models; // For PaginatedResult using Foxel.Models; // For PaginatedResult
using Foxel.Models.Request.Storage; using Foxel.Models.Request.Storage;
using Foxel.Models.Response.Storage; using Foxel.Models.Response.Storage;
using Foxel.Services.Attributes; // For StorageType using Foxel.Services.Storage;
// For StorageType
namespace Foxel.Services.Management; namespace Foxel.Services.Management;
@@ -2,12 +2,12 @@ using Foxel.Models;
using Foxel.Models.DataBase; using Foxel.Models.DataBase;
using Foxel.Models.Request.Storage; using Foxel.Models.Request.Storage;
using Foxel.Models.Response.Storage; using Foxel.Models.Response.Storage;
using Foxel.Services.Attributes;
using Foxel.Services.Storage.Providers; // Required for config types like LocalStorageConfig, etc. using Foxel.Services.Storage.Providers; // Required for config types like LocalStorageConfig, etc.
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Text.Json; using System.Text.Json;
using Foxel.Services.Configuration; // Added for IConfigService using Foxel.Services.Configuration;
using Foxel.Services.Storage; // Added for IConfigService
namespace Foxel.Services.Management; namespace Foxel.Services.Management;
+1 -1
View File
@@ -7,8 +7,8 @@ using Foxel.Services.AI;
using Foxel.Services.Background; using Foxel.Services.Background;
using Foxel.Services.Configuration; using Foxel.Services.Configuration;
using Foxel.Services.Storage; using Foxel.Services.Storage;
using Foxel.Services.VectorDB;
using Foxel.Services.Mapping; using Foxel.Services.Mapping;
using Foxel.Services.VectorDb;
using Foxel.Utils; using Foxel.Utils;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
+2 -4
View File
@@ -1,11 +1,9 @@
using Microsoft.EntityFrameworkCore;
using Foxel.Models; using Foxel.Models;
using Foxel.Models.DataBase; using Foxel.Models.DataBase;
using Foxel.Models.Response.Tag; using Foxel.Models.Response.Tag;
using Foxel.Services.Media; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace Foxel.Services; namespace Foxel.Services.Media;
public class TagService(IDbContextFactory<MyDbContext> contextFactory, ILogger<TagService> logger) : ITagService public class TagService(IDbContextFactory<MyDbContext> contextFactory, ILogger<TagService> logger) : ITagService
{ {
-2
View File
@@ -1,5 +1,3 @@
using Foxel.Services.Attributes;
namespace Foxel.Services.Storage; namespace Foxel.Services.Storage;
/// <summary> /// <summary>
@@ -4,7 +4,6 @@ using COSXML.CosException;
using COSXML.Model.Object; using COSXML.Model.Object;
using COSXML.Model.Tag; using COSXML.Model.Tag;
using COSXML.Transfer; using COSXML.Transfer;
using Foxel.Services.Attributes;
using Foxel.Services.Configuration; using Foxel.Services.Configuration;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@@ -1,4 +1,3 @@
using Foxel.Services.Attributes;
using Foxel.Services.Configuration; using Foxel.Services.Configuration;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@@ -1,7 +1,6 @@
using Amazon.S3; using Amazon.S3;
using Amazon.S3.Model; using Amazon.S3.Model;
using Amazon.S3.Transfer; using Amazon.S3.Transfer;
using Foxel.Services.Attributes;
using Foxel.Services.Configuration; using Foxel.Services.Configuration;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@@ -1,7 +1,6 @@
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Foxel.Services.Attributes;
using Foxel.Services.Configuration; using Foxel.Services.Configuration;
using System.Net; using System.Net;
@@ -1,6 +1,5 @@
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Text; using System.Text;
using Foxel.Services.Attributes;
using Foxel.Services.Configuration; using Foxel.Services.Configuration;
namespace Foxel.Services.Storage.Providers; namespace Foxel.Services.Storage.Providers;
+1 -3
View File
@@ -1,6 +1,4 @@
using Foxel.Models.DataBase; namespace Foxel.Services.Storage;
namespace Foxel.Services.Attributes;
public enum StorageType public enum StorageType
{ {
-1
View File
@@ -1,5 +1,4 @@
using System.Reflection; using System.Reflection;
using Foxel.Services.Attributes;
using Microsoft.EntityFrameworkCore; // For IDbContextFactory using Microsoft.EntityFrameworkCore; // For IDbContextFactory
using System.Text.Json; // For JsonSerializer using System.Text.Json; // For JsonSerializer
using Foxel.Services.Storage.Providers; // For specific config classes using Foxel.Services.Storage.Providers; // For specific config classes
+1 -1
View File
@@ -1,6 +1,6 @@
using Foxel.Models.Vector; using Foxel.Models.Vector;
namespace Foxel.Services.VectorDB; namespace Foxel.Services.VectorDb;
public interface IVectorDbService public interface IVectorDbService
{ {
+1 -1
View File
@@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.VectorData; using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel.Connectors.InMemory; using Microsoft.SemanticKernel.Connectors.InMemory;
namespace Foxel.Services.VectorDB; namespace Foxel.Services.VectorDb;
public class InMemoryVectorDbService : IVectorDbService public class InMemoryVectorDbService : IVectorDbService
{ {
+1 -1
View File
@@ -5,7 +5,7 @@ using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel.Connectors.Qdrant; using Microsoft.SemanticKernel.Connectors.Qdrant;
using Qdrant.Client; using Qdrant.Client;
namespace Foxel.Services.VectorDB; namespace Foxel.Services.VectorDb;
public class QdrantVectorDbService(IDbContextFactory<MyDbContext> contextFactory, IConfigService configService) public class QdrantVectorDbService(IDbContextFactory<MyDbContext> contextFactory, IConfigService configService)
: IVectorDbService : IVectorDbService
+1 -1
View File
@@ -1,6 +1,6 @@
using Foxel.Services.Configuration; using Foxel.Services.Configuration;
namespace Foxel.Services.VectorDB; namespace Foxel.Services.VectorDb;
public class VectorDbInitializer : IHostedService public class VectorDbInitializer : IHostedService
{ {
+1 -1
View File
@@ -2,7 +2,7 @@ using Foxel.Models.Vector;
using Foxel.Services.Configuration; using Foxel.Services.Configuration;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace Foxel.Services.VectorDB; namespace Foxel.Services.VectorDb;
public class VectorDbManager(IServiceProvider serviceProvider, IConfigService configService) public class VectorDbManager(IServiceProvider serviceProvider, IConfigService configService)
: IVectorDbService : IVectorDbService
+8 -12
View File
@@ -1,5 +1,3 @@
using Microsoft.Extensions.Logging;
namespace Foxel.Utils; namespace Foxel.Utils;
public static class AiHelper public static class AiHelper
@@ -10,17 +8,15 @@ public static class AiHelper
/// <param name="aiResponse">AI生成的响应文本</param> /// <param name="aiResponse">AI生成的响应文本</param>
/// <param name="logger">日志记录器</param> /// <param name="logger">日志记录器</param>
/// <returns>包含标题和描述的元组</returns> /// <returns>包含标题和描述的元组</returns>
public static (string title, string description) ExtractTitleAndDescription(string aiResponse, ILogger? logger = null) public static (string title, string description) ExtractTitleAndDescription(string aiResponse,
ILogger? logger = null)
{ {
string title = "AI生成的标题"; string title = "AI生成的标题";
string description = "AI生成的描述"; string description = "AI生成的描述";
try try
{ {
// 尝试解析JSON响应
if (aiResponse.Contains("{") && aiResponse.Contains("}")) if (aiResponse.Contains("{") && aiResponse.Contains("}"))
{ {
// 提取JSON部分
int jsonStartIndex = aiResponse.IndexOf('{'); int jsonStartIndex = aiResponse.IndexOf('{');
int jsonEndIndex = aiResponse.LastIndexOf('}') + 1; int jsonEndIndex = aiResponse.LastIndexOf('}') + 1;
@@ -34,7 +30,8 @@ public static class AiHelper
try try
{ {
var result = System.Text.Json.JsonSerializer.Deserialize<ImageAnalysisResult>(jsonPart, options); var result =
System.Text.Json.JsonSerializer.Deserialize<ImageAnalysisResult>(jsonPart, options);
if (result != null) if (result != null)
{ {
if (!string.IsNullOrWhiteSpace(result.Title)) if (!string.IsNullOrWhiteSpace(result.Title))
@@ -93,15 +90,14 @@ public static class AiHelper
return (title, description); return (title, description);
} }
// 用于解析JSON的类
public class ImageAnalysisResult public class ImageAnalysisResult
{ {
public string Title { get; set; } = string.Empty; public string Title { get; init; } = string.Empty;
public string Description { get; set; } = string.Empty; public string Description { get; init; } = string.Empty;
} }
public class TagsResult public class TagsResult
{ {
public string[] Tags { get; set; } = Array.Empty<string>(); public string[] Tags { get; init; } = [];
} }
} }
-68
View File
@@ -4,7 +4,6 @@ using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using System.Globalization; using System.Globalization;
using Foxel.Models; using Foxel.Models;
using Foxel.Models.Enums; using Foxel.Models.Enums;
using SixLabors.ImageSharp.PixelFormats;
namespace Foxel.Utils; namespace Foxel.Utils;
@@ -13,21 +12,6 @@ namespace Foxel.Utils;
/// </summary> /// </summary>
public static class ImageHelper public static class ImageHelper
{ {
/// <summary>
/// 获取完整URL路径
/// </summary>
/// <param name="serverUrl">服务器URL</param>
/// <param name="relativePath">相对路径</param>
/// <returns>完整URL路径</returns>
public static string GetFullPath(string serverUrl, string relativePath)
{
if (string.IsNullOrEmpty(relativePath))
return string.Empty;
if (relativePath.StartsWith("https://"))
return relativePath;
return $"{serverUrl.TrimEnd('/')}{relativePath}";
}
/// <summary> /// <summary>
/// 创建缩略图 /// 创建缩略图
/// </summary> /// </summary>
@@ -76,58 +60,6 @@ public static class ImageHelper
return thumbnailFileInfo.Length; return thumbnailFileInfo.Length;
} }
/// <summary>
/// 检查图像是否包含透明像素
/// </summary>
/// <param name="image">要检查的图像</param>
/// <returns>如果图像包含透明像素则返回true</returns>
private static bool HasTransparency(Image image)
{
// 检查图像格式是否支持透明度
if (image.PixelType.AlphaRepresentation == PixelAlphaRepresentation.None)
{
return false; // 图像格式不支持透明度
}
// 对于小图片,逐像素检查是否有透明度
if (image.Width * image.Height <= 1000 * 1000) // 对于不超过1000x1000的图片
{
using var imageWithAlpha = image.CloneAs<Rgba32>();
for (int y = 0; y < imageWithAlpha.Height; y++)
{
for (int x = 0; x < imageWithAlpha.Width; x++)
{
if (imageWithAlpha[x, y].A < 255)
{
return true;
}
}
}
return false;
}
else
{
using var imageWithAlpha = image.CloneAs<Rgba32>();
int sampleSize = Math.Max(image.Width, image.Height) / 100;
sampleSize = Math.Max(1, sampleSize);
for (int y = 0; y < imageWithAlpha.Height; y += sampleSize)
{
for (int x = 0; x < imageWithAlpha.Width; x += sampleSize)
{
if (imageWithAlpha[x, y].A < 255)
{
return true;
}
}
}
return false;
}
}
/// <summary> /// <summary>
/// 根据原始文件大小调整质量参数 /// 根据原始文件大小调整质量参数
/// </summary> /// </summary>