Skip to content

Commit

Permalink
♻️ refactor: simplify file size handling by using raw bytes instead o…
Browse files Browse the repository at this point in the history
…f ByteSize type

The commit removes the custom ByteSize type and uses raw integers for file size
limits, making the code simpler and more straightforward. File sizes are now
specified directly in bytes with clear comments indicating the values.
  • Loading branch information
watzon committed Nov 19, 2024
1 parent 301f30a commit b3e65fd
Show file tree
Hide file tree
Showing 11 changed files with 48 additions and 409 deletions.
14 changes: 9 additions & 5 deletions config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,15 @@ server:
# Public-facing URL (used for generating links)
base_url: http://localhost:3000

# Maximum upload size in bytes (default: 5MB)
max_upload_size: 100MB
api_upload_size: 50MB
default_upload_size: 10MB

# Maximum upload size in bytes (hard server limit)
max_upload_size: 104857600 # 100 MB

# Maximum upload size in bytes (normal users)
default_upload_size: 10485760 # 10 MB

# Maximim upload size in bytes (users with API key)
api_upload_size: 52428800 # 50 MB

# Preforking
prefork: false

Expand Down
29 changes: 15 additions & 14 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"time"

"github.com/spf13/viper"
"github.com/watzon/0x45/internal/utils/bytesize"
)

