Skip to main content

Overview

DefDrive implements a quota system that limits each user’s file count and total storage usage. These limits prevent resource abuse and ensure fair usage across all users. Limits are enforced at upload time and can be customized per user.

File Count Limit

Maximum number of files per user

Storage Limit

Maximum total storage in bytes

Default: 100 Files

New users start with 100 files

Default: 1 GB

New users get 1 GB storage

User Limits in the Model

Limits are stored directly in the User model with database-level defaults.

User Model Definition

// From models/user.go:7-18
type User struct {
    gorm.Model
    Name     string
    Email    string
    Username string `gorm:"unique"`
    Password string
    
    MaxFiles   int   `gorm:"default:100"`        // default 100 files
    MaxStorage int64 `gorm:"default:1073741824"` // default 1GB

    Files []File `gorm:"foreignKey:UserID;references:ID"`
}
Default Values:
  • MaxFiles: 100 files
  • MaxStorage: 1,073,741,824 bytes (1 GB)
These defaults are enforced at the database level using GORM’s default tag.

Field Types

FieldTypeDescription
MaxFilesintMaximum number of files allowed
MaxStorageint64Maximum total storage in bytes
Files[]FileRelationship to owned files

Enforcement During Upload

Limits are checked before accepting file uploads to ensure users cannot exceed their quotas.

Upload Limit Flow

The upload process checks both file count and storage limits:
// From controllers/file.go:24-122
func (fc *FileController) Upload(c *gin.Context) {
    // 1. Authenticate user
    userID, exists := c.Get("userID")
    
    // 2. Get uploaded file
    file, err := c.FormFile("file")
    
    // 3. Get user limits from database
    var user models.User
    fc.DB.First(&user, userID)
    
    // 4. Check file count limit
    // 5. Check storage limit
    // 6. Save file if within limits
}

Step 1: Check File Count

// From controllers/file.go:46-61
// Check current file count
var currentFileCount int64
if err := fc.DB.Model(&models.File{}).Where("user_id = ?", userID).Count(&currentFileCount).Error; err != nil {
    c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to check current file count"})
    return
}

// Check file limit
if currentFileCount >= int64(user.MaxFiles) {
    c.JSON(http.StatusForbidden, gin.H{
        "error":         "File limit exceeded",
        "current_files": currentFileCount,
        "max_files":     user.MaxFiles,
    })
    return
}
File count check uses >= - If a user has reached their limit (e.g., 100 files), they cannot upload additional files even if they have storage space remaining.

Step 2: Check Storage Limit

// From controllers/file.go:63-79
// Check current storage usage
var currentStorage int64
if err := fc.DB.Model(&models.File{}).Where("user_id = ?", userID).Select("COALESCE(SUM(size), 0)").Scan(&currentStorage).Error; err != nil {
    c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to check current storage usage"})
    return
}

// Check storage limit
if currentStorage+file.Size > user.MaxStorage {
    c.JSON(http.StatusForbidden, gin.H{
        "error":           "Storage limit exceeded",
        "current_storage": currentStorage,
        "max_storage":     user.MaxStorage,
        "file_size":       file.Size,
    })
    return
}
Storage check includes the new file: The system calculates currentStorage + file.Size to ensure the new file won’t cause the user to exceed their quota.

SQL Query for Storage Calculation

The storage calculation uses PostgreSQL aggregation:
SELECT COALESCE(SUM(size), 0) 
FROM files 
WHERE user_id = ? AND deleted_at IS NULL
COALESCE(SUM(size), 0) returns 0 when a user has no files, preventing NULL values which would cause errors in comparison operations.

Checking User Limits

Users can query their current usage and remaining quota.

Get User Limits Endpoint

// From controllers/user.go:102-137
func (uc *UserController) GetUserLimits(c *gin.Context) {
    userID, exists := c.Get("userID")
    if !exists {
        c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
        return
    }

    var user models.User
    if err := uc.DB.First(&user, userID).Error; err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get user information"})
        return
    }

    // Get current file count
    var currentFileCount int64
    if err := uc.DB.Model(&models.File{}).Where("user_id = ?", userID).Count(&currentFileCount).Error; err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get current file count"})
        return
    }

    // Get current storage usage
    var currentStorage int64
    if err := uc.DB.Model(&models.File{}).Where("user_id = ?", userID).Select("COALESCE(SUM(size), 0)").Scan(&currentStorage).Error; err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get current storage usage"})
        return
    }

    c.JSON(http.StatusOK, gin.H{
        "max_files":         user.MaxFiles,
        "max_storage":       user.MaxStorage,
        "current_files":     currentFileCount,
        "current_storage":   currentStorage,
        "remaining_files":   user.MaxFiles - int(currentFileCount),
        "remaining_storage": user.MaxStorage - currentStorage,
    })
}
Reference: controllers/user.go:102-137

Response Example

{
  "max_files": 100,
  "max_storage": 1073741824,
  "current_files": 42,
  "current_storage": 524288000,
  "remaining_files": 58,
  "remaining_storage": 549453824
}

Current Usage

Shows files uploaded and bytes used

Remaining Quota

Calculates available files and storage

Updating User Limits

Administrators can modify individual user limits.

Update Limits Endpoint

