51 lines
1.9 KiB
C#
51 lines
1.9 KiB
C#
using System.Security.Cryptography;
|
|
using System.Text;
|
|
|
|
namespace Fitness_core_BE.Helpers
|
|
{
|
|
// Short, shareable /sandbox/{token} code — replaces the old raw Guid. Made of a
|
|
// time-derived prefix (minutes since a fixed epoch, base62) so codes drift forward
|
|
// over time, plus a random suffix for uniqueness among requests created in the same
|
|
// minute. Not a security boundary: the sandbox is a temporary trial state, not a
|
|
// login credential.
|
|
public static class SandboxTokenHelper
|
|
{
|
|
private const string Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
private const int TimePartLength = 5;
|
|
private const int RandomPartLength = 5;
|
|
private static readonly DateTime Epoch = new(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
|
|
|
public static string Generate(DateTime createdAtUtc)
|
|
{
|
|
long minutesSinceEpoch = Math.Max(0, (long)(createdAtUtc - Epoch).TotalMinutes);
|
|
string timePart = ToBaseAlphabet(minutesSinceEpoch).PadLeft(TimePartLength, '0');
|
|
if (timePart.Length > TimePartLength)
|
|
timePart = timePart[^TimePartLength..];
|
|
|
|
return timePart + RandomSuffix(RandomPartLength);
|
|
}
|
|
|
|
private static string RandomSuffix(int length)
|
|
{
|
|
var bytes = new byte[length];
|
|
RandomNumberGenerator.Fill(bytes);
|
|
var sb = new StringBuilder(length);
|
|
foreach (var b in bytes)
|
|
sb.Append(Alphabet[b % Alphabet.Length]);
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string ToBaseAlphabet(long value)
|
|
{
|
|
if (value <= 0) return "0";
|
|
var sb = new StringBuilder();
|
|
while (value > 0)
|
|
{
|
|
sb.Insert(0, Alphabet[(int)(value % Alphabet.Length)]);
|
|
value /= Alphabet.Length;
|
|
}
|
|
return sb.ToString();
|
|
}
|
|
}
|
|
}
|