Files
Mocha-2023-RecRoom-Server/Classes/DBs/AgeVerificationDB.cs
T
mochaisvibecoded 42f7f57b72 :3
2026-08-24 17:00:26 -04:00

142 lines
4.7 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using LiteDB;
namespace Mocha2023.Classes.DBs
{
public static class AgeVerificationDB
{
private static readonly LiteDatabase Database =
new(Path.Combine(Program.dataDir, "DBs", "AgeVerification.db"));
private static readonly ILiteCollection<AgeVerificationRequest> Requests =
Database.GetCollection<AgeVerificationRequest>("Requests");
private const string CodeAlphabet = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
private static readonly Random Rng = new();
private static readonly object Sync = new();
static AgeVerificationDB()
{
LiteDbMaintenance.StartPeriodicCheckpoint("AgeVerification.db", Database);
Requests.EnsureIndex(value => value.AccountId);
Requests.EnsureIndex(value => value.Status);
}
public sealed class AgeVerificationRequest
{
[BsonId]
public string Code { get; set; } = string.Empty;
public long AccountId { get; set; }
public string Method { get; set; } = string.Empty;
public string Status { get; set; } = "Pending";
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime? SubmittedAt { get; set; }
public DateTime? ReviewedAt { get; set; }
public long? ReviewedByAccountId { get; set; }
public string? RejectionReason { get; set; }
}
public static string GenerateCode(long accountId)
{
lock (Sync)
{
foreach (AgeVerificationRequest stale in Requests
.Find(value => value.AccountId == accountId &&
(value.Status == "Pending" || value.Status == "UnderReview"))
.ToList())
{
Requests.Delete(stale.Code);
}
string code;
do
{
code = NewCode();
} while (Requests.Exists(value => value.Code == code));
Requests.Insert(new AgeVerificationRequest
{
Code = code,
AccountId = accountId,
Status = "Pending"
});
return code;
}
}
private static string NewCode()
{
char[] buffer = new char[6];
for (int i = 0; i < buffer.Length; i++)
buffer[i] = CodeAlphabet[Rng.Next(CodeAlphabet.Length)];
return new string(buffer);
}
public static AgeVerificationRequest? GetByCode(string? code)
{
string normalized = (code ?? string.Empty).Trim().ToUpperInvariant();
return normalized.Length == 0 ? null : Requests.FindById(normalized);
}
public static AgeVerificationRequest? GetActiveForAccount(long accountId)
{
return Requests.FindAll()
.Where(value => value.AccountId == accountId &&
(value.Status == "Pending" || value.Status == "UnderReview"))
.OrderByDescending(value => value.CreatedAt)
.FirstOrDefault();
}
public static bool MarkUnderReview(string code, long accountId, string method)
{
lock (Sync)
{
AgeVerificationRequest? request = GetByCode(code);
if (request == null ||
request.AccountId != accountId ||
request.Status != "Pending")
{
return false;
}
request.Method = method;
request.Status = "UnderReview";
request.SubmittedAt = DateTime.UtcNow;
return Requests.Update(request);
}
}
public static List<AgeVerificationRequest> GetForReview()
{
return Requests.Find(value => value.Status == "UnderReview")
.OrderBy(value => value.SubmittedAt)
.ToList();
}
public static bool Review(string code, bool approve, long reviewerId, string? reason)
{
lock (Sync)
{
AgeVerificationRequest? request = GetByCode(code);
if (request == null || request.Status != "UnderReview")
return false;
request.Status = approve ? "Approved" : "Rejected";
request.ReviewedAt = DateTime.UtcNow;
request.ReviewedByAccountId = reviewerId;
request.RejectionReason = reason;
return Requests.Update(request);
}
}
}
}