Move project files from uzdb/ subdirectory to root directory for cleaner project structure.
Changes:
- Move frontend/ to root
- Move internal/ to root
- Move build/ to root
- Move all config files (go.mod, wails.json, etc.) to root
- Remove redundant uzdb/ subdirectory nesting
Project structure is now:
├── frontend/ # React application
├── internal/ # Go backend
├── build/ # Wails build assets
├── doc/ # Design documentation
├── main.go # Entry point
└── ...
🤖 Generated with Qoder
137 lines
2.8 KiB
Go
137 lines
2.8 KiB
Go
package utils
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// GenerateID generates a unique ID (UUID-like)
|
|
func GenerateID() string {
|
|
b := make([]byte, 16)
|
|
rand.Read(b)
|
|
return formatUUID(b)
|
|
}
|
|
|
|
// formatUUID formats bytes as UUID string
|
|
func formatUUID(b []byte) string {
|
|
uuid := make([]byte, 36)
|
|
hex.Encode(uuid[0:8], b[0:4])
|
|
hex.Encode(uuid[9:13], b[4:6])
|
|
hex.Encode(uuid[14:18], b[6:8])
|
|
hex.Encode(uuid[19:23], b[8:10])
|
|
hex.Encode(uuid[24:], b[10:])
|
|
uuid[8] = '-'
|
|
uuid[13] = '-'
|
|
uuid[18] = '-'
|
|
uuid[23] = '-'
|
|
return string(uuid)
|
|
}
|
|
|
|
// FormatDuration formats duration in milliseconds
|
|
func FormatDuration(d time.Duration) int64 {
|
|
return d.Milliseconds()
|
|
}
|
|
|
|
// SanitizeSQL removes potentially dangerous SQL patterns
|
|
// Note: This is not a replacement for parameterized queries
|
|
func SanitizeSQL(sql string) string {
|
|
// Remove multiple semicolons
|
|
sql = strings.ReplaceAll(sql, ";;", ";")
|
|
// Trim whitespace
|
|
sql = strings.TrimSpace(sql)
|
|
return sql
|
|
}
|
|
|
|
// TruncateString truncates a string to max length
|
|
func TruncateString(s string, maxLen int) string {
|
|
if len(s) <= maxLen {
|
|
return s
|
|
}
|
|
return s[:maxLen]
|
|
}
|
|
|
|
// GenerateRandomBytes generates random bytes
|
|
func GenerateRandomBytes(n int) ([]byte, error) {
|
|
b := make([]byte, n)
|
|
_, err := rand.Read(b)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to generate random bytes: %w", err)
|
|
}
|
|
return b, nil
|
|
}
|
|
|
|
// EncodeBase64 encodes bytes to base64 string
|
|
func EncodeBase64(data []byte) string {
|
|
return base64.StdEncoding.EncodeToString(data)
|
|
}
|
|
|
|
// DecodeBase64 decodes base64 string to bytes
|
|
func DecodeBase64(s string) ([]byte, error) {
|
|
return base64.StdEncoding.DecodeString(s)
|
|
}
|
|
|
|
// MaskPassword masks password for logging
|
|
func MaskPassword(password string) string {
|
|
if len(password) <= 4 {
|
|
return strings.Repeat("*", len(password))
|
|
}
|
|
return password[:2] + strings.Repeat("*", len(password)-2)
|
|
}
|
|
|
|
// ContainsAny checks if string contains any of the substrings
|
|
func ContainsAny(s string, substrings ...string) bool {
|
|
for _, sub := range substrings {
|
|
if strings.Contains(s, sub) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// IsReadOnlyQuery checks if SQL query is read-only
|
|
func IsReadOnlyQuery(sql string) bool {
|
|
sql = strings.TrimSpace(strings.ToUpper(sql))
|
|
|
|
// Read-only operations
|
|
readOnlyPrefixes := []string{
|
|
"SELECT",
|
|
"SHOW",
|
|
"DESCRIBE",
|
|
"EXPLAIN",
|
|
"WITH",
|
|
}
|
|
|
|
for _, prefix := range readOnlyPrefixes {
|
|
if strings.HasPrefix(sql, prefix) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// IsDDLQuery checks if SQL query is DDL
|
|
func IsDDLQuery(sql string) bool {
|
|
sql = strings.TrimSpace(strings.ToUpper(sql))
|
|
|
|
ddlKeywords := []string{
|
|
"CREATE",
|
|
"ALTER",
|
|
"DROP",
|
|
"TRUNCATE",
|
|
"RENAME",
|
|
}
|
|
|
|
for _, keyword := range ddlKeywords {
|
|
if strings.HasPrefix(sql, keyword) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|