功能:删除账号时同步清理 session/json 文件
This commit is contained in:
1 parent
e6ae1bdfff
commit
93c5c5242a
1 file changed
+160
-1
@@ -1,3 +1,6 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using TelegramPanel.Core.Interfaces;
|
||||
using TelegramPanel.Data.Entities;
|
||||
using TelegramPanel.Data.Repositories;
|
||||
|
||||
@@ -11,15 +14,24 @@ public class AccountManagementService
|
||||
private readonly IAccountRepository _accountRepository;
|
||||
private readonly IChannelRepository _channelRepository;
|
||||
private readonly IGroupRepository _groupRepository;
|
||||
private readonly ITelegramClientPool _clientPool;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly ILogger<AccountManagementService> _logger;
|
||||
|
||||
public AccountManagementService(
|
||||
IAccountRepository accountRepository,
|
||||
IChannelRepository channelRepository,
|
||||
IGroupRepository groupRepository)
|
||||
IGroupRepository groupRepository,
|
||||
ITelegramClientPool clientPool,
|
||||
IConfiguration configuration,
|
||||
ILogger<AccountManagementService> logger)
|
||||
{
|
||||
_accountRepository = accountRepository;
|
||||
_channelRepository = channelRepository;
|
||||
_groupRepository = groupRepository;
|
||||
_clientPool = clientPool;
|
||||
_configuration = configuration;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<Account?> GetAccountAsync(int id)
|
||||
@@ -62,10 +74,157 @@ public class AccountManagementService
|
||||
var account = await _accountRepository.GetByIdAsync(id);
|
||||
if (account != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 先断开客户端,释放 session 文件锁
|
||||
await _clientPool.RemoveClientAsync(account.Id);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to remove client for account {AccountId} before deletion", account.Id);
|
||||
}
|
||||
|
||||
TryDeleteAccountFiles(account);
|
||||
await _accountRepository.DeleteAsync(account);
|
||||
}
|
||||
}
|
||||
|
||||
private void TryDeleteAccountFiles(Account account)
|
||||
{
|
||||
try
|
||||
{
|
||||
var candidates = ResolveSessionFileCandidates(account);
|
||||
foreach (var path in candidates)
|
||||
{
|
||||
TryDeleteFile(path);
|
||||
TryDeleteFile(BuildBackupPath(path, "sqlite.bak"));
|
||||
TryDeleteFile(BuildBackupPath(path, "corrupt.bak"));
|
||||
TryDeleteFile(BuildBackupPath(path, "bak"));
|
||||
TryDeleteFile(BuildBackupPath(path, "wt"));
|
||||
|
||||
var dir = Path.GetDirectoryName(path);
|
||||
var name = Path.GetFileNameWithoutExtension(path);
|
||||
if (!string.IsNullOrWhiteSpace(dir) && !string.IsNullOrWhiteSpace(name))
|
||||
TryDeleteFile(Path.Combine(dir, $"{name}.json"));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to delete account files for {Phone}", account.Phone);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<string> ResolveSessionFileCandidates(Account account)
|
||||
{
|
||||
var set = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(account.SessionPath))
|
||||
AddCandidatePath(account.SessionPath);
|
||||
|
||||
// 常见命名:sessions/<phone>.session
|
||||
var phoneDigits = NormalizePhone(account.Phone);
|
||||
if (!string.IsNullOrWhiteSpace(phoneDigits))
|
||||
{
|
||||
var sessionsPath = _configuration["Telegram:SessionsPath"] ?? "sessions";
|
||||
AddCandidatePath(Path.Combine(sessionsPath, $"{phoneDigits}.session"));
|
||||
AddCandidatePath(Path.Combine("sessions", $"{phoneDigits}.session"));
|
||||
AddCandidatePath(Path.Combine("src", "TelegramPanel.Web", "sessions", $"{phoneDigits}.session"));
|
||||
}
|
||||
|
||||
return set;
|
||||
|
||||
void AddCandidatePath(string path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (Path.IsPathRooted(path))
|
||||
{
|
||||
set.Add(Path.GetFullPath(path));
|
||||
return;
|
||||
}
|
||||
|
||||
// 1) 基于当前工作目录
|
||||
set.Add(Path.GetFullPath(path));
|
||||
|
||||
// 2) 基于仓库根目录(如果能找到 TelegramPanel.sln)
|
||||
var repoRoot = TryFindRepoRoot();
|
||||
if (!string.IsNullOrWhiteSpace(repoRoot))
|
||||
set.Add(Path.GetFullPath(Path.Combine(repoRoot, path)));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore invalid paths
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string BuildBackupPath(string originalSessionPath, string suffix)
|
||||
{
|
||||
try
|
||||
{
|
||||
var fullPath = Path.GetFullPath(originalSessionPath);
|
||||
var dir = Path.GetDirectoryName(fullPath) ?? Directory.GetCurrentDirectory();
|
||||
var name = Path.GetFileNameWithoutExtension(fullPath);
|
||||
var ext = Path.GetExtension(fullPath);
|
||||
return Path.Combine(dir, $"{name}.{suffix}{ext}");
|
||||
}
|
||||
catch
|
||||
{
|
||||
return originalSessionPath;
|
||||
}
|
||||
}
|
||||
|
||||
private void TryDeleteFile(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
return;
|
||||
|
||||
var fullPath = Path.GetFullPath(path);
|
||||
if (!File.Exists(fullPath))
|
||||
return;
|
||||
|
||||
File.Delete(fullPath);
|
||||
_logger.LogInformation("Deleted file: {Path}", fullPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to delete file: {Path}", path);
|
||||
}
|
||||
}
|
||||
|
||||
private static string NormalizePhone(string? phone)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(phone))
|
||||
return string.Empty;
|
||||
|
||||
var digits = new char[phone.Length];
|
||||
var count = 0;
|
||||
foreach (var ch in phone)
|
||||
{
|
||||
if (ch >= '0' && ch <= '9')
|
||||
digits[count++] = ch;
|
||||
}
|
||||
return count == 0 ? string.Empty : new string(digits, 0, count);
|
||||
}
|
||||
|
||||
private static string? TryFindRepoRoot()
|
||||
{
|
||||
var current = Directory.GetCurrentDirectory();
|
||||
for (int i = 0; i < 10 && !string.IsNullOrWhiteSpace(current); i++)
|
||||
{
|
||||
if (File.Exists(Path.Combine(current, "TelegramPanel.sln")))
|
||||
return current;
|
||||
current = Directory.GetParent(current)?.FullName;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<(int channels, int groups)> GetAccountStatisticsAsync(int accountId)
|
||||
{
|
||||
var channels = await _channelRepository.GetByCreatorAccountAsync(accountId);
|
||||
|
||||
Reference in new issue
Block a user