gohorsejobs/backend/internal/utils/uuid/uuid.go
Tiago Yamamoto 568b4ebb88 refactor: clean up legacy UUID v4, use UUID v7 everywhere
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)
2025-12-24 11:29:55 -03:00

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
}