mirror of
https://github.com/DrizzleTime/Foxel.git
synced 2026-05-11 18:10:10 +08:00
refactor(storage): update storage providers to use IConfigService for configuration management
This commit is contained in:
@@ -14,9 +14,17 @@ public class AiService : IAiService
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_configService = configService;
|
||||
}
|
||||
|
||||
private void ConfigureHttpClient()
|
||||
{
|
||||
string apiKey = _configService["AI:ApiKey"];
|
||||
string baseUrl = _configService["AI:ApiEndpoint"];
|
||||
_httpClient.BaseAddress = new Uri(baseUrl);
|
||||
|
||||
if (_httpClient.BaseAddress?.ToString() != baseUrl)
|
||||
{
|
||||
_httpClient.BaseAddress = new Uri(baseUrl);
|
||||
}
|
||||
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
|
||||
}
|
||||
|
||||
@@ -24,6 +32,7 @@ public class AiService : IAiService
|
||||
{
|
||||
try
|
||||
{
|
||||
ConfigureHttpClient();
|
||||
string model = _configService["AI:Model"];
|
||||
var imageUrl = new ImageUrl
|
||||
{
|
||||
@@ -83,10 +92,13 @@ public class AiService : IAiService
|
||||
{
|
||||
try
|
||||
{
|
||||
// 确保使用最新配置
|
||||
ConfigureHttpClient();
|
||||
|
||||
if (availableTags.Count == 0)
|
||||
return new List<string>();
|
||||
|
||||
string model = _configService["AI:Model"];
|
||||
string model = _configService["AI:Model"];
|
||||
var tagsText = string.Join(", ", availableTags);
|
||||
var textContent = new TextContent
|
||||
{
|
||||
@@ -189,8 +201,10 @@ public class AiService : IAiService
|
||||
{
|
||||
try
|
||||
{
|
||||
// 确保使用最新配置
|
||||
ConfigureHttpClient();
|
||||
|
||||
string model = _configService["AI:Model"];
|
||||
string model = _configService["AI:Model"];
|
||||
|
||||
var imageUrl = new ImageUrl
|
||||
{
|
||||
@@ -329,6 +343,8 @@ public class AiService : IAiService
|
||||
{
|
||||
try
|
||||
{
|
||||
// 确保使用最新配置
|
||||
ConfigureHttpClient();
|
||||
|
||||
string model = _configService["AI:EmbeddingModel"];
|
||||
|
||||
|
||||
@@ -39,42 +39,19 @@ public class CustomQCloudCredentialProvider : DefaultSessionQCloudCredentialProv
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[StorageProvider(StorageType.Cos)]
|
||||
public class CosStorageProvider : IStorageProvider
|
||||
public class CosStorageProvider(IConfigService configService) : IStorageProvider
|
||||
{
|
||||
private readonly string _bucketName;
|
||||
private readonly string _region;
|
||||
private readonly string _cdnUrl;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly CosXml _cosXmlClient;
|
||||
|
||||
private readonly bool _isPublicRead;
|
||||
|
||||
public CosStorageProvider(IConfigService configService)
|
||||
{
|
||||
_configService = configService;
|
||||
_bucketName = configService["Storage:CosStorageBucketName"];
|
||||
_region = configService["Storage:CosStorageRegion"];
|
||||
_cdnUrl = configService["Storage:CosStorageCdnUrl"];
|
||||
|
||||
// 检查桶是否为公开读取(从配置获取)
|
||||
bool.TryParse(configService["Storage:CosStoragePublicRead"], out _isPublicRead);
|
||||
|
||||
// 在构造函数中初始化客户端,作为单例使用
|
||||
_cosXmlClient = CreateClient();
|
||||
}
|
||||
|
||||
private CosXml CreateClient()
|
||||
{
|
||||
// 优化配置:启用HTTPS和日志
|
||||
var config = new CosXmlConfig.Builder()
|
||||
.IsHttps(true) // 设置默认HTTPS请求
|
||||
.SetRegion(_region)
|
||||
.SetDebugLog(true) // 显示日志
|
||||
.IsHttps(true)
|
||||
.SetRegion(configService["Storage:CosStorageRegion"])
|
||||
.SetDebugLog(true)
|
||||
.Build();
|
||||
|
||||
// 使用自定义凭证提供者,支持持续更新临时密钥
|
||||
var cosCredentialProvider = new CustomQCloudCredentialProvider(_configService);
|
||||
var cosCredentialProvider = new CustomQCloudCredentialProvider(configService);
|
||||
|
||||
return new CosXmlServer(config, cosCredentialProvider);
|
||||
}
|
||||
@@ -97,9 +74,10 @@ public class CosStorageProvider : IStorageProvider
|
||||
await fileStream.CopyToAsync(fileStream2);
|
||||
}
|
||||
|
||||
var cosXmlClient = CreateClient();
|
||||
var transferConfig = new TransferConfig();
|
||||
var transferManager = new TransferManager(_cosXmlClient, transferConfig);
|
||||
var uploadTask = new COSXMLUploadTask(_bucketName, objectKey);
|
||||
var transferManager = new TransferManager(cosXmlClient, transferConfig);
|
||||
var uploadTask = new COSXMLUploadTask(configService["Storage:CosStorageBucketName"], objectKey);
|
||||
uploadTask.SetSrcPath(tempPath);
|
||||
await transferManager.UploadAsync(uploadTask);
|
||||
return objectKey;
|
||||
@@ -137,8 +115,9 @@ public class CosStorageProvider : IStorageProvider
|
||||
if (string.IsNullOrEmpty(storagePath))
|
||||
return;
|
||||
|
||||
var request = new DeleteObjectRequest(_bucketName, storagePath);
|
||||
await Task.Run(() => _cosXmlClient.DeleteObject(request));
|
||||
var cosXmlClient = CreateClient();
|
||||
var request = new DeleteObjectRequest(configService["Storage:CosStorageBucketName"], storagePath);
|
||||
await Task.Run(() => cosXmlClient.DeleteObject(request));
|
||||
}
|
||||
catch (CosClientException clientEx)
|
||||
{
|
||||
@@ -161,27 +140,33 @@ public class CosStorageProvider : IStorageProvider
|
||||
if (string.IsNullOrEmpty(storagePath))
|
||||
return "/images/unavailable.gif";
|
||||
|
||||
string cdnUrl = configService["Storage:CosStorageCdnUrl"];
|
||||
string bucketName = configService["Storage:CosStorageBucketName"];
|
||||
string region = configService["Storage:CosStorageRegion"];
|
||||
bool isPublicRead = bool.TryParse(configService["Storage:CosStoragePublicRead"], out var publicRead) && publicRead;
|
||||
|
||||
// 优先使用CDN
|
||||
if (!string.IsNullOrEmpty(_cdnUrl))
|
||||
return $"{_cdnUrl}/{storagePath}";
|
||||
if (!string.IsNullOrEmpty(cdnUrl))
|
||||
return $"{cdnUrl}/{storagePath}";
|
||||
|
||||
// 公开读取的桶可直接访问
|
||||
if (_isPublicRead)
|
||||
return $"https://{_bucketName}.cos.{_region}.myqcloud.com/{storagePath}";
|
||||
if (isPublicRead)
|
||||
return $"https://{bucketName}.cos.{region}.myqcloud.com/{storagePath}";
|
||||
|
||||
var bucketParts = _bucketName.Split('-');
|
||||
var cosXmlClient = CreateClient();
|
||||
var bucketParts = bucketName.Split('-');
|
||||
var request = new PreSignatureStruct
|
||||
{
|
||||
bucket = bucketParts[0],
|
||||
appid = bucketParts[1],
|
||||
region = _region,
|
||||
region = region,
|
||||
key = storagePath,
|
||||
httpMethod = "GET",
|
||||
isHttps = true,
|
||||
signDurationSecond = 3600 * 24
|
||||
};
|
||||
|
||||
var url = _cosXmlClient.GenerateSignURL(request);
|
||||
var url = cosXmlClient.GenerateSignURL(request);
|
||||
return url;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -207,13 +192,15 @@ public class CosStorageProvider : IStorageProvider
|
||||
Directory.CreateDirectory(tempDir);
|
||||
}
|
||||
|
||||
string bucketName = configService["Storage:CosStorageBucketName"];
|
||||
string fileName = Path.GetFileName(storagePath);
|
||||
string localFilePath = Path.Combine(tempDir, fileName);
|
||||
|
||||
var cosXmlClient = CreateClient();
|
||||
var transferConfig = new TransferConfig();
|
||||
var transferManager = new TransferManager(_cosXmlClient, transferConfig);
|
||||
var downloadTask = new COSXMLDownloadTask(_bucketName, storagePath, tempDir, fileName);
|
||||
var transferManager = new TransferManager(cosXmlClient, transferConfig);
|
||||
var downloadTask = new COSXMLDownloadTask(bucketName, storagePath, tempDir, fileName);
|
||||
await transferManager.DownloadAsync(downloadTask);
|
||||
return localFilePath;
|
||||
return Path.Combine(tempDir, fileName);
|
||||
}
|
||||
catch (CosClientException clientEx)
|
||||
{
|
||||
|
||||
@@ -4,10 +4,9 @@ using Foxel.Services.Configuration;
|
||||
namespace Foxel.Services.Storage.Providers;
|
||||
|
||||
[StorageProvider(StorageType.Local)]
|
||||
public class LocalStorageProvider(IConfigService config) : IStorageProvider
|
||||
public class LocalStorageProvider(IConfigService configService) : IStorageProvider
|
||||
{
|
||||
private readonly string _baseDirectory = Path.Combine(Directory.GetCurrentDirectory(), "Uploads");
|
||||
private readonly string _serverUrl = config["AppSettings:ServerUrl"];
|
||||
|
||||
public async Task<string> SaveAsync(Stream fileStream, string fileName, string contentType)
|
||||
{
|
||||
@@ -36,9 +35,10 @@ public class LocalStorageProvider(IConfigService config) : IStorageProvider
|
||||
{
|
||||
if (string.IsNullOrEmpty(storagePath))
|
||||
return $"/images/unavailable.gif";
|
||||
return $"{_serverUrl}{storagePath}";
|
||||
}
|
||||
|
||||
string serverUrl = configService["AppSettings:ServerUrl"];
|
||||
return $"{serverUrl}{storagePath}";
|
||||
}
|
||||
|
||||
public Task<string> DownloadFileAsync(string storagePath)
|
||||
{
|
||||
|
||||
@@ -7,44 +7,31 @@ using Foxel.Services.Configuration;
|
||||
namespace Foxel.Services.Storage.Providers;
|
||||
|
||||
[StorageProvider(StorageType.S3)]
|
||||
public class S3StorageProvider : IStorageProvider
|
||||
public class S3StorageProvider(IConfigService configService) : IStorageProvider
|
||||
{
|
||||
private readonly string _accessKey;
|
||||
private readonly string _secretKey;
|
||||
private readonly string _bucketName;
|
||||
private readonly string _region;
|
||||
private readonly string _endpoint;
|
||||
private readonly bool _usePathStyleUrls;
|
||||
private readonly string _cdnUrl;
|
||||
|
||||
public S3StorageProvider(IConfigService configService)
|
||||
{
|
||||
_accessKey = configService["Storage:S3StorageAccessKey"];
|
||||
_secretKey = configService["Storage:S3StorageSecretKey"];
|
||||
_bucketName = configService["Storage:S3StorageBucketName"];
|
||||
_region = configService["Storage:S3StorageRegion"];
|
||||
_cdnUrl = configService["Storage:S3StorageCdnUrl"];
|
||||
_endpoint = configService["Storage:S3StorageEndpoint"];
|
||||
_usePathStyleUrls = bool.TryParse(configService["Storage:S3StorageUsePathStyleUrls"], out var usePathStyle) && usePathStyle;
|
||||
}
|
||||
|
||||
private AmazonS3Client CreateClient()
|
||||
{
|
||||
string accessKey = configService["Storage:S3StorageAccessKey"];
|
||||
string secretKey = configService["Storage:S3StorageSecretKey"];
|
||||
string endpoint = configService["Storage:S3StorageEndpoint"];
|
||||
string region = configService["Storage:S3StorageRegion"];
|
||||
bool usePathStyleUrls = bool.TryParse(configService["Storage:S3StorageUsePathStyleUrls"], out var usePathStyle) && usePathStyle;
|
||||
|
||||
var config = new AmazonS3Config
|
||||
{
|
||||
ServiceURL = _endpoint,
|
||||
UseHttp = !_endpoint.StartsWith("https", StringComparison.OrdinalIgnoreCase),
|
||||
ForcePathStyle = _usePathStyleUrls
|
||||
ServiceURL = endpoint,
|
||||
UseHttp = !endpoint.StartsWith("https", StringComparison.OrdinalIgnoreCase),
|
||||
ForcePathStyle = usePathStyleUrls
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(_region) && _endpoint.Contains("amazonaws.com"))
|
||||
if (!string.IsNullOrEmpty(region) && endpoint.Contains("amazonaws.com"))
|
||||
{
|
||||
config.RegionEndpoint = Amazon.RegionEndpoint.GetBySystemName(_region);
|
||||
config.RegionEndpoint = Amazon.RegionEndpoint.GetBySystemName(region);
|
||||
}
|
||||
|
||||
return new AmazonS3Client(
|
||||
_accessKey,
|
||||
_secretKey,
|
||||
accessKey,
|
||||
secretKey,
|
||||
config
|
||||
);
|
||||
}
|
||||
@@ -65,7 +52,7 @@ public class S3StorageProvider : IStorageProvider
|
||||
{
|
||||
InputStream = fileStream,
|
||||
Key = objectKey,
|
||||
BucketName = _bucketName,
|
||||
BucketName = configService["Storage:S3StorageBucketName"],
|
||||
ContentType = contentType
|
||||
};
|
||||
|
||||
@@ -91,7 +78,7 @@ public class S3StorageProvider : IStorageProvider
|
||||
using var client = CreateClient();
|
||||
var deleteRequest = new DeleteObjectRequest
|
||||
{
|
||||
BucketName = _bucketName,
|
||||
BucketName = configService["Storage:S3StorageBucketName"],
|
||||
Key = storagePath
|
||||
};
|
||||
|
||||
@@ -110,17 +97,19 @@ public class S3StorageProvider : IStorageProvider
|
||||
if (string.IsNullOrEmpty(storagePath))
|
||||
return "/images/unavailable.gif";
|
||||
|
||||
string cdnUrl = configService["Storage:S3StorageCdnUrl"];
|
||||
|
||||
// 如果配置了CDN URL,使用CDN
|
||||
if (!string.IsNullOrEmpty(_cdnUrl))
|
||||
if (!string.IsNullOrEmpty(cdnUrl))
|
||||
{
|
||||
return $"{_cdnUrl}/{storagePath}";
|
||||
return $"{cdnUrl}/{storagePath}";
|
||||
}
|
||||
|
||||
// 否则使用S3直链或生成预签名URL
|
||||
using var client = CreateClient();
|
||||
var request = new GetPreSignedUrlRequest
|
||||
{
|
||||
BucketName = _bucketName,
|
||||
BucketName = configService["Storage:S3StorageBucketName"],
|
||||
Key = storagePath,
|
||||
Expires = DateTime.UtcNow.AddHours(1) // URL有效期1小时
|
||||
};
|
||||
@@ -158,7 +147,7 @@ public class S3StorageProvider : IStorageProvider
|
||||
using var client = CreateClient();
|
||||
var request = new GetObjectRequest
|
||||
{
|
||||
BucketName = _bucketName,
|
||||
BucketName = configService["Storage:S3StorageBucketName"],
|
||||
Key = storagePath
|
||||
};
|
||||
|
||||
|
||||
@@ -9,15 +9,14 @@ namespace Foxel.Services.Storage.Providers;
|
||||
[StorageProvider(StorageType.Telegram)]
|
||||
public class TelegramStorageProvider(IConfigService configService) : IStorageProvider
|
||||
{
|
||||
private readonly string _botToken = configService["Storage:TelegramStorageBotToken"];
|
||||
private readonly string _chatId = configService["Storage:TelegramStorageChatId"];
|
||||
private readonly string _serverUrl = configService["AppSettings:ServerUrl"];
|
||||
|
||||
public async Task<string> SaveAsync(Stream fileStream, string fileName, string contentType)
|
||||
{
|
||||
string botToken = configService["Storage:TelegramStorageBotToken"];
|
||||
string chatId = configService["Storage:TelegramStorageChatId"];
|
||||
|
||||
using var httpClient = new HttpClient();
|
||||
using var formData = new MultipartFormDataContent();
|
||||
formData.Add(new StringContent(_chatId), "chat_id");
|
||||
formData.Add(new StringContent(chatId), "chat_id");
|
||||
var safeFileName = Path.GetFileNameWithoutExtension(fileName);
|
||||
if (safeFileName.Length > 100)
|
||||
safeFileName = safeFileName.Substring(0, 100);
|
||||
@@ -35,7 +34,7 @@ public class TelegramStorageProvider(IConfigService configService) : IStoragePro
|
||||
try
|
||||
{
|
||||
var response =
|
||||
await httpClient.PostAsync($"https://api.telegram.org/bot{_botToken}/sendDocument", formData);
|
||||
await httpClient.PostAsync($"https://api.telegram.org/bot{botToken}/sendDocument", formData);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
@@ -58,7 +57,7 @@ public class TelegramStorageProvider(IConfigService configService) : IStoragePro
|
||||
FileId = fileId,
|
||||
FileUniqueId = responseObj.Result.Document.FileUniqueId,
|
||||
MessageId = responseObj.Result.MessageId,
|
||||
ChatId = _chatId,
|
||||
ChatId = chatId,
|
||||
OriginalFileName = fileName,
|
||||
UploadDate = DateTime.UtcNow,
|
||||
MimeType = contentType
|
||||
@@ -82,9 +81,11 @@ public class TelegramStorageProvider(IConfigService configService) : IStoragePro
|
||||
return;
|
||||
}
|
||||
|
||||
string botToken = configService["Storage:TelegramStorageBotToken"];
|
||||
|
||||
using var httpClient = new HttpClient();
|
||||
var url =
|
||||
$"https://api.telegram.org/bot{_botToken}/deleteMessage?chat_id={metadata.ChatId}&message_id={metadata.MessageId}";
|
||||
$"https://api.telegram.org/bot{botToken}/deleteMessage?chat_id={metadata.ChatId}&message_id={metadata.MessageId}";
|
||||
await httpClient.GetAsync(url);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -103,7 +104,8 @@ public class TelegramStorageProvider(IConfigService configService) : IStoragePro
|
||||
throw new ApplicationException("无效的存储路径或元数据");
|
||||
}
|
||||
|
||||
return $"{_serverUrl}/api/picture/get_telegram_file?fileId={metadata.FileId}";
|
||||
string serverUrl = configService["AppSettings:ServerUrl"];
|
||||
return $"{serverUrl}/api/picture/get_telegram_file?fileId={metadata.FileId}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -127,8 +129,10 @@ public class TelegramStorageProvider(IConfigService configService) : IStoragePro
|
||||
throw new ApplicationException("无效的存储路径或元数据");
|
||||
}
|
||||
|
||||
string botToken = configService["Storage:TelegramStorageBotToken"];
|
||||
|
||||
using var httpClient = new HttpClient();
|
||||
var getFileUrl = $"https://api.telegram.org/bot{_botToken}/getFile?file_id={metadata.FileId}";
|
||||
var getFileUrl = $"https://api.telegram.org/bot{botToken}/getFile?file_id={metadata.FileId}";
|
||||
var getFileResponse = await httpClient.GetAsync(getFileUrl);
|
||||
|
||||
if (!getFileResponse.IsSuccessStatusCode)
|
||||
@@ -145,7 +149,7 @@ public class TelegramStorageProvider(IConfigService configService) : IStoragePro
|
||||
}
|
||||
|
||||
var filePath = getFileResult.Result.FilePath;
|
||||
var fileUrl = $"https://api.telegram.org/file/bot{_botToken}/{filePath}";
|
||||
var fileUrl = $"https://api.telegram.org/file/bot{botToken}/{filePath}";
|
||||
|
||||
var fileResponse = await httpClient.GetAsync(fileUrl);
|
||||
if (!fileResponse.IsSuccessStatusCode)
|
||||
|
||||
@@ -6,52 +6,49 @@ using Foxel.Services.Configuration;
|
||||
namespace Foxel.Services.Storage.Providers;
|
||||
|
||||
[StorageProvider(StorageType.WebDAV)]
|
||||
public class WebDavStorageProvider : IStorageProvider
|
||||
public class WebDavStorageProvider(IConfigService configService) : IStorageProvider
|
||||
{
|
||||
private readonly string _webDavServerUrl;
|
||||
private readonly string _serverUrl;
|
||||
private readonly string _basePath;
|
||||
private readonly string _publicUrl;
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public WebDavStorageProvider(IConfigService configService)
|
||||
private HttpClient CreateClient()
|
||||
{
|
||||
_webDavServerUrl = configService["Storage:WebDAVServerUrl"].TrimEnd('/');
|
||||
var httpClient = new HttpClient();
|
||||
var userName = configService["Storage:WebDAVUserName"];
|
||||
var password = configService["Storage:WebDAVPassword"];
|
||||
_basePath = configService["Storage:WebDAVBasePath"].Trim('/');
|
||||
_publicUrl = configService["Storage:WebDAVPublicUrl"].TrimEnd('/');
|
||||
_serverUrl = configService["AppSettings:ServerUrl"];
|
||||
_httpClient = new HttpClient();
|
||||
|
||||
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password))
|
||||
{
|
||||
var authValue = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{userName}:{password}"));
|
||||
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authValue);
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authValue);
|
||||
}
|
||||
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
public async Task<string> SaveAsync(Stream fileStream, string fileName, string contentType)
|
||||
{
|
||||
try
|
||||
{
|
||||
string webDavServerUrl = configService["Storage:WebDAVServerUrl"].TrimEnd('/');
|
||||
string basePath = configService["Storage:WebDAVBasePath"].Trim('/');
|
||||
|
||||
// 创建唯一的文件存储路径
|
||||
string currentDate = DateTime.Now.ToString("yyyy/MM");
|
||||
string ext = Path.GetExtension(fileName);
|
||||
string newFileName = $"{Guid.NewGuid()}{ext}";
|
||||
string relativePath = $"{_basePath}/{currentDate}/{newFileName}";
|
||||
string relativePath = $"{basePath}/{currentDate}/{newFileName}";
|
||||
|
||||
// 确保目录存在
|
||||
await EnsureDirectoryExistsAsync($"{_basePath}/{currentDate}");
|
||||
await EnsureDirectoryExistsAsync($"{basePath}/{currentDate}");
|
||||
|
||||
// 上传文件内容
|
||||
var requestUri = $"{_webDavServerUrl}/{relativePath}";
|
||||
var requestUri = $"{webDavServerUrl}/{relativePath}";
|
||||
using var client = CreateClient();
|
||||
using var content = new StreamContent(fileStream);
|
||||
content.Headers.ContentType = new MediaTypeHeaderValue(contentType);
|
||||
|
||||
using var request = new HttpRequestMessage(HttpMethod.Put, requestUri);
|
||||
request.Content = content;
|
||||
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
var response = await client.SendAsync(request);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
return relativePath;
|
||||
@@ -70,8 +67,11 @@ public class WebDavStorageProvider : IStorageProvider
|
||||
if (string.IsNullOrEmpty(storagePath))
|
||||
return;
|
||||
|
||||
var requestUri = $"{_webDavServerUrl}/{storagePath}";
|
||||
var response = await _httpClient.DeleteAsync(requestUri);
|
||||
string webDavServerUrl = configService["Storage:WebDAVServerUrl"].TrimEnd('/');
|
||||
var requestUri = $"{webDavServerUrl}/{storagePath}";
|
||||
|
||||
using var client = CreateClient();
|
||||
var response = await client.DeleteAsync(requestUri);
|
||||
|
||||
if (response.StatusCode != System.Net.HttpStatusCode.NotFound)
|
||||
{
|
||||
@@ -91,12 +91,15 @@ public class WebDavStorageProvider : IStorageProvider
|
||||
if (string.IsNullOrEmpty(storagePath))
|
||||
return "/images/unavailable.gif";
|
||||
|
||||
if (!string.IsNullOrEmpty(_publicUrl))
|
||||
string publicUrl = configService["Storage:WebDAVPublicUrl"].TrimEnd('/');
|
||||
string serverUrl = configService["AppSettings:ServerUrl"];
|
||||
|
||||
if (!string.IsNullOrEmpty(publicUrl))
|
||||
{
|
||||
return $"{_publicUrl}/{storagePath}";
|
||||
return $"{publicUrl}/{storagePath}";
|
||||
}
|
||||
|
||||
return $"{_serverUrl}/api/picture/proxy?path={Uri.EscapeDataString(storagePath)}";
|
||||
return $"{serverUrl}/api/picture/proxy?path={Uri.EscapeDataString(storagePath)}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -114,6 +117,8 @@ public class WebDavStorageProvider : IStorageProvider
|
||||
throw new ArgumentException("存储路径不能为空");
|
||||
}
|
||||
|
||||
string webDavServerUrl = configService["Storage:WebDAVServerUrl"].TrimEnd('/');
|
||||
|
||||
// 创建临时目录
|
||||
var tempDir = Path.Combine(Path.GetTempPath(), "FoxelWebDAVTemp");
|
||||
if (!Directory.Exists(tempDir))
|
||||
@@ -126,9 +131,10 @@ public class WebDavStorageProvider : IStorageProvider
|
||||
string tempFilePath = Path.Combine(tempDir, fileName);
|
||||
|
||||
// 下载文件
|
||||
var requestUri = $"{_webDavServerUrl}/{storagePath}";
|
||||
var requestUri = $"{webDavServerUrl}/{storagePath}";
|
||||
using var client = CreateClient();
|
||||
using var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
var response = await client.SendAsync(request);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
await using var fileStream = new FileStream(tempFilePath, FileMode.Create);
|
||||
@@ -150,18 +156,20 @@ public class WebDavStorageProvider : IStorageProvider
|
||||
{
|
||||
try
|
||||
{
|
||||
var requestUri = $"{_webDavServerUrl}/{directoryPath}";
|
||||
string webDavServerUrl = configService["Storage:WebDAVServerUrl"].TrimEnd('/');
|
||||
var requestUri = $"{webDavServerUrl}/{directoryPath}";
|
||||
using var client = CreateClient();
|
||||
|
||||
// 检查目录是否存在 - 使用新的请求对象
|
||||
using var headRequest = new HttpRequestMessage(HttpMethod.Head, requestUri);
|
||||
var response = await _httpClient.SendAsync(headRequest);
|
||||
var response = await client.SendAsync(headRequest);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
return;
|
||||
|
||||
// 创建目录 - 使用新的请求对象
|
||||
using var mkcolRequest = new HttpRequestMessage(new HttpMethod("MKCOL"), requestUri);
|
||||
response = await _httpClient.SendAsync(mkcolRequest);
|
||||
response = await client.SendAsync(mkcolRequest);
|
||||
|
||||
// 处理状态码
|
||||
if (response.StatusCode == System.Net.HttpStatusCode.Conflict ||
|
||||
@@ -174,13 +182,13 @@ public class WebDavStorageProvider : IStorageProvider
|
||||
await EnsureDirectoryExistsAsync(parentPath);
|
||||
|
||||
using var retryRequest = new HttpRequestMessage(new HttpMethod("MKCOL"), requestUri);
|
||||
response = await _httpClient.SendAsync(retryRequest);
|
||||
response = await client.SendAsync(retryRequest);
|
||||
|
||||
if (response.StatusCode == System.Net.HttpStatusCode.MethodNotAllowed)
|
||||
{
|
||||
using var putRequest = new HttpRequestMessage(HttpMethod.Put, $"{requestUri}/.dummy");
|
||||
putRequest.Content = new StringContent(string.Empty);
|
||||
await _httpClient.SendAsync(putRequest);
|
||||
await client.SendAsync(putRequest);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -192,7 +200,7 @@ public class WebDavStorageProvider : IStorageProvider
|
||||
{
|
||||
using var putRequest = new HttpRequestMessage(HttpMethod.Put, $"{requestUri}/.dummy");
|
||||
putRequest.Content = new StringContent(string.Empty);
|
||||
var putResponse = await _httpClient.SendAsync(putRequest);
|
||||
var putResponse = await client.SendAsync(putRequest);
|
||||
putResponse.EnsureSuccessStatusCode();
|
||||
}
|
||||
else
|
||||
|
||||
@@ -137,21 +137,43 @@ const SystemConfig: React.FC = () => {
|
||||
/>
|
||||
</TabPane>
|
||||
|
||||
<TabPane tab="JWT 设置" key="Jwt">
|
||||
<ConfigGroup
|
||||
groupName="Jwt"
|
||||
configs={{
|
||||
SecretKey: configs.Jwt?.SecretKey || '',
|
||||
Issuer: configs.Jwt?.Issuer || '',
|
||||
Audience: configs.Jwt?.Audience || '',
|
||||
}}
|
||||
onSave={handleSaveConfig}
|
||||
descriptions={{
|
||||
SecretKey: 'JWT 加密密钥',
|
||||
Issuer: 'JWT 签发者',
|
||||
Audience: 'JWT 接收者',
|
||||
}}
|
||||
/>
|
||||
<TabPane tab="授权配置" key="Authorization">
|
||||
<Tabs defaultActiveKey="jwt" type="card" size={isMobile ? "small" : "middle"}>
|
||||
<TabPane tab="JWT 设置" key="jwt">
|
||||
<ConfigGroup
|
||||
groupName="Jwt"
|
||||
configs={{
|
||||
SecretKey: configs.Jwt?.SecretKey || '',
|
||||
Issuer: configs.Jwt?.Issuer || '',
|
||||
Audience: configs.Jwt?.Audience || '',
|
||||
}}
|
||||
onSave={handleSaveConfig}
|
||||
descriptions={{
|
||||
SecretKey: 'JWT 加密密钥',
|
||||
Issuer: 'JWT 签发者',
|
||||
Audience: 'JWT 接收者',
|
||||
}}
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
</TabPane>
|
||||
<TabPane tab="GitHub认证" key="github">
|
||||
<ConfigGroup
|
||||
groupName="Authentication"
|
||||
configs={{
|
||||
"GitHubClientId": configs.Authentication?.["GitHubClientId"] || '',
|
||||
"GitHubClientSecret": configs.Authentication?.["GitHubClientSecret"] || '',
|
||||
"GitHubCallbackUrl": configs.Authentication?.["GitHubCallbackUrl"] || ''
|
||||
}}
|
||||
onSave={(_group, key, value) => handleSaveConfig('Authentication', key, value)}
|
||||
descriptions={{
|
||||
"GitHubClientId": 'GitHub OAuth 应用客户端ID',
|
||||
"GitHubClientSecret": 'GitHub OAuth 应用客户端密钥',
|
||||
"GitHubCallbackUrl": 'GitHub OAuth 认证回调地址'
|
||||
}}
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</TabPane>
|
||||
|
||||
<TabPane tab="应用设置" key="AppSettings">
|
||||
@@ -167,23 +189,6 @@ const SystemConfig: React.FC = () => {
|
||||
/>
|
||||
</TabPane>
|
||||
|
||||
<TabPane tab="GitHub认证" key="Authentication">
|
||||
<ConfigGroup
|
||||
groupName="Authentication"
|
||||
configs={{
|
||||
"GitHubClientId": configs.Authentication?.["GitHubClientId"] || '',
|
||||
"GitHubClientSecret": configs.Authentication?.["GitHubClientSecret"] || '',
|
||||
"GitHubCallbackUrl": configs.Authentication?.["GitHubCallbackUrl"] || ''
|
||||
}}
|
||||
onSave={(_group, key, value) => handleSaveConfig('Authentication', key, value)}
|
||||
descriptions={{
|
||||
"GitHubClientId": 'GitHub OAuth 应用客户端ID',
|
||||
"GitHubClientSecret": 'GitHub OAuth 应用客户端密钥',
|
||||
"GitHubCallbackUrl": 'GitHub OAuth 认证回调地址'
|
||||
}}
|
||||
/>
|
||||
</TabPane>
|
||||
|
||||
<TabPane tab="存储设置" key="Storage">
|
||||
<div style={{
|
||||
marginBottom: isMobile ? 16 : 20,
|
||||
|
||||
Reference in New Issue
Block a user