2022-11-20 22:18:24 +01:00
|
|
|
package models
|
|
|
|
|
2023-01-14 00:48:51 +01:00
|
|
|
import (
|
|
|
|
"crypto/rand"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"github.com/go-playground/validator/v10"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
2023-01-15 06:30:30 +01:00
|
|
|
"gogs.mikescher.com/BlackForestBytes/goext/rext"
|
2023-01-14 00:48:51 +01:00
|
|
|
"math/big"
|
|
|
|
"reflect"
|
|
|
|
"regexp"
|
|
|
|
"strings"
|
|
|
|
)
|
2022-11-20 22:18:24 +01:00
|
|
|
|
|
|
|
type EntityID interface {
|
|
|
|
String() string
|
2023-01-14 00:48:51 +01:00
|
|
|
Valid() error
|
|
|
|
Prefix() string
|
|
|
|
Raw() string
|
|
|
|
CheckString() string
|
2023-01-15 06:30:30 +01:00
|
|
|
Regex() rext.Regex
|
2022-11-20 22:18:24 +01:00
|
|
|
}
|
|
|
|
|
2023-01-14 00:48:51 +01:00
|
|
|
const idlen = 24
|
2022-11-20 22:18:24 +01:00
|
|
|
|
2023-01-14 00:48:51 +01:00
|
|
|
const checklen = 1
|
|
|
|
|
|
|
|
const idCharset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
|
|
|
const idCharsetLen = len(idCharset)
|
|
|
|
|
|
|
|
var charSetReverseMap = generateCharsetMap()
|
|
|
|
|
|
|
|
const (
|
|
|
|
prefixUserID = "USR"
|
|
|
|
prefixChannelID = "CHA"
|
|
|
|
prefixDeliveryID = "DEL"
|
|
|
|
prefixMessageID = "MSG"
|
|
|
|
prefixSubscriptionID = "SUB"
|
|
|
|
prefixClientID = "CLN"
|
|
|
|
prefixRequestID = "REQ"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
regexUserID = generateRegex(prefixUserID)
|
|
|
|
regexChannelID = generateRegex(prefixChannelID)
|
|
|
|
regexDeliveryID = generateRegex(prefixDeliveryID)
|
|
|
|
regexMessageID = generateRegex(prefixMessageID)
|
|
|
|
regexSubscriptionID = generateRegex(prefixSubscriptionID)
|
|
|
|
regexClientID = generateRegex(prefixClientID)
|
|
|
|
regexRequestID = generateRegex(prefixRequestID)
|
|
|
|
)
|
|
|
|
|
2023-01-15 06:30:30 +01:00
|
|
|
func generateRegex(prefix string) rext.Regex {
|
|
|
|
return rext.W(regexp.MustCompile(fmt.Sprintf("^%s[%s]{%d}[%s]{%d}$", prefix, idCharset, idlen-len(prefix)-checklen, idCharset, checklen)))
|
2023-01-14 00:48:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func generateCharsetMap() []int {
|
|
|
|
result := make([]int, 128)
|
|
|
|
for i := 0; i < len(result); i++ {
|
|
|
|
result[i] = -1
|
|
|
|
}
|
|
|
|
for idx, chr := range idCharset {
|
|
|
|
result[int(chr)] = idx
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
func generateID(prefix string) string {
|
|
|
|
k := ""
|
|
|
|
max := big.NewInt(int64(idCharsetLen))
|
|
|
|
checksum := 0
|
|
|
|
for i := 0; i < idlen-len(prefix)-checklen; i++ {
|
|
|
|
v, err := rand.Int(rand.Reader, max)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
v64 := v.Int64()
|
|
|
|
k += string(idCharset[v64])
|
|
|
|
checksum = (checksum + int(v64)) % (idCharsetLen)
|
|
|
|
}
|
|
|
|
checkstr := string(idCharset[checksum%idCharsetLen])
|
|
|
|
return prefix + k + checkstr
|
|
|
|
}
|
|
|
|
|
|
|
|
func validateID(prefix string, value string) error {
|
|
|
|
if len(value) != idlen {
|
|
|
|
return errors.New("id has the wrong length")
|
|
|
|
}
|
|
|
|
|
|
|
|
if !strings.HasPrefix(value, prefix) {
|
|
|
|
return errors.New("id is missing the correct prefix")
|
|
|
|
}
|
|
|
|
|
|
|
|
checksum := 0
|
|
|
|
for i := len(prefix); i < len(value)-checklen; i++ {
|
|
|
|
ichr := int(value[i])
|
|
|
|
if ichr < 0 || ichr >= len(charSetReverseMap) || charSetReverseMap[ichr] == -1 {
|
|
|
|
return errors.New("id contains invalid characters")
|
|
|
|
}
|
|
|
|
checksum = (checksum + charSetReverseMap[ichr]) % (idCharsetLen)
|
|
|
|
}
|
|
|
|
|
|
|
|
checkstr := string(idCharset[checksum%idCharsetLen])
|
|
|
|
|
|
|
|
if !strings.HasSuffix(value, checkstr) {
|
|
|
|
return errors.New("id checkstring is invalid")
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func getRawData(prefix string, value string) string {
|
|
|
|
if len(value) != idlen {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return value[len(prefix) : idlen-checklen]
|
|
|
|
}
|
|
|
|
|
|
|
|
func getCheckString(prefix string, value string) string {
|
|
|
|
if len(value) != idlen {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return value[idlen-checklen:]
|
|
|
|
}
|
|
|
|
|
|
|
|
func ValidateEntityID(vfl validator.FieldLevel) bool {
|
|
|
|
if !vfl.Field().CanInterface() {
|
|
|
|
log.Error().Msgf("Failed to validate EntityID (cannot interface ?!?)")
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
ifvalue := vfl.Field().Interface()
|
|
|
|
|
|
|
|
if value1, ok := ifvalue.(EntityID); ok {
|
|
|
|
|
|
|
|
if vfl.Field().Type().Kind() == reflect.Pointer && langext.IsNil(value1) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := value1.Valid(); err != nil {
|
|
|
|
log.Debug().Msgf("Failed to validate EntityID '%s' (%s)", value1.String(), err.Error())
|
|
|
|
return false
|
|
|
|
} else {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
log.Error().Msgf("Failed to validate EntityID (wrong type: %T)", ifvalue)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------
|
|
|
|
|
|
|
|
type UserID string
|
|
|
|
|
|
|
|
func NewUserID() UserID {
|
|
|
|
return UserID(generateID(prefixUserID))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (id UserID) Valid() error {
|
|
|
|
return validateID(prefixUserID, string(id))
|
2022-11-20 22:18:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (id UserID) String() string {
|
2023-01-14 00:48:51 +01:00
|
|
|
return string(id)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (id UserID) Prefix() string {
|
|
|
|
return prefixUserID
|
|
|
|
}
|
|
|
|
|
|
|
|
func (id UserID) Raw() string {
|
|
|
|
return getRawData(prefixUserID, string(id))
|
2022-11-20 22:18:24 +01:00
|
|
|
}
|
|
|
|
|
2023-01-14 00:48:51 +01:00
|
|
|
func (id UserID) CheckString() string {
|
|
|
|
return getCheckString(prefixUserID, string(id))
|
|
|
|
}
|
2022-11-20 22:18:24 +01:00
|
|
|
|
2023-01-15 06:30:30 +01:00
|
|
|
func (id UserID) Regex() rext.Regex {
|
2023-01-14 00:48:51 +01:00
|
|
|
return regexUserID
|
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------
|
|
|
|
|
|
|
|
type ChannelID string
|
|
|
|
|
|
|
|
func NewChannelID() ChannelID {
|
|
|
|
return ChannelID(generateID(prefixChannelID))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (id ChannelID) Valid() error {
|
|
|
|
return validateID(prefixChannelID, string(id))
|
2022-11-20 22:18:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (id ChannelID) String() string {
|
2023-01-14 00:48:51 +01:00
|
|
|
return string(id)
|
2022-11-20 22:18:24 +01:00
|
|
|
}
|
|
|
|
|
2023-01-14 00:48:51 +01:00
|
|
|
func (id ChannelID) Prefix() string {
|
|
|
|
return prefixChannelID
|
|
|
|
}
|
2022-11-20 22:18:24 +01:00
|
|
|
|
2023-01-14 00:48:51 +01:00
|
|
|
func (id ChannelID) Raw() string {
|
|
|
|
return getRawData(prefixChannelID, string(id))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (id ChannelID) CheckString() string {
|
|
|
|
return getCheckString(prefixChannelID, string(id))
|
|
|
|
}
|
|
|
|
|
2023-01-15 06:30:30 +01:00
|
|
|
func (id ChannelID) Regex() rext.Regex {
|
2023-01-14 00:48:51 +01:00
|
|
|
return regexChannelID
|
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------
|
|
|
|
|
|
|
|
type DeliveryID string
|
|
|
|
|
|
|
|
func NewDeliveryID() DeliveryID {
|
|
|
|
return DeliveryID(generateID(prefixDeliveryID))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (id DeliveryID) Valid() error {
|
|
|
|
return validateID(prefixDeliveryID, string(id))
|
2022-11-20 22:18:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (id DeliveryID) String() string {
|
2023-01-14 00:48:51 +01:00
|
|
|
return string(id)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (id DeliveryID) Prefix() string {
|
|
|
|
return prefixDeliveryID
|
|
|
|
}
|
|
|
|
|
|
|
|
func (id DeliveryID) Raw() string {
|
|
|
|
return getRawData(prefixDeliveryID, string(id))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (id DeliveryID) CheckString() string {
|
|
|
|
return getCheckString(prefixDeliveryID, string(id))
|
2022-11-20 22:18:24 +01:00
|
|
|
}
|
|
|
|
|
2023-01-15 06:30:30 +01:00
|
|
|
func (id DeliveryID) Regex() rext.Regex {
|
2023-01-14 00:48:51 +01:00
|
|
|
return regexDeliveryID
|
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------
|
2022-11-20 22:18:24 +01:00
|
|
|
|
2023-01-14 00:48:51 +01:00
|
|
|
type MessageID string
|
|
|
|
|
|
|
|
func NewMessageID() MessageID {
|
|
|
|
return MessageID(generateID(prefixMessageID))
|
2022-11-20 22:18:24 +01:00
|
|
|
}
|
|
|
|
|
2023-01-14 00:48:51 +01:00
|
|
|
func (id MessageID) Valid() error {
|
|
|
|
return validateID(prefixMessageID, string(id))
|
2022-11-20 22:18:24 +01:00
|
|
|
}
|
|
|
|
|
2023-01-14 00:48:51 +01:00
|
|
|
func (id MessageID) String() string {
|
|
|
|
return string(id)
|
|
|
|
}
|
2022-11-20 22:18:24 +01:00
|
|
|
|
2023-01-14 00:48:51 +01:00
|
|
|
func (id MessageID) Prefix() string {
|
|
|
|
return prefixMessageID
|
|
|
|
}
|
|
|
|
|
|
|
|
func (id MessageID) Raw() string {
|
|
|
|
return getRawData(prefixMessageID, string(id))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (id MessageID) CheckString() string {
|
|
|
|
return getCheckString(prefixMessageID, string(id))
|
|
|
|
}
|
|
|
|
|
2023-01-15 06:30:30 +01:00
|
|
|
func (id MessageID) Regex() rext.Regex {
|
2023-01-14 00:48:51 +01:00
|
|
|
return regexMessageID
|
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------
|
|
|
|
|
|
|
|
type SubscriptionID string
|
|
|
|
|
|
|
|
func NewSubscriptionID() SubscriptionID {
|
|
|
|
return SubscriptionID(generateID(prefixSubscriptionID))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (id SubscriptionID) Valid() error {
|
|
|
|
return validateID(prefixSubscriptionID, string(id))
|
2022-11-20 22:18:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (id SubscriptionID) String() string {
|
2023-01-14 00:48:51 +01:00
|
|
|
return string(id)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (id SubscriptionID) Prefix() string {
|
|
|
|
return prefixSubscriptionID
|
2022-11-20 22:18:24 +01:00
|
|
|
}
|
|
|
|
|
2023-01-14 00:48:51 +01:00
|
|
|
func (id SubscriptionID) Raw() string {
|
|
|
|
return getRawData(prefixSubscriptionID, string(id))
|
|
|
|
}
|
2022-11-20 22:18:24 +01:00
|
|
|
|
2023-01-14 00:48:51 +01:00
|
|
|
func (id SubscriptionID) CheckString() string {
|
|
|
|
return getCheckString(prefixSubscriptionID, string(id))
|
|
|
|
}
|
|
|
|
|
2023-01-15 06:30:30 +01:00
|
|
|
func (id SubscriptionID) Regex() rext.Regex {
|
2023-01-14 00:48:51 +01:00
|
|
|
return regexSubscriptionID
|
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------
|
|
|
|
|
|
|
|
type ClientID string
|
|
|
|
|
|
|
|
func NewClientID() ClientID {
|
|
|
|
return ClientID(generateID(prefixClientID))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (id ClientID) Valid() error {
|
|
|
|
return validateID(prefixClientID, string(id))
|
2022-11-20 22:18:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (id ClientID) String() string {
|
2023-01-14 00:48:51 +01:00
|
|
|
return string(id)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (id ClientID) Prefix() string {
|
|
|
|
return prefixClientID
|
|
|
|
}
|
|
|
|
|
|
|
|
func (id ClientID) Raw() string {
|
|
|
|
return getRawData(prefixClientID, string(id))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (id ClientID) CheckString() string {
|
|
|
|
return getCheckString(prefixClientID, string(id))
|
2022-11-20 22:18:24 +01:00
|
|
|
}
|
2023-01-13 17:17:17 +01:00
|
|
|
|
2023-01-15 06:30:30 +01:00
|
|
|
func (id ClientID) Regex() rext.Regex {
|
2023-01-14 00:48:51 +01:00
|
|
|
return regexClientID
|
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------
|
2023-01-13 17:17:17 +01:00
|
|
|
|
2023-01-14 00:48:51 +01:00
|
|
|
type RequestID string
|
|
|
|
|
|
|
|
func NewRequestID() RequestID {
|
|
|
|
return RequestID(generateID(prefixRequestID))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (id RequestID) Valid() error {
|
|
|
|
return validateID(prefixRequestID, string(id))
|
2023-01-13 17:17:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (id RequestID) String() string {
|
2023-01-14 00:48:51 +01:00
|
|
|
return string(id)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (id RequestID) Prefix() string {
|
|
|
|
return prefixRequestID
|
|
|
|
}
|
|
|
|
|
|
|
|
func (id RequestID) Raw() string {
|
|
|
|
return getRawData(prefixRequestID, string(id))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (id RequestID) CheckString() string {
|
|
|
|
return getCheckString(prefixRequestID, string(id))
|
|
|
|
}
|
|
|
|
|
2023-01-15 06:30:30 +01:00
|
|
|
func (id RequestID) Regex() rext.Regex {
|
2023-01-14 00:48:51 +01:00
|
|
|
return regexRequestID
|
2023-01-13 17:17:17 +01:00
|
|
|
}
|