first commit
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user