♻️ Extract the Storage service to DysonNetwork.Drive microservice

This commit is contained in:
2025-07-06 17:29:26 +08:00
parent 6a3d04af3d
commit 14b79f16f4
71 changed files with 2629 additions and 346 deletions

View File

@ -0,0 +1,131 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using DysonNetwork.Common.Interfaces;
using DysonNetwork.Common.Models;
using Microsoft.Extensions.Logging;
using NodaTime;
using NodaTime.Serialization.SystemTextJson;
namespace DysonNetwork.Drive.Clients
{
public class FileReferenceServiceClient : IFileReferenceServiceClient, IDisposable
{
private readonly HttpClient _httpClient;
private readonly ILogger<FileReferenceServiceClient> _logger;
private readonly JsonSerializerOptions _jsonOptions;
public FileReferenceServiceClient(HttpClient httpClient, ILogger<FileReferenceServiceClient> logger)
{
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_jsonOptions = new JsonSerializerOptions()
.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
_jsonOptions.PropertyNameCaseInsensitive = true;
}
public async Task<CloudFileReference> CreateReferenceAsync(
string fileId,
string usage,
string resourceId,
Instant? expiredAt = null,
Duration? duration = null)
{
var request = new
{
FileId = fileId,
Usage = usage,
ResourceId = resourceId,
ExpiredAt = expiredAt,
Duration = duration
};
var content = new StringContent(
JsonSerializer.Serialize(request, _jsonOptions),
Encoding.UTF8,
"application/json");
var response = await _httpClient.PostAsync("api/filereferences", content);
response.EnsureSuccessStatusCode();
await using var responseStream = await response.Content.ReadAsStreamAsync();
return await JsonSerializer.DeserializeAsync<CloudFileReference>(responseStream, _jsonOptions)
?? throw new InvalidOperationException("Failed to deserialize reference response");
}
public async Task DeleteReferenceAsync(string referenceId)
{
var response = await _httpClient.DeleteAsync($"api/filereferences/{referenceId}");
response.EnsureSuccessStatusCode();
}
public async Task DeleteResourceReferencesAsync(string resourceId, string? usage = null)
{
var url = $"api/filereferences/resource/{Uri.EscapeDataString(resourceId)}";
if (!string.IsNullOrEmpty(usage))
{
url += $"?usage={Uri.EscapeDataString(usage)}";
}
var response = await _httpClient.DeleteAsync(url);
response.EnsureSuccessStatusCode();
}
public async Task<List<CloudFileReference>> GetFileReferencesAsync(string fileId)
{
var response = await _httpClient.GetAsync($"api/filereferences/file/{fileId}");
response.EnsureSuccessStatusCode();
await using var responseStream = await response.Content.ReadAsStreamAsync();
return await JsonSerializer.DeserializeAsync<List<CloudFileReference>>(responseStream, _jsonOptions)
?? new List<CloudFileReference>();
}
public async Task<List<CloudFileReference>> GetResourceReferencesAsync(string resourceId, string? usage = null)
{
var url = $"api/filereferences/resource/{Uri.EscapeDataString(resourceId)}";
if (!string.IsNullOrEmpty(usage))
{
url += $"?usage={Uri.EscapeDataString(usage)}";
}
var response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
await using var responseStream = await response.Content.ReadAsStreamAsync();
return await JsonSerializer.DeserializeAsync<List<CloudFileReference>>(responseStream, _jsonOptions)
?? new List<CloudFileReference>();
}
public async Task<bool> HasReferencesAsync(string fileId)
{
var response = await _httpClient.GetAsync($"api/filereferences/file/{fileId}/has-references");
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<bool>(result, _jsonOptions);
}
public async Task UpdateReferenceExpirationAsync(string referenceId, Instant? expiredAt)
{
var request = new { ExpiredAt = expiredAt };
var content = new StringContent(
JsonSerializer.Serialize(request, _jsonOptions),
Encoding.UTF8,
"application/json");
var response = await _httpClient.PatchAsync($"api/filereferences/{referenceId}", content);
response.EnsureSuccessStatusCode();
}
public void Dispose()
{
_httpClient?.Dispose();
GC.SuppressFinalize(this);
}
}
}

View File

@ -0,0 +1,99 @@
using System;
using System.IO;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using DysonNetwork.Common.Interfaces;
using DysonNetwork.Common.Models;
using Microsoft.Extensions.Logging;
namespace DysonNetwork.Drive.Clients
{
public class FileServiceClient : IFileServiceClient, IDisposable
{
private readonly HttpClient _httpClient;
private readonly ILogger<FileServiceClient> _logger;
private readonly JsonSerializerOptions _jsonOptions;
public FileServiceClient(HttpClient httpClient, ILogger<FileServiceClient> logger)
{
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
}
public async Task<CloudFile> GetFileAsync(string fileId)
{
var response = await _httpClient.GetAsync($"api/files/{fileId}");
response.EnsureSuccessStatusCode();
await using var stream = await response.Content.ReadAsStreamAsync();
return await JsonSerializer.DeserializeAsync<CloudFile>(stream, _jsonOptions)
?? throw new InvalidOperationException("Failed to deserialize file response");
}
public async Task<Stream> GetFileStreamAsync(string fileId)
{
var response = await _httpClient.GetAsync($"api/files/{fileId}/download");
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStreamAsync();
}
public async Task<CloudFile> UploadFileAsync(Stream fileStream, string fileName, string? contentType = null)
{
using var content = new MultipartFormDataContent
{
{ new StreamContent(fileStream), "file", fileName }
};
var response = await _httpClient.PostAsync("api/files/upload", content);
response.EnsureSuccessStatusCode();
await using var responseStream = await response.Content.ReadAsStreamAsync();
return await JsonSerializer.DeserializeAsync<CloudFile>(responseStream, _jsonOptions)
?? throw new InvalidOperationException("Failed to deserialize upload response");
}
public async Task DeleteFileAsync(string fileId)
{
var response = await _httpClient.DeleteAsync($"api/files/{fileId}");
response.EnsureSuccessStatusCode();
}
public async Task<CloudFile> ProcessImageAsync(Stream imageStream, string fileName, string? contentType = null)
{
using var content = new MultipartFormDataContent
{
{ new StreamContent(imageStream), "image", fileName }
};
var response = await _httpClient.PostAsync("api/files/process-image", content);
response.EnsureSuccessStatusCode();
await using var responseStream = await response.Content.ReadAsStreamAsync();
return await JsonSerializer.DeserializeAsync<CloudFile>(responseStream, _jsonOptions)
?? throw new InvalidOperationException("Failed to deserialize image processing response");
}
public async Task<string> GetFileUrl(string fileId, bool useCdn = false)
{
var url = $"api/files/{fileId}/url";
if (useCdn)
{
url += "?useCdn=true";
}
var response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<string>(result, _jsonOptions) ?? string.Empty;
}
public void Dispose()
{
_httpClient?.Dispose();
GC.SuppressFinalize(this);
}
}
}