feat: add fnOS packaging, storage workflows and release pipeline
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
using dy.net.model.dto;
|
||||
using dy.net.model.entity;
|
||||
using dy.net.extension;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using SqlSugar;
|
||||
using dy.net.utils;
|
||||
using MediaStorageType = dy.net.model.dto.StorageType;
|
||||
|
||||
namespace dy.net.service
|
||||
{
|
||||
public class OpenListSettingsService
|
||||
{
|
||||
private const string SettingsId = "default";
|
||||
private readonly ISqlSugarClient _db;
|
||||
private readonly IDataProtector _protector;
|
||||
private readonly WebDavSettingsService _legacySettings;
|
||||
|
||||
public OpenListSettingsService(
|
||||
ISqlSugarClient db,
|
||||
IDataProtectionProvider provider,
|
||||
WebDavSettingsService legacySettings)
|
||||
{
|
||||
_db = db;
|
||||
_protector = provider.CreateProtector("dysync.openlist.password.v1");
|
||||
_legacySettings = legacySettings;
|
||||
}
|
||||
|
||||
public async Task<OpenListSettings> GetAsync()
|
||||
{
|
||||
var existing = await _db.Queryable<OpenListSettings>().InSingleAsync(SettingsId);
|
||||
if (existing != null)
|
||||
{
|
||||
Normalize(existing);
|
||||
return existing;
|
||||
}
|
||||
|
||||
var legacy = await _legacySettings.GetAsync();
|
||||
var password = await _legacySettings.GetPasswordAsync(legacy);
|
||||
var suggested = new OpenListSettings
|
||||
{
|
||||
Id = SettingsId,
|
||||
Endpoint = NormalizeLegacyEndpoint(legacy.Endpoint),
|
||||
BasePath = string.IsNullOrWhiteSpace(legacy.BasePath) ? "/dysync" : legacy.BasePath,
|
||||
LocalStagingPath = GetDefaultLocalStagingPath(),
|
||||
SourcePath = "/dysync-staging",
|
||||
UserName = legacy.UserName,
|
||||
ProtectedPassword = string.IsNullOrWhiteSpace(password) ? null : _protector.Protect(password)
|
||||
};
|
||||
Normalize(suggested);
|
||||
return suggested;
|
||||
}
|
||||
|
||||
public async Task<string> GetPasswordAsync(OpenListSettings settings = null)
|
||||
{
|
||||
settings ??= await GetAsync();
|
||||
if (string.IsNullOrWhiteSpace(settings.ProtectedPassword)) return string.Empty;
|
||||
try
|
||||
{
|
||||
return _protector.Unprotect(settings.ProtectedPassword);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Serilog.Log.Error(ex, "OpenList 密码解密失败,请重新输入密码");
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<OpenListSettings> BuildCandidateAsync(OpenListTestRequest request)
|
||||
{
|
||||
request ??= new OpenListTestRequest();
|
||||
var existing = await GetAsync();
|
||||
var password = string.IsNullOrWhiteSpace(request.Password)
|
||||
? await GetPasswordAsync(existing)
|
||||
: request.Password;
|
||||
|
||||
var candidate = new OpenListSettings
|
||||
{
|
||||
Id = SettingsId,
|
||||
Endpoint = string.IsNullOrWhiteSpace(request.Endpoint) ? existing.Endpoint : request.Endpoint.Trim(),
|
||||
BasePath = string.IsNullOrWhiteSpace(request.BasePath) ? existing.BasePath : request.BasePath.Trim(),
|
||||
LocalStagingPath = string.IsNullOrWhiteSpace(request.LocalStagingPath)
|
||||
? existing.LocalStagingPath : request.LocalStagingPath.Trim(),
|
||||
SourcePath = string.IsNullOrWhiteSpace(request.SourcePath) ? existing.SourcePath : request.SourcePath.Trim(),
|
||||
UserName = string.IsNullOrWhiteSpace(request.UserName) ? existing.UserName : request.UserName.Trim(),
|
||||
ProtectedPassword = string.IsNullOrWhiteSpace(password) ? null : _protector.Protect(password)
|
||||
};
|
||||
Normalize(candidate);
|
||||
return candidate;
|
||||
}
|
||||
|
||||
public async Task SaveAsync(OpenListSettings settings, bool tested, string testMessage)
|
||||
{
|
||||
settings.Id = SettingsId;
|
||||
Normalize(settings);
|
||||
settings.LastTestedAt = tested ? DateTime.Now : null;
|
||||
settings.LastTestMessage = testMessage;
|
||||
await _db.Storageable(settings).ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
public async Task<OpenListSettingsDto> ToDtoAsync(MediaStorageType storageType)
|
||||
{
|
||||
var settings = await GetAsync();
|
||||
return new OpenListSettingsDto
|
||||
{
|
||||
StorageType = storageType,
|
||||
Endpoint = settings.Endpoint,
|
||||
BasePath = settings.BasePath,
|
||||
LocalStagingPath = settings.LocalStagingPath,
|
||||
SourcePath = settings.SourcePath,
|
||||
UserName = settings.UserName,
|
||||
HasPassword = !string.IsNullOrWhiteSpace(settings.ProtectedPassword),
|
||||
LastTestedAt = settings.LastTestedAt,
|
||||
LastTestMessage = settings.LastTestMessage
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<bool> HasSavedSettingsAsync() =>
|
||||
await _db.Queryable<OpenListSettings>().Where(x => x.Id == SettingsId).AnyAsync();
|
||||
|
||||
public static string GetDefaultLocalStagingPath()
|
||||
{
|
||||
var dataRoot = !string.IsNullOrWhiteSpace(ServiceExtension.FnDataFolder)
|
||||
? Path.GetDirectoryName(Path.GetFullPath(ServiceExtension.FnDataFolder))
|
||||
: null;
|
||||
return Path.Combine(dataRoot ?? Environment.CurrentDirectory, "openlist-staging");
|
||||
}
|
||||
|
||||
private static string NormalizeLegacyEndpoint(string endpoint)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(endpoint)) return endpoint;
|
||||
var value = endpoint.Trim().TrimEnd('/');
|
||||
var dav = value.LastIndexOf("/dav", StringComparison.OrdinalIgnoreCase);
|
||||
return dav >= 0 && dav + 4 == value.Length ? value[..dav] : value;
|
||||
}
|
||||
|
||||
private static void Normalize(OpenListSettings settings)
|
||||
{
|
||||
settings.Endpoint = NormalizeLegacyEndpoint(settings.Endpoint);
|
||||
if (string.IsNullOrWhiteSpace(settings.BasePath))
|
||||
settings.BasePath = string.IsNullOrWhiteSpace(settings.DestinationPath) ? "/dysync" : settings.DestinationPath;
|
||||
if (string.IsNullOrWhiteSpace(settings.LocalStagingPath))
|
||||
settings.LocalStagingPath = GetDefaultLocalStagingPath();
|
||||
if (string.IsNullOrWhiteSpace(settings.SourcePath)) settings.SourcePath = "/dysync-staging";
|
||||
settings.DestinationPath = settings.BasePath;
|
||||
}
|
||||
|
||||
public async Task InitializeCookiePathsAsync()
|
||||
{
|
||||
var cookies = await _db.Queryable<DouyinCookie>().ToListAsync();
|
||||
foreach (var cookie in cookies)
|
||||
{
|
||||
ApplyDefaultCookiePaths(cookie);
|
||||
}
|
||||
if (cookies.Count > 0) await _db.Updateable(cookies).ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
public void ApplyDefaultCookiePaths(DouyinCookie cookie)
|
||||
{
|
||||
if (cookie == null) return;
|
||||
var root = DouyinFileNameHelper.SanitizeLinuxFileName(cookie.UserName, cookie.Id, true);
|
||||
if (string.IsNullOrWhiteSpace(cookie.WebDavCollectPath)) cookie.WebDavCollectPath = $"/{root}/collect";
|
||||
if (string.IsNullOrWhiteSpace(cookie.WebDavFavoritePath)) cookie.WebDavFavoritePath = $"/{root}/favorite";
|
||||
if (string.IsNullOrWhiteSpace(cookie.WebDavFollowPath)) cookie.WebDavFollowPath = $"/{root}/follow";
|
||||
if (string.IsNullOrWhiteSpace(cookie.WebDavMixPath)) cookie.WebDavMixPath = $"/{root}/mix";
|
||||
if (string.IsNullOrWhiteSpace(cookie.WebDavSeriesPath)) cookie.WebDavSeriesPath = $"/{root}/series";
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user