Migrations: - 016, 017, 019: Replace gen_random_uuid() with uuid_generate_v7() - All UUID tables now use custom uuid_generate_v7() function Backend: - Create internal/utils/uuid/uuid.go with V7() function (RFC 9562) - Update storage_handler.go to use internal uuid.V7() - Remove dependency on google/uuid for file naming All new UUIDs in the system are now UUID v7 (time-ordered)
64 lines
1.6 KiB
Go
64 lines
1.6 KiB
Go
package uuid
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
// V7 generates a UUID v7 (time-ordered) following RFC 9562
|
|
// Format: tttttttt-tttt-7xxx-yxxx-xxxxxxxxxxxx
|
|
// Where: t = timestamp (48 bits), 7 = version, y = variant, x = random
|
|
func V7() string {
|
|
// Get current timestamp in milliseconds
|
|
timestamp := time.Now().UnixMilli()
|
|
|
|
// Generate 10 random bytes
|
|
randomBytes := make([]byte, 10)
|
|
rand.Read(randomBytes)
|
|
|
|
// Build UUID bytes (16 total)
|
|
var bytes [16]byte
|
|
|
|
// First 6 bytes: timestamp (48 bits, big-endian)
|
|
binary.BigEndian.PutUint32(bytes[0:4], uint32(timestamp>>16))
|
|
binary.BigEndian.PutUint16(bytes[4:6], uint16(timestamp))
|
|
|
|
// Next 2 bytes: version (4 bits = 7) + random (12 bits)
|
|
bytes[6] = 0x70 | (randomBytes[0] & 0x0F) // version 7
|
|
bytes[7] = randomBytes[1]
|
|
|
|
// Last 8 bytes: variant (2 bits = 10) + random (62 bits)
|
|
bytes[8] = 0x80 | (randomBytes[2] & 0x3F) // variant RFC 4122
|
|
copy(bytes[9:16], randomBytes[3:10])
|
|
|
|
// Format as UUID string
|
|
return fmt.Sprintf("%08x-%04x-%04x-%04x-%012x",
|
|
binary.BigEndian.Uint32(bytes[0:4]),
|
|
binary.BigEndian.Uint16(bytes[4:6]),
|
|
binary.BigEndian.Uint16(bytes[6:8]),
|
|
binary.BigEndian.Uint16(bytes[8:10]),
|
|
bytes[10:16],
|
|
)
|
|
}
|
|
|
|
// IsValid checks if a string is a valid UUID (any version)
|
|
func IsValid(s string) bool {
|
|
// Basic UUID format check: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
if len(s) != 36 {
|
|
return false
|
|
}
|
|
for i, c := range s {
|
|
if i == 8 || i == 13 || i == 18 || i == 23 {
|
|
if c != '-' {
|
|
return false
|
|
}
|
|
} else {
|
|
if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}
|