📝 docs: add comprehensive SOLID analysis and code review findings
Some checks failed
CI/CD Pipeline / CI Pipeline (push) Has been cancelled
CI/CD Pipeline / CI Pipeline (pull_request) Successful in 15m34s

- Documented SOLID principle violations across codebase
- Identified security best practice improvements needed
- Analyzed performance optimization opportunities
- Added detailed refactoring recommendations
- Updated ADR-0018 with JWT secret rotation reference
- Enabled gitea-client skill for programmer agent

This commit captures the current state analysis before implementing improvements.
This commit is contained in:
2026-04-06 23:49:03 +02:00
parent 72b9d35299
commit 08202a578d
16 changed files with 1015 additions and 3 deletions

View File

@@ -0,0 +1,174 @@
package user
import (
"context"
"errors"
"fmt"
"log"
"os"
"path/filepath"
"time"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
// SQLiteRepository implements UserRepository using SQLite
type SQLiteRepository struct {
db *gorm.DB
dbPath string
}
// NewSQLiteRepository creates a new SQLite repository
func NewSQLiteRepository(dbPath string) (*SQLiteRepository, error) {
repo := &SQLiteRepository{
dbPath: dbPath,
}
if err := repo.initializeDatabase(); err != nil {
return nil, fmt.Errorf("failed to initialize database: %w", err)
}
return repo, nil
}
// initializeDatabase sets up the SQLite database and runs migrations
func (r *SQLiteRepository) initializeDatabase() error {
// Create directory if it doesn't exist
dir := filepath.Dir(r.dbPath)
if err := os.MkdirAll(dir, 0755); err != nil {
return fmt.Errorf("failed to create directory: %w", err)
}
// Configure GORM logger to use standard log
gormLogger := logger.New(
log.New(os.Stdout, "\n", log.LstdFlags),
logger.Config{
SlowThreshold: time.Second,
LogLevel: logger.Warn,
IgnoreRecordNotFoundError: true,
Colorful: true,
},
)
var err error
r.db, err = gorm.Open(sqlite.Open(r.dbPath), &gorm.Config{
Logger: gormLogger,
})
if err != nil {
return fmt.Errorf("failed to connect to database: %w", err)
}
// Auto-migrate the User model
if err := r.db.AutoMigrate(&User{}); err != nil {
return fmt.Errorf("failed to auto-migrate: %w", err)
}
return nil
}
// CreateUser creates a new user in the database
func (r *SQLiteRepository) CreateUser(ctx context.Context, user *User) error {
result := r.db.WithContext(ctx).Create(user)
if result.Error != nil {
return fmt.Errorf("failed to create user: %w", result.Error)
}
return nil
}
// GetUserByUsername retrieves a user by username
func (r *SQLiteRepository) GetUserByUsername(ctx context.Context, username string) (*User, error) {
var user User
result := r.db.WithContext(ctx).Where("username = ?", username).First(&user)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, fmt.Errorf("failed to get user by username: %w", result.Error)
}
return &user, nil
}
// GetUserByID retrieves a user by ID
func (r *SQLiteRepository) GetUserByID(ctx context.Context, id uint) (*User, error) {
var user User
result := r.db.WithContext(ctx).First(&user, id)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, fmt.Errorf("failed to get user by ID: %w", result.Error)
}
return &user, nil
}
// UpdateUser updates a user in the database
func (r *SQLiteRepository) UpdateUser(ctx context.Context, user *User) error {
result := r.db.WithContext(ctx).Save(user)
if result.Error != nil {
return fmt.Errorf("failed to update user: %w", result.Error)
}
return nil
}
// DeleteUser deletes a user from the database
func (r *SQLiteRepository) DeleteUser(ctx context.Context, id uint) error {
result := r.db.WithContext(ctx).Delete(&User{}, id)
if result.Error != nil {
return fmt.Errorf("failed to delete user: %w", result.Error)
}
return nil
}
// AllowPasswordReset flags a user for password reset
func (r *SQLiteRepository) AllowPasswordReset(ctx context.Context, username string) error {
user, err := r.GetUserByUsername(ctx, username)
if err != nil {
return fmt.Errorf("failed to get user for password reset: %w", err)
}
if user == nil {
return fmt.Errorf("user not found: %s", username)
}
user.AllowPasswordReset = true
return r.UpdateUser(ctx, user)
}
// CompletePasswordReset completes the password reset process
func (r *SQLiteRepository) CompletePasswordReset(ctx context.Context, username, newPasswordHash string) error {
user, err := r.GetUserByUsername(ctx, username)
if err != nil {
return fmt.Errorf("failed to get user for password reset completion: %w", err)
}
if user == nil {
return fmt.Errorf("user not found: %s", username)
}
if !user.AllowPasswordReset {
return fmt.Errorf("password reset not allowed for user: %s", username)
}
user.PasswordHash = newPasswordHash
user.AllowPasswordReset = false
return r.UpdateUser(ctx, user)
}
// UserExists checks if a user exists by username
func (r *SQLiteRepository) UserExists(ctx context.Context, username string) (bool, error) {
var count int64
result := r.db.WithContext(ctx).Model(&User{}).Where("username = ?", username).Count(&count)
if result.Error != nil {
return false, fmt.Errorf("failed to check if user exists: %w", result.Error)
}
return count > 0, nil
}
// Close closes the database connection
func (r *SQLiteRepository) Close() error {
sqlDB, err := r.db.DB()
if err != nil {
return fmt.Errorf("failed to get database connection: %w", err)
}
return sqlDB.Close()
}