// From controllers/user.go:140-194
func (uc *UserController) UpdateUserLimits(c *gin.Context) {
    userID := c.Param("userID")
    if userID == "" {
        c.JSON(http.StatusBadRequest, gin.H{"error": "User ID is required"})
        return
    }

    var updateRequest struct {
        MaxFiles   *int   `json:"max_files"`
        MaxStorage *int64 `json:"max_storage"`
    }

    if err := c.ShouldBindJSON(&updateRequest); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    var user models.User
    if err := uc.DB.First(&user, userID).Error; err != nil {
        c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
        return
    }

    // Update limits if provided
    if updateRequest.MaxFiles != nil {
        if *updateRequest.MaxFiles < 0 {
            c.JSON(http.StatusBadRequest, gin.H{"error": "Max files cannot be negative"})
            return
        }
        user.MaxFiles = *updateRequest.MaxFiles
    }

    if updateRequest.MaxStorage != nil {
        if *updateRequest.MaxStorage < 0 {
            c.JSON(http.StatusBadRequest, gin.H{"error": "Max storage cannot be negative"})
            return
        }
        user.MaxStorage = *updateRequest.MaxStorage
    }

    if err := uc.DB.Save(&user).Error; err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update user limits"})
        return
    }

    c.JSON(http.StatusOK, gin.H{
        "message": "User limits updated successfully",
        "user": gin.H{
            "id":          user.ID,
            "username":    user.Username,
            "max_files":   user.MaxFiles,
            "max_storage": user.MaxStorage,
        },
    })
}
Reference: controllers/user.go:140-194

API Request Example

PATCH /api/users/:userID/limits
Content-Type: application/json

{
  "max_files": 200,
  "max_storage": 5368709120
}
Partial updates supported: You can update max_files or max_storage independently. Only provide the fields you want to change.

Validation Rules

  • Both MaxFiles and MaxStorage cannot be negative
  • Values must be provided as pointers (*int, *int64) to distinguish between “not provided” and “zero”
  • No validation against current usage (admins can set limits below current usage)
Setting limits below current usage: If an admin sets MaxFiles = 50 for a user who already has 75 files, the user keeps their existing files but cannot upload new ones until they delete enough to get below the limit.

Admin Endpoints

Get All Users’ Limits

Retrieve limits and usage for all users in the system:
// From controllers/user.go:197-230
func (uc *UserController) GetAllUsersLimits(c *gin.Context) {
    var users []models.User
    if err := uc.DB.Find(&users).Error; err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get users"})
        return
    }

    var userLimits []gin.H
    for _, user := range users {
        // Get current file count for each user
        var currentFileCount int64
        uc.DB.Model(&models.File{}).Where("user_id = ?", user.ID).Count(&currentFileCount)

        // Get current storage usage for each user
        var currentStorage int64
        uc.DB.Model(&models.File{}).Where("user_id = ?", user.ID).Select("COALESCE(SUM(size), 0)").Scan(&currentStorage)

        userLimits = append(userLimits, gin.H{
            "user_id":           user.ID,
            "username":          user.Username,
            "email":             user.Email,
            "max_files":         user.MaxFiles,
            "max_storage":       user.MaxStorage,
            "current_files":     currentFileCount,
            "current_storage":   currentStorage,
            "remaining_files":   user.MaxFiles - int(currentFileCount),
            "remaining_storage": user.MaxStorage - currentStorage,
        })
    }

    c.JSON(http.StatusOK, gin.H{"users": userLimits})
}
Reference: controllers/user.go:197-230

Response Example

{
  "users": [
    {
      "user_id": 1,
      "username": "johndoe",
      "email": "[email protected]",
      "max_files": 100,
      "max_storage": 1073741824,
      "current_files": 42,
      "current_storage": 524288000,
      "remaining_files": 58,
      "remaining_storage": 549453824
    },
    {
      "user_id": 2,
      "username": "janedoe",
      "email": "[email protected]",
      "max_files": 200,
      "max_storage": 5368709120,
      "current_files": 150,
      "current_storage": 4294967296,
      "remaining_files": 50,
      "remaining_storage": 1073741824
    }
  ]
}
This endpoint is useful for monitoring resource usage across all users and identifying users who are approaching their limits.

Storage Size Reference

1 MB

1,048,576 bytes

1 GB (default)

1,073,741,824 bytes

5 GB

5,368,709,120 bytes

10 GB

10,737,418,240 bytes

Common Limit Configurations

User TypeMaxFilesMaxStorageDescription
Free1001 GBDefault new user limits
Basic5005 GBEntry-level paid tier
Pro200025 GBProfessional tier
Enterprise10000100 GBHigh-volume users
Unlimited-1*-1*Admin/special accounts
*DefDrive doesn’t currently implement “unlimited” quotas with negative values. To approximate unlimited access, set very high limits (e.g., 1,000,000 files and 1 TB storage).

Limit Enforcement Summary

Upload Time Checks

  1. File count check - Prevents upload if currentFiles >= MaxFiles
  2. Storage check - Prevents upload if currentStorage + newFileSize > MaxStorage
  3. Both must pass - If either check fails, the upload is rejected

Error Responses

File limit exceeded:
{
  "error": "File limit exceeded",
  "current_files": 100,
  "max_files": 100
}
Storage limit exceeded:
{
  "error": "Storage limit exceeded",
  "current_storage": 1073741824,
  "max_storage": 1073741824,
  "file_size": 5242880
}
Error responses include helpful context about current usage, limits, and the attempted file size to help users understand why the upload was rejected.

File Management

Learn how files are stored and managed

Authentication

Understand user authentication

Access Control

Explore file sharing and restrictions

Build docs developers (and LLMs) love