Files
Notemark/Notemark/Services/ShareService.cs
T
2026-09-22 12:53:08 +02:00

126 lines
3.7 KiB
C#

using System.Text.Json;
namespace Notemark.Services;
public class ShareService : IShareService
{
private readonly string _dataPath;
private readonly JsonSerializerOptions _jsonOptions;
public ShareService(IWebHostEnvironment environment)
{
var baseDataPath = Environment.GetEnvironmentVariable("NOTEMARK_DATA_DIR");
if (string.IsNullOrWhiteSpace(baseDataPath))
{
baseDataPath = Path.Combine(environment.ContentRootPath, "data");
}
_dataPath = Path.Combine(baseDataPath, "shares");
try
{
Directory.CreateDirectory(_dataPath);
}
catch (Exception ex)
{
Console.Error.WriteLine($"[ShareService Init Error] Failed to create {_dataPath}: {ex.Message}");
}
_jsonOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
};
}
public async Task<string> CreateShareTokenAsync(string sessionId, string mode)
{
if (string.IsNullOrWhiteSpace(sessionId))
throw new ArgumentException("Session ID cannot be empty", nameof(sessionId));
if (mode != "readonly" && mode != "editable")
throw new ArgumentException("Mode must be 'readonly' or 'editable'", nameof(mode));
var token = GenerateToken();
var shareToken = new ShareToken(
SessionId: sessionId,
Mode: mode,
CreatedAt: DateTime.UtcNow
);
var filePath = GetTokenFilePath(token);
var dir = Path.GetDirectoryName(filePath);
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
var json = JsonSerializer.Serialize(shareToken, _jsonOptions);
await File.WriteAllTextAsync(filePath, json, System.Text.Encoding.UTF8);
return token;
}
public async Task<ShareToken?> GetShareTokenAsync(string token)
{
var filePath = GetTokenFilePath(token);
if (!File.Exists(filePath))
return null;
try
{
var json = await File.ReadAllTextAsync(filePath);
return JsonSerializer.Deserialize<ShareToken>(json, _jsonOptions);
}
catch (JsonException)
{
return null;
}
}
public async Task RevokeShareTokenAsync(string token)
{
var shareToken = await GetShareTokenAsync(token);
if (shareToken == null)
return;
var revokedToken = shareToken with { RevokedAt = DateTime.UtcNow };
var filePath = GetTokenFilePath(token);
var json = JsonSerializer.Serialize(revokedToken, _jsonOptions);
await File.WriteAllTextAsync(filePath, json);
}
public async Task<bool> IsValidTokenAsync(string token)
{
var shareToken = await GetShareTokenAsync(token);
if (shareToken == null)
return false;
// Check if revoked
if (shareToken.RevokedAt.HasValue)
return false;
// Check if expired
if (shareToken.ExpiresAt.HasValue && shareToken.ExpiresAt.Value < DateTime.UtcNow)
return false;
return true;
}
private string GenerateToken()
{
// Generate a URL-safe random token
return Convert.ToBase64String(Guid.NewGuid().ToByteArray())
.Replace('+', '-')
.Replace('/', '_')
.Replace("=", "");
}
private string GetTokenFilePath(string token)
{
// Sanitize token for filename
var sanitizedToken = string.Concat(token.Where(c => char.IsLetterOrDigit(c) || c == '-' || c == '_'));
return Path.Combine(_dataPath, $"{sanitizedToken}.json");
}
}