Unified Downloads & 10 Daily Free Quota Architecture (user_download_list Engine)
# Unified Downloads & 10 Daily Free Quota Architecture (user_download_list Engine)
## 1. Core Philosophy & Anti-Table-Duplication Standard
To maintain extreme database efficiency and avoid table fragmentation, Pixahunt strictly adheres to the **Anti-Table-Duplication Rule**. We do not create separate per-module download tracking tables (such as `photo_downloads`, `wallpaper_downloads`, `mockup_downloads`, `svg_downloads`).
Instead, all asset downloads across all media categories are consolidated into a single, high-performance master table: `user_download_list`.
---
## 2. Database Schema & Storage Efficiency
### Migration
* `database/migrations/2026_10_12_202000_create_user_download_list_table.php`
### Table: `user_download_list`
| Column | Type | Description |
|---|---|---|
| `id` | `BIGINT UNSIGNED AUTO_INCREMENT` | Primary Key |
| `user_id` | `BIGINT UNSIGNED` | Foreign Key referencing `users.id` (Indexed, Cascade on Delete) |
| `download_date` | `DATE` | Current UTC calendar date (Indexed) |
| `downloads` | `JSON` | List of downloaded asset items for that date |
| `download_count` | `INT UNSIGNED` | Running total count for the day (Default 0) |
| `created_at` | `TIMESTAMP` | Record creation timestamp |
| `updated_at` | `TIMESTAMP` | Last download update timestamp |
### Composite Index
* `UNIQUE INDEX unique_user_daily_download (user_id, download_date)`
> [!IMPORTANT]
> **1 Row Per User Per Day Guarantee:**
> Regardless of whether a user downloads 1 asset or 100 assets, exactly **ONE single database row** is stored for that user on that calendar day. Each download appends a lightweight object to the `downloads` JSON array and increments `download_count`. This eliminates millions of redundant rows annually.
---
## 3. Daily Free Quota & Pro Rules
### 10 Daily Free Quota Rule
* Every registered user without an active Pro subscription receives **10 free downloads every calendar day**.
* Once 10 downloads are consumed on a given date, the user is prevented from downloading further free assets until the next calendar day, or until they upgrade to Pro.
* Friendly warning banner is displayed: *"Daily download limit reached (10/10). Upgrade to Pro for unlimited downloads."*
### Pro Subscription Status
* Checked via `User::isPro()` which inspects active verified Pro subscriptions.
* Pro users have **unlimited daily downloads** and are never capped by the 10-item limit.
### Pro Assets vs. Free Assets
* **Free Assets:** Can be downloaded by any user within their 10 daily download quota. Free assets **MUST NOT** show a PRO lock badge or disable download buttons for regular users.
* **Pro Assets (`is_pro = 1`):** Require an active Pro subscription to unlock and download.
---
## 4. Service Layer: `DownloadManagerService`
**Namespace:** `App\Services\DownloadManagerService`
### Core Methods:
1. `canDownload(?User $user, bool $isProAsset): array`
- Evaluates whether the user can download the item.
- Returns:
```php
[
'allowed' => true|false,
'reason' => 'ok'|'login_required'|'pro_required'|'daily_limit_reached',
'remaining' => 7, // integer remaining downloads
'is_pro_user' => false,
]
```
2. `recordDownload(User $user, string $assetType, int $assetId, array $metadata = []): UserDownloadList`
- Atomically fetches or creates the daily row via `firstOrCreate(['user_id' => $user->id, 'download_date' => $today])`.
- Appends item: `['type' => $assetType, 'id' => $assetId, 'downloaded_at' => now(), 'metadata' => $metadata]`.
- Increments `download_count`.
3. `getDailyRecord(User $user, ?string $date = null): ?UserDownloadList`
4. `getRemainingDownloads(User $user): int`
---
## 5. Front-End & UI Guidelines
1. **Brand Colors:**
- Primary download action buttons must use the secondary action brand color: `bg-orange-500` / `hover:bg-orange-600` (text: `text-white`).
2. **Quota Indicator:**
- Display live remaining count on detail pages: `{{ $remainingDownloads }}/10 Left Today`.
- If user is Pro, show `Pro Member (Unlimited)`.
3. **CDN Strict Delivery:**
- All display thumbnails are delivered via Bunny CDN (`\App\Models\SiteSetting::current()->cdn`).
- Actual original assets are streamed securely through controller download endpoints with quota enforcement.
4. **Templates Updated:**
- `resources/views/frontend/photos/show.blade.php`
- `resources/views/frontend/photos/wallpaper_show.blade.php`