Files

118 lines
4.0 KiB
C#
Raw Permalink Normal View History

2026-06-01 05:09:20 +10:00
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
namespace SNote.Server.Security;
public class PeerCache
{
2026-06-01 17:11:09 +10:00
// Mapping: PeerGuid -> PeerDetails (SessionToken, PublicKeyPem, PeerUrl)
2026-06-01 05:49:08 +10:00
private readonly ConcurrentDictionary<string, PeerDetails> _downstreamPeers = new(StringComparer.OrdinalIgnoreCase);
2026-06-01 05:09:20 +10:00
private readonly ConcurrentDictionary<string, DateTime> _recentBroadcastIds = new(StringComparer.OrdinalIgnoreCase);
2026-06-01 17:11:09 +10:00
public void RegisterPeer(string peerGuid, string sessionToken, string publicKeyPem, string peerUrl)
2026-06-01 05:09:20 +10:00
{
2026-06-01 17:11:09 +10:00
if (string.IsNullOrWhiteSpace(peerGuid) || string.IsNullOrWhiteSpace(sessionToken)) return;
2026-06-01 05:09:20 +10:00
var cleanUrl = peerUrl.Trim().TrimEnd('/');
2026-06-01 17:11:09 +10:00
var details = new PeerDetails(sessionToken, publicKeyPem.Trim(), cleanUrl);
2026-06-01 05:49:08 +10:00
2026-06-01 17:11:09 +10:00
_downstreamPeers[peerGuid] = details;
Console.WriteLine($"[PeerCache] Registered downstream peer node: {peerGuid} (URL: {cleanUrl}) with secure session token.");
2026-06-01 05:09:20 +10:00
}
2026-06-01 17:11:09 +10:00
public void RemovePeer(string peerGuid)
2026-06-01 05:09:20 +10:00
{
2026-06-01 17:11:09 +10:00
if (string.IsNullOrWhiteSpace(peerGuid)) return;
2026-06-01 05:09:20 +10:00
2026-06-01 17:11:09 +10:00
if (_downstreamPeers.TryRemove(peerGuid, out var details))
{
Console.WriteLine($"[PeerCache] Evicted offline peer node: {peerGuid} (URL: {details.PeerUrl})");
}
}
public void RemovePeerByUrl(string peerUrl)
{
if (string.IsNullOrWhiteSpace(peerUrl)) return;
2026-06-01 05:09:20 +10:00
var cleanUrl = peerUrl.Trim().TrimEnd('/');
2026-06-01 17:11:09 +10:00
foreach (var kvp in _downstreamPeers)
2026-06-01 05:09:20 +10:00
{
2026-06-01 17:11:09 +10:00
if (string.Equals(kvp.Value.PeerUrl, cleanUrl, StringComparison.OrdinalIgnoreCase))
{
if (_downstreamPeers.TryRemove(kvp.Key, out _))
{
Console.WriteLine($"[PeerCache] Evicted offline peer node by URL: {cleanUrl} (GUID: {kvp.Key})");
}
}
2026-06-01 05:09:20 +10:00
}
}
public List<string> GetPeers()
{
return new List<string>(_downstreamPeers.Keys);
}
2026-06-01 17:11:09 +10:00
public string? GetToken(string peerGuid)
2026-06-01 05:49:08 +10:00
{
2026-06-01 17:11:09 +10:00
return _downstreamPeers.TryGetValue(peerGuid, out var details) ? details.SessionToken : null;
2026-06-01 05:49:08 +10:00
}
2026-06-01 17:11:09 +10:00
public string? GetPublicKey(string peerGuid)
{
return _downstreamPeers.TryGetValue(peerGuid, out var details) ? details.PublicKeyPem : null;
}
public string? GetPublicKeyByUrl(string peerUrl)
2026-06-01 05:49:08 +10:00
{
var cleanUrl = peerUrl.Trim().TrimEnd('/');
2026-06-01 17:11:09 +10:00
foreach (var details in _downstreamPeers.Values)
{
if (string.Equals(details.PeerUrl, cleanUrl, StringComparison.OrdinalIgnoreCase))
{
return details.PublicKeyPem;
}
}
return null;
2026-06-01 05:49:08 +10:00
}
2026-06-01 17:11:09 +10:00
public bool VerifySessionToken(string peerGuid, string token)
2026-06-01 05:49:08 +10:00
{
2026-06-01 17:11:09 +10:00
if (string.IsNullOrEmpty(peerGuid) || string.IsNullOrEmpty(token)) return false;
2026-06-01 05:49:08 +10:00
2026-06-01 17:11:09 +10:00
if (_downstreamPeers.TryGetValue(peerGuid, out var details))
2026-06-01 05:49:08 +10:00
{
return string.Equals(details.SessionToken, token, StringComparison.Ordinal);
}
return false;
}
2026-06-01 17:11:09 +10:00
public List<(string Guid, string Url, string Token)> GetActivePeers()
2026-06-01 05:49:08 +10:00
{
2026-06-01 17:11:09 +10:00
var list = new List<(string Guid, string Url, string Token)>();
2026-06-01 05:49:08 +10:00
foreach (var kvp in _downstreamPeers)
{
2026-06-01 17:11:09 +10:00
list.Add((kvp.Key, kvp.Value.PeerUrl, kvp.Value.SessionToken));
2026-06-01 05:49:08 +10:00
}
return list;
}
2026-06-01 05:09:20 +10:00
public bool TryProcessBroadcast(string broadcastId)
{
if (string.IsNullOrWhiteSpace(broadcastId)) return false;
// Clean up old entries to prevent infinite memory growth (older than 10 minutes)
var cutoff = DateTime.UtcNow.AddMinutes(-10);
foreach (var kvp in _recentBroadcastIds)
{
if (kvp.Value < cutoff)
{
_recentBroadcastIds.TryRemove(kvp.Key, out _);
}
}
return _recentBroadcastIds.TryAdd(broadcastId, DateTime.UtcNow);
}
}
2026-06-01 05:49:08 +10:00
2026-06-01 17:11:09 +10:00
public record PeerDetails(string SessionToken, string PublicKeyPem, string PeerUrl);