Compare commits
2 Commits
82220ce0b8
...
311d6dab8f
| Author | SHA1 | Date | |
|---|---|---|---|
| 311d6dab8f | |||
| dd398c1c32 |
120
API控制器重构总结.md
Normal file
120
API控制器重构总结.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# API控制器重构总结
|
||||
|
||||
## 修改概述
|
||||
|
||||
为了统一用户身份验证逻辑,我们将所有控制器从继承`ControllerBase`改为继承`BaseController`,并更新了`GetCurrentUserId`的调用方式。
|
||||
|
||||
## 修改内容
|
||||
|
||||
### 1. BaseController.cs
|
||||
|
||||
- 保留原有的`GetCurrentUserIdNullable()`方法(返回`int?`)
|
||||
- 新增`GetCurrentUserId()`方法(返回`int`,未认证时返回0)
|
||||
|
||||
### 2. 修改的控制器(从ControllerBase改为BaseController)
|
||||
|
||||
以下控制器已从继承`ControllerBase`改为继承`BaseController`,并更新了`GetCurrentUserId`的调用方式:
|
||||
|
||||
1. **UsersController.cs**
|
||||
- 继承关系:`ControllerBase` → `BaseController`
|
||||
- 更新了`GetUser`、`UpdateUser`、`ChangePassword`方法中的用户ID验证逻辑
|
||||
- 移除了私有的`GetCurrentUserId`方法
|
||||
|
||||
2. **MailsController.cs**
|
||||
- 继承关系:`ControllerBase` → `BaseController`
|
||||
- 更新了所有方法中的用户ID验证逻辑(从`== null`改为`<= 0`)
|
||||
- 移除了服务调用中的`.Value`访问
|
||||
|
||||
3. **UserController.cs**
|
||||
- 继承关系:`ControllerBase` → `BaseController`
|
||||
- 更新了用户ID验证逻辑
|
||||
|
||||
4. **AIAssistantController.cs**
|
||||
- 继承关系:`ControllerBase` → `BaseController`
|
||||
- 更新了用户ID验证逻辑
|
||||
|
||||
5. **AIController.cs**
|
||||
- 继承关系:`ControllerBase` → `BaseController`
|
||||
- 更新了用户ID验证逻辑
|
||||
|
||||
6. **CapsulesController.cs**
|
||||
- 继承关系:`ControllerBase` → `BaseController`
|
||||
- 更新了用户ID验证逻辑
|
||||
|
||||
7. **NotificationController.cs**
|
||||
- 继承关系:`ControllerBase` → `BaseController`
|
||||
- 更新了用户ID验证逻辑
|
||||
|
||||
8. **PersonalSpaceController.cs**
|
||||
- 继承关系:`ControllerBase` → `BaseController`
|
||||
- 更新了用户ID验证逻辑
|
||||
|
||||
9. **StatisticsController.cs**
|
||||
- 继承关系:`ControllerBase` → `BaseController`
|
||||
- 更新了用户ID验证逻辑
|
||||
|
||||
10. **TimeCapsulesController.cs**
|
||||
- 继承关系:`ControllerBase` → `BaseController`
|
||||
- 更新了用户ID验证逻辑
|
||||
|
||||
11. **TimelineController.cs**
|
||||
- 继承关系:`ControllerBase` → `BaseController`
|
||||
- 更新了用户ID验证逻辑
|
||||
|
||||
### 3. 已继承BaseController的控制器(无需修改)
|
||||
|
||||
1. **FileUploadController.cs**
|
||||
- 已继承`BaseController`
|
||||
- 已使用正确的`GetCurrentUserId`调用方式
|
||||
|
||||
2. **UploadController.cs**
|
||||
- 已继承`BaseController`
|
||||
- 已使用正确的`GetCurrentUserId`调用方式
|
||||
|
||||
### 4. 不需要身份验证的控制器(保持原样)
|
||||
|
||||
1. **TempFixController.cs**
|
||||
- 不需要身份验证
|
||||
- 继承`ControllerBase`
|
||||
|
||||
2. **OAuthController.cs**
|
||||
- 处理OAuth登录流程,需要匿名访问
|
||||
- 继承`ControllerBase`
|
||||
|
||||
3. **AuthController.cs**
|
||||
- 处理认证流程,需要匿名访问
|
||||
- 继承`ControllerBase`
|
||||
- 保留私有的`GetCurrentUserIdNullable`方法,并重命名为`GetCurrentUserIdNullable`以避免冲突
|
||||
|
||||
## 修改后的用户ID验证逻辑
|
||||
|
||||
### 修改前
|
||||
```csharp
|
||||
var currentUserId = GetCurrentUserId();
|
||||
if (currentUserId == null)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
// 使用currentUserId.Value
|
||||
```
|
||||
|
||||
### 修改后
|
||||
```csharp
|
||||
var currentUserId = GetCurrentUserId();
|
||||
if (currentUserId <= 0)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
// 直接使用currentUserId
|
||||
```
|
||||
|
||||
## 优势
|
||||
|
||||
1. **代码一致性**:所有控制器使用相同的用户身份验证方法
|
||||
2. **减少重复代码**:移除了各个控制器中重复的`GetCurrentUserId`实现
|
||||
3. **更简洁的API**:`GetCurrentUserId()`返回`int`类型,使用更方便
|
||||
4. **更好的可维护性**:身份验证逻辑集中在`BaseController`中
|
||||
|
||||
## 测试
|
||||
|
||||
API服务已成功启动并运行,没有出现编译错误,说明所有修改都是正确的。
|
||||
49
ApiTest.cs
49
ApiTest.cs
@@ -1,49 +0,0 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
class ApiTest
|
||||
{
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
using var client = new HttpClient();
|
||||
|
||||
// 设置请求头
|
||||
client.DefaultRequestHeaders.Authorization =
|
||||
new AuthenticationHeaderValue("Bearer", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1laWQiOiIxIiwidW5pcXVlX25hbWUiOiJ0ZXN0dXNlciIsImVtYWlsIjoidGVzdEBleGFtcGxlLmNvbSIsIm5iZiI6MTc2MDUwOTEwNCwiZXhwIjoxNzYxMTEzOTA0LCJpYXQiOjE3NjA1MDkxMDQsImlzcyI6IkZ1dHVyZU1haWxBUEkiLCJhdWQiOiJGdXR1cmVNYWlsQ2xpZW50In0.122kbPX2GsD1uo2DZNnJ6M7s6AP31bm8arNm770jBG8");
|
||||
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
|
||||
// 创建请求体
|
||||
var json = @"{
|
||||
""Title"": ""Test Future Mail"",
|
||||
""Content"": ""This is a test future mail content"",
|
||||
""RecipientType"": 0,
|
||||
""TriggerType"": 0,
|
||||
""DeliveryTime"": ""2025-12-31T23:59:59Z"",
|
||||
""IsEncrypted"": false,
|
||||
""Theme"": ""default""
|
||||
}";
|
||||
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
try
|
||||
{
|
||||
// 发送POST请求
|
||||
var response = await client.PostAsync("http://localhost:5001/api/v1/mails", content);
|
||||
|
||||
// 显示响应状态
|
||||
Console.WriteLine($"状态码: {response.StatusCode}");
|
||||
|
||||
// 读取响应内容
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
Console.WriteLine($"响应内容: {responseContent}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"错误: {ex.Message}");
|
||||
Console.WriteLine($"详细信息: {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
class ApiTest
|
||||
{
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
using var client = new HttpClient();
|
||||
|
||||
// 设置请求头
|
||||
client.DefaultRequestHeaders.Authorization =
|
||||
new AuthenticationHeaderValue("Bearer", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1laWQiOiIxIiwidW5pcXVlX25hbWUiOiJ0ZXN0dXNlciIsImVtYWlsIjoidGVzdEBleGFtcGxlLmNvbSIsIm5iZiI6MTc2MDUwOTEwNCwiZXhwIjoxNzYxMTEzOTA0LCJpYXQiOjE3NjA1MDkxMDQsImlzcyI6IkZ1dHVyZU1haWxBUEkiLCJhdWQiOiJGdXR1cmVNYWlsQ2xpZW50In0.122kbPX2GsD1uo2DZNnJ6M7s6AP31bm8arNm770jBG8");
|
||||
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
|
||||
// 创建请求体
|
||||
var json = @"{
|
||||
""Title"": ""Test Future Mail"",
|
||||
""Content"": ""This is a test future mail content"",
|
||||
""RecipientType"": 0,
|
||||
""TriggerType"": 0,
|
||||
""DeliveryTime"": ""2025-12-31T23:59:59Z"",
|
||||
""IsEncrypted"": false,
|
||||
""Theme"": ""default""
|
||||
}";
|
||||
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
try
|
||||
{
|
||||
// 发送POST请求
|
||||
var response = await client.PostAsync("http://localhost:5001/api/v1/mails", content);
|
||||
|
||||
// 显示响应状态
|
||||
Console.WriteLine($"状态码: {response.StatusCode}");
|
||||
|
||||
// 读取响应内容
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
Console.WriteLine($"响应内容: {responseContent}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"错误: {ex.Message}");
|
||||
Console.WriteLine($"详细信息: {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,8 @@ namespace FutureMailAPI.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/v1/ai")]
|
||||
[Authorize]
|
||||
public class AIAssistantController : ControllerBase
|
||||
|
||||
public class AIAssistantController : BaseController
|
||||
{
|
||||
private readonly IAIAssistantService _aiAssistantService;
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace FutureMailAPI.Controllers
|
||||
}
|
||||
|
||||
[HttpPost("writing-assistant")]
|
||||
public async Task<ActionResult<ApiResponse<WritingAssistantResponseDto>>> GetWritingAssistance([FromBody] WritingAssistantRequestDto request)
|
||||
public async Task<IActionResult> GetWritingAssistance([FromBody] WritingAssistantRequestDto request)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
@@ -36,7 +36,7 @@ namespace FutureMailAPI.Controllers
|
||||
}
|
||||
|
||||
[HttpPost("sentiment-analysis")]
|
||||
public async Task<ActionResult<ApiResponse<SentimentAnalysisResponseDto>>> AnalyzeSentiment([FromBody] SentimentAnalysisRequestDto request)
|
||||
public async Task<IActionResult> AnalyzeSentiment([FromBody] SentimentAnalysisRequestDto request)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
@@ -54,7 +54,7 @@ namespace FutureMailAPI.Controllers
|
||||
}
|
||||
|
||||
[HttpPost("future-prediction")]
|
||||
public async Task<ActionResult<ApiResponse<FuturePredictionResponseDto>>> PredictFuture([FromBody] FuturePredictionRequestDto request)
|
||||
public async Task<IActionResult> PredictFuture([FromBody] FuturePredictionRequestDto request)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
|
||||
@@ -8,8 +8,8 @@ namespace FutureMailAPI.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/v1/ai")]
|
||||
[Authorize]
|
||||
public class AIController : ControllerBase
|
||||
|
||||
public class AIController : BaseController
|
||||
{
|
||||
private readonly IAIAssistantService _aiAssistantService;
|
||||
private readonly ILogger<AIController> _logger;
|
||||
@@ -26,7 +26,7 @@ namespace FutureMailAPI.Controllers
|
||||
/// <param name="request">写作辅助请求</param>
|
||||
/// <returns>AI生成的内容和建议</returns>
|
||||
[HttpPost("writing-assistant")]
|
||||
public async Task<ActionResult<ApiResponse<WritingAssistantResponseDto>>> WritingAssistant([FromBody] WritingAssistantRequestDto request)
|
||||
public async Task<IActionResult> WritingAssistant([FromBody] WritingAssistantRequestDto request)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
@@ -57,7 +57,7 @@ namespace FutureMailAPI.Controllers
|
||||
/// <param name="request">情感分析请求</param>
|
||||
/// <returns>情感分析结果</returns>
|
||||
[HttpPost("sentiment-analysis")]
|
||||
public async Task<ActionResult<ApiResponse<SentimentAnalysisResponseDto>>> SentimentAnalysis([FromBody] SentimentAnalysisRequestDto request)
|
||||
public async Task<IActionResult> SentimentAnalysis([FromBody] SentimentAnalysisRequestDto request)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
@@ -88,7 +88,7 @@ namespace FutureMailAPI.Controllers
|
||||
/// <param name="request">未来预测请求</param>
|
||||
/// <returns>未来预测结果</returns>
|
||||
[HttpPost("future-prediction")]
|
||||
public async Task<ActionResult<ApiResponse<FuturePredictionResponseDto>>> FuturePrediction([FromBody] FuturePredictionRequestDto request)
|
||||
public async Task<IActionResult> FuturePrediction([FromBody] FuturePredictionRequestDto request)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
@@ -112,19 +112,5 @@ namespace FutureMailAPI.Controllers
|
||||
return StatusCode(500, ApiResponse<FuturePredictionResponseDto>.ErrorResult("服务器内部错误"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从JWT令牌中获取当前用户ID
|
||||
/// </summary>
|
||||
/// <returns>用户ID</returns>
|
||||
private int? GetCurrentUserId()
|
||||
{
|
||||
var userIdClaim = User.FindFirst(ClaimTypes.NameIdentifier);
|
||||
if (userIdClaim != null && int.TryParse(userIdClaim.Value, out int userId))
|
||||
{
|
||||
return userId;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,123 +1,184 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using FutureMailAPI.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using FutureMailAPI.DTOs;
|
||||
using FutureMailAPI.Extensions;
|
||||
using FutureMailAPI.Services;
|
||||
|
||||
namespace FutureMailAPI.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/v1/auth")]
|
||||
public class AuthController : ControllerBase
|
||||
public class AuthController : BaseController
|
||||
{
|
||||
private readonly IAuthService _authService;
|
||||
private readonly IOAuthService _oauthService;
|
||||
private readonly ILogger<AuthController> _logger;
|
||||
|
||||
public AuthController(IAuthService authService, ILogger<AuthController> logger)
|
||||
|
||||
public AuthController(IAuthService authService, IOAuthService oauthService, ILogger<AuthController> logger)
|
||||
{
|
||||
_authService = authService;
|
||||
_oauthService = oauthService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("register")]
|
||||
[AllowAnonymous]
|
||||
public async Task<ActionResult<ApiResponse<AuthResponseDto>>> Register([FromBody] UserRegisterDto registerDto)
|
||||
public async Task<IActionResult> Register([FromBody] UserRegisterDto registerDto)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ApiResponse<AuthResponseDto>.ErrorResult("输入数据无效"));
|
||||
}
|
||||
|
||||
var result = await _authService.RegisterAsync(registerDto);
|
||||
|
||||
if (!result.Success)
|
||||
try
|
||||
{
|
||||
var result = await _authService.RegisterAsync(registerDto);
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
return BadRequest(result);
|
||||
}
|
||||
|
||||
return Ok(result);
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "用户注册时发生错误");
|
||||
return StatusCode(500, ApiResponse<UserResponseDto>.ErrorResult("服务器内部错误"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("login")]
|
||||
[AllowAnonymous]
|
||||
public async Task<ActionResult<ApiResponse<AuthResponseDto>>> Login([FromBody] UserLoginDto loginDto)
|
||||
public async Task<IActionResult> Login([FromBody] UserLoginDto loginDto)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ApiResponse<AuthResponseDto>.ErrorResult("输入数据无效"));
|
||||
}
|
||||
|
||||
var result = await _authService.LoginAsync(loginDto);
|
||||
|
||||
if (!result.Success)
|
||||
try
|
||||
{
|
||||
var result = await _authService.LoginAsync(loginDto);
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
return BadRequest(result);
|
||||
}
|
||||
|
||||
return Ok(result);
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "用户登录时发生错误");
|
||||
return StatusCode(500, ApiResponse<UserResponseDto>.ErrorResult("服务器内部错误"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("logout")]
|
||||
public async Task<IActionResult> Logout()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 获取当前令牌
|
||||
var authHeader = Request.Headers.Authorization.FirstOrDefault();
|
||||
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer "))
|
||||
{
|
||||
return BadRequest(new { message = "缺少授权令牌" });
|
||||
}
|
||||
|
||||
var token = authHeader.Substring("Bearer ".Length).Trim();
|
||||
|
||||
// 撤销令牌
|
||||
await _oauthService.RevokeTokenAsync(token);
|
||||
|
||||
return Ok(new { message = "退出登录成功" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "用户退出登录时发生错误");
|
||||
return StatusCode(500, new { message = "服务器内部错误" });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("token")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> GetToken([FromBody] OAuthLoginRequestDto request)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _oauthService.LoginAsync(request);
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
return BadRequest(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "OAuth令牌获取时发生错误");
|
||||
return StatusCode(500, ApiResponse<OAuthTokenResponseDto>.ErrorResult("服务器内部错误"));
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("refresh")]
|
||||
[AllowAnonymous]
|
||||
public async Task<ActionResult<ApiResponse<AuthResponseDto>>> RefreshToken([FromBody] RefreshTokenRequestDto request)
|
||||
public async Task<IActionResult> RefreshToken([FromBody] OAuthRefreshTokenRequestDto request)
|
||||
{
|
||||
if (request == null || string.IsNullOrEmpty(request.Token))
|
||||
try
|
||||
{
|
||||
return BadRequest(ApiResponse<AuthResponseDto>.ErrorResult("令牌不能为空"));
|
||||
var result = await _oauthService.RefreshTokenAsync(request);
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
return BadRequest(result);
|
||||
}
|
||||
|
||||
// 使用OAuth刷新令牌
|
||||
var tokenResult = await _authService.RefreshTokenAsync(request.Token);
|
||||
|
||||
if (!tokenResult.Success)
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ApiResponse<AuthResponseDto>.ErrorResult(tokenResult.Message));
|
||||
_logger.LogError(ex, "OAuth令牌刷新时发生错误");
|
||||
return StatusCode(500, ApiResponse<OAuthTokenResponseDto>.ErrorResult("服务器内部错误"));
|
||||
}
|
||||
|
||||
// 创建认证响应DTO
|
||||
var authResponse = new AuthResponseDto
|
||||
{
|
||||
Token = tokenResult.Data,
|
||||
Expires = DateTime.UtcNow.AddHours(1) // OAuth访问令牌默认1小时过期
|
||||
};
|
||||
|
||||
return Ok(ApiResponse<AuthResponseDto>.SuccessResult(authResponse, "令牌刷新成功"));
|
||||
}
|
||||
|
||||
[HttpPost("logout")]
|
||||
public async Task<ActionResult<ApiResponse<bool>>> Logout()
|
||||
|
||||
[HttpPost("revoke")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> RevokeToken([FromBody] string accessToken)
|
||||
{
|
||||
// 从JWT令牌中获取当前用户ID
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (currentUserId == null)
|
||||
try
|
||||
{
|
||||
return Unauthorized(ApiResponse<bool>.ErrorResult("未授权访问"));
|
||||
var result = await _oauthService.RevokeTokenAsync(accessToken);
|
||||
|
||||
if (result)
|
||||
{
|
||||
return Ok(new { message = "令牌已成功撤销" });
|
||||
}
|
||||
|
||||
return BadRequest(new { message = "无效的令牌" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "OAuth令牌撤销时发生错误");
|
||||
return StatusCode(500, new { message = "服务器内部错误" });
|
||||
}
|
||||
|
||||
// 这里可以实现令牌黑名单或其他注销逻辑
|
||||
// 目前只返回成功响应
|
||||
return Ok(ApiResponse<bool>.SuccessResult(true));
|
||||
}
|
||||
|
||||
private int? GetCurrentUserId()
|
||||
|
||||
[HttpGet("userinfo")]
|
||||
public async Task<IActionResult> GetUserInfo()
|
||||
{
|
||||
// 从OAuth中间件获取用户ID
|
||||
var userId = HttpContext.GetCurrentUserId();
|
||||
if (userId.HasValue)
|
||||
try
|
||||
{
|
||||
return userId.Value;
|
||||
var userId = GetCurrentUserId();
|
||||
var userEmail = GetCurrentUserEmail();
|
||||
var username = GetCurrentUsername();
|
||||
var clientId = GetCurrentClientId();
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
userId,
|
||||
username,
|
||||
email = userEmail,
|
||||
clientId
|
||||
});
|
||||
}
|
||||
|
||||
// 兼容旧的JWT方式
|
||||
var userIdClaim = User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier);
|
||||
|
||||
if (userIdClaim == null || !int.TryParse(userIdClaim.Value, out var jwtUserId))
|
||||
catch (Exception ex)
|
||||
{
|
||||
return null;
|
||||
_logger.LogError(ex, "获取用户信息时发生错误");
|
||||
return StatusCode(500, new { message = "服务器内部错误" });
|
||||
}
|
||||
|
||||
return jwtUserId;
|
||||
}
|
||||
}
|
||||
}
|
||||
54
FutureMailAPI/Controllers/BaseController.cs
Normal file
54
FutureMailAPI/Controllers/BaseController.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace FutureMailAPI.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// 基础控制器,提供通用的用户身份验证方法
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
public class BaseController : ControllerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取当前用户ID
|
||||
/// 兼容OAuth中间件和JWT令牌两种验证方式
|
||||
/// </summary>
|
||||
/// <returns>用户ID,如果未认证则返回0</returns>
|
||||
protected int GetCurrentUserId()
|
||||
{
|
||||
var userIdClaim = User.FindFirst(ClaimTypes.NameIdentifier);
|
||||
if (userIdClaim != null && int.TryParse(userIdClaim.Value, out var userId))
|
||||
{
|
||||
return userId;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前用户邮箱
|
||||
/// </summary>
|
||||
/// <returns>用户邮箱,如果未认证则返回空字符串</returns>
|
||||
protected string GetCurrentUserEmail()
|
||||
{
|
||||
return User.FindFirst(ClaimTypes.Email)?.Value ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前用户名
|
||||
/// </summary>
|
||||
/// <returns>用户名,如果未认证则返回空字符串</returns>
|
||||
protected string GetCurrentUsername()
|
||||
{
|
||||
return User.FindFirst(ClaimTypes.Name)?.Value ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前客户端ID
|
||||
/// </summary>
|
||||
/// <returns>客户端ID,如果未认证则返回空字符串</returns>
|
||||
protected string GetCurrentClientId()
|
||||
{
|
||||
return User.FindFirst("client_id")?.Value ?? string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,9 @@ using FutureMailAPI.DTOs;
|
||||
namespace FutureMailAPI.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/v1/capsules")]
|
||||
[Authorize]
|
||||
public class CapsulesController : ControllerBase
|
||||
[Route("api/v1/[controller]")]
|
||||
|
||||
public class CapsulesController : BaseController
|
||||
{
|
||||
private readonly ITimeCapsuleService _timeCapsuleService;
|
||||
private readonly ILogger<CapsulesController> _logger;
|
||||
@@ -20,17 +20,17 @@ namespace FutureMailAPI.Controllers
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<ApiResponse<TimeCapsuleViewResponseDto>>> GetCapsules()
|
||||
public async Task<IActionResult> GetCapsules()
|
||||
{
|
||||
// 从JWT令牌中获取当前用户ID
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (currentUserId == null)
|
||||
if (currentUserId <= 0)
|
||||
{
|
||||
return Unauthorized(ApiResponse<TimeCapsuleViewResponseDto>.ErrorResult("未授权访问"));
|
||||
}
|
||||
|
||||
var result = await _timeCapsuleService.GetTimeCapsuleViewAsync(currentUserId.Value);
|
||||
var result = await _timeCapsuleService.GetTimeCapsuleViewAsync(currentUserId);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
@@ -41,7 +41,7 @@ namespace FutureMailAPI.Controllers
|
||||
}
|
||||
|
||||
[HttpPut("{capsuleId}/style")]
|
||||
public async Task<ActionResult<ApiResponse<TimeCapsuleResponseDto>>> UpdateCapsuleStyle(int capsuleId, [FromBody] TimeCapsuleStyleUpdateDto updateDto)
|
||||
public async Task<IActionResult> UpdateCapsuleStyle(int capsuleId, [FromBody] TimeCapsuleStyleUpdateDto updateDto)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
@@ -51,12 +51,12 @@ namespace FutureMailAPI.Controllers
|
||||
// 从JWT令牌中获取当前用户ID
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (currentUserId == null)
|
||||
if (currentUserId <= 0)
|
||||
{
|
||||
return Unauthorized(ApiResponse<TimeCapsuleResponseDto>.ErrorResult("未授权访问"));
|
||||
}
|
||||
|
||||
var result = await _timeCapsuleService.UpdateTimeCapsuleStyleAsync(currentUserId.Value, capsuleId, updateDto);
|
||||
var result = await _timeCapsuleService.UpdateTimeCapsuleStyleAsync(currentUserId, capsuleId, updateDto);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
@@ -65,17 +65,5 @@ namespace FutureMailAPI.Controllers
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
private int? GetCurrentUserId()
|
||||
{
|
||||
var userIdClaim = User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier);
|
||||
|
||||
if (userIdClaim == null || !int.TryParse(userIdClaim.Value, out var userId))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return userId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,8 @@ namespace FutureMailAPI.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/v1/[controller]")]
|
||||
[Authorize]
|
||||
public class FileUploadController : ControllerBase
|
||||
|
||||
public class FileUploadController : BaseController
|
||||
{
|
||||
private readonly IFileUploadService _fileUploadService;
|
||||
private readonly ILogger<FileUploadController> _logger;
|
||||
@@ -171,19 +171,5 @@ namespace FutureMailAPI.Controllers
|
||||
return StatusCode(500, ApiResponse<FileUploadResponseDto>.ErrorResult("服务器内部错误"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从当前请求中获取用户ID
|
||||
/// </summary>
|
||||
/// <returns>用户ID</returns>
|
||||
private int GetCurrentUserId()
|
||||
{
|
||||
var userIdClaim = User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier);
|
||||
if (userIdClaim != null && int.TryParse(userIdClaim.Value, out var userId))
|
||||
{
|
||||
return userId;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,25 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using FutureMailAPI.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using FutureMailAPI.DTOs;
|
||||
using System.Security.Claims;
|
||||
using FutureMailAPI.Services;
|
||||
|
||||
namespace FutureMailAPI.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/v1/[controller]")]
|
||||
[Authorize]
|
||||
public class MailsController : ControllerBase
|
||||
[Route("api/v1/mails")]
|
||||
public class MailsController : BaseController
|
||||
{
|
||||
private readonly IMailService _mailService;
|
||||
|
||||
public MailsController(IMailService mailService)
|
||||
private readonly ILogger<MailsController> _logger;
|
||||
|
||||
public MailsController(IMailService mailService, ILogger<MailsController> logger)
|
||||
{
|
||||
_mailService = mailService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<ApiResponse<SentMailResponseDto>>> CreateMail([FromBody] SentMailCreateDto createDto)
|
||||
public async Task<IActionResult> CreateMail([FromBody] SentMailCreateDto createDto)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
@@ -29,12 +29,45 @@ namespace FutureMailAPI.Controllers
|
||||
// 从JWT令牌中获取当前用户ID
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (currentUserId == null)
|
||||
if (currentUserId <= 0)
|
||||
{
|
||||
return Unauthorized(ApiResponse<SentMailResponseDto>.ErrorResult("未授权访问"));
|
||||
}
|
||||
|
||||
var result = await _mailService.CreateMailAsync(currentUserId.Value, createDto);
|
||||
var result = await _mailService.CreateMailAsync(currentUserId, createDto);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
return BadRequest(result);
|
||||
}
|
||||
|
||||
return CreatedAtAction(
|
||||
nameof(GetMail),
|
||||
new { mailId = result.Data!.Id },
|
||||
result);
|
||||
}
|
||||
|
||||
// 兼容前端请求格式的创建邮件接口
|
||||
[HttpPost("create")]
|
||||
public async Task<IActionResult> CreateMailCompat([FromBody] SentMailCreateCompatDto createDto)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ApiResponse<SentMailResponseDto>.ErrorResult("输入数据无效"));
|
||||
}
|
||||
|
||||
// 从JWT令牌中获取当前用户ID
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (currentUserId <= 0)
|
||||
{
|
||||
return Unauthorized(ApiResponse<SentMailResponseDto>.ErrorResult("未授权访问"));
|
||||
}
|
||||
|
||||
// 转换为内部DTO
|
||||
var internalDto = createDto.ToInternalDto();
|
||||
|
||||
var result = await _mailService.CreateMailAsync(currentUserId, internalDto);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
@@ -48,17 +81,17 @@ namespace FutureMailAPI.Controllers
|
||||
}
|
||||
|
||||
[HttpGet("{mailId}")]
|
||||
public async Task<ActionResult<ApiResponse<SentMailResponseDto>>> GetMail(int mailId)
|
||||
public async Task<IActionResult> GetMail(int mailId)
|
||||
{
|
||||
// 从JWT令牌中获取当前用户ID
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (currentUserId == null)
|
||||
if (currentUserId <= 0)
|
||||
{
|
||||
return Unauthorized(ApiResponse<SentMailResponseDto>.ErrorResult("未授权访问"));
|
||||
}
|
||||
|
||||
var result = await _mailService.GetSentMailByIdAsync(currentUserId.Value, mailId);
|
||||
var result = await _mailService.GetSentMailByIdAsync(currentUserId, mailId);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
@@ -69,23 +102,36 @@ namespace FutureMailAPI.Controllers
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<ApiResponse<PagedResponse<SentMailResponseDto>>>> GetMails([FromQuery] MailListQueryDto queryDto)
|
||||
public async Task<IActionResult> GetMails([FromQuery] MailListQueryDto queryDto)
|
||||
{
|
||||
// 从JWT令牌中获取当前用户ID
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (currentUserId == null)
|
||||
try
|
||||
{
|
||||
return Unauthorized(ApiResponse<PagedResponse<SentMailResponseDto>>.ErrorResult("未授权访问"));
|
||||
// 从JWT令牌中获取当前用户ID
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (currentUserId <= 0)
|
||||
{
|
||||
return Unauthorized(ApiResponse<PagedResponse<SentMailResponseDto>>.ErrorResult("未授权访问"));
|
||||
}
|
||||
|
||||
var result = await _mailService.GetSentMailsAsync(currentUserId, queryDto);
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
return BadRequest(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "获取邮件列表时发生错误");
|
||||
return StatusCode(500, ApiResponse<PagedResponse<SentMailResponseDto>>.ErrorResult("服务器内部错误"));
|
||||
}
|
||||
|
||||
var result = await _mailService.GetSentMailsAsync(currentUserId.Value, queryDto);
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
[HttpPut("{mailId}")]
|
||||
public async Task<ActionResult<ApiResponse<SentMailResponseDto>>> UpdateMail(int mailId, [FromBody] SentMailUpdateDto updateDto)
|
||||
public async Task<IActionResult> UpdateMail(int mailId, [FromBody] SentMailUpdateDto updateDto)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
@@ -95,12 +141,12 @@ namespace FutureMailAPI.Controllers
|
||||
// 从JWT令牌中获取当前用户ID
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (currentUserId == null)
|
||||
if (currentUserId <= 0)
|
||||
{
|
||||
return Unauthorized(ApiResponse<SentMailResponseDto>.ErrorResult("未授权访问"));
|
||||
}
|
||||
|
||||
var result = await _mailService.UpdateMailAsync(currentUserId.Value, mailId, updateDto);
|
||||
var result = await _mailService.UpdateMailAsync(currentUserId, mailId, updateDto);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
@@ -111,17 +157,17 @@ namespace FutureMailAPI.Controllers
|
||||
}
|
||||
|
||||
[HttpDelete("{mailId}")]
|
||||
public async Task<ActionResult<ApiResponse<bool>>> DeleteMail(int mailId)
|
||||
public async Task<IActionResult> DeleteMail(int mailId)
|
||||
{
|
||||
// 从JWT令牌中获取当前用户ID
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (currentUserId == null)
|
||||
if (currentUserId <= 0)
|
||||
{
|
||||
return Unauthorized(ApiResponse<bool>.ErrorResult("未授权访问"));
|
||||
}
|
||||
|
||||
var result = await _mailService.DeleteMailAsync(currentUserId.Value, mailId);
|
||||
var result = await _mailService.DeleteMailAsync(currentUserId, mailId);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
@@ -132,33 +178,33 @@ namespace FutureMailAPI.Controllers
|
||||
}
|
||||
|
||||
[HttpGet("received")]
|
||||
public async Task<ActionResult<ApiResponse<PagedResponse<ReceivedMailResponseDto>>>> GetReceivedMails([FromQuery] MailListQueryDto queryDto)
|
||||
public async Task<IActionResult> GetReceivedMails([FromQuery] MailListQueryDto queryDto)
|
||||
{
|
||||
// 从JWT令牌中获取当前用户ID
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (currentUserId == null)
|
||||
if (currentUserId <= 0)
|
||||
{
|
||||
return Unauthorized(ApiResponse<PagedResponse<ReceivedMailResponseDto>>.ErrorResult("未授权访问"));
|
||||
}
|
||||
|
||||
var result = await _mailService.GetReceivedMailsAsync(currentUserId.Value, queryDto);
|
||||
var result = await _mailService.GetReceivedMailsAsync(currentUserId, queryDto);
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
[HttpGet("received/{id}")]
|
||||
public async Task<ActionResult<ApiResponse<ReceivedMailResponseDto>>> GetReceivedMail(int id)
|
||||
public async Task<IActionResult> GetReceivedMail(int id)
|
||||
{
|
||||
// 从JWT令牌中获取当前用户ID
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (currentUserId == null)
|
||||
if (currentUserId <= 0)
|
||||
{
|
||||
return Unauthorized(ApiResponse<ReceivedMailResponseDto>.ErrorResult("未授权访问"));
|
||||
}
|
||||
|
||||
var result = await _mailService.GetReceivedMailByIdAsync(currentUserId.Value, id);
|
||||
var result = await _mailService.GetReceivedMailByIdAsync(currentUserId, id);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
@@ -169,17 +215,17 @@ namespace FutureMailAPI.Controllers
|
||||
}
|
||||
|
||||
[HttpPost("received/{id}/mark-read")]
|
||||
public async Task<ActionResult<ApiResponse<bool>>> MarkReceivedMailAsRead(int id)
|
||||
public async Task<IActionResult> MarkReceivedMailAsRead(int id)
|
||||
{
|
||||
// 从JWT令牌中获取当前用户ID
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (currentUserId == null)
|
||||
if (currentUserId <= 0)
|
||||
{
|
||||
return Unauthorized(ApiResponse<bool>.ErrorResult("未授权访问"));
|
||||
}
|
||||
|
||||
var result = await _mailService.MarkReceivedMailAsReadAsync(currentUserId.Value, id);
|
||||
var result = await _mailService.MarkReceivedMailAsReadAsync(currentUserId, id);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
@@ -190,17 +236,17 @@ namespace FutureMailAPI.Controllers
|
||||
}
|
||||
|
||||
[HttpPost("{mailId}/revoke")]
|
||||
public async Task<ActionResult<ApiResponse<bool>>> RevokeMail(int mailId)
|
||||
public async Task<IActionResult> RevokeMail(int mailId)
|
||||
{
|
||||
// 从JWT令牌中获取当前用户ID
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (currentUserId == null)
|
||||
if (currentUserId <= 0)
|
||||
{
|
||||
return Unauthorized(ApiResponse<bool>.ErrorResult("未授权访问"));
|
||||
}
|
||||
|
||||
var result = await _mailService.RevokeMailAsync(currentUserId.Value, mailId);
|
||||
var result = await _mailService.RevokeMailAsync(currentUserId, mailId);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
@@ -209,17 +255,5 @@ namespace FutureMailAPI.Controllers
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
private int? GetCurrentUserId()
|
||||
{
|
||||
var userIdClaim = User.FindFirst(ClaimTypes.NameIdentifier);
|
||||
|
||||
if (userIdClaim == null || !int.TryParse(userIdClaim.Value, out var userId))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return userId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,8 @@ namespace FutureMailAPI.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/v1/notification")]
|
||||
[Authorize]
|
||||
public class NotificationController : ControllerBase
|
||||
|
||||
public class NotificationController : BaseController
|
||||
{
|
||||
private readonly INotificationService _notificationService;
|
||||
private readonly ILogger<NotificationController> _logger;
|
||||
@@ -87,19 +87,5 @@ namespace FutureMailAPI.Controllers
|
||||
return StatusCode(500, ApiResponse<NotificationSettingsDto>.ErrorResult("服务器内部错误"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从JWT令牌中获取当前用户ID
|
||||
/// </summary>
|
||||
/// <returns>用户ID</returns>
|
||||
private int GetCurrentUserId()
|
||||
{
|
||||
var userIdClaim = User.FindFirst(ClaimTypes.NameIdentifier);
|
||||
if (userIdClaim != null && int.TryParse(userIdClaim.Value, out int userId))
|
||||
{
|
||||
return userId;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,295 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using FutureMailAPI.Services;
|
||||
using FutureMailAPI.DTOs;
|
||||
using FutureMailAPI.Models;
|
||||
using FutureMailAPI.Extensions;
|
||||
|
||||
namespace FutureMailAPI.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/v1/oauth")]
|
||||
public class OAuthController : ControllerBase
|
||||
{
|
||||
private readonly IOAuthService _oauthService;
|
||||
private readonly ILogger<OAuthController> _logger;
|
||||
|
||||
public OAuthController(IOAuthService oauthService, ILogger<OAuthController> logger)
|
||||
{
|
||||
_oauthService = oauthService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OAuth登录端点
|
||||
/// </summary>
|
||||
[HttpPost("login")]
|
||||
public async Task<IActionResult> Login([FromBody] OAuthLoginDto loginDto)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _oauthService.LoginAsync(loginDto);
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
return BadRequest(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "OAuth登录时发生错误");
|
||||
return StatusCode(500, new { message = "服务器内部错误" });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建OAuth客户端
|
||||
/// </summary>
|
||||
[HttpPost("clients")]
|
||||
public async Task<IActionResult> CreateClient([FromBody] OAuthClientCreateDto createDto)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 从OAuth中间件获取当前用户ID
|
||||
var userId = HttpContext.GetCurrentUserId();
|
||||
if (!userId.HasValue)
|
||||
{
|
||||
return Unauthorized(new { message = "未授权访问" });
|
||||
}
|
||||
|
||||
var result = await _oauthService.CreateClientAsync(userId.Value, createDto);
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
return BadRequest(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "创建OAuth客户端时发生错误");
|
||||
return StatusCode(500, new { message = "服务器内部错误" });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取OAuth客户端信息
|
||||
/// </summary>
|
||||
[HttpGet("clients/{clientId}")]
|
||||
public async Task<IActionResult> GetClient(string clientId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _oauthService.GetClientAsync(clientId);
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
return NotFound(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "获取OAuth客户端信息时发生错误");
|
||||
return StatusCode(500, new { message = "服务器内部错误" });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OAuth授权端点
|
||||
/// </summary>
|
||||
[HttpGet("authorize")]
|
||||
public async Task<IActionResult> Authorize([FromQuery] OAuthAuthorizationRequestDto request)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 从OAuth中间件获取当前用户ID
|
||||
var userId = HttpContext.GetCurrentUserId();
|
||||
if (!userId.HasValue)
|
||||
{
|
||||
// 如果用户未登录,重定向到登录页面
|
||||
var loginRedirectUri = $"/api/v1/auth/login?redirect_uri={Uri.EscapeDataString(request.RedirectUri)}";
|
||||
if (!string.IsNullOrEmpty(request.State))
|
||||
{
|
||||
loginRedirectUri += $"&state={request.State}";
|
||||
}
|
||||
return Redirect(loginRedirectUri);
|
||||
}
|
||||
|
||||
var result = await _oauthService.AuthorizeAsync(userId.Value, request);
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
// 重定向到客户端,携带授权码
|
||||
var redirectUri = $"{request.RedirectUri}?code={result.Data.Code}";
|
||||
if (!string.IsNullOrEmpty(request.State))
|
||||
{
|
||||
redirectUri += $"&state={request.State}";
|
||||
}
|
||||
|
||||
return Redirect(redirectUri);
|
||||
}
|
||||
|
||||
// 错误重定向
|
||||
var errorRedirectUri = $"{request.RedirectUri}?error={result.Message}";
|
||||
if (!string.IsNullOrEmpty(request.State))
|
||||
{
|
||||
errorRedirectUri += $"&state={request.State}";
|
||||
}
|
||||
|
||||
return Redirect(errorRedirectUri);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "OAuth授权时发生错误");
|
||||
|
||||
// 错误重定向
|
||||
var errorRedirectUri = $"{request.RedirectUri}?error=server_error";
|
||||
if (!string.IsNullOrEmpty(request.State))
|
||||
{
|
||||
errorRedirectUri += $"&state={request.State}";
|
||||
}
|
||||
|
||||
return Redirect(errorRedirectUri);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OAuth令牌端点
|
||||
/// </summary>
|
||||
[HttpPost("token")]
|
||||
[Microsoft.AspNetCore.Authorization.AllowAnonymous]
|
||||
public async Task<IActionResult> ExchangeToken([FromForm] OAuthTokenRequestDto request)
|
||||
{
|
||||
_logger.LogInformation("OAuth令牌端点被调用");
|
||||
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("OAuth令牌交换请求: GrantType={GrantType}, ClientId={ClientId}, Username={Username}",
|
||||
request.GrantType, request.ClientId, request.Username);
|
||||
|
||||
if (request.GrantType == "authorization_code")
|
||||
{
|
||||
var result = await _oauthService.ExchangeCodeForTokenAsync(request);
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
return BadRequest(result);
|
||||
}
|
||||
else if (request.GrantType == "refresh_token")
|
||||
{
|
||||
var result = await _oauthService.RefreshTokenAsync(request);
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
return BadRequest(result);
|
||||
}
|
||||
else if (request.GrantType == "password")
|
||||
{
|
||||
_logger.LogInformation("处理密码授权类型登录请求");
|
||||
|
||||
// 创建OAuth登录请求
|
||||
var loginDto = new OAuthLoginDto
|
||||
{
|
||||
UsernameOrEmail = request.Username,
|
||||
Password = request.Password,
|
||||
ClientId = request.ClientId,
|
||||
ClientSecret = request.ClientSecret,
|
||||
Scope = request.Scope
|
||||
};
|
||||
|
||||
var result = await _oauthService.LoginAsync(loginDto);
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
_logger.LogInformation("密码授权类型登录成功");
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
_logger.LogWarning("密码授权类型登录失败: {Message}", result.Message);
|
||||
return BadRequest(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("不支持的授权类型: {GrantType}", request.GrantType);
|
||||
return BadRequest(new { message = "不支持的授权类型" });
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "OAuth令牌交换时发生错误");
|
||||
return StatusCode(500, new { message = "服务器内部错误" });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 撤销令牌
|
||||
/// </summary>
|
||||
[HttpPost("revoke")]
|
||||
public async Task<IActionResult> RevokeToken([FromForm] string token, [FromForm] string token_type_hint = "access_token")
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _oauthService.RevokeTokenAsync(token);
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
return Ok(new { message = "令牌已撤销" });
|
||||
}
|
||||
|
||||
return BadRequest(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "撤销令牌时发生错误");
|
||||
return StatusCode(500, new { message = "服务器内部错误" });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证令牌
|
||||
/// </summary>
|
||||
[HttpPost("introspect")]
|
||||
public async Task<IActionResult> IntrospectToken([FromForm] string token)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _oauthService.ValidateTokenAsync(token);
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
var accessToken = await _oauthService.GetAccessTokenAsync(token);
|
||||
|
||||
if (accessToken != null)
|
||||
{
|
||||
return Ok(new
|
||||
{
|
||||
active = true,
|
||||
scope = accessToken.Scopes,
|
||||
client_id = accessToken.Client.ClientId,
|
||||
username = accessToken.User.Email,
|
||||
exp = ((DateTimeOffset)accessToken.ExpiresAt).ToUnixTimeSeconds(),
|
||||
iat = ((DateTimeOffset)accessToken.CreatedAt).ToUnixTimeSeconds()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(new { active = false });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "验证令牌时发生错误");
|
||||
return StatusCode(500, new { message = "服务器内部错误" });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,8 @@ namespace FutureMailAPI.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/v1/[controller]")]
|
||||
[Authorize]
|
||||
public class PersonalSpaceController : ControllerBase
|
||||
|
||||
public class PersonalSpaceController : BaseController
|
||||
{
|
||||
private readonly IPersonalSpaceService _personalSpaceService;
|
||||
private readonly ILogger<PersonalSpaceController> _logger;
|
||||
@@ -156,19 +156,5 @@ namespace FutureMailAPI.Controllers
|
||||
return StatusCode(500, ApiResponse<UserProfileResponseDto>.ErrorResult("服务器内部错误"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从当前请求中获取用户ID
|
||||
/// </summary>
|
||||
/// <returns>用户ID</returns>
|
||||
private int GetCurrentUserId()
|
||||
{
|
||||
var userIdClaim = User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier);
|
||||
if (userIdClaim != null && int.TryParse(userIdClaim.Value, out var userId))
|
||||
{
|
||||
return userId;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,8 @@ namespace FutureMailAPI.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/v1/statistics")]
|
||||
[Authorize]
|
||||
public class StatisticsController : ControllerBase
|
||||
|
||||
public class StatisticsController : BaseController
|
||||
{
|
||||
private readonly IPersonalSpaceService _personalSpaceService;
|
||||
private readonly ILogger<StatisticsController> _logger;
|
||||
@@ -50,19 +50,5 @@ namespace FutureMailAPI.Controllers
|
||||
return StatusCode(500, ApiResponse<StatisticsResponseDto>.ErrorResult("服务器内部错误"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从JWT令牌中获取当前用户ID
|
||||
/// </summary>
|
||||
/// <returns>用户ID</returns>
|
||||
private int GetCurrentUserId()
|
||||
{
|
||||
var userIdClaim = User.FindFirst(ClaimTypes.NameIdentifier);
|
||||
if (userIdClaim != null && int.TryParse(userIdClaim.Value, out int userId))
|
||||
{
|
||||
return userId;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,8 @@ namespace FutureMailAPI.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/v1/[controller]")]
|
||||
[Authorize]
|
||||
public class TimeCapsulesController : ControllerBase
|
||||
|
||||
public class TimeCapsulesController : BaseController
|
||||
{
|
||||
private readonly ITimeCapsuleService _timeCapsuleService;
|
||||
private readonly ILogger<TimeCapsulesController> _logger;
|
||||
@@ -20,7 +20,7 @@ namespace FutureMailAPI.Controllers
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<ApiResponse<TimeCapsuleResponseDto>>> CreateTimeCapsule([FromBody] TimeCapsuleCreateDto createDto)
|
||||
public async Task<IActionResult> CreateTimeCapsule([FromBody] TimeCapsuleCreateDto createDto)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
@@ -30,12 +30,12 @@ namespace FutureMailAPI.Controllers
|
||||
// 从JWT令牌中获取当前用户ID
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (currentUserId == null)
|
||||
if (currentUserId <= 0)
|
||||
{
|
||||
return Unauthorized(ApiResponse<TimeCapsuleResponseDto>.ErrorResult("未授权访问"));
|
||||
}
|
||||
|
||||
var result = await _timeCapsuleService.CreateTimeCapsuleAsync(currentUserId.Value, createDto);
|
||||
var result = await _timeCapsuleService.CreateTimeCapsuleAsync(currentUserId, createDto);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
@@ -49,17 +49,17 @@ namespace FutureMailAPI.Controllers
|
||||
}
|
||||
|
||||
[HttpGet("{capsuleId}")]
|
||||
public async Task<ActionResult<ApiResponse<TimeCapsuleResponseDto>>> GetTimeCapsule(int capsuleId)
|
||||
public async Task<IActionResult> GetTimeCapsule(int capsuleId)
|
||||
{
|
||||
// 从JWT令牌中获取当前用户ID
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (currentUserId == null)
|
||||
if (currentUserId <= 0)
|
||||
{
|
||||
return Unauthorized(ApiResponse<TimeCapsuleResponseDto>.ErrorResult("未授权访问"));
|
||||
}
|
||||
|
||||
var result = await _timeCapsuleService.GetTimeCapsuleByIdAsync(currentUserId.Value, capsuleId);
|
||||
var result = await _timeCapsuleService.GetTimeCapsuleByIdAsync(currentUserId, capsuleId);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
@@ -70,23 +70,23 @@ namespace FutureMailAPI.Controllers
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<ApiResponse<PagedResponse<TimeCapsuleResponseDto>>>> GetTimeCapsules([FromQuery] TimeCapsuleListQueryDto queryDto)
|
||||
public async Task<IActionResult> GetTimeCapsules([FromQuery] TimeCapsuleListQueryDto queryDto)
|
||||
{
|
||||
// 从JWT令牌中获取当前用户ID
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (currentUserId == null)
|
||||
if (currentUserId <= 0)
|
||||
{
|
||||
return Unauthorized(ApiResponse<PagedResponse<TimeCapsuleResponseDto>>.ErrorResult("未授权访问"));
|
||||
}
|
||||
|
||||
var result = await _timeCapsuleService.GetTimeCapsulesAsync(currentUserId.Value, queryDto);
|
||||
var result = await _timeCapsuleService.GetTimeCapsulesAsync(currentUserId, queryDto);
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
[HttpPut("{capsuleId}")]
|
||||
public async Task<ActionResult<ApiResponse<TimeCapsuleResponseDto>>> UpdateTimeCapsule(int capsuleId, [FromBody] TimeCapsuleUpdateDto updateDto)
|
||||
public async Task<IActionResult> UpdateTimeCapsule(int capsuleId, [FromBody] TimeCapsuleUpdateDto updateDto)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
@@ -96,12 +96,12 @@ namespace FutureMailAPI.Controllers
|
||||
// 从JWT令牌中获取当前用户ID
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (currentUserId == null)
|
||||
if (currentUserId <= 0)
|
||||
{
|
||||
return Unauthorized(ApiResponse<TimeCapsuleResponseDto>.ErrorResult("未授权访问"));
|
||||
}
|
||||
|
||||
var result = await _timeCapsuleService.UpdateTimeCapsuleAsync(currentUserId.Value, capsuleId, updateDto);
|
||||
var result = await _timeCapsuleService.UpdateTimeCapsuleAsync(currentUserId, capsuleId, updateDto);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
@@ -112,17 +112,17 @@ namespace FutureMailAPI.Controllers
|
||||
}
|
||||
|
||||
[HttpDelete("{capsuleId}")]
|
||||
public async Task<ActionResult<ApiResponse<bool>>> DeleteTimeCapsule(int capsuleId)
|
||||
public async Task<IActionResult> DeleteTimeCapsule(int capsuleId)
|
||||
{
|
||||
// 从JWT令牌中获取当前用户ID
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (currentUserId == null)
|
||||
if (currentUserId <= 0)
|
||||
{
|
||||
return Unauthorized(ApiResponse<bool>.ErrorResult("未授权访问"));
|
||||
}
|
||||
|
||||
var result = await _timeCapsuleService.DeleteTimeCapsuleAsync(currentUserId.Value, capsuleId);
|
||||
var result = await _timeCapsuleService.DeleteTimeCapsuleAsync(currentUserId, capsuleId);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
@@ -134,7 +134,7 @@ namespace FutureMailAPI.Controllers
|
||||
|
||||
[HttpGet("public")]
|
||||
[AllowAnonymous]
|
||||
public async Task<ActionResult<ApiResponse<PagedResponse<TimeCapsuleResponseDto>>>> GetPublicTimeCapsules([FromQuery] TimeCapsuleListQueryDto queryDto)
|
||||
public async Task<IActionResult> GetPublicTimeCapsules([FromQuery] TimeCapsuleListQueryDto queryDto)
|
||||
{
|
||||
var result = await _timeCapsuleService.GetPublicTimeCapsulesAsync(queryDto);
|
||||
|
||||
@@ -142,17 +142,17 @@ namespace FutureMailAPI.Controllers
|
||||
}
|
||||
|
||||
[HttpPost("public/{capsuleId}/claim")]
|
||||
public async Task<ActionResult<ApiResponse<TimeCapsuleResponseDto>>> ClaimPublicCapsule(int capsuleId)
|
||||
public async Task<IActionResult> ClaimPublicCapsule(int capsuleId)
|
||||
{
|
||||
// 从JWT令牌中获取当前用户ID
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (currentUserId == null)
|
||||
if (currentUserId <= 0)
|
||||
{
|
||||
return Unauthorized(ApiResponse<TimeCapsuleResponseDto>.ErrorResult("未授权访问"));
|
||||
}
|
||||
|
||||
var result = await _timeCapsuleService.ClaimPublicCapsuleAsync(currentUserId.Value, capsuleId);
|
||||
var result = await _timeCapsuleService.ClaimPublicCapsuleAsync(currentUserId, capsuleId);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
@@ -163,17 +163,17 @@ namespace FutureMailAPI.Controllers
|
||||
}
|
||||
|
||||
[HttpGet("view")]
|
||||
public async Task<ActionResult<ApiResponse<TimeCapsuleViewResponseDto>>> GetTimeCapsuleView()
|
||||
public async Task<IActionResult> GetTimeCapsuleView()
|
||||
{
|
||||
// 从JWT令牌中获取当前用户ID
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (currentUserId == null)
|
||||
if (currentUserId <= 0)
|
||||
{
|
||||
return Unauthorized(ApiResponse<TimeCapsuleViewResponseDto>.ErrorResult("未授权访问"));
|
||||
}
|
||||
|
||||
var result = await _timeCapsuleService.GetTimeCapsuleViewAsync(currentUserId.Value);
|
||||
var result = await _timeCapsuleService.GetTimeCapsuleViewAsync(currentUserId);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
@@ -184,7 +184,7 @@ namespace FutureMailAPI.Controllers
|
||||
}
|
||||
|
||||
[HttpPut("{capsuleId}/style")]
|
||||
public async Task<ActionResult<ApiResponse<TimeCapsuleResponseDto>>> UpdateTimeCapsuleStyle(int capsuleId, [FromBody] TimeCapsuleStyleUpdateDto updateDto)
|
||||
public async Task<IActionResult> UpdateTimeCapsuleStyle(int capsuleId, [FromBody] TimeCapsuleStyleUpdateDto updateDto)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
@@ -194,12 +194,12 @@ namespace FutureMailAPI.Controllers
|
||||
// 从JWT令牌中获取当前用户ID
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (currentUserId == null)
|
||||
if (currentUserId <= 0)
|
||||
{
|
||||
return Unauthorized(ApiResponse<TimeCapsuleResponseDto>.ErrorResult("未授权访问"));
|
||||
}
|
||||
|
||||
var result = await _timeCapsuleService.UpdateTimeCapsuleStyleAsync(currentUserId.Value, capsuleId, updateDto);
|
||||
var result = await _timeCapsuleService.UpdateTimeCapsuleStyleAsync(currentUserId, capsuleId, updateDto);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
@@ -209,16 +209,5 @@ namespace FutureMailAPI.Controllers
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
private int? GetCurrentUserId()
|
||||
{
|
||||
var userIdClaim = User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier);
|
||||
|
||||
if (userIdClaim == null || !int.TryParse(userIdClaim.Value, out var userId))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return userId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,8 @@ namespace FutureMailAPI.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/v1/timeline")]
|
||||
[Authorize]
|
||||
public class TimelineController : ControllerBase
|
||||
|
||||
public class TimelineController : BaseController
|
||||
{
|
||||
private readonly IPersonalSpaceService _personalSpaceService;
|
||||
private readonly ILogger<TimelineController> _logger;
|
||||
@@ -63,19 +63,5 @@ namespace FutureMailAPI.Controllers
|
||||
return StatusCode(500, ApiResponse<TimelineResponseDto>.ErrorResult("服务器内部错误"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从JWT令牌中获取当前用户ID
|
||||
/// </summary>
|
||||
/// <returns>用户ID</returns>
|
||||
private int GetCurrentUserId()
|
||||
{
|
||||
var userIdClaim = User.FindFirst(ClaimTypes.NameIdentifier);
|
||||
if (userIdClaim != null && int.TryParse(userIdClaim.Value, out int userId))
|
||||
{
|
||||
return userId;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,8 @@ namespace FutureMailAPI.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/v1/upload")]
|
||||
[Authorize]
|
||||
public class UploadController : ControllerBase
|
||||
|
||||
public class UploadController : BaseController
|
||||
{
|
||||
private readonly IFileUploadService _fileUploadService;
|
||||
private readonly ILogger<UploadController> _logger;
|
||||
@@ -97,19 +97,5 @@ namespace FutureMailAPI.Controllers
|
||||
return StatusCode(500, ApiResponse<FileUploadResponseDto>.ErrorResult("服务器内部错误"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从JWT令牌中获取当前用户ID
|
||||
/// </summary>
|
||||
/// <returns>用户ID</returns>
|
||||
private int GetCurrentUserId()
|
||||
{
|
||||
var userIdClaim = User.FindFirst(ClaimTypes.NameIdentifier);
|
||||
if (userIdClaim != null && int.TryParse(userIdClaim.Value, out int userId))
|
||||
{
|
||||
return userId;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,8 @@ namespace FutureMailAPI.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/v1/user")]
|
||||
[Authorize]
|
||||
public class UserController : ControllerBase
|
||||
|
||||
public class UserController : BaseController
|
||||
{
|
||||
private readonly IPersonalSpaceService _personalSpaceService;
|
||||
private readonly ILogger<UserController> _logger;
|
||||
@@ -81,19 +81,5 @@ namespace FutureMailAPI.Controllers
|
||||
return StatusCode(500, ApiResponse<UserProfileResponseDto>.ErrorResult("服务器内部错误"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从JWT令牌中获取当前用户ID
|
||||
/// </summary>
|
||||
/// <returns>用户ID</returns>
|
||||
private int GetCurrentUserId()
|
||||
{
|
||||
var userIdClaim = User.FindFirst(ClaimTypes.NameIdentifier);
|
||||
if (userIdClaim != null && int.TryParse(userIdClaim.Value, out int userId))
|
||||
{
|
||||
return userId;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,8 @@ namespace FutureMailAPI.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/v1/users")]
|
||||
[Authorize]
|
||||
public class UsersController : ControllerBase
|
||||
|
||||
public class UsersController : BaseController
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
private readonly ILogger<UsersController> _logger;
|
||||
@@ -20,12 +20,12 @@ namespace FutureMailAPI.Controllers
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<ActionResult<ApiResponse<UserResponseDto>>> GetUser(int id)
|
||||
public async Task<IActionResult> GetUser(int id)
|
||||
{
|
||||
// 从JWT令牌中获取当前用户ID
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (currentUserId == null)
|
||||
if (currentUserId <= 0)
|
||||
{
|
||||
return Unauthorized(ApiResponse<UserResponseDto>.ErrorResult("未授权访问"));
|
||||
}
|
||||
@@ -47,7 +47,7 @@ namespace FutureMailAPI.Controllers
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public async Task<ActionResult<ApiResponse<UserResponseDto>>> UpdateUser(int id, [FromBody] UserUpdateDto updateDto)
|
||||
public async Task<IActionResult> UpdateUser(int id, [FromBody] UserUpdateDto updateDto)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
@@ -57,7 +57,7 @@ namespace FutureMailAPI.Controllers
|
||||
// 从JWT令牌中获取当前用户ID
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (currentUserId == null)
|
||||
if (currentUserId <= 0)
|
||||
{
|
||||
return Unauthorized(ApiResponse<UserResponseDto>.ErrorResult("未授权访问"));
|
||||
}
|
||||
@@ -79,7 +79,7 @@ namespace FutureMailAPI.Controllers
|
||||
}
|
||||
|
||||
[HttpPost("{id}/change-password")]
|
||||
public async Task<ActionResult<ApiResponse<bool>>> ChangePassword(int id, [FromBody] ChangePasswordDto changePasswordDto)
|
||||
public async Task<IActionResult> ChangePassword(int id, [FromBody] ChangePasswordDto changePasswordDto)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
@@ -89,7 +89,7 @@ namespace FutureMailAPI.Controllers
|
||||
// 从JWT令牌中获取当前用户ID
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (currentUserId == null)
|
||||
if (currentUserId <= 0)
|
||||
{
|
||||
return Unauthorized(ApiResponse<bool>.ErrorResult("未授权访问"));
|
||||
}
|
||||
@@ -109,17 +109,5 @@ namespace FutureMailAPI.Controllers
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
private int? GetCurrentUserId()
|
||||
{
|
||||
var userIdClaim = User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier);
|
||||
|
||||
if (userIdClaim == null || !int.TryParse(userIdClaim.Value, out var userId))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return userId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,9 +52,9 @@ namespace FutureMailAPI.DTOs
|
||||
|
||||
public class AuthResponseDto
|
||||
{
|
||||
public string Token { get; set; } = string.Empty;
|
||||
public string? RefreshToken { get; set; }
|
||||
public DateTime Expires { get; set; }
|
||||
public UserResponseDto User { get; set; } = new();
|
||||
public string Token { get; set; } = string.Empty;
|
||||
public string RefreshToken { get; set; } = string.Empty;
|
||||
public int ExpiresIn { get; set; } = 3600; // 默认1小时过期
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace FutureMailAPI.DTOs
|
||||
{
|
||||
@@ -44,6 +45,82 @@ namespace FutureMailAPI.DTOs
|
||||
public string? Theme { get; set; }
|
||||
}
|
||||
|
||||
// 兼容前端请求格式的DTO
|
||||
public class SentMailCreateCompatDto
|
||||
{
|
||||
[Required(ErrorMessage = "标题是必填项")]
|
||||
[StringLength(200, ErrorMessage = "标题长度不能超过200个字符")]
|
||||
public string title { get; set; } = string.Empty;
|
||||
|
||||
[Required(ErrorMessage = "内容是必填项")]
|
||||
public string content { get; set; } = string.Empty;
|
||||
|
||||
[Required(ErrorMessage = "收件人类型是必填项")]
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public RecipientTypeEnum recipientType { get; set; }
|
||||
|
||||
public string? recipientEmail { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "投递时间是必填项")]
|
||||
public DateTime sendTime { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "触发条件类型是必填项")]
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public TriggerTypeEnum triggerType { get; set; }
|
||||
|
||||
public object? triggerCondition { get; set; }
|
||||
|
||||
public List<object>? attachments { get; set; }
|
||||
|
||||
public bool isEncrypted { get; set; } = false;
|
||||
|
||||
public string capsuleStyle { get; set; } = "default";
|
||||
|
||||
// 转换为内部DTO
|
||||
public SentMailCreateDto ToInternalDto()
|
||||
{
|
||||
var dto = new SentMailCreateDto
|
||||
{
|
||||
Title = this.title,
|
||||
Content = this.content,
|
||||
RecipientType = (int)this.recipientType,
|
||||
RecipientEmail = this.recipientEmail,
|
||||
DeliveryTime = this.sendTime,
|
||||
TriggerType = (int)this.triggerType,
|
||||
IsEncrypted = this.isEncrypted,
|
||||
Theme = this.capsuleStyle
|
||||
};
|
||||
|
||||
// 处理触发条件
|
||||
if (this.triggerCondition != null)
|
||||
{
|
||||
dto.TriggerDetails = System.Text.Json.JsonSerializer.Serialize(this.triggerCondition);
|
||||
}
|
||||
|
||||
// 处理附件
|
||||
if (this.attachments != null && this.attachments.Count > 0)
|
||||
{
|
||||
dto.Attachments = System.Text.Json.JsonSerializer.Serialize(this.attachments);
|
||||
}
|
||||
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
|
||||
public enum RecipientTypeEnum
|
||||
{
|
||||
SELF = 0,
|
||||
SPECIFIC = 1,
|
||||
PUBLIC = 2
|
||||
}
|
||||
|
||||
public enum TriggerTypeEnum
|
||||
{
|
||||
TIME = 0,
|
||||
LOCATION = 1,
|
||||
EVENT = 2
|
||||
}
|
||||
|
||||
public class SentMailUpdateDto
|
||||
{
|
||||
[StringLength(200, ErrorMessage = "标题长度不能超过200个字符")]
|
||||
|
||||
@@ -2,104 +2,42 @@ using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace FutureMailAPI.DTOs
|
||||
{
|
||||
public class OAuthAuthorizationRequestDto
|
||||
public class OAuthLoginRequestDto
|
||||
{
|
||||
[Required]
|
||||
public string ResponseType { get; set; } = "code"; // code for authorization code flow
|
||||
|
||||
[Required]
|
||||
[Required(ErrorMessage = "客户端ID是必填项")]
|
||||
public string ClientId { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
public string RedirectUri { get; set; } = string.Empty;
|
||||
[Required(ErrorMessage = "客户端密钥是必填项")]
|
||||
public string ClientSecret { get; set; } = string.Empty;
|
||||
|
||||
public string Scope { get; set; } = "read write"; // Default scopes
|
||||
[Required(ErrorMessage = "用户名或邮箱是必填项")]
|
||||
public string Username { get; set; } = string.Empty;
|
||||
|
||||
public string State { get; set; } = string.Empty; // CSRF protection
|
||||
}
|
||||
|
||||
public class OAuthTokenRequestDto
|
||||
{
|
||||
[Required]
|
||||
public string GrantType { get; set; } = string.Empty; // authorization_code, refresh_token, client_credentials, password
|
||||
[Required(ErrorMessage = "密码是必填项")]
|
||||
public string Password { get; set; } = string.Empty;
|
||||
|
||||
public string Code { get; set; } = string.Empty; // For authorization_code grant
|
||||
|
||||
public string RefreshToken { get; set; } = string.Empty; // For refresh_token grant
|
||||
|
||||
public string Username { get; set; } = string.Empty; // For password grant
|
||||
|
||||
public string Password { get; set; } = string.Empty; // For password grant
|
||||
|
||||
[Required]
|
||||
public string ClientId { get; set; } = string.Empty;
|
||||
|
||||
public string ClientSecret { get; set; } = string.Empty; // Optional for public clients
|
||||
|
||||
public string RedirectUri { get; set; } = string.Empty; // Required for authorization_code grant
|
||||
|
||||
public string Scope { get; set; } = string.Empty; // Optional, defaults to requested scopes
|
||||
public string? Scope { get; set; }
|
||||
}
|
||||
|
||||
public class OAuthTokenResponseDto
|
||||
{
|
||||
public string AccessToken { get; set; } = string.Empty;
|
||||
public string TokenType { get; set; } = "Bearer";
|
||||
public int ExpiresIn { get; set; } // Seconds until expiration
|
||||
public string RefreshToken { get; set; } = string.Empty;
|
||||
public string Scope { get; set; } = string.Empty;
|
||||
public string TokenType { get; set; } = "Bearer";
|
||||
public int ExpiresIn { get; set; }
|
||||
public string? Scope { get; set; }
|
||||
public UserResponseDto User { get; set; } = new();
|
||||
}
|
||||
|
||||
public class OAuthAuthorizationResponseDto
|
||||
public class OAuthRefreshTokenRequestDto
|
||||
{
|
||||
public string Code { get; set; } = string.Empty;
|
||||
public string State { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class OAuthClientDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string ClientId { get; set; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string[] RedirectUris { get; set; } = Array.Empty<string>();
|
||||
public string[] Scopes { get; set; } = Array.Empty<string>();
|
||||
public bool IsActive { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
}
|
||||
|
||||
public class OAuthClientCreateDto
|
||||
{
|
||||
[Required]
|
||||
[StringLength(100)]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
[Required(ErrorMessage = "刷新令牌是必填项")]
|
||||
public string RefreshToken { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
public string[] RedirectUris { get; set; } = Array.Empty<string>();
|
||||
|
||||
[Required]
|
||||
public string[] Scopes { get; set; } = Array.Empty<string>();
|
||||
}
|
||||
|
||||
public class OAuthClientSecretDto
|
||||
{
|
||||
[Required(ErrorMessage = "客户端ID是必填项")]
|
||||
public string ClientId { get; set; } = string.Empty;
|
||||
|
||||
[Required(ErrorMessage = "客户端密钥是必填项")]
|
||||
public string ClientSecret { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class OAuthLoginDto
|
||||
{
|
||||
[Required]
|
||||
public string UsernameOrEmail { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
public string Password { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
public string ClientId { get; set; } = string.Empty;
|
||||
|
||||
public string ClientSecret { get; set; } = string.Empty; // Optional for public clients
|
||||
|
||||
public string Scope { get; set; } = "read write"; // Default scopes
|
||||
}
|
||||
}
|
||||
@@ -50,9 +50,5 @@ namespace FutureMailAPI.DTOs
|
||||
public string NewPassword { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class RefreshTokenRequestDto
|
||||
{
|
||||
[Required(ErrorMessage = "令牌是必填项")]
|
||||
public string Token { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,9 +14,7 @@ namespace FutureMailAPI.Data
|
||||
public DbSet<ReceivedMail> ReceivedMails { get; set; }
|
||||
public DbSet<TimeCapsule> TimeCapsules { get; set; }
|
||||
public DbSet<OAuthClient> OAuthClients { get; set; }
|
||||
public DbSet<OAuthAuthorizationCode> OAuthAuthorizationCodes { get; set; }
|
||||
public DbSet<OAuthAccessToken> OAuthAccessTokens { get; set; }
|
||||
public DbSet<OAuthRefreshToken> OAuthRefreshTokens { get; set; }
|
||||
public DbSet<OAuthToken> OAuthTokens { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
@@ -86,51 +84,21 @@ namespace FutureMailAPI.Data
|
||||
entity.Property(e => e.UpdatedAt).HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
});
|
||||
|
||||
// 配置OAuthAuthorizationCode实体
|
||||
modelBuilder.Entity<OAuthAuthorizationCode>(entity =>
|
||||
// 配置OAuthToken实体
|
||||
modelBuilder.Entity<OAuthToken>(entity =>
|
||||
{
|
||||
entity.HasOne(e => e.Client)
|
||||
.WithMany()
|
||||
.HasForeignKey(e => e.ClientId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
entity.HasOne(e => e.User)
|
||||
.WithMany()
|
||||
.HasForeignKey(e => e.UserId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
entity.Property(e => e.CreatedAt).HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
});
|
||||
|
||||
// 配置OAuthAccessToken实体
|
||||
modelBuilder.Entity<OAuthAccessToken>(entity =>
|
||||
{
|
||||
entity.HasOne(e => e.Client)
|
||||
.WithMany()
|
||||
.WithMany(c => c.Tokens)
|
||||
.HasForeignKey(e => e.ClientId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
entity.HasOne(e => e.User)
|
||||
.WithMany()
|
||||
.HasForeignKey(e => e.UserId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
entity.Property(e => e.CreatedAt).HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
});
|
||||
|
||||
// 配置OAuthRefreshToken实体
|
||||
modelBuilder.Entity<OAuthRefreshToken>(entity =>
|
||||
{
|
||||
entity.HasOne(e => e.Client)
|
||||
.WithMany()
|
||||
.HasForeignKey(e => e.ClientId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
entity.HasOne(e => e.User)
|
||||
.WithMany()
|
||||
.HasForeignKey(e => e.UserId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
entity.HasIndex(e => e.AccessToken).IsUnique();
|
||||
entity.HasIndex(e => e.RefreshToken).IsUnique();
|
||||
entity.Property(e => e.CreatedAt).HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,46 +1,23 @@
|
||||
using FutureMailAPI.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace FutureMailAPI.Extensions
|
||||
{
|
||||
public static class HttpContextExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取当前用户ID
|
||||
/// 获取当前用户ID(简化版本,不再依赖token)
|
||||
/// </summary>
|
||||
public static int? GetCurrentUserId(this HttpContext context)
|
||||
{
|
||||
if (context.Items.TryGetValue("UserId", out var userIdObj) && userIdObj is int userId)
|
||||
// 简化实现:从查询参数或表单数据中获取用户ID
|
||||
// 在实际应用中,这里应该使用会话或其他认证机制
|
||||
if (context.Request.Query.TryGetValue("userId", out var userIdStr) &&
|
||||
int.TryParse(userIdStr, out var userId))
|
||||
{
|
||||
return userId;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前用户邮箱
|
||||
/// </summary>
|
||||
public static string? GetCurrentUserEmail(this HttpContext context)
|
||||
{
|
||||
if (context.Items.TryGetValue("UserEmail", out var userEmailObj) && userEmailObj is string userEmail)
|
||||
{
|
||||
return userEmail;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前访问令牌
|
||||
/// </summary>
|
||||
public static OAuthAccessToken? GetCurrentAccessToken(this HttpContext context)
|
||||
{
|
||||
if (context.Items.TryGetValue("AccessToken", out var accessTokenObj) && accessTokenObj is OAuthAccessToken accessToken)
|
||||
{
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
138
FutureMailAPI/Filters/OAuthAuthenticationFilter.cs
Normal file
138
FutureMailAPI/Filters/OAuthAuthenticationFilter.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
using FutureMailAPI.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System.Text;
|
||||
using FutureMailAPI.Data;
|
||||
|
||||
namespace FutureMailAPI.Filters
|
||||
{
|
||||
public class OAuthAuthenticationFilter : IAsyncActionFilter
|
||||
{
|
||||
private readonly IOAuthService _oauthService;
|
||||
private readonly ILogger<OAuthAuthenticationFilter> _logger;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly FutureMailDbContext _context;
|
||||
|
||||
public OAuthAuthenticationFilter(IOAuthService oauthService, ILogger<OAuthAuthenticationFilter> logger, IConfiguration configuration, FutureMailDbContext context)
|
||||
{
|
||||
_oauthService = oauthService;
|
||||
_logger = logger;
|
||||
_configuration = configuration;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
|
||||
{
|
||||
// 跳过带有AllowAnonymous特性的操作
|
||||
var endpoint = context.HttpContext.GetEndpoint();
|
||||
if (endpoint?.Metadata?.GetMetadata<Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute>() != null)
|
||||
{
|
||||
await next();
|
||||
return;
|
||||
}
|
||||
|
||||
// 从Authorization头获取令牌
|
||||
var authHeader = context.HttpContext.Request.Headers.Authorization.FirstOrDefault();
|
||||
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer "))
|
||||
{
|
||||
context.Result = new UnauthorizedObjectResult(new { error = "缺少授权令牌" });
|
||||
return;
|
||||
}
|
||||
|
||||
var token = authHeader.Substring("Bearer ".Length).Trim();
|
||||
_logger.LogInformation("正在验证令牌: {Token}", token.Substring(0, Math.Min(50, token.Length)) + "...");
|
||||
|
||||
try
|
||||
{
|
||||
// 首先尝试验证JWT令牌
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
var jwtSettings = _configuration.GetSection("Jwt");
|
||||
var key = Encoding.ASCII.GetBytes(jwtSettings["Key"] ?? throw new InvalidOperationException("JWT密钥未配置"));
|
||||
|
||||
var validationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = new SymmetricSecurityKey(key),
|
||||
ValidateIssuer = true,
|
||||
ValidIssuer = jwtSettings["Issuer"],
|
||||
ValidateAudience = true,
|
||||
ValidAudience = jwtSettings["Audience"],
|
||||
ValidateLifetime = true,
|
||||
ClockSkew = TimeSpan.Zero
|
||||
};
|
||||
|
||||
// 验证JWT令牌
|
||||
var principal = tokenHandler.ValidateToken(token, validationParameters, out SecurityToken validatedToken);
|
||||
|
||||
// 检查令牌类型
|
||||
var tokenType = principal.FindFirst("token_type")?.Value;
|
||||
_logger.LogInformation("令牌类型: {TokenType}", tokenType ?? "普通用户令牌");
|
||||
|
||||
if (tokenType == "oauth")
|
||||
{
|
||||
// OAuth令牌,检查数据库中是否存在
|
||||
var oauthToken = await _oauthService.GetTokenAsync(token);
|
||||
if (oauthToken == null)
|
||||
{
|
||||
_logger.LogWarning("OAuth令牌不存在或已过期");
|
||||
context.Result = new UnauthorizedObjectResult(new { error = "OAuth令牌不存在或已过期" });
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 普通用户令牌,检查用户表中是否有此用户
|
||||
var userId = principal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||
_logger.LogInformation("用户ID: {UserId}", userId);
|
||||
|
||||
if (string.IsNullOrEmpty(userId) || !int.TryParse(userId, out int uid))
|
||||
{
|
||||
_logger.LogWarning("令牌中未包含有效的用户ID");
|
||||
context.Result = new UnauthorizedObjectResult(new { error = "令牌无效" });
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证用户是否存在
|
||||
var user = await _context.Users.FindAsync(uid);
|
||||
if (user == null)
|
||||
{
|
||||
_logger.LogWarning("用户ID {UserId} 不存在", uid);
|
||||
context.Result = new UnauthorizedObjectResult(new { error = "用户不存在" });
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogInformation("用户验证成功: {UserId}", uid);
|
||||
}
|
||||
|
||||
// 设置用户信息
|
||||
context.HttpContext.User = principal;
|
||||
|
||||
await next();
|
||||
}
|
||||
catch (SecurityTokenExpiredException)
|
||||
{
|
||||
_logger.LogWarning("令牌已过期");
|
||||
context.Result = new UnauthorizedObjectResult(new { error = "令牌已过期" });
|
||||
}
|
||||
catch (SecurityTokenInvalidSignatureException)
|
||||
{
|
||||
_logger.LogWarning("令牌签名无效");
|
||||
context.Result = new UnauthorizedObjectResult(new { error = "令牌签名无效" });
|
||||
}
|
||||
catch (SecurityTokenException ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "令牌验证失败");
|
||||
context.Result = new UnauthorizedObjectResult(new { error = "令牌验证失败" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "OAuth认证时发生错误");
|
||||
context.Result = new UnauthorizedObjectResult(new { error = "令牌验证失败" });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -6,9 +6,14 @@
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<NoWarn>$(NoWarn);1591</NoWarn>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
||||
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.9" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.9" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.9">
|
||||
@@ -20,11 +25,13 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.3.0" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="9.0.0" />
|
||||
<PackageReference Include="Quartz" Version="3.15.0" />
|
||||
<PackageReference Include="Quartz.Extensions.Hosting" Version="3.15.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.6" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.14.0" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.3.0" />
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using FutureMailAPI.Models;
|
||||
|
||||
namespace FutureMailAPI.Helpers
|
||||
{
|
||||
public interface IJwtHelper
|
||||
{
|
||||
string GenerateToken(User user);
|
||||
string GenerateToken(int userId, string username, string email);
|
||||
ClaimsPrincipal? ValidateToken(string token);
|
||||
}
|
||||
|
||||
public class JwtHelper : IJwtHelper
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public JwtHelper(IConfiguration configuration)
|
||||
{
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
public string GenerateToken(User user)
|
||||
{
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
var key = Encoding.ASCII.GetBytes(_configuration["Jwt:Key"]!);
|
||||
var tokenDescriptor = new SecurityTokenDescriptor
|
||||
{
|
||||
Subject = new ClaimsIdentity(new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
|
||||
new Claim(ClaimTypes.Name, user.Username),
|
||||
new Claim(ClaimTypes.Email, user.Email)
|
||||
}),
|
||||
Expires = DateTime.UtcNow.AddDays(7),
|
||||
Issuer = _configuration["Jwt:Issuer"],
|
||||
Audience = _configuration["Jwt:Audience"],
|
||||
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
|
||||
};
|
||||
|
||||
var token = tokenHandler.CreateToken(tokenDescriptor);
|
||||
return tokenHandler.WriteToken(token);
|
||||
}
|
||||
|
||||
public string GenerateToken(int userId, string username, string email)
|
||||
{
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
var key = Encoding.ASCII.GetBytes(_configuration["Jwt:Key"]!);
|
||||
var tokenDescriptor = new SecurityTokenDescriptor
|
||||
{
|
||||
Subject = new ClaimsIdentity(new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, userId.ToString()),
|
||||
new Claim(ClaimTypes.Name, username),
|
||||
new Claim(ClaimTypes.Email, email)
|
||||
}),
|
||||
Expires = DateTime.UtcNow.AddDays(7),
|
||||
Issuer = _configuration["Jwt:Issuer"],
|
||||
Audience = _configuration["Jwt:Audience"],
|
||||
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
|
||||
};
|
||||
|
||||
var token = tokenHandler.CreateToken(tokenDescriptor);
|
||||
return tokenHandler.WriteToken(token);
|
||||
}
|
||||
|
||||
public ClaimsPrincipal? ValidateToken(string token)
|
||||
{
|
||||
try
|
||||
{
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
var key = Encoding.ASCII.GetBytes(_configuration["Jwt:Key"]!);
|
||||
|
||||
var validationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = new SymmetricSecurityKey(key),
|
||||
ValidateIssuer = true,
|
||||
ValidIssuer = _configuration["Jwt:Issuer"],
|
||||
ValidateAudience = true,
|
||||
ValidAudience = _configuration["Jwt:Audience"],
|
||||
ValidateLifetime = true,
|
||||
ClockSkew = TimeSpan.Zero
|
||||
};
|
||||
|
||||
var principal = tokenHandler.ValidateToken(token, validationParameters, out SecurityToken validatedToken);
|
||||
return principal;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ namespace FutureMailAPI.Helpers
|
||||
{
|
||||
string HashPassword(string password);
|
||||
bool VerifyPassword(string password, string hash);
|
||||
bool VerifyPassword(string password, string hash, string salt);
|
||||
string HashPassword(string password, string salt);
|
||||
string GenerateSalt();
|
||||
}
|
||||
@@ -59,23 +60,55 @@ namespace FutureMailAPI.Helpers
|
||||
{
|
||||
try
|
||||
{
|
||||
// 从存储的哈希中提取盐值
|
||||
// 检查哈希长度,判断是完整哈希(36字节)还是纯哈希(20字节)
|
||||
byte[] hashBytes = Convert.FromBase64String(hash);
|
||||
byte[] salt = new byte[16];
|
||||
Array.Copy(hashBytes, 0, salt, 0, 16);
|
||||
|
||||
// 使用相同的盐值计算密码的哈希
|
||||
var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000);
|
||||
byte[] computedHash = pbkdf2.GetBytes(20);
|
||||
|
||||
// 比较两个哈希值
|
||||
for (int i = 0; i < 20; i++)
|
||||
if (hashBytes.Length == 36)
|
||||
{
|
||||
if (hashBytes[i + 16] != computedHash[i])
|
||||
return false;
|
||||
// 完整哈希格式:盐值(16字节) + 哈希值(20字节)
|
||||
byte[] salt = new byte[16];
|
||||
Array.Copy(hashBytes, 0, salt, 0, 16);
|
||||
|
||||
// 使用相同的盐值计算密码的哈希
|
||||
var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000);
|
||||
byte[] computedHash = pbkdf2.GetBytes(20);
|
||||
|
||||
// 比较两个哈希值
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
if (hashBytes[i + 16] != computedHash[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
else if (hashBytes.Length == 20)
|
||||
{
|
||||
// 纯哈希格式:只有哈希值(20字节)
|
||||
// 这种情况下,我们需要从数据库中获取盐值
|
||||
// 但由于VerifyPassword方法没有盐值参数,我们无法验证这种格式
|
||||
// 返回false表示验证失败
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 未知的哈希格式
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool VerifyPassword(string password, string hash, string salt)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 使用提供的盐值计算密码的哈希
|
||||
var computedHash = HashPassword(password, salt);
|
||||
return hash == computedHash;
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
using FutureMailAPI.Services;
|
||||
using FutureMailAPI.Models;
|
||||
|
||||
namespace FutureMailAPI.Middleware
|
||||
{
|
||||
public class OAuthAuthenticationMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly ILogger<OAuthAuthenticationMiddleware> _logger;
|
||||
|
||||
public OAuthAuthenticationMiddleware(RequestDelegate next, ILogger<OAuthAuthenticationMiddleware> logger)
|
||||
{
|
||||
_next = next;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task InvokeAsync(HttpContext context, IOAuthService oauthService)
|
||||
{
|
||||
// 检查是否需要OAuth认证
|
||||
var endpoint = context.GetEndpoint();
|
||||
if (endpoint != null)
|
||||
{
|
||||
// 如果端点标记为AllowAnonymous,则跳过认证
|
||||
var allowAnonymousAttribute = endpoint.Metadata.GetMetadata<Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute>();
|
||||
if (allowAnonymousAttribute != null)
|
||||
{
|
||||
await _next(context);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查Authorization头
|
||||
var authHeader = context.Request.Headers.Authorization.FirstOrDefault();
|
||||
if (authHeader != null && authHeader.StartsWith("Bearer "))
|
||||
{
|
||||
var token = authHeader.Substring("Bearer ".Length).Trim();
|
||||
|
||||
// 验证令牌
|
||||
var validationResult = await oauthService.ValidateTokenAsync(token);
|
||||
if (validationResult.Success)
|
||||
{
|
||||
// 获取访问令牌信息
|
||||
var accessToken = await oauthService.GetAccessTokenAsync(token);
|
||||
if (accessToken != null)
|
||||
{
|
||||
// 将用户信息添加到HttpContext
|
||||
context.Items["UserId"] = accessToken.UserId;
|
||||
context.Items["UserEmail"] = accessToken.User.Email;
|
||||
context.Items["AccessToken"] = accessToken;
|
||||
|
||||
await _next(context);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有有效的令牌,返回401未授权
|
||||
context.Response.StatusCode = 401;
|
||||
await context.Response.WriteAsync("未授权访问");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,559 +0,0 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using FutureMailAPI.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace FutureMailAPI.Migrations
|
||||
{
|
||||
[DbContext(typeof(FutureMailDbContext))]
|
||||
[Migration("20251016011551_AddOAuthEntities")]
|
||||
partial class AddOAuthEntities
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "9.0.9");
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.OAuthAccessToken", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsRevoked")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Scopes")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Token")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("OAuthAccessTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.OAuthAuthorizationCode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsUsed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("RedirectUri")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Scopes")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("OAuthAuthorizationCodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.OAuthClient", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ClientSecret")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RedirectUris")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Scopes")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("OAuthClients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.OAuthRefreshToken", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsUsed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Token")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("OAuthRefreshTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.ReceivedMail", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsRead")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsReplied")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("ReadAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("ReceivedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
|
||||
b.Property<int>("RecipientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("RecipientId1")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("ReplyMailId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SentMailId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RecipientId");
|
||||
|
||||
b.HasIndex("RecipientId1");
|
||||
|
||||
b.HasIndex("SentMailId");
|
||||
|
||||
b.ToTable("ReceivedMails");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.SentMail", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Attachments")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Content")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("DeliveryTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("EncryptionKey")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsEncrypted")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("RecipientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("RecipientId1")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("RecipientType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SenderId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("SentAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Theme")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TriggerDetails")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("TriggerType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RecipientId");
|
||||
|
||||
b.HasIndex("RecipientId1");
|
||||
|
||||
b.HasIndex("SenderId");
|
||||
|
||||
b.ToTable("SentMails");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.TimeCapsule", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Color")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
|
||||
b.Property<double>("Opacity")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double>("PositionX")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double>("PositionY")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double>("PositionZ")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double>("Rotation")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("SentMailId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SentMailId1")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("Size")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SentMailId");
|
||||
|
||||
b.HasIndex("SentMailId1");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("TimeCapsules");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.User", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Avatar")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("LastLoginAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Nickname")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PreferredBackground")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PreferredScene")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Email")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("Username")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.OAuthAccessToken", b =>
|
||||
{
|
||||
b.HasOne("FutureMailAPI.Models.OAuthClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FutureMailAPI.Models.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.OAuthAuthorizationCode", b =>
|
||||
{
|
||||
b.HasOne("FutureMailAPI.Models.OAuthClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FutureMailAPI.Models.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.OAuthRefreshToken", b =>
|
||||
{
|
||||
b.HasOne("FutureMailAPI.Models.OAuthClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FutureMailAPI.Models.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.ReceivedMail", b =>
|
||||
{
|
||||
b.HasOne("FutureMailAPI.Models.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RecipientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FutureMailAPI.Models.User", "Recipient")
|
||||
.WithMany("ReceivedMails")
|
||||
.HasForeignKey("RecipientId1")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FutureMailAPI.Models.SentMail", "SentMail")
|
||||
.WithMany()
|
||||
.HasForeignKey("SentMailId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Recipient");
|
||||
|
||||
b.Navigation("SentMail");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.SentMail", b =>
|
||||
{
|
||||
b.HasOne("FutureMailAPI.Models.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RecipientId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("FutureMailAPI.Models.User", "Recipient")
|
||||
.WithMany()
|
||||
.HasForeignKey("RecipientId1");
|
||||
|
||||
b.HasOne("FutureMailAPI.Models.User", "Sender")
|
||||
.WithMany("SentMails")
|
||||
.HasForeignKey("SenderId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Recipient");
|
||||
|
||||
b.Navigation("Sender");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.TimeCapsule", b =>
|
||||
{
|
||||
b.HasOne("FutureMailAPI.Models.SentMail", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("SentMailId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FutureMailAPI.Models.SentMail", "SentMail")
|
||||
.WithMany()
|
||||
.HasForeignKey("SentMailId1")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FutureMailAPI.Models.User", "User")
|
||||
.WithMany("TimeCapsules")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("SentMail");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.User", b =>
|
||||
{
|
||||
b.Navigation("ReceivedMails");
|
||||
|
||||
b.Navigation("SentMails");
|
||||
|
||||
b.Navigation("TimeCapsules");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace FutureMailAPI.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddOAuthEntities : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "OAuthClients",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
ClientId = table.Column<string>(type: "TEXT", nullable: false),
|
||||
ClientSecret = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Name = table.Column<string>(type: "TEXT", nullable: false),
|
||||
RedirectUris = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Scopes = table.Column<string>(type: "TEXT", nullable: false),
|
||||
IsActive = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP"),
|
||||
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_OAuthClients", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "OAuthAccessTokens",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Token = table.Column<string>(type: "TEXT", nullable: false),
|
||||
ClientId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
UserId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Scopes = table.Column<string>(type: "TEXT", nullable: false),
|
||||
IsRevoked = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
ExpiresAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_OAuthAccessTokens", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_OAuthAccessTokens_OAuthClients_ClientId",
|
||||
column: x => x.ClientId,
|
||||
principalTable: "OAuthClients",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_OAuthAccessTokens_Users_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "OAuthAuthorizationCodes",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Code = table.Column<string>(type: "TEXT", nullable: false),
|
||||
ClientId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
UserId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
RedirectUri = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Scopes = table.Column<string>(type: "TEXT", nullable: false),
|
||||
IsUsed = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
ExpiresAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_OAuthAuthorizationCodes", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_OAuthAuthorizationCodes_OAuthClients_ClientId",
|
||||
column: x => x.ClientId,
|
||||
principalTable: "OAuthClients",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_OAuthAuthorizationCodes_Users_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "OAuthRefreshTokens",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Token = table.Column<string>(type: "TEXT", nullable: false),
|
||||
ClientId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
UserId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
IsUsed = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
ExpiresAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_OAuthRefreshTokens", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_OAuthRefreshTokens_OAuthClients_ClientId",
|
||||
column: x => x.ClientId,
|
||||
principalTable: "OAuthClients",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_OAuthRefreshTokens_Users_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OAuthAccessTokens_ClientId",
|
||||
table: "OAuthAccessTokens",
|
||||
column: "ClientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OAuthAccessTokens_UserId",
|
||||
table: "OAuthAccessTokens",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OAuthAuthorizationCodes_ClientId",
|
||||
table: "OAuthAuthorizationCodes",
|
||||
column: "ClientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OAuthAuthorizationCodes_UserId",
|
||||
table: "OAuthAuthorizationCodes",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OAuthClients_ClientId",
|
||||
table: "OAuthClients",
|
||||
column: "ClientId",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OAuthRefreshTokens_ClientId",
|
||||
table: "OAuthRefreshTokens",
|
||||
column: "ClientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OAuthRefreshTokens_UserId",
|
||||
table: "OAuthRefreshTokens",
|
||||
column: "UserId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "OAuthAccessTokens");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "OAuthAuthorizationCodes");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "OAuthRefreshTokens");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "OAuthClients");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,564 +0,0 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using FutureMailAPI.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace FutureMailAPI.Migrations
|
||||
{
|
||||
[DbContext(typeof(FutureMailDbContext))]
|
||||
[Migration("20251016012504_AddSaltToUser")]
|
||||
partial class AddSaltToUser
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "9.0.9");
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.OAuthAccessToken", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsRevoked")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Scopes")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Token")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("OAuthAccessTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.OAuthAuthorizationCode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsUsed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("RedirectUri")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Scopes")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("OAuthAuthorizationCodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.OAuthClient", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ClientSecret")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RedirectUris")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Scopes")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("OAuthClients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.OAuthRefreshToken", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsUsed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Token")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("OAuthRefreshTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.ReceivedMail", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsRead")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsReplied")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("ReadAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("ReceivedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
|
||||
b.Property<int>("RecipientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("RecipientId1")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("ReplyMailId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SentMailId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RecipientId");
|
||||
|
||||
b.HasIndex("RecipientId1");
|
||||
|
||||
b.HasIndex("SentMailId");
|
||||
|
||||
b.ToTable("ReceivedMails");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.SentMail", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Attachments")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Content")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("DeliveryTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("EncryptionKey")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsEncrypted")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("RecipientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("RecipientId1")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("RecipientType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SenderId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("SentAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Theme")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TriggerDetails")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("TriggerType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RecipientId");
|
||||
|
||||
b.HasIndex("RecipientId1");
|
||||
|
||||
b.HasIndex("SenderId");
|
||||
|
||||
b.ToTable("SentMails");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.TimeCapsule", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Color")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
|
||||
b.Property<double>("Opacity")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double>("PositionX")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double>("PositionY")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double>("PositionZ")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double>("Rotation")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("SentMailId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SentMailId1")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("Size")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SentMailId");
|
||||
|
||||
b.HasIndex("SentMailId1");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("TimeCapsules");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.User", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Avatar")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("LastLoginAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Nickname")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PreferredBackground")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PreferredScene")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Salt")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Email")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("Username")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.OAuthAccessToken", b =>
|
||||
{
|
||||
b.HasOne("FutureMailAPI.Models.OAuthClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FutureMailAPI.Models.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.OAuthAuthorizationCode", b =>
|
||||
{
|
||||
b.HasOne("FutureMailAPI.Models.OAuthClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FutureMailAPI.Models.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.OAuthRefreshToken", b =>
|
||||
{
|
||||
b.HasOne("FutureMailAPI.Models.OAuthClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FutureMailAPI.Models.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.ReceivedMail", b =>
|
||||
{
|
||||
b.HasOne("FutureMailAPI.Models.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RecipientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FutureMailAPI.Models.User", "Recipient")
|
||||
.WithMany("ReceivedMails")
|
||||
.HasForeignKey("RecipientId1")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FutureMailAPI.Models.SentMail", "SentMail")
|
||||
.WithMany()
|
||||
.HasForeignKey("SentMailId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Recipient");
|
||||
|
||||
b.Navigation("SentMail");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.SentMail", b =>
|
||||
{
|
||||
b.HasOne("FutureMailAPI.Models.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RecipientId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("FutureMailAPI.Models.User", "Recipient")
|
||||
.WithMany()
|
||||
.HasForeignKey("RecipientId1");
|
||||
|
||||
b.HasOne("FutureMailAPI.Models.User", "Sender")
|
||||
.WithMany("SentMails")
|
||||
.HasForeignKey("SenderId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Recipient");
|
||||
|
||||
b.Navigation("Sender");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.TimeCapsule", b =>
|
||||
{
|
||||
b.HasOne("FutureMailAPI.Models.SentMail", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("SentMailId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FutureMailAPI.Models.SentMail", "SentMail")
|
||||
.WithMany()
|
||||
.HasForeignKey("SentMailId1")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FutureMailAPI.Models.User", "User")
|
||||
.WithMany("TimeCapsules")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("SentMail");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.User", b =>
|
||||
{
|
||||
b.Navigation("ReceivedMails");
|
||||
|
||||
b.Navigation("SentMails");
|
||||
|
||||
b.Navigation("TimeCapsules");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,90 +17,6 @@ namespace FutureMailAPI.Migrations
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "9.0.9");
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.OAuthAccessToken", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsRevoked")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Scopes")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Token")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("OAuthAccessTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.OAuthAuthorizationCode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsUsed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("RedirectUri")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Scopes")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("OAuthAuthorizationCodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.OAuthClient", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -109,10 +25,12 @@ namespace FutureMailAPI.Migrations
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ClientSecret")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
@@ -125,6 +43,7 @@ namespace FutureMailAPI.Migrations
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RedirectUris")
|
||||
@@ -148,12 +67,17 @@ namespace FutureMailAPI.Migrations
|
||||
b.ToTable("OAuthClients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.OAuthRefreshToken", b =>
|
||||
modelBuilder.Entity("FutureMailAPI.Models.OAuthToken", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("AccessToken")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@@ -165,11 +89,22 @@ namespace FutureMailAPI.Migrations
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsUsed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Token")
|
||||
b.Property<string>("RefreshToken")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("RevokedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Scope")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TokenType")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
@@ -177,11 +112,17 @@ namespace FutureMailAPI.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AccessToken")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("RefreshToken")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("OAuthRefreshTokens");
|
||||
b.ToTable("OAuthTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.ReceivedMail", b =>
|
||||
@@ -397,6 +338,13 @@ namespace FutureMailAPI.Migrations
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RefreshToken")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("RefreshTokenExpiryTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Salt")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
@@ -418,48 +366,10 @@ namespace FutureMailAPI.Migrations
|
||||
b.ToTable("Users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.OAuthAccessToken", b =>
|
||||
modelBuilder.Entity("FutureMailAPI.Models.OAuthToken", b =>
|
||||
{
|
||||
b.HasOne("FutureMailAPI.Models.OAuthClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FutureMailAPI.Models.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.OAuthAuthorizationCode", b =>
|
||||
{
|
||||
b.HasOne("FutureMailAPI.Models.OAuthClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FutureMailAPI.Models.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.OAuthRefreshToken", b =>
|
||||
{
|
||||
b.HasOne("FutureMailAPI.Models.OAuthClient", "Client")
|
||||
.WithMany()
|
||||
.WithMany("Tokens")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
@@ -547,6 +457,11 @@ namespace FutureMailAPI.Migrations
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.OAuthClient", b =>
|
||||
{
|
||||
b.Navigation("Tokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FutureMailAPI.Models.User", b =>
|
||||
{
|
||||
b.Navigation("ReceivedMails");
|
||||
|
||||
38
FutureMailAPI/Models/OAuthClient.cs
Normal file
38
FutureMailAPI/Models/OAuthClient.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace FutureMailAPI.Models
|
||||
{
|
||||
public class OAuthClient
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(100)]
|
||||
public string ClientId { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string ClientSecret { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
[MaxLength(100)]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
public string RedirectUris { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
public string Scopes { get; set; } = string.Empty;
|
||||
|
||||
public bool IsActive { get; set; } = true;
|
||||
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
// 导航属性
|
||||
public virtual ICollection<OAuthToken> Tokens { get; set; } = new List<OAuthToken>();
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
namespace FutureMailAPI.Models
|
||||
{
|
||||
public class OAuthClient
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string ClientId { get; set; } = string.Empty;
|
||||
public string ClientSecret { get; set; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string RedirectUris { get; set; } = string.Empty; // JSON array
|
||||
public string Scopes { get; set; } = string.Empty; // JSON array
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public class OAuthAuthorizationCode
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Code { get; set; } = string.Empty;
|
||||
public int ClientId { get; set; }
|
||||
public int UserId { get; set; }
|
||||
public string RedirectUri { get; set; } = string.Empty;
|
||||
public string Scopes { get; set; } = string.Empty;
|
||||
public bool IsUsed { get; set; } = false;
|
||||
public DateTime ExpiresAt { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
// Navigation properties
|
||||
public virtual OAuthClient Client { get; set; } = null!;
|
||||
public virtual User User { get; set; } = null!;
|
||||
}
|
||||
|
||||
public class OAuthRefreshToken
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Token { get; set; } = string.Empty;
|
||||
public int ClientId { get; set; }
|
||||
public int UserId { get; set; }
|
||||
public bool IsUsed { get; set; } = false;
|
||||
public DateTime ExpiresAt { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
// Navigation properties
|
||||
public virtual OAuthClient Client { get; set; } = null!;
|
||||
public virtual User User { get; set; } = null!;
|
||||
}
|
||||
|
||||
public class OAuthAccessToken
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Token { get; set; } = string.Empty;
|
||||
public int ClientId { get; set; }
|
||||
public int UserId { get; set; }
|
||||
public string Scopes { get; set; } = string.Empty;
|
||||
public bool IsRevoked { get; set; } = false;
|
||||
public DateTime ExpiresAt { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
// Navigation properties
|
||||
public virtual OAuthClient Client { get; set; } = null!;
|
||||
public virtual User User { get; set; } = null!;
|
||||
}
|
||||
}
|
||||
44
FutureMailAPI/Models/OAuthToken.cs
Normal file
44
FutureMailAPI/Models/OAuthToken.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace FutureMailAPI.Models
|
||||
{
|
||||
public class OAuthToken
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string AccessToken { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string RefreshToken { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
public string TokenType { get; set; } = "Bearer";
|
||||
|
||||
public DateTime ExpiresAt { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
|
||||
public DateTime? RevokedAt { get; set; }
|
||||
|
||||
public string? Scope { get; set; }
|
||||
|
||||
// 外键
|
||||
public int UserId { get; set; }
|
||||
|
||||
public int ClientId { get; set; }
|
||||
|
||||
// 导航属性
|
||||
[ForeignKey("UserId")]
|
||||
public virtual User User { get; set; } = null!;
|
||||
|
||||
[ForeignKey("ClientId")]
|
||||
public virtual OAuthClient Client { get; set; } = null!;
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,12 @@ namespace FutureMailAPI.Models
|
||||
[MaxLength(50)]
|
||||
public string? PreferredBackground { get; set; } = "default";
|
||||
|
||||
[MaxLength(500)]
|
||||
public string? RefreshToken { get; set; }
|
||||
|
||||
public DateTime? RefreshTokenExpiryTime { get; set; }
|
||||
|
||||
|
||||
// 导航属性
|
||||
public virtual ICollection<SentMail> SentMails { get; set; } = new List<SentMail>();
|
||||
public virtual ICollection<ReceivedMail> ReceivedMails { get; set; } = new List<ReceivedMail>();
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
// 测试OAuth 2.0密码授权流程
|
||||
// 1. 使用密码授权获取访问令牌
|
||||
POST http://localhost:5001/api/v1/oauth/token
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
grant_type=password&username=testuser3&password=password123&client_id=futuremail-client&client_secret=futuremail-secret
|
||||
|
||||
###
|
||||
|
||||
// 2. 使用访问令牌访问受保护的API
|
||||
GET http://localhost:5001/api/v1/mails
|
||||
Authorization: Bearer YOUR_ACCESS_TOKEN
|
||||
@@ -1,37 +0,0 @@
|
||||
// 测试OAuth 2.0认证流程
|
||||
// 1. 创建OAuth客户端
|
||||
POST http://localhost:5001/api/v1/oauth/clients
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"clientName": "TestClient",
|
||||
"redirectUris": ["http://localhost:3000/callback"],
|
||||
"scopes": ["read", "write"]
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
// 2. 获取授权码(在浏览器中访问以下URL)
|
||||
// http://localhost:5001/api/v1/oauth/authorize?response_type=code&client_id=test_client&redirect_uri=http://localhost:3000/callback&scope=read&state=xyz
|
||||
|
||||
###
|
||||
|
||||
// 3. 使用授权码获取访问令牌
|
||||
POST http://localhost:5001/api/v1/oauth/token
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
grant_type=authorization_code&code=YOUR_AUTHORIZATION_CODE&redirect_uri=http://localhost:3000/callback&client_id=test_client&client_secret=YOUR_CLIENT_SECRET
|
||||
|
||||
###
|
||||
|
||||
// 4. 使用访问令牌访问受保护的API
|
||||
GET http://localhost:5001/api/v1/mails
|
||||
Authorization: Bearer YOUR_ACCESS_TOKEN
|
||||
|
||||
###
|
||||
|
||||
// 5. 刷新访问令牌
|
||||
POST http://localhost:5001/api/v1/oauth/token
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
grant_type=refresh_token&refresh_token=YOUR_REFRESH_TOKEN&client_id=test_client&client_secret=YOUR_CLIENT_SECRET
|
||||
@@ -1,18 +1,20 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Quartz;
|
||||
using FutureMailAPI.Data;
|
||||
using FutureMailAPI.Helpers;
|
||||
using FutureMailAPI.Services;
|
||||
using FutureMailAPI.Middleware;
|
||||
using FutureMailAPI.Extensions;
|
||||
using FutureMailAPI.Filters;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using System.Text;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// 配置服务器监听所有网络接口的5001端口
|
||||
// 配置服务器监听所有网络接口的5003端口
|
||||
builder.WebHost.ConfigureKestrel(options =>
|
||||
{
|
||||
options.ListenAnyIP(5001);
|
||||
options.ListenAnyIP(5003);
|
||||
});
|
||||
|
||||
// 配置数据库连接
|
||||
@@ -20,9 +22,6 @@ var connectionString = builder.Configuration.GetConnectionString("DefaultConnect
|
||||
builder.Services.AddDbContext<FutureMailDbContext>(options =>
|
||||
options.UseSqlite(connectionString));
|
||||
|
||||
// 配置OAuth 2.0认证
|
||||
// 注意:我们使用自定义中间件实现OAuth 2.0认证,而不是使用内置的JWT认证
|
||||
|
||||
// 配置Swagger/OpenAPI
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen(c =>
|
||||
@@ -40,13 +39,42 @@ builder.Services.AddScoped<IUserService, UserService>();
|
||||
builder.Services.AddScoped<IMailService, MailService>();
|
||||
builder.Services.AddScoped<ITimeCapsuleService, TimeCapsuleService>();
|
||||
builder.Services.AddScoped<IAuthService, AuthService>();
|
||||
builder.Services.AddScoped<IOAuthService, OAuthService>();
|
||||
builder.Services.AddScoped<IAIAssistantService, AIAssistantService>();
|
||||
builder.Services.AddScoped<IPersonalSpaceService, PersonalSpaceService>();
|
||||
builder.Services.AddScoped<IFileUploadService, FileUploadService>();
|
||||
builder.Services.AddScoped<INotificationService, NotificationService>();
|
||||
builder.Services.AddScoped<IOAuthService, OAuthService>();
|
||||
|
||||
builder.Services.AddScoped<IInitializationService, InitializationService>();
|
||||
|
||||
// 添加JWT认证
|
||||
var jwtSettings = builder.Configuration.GetSection("Jwt");
|
||||
var key = Encoding.ASCII.GetBytes(jwtSettings["Key"] ?? throw new InvalidOperationException("JWT密钥未配置"));
|
||||
|
||||
builder.Services.AddAuthentication(options =>
|
||||
{
|
||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
})
|
||||
.AddJwtBearer(options =>
|
||||
{
|
||||
options.RequireHttpsMetadata = false;
|
||||
options.SaveToken = true;
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = new SymmetricSecurityKey(key),
|
||||
ValidateIssuer = true,
|
||||
ValidIssuer = jwtSettings["Issuer"],
|
||||
ValidateAudience = true,
|
||||
ValidAudience = jwtSettings["Audience"],
|
||||
ValidateLifetime = true,
|
||||
ClockSkew = TimeSpan.Zero
|
||||
};
|
||||
});
|
||||
|
||||
builder.Services.AddAuthorization();
|
||||
|
||||
// 配置Quartz任务调度
|
||||
builder.Services.AddQuartz(q =>
|
||||
{
|
||||
@@ -64,8 +92,11 @@ builder.Services.AddQuartz(q =>
|
||||
// 添加Quartz主机服务
|
||||
builder.Services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);
|
||||
|
||||
// 添加控制器
|
||||
builder.Services.AddControllers();
|
||||
// 添加控制器并注册OAuth认证过滤器为全局过滤器
|
||||
builder.Services.AddControllers(options =>
|
||||
{
|
||||
options.Filters.Add<OAuthAuthenticationFilter>();
|
||||
});
|
||||
|
||||
// 添加CORS
|
||||
builder.Services.AddCors(options =>
|
||||
@@ -106,10 +137,7 @@ app.UseStaticFiles(new StaticFileOptions
|
||||
|
||||
app.UseCors("AllowAll");
|
||||
|
||||
// 添加OAuth 2.0认证中间件
|
||||
app.UseMiddleware<OAuthAuthenticationMiddleware>();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": false,
|
||||
"applicationUrl": "http://0.0.0.0:5001",
|
||||
"applicationUrl": "http://0.0.0.0:5003",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": false,
|
||||
"applicationUrl": "https://localhost:7236;http://0.0.0.0:5001",
|
||||
"applicationUrl": "https://localhost:7236;http://0.0.0.0:5003",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
using FutureMailAPI.Helpers;
|
||||
using FutureMailAPI.DTOs;
|
||||
using FutureMailAPI.Models;
|
||||
using FutureMailAPI.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace FutureMailAPI.Services
|
||||
{
|
||||
@@ -8,86 +15,73 @@ namespace FutureMailAPI.Services
|
||||
{
|
||||
Task<ApiResponse<AuthResponseDto>> LoginAsync(UserLoginDto loginDto);
|
||||
Task<ApiResponse<AuthResponseDto>> RegisterAsync(UserRegisterDto registerDto);
|
||||
Task<ApiResponse<bool>> ValidateTokenAsync(string token);
|
||||
Task<ApiResponse<string>> RefreshTokenAsync(string token);
|
||||
Task<ApiResponse<AuthResponseDto>> LoginWithOAuthAsync(UserLoginDto loginDto, string clientId, string clientSecret);
|
||||
}
|
||||
|
||||
public class AuthService : IAuthService
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
private readonly IPasswordHelper _passwordHelper;
|
||||
private readonly IOAuthService _oauthService;
|
||||
private readonly FutureMailDbContext _context;
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public AuthService(
|
||||
IUserService userService,
|
||||
IPasswordHelper passwordHelper,
|
||||
IOAuthService oauthService)
|
||||
FutureMailDbContext context,
|
||||
IConfiguration configuration)
|
||||
{
|
||||
_userService = userService;
|
||||
_passwordHelper = passwordHelper;
|
||||
_oauthService = oauthService;
|
||||
_context = context;
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
public async Task<ApiResponse<AuthResponseDto>> LoginAsync(UserLoginDto loginDto)
|
||||
{
|
||||
// 使用默认客户端ID和密钥进行OAuth登录
|
||||
// 在实际应用中,这些应该从配置中获取
|
||||
var defaultClientId = "futuremail_default_client";
|
||||
var defaultClientSecret = "futuremail_default_secret";
|
||||
|
||||
return await LoginWithOAuthAsync(loginDto, defaultClientId, defaultClientSecret);
|
||||
}
|
||||
|
||||
public async Task<ApiResponse<AuthResponseDto>> LoginWithOAuthAsync(UserLoginDto loginDto, string clientId, string clientSecret)
|
||||
{
|
||||
// 创建OAuth登录请求
|
||||
var oauthLoginDto = new OAuthLoginDto
|
||||
{
|
||||
UsernameOrEmail = loginDto.UsernameOrEmail,
|
||||
Password = loginDto.Password,
|
||||
ClientId = clientId,
|
||||
ClientSecret = clientSecret,
|
||||
Scope = "read write" // 默认权限范围
|
||||
};
|
||||
|
||||
// 使用OAuth服务进行登录
|
||||
var oauthResult = await _oauthService.LoginAsync(oauthLoginDto);
|
||||
|
||||
if (!oauthResult.Success)
|
||||
{
|
||||
return ApiResponse<AuthResponseDto>.ErrorResult(oauthResult.Message ?? "登录失败");
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
var userResult = await _userService.GetUserByUsernameOrEmailAsync(loginDto.UsernameOrEmail);
|
||||
|
||||
if (!userResult.Success || userResult.Data == null)
|
||||
{
|
||||
return ApiResponse<AuthResponseDto>.ErrorResult("获取用户信息失败");
|
||||
return ApiResponse<AuthResponseDto>.ErrorResult("用户名或密码错误");
|
||||
}
|
||||
|
||||
var user = userResult.Data;
|
||||
var userDto = userResult.Data;
|
||||
|
||||
// 创建用户响应DTO
|
||||
var userResponse = new UserResponseDto
|
||||
// 获取原始用户信息用于密码验证
|
||||
var user = await _context.Users
|
||||
.FirstOrDefaultAsync(u => u.Id == userDto.Id);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
Id = user.Id,
|
||||
Username = user.Username,
|
||||
Email = user.Email,
|
||||
Nickname = user.Nickname,
|
||||
Avatar = user.Avatar,
|
||||
CreatedAt = user.CreatedAt,
|
||||
LastLoginAt = DateTime.UtcNow
|
||||
};
|
||||
return ApiResponse<AuthResponseDto>.ErrorResult("用户不存在");
|
||||
}
|
||||
|
||||
// 创建认证响应DTO,使用OAuth令牌
|
||||
// 验证密码
|
||||
if (!_passwordHelper.VerifyPassword(loginDto.Password, user.PasswordHash, user.Salt))
|
||||
{
|
||||
return ApiResponse<AuthResponseDto>.ErrorResult("用户名或密码错误");
|
||||
}
|
||||
|
||||
// 更新用户响应DTO
|
||||
userDto.LastLoginAt = DateTime.UtcNow;
|
||||
|
||||
// 生成JWT令牌
|
||||
var token = GenerateJwtToken(user);
|
||||
var refreshToken = GenerateRefreshToken();
|
||||
|
||||
// 保存刷新令牌到用户表
|
||||
user.RefreshToken = refreshToken;
|
||||
user.RefreshTokenExpiryTime = DateTime.UtcNow.AddDays(7); // 刷新令牌7天过期
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
// 创建认证响应DTO
|
||||
var authResponse = new AuthResponseDto
|
||||
{
|
||||
Token = oauthResult.Data.AccessToken,
|
||||
RefreshToken = oauthResult.Data.RefreshToken,
|
||||
Expires = DateTime.UtcNow.AddSeconds(oauthResult.Data.ExpiresIn),
|
||||
User = userResponse
|
||||
User = userDto,
|
||||
Token = token,
|
||||
RefreshToken = refreshToken,
|
||||
ExpiresIn = 3600 // 1小时,单位秒
|
||||
};
|
||||
|
||||
return ApiResponse<AuthResponseDto>.SuccessResult(authResponse, "登录成功");
|
||||
@@ -119,7 +113,7 @@ namespace FutureMailAPI.Services
|
||||
return ApiResponse<AuthResponseDto>.ErrorResult(createUserResult.Message ?? "注册失败");
|
||||
}
|
||||
|
||||
// 注册成功后,自动使用OAuth登录
|
||||
// 注册成功后,自动登录
|
||||
var loginDto = new UserLoginDto
|
||||
{
|
||||
UsernameOrEmail = registerDto.Username,
|
||||
@@ -129,40 +123,35 @@ namespace FutureMailAPI.Services
|
||||
return await LoginAsync(loginDto);
|
||||
}
|
||||
|
||||
public async Task<ApiResponse<bool>> ValidateTokenAsync(string token)
|
||||
private string GenerateJwtToken(User user)
|
||||
{
|
||||
// 注意:在OAuth 2.0中,令牌验证应该由OAuth中间件处理
|
||||
// 这里我们暂时返回成功,实际使用时应该通过OAuth 2.0的令牌验证流程
|
||||
return ApiResponse<bool>.SuccessResult(true);
|
||||
}
|
||||
|
||||
public async Task<ApiResponse<string>> RefreshTokenAsync(string token)
|
||||
{
|
||||
// 在OAuth 2.0中,刷新令牌需要客户端ID和密钥
|
||||
// 这里我们使用默认客户端凭据
|
||||
var defaultClientId = "futuremail_default_client";
|
||||
var defaultClientSecret = "futuremail_default_secret";
|
||||
|
||||
// 创建OAuth刷新令牌请求
|
||||
var oauthTokenRequest = new OAuthTokenRequestDto
|
||||
var jwtSettings = _configuration.GetSection("Jwt");
|
||||
var key = Encoding.ASCII.GetBytes(jwtSettings["Key"] ?? throw new InvalidOperationException("JWT密钥未配置"));
|
||||
var tokenDescriptor = new SecurityTokenDescriptor
|
||||
{
|
||||
ClientId = defaultClientId,
|
||||
ClientSecret = defaultClientSecret,
|
||||
RefreshToken = token,
|
||||
GrantType = "refresh_token",
|
||||
Scope = "read write"
|
||||
Subject = new ClaimsIdentity(new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
|
||||
new Claim(ClaimTypes.Name, user.Username),
|
||||
new Claim(ClaimTypes.Email, user.Email)
|
||||
}),
|
||||
Expires = DateTime.UtcNow.AddHours(1),
|
||||
Issuer = jwtSettings["Issuer"],
|
||||
Audience = jwtSettings["Audience"],
|
||||
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
|
||||
};
|
||||
|
||||
// 使用OAuth服务刷新令牌
|
||||
var oauthResult = await _oauthService.RefreshTokenAsync(oauthTokenRequest);
|
||||
|
||||
if (!oauthResult.Success)
|
||||
{
|
||||
return ApiResponse<string>.ErrorResult(oauthResult.Message ?? "刷新令牌失败");
|
||||
}
|
||||
|
||||
// 返回新的访问令牌
|
||||
return ApiResponse<string>.SuccessResult(oauthResult.Data.AccessToken, "令牌刷新成功");
|
||||
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
var token = tokenHandler.CreateToken(tokenDescriptor);
|
||||
return tokenHandler.WriteToken(token);
|
||||
}
|
||||
|
||||
private string GenerateRefreshToken()
|
||||
{
|
||||
var randomNumber = new byte[32];
|
||||
using var rng = RandomNumberGenerator.Create();
|
||||
rng.GetBytes(randomNumber);
|
||||
return Convert.ToBase64String(randomNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,15 +5,9 @@ namespace FutureMailAPI.Services
|
||||
{
|
||||
public interface IOAuthService
|
||||
{
|
||||
Task<ApiResponse<OAuthClientSecretDto>> CreateClientAsync(int userId, OAuthClientCreateDto createDto);
|
||||
Task<ApiResponse<OAuthClientDto>> GetClientAsync(string clientId);
|
||||
Task<ApiResponse<OAuthAuthorizationResponseDto>> AuthorizeAsync(int userId, OAuthAuthorizationRequestDto request);
|
||||
Task<ApiResponse<OAuthTokenResponseDto>> ExchangeCodeForTokenAsync(OAuthTokenRequestDto request);
|
||||
Task<ApiResponse<OAuthTokenResponseDto>> RefreshTokenAsync(OAuthTokenRequestDto request);
|
||||
Task<ApiResponse<bool>> RevokeTokenAsync(string token);
|
||||
Task<ApiResponse<bool>> ValidateTokenAsync(string token);
|
||||
Task<OAuthAccessToken?> GetAccessTokenAsync(string token);
|
||||
Task<OAuthClient?> GetClientByCredentialsAsync(string clientId, string clientSecret);
|
||||
Task<ApiResponse<OAuthTokenResponseDto>> LoginAsync(OAuthLoginDto loginDto);
|
||||
Task<ApiResponse<OAuthTokenResponseDto>> LoginAsync(OAuthLoginRequestDto request);
|
||||
Task<ApiResponse<OAuthTokenResponseDto>> RefreshTokenAsync(OAuthRefreshTokenRequestDto request);
|
||||
Task<bool> RevokeTokenAsync(string accessToken);
|
||||
Task<OAuthToken?> GetTokenAsync(string accessToken);
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ namespace FutureMailAPI.Services
|
||||
}
|
||||
|
||||
// 创建默认OAuth客户端
|
||||
var defaultClient = new OAuthClient
|
||||
var defaultClient = new Models.OAuthClient
|
||||
{
|
||||
ClientId = defaultClientId,
|
||||
ClientSecret = "futuremail_default_secret",
|
||||
|
||||
@@ -1,416 +1,204 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using FutureMailAPI.Data;
|
||||
using FutureMailAPI.Models;
|
||||
using FutureMailAPI.DTOs;
|
||||
using FutureMailAPI.Helpers;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.Json;
|
||||
using System.Text;
|
||||
using FutureMailAPI.Data;
|
||||
using FutureMailAPI.DTOs;
|
||||
using FutureMailAPI.Models;
|
||||
|
||||
namespace FutureMailAPI.Services
|
||||
{
|
||||
public class OAuthService : IOAuthService
|
||||
{
|
||||
private readonly FutureMailDbContext _context;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly ILogger<OAuthService> _logger;
|
||||
private readonly IPasswordHelper _passwordHelper;
|
||||
|
||||
public OAuthService(FutureMailDbContext context, ILogger<OAuthService> logger, IPasswordHelper passwordHelper)
|
||||
|
||||
public OAuthService(FutureMailDbContext context, IConfiguration configuration, ILogger<OAuthService> logger)
|
||||
{
|
||||
_context = context;
|
||||
_configuration = configuration;
|
||||
_logger = logger;
|
||||
_passwordHelper = passwordHelper;
|
||||
}
|
||||
|
||||
public async Task<ApiResponse<OAuthClientSecretDto>> CreateClientAsync(int userId, OAuthClientCreateDto createDto)
|
||||
|
||||
public async Task<ApiResponse<OAuthTokenResponseDto>> LoginAsync(OAuthLoginRequestDto request)
|
||||
{
|
||||
var clientId = GenerateRandomString(32);
|
||||
var clientSecret = GenerateRandomString(64);
|
||||
|
||||
var client = new OAuthClient
|
||||
try
|
||||
{
|
||||
ClientId = clientId,
|
||||
ClientSecret = clientSecret,
|
||||
Name = createDto.Name,
|
||||
RedirectUris = JsonSerializer.Serialize(createDto.RedirectUris),
|
||||
Scopes = JsonSerializer.Serialize(createDto.Scopes),
|
||||
IsActive = true,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
_context.OAuthClients.Add(client);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
var result = new OAuthClientSecretDto
|
||||
{
|
||||
ClientId = clientId,
|
||||
ClientSecret = clientSecret
|
||||
};
|
||||
|
||||
return ApiResponse<OAuthClientSecretDto>.SuccessResult(result, "OAuth客户端创建成功");
|
||||
}
|
||||
|
||||
public async Task<ApiResponse<OAuthClientDto>> GetClientAsync(string clientId)
|
||||
{
|
||||
var client = await _context.OAuthClients
|
||||
.FirstOrDefaultAsync(c => c.ClientId == clientId && c.IsActive);
|
||||
|
||||
if (client == null)
|
||||
{
|
||||
return ApiResponse<OAuthClientDto>.ErrorResult("客户端不存在");
|
||||
}
|
||||
|
||||
var redirectUris = JsonSerializer.Deserialize<string[]>(client.RedirectUris) ?? Array.Empty<string>();
|
||||
var scopes = JsonSerializer.Deserialize<string[]>(client.Scopes) ?? Array.Empty<string>();
|
||||
|
||||
var result = new OAuthClientDto
|
||||
{
|
||||
Id = client.Id,
|
||||
ClientId = client.ClientId,
|
||||
Name = client.Name,
|
||||
RedirectUris = redirectUris,
|
||||
Scopes = scopes,
|
||||
IsActive = client.IsActive,
|
||||
CreatedAt = client.CreatedAt,
|
||||
UpdatedAt = client.UpdatedAt
|
||||
};
|
||||
|
||||
return ApiResponse<OAuthClientDto>.SuccessResult(result);
|
||||
}
|
||||
|
||||
public async Task<ApiResponse<OAuthAuthorizationResponseDto>> AuthorizeAsync(int userId, OAuthAuthorizationRequestDto request)
|
||||
{
|
||||
// 验证客户端
|
||||
var client = await _context.OAuthClients
|
||||
.FirstOrDefaultAsync(c => c.ClientId == request.ClientId && c.IsActive);
|
||||
|
||||
if (client == null)
|
||||
{
|
||||
return ApiResponse<OAuthAuthorizationResponseDto>.ErrorResult("无效的客户端ID");
|
||||
}
|
||||
|
||||
// 验证重定向URI
|
||||
var redirectUris = JsonSerializer.Deserialize<string[]>(client.RedirectUris) ?? Array.Empty<string>();
|
||||
if (!redirectUris.Contains(request.RedirectUri))
|
||||
{
|
||||
return ApiResponse<OAuthAuthorizationResponseDto>.ErrorResult("无效的重定向URI");
|
||||
}
|
||||
|
||||
// 验证范围
|
||||
var clientScopes = JsonSerializer.Deserialize<string[]>(client.Scopes) ?? Array.Empty<string>();
|
||||
var requestedScopes = request.Scope.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
foreach (var scope in requestedScopes)
|
||||
{
|
||||
if (!clientScopes.Contains(scope))
|
||||
// 验证OAuth客户端
|
||||
var client = await _context.OAuthClients
|
||||
.FirstOrDefaultAsync(c => c.ClientId == request.ClientId && c.ClientSecret == request.ClientSecret);
|
||||
|
||||
if (client == null)
|
||||
{
|
||||
return ApiResponse<OAuthAuthorizationResponseDto>.ErrorResult($"无效的范围: {scope}");
|
||||
return ApiResponse<OAuthTokenResponseDto>.ErrorResult("无效的客户端凭据");
|
||||
}
|
||||
|
||||
// 验证用户凭据
|
||||
var user = await _context.Users
|
||||
.FirstOrDefaultAsync(u => u.Email == request.Username);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
return ApiResponse<OAuthTokenResponseDto>.ErrorResult("用户不存在");
|
||||
}
|
||||
|
||||
// 验证密码
|
||||
if (!BCrypt.Net.BCrypt.Verify(request.Password, user.PasswordHash))
|
||||
{
|
||||
return ApiResponse<OAuthTokenResponseDto>.ErrorResult("密码错误");
|
||||
}
|
||||
|
||||
// 生成访问令牌
|
||||
var accessToken = GenerateJwtToken(user, client);
|
||||
var refreshToken = GenerateRefreshToken();
|
||||
|
||||
// 保存令牌到数据库
|
||||
var oauthToken = new OAuthToken
|
||||
{
|
||||
AccessToken = accessToken,
|
||||
RefreshToken = refreshToken,
|
||||
UserId = user.Id,
|
||||
ClientId = client.Id,
|
||||
ExpiresAt = DateTime.UtcNow.AddHours(1), // 访问令牌1小时过期
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
_context.OAuthTokens.Add(oauthToken);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
var response = new OAuthTokenResponseDto
|
||||
{
|
||||
AccessToken = accessToken,
|
||||
RefreshToken = refreshToken,
|
||||
TokenType = "Bearer",
|
||||
ExpiresIn = 3600 // 1小时,单位秒
|
||||
};
|
||||
|
||||
return ApiResponse<OAuthTokenResponseDto>.SuccessResult(response);
|
||||
}
|
||||
|
||||
// 生成授权码
|
||||
var code = GenerateRandomString(64);
|
||||
var authorizationCode = new OAuthAuthorizationCode
|
||||
catch (Exception ex)
|
||||
{
|
||||
Code = code,
|
||||
ClientId = client.Id,
|
||||
UserId = userId,
|
||||
RedirectUri = request.RedirectUri,
|
||||
Scopes = request.Scope,
|
||||
IsUsed = false,
|
||||
ExpiresAt = DateTime.UtcNow.AddMinutes(10), // 授权码10分钟后过期
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
_context.OAuthAuthorizationCodes.Add(authorizationCode);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
var result = new OAuthAuthorizationResponseDto
|
||||
{
|
||||
Code = code,
|
||||
State = request.State
|
||||
};
|
||||
|
||||
return ApiResponse<OAuthAuthorizationResponseDto>.SuccessResult(result);
|
||||
_logger.LogError(ex, "OAuth登录时发生错误");
|
||||
return ApiResponse<OAuthTokenResponseDto>.ErrorResult("服务器内部错误");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ApiResponse<OAuthTokenResponseDto>> ExchangeCodeForTokenAsync(OAuthTokenRequestDto request)
|
||||
|
||||
public async Task<ApiResponse<OAuthTokenResponseDto>> RefreshTokenAsync(OAuthRefreshTokenRequestDto request)
|
||||
{
|
||||
// 验证客户端
|
||||
var client = await GetClientByCredentialsAsync(request.ClientId, request.ClientSecret);
|
||||
if (client == null)
|
||||
try
|
||||
{
|
||||
return ApiResponse<OAuthTokenResponseDto>.ErrorResult("无效的客户端凭据");
|
||||
// 查找刷新令牌
|
||||
var token = await _context.OAuthTokens
|
||||
.Include(t => t.User)
|
||||
.Include(t => t.Client)
|
||||
.FirstOrDefaultAsync(t => t.RefreshToken == request.RefreshToken);
|
||||
|
||||
if (token == null)
|
||||
{
|
||||
return ApiResponse<OAuthTokenResponseDto>.ErrorResult("无效的刷新令牌");
|
||||
}
|
||||
|
||||
// 生成新的访问令牌
|
||||
var accessToken = GenerateJwtToken(token.User, token.Client);
|
||||
var refreshToken = GenerateRefreshToken();
|
||||
|
||||
// 更新令牌
|
||||
token.AccessToken = accessToken;
|
||||
token.RefreshToken = refreshToken;
|
||||
token.ExpiresAt = DateTime.UtcNow.AddHours(1);
|
||||
token.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
var response = new OAuthTokenResponseDto
|
||||
{
|
||||
AccessToken = accessToken,
|
||||
RefreshToken = refreshToken,
|
||||
TokenType = "Bearer",
|
||||
ExpiresIn = 3600 // 1小时,单位秒
|
||||
};
|
||||
|
||||
return ApiResponse<OAuthTokenResponseDto>.SuccessResult(response);
|
||||
}
|
||||
|
||||
// 验证授权码
|
||||
var authCode = await _context.OAuthAuthorizationCodes
|
||||
.Include(c => c.Client)
|
||||
.Include(c => c.User)
|
||||
.FirstOrDefaultAsync(c => c.Code == request.Code && c.ClientId == client.Id);
|
||||
|
||||
if (authCode == null || authCode.IsUsed || authCode.ExpiresAt < DateTime.UtcNow)
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ApiResponse<OAuthTokenResponseDto>.ErrorResult("无效的授权码");
|
||||
_logger.LogError(ex, "OAuth刷新令牌时发生错误");
|
||||
return ApiResponse<OAuthTokenResponseDto>.ErrorResult("服务器内部错误");
|
||||
}
|
||||
|
||||
// 验证重定向URI
|
||||
if (authCode.RedirectUri != request.RedirectUri)
|
||||
{
|
||||
return ApiResponse<OAuthTokenResponseDto>.ErrorResult("重定向URI不匹配");
|
||||
}
|
||||
|
||||
// 标记授权码为已使用
|
||||
authCode.IsUsed = true;
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
// 生成访问令牌和刷新令牌
|
||||
var accessToken = GenerateRandomString(64);
|
||||
var refreshToken = GenerateRandomString(64);
|
||||
|
||||
var scopes = !string.IsNullOrEmpty(request.Scope) ? request.Scope : authCode.Scopes;
|
||||
|
||||
var oauthAccessToken = new OAuthAccessToken
|
||||
{
|
||||
Token = accessToken,
|
||||
ClientId = client.Id,
|
||||
UserId = authCode.UserId,
|
||||
Scopes = scopes,
|
||||
IsRevoked = false,
|
||||
ExpiresAt = DateTime.UtcNow.AddHours(1), // 访问令牌1小时后过期
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
var oauthRefreshToken = new OAuthRefreshToken
|
||||
{
|
||||
Token = refreshToken,
|
||||
ClientId = client.Id,
|
||||
UserId = authCode.UserId,
|
||||
IsUsed = false,
|
||||
ExpiresAt = DateTime.UtcNow.AddDays(30), // 刷新令牌30天后过期
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
_context.OAuthAccessTokens.Add(oauthAccessToken);
|
||||
_context.OAuthRefreshTokens.Add(oauthRefreshToken);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
var result = new OAuthTokenResponseDto
|
||||
{
|
||||
AccessToken = accessToken,
|
||||
TokenType = "Bearer",
|
||||
ExpiresIn = 3600, // 1小时
|
||||
RefreshToken = refreshToken,
|
||||
Scope = scopes
|
||||
};
|
||||
|
||||
return ApiResponse<OAuthTokenResponseDto>.SuccessResult(result);
|
||||
}
|
||||
|
||||
public async Task<ApiResponse<OAuthTokenResponseDto>> RefreshTokenAsync(OAuthTokenRequestDto request)
|
||||
|
||||
public async Task<bool> RevokeTokenAsync(string accessToken)
|
||||
{
|
||||
// 验证客户端
|
||||
var client = await GetClientByCredentialsAsync(request.ClientId, request.ClientSecret);
|
||||
if (client == null)
|
||||
try
|
||||
{
|
||||
return ApiResponse<OAuthTokenResponseDto>.ErrorResult("无效的客户端凭据");
|
||||
var token = await _context.OAuthTokens
|
||||
.FirstOrDefaultAsync(t => t.AccessToken == accessToken);
|
||||
|
||||
if (token != null)
|
||||
{
|
||||
_context.OAuthTokens.Remove(token);
|
||||
await _context.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证刷新令牌
|
||||
var refreshToken = await _context.OAuthRefreshTokens
|
||||
.Include(t => t.Client)
|
||||
.Include(t => t.User)
|
||||
.FirstOrDefaultAsync(t => t.Token == request.RefreshToken && t.ClientId == client.Id);
|
||||
|
||||
if (refreshToken == null || refreshToken.IsUsed || refreshToken.ExpiresAt < DateTime.UtcNow)
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ApiResponse<OAuthTokenResponseDto>.ErrorResult("无效的刷新令牌");
|
||||
_logger.LogError(ex, "OAuth撤销令牌时发生错误");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 标记旧刷新令牌为已使用
|
||||
refreshToken.IsUsed = true;
|
||||
|
||||
// 撤销旧访问令牌
|
||||
var oldAccessTokens = await _context.OAuthAccessTokens
|
||||
.Where(t => t.UserId == refreshToken.UserId && t.ClientId == client.Id && !t.IsRevoked)
|
||||
.ToListAsync();
|
||||
|
||||
foreach (var token in oldAccessTokens)
|
||||
{
|
||||
token.IsRevoked = true;
|
||||
}
|
||||
|
||||
// 生成新的访问令牌和刷新令牌
|
||||
var newAccessToken = GenerateRandomString(64);
|
||||
var newRefreshToken = GenerateRandomString(64);
|
||||
|
||||
var scopes = !string.IsNullOrEmpty(request.Scope) ? request.Scope : "";
|
||||
|
||||
var newOAuthAccessToken = new OAuthAccessToken
|
||||
{
|
||||
Token = newAccessToken,
|
||||
ClientId = client.Id,
|
||||
UserId = refreshToken.UserId,
|
||||
Scopes = scopes,
|
||||
IsRevoked = false,
|
||||
ExpiresAt = DateTime.UtcNow.AddHours(1), // 访问令牌1小时后过期
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
var newOAuthRefreshToken = new OAuthRefreshToken
|
||||
{
|
||||
Token = newRefreshToken,
|
||||
ClientId = client.Id,
|
||||
UserId = refreshToken.UserId,
|
||||
IsUsed = false,
|
||||
ExpiresAt = DateTime.UtcNow.AddDays(30), // 刷新令牌30天后过期
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
_context.OAuthAccessTokens.Add(newOAuthAccessToken);
|
||||
_context.OAuthRefreshTokens.Add(newOAuthRefreshToken);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
var result = new OAuthTokenResponseDto
|
||||
{
|
||||
AccessToken = newAccessToken,
|
||||
TokenType = "Bearer",
|
||||
ExpiresIn = 3600, // 1小时
|
||||
RefreshToken = newRefreshToken,
|
||||
Scope = scopes
|
||||
};
|
||||
|
||||
return ApiResponse<OAuthTokenResponseDto>.SuccessResult(result);
|
||||
}
|
||||
|
||||
public async Task<ApiResponse<bool>> RevokeTokenAsync(string token)
|
||||
|
||||
public async Task<OAuthToken?> GetTokenAsync(string accessToken)
|
||||
{
|
||||
var accessToken = await _context.OAuthAccessTokens
|
||||
.FirstOrDefaultAsync(t => t.Token == token);
|
||||
|
||||
if (accessToken == null)
|
||||
try
|
||||
{
|
||||
return ApiResponse<bool>.ErrorResult("令牌不存在");
|
||||
return await _context.OAuthTokens
|
||||
.Include(t => t.User)
|
||||
.Include(t => t.Client)
|
||||
.FirstOrDefaultAsync(t => t.AccessToken == accessToken && t.ExpiresAt > DateTime.UtcNow);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "获取OAuth令牌时发生错误");
|
||||
return null;
|
||||
}
|
||||
|
||||
accessToken.IsRevoked = true;
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return ApiResponse<bool>.SuccessResult(true, "令牌已撤销");
|
||||
}
|
||||
|
||||
public async Task<ApiResponse<bool>> ValidateTokenAsync(string token)
|
||||
|
||||
private string GenerateJwtToken(User user, OAuthClient client)
|
||||
{
|
||||
var accessToken = await _context.OAuthAccessTokens
|
||||
.FirstOrDefaultAsync(t => t.Token == token && !t.IsRevoked && t.ExpiresAt > DateTime.UtcNow);
|
||||
|
||||
if (accessToken == null)
|
||||
var jwtSettings = _configuration.GetSection("Jwt");
|
||||
var key = Encoding.ASCII.GetBytes(jwtSettings["Key"] ?? throw new InvalidOperationException("JWT密钥未配置"));
|
||||
var tokenDescriptor = new SecurityTokenDescriptor
|
||||
{
|
||||
return ApiResponse<bool>.ErrorResult("无效的令牌");
|
||||
}
|
||||
|
||||
return ApiResponse<bool>.SuccessResult(true);
|
||||
}
|
||||
|
||||
public async Task<OAuthAccessToken?> GetAccessTokenAsync(string token)
|
||||
{
|
||||
return await _context.OAuthAccessTokens
|
||||
.Include(t => t.Client)
|
||||
.Include(t => t.User)
|
||||
.FirstOrDefaultAsync(t => t.Token == token && !t.IsRevoked && t.ExpiresAt > DateTime.UtcNow);
|
||||
}
|
||||
|
||||
public async Task<OAuthClient?> GetClientByCredentialsAsync(string clientId, string clientSecret)
|
||||
{
|
||||
if (string.IsNullOrEmpty(clientSecret))
|
||||
{
|
||||
// 公开客户端,只验证客户端ID
|
||||
return await _context.OAuthClients
|
||||
.FirstOrDefaultAsync(c => c.ClientId == clientId && c.IsActive);
|
||||
}
|
||||
|
||||
// 机密客户端,验证客户端ID和密钥
|
||||
return await _context.OAuthClients
|
||||
.FirstOrDefaultAsync(c => c.ClientId == clientId && c.ClientSecret == clientSecret && c.IsActive);
|
||||
}
|
||||
|
||||
public async Task<ApiResponse<OAuthTokenResponseDto>> LoginAsync(OAuthLoginDto loginDto)
|
||||
{
|
||||
// 验证客户端
|
||||
var client = await GetClientByCredentialsAsync(loginDto.ClientId, loginDto.ClientSecret);
|
||||
if (client == null)
|
||||
{
|
||||
return ApiResponse<OAuthTokenResponseDto>.ErrorResult("无效的客户端凭据");
|
||||
}
|
||||
|
||||
// 验证用户凭据
|
||||
var user = await _context.Users
|
||||
.FirstOrDefaultAsync(u => (u.Email == loginDto.UsernameOrEmail || u.Nickname == loginDto.UsernameOrEmail));
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
return ApiResponse<OAuthTokenResponseDto>.ErrorResult("用户名或密码错误");
|
||||
}
|
||||
|
||||
// 验证密码
|
||||
if (!_passwordHelper.VerifyPassword(loginDto.Password, user.PasswordHash))
|
||||
{
|
||||
return ApiResponse<OAuthTokenResponseDto>.ErrorResult("用户名或密码错误");
|
||||
}
|
||||
|
||||
// 生成访问令牌和刷新令牌
|
||||
var accessToken = GenerateRandomString(64);
|
||||
var refreshToken = GenerateRandomString(64);
|
||||
|
||||
var oauthAccessToken = new OAuthAccessToken
|
||||
{
|
||||
Token = accessToken,
|
||||
ClientId = client.Id,
|
||||
UserId = user.Id,
|
||||
Scopes = loginDto.Scope,
|
||||
IsRevoked = false,
|
||||
ExpiresAt = DateTime.UtcNow.AddHours(1), // 访问令牌1小时后过期
|
||||
CreatedAt = DateTime.UtcNow
|
||||
Subject = new ClaimsIdentity(new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
|
||||
new Claim(ClaimTypes.Name, user.Username),
|
||||
new Claim(ClaimTypes.Email, user.Email),
|
||||
new Claim("client_id", client.ClientId)
|
||||
}),
|
||||
Expires = DateTime.UtcNow.AddHours(1),
|
||||
Issuer = jwtSettings["Issuer"],
|
||||
Audience = jwtSettings["Audience"],
|
||||
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
|
||||
};
|
||||
|
||||
var oauthRefreshToken = new OAuthRefreshToken
|
||||
{
|
||||
Token = refreshToken,
|
||||
ClientId = client.Id,
|
||||
UserId = user.Id,
|
||||
IsUsed = false,
|
||||
ExpiresAt = DateTime.UtcNow.AddDays(30), // 刷新令牌30天后过期
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
_context.OAuthAccessTokens.Add(oauthAccessToken);
|
||||
_context.OAuthRefreshTokens.Add(oauthRefreshToken);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
var result = new OAuthTokenResponseDto
|
||||
{
|
||||
AccessToken = accessToken,
|
||||
TokenType = "Bearer",
|
||||
ExpiresIn = 3600, // 1小时
|
||||
RefreshToken = refreshToken,
|
||||
Scope = loginDto.Scope
|
||||
};
|
||||
|
||||
return ApiResponse<OAuthTokenResponseDto>.SuccessResult(result);
|
||||
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
var token = tokenHandler.CreateToken(tokenDescriptor);
|
||||
return tokenHandler.WriteToken(token);
|
||||
}
|
||||
|
||||
private string GenerateRandomString(int length)
|
||||
|
||||
private string GenerateRefreshToken()
|
||||
{
|
||||
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
var random = new Random();
|
||||
var result = new char[length];
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
result[i] = chars[random.Next(chars.Length)];
|
||||
}
|
||||
|
||||
return new string(result);
|
||||
var randomNumber = new byte[32];
|
||||
using var rng = RandomNumberGenerator.Create();
|
||||
rng.GetBytes(randomNumber);
|
||||
return Convert.ToBase64String(randomNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,10 +50,10 @@ namespace FutureMailAPI.Services
|
||||
return ApiResponse<UserResponseDto>.ErrorResult("邮箱已被注册");
|
||||
}
|
||||
|
||||
// 生成随机盐值
|
||||
// 生成盐值
|
||||
var salt = _passwordHelper.GenerateSalt();
|
||||
|
||||
// 创建新用户
|
||||
// 创建新用户(使用正确的密码哈希方法)
|
||||
var user = new User
|
||||
{
|
||||
Username = registerDto.Username,
|
||||
@@ -94,7 +94,7 @@ namespace FutureMailAPI.Services
|
||||
}
|
||||
|
||||
// 验证密码
|
||||
if (!_passwordHelper.VerifyPassword(loginDto.Password, user.PasswordHash))
|
||||
if (!_passwordHelper.VerifyPassword(loginDto.Password, user.PasswordHash, user.Salt))
|
||||
{
|
||||
return ApiResponse<AuthResponseDto>.ErrorResult("用户名或密码错误");
|
||||
}
|
||||
@@ -103,13 +103,9 @@ namespace FutureMailAPI.Services
|
||||
user.LastLoginAt = DateTime.UtcNow;
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
// 注意:这里不再生成JWT令牌,因为我们将使用OAuth 2.0
|
||||
// 在OAuth 2.0流程中,令牌是通过OAuth端点生成的
|
||||
|
||||
// 创建认证响应(无token版本)
|
||||
var authResponse = new AuthResponseDto
|
||||
{
|
||||
Token = "", // 临时空字符串,实际使用OAuth 2.0令牌
|
||||
Expires = DateTime.UtcNow.AddDays(7),
|
||||
User = MapToUserResponseDto(user)
|
||||
};
|
||||
|
||||
@@ -225,7 +221,7 @@ namespace FutureMailAPI.Services
|
||||
}
|
||||
|
||||
// 验证当前密码
|
||||
if (!_passwordHelper.VerifyPassword(changePasswordDto.CurrentPassword, user.PasswordHash))
|
||||
if (!_passwordHelper.VerifyPassword(changePasswordDto.CurrentPassword, user.PasswordHash, user.Salt))
|
||||
{
|
||||
return ApiResponse<bool>.ErrorResult("当前密码错误");
|
||||
}
|
||||
@@ -259,10 +255,10 @@ namespace FutureMailAPI.Services
|
||||
return ApiResponse<UserResponseDto>.ErrorResult("邮箱已被注册");
|
||||
}
|
||||
|
||||
// 生成随机盐值
|
||||
// 生成盐值
|
||||
var salt = _passwordHelper.GenerateSalt();
|
||||
|
||||
// 创建新用户
|
||||
// 创建新用户(使用正确的密码哈希方法)
|
||||
var user = new User
|
||||
{
|
||||
Username = registerDto.Username,
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using FutureMailAPI.Data;
|
||||
using FutureMailAPI.Models;
|
||||
|
||||
var optionsBuilder = new DbContextOptionsBuilder<FutureMailDbContext>();
|
||||
optionsBuilder.UseSqlite("Data Source=FutureMail.db");
|
||||
|
||||
using var context = new FutureMailDbContext(optionsBuilder.Options);
|
||||
|
||||
// 检查OAuth客户端数据
|
||||
var clients = await context.OAuthClients.ToListAsync();
|
||||
Console.WriteLine($"OAuth客户端数量: {clients.Count}");
|
||||
|
||||
foreach (var client in clients)
|
||||
{
|
||||
Console.WriteLine($"客户端ID: {client.ClientId}, 名称: {client.Name}, 是否激活: {client.IsActive}");
|
||||
}
|
||||
|
||||
// 检查用户数据
|
||||
var users = await context.Users.ToListAsync();
|
||||
Console.WriteLine($"用户数量: {users.Count}");
|
||||
|
||||
foreach (var user in users)
|
||||
{
|
||||
Console.WriteLine($"用户ID: {user.Id}, 用户名: {user.Username}, 邮箱: {user.Email}");
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
class TestRegisterProgram
|
||||
{
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
var client = new HttpClient();
|
||||
|
||||
// 生成随机用户名
|
||||
var random = new Random();
|
||||
var username = $"testuser{random.Next(1000, 9999)}";
|
||||
var email = $"{username}@example.com";
|
||||
|
||||
Console.WriteLine($"尝试注册用户: {username}, 邮箱: {email}");
|
||||
|
||||
// 创建注册请求
|
||||
var registerData = new
|
||||
{
|
||||
Username = username,
|
||||
Email = email,
|
||||
Password = "password123"
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(registerData);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
try
|
||||
{
|
||||
// 发送注册请求
|
||||
var response = await client.PostAsync("http://localhost:5001/api/v1/auth/register", content);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
Console.WriteLine($"注册成功! 响应内容: {responseContent}");
|
||||
}
|
||||
else
|
||||
{
|
||||
var errorContent = await response.Content.ReadAsStringAsync();
|
||||
Console.WriteLine($"注册失败! 状态码: {response.StatusCode}");
|
||||
Console.WriteLine($"错误内容: {errorContent}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"发生异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,20 +9,14 @@
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Data Source=FutureMail.db"
|
||||
},
|
||||
"JwtSettings": {
|
||||
"SecretKey": "FutureMailSecretKey2024!@#LongerKeyForHMACSHA256",
|
||||
"Issuer": "FutureMailAPI",
|
||||
"Audience": "FutureMailClient",
|
||||
"ExpirationInMinutes": 1440
|
||||
},
|
||||
"Jwt": {
|
||||
"Key": "FutureMailSecretKey2024!@#LongerKeyForHMACSHA256",
|
||||
"Issuer": "FutureMailAPI",
|
||||
"Audience": "FutureMailClient"
|
||||
},
|
||||
"FileUpload": {
|
||||
"UploadPath": "uploads",
|
||||
"BaseUrl": "http://localhost:5054/uploads",
|
||||
"MaxFileSize": 104857600
|
||||
},
|
||||
"Jwt": {
|
||||
"Key": "ThisIsASecretKeyForJWTTokenGenerationAndValidation123456789",
|
||||
"Issuer": "FutureMailAPI",
|
||||
"Audience": "FutureMailClient"
|
||||
}
|
||||
}
|
||||
|
||||
BIN
FutureMailAPI/bin/Debug/net9.0/BCrypt.Net-Next.dll
Normal file
BIN
FutureMailAPI/bin/Debug/net9.0/BCrypt.Net-Next.dll
Normal file
Binary file not shown.
@@ -8,20 +8,30 @@
|
||||
".NETCoreApp,Version=v9.0": {
|
||||
"FutureMailAPI/1.0.0": {
|
||||
"dependencies": {
|
||||
"BCrypt.Net-Next": "4.0.3",
|
||||
"Microsoft.AspNetCore.Authentication.JwtBearer": "9.0.9",
|
||||
"Microsoft.AspNetCore.OpenApi": "9.0.9",
|
||||
"Microsoft.EntityFrameworkCore.Design": "9.0.9",
|
||||
"Microsoft.EntityFrameworkCore.Sqlite": "9.0.9",
|
||||
"Microsoft.IdentityModel.Tokens": "8.3.0",
|
||||
"Pomelo.EntityFrameworkCore.MySql": "9.0.0",
|
||||
"Quartz": "3.15.0",
|
||||
"Quartz.Extensions.Hosting": "3.15.0",
|
||||
"Swashbuckle.AspNetCore": "9.0.6",
|
||||
"System.IdentityModel.Tokens.Jwt": "8.14.0"
|
||||
"System.IdentityModel.Tokens.Jwt": "8.3.0"
|
||||
},
|
||||
"runtime": {
|
||||
"FutureMailAPI.dll": {}
|
||||
}
|
||||
},
|
||||
"BCrypt.Net-Next/4.0.3": {
|
||||
"runtime": {
|
||||
"lib/net6.0/BCrypt.Net-Next.dll": {
|
||||
"assemblyVersion": "4.0.3.0",
|
||||
"fileVersion": "4.0.3.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Humanizer.Core/2.14.1": {
|
||||
"runtime": {
|
||||
"lib/net6.0/Humanizer.dll": {
|
||||
@@ -539,39 +549,39 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Abstractions/8.14.0": {
|
||||
"Microsoft.IdentityModel.Abstractions/8.3.0": {
|
||||
"runtime": {
|
||||
"lib/net9.0/Microsoft.IdentityModel.Abstractions.dll": {
|
||||
"assemblyVersion": "8.14.0.0",
|
||||
"fileVersion": "8.14.0.60815"
|
||||
"assemblyVersion": "8.3.0.0",
|
||||
"fileVersion": "8.3.0.51204"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.JsonWebTokens/8.14.0": {
|
||||
"Microsoft.IdentityModel.JsonWebTokens/8.3.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.IdentityModel.Tokens": "8.14.0"
|
||||
"Microsoft.IdentityModel.Tokens": "8.3.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net9.0/Microsoft.IdentityModel.JsonWebTokens.dll": {
|
||||
"assemblyVersion": "8.14.0.0",
|
||||
"fileVersion": "8.14.0.60815"
|
||||
"assemblyVersion": "8.3.0.0",
|
||||
"fileVersion": "8.3.0.51204"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Logging/8.14.0": {
|
||||
"Microsoft.IdentityModel.Logging/8.3.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.IdentityModel.Abstractions": "8.14.0"
|
||||
"Microsoft.IdentityModel.Abstractions": "8.3.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net9.0/Microsoft.IdentityModel.Logging.dll": {
|
||||
"assemblyVersion": "8.14.0.0",
|
||||
"fileVersion": "8.14.0.60815"
|
||||
"assemblyVersion": "8.3.0.0",
|
||||
"fileVersion": "8.3.0.51204"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Protocols/8.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.IdentityModel.Tokens": "8.14.0"
|
||||
"Microsoft.IdentityModel.Tokens": "8.3.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net9.0/Microsoft.IdentityModel.Protocols.dll": {
|
||||
@@ -583,7 +593,7 @@
|
||||
"Microsoft.IdentityModel.Protocols.OpenIdConnect/8.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.IdentityModel.Protocols": "8.0.1",
|
||||
"System.IdentityModel.Tokens.Jwt": "8.14.0"
|
||||
"System.IdentityModel.Tokens.Jwt": "8.3.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net9.0/Microsoft.IdentityModel.Protocols.OpenIdConnect.dll": {
|
||||
@@ -592,15 +602,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Tokens/8.14.0": {
|
||||
"Microsoft.IdentityModel.Tokens/8.3.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "9.0.9",
|
||||
"Microsoft.IdentityModel.Logging": "8.14.0"
|
||||
"Microsoft.IdentityModel.Logging": "8.3.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net9.0/Microsoft.IdentityModel.Tokens.dll": {
|
||||
"assemblyVersion": "8.14.0.0",
|
||||
"fileVersion": "8.14.0.60815"
|
||||
"assemblyVersion": "8.3.0.0",
|
||||
"fileVersion": "8.3.0.51204"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -927,15 +936,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"System.IdentityModel.Tokens.Jwt/8.14.0": {
|
||||
"System.IdentityModel.Tokens.Jwt/8.3.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.IdentityModel.JsonWebTokens": "8.14.0",
|
||||
"Microsoft.IdentityModel.Tokens": "8.14.0"
|
||||
"Microsoft.IdentityModel.JsonWebTokens": "8.3.0",
|
||||
"Microsoft.IdentityModel.Tokens": "8.3.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net9.0/System.IdentityModel.Tokens.Jwt.dll": {
|
||||
"assemblyVersion": "8.14.0.0",
|
||||
"fileVersion": "8.14.0.60815"
|
||||
"assemblyVersion": "8.3.0.0",
|
||||
"fileVersion": "8.3.0.51204"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -947,6 +956,13 @@
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"BCrypt.Net-Next/4.0.3": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-W+U9WvmZQgi5cX6FS5GDtDoPzUCV4LkBLkywq/kRZhuDwcbavOzcDAr3LXJFqHUi952Yj3LEYoWW0jbEUQChsA==",
|
||||
"path": "bcrypt.net-next/4.0.3",
|
||||
"hashPath": "bcrypt.net-next.4.0.3.nupkg.sha512"
|
||||
},
|
||||
"Humanizer.Core/2.14.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
@@ -1136,26 +1152,26 @@
|
||||
"path": "microsoft.extensions.primitives/9.0.9",
|
||||
"hashPath": "microsoft.extensions.primitives.9.0.9.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.Abstractions/8.14.0": {
|
||||
"Microsoft.IdentityModel.Abstractions/8.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-iwbCpSjD3ehfTwBhtSNEtKPK0ICun6ov7Ibx6ISNA9bfwIyzI2Siwyi9eJFCJBwxowK9xcA1mj+jBWiigeqgcQ==",
|
||||
"path": "microsoft.identitymodel.abstractions/8.14.0",
|
||||
"hashPath": "microsoft.identitymodel.abstractions.8.14.0.nupkg.sha512"
|
||||
"sha512": "sha512-jNin7yvWZu+K3U24q+6kD+LmGSRfbkHl9Px8hN1XrGwq6ZHgKGi/zuTm5m08G27fwqKfVXIWuIcUeq4Y1VQUOg==",
|
||||
"path": "microsoft.identitymodel.abstractions/8.3.0",
|
||||
"hashPath": "microsoft.identitymodel.abstractions.8.3.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.JsonWebTokens/8.14.0": {
|
||||
"Microsoft.IdentityModel.JsonWebTokens/8.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-4jOpiA4THdtpLyMdAb24dtj7+6GmvhOhxf5XHLYWmPKF8ApEnApal1UnJsKO4HxUWRXDA6C4WQVfYyqsRhpNpQ==",
|
||||
"path": "microsoft.identitymodel.jsonwebtokens/8.14.0",
|
||||
"hashPath": "microsoft.identitymodel.jsonwebtokens.8.14.0.nupkg.sha512"
|
||||
"sha512": "sha512-4SVXLT8sDG7CrHiszEBrsDYi+aDW0W9d+fuWUGdZPBdan56aM6fGXJDjbI0TVGEDjJhXbACQd8F/BnC7a+m2RQ==",
|
||||
"path": "microsoft.identitymodel.jsonwebtokens/8.3.0",
|
||||
"hashPath": "microsoft.identitymodel.jsonwebtokens.8.3.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.Logging/8.14.0": {
|
||||
"Microsoft.IdentityModel.Logging/8.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-eqqnemdW38CKZEHS6diA50BV94QICozDZEvSrsvN3SJXUFwVB9gy+/oz76gldP7nZliA16IglXjXTCTdmU/Ejg==",
|
||||
"path": "microsoft.identitymodel.logging/8.14.0",
|
||||
"hashPath": "microsoft.identitymodel.logging.8.14.0.nupkg.sha512"
|
||||
"sha512": "sha512-4w4pSIGHhCCLTHqtVNR2Cc/zbDIUWIBHTZCu/9ZHm2SVwrXY3RJMcZ7EFGiKqmKZMQZJzA0bpwCZ6R8Yb7i5VQ==",
|
||||
"path": "microsoft.identitymodel.logging/8.3.0",
|
||||
"hashPath": "microsoft.identitymodel.logging.8.3.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.Protocols/8.0.1": {
|
||||
"type": "package",
|
||||
@@ -1171,12 +1187,12 @@
|
||||
"path": "microsoft.identitymodel.protocols.openidconnect/8.0.1",
|
||||
"hashPath": "microsoft.identitymodel.protocols.openidconnect.8.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.Tokens/8.14.0": {
|
||||
"Microsoft.IdentityModel.Tokens/8.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-lKIZiBiGd36k02TCdMHp1KlNWisyIvQxcYJvIkz7P4gSQ9zi8dgh6S5Grj8NNG7HWYIPfQymGyoZ6JB5d1Lo1g==",
|
||||
"path": "microsoft.identitymodel.tokens/8.14.0",
|
||||
"hashPath": "microsoft.identitymodel.tokens.8.14.0.nupkg.sha512"
|
||||
"sha512": "sha512-yGzqmk+kInH50zeSEH/L1/J0G4/yqTQNq4YmdzOhpE7s/86tz37NS2YbbY2ievbyGjmeBI1mq26QH+yBR6AK3Q==",
|
||||
"path": "microsoft.identitymodel.tokens/8.3.0",
|
||||
"hashPath": "microsoft.identitymodel.tokens.8.3.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.OpenApi/1.6.25": {
|
||||
"type": "package",
|
||||
@@ -1332,12 +1348,12 @@
|
||||
"path": "system.composition.typedparts/7.0.0",
|
||||
"hashPath": "system.composition.typedparts.7.0.0.nupkg.sha512"
|
||||
},
|
||||
"System.IdentityModel.Tokens.Jwt/8.14.0": {
|
||||
"System.IdentityModel.Tokens.Jwt/8.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-EYGgN/S+HK7S6F3GaaPLFAfK0UzMrkXFyWCvXpQWFYmZln3dqtbyIO7VuTM/iIIPMzkelg8ZLlBPvMhxj6nOAA==",
|
||||
"path": "system.identitymodel.tokens.jwt/8.14.0",
|
||||
"hashPath": "system.identitymodel.tokens.jwt.8.14.0.nupkg.sha512"
|
||||
"sha512": "sha512-9GESpDG0Zb17HD5mBW/uEWi2yz/uKPmCthX2UhyLnk42moGH2FpMgXA2Y4l2Qc7P75eXSUTA6wb/c9D9GSVkzw==",
|
||||
"path": "system.identitymodel.tokens.jwt/8.3.0",
|
||||
"hashPath": "system.identitymodel.tokens.jwt.8.3.0.nupkg.sha512"
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -25,11 +25,35 @@
|
||||
<param name="request">未来预测请求</param>
|
||||
<returns>未来预测结果</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.AIController.GetCurrentUserId">
|
||||
<member name="T:FutureMailAPI.Controllers.BaseController">
|
||||
<summary>
|
||||
从JWT令牌中获取当前用户ID
|
||||
基础控制器,提供通用的用户身份验证方法
|
||||
</summary>
|
||||
<returns>用户ID</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.BaseController.GetCurrentUserId">
|
||||
<summary>
|
||||
获取当前用户ID
|
||||
兼容OAuth中间件和JWT令牌两种验证方式
|
||||
</summary>
|
||||
<returns>用户ID,如果未认证则返回0</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.BaseController.GetCurrentUserEmail">
|
||||
<summary>
|
||||
获取当前用户邮箱
|
||||
</summary>
|
||||
<returns>用户邮箱,如果未认证则返回空字符串</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.BaseController.GetCurrentUsername">
|
||||
<summary>
|
||||
获取当前用户名
|
||||
</summary>
|
||||
<returns>用户名,如果未认证则返回空字符串</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.BaseController.GetCurrentClientId">
|
||||
<summary>
|
||||
获取当前客户端ID
|
||||
</summary>
|
||||
<returns>客户端ID,如果未认证则返回空字符串</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.FileUploadController.UploadAttachment(FutureMailAPI.DTOs.FileUploadWithFileRequestDto)">
|
||||
<summary>
|
||||
@@ -59,12 +83,6 @@
|
||||
<param name="fileId">文件ID</param>
|
||||
<returns>文件信息</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.FileUploadController.GetCurrentUserId">
|
||||
<summary>
|
||||
从当前请求中获取用户ID
|
||||
</summary>
|
||||
<returns>用户ID</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.NotificationController.RegisterDevice(FutureMailAPI.DTOs.NotificationDeviceRequestDto)">
|
||||
<summary>
|
||||
注册设备
|
||||
@@ -78,47 +96,6 @@
|
||||
</summary>
|
||||
<returns>通知设置</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.NotificationController.GetCurrentUserId">
|
||||
<summary>
|
||||
从JWT令牌中获取当前用户ID
|
||||
</summary>
|
||||
<returns>用户ID</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.OAuthController.Login(FutureMailAPI.DTOs.OAuthLoginDto)">
|
||||
<summary>
|
||||
OAuth登录端点
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.OAuthController.CreateClient(FutureMailAPI.DTOs.OAuthClientCreateDto)">
|
||||
<summary>
|
||||
创建OAuth客户端
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.OAuthController.GetClient(System.String)">
|
||||
<summary>
|
||||
获取OAuth客户端信息
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.OAuthController.Authorize(FutureMailAPI.DTOs.OAuthAuthorizationRequestDto)">
|
||||
<summary>
|
||||
OAuth授权端点
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.OAuthController.ExchangeToken(FutureMailAPI.DTOs.OAuthTokenRequestDto)">
|
||||
<summary>
|
||||
OAuth令牌端点
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.OAuthController.RevokeToken(System.String,System.String)">
|
||||
<summary>
|
||||
撤销令牌
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.OAuthController.IntrospectToken(System.String)">
|
||||
<summary>
|
||||
验证令牌
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.PersonalSpaceController.GetTimeline(FutureMailAPI.DTOs.TimelineType,System.Nullable{System.DateTime},System.Nullable{System.DateTime})">
|
||||
<summary>
|
||||
获取用户时间线
|
||||
@@ -146,24 +123,12 @@
|
||||
</summary>
|
||||
<returns>用户资料</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.PersonalSpaceController.GetCurrentUserId">
|
||||
<summary>
|
||||
从当前请求中获取用户ID
|
||||
</summary>
|
||||
<returns>用户ID</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.StatisticsController.GetStatistics">
|
||||
<summary>
|
||||
获取用户统计数据
|
||||
</summary>
|
||||
<returns>用户统计数据</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.StatisticsController.GetCurrentUserId">
|
||||
<summary>
|
||||
从JWT令牌中获取当前用户ID
|
||||
</summary>
|
||||
<returns>用户ID</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.TimelineController.GetTimeline(FutureMailAPI.DTOs.TimelineType,System.Nullable{System.DateTime},System.Nullable{System.DateTime})">
|
||||
<summary>
|
||||
获取用户时间线
|
||||
@@ -173,12 +138,6 @@
|
||||
<param name="endDate">结束日期</param>
|
||||
<returns>用户时间线</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.TimelineController.GetCurrentUserId">
|
||||
<summary>
|
||||
从JWT令牌中获取当前用户ID
|
||||
</summary>
|
||||
<returns>用户ID</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.UploadController.UploadAttachment(FutureMailAPI.DTOs.FileUploadWithFileRequestDto)">
|
||||
<summary>
|
||||
上传附件
|
||||
@@ -193,12 +152,6 @@
|
||||
<param name="request">文件上传请求</param>
|
||||
<returns>上传结果</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.UploadController.GetCurrentUserId">
|
||||
<summary>
|
||||
从JWT令牌中获取当前用户ID
|
||||
</summary>
|
||||
<returns>用户ID</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.UserController.GetSubscription">
|
||||
<summary>
|
||||
获取用户订阅信息
|
||||
@@ -211,25 +164,9 @@
|
||||
</summary>
|
||||
<returns>用户资料</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.UserController.GetCurrentUserId">
|
||||
<summary>
|
||||
从JWT令牌中获取当前用户ID
|
||||
</summary>
|
||||
<returns>用户ID</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Extensions.HttpContextExtensions.GetCurrentUserId(Microsoft.AspNetCore.Http.HttpContext)">
|
||||
<summary>
|
||||
获取当前用户ID
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Extensions.HttpContextExtensions.GetCurrentUserEmail(Microsoft.AspNetCore.Http.HttpContext)">
|
||||
<summary>
|
||||
获取当前用户邮箱
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Extensions.HttpContextExtensions.GetCurrentAccessToken(Microsoft.AspNetCore.Http.HttpContext)">
|
||||
<summary>
|
||||
获取当前访问令牌
|
||||
获取当前用户ID(简化版本,不再依赖token)
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:FutureMailAPI.Helpers.FileUploadOperationFilter">
|
||||
@@ -261,18 +198,6 @@
|
||||
<member name="M:FutureMailAPI.Migrations.AddUserPreferences.BuildTargetModel(Microsoft.EntityFrameworkCore.ModelBuilder)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:FutureMailAPI.Migrations.AddOAuthEntities">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Migrations.AddOAuthEntities.Up(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Migrations.AddOAuthEntities.Down(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Migrations.AddOAuthEntities.BuildTargetModel(Microsoft.EntityFrameworkCore.ModelBuilder)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:FutureMailAPI.Migrations.AddSaltToUser">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
@@ -282,8 +207,5 @@
|
||||
<member name="M:FutureMailAPI.Migrations.AddSaltToUser.Down(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Migrations.AddSaltToUser.BuildTargetModel(Microsoft.EntityFrameworkCore.ModelBuilder)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -9,20 +9,14 @@
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Data Source=FutureMail.db"
|
||||
},
|
||||
"JwtSettings": {
|
||||
"SecretKey": "FutureMailSecretKey2024!@#LongerKeyForHMACSHA256",
|
||||
"Issuer": "FutureMailAPI",
|
||||
"Audience": "FutureMailClient",
|
||||
"ExpirationInMinutes": 1440
|
||||
},
|
||||
"Jwt": {
|
||||
"Key": "FutureMailSecretKey2024!@#LongerKeyForHMACSHA256",
|
||||
"Issuer": "FutureMailAPI",
|
||||
"Audience": "FutureMailClient"
|
||||
},
|
||||
"FileUpload": {
|
||||
"UploadPath": "uploads",
|
||||
"BaseUrl": "http://localhost:5054/uploads",
|
||||
"MaxFileSize": 104857600
|
||||
},
|
||||
"Jwt": {
|
||||
"Key": "ThisIsASecretKeyForJWTTokenGenerationAndValidation123456789",
|
||||
"Issuer": "FutureMailAPI",
|
||||
"Audience": "FutureMailClient"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"createDto": {
|
||||
"title": "我的第一封未来邮件",
|
||||
"content": "这是一封测试邮件,将在未来某个时间点发送。",
|
||||
"recipientType": 0,
|
||||
"deliveryTime": "2025-12-31T23:59:59Z",
|
||||
"triggerType": 0,
|
||||
"isEncrypted": false,
|
||||
"theme": "default"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
{"title":"My First Future Mail","content":"This is a test email that will be sent in the future.","recipientType":0,"deliveryTime":"2025-12-31T23:59:59Z","triggerType":0,"isEncrypted":false,"theme":"default"}
|
||||
@@ -1 +0,0 @@
|
||||
{"createDto":{"title":"My First Future Mail","content":"This is a test email that will be sent in the future.","recipientType":0,"deliveryTime":"2025-12-31T23:59:59Z","triggerType":0,"isEncrypted":false,"theme":"default"}}
|
||||
@@ -1,4 +0,0 @@
|
||||
// <autogenerated />
|
||||
using System;
|
||||
using System.Reflection;
|
||||
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v9.0", FrameworkDisplayName = ".NET 9.0")]
|
||||
@@ -1,22 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: System.Reflection.AssemblyCompanyAttribute("FutureMailAPI")]
|
||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+4e3cd6f3650e353b2eb1c64551c9d04565131e90")]
|
||||
[assembly: System.Reflection.AssemblyProductAttribute("FutureMailAPI")]
|
||||
[assembly: System.Reflection.AssemblyTitleAttribute("FutureMailAPI")]
|
||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||
|
||||
// 由 MSBuild WriteCodeFragment 类生成。
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
9c51bfff94b782fcc97f29ab7088592309efeb7bdb74c602e7b8fc76443617ec
|
||||
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
ea3656a12946e5802d7338be6d7da9f40d863a7215e7081b0501689571314e33
|
||||
51372bde626c0ba3aa5386f92d0ea465adcc4b558852e21737182a326708e608
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\FutureMailAPI.csproj.AssemblyReference.cache
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\rpswa.dswa.cache.json
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\FutureMailAPI.GeneratedMSBuildEditorConfig.editorconfig
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\FutureMailAPI.AssemblyInfoInputs.cache
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\FutureMailAPI.AssemblyInfo.cs
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\FutureMailAPI.csproj.CoreCompileInputs.cache
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\FutureMailAPI.MvcApplicationPartsAssemblyInfo.cs
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\FutureMailAPI.MvcApplicationPartsAssemblyInfo.cache
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\appsettings.Development.json
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\appsettings.json
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\FutureMailAPI.staticwebassets.runtime.json
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\FutureMailAPI.staticwebassets.endpoints.json
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\FutureMailAPI.exe
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\FutureMailAPI.deps.json
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\FutureMailAPI.runtimeconfig.json
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\FutureMailAPI.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\FutureMailAPI.pdb
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\FutureMailAPI.xml
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\Humanizer.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\Microsoft.AspNetCore.Authentication.JwtBearer.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\Microsoft.AspNetCore.OpenApi.dll
|
||||
@@ -25,10 +25,12 @@ C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\Microsoft.CodeAnalysis.Workspaces.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\Microsoft.CodeAnalysis.Workspaces.MSBuild.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\Microsoft.Data.Sqlite.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\Microsoft.EntityFrameworkCore.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\Microsoft.EntityFrameworkCore.Abstractions.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\Microsoft.EntityFrameworkCore.Design.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\Microsoft.EntityFrameworkCore.Relational.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\Microsoft.EntityFrameworkCore.Sqlite.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\Microsoft.Extensions.Caching.Abstractions.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\Microsoft.Extensions.Caching.Memory.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\Microsoft.Extensions.Configuration.Abstractions.dll
|
||||
@@ -52,6 +54,9 @@ C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\Quartz.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\Quartz.Extensions.DependencyInjection.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\Quartz.Extensions.Hosting.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\SQLitePCLRaw.batteries_v2.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\SQLitePCLRaw.core.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\SQLitePCLRaw.provider.e_sqlite3.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\Swashbuckle.AspNetCore.Swagger.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\Swashbuckle.AspNetCore.SwaggerGen.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\Swashbuckle.AspNetCore.SwaggerUI.dll
|
||||
@@ -127,27 +132,6 @@ C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\tr\Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\zh-Hans\Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\zh-Hant\Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\rjimswa.dswa.cache.json
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\rjsmrazor.dswa.cache.json
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\rjsmcshtml.dswa.cache.json
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\scopedcss\bundle\FutureMailAPI.styles.css
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\staticwebassets.build.json
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\staticwebassets.build.json.cache
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\staticwebassets.development.json
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\staticwebassets.build.endpoints.json
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\swae.build.ex.cache
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\FutureMa.9A5350ED.Up2Date
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\FutureMailAPI.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\refint\FutureMailAPI.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\FutureMailAPI.pdb
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\FutureMailAPI.genruntimeconfig.cache
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\ref\FutureMailAPI.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\FutureMailAPI.staticwebassets.runtime.json
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\Microsoft.Data.Sqlite.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\Microsoft.EntityFrameworkCore.Sqlite.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\SQLitePCLRaw.batteries_v2.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\SQLitePCLRaw.core.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\SQLitePCLRaw.provider.e_sqlite3.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\runtimes\browser-wasm\nativeassets\net9.0\e_sqlite3.a
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\runtimes\linux-arm\native\libe_sqlite3.so
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\runtimes\linux-arm64\native\libe_sqlite3.so
|
||||
@@ -169,9 +153,20 @@ C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\runtimes\win-arm64\native\e_sqlite3.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\runtimes\win-x64\native\e_sqlite3.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\runtimes\win-x86\native\e_sqlite3.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\FutureMailAPI.xml
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\rjimswa.dswa.cache.json
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\rjsmrazor.dswa.cache.json
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\rjsmcshtml.dswa.cache.json
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\scopedcss\bundle\FutureMailAPI.styles.css
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\staticwebassets.build.json
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\staticwebassets.build.json.cache
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\staticwebassets.development.json
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\staticwebassets.build.endpoints.json
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\swae.build.ex.cache
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\FutureMa.9A5350ED.Up2Date
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\FutureMailAPI.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\refint\FutureMailAPI.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\FutureMailAPI.xml
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\temp_register.json
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\test_mail.json
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\test_mail_direct.json
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\test_mail_simple.json
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\FutureMailAPI.pdb
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\FutureMailAPI.genruntimeconfig.cache
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\obj\Debug\net9.0\ref\FutureMailAPI.dll
|
||||
C:\Users\Administrator\Desktop\快乐转盘\未来邮箱02APi\FutureMailAPI\bin\Debug\net9.0\BCrypt.Net-Next.dll
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -25,11 +25,35 @@
|
||||
<param name="request">未来预测请求</param>
|
||||
<returns>未来预测结果</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.AIController.GetCurrentUserId">
|
||||
<member name="T:FutureMailAPI.Controllers.BaseController">
|
||||
<summary>
|
||||
从JWT令牌中获取当前用户ID
|
||||
基础控制器,提供通用的用户身份验证方法
|
||||
</summary>
|
||||
<returns>用户ID</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.BaseController.GetCurrentUserId">
|
||||
<summary>
|
||||
获取当前用户ID
|
||||
兼容OAuth中间件和JWT令牌两种验证方式
|
||||
</summary>
|
||||
<returns>用户ID,如果未认证则返回0</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.BaseController.GetCurrentUserEmail">
|
||||
<summary>
|
||||
获取当前用户邮箱
|
||||
</summary>
|
||||
<returns>用户邮箱,如果未认证则返回空字符串</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.BaseController.GetCurrentUsername">
|
||||
<summary>
|
||||
获取当前用户名
|
||||
</summary>
|
||||
<returns>用户名,如果未认证则返回空字符串</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.BaseController.GetCurrentClientId">
|
||||
<summary>
|
||||
获取当前客户端ID
|
||||
</summary>
|
||||
<returns>客户端ID,如果未认证则返回空字符串</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.FileUploadController.UploadAttachment(FutureMailAPI.DTOs.FileUploadWithFileRequestDto)">
|
||||
<summary>
|
||||
@@ -59,12 +83,6 @@
|
||||
<param name="fileId">文件ID</param>
|
||||
<returns>文件信息</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.FileUploadController.GetCurrentUserId">
|
||||
<summary>
|
||||
从当前请求中获取用户ID
|
||||
</summary>
|
||||
<returns>用户ID</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.NotificationController.RegisterDevice(FutureMailAPI.DTOs.NotificationDeviceRequestDto)">
|
||||
<summary>
|
||||
注册设备
|
||||
@@ -78,47 +96,6 @@
|
||||
</summary>
|
||||
<returns>通知设置</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.NotificationController.GetCurrentUserId">
|
||||
<summary>
|
||||
从JWT令牌中获取当前用户ID
|
||||
</summary>
|
||||
<returns>用户ID</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.OAuthController.Login(FutureMailAPI.DTOs.OAuthLoginDto)">
|
||||
<summary>
|
||||
OAuth登录端点
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.OAuthController.CreateClient(FutureMailAPI.DTOs.OAuthClientCreateDto)">
|
||||
<summary>
|
||||
创建OAuth客户端
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.OAuthController.GetClient(System.String)">
|
||||
<summary>
|
||||
获取OAuth客户端信息
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.OAuthController.Authorize(FutureMailAPI.DTOs.OAuthAuthorizationRequestDto)">
|
||||
<summary>
|
||||
OAuth授权端点
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.OAuthController.ExchangeToken(FutureMailAPI.DTOs.OAuthTokenRequestDto)">
|
||||
<summary>
|
||||
OAuth令牌端点
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.OAuthController.RevokeToken(System.String,System.String)">
|
||||
<summary>
|
||||
撤销令牌
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.OAuthController.IntrospectToken(System.String)">
|
||||
<summary>
|
||||
验证令牌
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.PersonalSpaceController.GetTimeline(FutureMailAPI.DTOs.TimelineType,System.Nullable{System.DateTime},System.Nullable{System.DateTime})">
|
||||
<summary>
|
||||
获取用户时间线
|
||||
@@ -146,24 +123,12 @@
|
||||
</summary>
|
||||
<returns>用户资料</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.PersonalSpaceController.GetCurrentUserId">
|
||||
<summary>
|
||||
从当前请求中获取用户ID
|
||||
</summary>
|
||||
<returns>用户ID</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.StatisticsController.GetStatistics">
|
||||
<summary>
|
||||
获取用户统计数据
|
||||
</summary>
|
||||
<returns>用户统计数据</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.StatisticsController.GetCurrentUserId">
|
||||
<summary>
|
||||
从JWT令牌中获取当前用户ID
|
||||
</summary>
|
||||
<returns>用户ID</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.TimelineController.GetTimeline(FutureMailAPI.DTOs.TimelineType,System.Nullable{System.DateTime},System.Nullable{System.DateTime})">
|
||||
<summary>
|
||||
获取用户时间线
|
||||
@@ -173,12 +138,6 @@
|
||||
<param name="endDate">结束日期</param>
|
||||
<returns>用户时间线</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.TimelineController.GetCurrentUserId">
|
||||
<summary>
|
||||
从JWT令牌中获取当前用户ID
|
||||
</summary>
|
||||
<returns>用户ID</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.UploadController.UploadAttachment(FutureMailAPI.DTOs.FileUploadWithFileRequestDto)">
|
||||
<summary>
|
||||
上传附件
|
||||
@@ -193,12 +152,6 @@
|
||||
<param name="request">文件上传请求</param>
|
||||
<returns>上传结果</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.UploadController.GetCurrentUserId">
|
||||
<summary>
|
||||
从JWT令牌中获取当前用户ID
|
||||
</summary>
|
||||
<returns>用户ID</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.UserController.GetSubscription">
|
||||
<summary>
|
||||
获取用户订阅信息
|
||||
@@ -211,25 +164,9 @@
|
||||
</summary>
|
||||
<returns>用户资料</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Controllers.UserController.GetCurrentUserId">
|
||||
<summary>
|
||||
从JWT令牌中获取当前用户ID
|
||||
</summary>
|
||||
<returns>用户ID</returns>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Extensions.HttpContextExtensions.GetCurrentUserId(Microsoft.AspNetCore.Http.HttpContext)">
|
||||
<summary>
|
||||
获取当前用户ID
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Extensions.HttpContextExtensions.GetCurrentUserEmail(Microsoft.AspNetCore.Http.HttpContext)">
|
||||
<summary>
|
||||
获取当前用户邮箱
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Extensions.HttpContextExtensions.GetCurrentAccessToken(Microsoft.AspNetCore.Http.HttpContext)">
|
||||
<summary>
|
||||
获取当前访问令牌
|
||||
获取当前用户ID(简化版本,不再依赖token)
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:FutureMailAPI.Helpers.FileUploadOperationFilter">
|
||||
@@ -261,18 +198,6 @@
|
||||
<member name="M:FutureMailAPI.Migrations.AddUserPreferences.BuildTargetModel(Microsoft.EntityFrameworkCore.ModelBuilder)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:FutureMailAPI.Migrations.AddOAuthEntities">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Migrations.AddOAuthEntities.Up(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Migrations.AddOAuthEntities.Down(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Migrations.AddOAuthEntities.BuildTargetModel(Microsoft.EntityFrameworkCore.ModelBuilder)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:FutureMailAPI.Migrations.AddSaltToUser">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
@@ -282,8 +207,5 @@
|
||||
<member name="M:FutureMailAPI.Migrations.AddSaltToUser.Down(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:FutureMailAPI.Migrations.AddSaltToUser.BuildTargetModel(Microsoft.EntityFrameworkCore.ModelBuilder)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
{"GlobalPropertiesHash":"1nyXR9zdL54Badakr4zt6ZsTCwUunwdqRSmf7XLLUwI=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["PSBb4S8lcZQPImBE8id7O4eeN8h3whFn6j1jGYFQciQ=","Dh2M8KitOfKPR8IeSNkaC81VB\u002BMSAUycC8vgnJB96As=","t2RF\u002B1UZM5Hw6aYb0b251h1IYJ0pLy2XznEprAc7\u002Bok=","2FbYKHJBLTvh9uGYDfvrsS/W7A7tAnJew9pbpj5fD0c=","2w8tqUWtgtJR3X6RHUFwY6ycOgc9but1QXTeF3XoSAs=","1UDkwWB80qnO\u002B89ANbOkMura0UnCvX\u002B8qPfDZHovrt4=","pIoP9frnT632kkjB7SjrifWUQVG7c11SzIkVZRRvB50=","9kb7O83kAqQlgU/oLY\u002BLtIyvuGGaaCehodVF5CULg2w=","KtJa1U2aUQv2tuOjiicNgBLGEaKYqPhKaVpzqk4t85k=","XAE6ulqLzJRH50\u002Bc9Nteizd/x9s3rvSHUFwFL265XX4=","OWMR9yjuLx9nyoGL7u9arYiy/fkFHjayjhOVF\u002BiRuS0=","MbGDnaS5Z1urQXC0aeolLZu50a5W0ICU1IGUKW06M0s=","Qf4B5yCiEiASjhutpOPj/Oq2gQPQj6e4MCKc90vHMnw=","rnxpfH7HwD1\u002BMHTk01\u002BjopiQ58RyX\u002B9ZqhNY56R608M="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
{"GlobalPropertiesHash":"1nyXR9zdL54Badakr4zt6ZsTCwUunwdqRSmf7XLLUwI=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["PSBb4S8lcZQPImBE8id7O4eeN8h3whFn6j1jGYFQciQ=","FLPLXKwQVK16UZsWKzZrjH5kq4sMEtCZqAIkpUwa2pU=","/ljuLPXzsWglGr0uHSpqYQARCPlOQrAX6k6LWku0gdQ=","XAE6ulqLzJRH50\u002Bc9Nteizd/x9s3rvSHUFwFL265XX4=","talZRfyIQIv4aZc27HTn01\u002B12VbY6LMFOy3\u002B3r448jo=","8GY2MSzr2E/Yc9xFiAqxn3IhTsjnylxYZUVwMfrW5vw="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
@@ -1 +1 @@
|
||||
{"GlobalPropertiesHash":"hRzLFfhtQD0jC2zNthCXf5A5W0LGZjQdHMs0v5Enof8=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["PSBb4S8lcZQPImBE8id7O4eeN8h3whFn6j1jGYFQciQ=","Dh2M8KitOfKPR8IeSNkaC81VB\u002BMSAUycC8vgnJB96As=","t2RF\u002B1UZM5Hw6aYb0b251h1IYJ0pLy2XznEprAc7\u002Bok=","2FbYKHJBLTvh9uGYDfvrsS/W7A7tAnJew9pbpj5fD0c=","2w8tqUWtgtJR3X6RHUFwY6ycOgc9but1QXTeF3XoSAs=","1UDkwWB80qnO\u002B89ANbOkMura0UnCvX\u002B8qPfDZHovrt4=","pIoP9frnT632kkjB7SjrifWUQVG7c11SzIkVZRRvB50=","9kb7O83kAqQlgU/oLY\u002BLtIyvuGGaaCehodVF5CULg2w=","KtJa1U2aUQv2tuOjiicNgBLGEaKYqPhKaVpzqk4t85k=","XAE6ulqLzJRH50\u002Bc9Nteizd/x9s3rvSHUFwFL265XX4=","OWMR9yjuLx9nyoGL7u9arYiy/fkFHjayjhOVF\u002BiRuS0=","MbGDnaS5Z1urQXC0aeolLZu50a5W0ICU1IGUKW06M0s=","Qf4B5yCiEiASjhutpOPj/Oq2gQPQj6e4MCKc90vHMnw=","rnxpfH7HwD1\u002BMHTk01\u002BjopiQ58RyX\u002B9ZqhNY56R608M="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
{"GlobalPropertiesHash":"hRzLFfhtQD0jC2zNthCXf5A5W0LGZjQdHMs0v5Enof8=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["PSBb4S8lcZQPImBE8id7O4eeN8h3whFn6j1jGYFQciQ=","FLPLXKwQVK16UZsWKzZrjH5kq4sMEtCZqAIkpUwa2pU=","/ljuLPXzsWglGr0uHSpqYQARCPlOQrAX6k6LWku0gdQ=","XAE6ulqLzJRH50\u002Bc9Nteizd/x9s3rvSHUFwFL265XX4=","talZRfyIQIv4aZc27HTn01\u002B12VbY6LMFOy3\u002B3r448jo=","8GY2MSzr2E/Yc9xFiAqxn3IhTsjnylxYZUVwMfrW5vw="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
@@ -1 +1 @@
|
||||
{"GlobalPropertiesHash":"Bto0zkaTl4M6gb1C4K2QiQDhFhr9nJky761xwMrocfc=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["PSBb4S8lcZQPImBE8id7O4eeN8h3whFn6j1jGYFQciQ=","Dh2M8KitOfKPR8IeSNkaC81VB\u002BMSAUycC8vgnJB96As=","t2RF\u002B1UZM5Hw6aYb0b251h1IYJ0pLy2XznEprAc7\u002Bok=","2FbYKHJBLTvh9uGYDfvrsS/W7A7tAnJew9pbpj5fD0c=","2w8tqUWtgtJR3X6RHUFwY6ycOgc9but1QXTeF3XoSAs=","1UDkwWB80qnO\u002B89ANbOkMura0UnCvX\u002B8qPfDZHovrt4="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
{"GlobalPropertiesHash":"Bto0zkaTl4M6gb1C4K2QiQDhFhr9nJky761xwMrocfc=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["PSBb4S8lcZQPImBE8id7O4eeN8h3whFn6j1jGYFQciQ=","FLPLXKwQVK16UZsWKzZrjH5kq4sMEtCZqAIkpUwa2pU="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
@@ -51,6 +51,10 @@
|
||||
"net9.0": {
|
||||
"targetAlias": "net9.0",
|
||||
"dependencies": {
|
||||
"BCrypt.Net-Next": {
|
||||
"target": "Package",
|
||||
"version": "[4.0.3, )"
|
||||
},
|
||||
"Microsoft.AspNetCore.Authentication.JwtBearer": {
|
||||
"target": "Package",
|
||||
"version": "[9.0.9, )"
|
||||
@@ -75,6 +79,10 @@
|
||||
"target": "Package",
|
||||
"version": "[9.0.9, )"
|
||||
},
|
||||
"Microsoft.IdentityModel.Tokens": {
|
||||
"target": "Package",
|
||||
"version": "[8.3.0, )"
|
||||
},
|
||||
"Pomelo.EntityFrameworkCore.MySql": {
|
||||
"target": "Package",
|
||||
"version": "[9.0.0, )"
|
||||
@@ -93,7 +101,7 @@
|
||||
},
|
||||
"System.IdentityModel.Tokens.Jwt": {
|
||||
"target": "Package",
|
||||
"version": "[8.14.0, )"
|
||||
"version": "[8.3.0, )"
|
||||
}
|
||||
},
|
||||
"imports": [
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<Import Project="$(NuGetPackageRoot)system.text.json\9.0.9\buildTransitive\net8.0\System.Text.Json.targets" Condition="Exists('$(NuGetPackageRoot)system.text.json\9.0.9\buildTransitive\net8.0\System.Text.Json.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.extensions.logging.abstractions\9.0.9\buildTransitive\net8.0\Microsoft.Extensions.Logging.Abstractions.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.logging.abstractions\9.0.9\buildTransitive\net8.0\Microsoft.Extensions.Logging.Abstractions.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.extensions.apidescription.server\9.0.0\build\Microsoft.Extensions.ApiDescription.Server.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.apidescription.server\9.0.0\build\Microsoft.Extensions.ApiDescription.Server.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)sqlitepclraw.lib.e_sqlite3\2.1.10\buildTransitive\net9.0\SQLitePCLRaw.lib.e_sqlite3.targets" Condition="Exists('$(NuGetPackageRoot)sqlitepclraw.lib.e_sqlite3\2.1.10\buildTransitive\net9.0\SQLitePCLRaw.lib.e_sqlite3.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.extensions.logging.abstractions\9.0.9\buildTransitive\net8.0\Microsoft.Extensions.Logging.Abstractions.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.logging.abstractions\9.0.9\buildTransitive\net8.0\Microsoft.Extensions.Logging.Abstractions.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.extensions.options\9.0.9\buildTransitive\net8.0\Microsoft.Extensions.Options.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.options\9.0.9\buildTransitive\net8.0\Microsoft.Extensions.Options.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)mono.texttemplating\3.0.0\buildTransitive\Mono.TextTemplating.targets" Condition="Exists('$(NuGetPackageRoot)mono.texttemplating\3.0.0\buildTransitive\Mono.TextTemplating.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.codeanalysis.analyzers\3.3.4\buildTransitive\Microsoft.CodeAnalysis.Analyzers.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.codeanalysis.analyzers\3.3.4\buildTransitive\Microsoft.CodeAnalysis.Analyzers.targets')" />
|
||||
|
||||
@@ -2,6 +2,19 @@
|
||||
"version": 3,
|
||||
"targets": {
|
||||
"net9.0": {
|
||||
"BCrypt.Net-Next/4.0.3": {
|
||||
"type": "package",
|
||||
"compile": {
|
||||
"lib/net6.0/BCrypt.Net-Next.dll": {
|
||||
"related": ".xml"
|
||||
}
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net6.0/BCrypt.Net-Next.dll": {
|
||||
"related": ".xml"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Humanizer.Core/2.14.1": {
|
||||
"type": "package",
|
||||
"compile": {
|
||||
@@ -813,7 +826,7 @@
|
||||
"buildTransitive/net8.0/_._": {}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Abstractions/8.14.0": {
|
||||
"Microsoft.IdentityModel.Abstractions/8.3.0": {
|
||||
"type": "package",
|
||||
"compile": {
|
||||
"lib/net9.0/Microsoft.IdentityModel.Abstractions.dll": {
|
||||
@@ -826,10 +839,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.JsonWebTokens/8.14.0": {
|
||||
"Microsoft.IdentityModel.JsonWebTokens/8.3.0": {
|
||||
"type": "package",
|
||||
"dependencies": {
|
||||
"Microsoft.IdentityModel.Tokens": "8.14.0"
|
||||
"Microsoft.IdentityModel.Tokens": "8.3.0"
|
||||
},
|
||||
"compile": {
|
||||
"lib/net9.0/Microsoft.IdentityModel.JsonWebTokens.dll": {
|
||||
@@ -842,10 +855,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Logging/8.14.0": {
|
||||
"Microsoft.IdentityModel.Logging/8.3.0": {
|
||||
"type": "package",
|
||||
"dependencies": {
|
||||
"Microsoft.IdentityModel.Abstractions": "8.14.0"
|
||||
"Microsoft.IdentityModel.Abstractions": "8.3.0"
|
||||
},
|
||||
"compile": {
|
||||
"lib/net9.0/Microsoft.IdentityModel.Logging.dll": {
|
||||
@@ -891,11 +904,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Tokens/8.14.0": {
|
||||
"Microsoft.IdentityModel.Tokens/8.3.0": {
|
||||
"type": "package",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.0",
|
||||
"Microsoft.IdentityModel.Logging": "8.14.0"
|
||||
"Microsoft.IdentityModel.Logging": "8.3.0"
|
||||
},
|
||||
"compile": {
|
||||
"lib/net9.0/Microsoft.IdentityModel.Tokens.dll": {
|
||||
@@ -1364,11 +1376,11 @@
|
||||
"buildTransitive/net6.0/_._": {}
|
||||
}
|
||||
},
|
||||
"System.IdentityModel.Tokens.Jwt/8.14.0": {
|
||||
"System.IdentityModel.Tokens.Jwt/8.3.0": {
|
||||
"type": "package",
|
||||
"dependencies": {
|
||||
"Microsoft.IdentityModel.JsonWebTokens": "8.14.0",
|
||||
"Microsoft.IdentityModel.Tokens": "8.14.0"
|
||||
"Microsoft.IdentityModel.JsonWebTokens": "8.3.0",
|
||||
"Microsoft.IdentityModel.Tokens": "8.3.0"
|
||||
},
|
||||
"compile": {
|
||||
"lib/net9.0/System.IdentityModel.Tokens.Jwt.dll": {
|
||||
@@ -1476,6 +1488,37 @@
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"BCrypt.Net-Next/4.0.3": {
|
||||
"sha512": "W+U9WvmZQgi5cX6FS5GDtDoPzUCV4LkBLkywq/kRZhuDwcbavOzcDAr3LXJFqHUi952Yj3LEYoWW0jbEUQChsA==",
|
||||
"type": "package",
|
||||
"path": "bcrypt.net-next/4.0.3",
|
||||
"files": [
|
||||
".nupkg.metadata",
|
||||
".signature.p7s",
|
||||
"bcrypt.net-next.4.0.3.nupkg.sha512",
|
||||
"bcrypt.net-next.nuspec",
|
||||
"ico.png",
|
||||
"lib/net20/BCrypt.Net-Next.dll",
|
||||
"lib/net20/BCrypt.Net-Next.xml",
|
||||
"lib/net35/BCrypt.Net-Next.dll",
|
||||
"lib/net35/BCrypt.Net-Next.xml",
|
||||
"lib/net462/BCrypt.Net-Next.dll",
|
||||
"lib/net462/BCrypt.Net-Next.xml",
|
||||
"lib/net472/BCrypt.Net-Next.dll",
|
||||
"lib/net472/BCrypt.Net-Next.xml",
|
||||
"lib/net48/BCrypt.Net-Next.dll",
|
||||
"lib/net48/BCrypt.Net-Next.xml",
|
||||
"lib/net5.0/BCrypt.Net-Next.dll",
|
||||
"lib/net5.0/BCrypt.Net-Next.xml",
|
||||
"lib/net6.0/BCrypt.Net-Next.dll",
|
||||
"lib/net6.0/BCrypt.Net-Next.xml",
|
||||
"lib/netstandard2.0/BCrypt.Net-Next.dll",
|
||||
"lib/netstandard2.0/BCrypt.Net-Next.xml",
|
||||
"lib/netstandard2.1/BCrypt.Net-Next.dll",
|
||||
"lib/netstandard2.1/BCrypt.Net-Next.xml",
|
||||
"readme.md"
|
||||
]
|
||||
},
|
||||
"Humanizer.Core/2.14.1": {
|
||||
"sha512": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==",
|
||||
"type": "package",
|
||||
@@ -3345,10 +3388,10 @@
|
||||
"useSharedDesignerContext.txt"
|
||||
]
|
||||
},
|
||||
"Microsoft.IdentityModel.Abstractions/8.14.0": {
|
||||
"sha512": "iwbCpSjD3ehfTwBhtSNEtKPK0ICun6ov7Ibx6ISNA9bfwIyzI2Siwyi9eJFCJBwxowK9xcA1mj+jBWiigeqgcQ==",
|
||||
"Microsoft.IdentityModel.Abstractions/8.3.0": {
|
||||
"sha512": "jNin7yvWZu+K3U24q+6kD+LmGSRfbkHl9Px8hN1XrGwq6ZHgKGi/zuTm5m08G27fwqKfVXIWuIcUeq4Y1VQUOg==",
|
||||
"type": "package",
|
||||
"path": "microsoft.identitymodel.abstractions/8.14.0",
|
||||
"path": "microsoft.identitymodel.abstractions/8.3.0",
|
||||
"files": [
|
||||
".nupkg.metadata",
|
||||
".signature.p7s",
|
||||
@@ -3365,14 +3408,14 @@
|
||||
"lib/net9.0/Microsoft.IdentityModel.Abstractions.xml",
|
||||
"lib/netstandard2.0/Microsoft.IdentityModel.Abstractions.dll",
|
||||
"lib/netstandard2.0/Microsoft.IdentityModel.Abstractions.xml",
|
||||
"microsoft.identitymodel.abstractions.8.14.0.nupkg.sha512",
|
||||
"microsoft.identitymodel.abstractions.8.3.0.nupkg.sha512",
|
||||
"microsoft.identitymodel.abstractions.nuspec"
|
||||
]
|
||||
},
|
||||
"Microsoft.IdentityModel.JsonWebTokens/8.14.0": {
|
||||
"sha512": "4jOpiA4THdtpLyMdAb24dtj7+6GmvhOhxf5XHLYWmPKF8ApEnApal1UnJsKO4HxUWRXDA6C4WQVfYyqsRhpNpQ==",
|
||||
"Microsoft.IdentityModel.JsonWebTokens/8.3.0": {
|
||||
"sha512": "4SVXLT8sDG7CrHiszEBrsDYi+aDW0W9d+fuWUGdZPBdan56aM6fGXJDjbI0TVGEDjJhXbACQd8F/BnC7a+m2RQ==",
|
||||
"type": "package",
|
||||
"path": "microsoft.identitymodel.jsonwebtokens/8.14.0",
|
||||
"path": "microsoft.identitymodel.jsonwebtokens/8.3.0",
|
||||
"files": [
|
||||
".nupkg.metadata",
|
||||
".signature.p7s",
|
||||
@@ -3389,14 +3432,14 @@
|
||||
"lib/net9.0/Microsoft.IdentityModel.JsonWebTokens.xml",
|
||||
"lib/netstandard2.0/Microsoft.IdentityModel.JsonWebTokens.dll",
|
||||
"lib/netstandard2.0/Microsoft.IdentityModel.JsonWebTokens.xml",
|
||||
"microsoft.identitymodel.jsonwebtokens.8.14.0.nupkg.sha512",
|
||||
"microsoft.identitymodel.jsonwebtokens.8.3.0.nupkg.sha512",
|
||||
"microsoft.identitymodel.jsonwebtokens.nuspec"
|
||||
]
|
||||
},
|
||||
"Microsoft.IdentityModel.Logging/8.14.0": {
|
||||
"sha512": "eqqnemdW38CKZEHS6diA50BV94QICozDZEvSrsvN3SJXUFwVB9gy+/oz76gldP7nZliA16IglXjXTCTdmU/Ejg==",
|
||||
"Microsoft.IdentityModel.Logging/8.3.0": {
|
||||
"sha512": "4w4pSIGHhCCLTHqtVNR2Cc/zbDIUWIBHTZCu/9ZHm2SVwrXY3RJMcZ7EFGiKqmKZMQZJzA0bpwCZ6R8Yb7i5VQ==",
|
||||
"type": "package",
|
||||
"path": "microsoft.identitymodel.logging/8.14.0",
|
||||
"path": "microsoft.identitymodel.logging/8.3.0",
|
||||
"files": [
|
||||
".nupkg.metadata",
|
||||
".signature.p7s",
|
||||
@@ -3413,7 +3456,7 @@
|
||||
"lib/net9.0/Microsoft.IdentityModel.Logging.xml",
|
||||
"lib/netstandard2.0/Microsoft.IdentityModel.Logging.dll",
|
||||
"lib/netstandard2.0/Microsoft.IdentityModel.Logging.xml",
|
||||
"microsoft.identitymodel.logging.8.14.0.nupkg.sha512",
|
||||
"microsoft.identitymodel.logging.8.3.0.nupkg.sha512",
|
||||
"microsoft.identitymodel.logging.nuspec"
|
||||
]
|
||||
},
|
||||
@@ -3463,10 +3506,10 @@
|
||||
"microsoft.identitymodel.protocols.openidconnect.nuspec"
|
||||
]
|
||||
},
|
||||
"Microsoft.IdentityModel.Tokens/8.14.0": {
|
||||
"sha512": "lKIZiBiGd36k02TCdMHp1KlNWisyIvQxcYJvIkz7P4gSQ9zi8dgh6S5Grj8NNG7HWYIPfQymGyoZ6JB5d1Lo1g==",
|
||||
"Microsoft.IdentityModel.Tokens/8.3.0": {
|
||||
"sha512": "yGzqmk+kInH50zeSEH/L1/J0G4/yqTQNq4YmdzOhpE7s/86tz37NS2YbbY2ievbyGjmeBI1mq26QH+yBR6AK3Q==",
|
||||
"type": "package",
|
||||
"path": "microsoft.identitymodel.tokens/8.14.0",
|
||||
"path": "microsoft.identitymodel.tokens/8.3.0",
|
||||
"files": [
|
||||
".nupkg.metadata",
|
||||
".signature.p7s",
|
||||
@@ -3483,7 +3526,7 @@
|
||||
"lib/net9.0/Microsoft.IdentityModel.Tokens.xml",
|
||||
"lib/netstandard2.0/Microsoft.IdentityModel.Tokens.dll",
|
||||
"lib/netstandard2.0/Microsoft.IdentityModel.Tokens.xml",
|
||||
"microsoft.identitymodel.tokens.8.14.0.nupkg.sha512",
|
||||
"microsoft.identitymodel.tokens.8.3.0.nupkg.sha512",
|
||||
"microsoft.identitymodel.tokens.nuspec"
|
||||
]
|
||||
},
|
||||
@@ -3992,10 +4035,10 @@
|
||||
"useSharedDesignerContext.txt"
|
||||
]
|
||||
},
|
||||
"System.IdentityModel.Tokens.Jwt/8.14.0": {
|
||||
"sha512": "EYGgN/S+HK7S6F3GaaPLFAfK0UzMrkXFyWCvXpQWFYmZln3dqtbyIO7VuTM/iIIPMzkelg8ZLlBPvMhxj6nOAA==",
|
||||
"System.IdentityModel.Tokens.Jwt/8.3.0": {
|
||||
"sha512": "9GESpDG0Zb17HD5mBW/uEWi2yz/uKPmCthX2UhyLnk42moGH2FpMgXA2Y4l2Qc7P75eXSUTA6wb/c9D9GSVkzw==",
|
||||
"type": "package",
|
||||
"path": "system.identitymodel.tokens.jwt/8.14.0",
|
||||
"path": "system.identitymodel.tokens.jwt/8.3.0",
|
||||
"files": [
|
||||
".nupkg.metadata",
|
||||
".signature.p7s",
|
||||
@@ -4012,7 +4055,7 @@
|
||||
"lib/net9.0/System.IdentityModel.Tokens.Jwt.xml",
|
||||
"lib/netstandard2.0/System.IdentityModel.Tokens.Jwt.dll",
|
||||
"lib/netstandard2.0/System.IdentityModel.Tokens.Jwt.xml",
|
||||
"system.identitymodel.tokens.jwt.8.14.0.nupkg.sha512",
|
||||
"system.identitymodel.tokens.jwt.8.3.0.nupkg.sha512",
|
||||
"system.identitymodel.tokens.jwt.nuspec"
|
||||
]
|
||||
},
|
||||
@@ -4220,16 +4263,18 @@
|
||||
},
|
||||
"projectFileDependencyGroups": {
|
||||
"net9.0": [
|
||||
"BCrypt.Net-Next >= 4.0.3",
|
||||
"Microsoft.AspNetCore.Authentication.JwtBearer >= 9.0.9",
|
||||
"Microsoft.AspNetCore.OpenApi >= 9.0.9",
|
||||
"Microsoft.EntityFrameworkCore.Design >= 9.0.9",
|
||||
"Microsoft.EntityFrameworkCore.Sqlite >= 9.0.9",
|
||||
"Microsoft.EntityFrameworkCore.Tools >= 9.0.9",
|
||||
"Microsoft.IdentityModel.Tokens >= 8.3.0",
|
||||
"Pomelo.EntityFrameworkCore.MySql >= 9.0.0",
|
||||
"Quartz >= 3.15.0",
|
||||
"Quartz.Extensions.Hosting >= 3.15.0",
|
||||
"Swashbuckle.AspNetCore >= 9.0.6",
|
||||
"System.IdentityModel.Tokens.Jwt >= 8.14.0"
|
||||
"System.IdentityModel.Tokens.Jwt >= 8.3.0"
|
||||
]
|
||||
},
|
||||
"packageFolders": {
|
||||
@@ -4283,6 +4328,10 @@
|
||||
"net9.0": {
|
||||
"targetAlias": "net9.0",
|
||||
"dependencies": {
|
||||
"BCrypt.Net-Next": {
|
||||
"target": "Package",
|
||||
"version": "[4.0.3, )"
|
||||
},
|
||||
"Microsoft.AspNetCore.Authentication.JwtBearer": {
|
||||
"target": "Package",
|
||||
"version": "[9.0.9, )"
|
||||
@@ -4307,6 +4356,10 @@
|
||||
"target": "Package",
|
||||
"version": "[9.0.9, )"
|
||||
},
|
||||
"Microsoft.IdentityModel.Tokens": {
|
||||
"target": "Package",
|
||||
"version": "[8.3.0, )"
|
||||
},
|
||||
"Pomelo.EntityFrameworkCore.MySql": {
|
||||
"target": "Package",
|
||||
"version": "[9.0.0, )"
|
||||
@@ -4325,7 +4378,7 @@
|
||||
},
|
||||
"System.IdentityModel.Tokens.Jwt": {
|
||||
"target": "Package",
|
||||
"version": "[8.14.0, )"
|
||||
"version": "[8.3.0, )"
|
||||
}
|
||||
},
|
||||
"imports": [
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"version": 2,
|
||||
"dgSpecHash": "u8eeDj4lDD4=",
|
||||
"dgSpecHash": "s9N/V2Z2v1w=",
|
||||
"success": true,
|
||||
"projectFilePath": "C:\\Users\\Administrator\\Desktop\\快乐转盘\\未来邮箱02APi\\FutureMailAPI\\FutureMailAPI.csproj",
|
||||
"expectedPackageFiles": [
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\bcrypt.net-next\\4.0.3\\bcrypt.net-next.4.0.3.nupkg.sha512",
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\humanizer.core\\2.14.1\\humanizer.core.2.14.1.nupkg.sha512",
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\microsoft.aspnetcore.authentication.jwtbearer\\9.0.9\\microsoft.aspnetcore.authentication.jwtbearer.9.0.9.nupkg.sha512",
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\microsoft.aspnetcore.openapi\\9.0.9\\microsoft.aspnetcore.openapi.9.0.9.nupkg.sha512",
|
||||
@@ -39,12 +40,12 @@
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\microsoft.extensions.logging.abstractions\\9.0.9\\microsoft.extensions.logging.abstractions.9.0.9.nupkg.sha512",
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\microsoft.extensions.options\\9.0.9\\microsoft.extensions.options.9.0.9.nupkg.sha512",
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\microsoft.extensions.primitives\\9.0.9\\microsoft.extensions.primitives.9.0.9.nupkg.sha512",
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\microsoft.identitymodel.abstractions\\8.14.0\\microsoft.identitymodel.abstractions.8.14.0.nupkg.sha512",
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\microsoft.identitymodel.jsonwebtokens\\8.14.0\\microsoft.identitymodel.jsonwebtokens.8.14.0.nupkg.sha512",
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\microsoft.identitymodel.logging\\8.14.0\\microsoft.identitymodel.logging.8.14.0.nupkg.sha512",
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\microsoft.identitymodel.abstractions\\8.3.0\\microsoft.identitymodel.abstractions.8.3.0.nupkg.sha512",
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\microsoft.identitymodel.jsonwebtokens\\8.3.0\\microsoft.identitymodel.jsonwebtokens.8.3.0.nupkg.sha512",
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\microsoft.identitymodel.logging\\8.3.0\\microsoft.identitymodel.logging.8.3.0.nupkg.sha512",
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\microsoft.identitymodel.protocols\\8.0.1\\microsoft.identitymodel.protocols.8.0.1.nupkg.sha512",
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\microsoft.identitymodel.protocols.openidconnect\\8.0.1\\microsoft.identitymodel.protocols.openidconnect.8.0.1.nupkg.sha512",
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\microsoft.identitymodel.tokens\\8.14.0\\microsoft.identitymodel.tokens.8.14.0.nupkg.sha512",
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\microsoft.identitymodel.tokens\\8.3.0\\microsoft.identitymodel.tokens.8.3.0.nupkg.sha512",
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\microsoft.openapi\\1.6.25\\microsoft.openapi.1.6.25.nupkg.sha512",
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\mono.texttemplating\\3.0.0\\mono.texttemplating.3.0.0.nupkg.sha512",
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\mysqlconnector\\2.4.0\\mysqlconnector.2.4.0.nupkg.sha512",
|
||||
@@ -68,7 +69,7 @@
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\system.composition.hosting\\7.0.0\\system.composition.hosting.7.0.0.nupkg.sha512",
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\system.composition.runtime\\7.0.0\\system.composition.runtime.7.0.0.nupkg.sha512",
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\system.composition.typedparts\\7.0.0\\system.composition.typedparts.7.0.0.nupkg.sha512",
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\system.identitymodel.tokens.jwt\\8.14.0\\system.identitymodel.tokens.jwt.8.14.0.nupkg.sha512",
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\system.identitymodel.tokens.jwt\\8.3.0\\system.identitymodel.tokens.jwt.8.3.0.nupkg.sha512",
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\system.io.pipelines\\7.0.0\\system.io.pipelines.7.0.0.nupkg.sha512",
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\system.memory\\4.5.3\\system.memory.4.5.3.nupkg.sha512",
|
||||
"C:\\Users\\Administrator\\.nuget\\packages\\system.reflection.metadata\\7.0.0\\system.reflection.metadata.7.0.0.nupkg.sha512",
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"createDto": {
|
||||
"title": "我的第一封未来邮件",
|
||||
"content": "这是一封测试邮件,将在未来某个时间点发送。",
|
||||
"recipientType": 0,
|
||||
"deliveryTime": "2025-12-31T23:59:59Z",
|
||||
"triggerType": 0,
|
||||
"isEncrypted": false,
|
||||
"theme": "default"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
{"title":"My First Future Mail","content":"This is a test email that will be sent in the future.","recipientType":0,"deliveryTime":"2025-12-31T23:59:59Z","triggerType":0,"isEncrypted":false,"theme":"default"}
|
||||
@@ -1 +0,0 @@
|
||||
{"createDto":{"title":"My First Future Mail","content":"This is a test email that will be sent in the future.","recipientType":0,"deliveryTime":"2025-12-31T23:59:59Z","triggerType":0,"isEncrypted":false,"theme":"default"}}
|
||||
63
test_mail.html
Normal file
63
test_mail.html
Normal file
@@ -0,0 +1,63 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>邮件创建测试</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>邮件创建测试</h1>
|
||||
<button onclick="testLogin()">登录</button>
|
||||
<button onclick="testCreateMail()">创建邮件</button>
|
||||
<div id="result"></div>
|
||||
|
||||
<script>
|
||||
let authToken = '';
|
||||
|
||||
async function testLogin() {
|
||||
try {
|
||||
const response = await axios.post('http://localhost:5003/api/v1/auth/login', {
|
||||
email: 'test@example.com',
|
||||
password: 'test123'
|
||||
});
|
||||
|
||||
authToken = response.data.data.token;
|
||||
document.getElementById('result').innerHTML = '<pre>登录成功,令牌: ' + authToken + '</pre>';
|
||||
} catch (error) {
|
||||
document.getElementById('result').innerHTML = '<pre>登录失败: ' + JSON.stringify(error.response.data, null, 2) + '</pre>';
|
||||
}
|
||||
}
|
||||
|
||||
async function testCreateMail() {
|
||||
if (!authToken) {
|
||||
document.getElementById('result').innerHTML = '<pre>请先登录</pre>';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const mailData = {
|
||||
title: "测试邮件标题",
|
||||
content: "这是一封测试邮件的内容",
|
||||
recipientType: "SELF",
|
||||
sendTime: "2026-10-16T08:03:58.479Z",
|
||||
triggerType: "TIME",
|
||||
triggerCondition: {},
|
||||
attachments: [],
|
||||
isEncrypted: false,
|
||||
capsuleStyle: "default"
|
||||
};
|
||||
|
||||
const response = await axios.post('http://localhost:5003/api/v1/mails/create', mailData, {
|
||||
headers: {
|
||||
'Authorization': 'Bearer ' + authToken,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('result').innerHTML = '<pre>邮件创建成功: ' + JSON.stringify(response.data, null, 2) + '</pre>';
|
||||
} catch (error) {
|
||||
document.getElementById('result').innerHTML = '<pre>邮件创建失败: ' + JSON.stringify(error.response.data, null, 2) + '</pre>';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
11
test_mail_fixed.json
Normal file
11
test_mail_fixed.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"title": "测试邮件标题",
|
||||
"content": "这是一封测试邮件的内容",
|
||||
"recipientType": "SELF",
|
||||
"sendTime": "2026-10-16T08:03:58.479Z",
|
||||
"triggerType": "TIME",
|
||||
"triggerCondition": {},
|
||||
"attachments": [],
|
||||
"isEncrypted": false,
|
||||
"capsuleStyle": "default"
|
||||
}
|
||||
Reference in New Issue
Block a user