type StorageConfig struct {
Expand Down Expand Up @@ -56,19 +55,19 @@ type RateLimitConfig struct {
}

type ServerConfig struct {
Address string `mapstructure:"address"`
BaseURL string `mapstructure:"base_url"`
MaxUploadSize bytesize.ByteSize `mapstructure:"max_upload_size"`
DefaultUploadSize bytesize.ByteSize `mapstructure:"default_upload_size"`
APIUploadSize bytesize.ByteSize `mapstructure:"api_upload_size"`
Prefork bool `mapstructure:"prefork"`
ServerHeader string `mapstructure:"server_header"`
AppName string `mapstructure:"app_name"`
Cleanup CleanupConfig `mapstructure:"cleanup"`
RateLimit RateLimitConfig `mapstructure:"rate_limit"`
CORSOrigins []string `mapstructure:"cors_origins"`
ViewsDirectory string `mapstructure:"views_directory"`
PublicDirectory string `mapstructure:"public_directory"`
Address string `mapstructure:"address"`
BaseURL string `mapstructure:"base_url"`
MaxUploadSize int `mapstructure:"max_upload_size"`
DefaultUploadSize int `mapstructure:"default_upload_size"`
APIUploadSize int `mapstructure:"api_upload_size"`
Prefork bool `mapstructure:"prefork"`
ServerHeader string `mapstructure:"server_header"`
AppName string `mapstructure:"app_name"`
Cleanup CleanupConfig `mapstructure:"cleanup"`
RateLimit RateLimitConfig `mapstructure:"rate_limit"`
CORSOrigins []string `mapstructure:"cors_origins"`
ViewsDirectory string `mapstructure:"views_directory"`
PublicDirectory string `mapstructure:"public_directory"`
}

type SMTPConfig struct {
Expand Down Expand Up @@ -128,6 +127,8 @@ func Load() (*Config, error) {
_ = viper.BindEnv("server.address", "0X_SERVER_ADDRESS")
_ = viper.BindEnv("server.base_url", "0X_SERVER_BASE_URL")
_ = viper.BindEnv("server.max_upload_size", "0X_SERVER_MAX_UPLOAD_SIZE")
_ = viper.BindEnv("server.default_upload_size", "0X_SERVER_DEFAULT_UPLOAD_SIZE")
_ = viper.BindEnv("server.api_upload_size", "0X_SERVER_API_UPLOAD_SIZE")
_ = viper.BindEnv("server.prefork", "0X_SERVER_PREFORK")
_ = viper.BindEnv("server.server_header", "0X_SERVER_SERVER_HEADER")
_ = viper.BindEnv("server.app_name", "0X_SERVER_APP_NAME")
Expand Down
19 changes: 4 additions & 15 deletions internal/models/api_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ type APIKey struct {
DeletedAt gorm.DeletedAt `gorm:"index"`

// Paste-related limits and permissions
MaxFileSize int64 `gorm:"default:10485760"` // 10MB default
RateLimit int `gorm:"default:100"` // Requests per hour
MaxFileSize int64 // 10MB default
RateLimit int // Requests per hour
AllowPrivate bool `gorm:"default:true"`
AllowUpdates bool `gorm:"default:false"`
AllowUpdates bool `gorm:"default:true"`

// URL shortening permissions
AllowShortlinks bool `gorm:"default:false"` // Whether this key can create shortlinks
AllowShortlinks bool `gorm:"default:true"` // Whether this key can create shortlinks
ShortlinkQuota int `gorm:"default:0"` // 0 = unlimited
ShortlinkPrefix string `gorm:"type:varchar(16)"` // Optional custom prefix for shortened URLs

Expand Down Expand Up @@ -51,17 +51,6 @@ func (k *APIKey) BeforeCreate(tx *gorm.DB) error {
k.Key = GenerateAPIKey()
}

// Set defaults if not specified
if k.MaxFileSize == 0 {
k.MaxFileSize = 10485760 // 10MB
}
if k.RateLimit == 0 {
k.RateLimit = 100
}
if !k.AllowPrivate && !k.AllowUpdates && !k.AllowShortlinks {
k.AllowPrivate = true // Default to allowing private pastes
}

return nil
}

Expand Down
2 changes: 1 addition & 1 deletion internal/server/middleware/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func (m *Middleware) ETag() fiber.Handler {
func (m *Middleware) GetMiddleware() []fiber.Handler {
return []fiber.Handler{
m.RequestID(),
m.Logger(),
// m.Logger(),
m.Recover(),
m.CORS(),
m.Compression(),
Expand Down
9 changes: 4 additions & 5 deletions internal/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
"github.com/watzon/hdur"
"go.uber.org/zap"
"gorm.io/gorm"
"moul.io/zapgorm2"
)

type Server struct {
Expand All @@ -30,8 +29,8 @@ type Server struct {
}

func New(config *config.Config, logger *zap.Logger) *Server {
gormLogger := zapgorm2.New(logger)
gormLogger.SetAsDefault()
// gormLogger := zapgorm2.New(logger)
// gormLogger.SetAsDefault()

// Custom parsers for fiber
fiber.SetParserDecoder(fiber.ParserConfig{
Expand All @@ -47,7 +46,7 @@ func New(config *config.Config, logger *zap.Logger) *Server {

// Initialize database
db, err := database.New(config, &gorm.Config{
Logger: gormLogger,
// Logger: gormLogger,
})
if err != nil {
logger.Fatal("Error connecting to database", zap.Error(err))
Expand Down Expand Up @@ -79,7 +78,7 @@ func New(config *config.Config, logger *zap.Logger) *Server {
// Initialize Fiber app
app := fiber.New(fiber.Config{
ErrorHandler: errorHandler,
BodyLimit: int(config.Server.MaxUploadSize.Int64()),
BodyLimit: int(config.Server.MaxUploadSize),
Views: engine,
Prefork: config.Server.Prefork,
ServerHeader: config.Server.ServerHeader,
Expand Down
2 changes: 2 additions & 0 deletions internal/server/services/apikey.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ func (s *APIKeyService) RequestKey(c *fiber.Ctx) error {
apiKey.Name = req.Name
apiKey.VerifyToken = token
apiKey.VerifyExpiry = time.Now().Add(24 * time.Hour)
apiKey.MaxFileSize = int64(s.config.Server.APIUploadSize)
apiKey.RateLimit = int(s.config.Server.RateLimit.Global.Rate)

if err := s.db.Create(apiKey).Error; err != nil {
s.logger.Error("failed to create API key", zap.Error(err))
Expand Down
12 changes: 6 additions & 6 deletions internal/server/services/paste.go
Original file line number Diff line number Diff line change
Expand Up @@ -397,18 +397,18 @@ func (s *PasteService) CleanupExpired() (int64, error) {
// validateFileSize checks if the file size is within the allowed limits
func (s *PasteService) validateFileSize(size int64, apiKey *models.APIKey) error {
// First check against absolute maximum size for security
if size > s.config.Server.MaxUploadSize.Int64() {
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("File exceeds maximum allowed size of %s", s.config.Server.MaxUploadSize))
if size > int64(s.config.Server.MaxUploadSize) {
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("File exceeds maximum allowed size of %d bytes", s.config.Server.MaxUploadSize))
}

// Then check against the appropriate tier limit
if apiKey != nil {
if size > s.config.Server.APIUploadSize.Int64() {
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("File exceeds API upload limit of %s", s.config.Server.APIUploadSize))
if size > int64(s.config.Server.APIUploadSize) {
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("File exceeds API upload limit of %d bytes", s.config.Server.APIUploadSize))
}
} else {
if size > s.config.Server.DefaultUploadSize.Int64() {
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("File exceeds default upload limit of %s", s.config.Server.DefaultUploadSize))
if size > int64(s.config.Server.DefaultUploadSize) {
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("File exceeds default upload limit of %d bytes", s.config.Server.DefaultUploadSize))
}
}

Expand Down
4 changes: 2 additions & 2 deletions internal/server/tests/paste_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func TestMultipartPasteUpload(t *testing.T) {
},
{
name: "Large content within API limit",
content: strings.Repeat("a", 1024*1024*9), // 9MB
content: strings.Repeat("a", 1024*1024*7), // 7MB
private: false,
mimeType: "text/plain; charset=utf-8",
expectedStatus: 200,
Expand All @@ -96,7 +96,7 @@ func TestMultipartPasteUpload(t *testing.T) {
},
{
name: "Large content exceeding API limit",
content: strings.Repeat("a", 1024*1024*11), // 11MB
content: strings.Repeat("a", 1024*1024*9), // 9MB
private: false,
mimeType: "text/plain; charset=utf-8",
expectedStatus: 400,
Expand Down
11 changes: 5 additions & 6 deletions internal/server/tests/testutils/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"github.com/watzon/0x45/internal/models"
"github.com/watzon/0x45/internal/server"
"github.com/watzon/0x45/internal/storage"
"github.com/watzon/0x45/internal/utils/bytesize"
"go.uber.org/zap"
)

Expand Down Expand Up @@ -63,9 +62,9 @@ func SetupTestEnv(t *testing.T) *TestEnv {
},
},
Server: config.ServerConfig{
MaxUploadSize: bytesize.ByteSize(10 * 1024 * 1024), // 10MB
DefaultUploadSize: bytesize.ByteSize(5 * 1024 * 1024), // 5MB
APIUploadSize: bytesize.ByteSize(10 * 1024 * 1024), // 10MB
MaxUploadSize: 100 * 1024 * 1024, // 10MB
DefaultUploadSize: 5 * 1024 * 1024, // 5MB
APIUploadSize: 8 * 1024 * 1024, // 8MB
AppName: "0x45-test",
ServerHeader: "0x45-test",
ViewsDirectory: viewsDir,
Expand All @@ -92,8 +91,8 @@ func SetupTestEnv(t *testing.T) *TestEnv {
defer func() { _ = logger.Sync() }()

// Create server instance with modified config
origCfg := *cfg // Make a copy of the original config
cfg.Server.MaxUploadSize = bytesize.ByteSize(10 * 1024 * 1024) // 10MB
origCfg := *cfg // Make a copy of the original config
cfg.Server.MaxUploadSize = 10 * 1024 * 1024 // 10MB
cfg.Server.AppName = "0x45-test"
cfg.Server.ServerHeader = "0x45-test"

Expand Down
Loading

0 comments on commit b3e65fd

Please sign in to comment.