refactor: Flatten directory structure

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
This commit is contained in:
loveuer
2026-04-04 07:14:00 -07:00
parent 5a83e86bc9
commit 9874561410
83 changed files with 0 additions and 46 deletions

136
internal/utils/errors.go Normal file
View File

@@ -0,0 +1,136 @@
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
}

102
internal/utils/response.go Normal file
View File

@@ -0,0 +1,102 @@
package utils
import (
"errors"
"net/http"
"time"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"uzdb/internal/config"
"uzdb/internal/models"
)
// ErrorResponse sends an error response
func ErrorResponse(c *gin.Context, statusCode int, err error, message string) {
logger := config.GetLogger()
response := models.ErrorResponse{
Error: getErrorCode(err),
Message: message,
Timestamp: time.Now(),
Path: c.Request.URL.Path,
}
if message == "" {
response.Message = err.Error()
}
// Log the error
logger.Error("error response",
zap.Int("status_code", statusCode),
zap.String("error", response.Error),
zap.String("message", response.Message),
zap.String("path", response.Path),
)
c.JSON(statusCode, response)
}
// SuccessResponse sends a success response
func SuccessResponse(c *gin.Context, data interface{}) {
c.JSON(http.StatusOK, models.NewAPIResponse(data))
}
// CreatedResponse sends a created response
func CreatedResponse(c *gin.Context, data interface{}) {
c.JSON(http.StatusCreated, models.NewAPIResponse(data))
}
// getErrorCode maps errors to error codes
func getErrorCode(err error) string {
if err == nil {
return string(models.CodeSuccess)
}
switch {
case errors.Is(err, models.ErrNotFound):
return string(models.CodeNotFound)
case errors.Is(err, models.ErrValidationFailed):
return string(models.CodeValidation)
case errors.Is(err, models.ErrUnauthorized):
return string(models.CodeUnauthorized)
case errors.Is(err, models.ErrForbidden):
return string(models.CodeForbidden)
case errors.Is(err, models.ErrConnectionFailed):
return string(models.CodeConnection)
case errors.Is(err, models.ErrQueryFailed):
return string(models.CodeQuery)
case errors.Is(err, models.ErrEncryptionFailed):
return string(models.CodeEncryption)
default:
return string(models.CodeInternal)
}
}
// WrapError wraps an error with context
func WrapError(err error, message string) error {
if err == nil {
return nil
}
return &wrappedError{
err: err,
message: message,
}
}
type wrappedError struct {
err error
message string
}
func (w *wrappedError) Error() string {
if w.message != "" {
return w.message + ": " + w.err.Error()
}
return w.err.Error()
}
func (w *wrappedError) Unwrap() error {
return w.err
}