XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

LumaTunnel

【WinUI】LumaTunnel 是一个面向个人多设备的 Windows 代理系统。客户端在本机提供 HTTP/HTTPS CONNECT 与 SOCKS5 TCP 代理,并通过一个受信任 TLS 证书保护的 WSS 会话,将多个 TCP 流复用到自建 Windows Server 节点。

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Text.Json;
using LumaTunnel.Shared.Protocol;

namespace LumaTunnel.Client.Profiles;

public sealed class ClientProfileStore : IDisposable
{
    private readonly SemaphoreSlim _gate = new(1, 1);
    public string RootPath { get; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "LumaTunnel");
    public string ProfilePath => Path.Combine(RootPath, "config", "client.json");
    public string LogPath => Path.Combine(RootPath, "logs");
    public string StatePath => Path.Combine(RootPath, "state");

    public async Task<ClientProfile> LoadAsync(CancellationToken cancellationToken = default)
    {
        Directory.CreateDirectory(Path.GetDirectoryName(ProfilePath)!);
        Directory.CreateDirectory(LogPath);
        Directory.CreateDirectory(StatePath);
        if (!File.Exists(ProfilePath))
        {
            var profile = new ClientProfile();
            await SaveAsync(profile, cancellationToken).ConfigureAwait(false);
            return profile;
        }
        await using var stream = File.OpenRead(ProfilePath);
        return await JsonSerializer.DeserializeAsync<ClientProfile>(stream, TunnelMessageSerializer.Options, cancellationToken).ConfigureAwait(false)
               ?? new ClientProfile();
    }

    public async Task SaveAsync(ClientProfile profile, CancellationToken cancellationToken = default)
    {
        await _gate.WaitAsync(cancellationToken).ConfigureAwait(false);
        try
        {
            Directory.CreateDirectory(Path.GetDirectoryName(ProfilePath)!);
            var temp = ProfilePath + ".tmp";
            try
            {
                await using (var stream = new FileStream(temp, FileMode.Create, FileAccess.Write, FileShare.None, 8192, FileOptions.WriteThrough | FileOptions.Asynchronous))
                    await JsonSerializer.SerializeAsync(stream, profile, TunnelMessageSerializer.Options, cancellationToken).ConfigureAwait(false);
                File.Move(temp, ProfilePath, true);
            }
            finally
            {
                if (File.Exists(temp)) File.Delete(temp);
            }
        }
        finally
        {
            _gate.Release();
        }
    }

    public void Dispose()
    {
        _gate.Dispose();
        GC.SuppressFinalize(this);
    }
}