Add multi-image compositions, docker setup, tests, and docs

- Add CompositionImage model with multi-image upload and main image flag
- Add composition endpoints: add-images, set-main-image, delete image, by-created-at filter
- Make contact-us by_category public
- Add 24 API tests covering all endpoints
- Add Dockerfile, docker-compose, entrypoint and docker docs
- Add Persian API docs and update README and Postman collection

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Shayan Azadi
2026-06-24 13:43:34 +03:30
parent cc2e082d53
commit 7e71d922d3
18 changed files with 2332 additions and 398 deletions

326
README.md
View File

@@ -1,135 +1,261 @@
# Zoneco ORG Backend API
A professional Django REST Framework backend for Zoneco ORG with PostgreSQL database.
A Django REST Framework backend for Zoneco ORG with PostgreSQL database.
---
## Test Status
```
Ran 24 tests in ~12s — OK (0 failures)
```
| Group | Tests | Status |
|-------|-------|--------|
| Contact Us | 6 | ✅ All pass |
| Compositions | 11 | ✅ All pass |
| Campaigns | 7 | ✅ All pass |
Run tests at any time:
```bash
cd backend
python manage.py test api -v 2
```
---
## Features
- **Contact Us API**: Handle contact form submissions with name, email/phone, description, and category
- **Composition API**: Manage compositions with name, description, and image
- **Campaign API**: Manage campaigns with name, description, start/end times, and image
- **RESTful API**: Full CRUD operations for all models
- **CORS Enabled**: Configured for frontend integration
- **Image Upload**: Support for image uploads with proper media handling
- **Admin Panel**: Django admin interface for managing data
- **Contact Us API** — Contact form submissions with category filter
- **Composition API** — Multi-image upload, main image flag, date-range filter
- **Campaign API** — Campaigns with active / upcoming / ended filters
- **Admin Panel** — Full Django admin at `/admin/`
- **Image Upload** — JPEG, PNG, WebP, GIF, max 5 MB, stored in `media/`
- **CORS Enabled** — Configured for frontend integration
- **Pagination** — 20 items per page
## Database Configuration
---
- **Database Name**: Zoneco_ORG
- **User**: postgres
- **Password**: postgres
- **Host**: localhost
- **Port**: 5432
## Quick Start
## Installation
### 1. Install dependencies
1. **Install Python dependencies:**
```bash
pip install -r requirements.txt
```
```bash
pip install -r requirements.txt
```
2. **Create PostgreSQL database:**
```sql
CREATE DATABASE "Zoneco_ORG";
```
### 2. Create PostgreSQL database
3. **Run migrations:**
```bash
python manage.py makemigrations
python manage.py migrate
```
```sql
CREATE DATABASE "Zoneco_ORG";
```
4. **Create superuser (optional):**
```bash
python manage.py createsuperuser
```
### 3. Apply migrations
5. **Run development server:**
```bash
python manage.py runserver
```
```bash
python manage.py migrate
```
The API will be available at `http://localhost:8000/api/`
### 4. Run development server
```bash
python manage.py runserver
```
API available at `{{base_url}}/api/`
---
## Admin Credentials
| Field | Value |
|-------|-------|
| URL | `{{base_url}}/admin/` |
| Username | `Zoneco_@1405` |
| Password | `Fun_@_zone2026` |
---
## API Endpoints
### Contact Us
- `GET /api/contact-us/` - List all contacts
- `POST /api/contact-us/` - Create new contact (public)
- `GET /api/contact-us/{id}/` - Get specific contact
- `PUT /api/contact-us/{id}/` - Update contact (authenticated)
- `PATCH /api/contact-us/{id}/` - Partial update (authenticated)
- `DELETE /api/contact-us/{id}/` - Delete contact (authenticated)
- `GET /api/contact-us/by_category/?category={category}` - Filter by category
### Authentication
### Compositions
- `GET /api/compositions/` - List all compositions
- `POST /api/compositions/` - Create new composition (authenticated)
- `GET /api/compositions/{id}/` - Get specific composition
- `PUT /api/compositions/{id}/` - Update composition (authenticated)
- `PATCH /api/compositions/{id}/` - Partial update (authenticated)
- `DELETE /api/compositions/{id}/` - Delete composition (authenticated)
| Action | Access |
|--------|--------|
| GET (list, retrieve, custom filters) | Public — no login required |
| POST (create) | Public — no login required |
| PUT / PATCH / DELETE | Admin only — Basic Auth required |
### Campaigns
- `GET /api/campaigns/` - List all campaigns
- `POST /api/campaigns/` - Create new campaign (authenticated)
- `GET /api/campaigns/{id}/` - Get specific campaign
- `PUT /api/campaigns/{id}/` - Update campaign (authenticated)
- `PATCH /api/campaigns/{id}/` - Partial update (authenticated)
- `DELETE /api/campaigns/{id}/` - Delete campaign (authenticated)
- `GET /api/campaigns/active/` - Get active campaigns
- `GET /api/campaigns/upcoming/` - Get upcoming campaigns
- `GET /api/campaigns/ended/` - Get ended campaigns
---
### Contact Us — `/api/contact-us/`
| Method | Endpoint | Access | Description |
|--------|----------|--------|-------------|
| GET | `/api/contact-us/` | Public | List all submissions |
| POST | `/api/contact-us/` | Public | Submit a contact form |
| GET | `/api/contact-us/{id}/` | Public | Get one submission |
| GET | `/api/contact-us/by_category/?category=...` | Public | Filter by category |
| PATCH | `/api/contact-us/{id}/` | Admin | Partial update |
| PUT | `/api/contact-us/{id}/` | Admin | Full update |
| DELETE | `/api/contact-us/{id}/` | Admin | Delete |
**Category choices:** `همکاری` · `فروش` · `پشتیبانی` · `درخواست مشاور` · `سایر`
**Fields:**
| Field | Type | Required |
|-------|------|----------|
| `name` | string | Yes |
| `email_or_phone` | string | Yes |
| `description` | string | Yes |
| `category` | string | Yes |
---
### Compositions — `/api/compositions/`
Each composition can have **multiple images**. One image can be flagged as the **main image** (`is_main: true`).
| Method | Endpoint | Access | Description |
|--------|----------|--------|-------------|
| GET | `/api/compositions/` | Public | List all compositions |
| POST | `/api/compositions/` | Public | Create composition (with optional images) |
| GET | `/api/compositions/{id}/` | Public | Get one composition |
| GET | `/api/compositions/by-created-at/?from=...&to=...` | Public | Filter by creation date range |
| POST | `/api/compositions/{id}/add-images/` | Admin | Add images to existing composition |
| POST | `/api/compositions/{id}/set-main-image/` | Admin | Set main image |
| DELETE | `/api/compositions/{id}/images/{image_id}/` | Admin | Delete a single image |
| PATCH | `/api/compositions/{id}/` | Admin | Partial update |
| PUT | `/api/compositions/{id}/` | Admin | Full update |
| DELETE | `/api/compositions/{id}/` | Admin | Delete composition |
**Fields:**
| Field | Type | Required | Notes |
|-------|------|----------|-------|
| `name` | string | Yes | |
| `description` | string | Yes | |
| `uploaded_images` | file[] | No | One or more image files (multipart) |
| `main_image_index` | integer | No | 0-based index of main image in `uploaded_images`. Defaults to 0. |
**Image constraints:** JPEG, PNG, WebP, GIF — max **5 MB** each — stored at `media/compositions/`
**`by-created-at` query params:**
| Param | Required | Format |
|-------|----------|--------|
| `from` | At least one | ISO 8601 e.g. `2026-06-01T00:00:00Z` |
| `to` | At least one | ISO 8601 e.g. `2026-06-30T23:59:59Z` |
---
### Campaigns — `/api/campaigns/`
| Method | Endpoint | Access | Description |
|--------|----------|--------|-------------|
| GET | `/api/campaigns/` | Public | List all campaigns |
| POST | `/api/campaigns/` | Public | Create campaign |
| GET | `/api/campaigns/{id}/` | Public | Get one campaign |
| GET | `/api/campaigns/active/` | Public | Currently active campaigns |
| GET | `/api/campaigns/upcoming/` | Public | Not started yet |
| GET | `/api/campaigns/ended/` | Public | Already finished |
| PATCH | `/api/campaigns/{id}/` | Admin | Partial update |
| PUT | `/api/campaigns/{id}/` | Admin | Full update |
| DELETE | `/api/campaigns/{id}/` | Admin | Delete |
**Fields:**
| Field | Type | Required | Notes |
|-------|------|----------|-------|
| `name` | string | Yes | |
| `description` | string | Yes | |
| `start_time` | datetime | Yes | ISO 8601 |
| `end_time` | datetime | Yes | Must be after `start_time` |
| `image` | file | No | Campaign image (multipart) |
**Read-only computed fields:** `is_active`, `is_upcoming`, `is_ended`
---
## Models
### ContactUs
- `name` (CharField): Name of the contact
- `email_or_phone` (CharField): Email or phone number
- `description` (TextField): Message description
- `category` (CharField): Category (همکاری, فروش, پشتیبانی, درخواست مشاور, سایر)
- `created_at` (DateTimeField): Creation timestamp
- `updated_at` (DateTimeField): Last update timestamp
| Field | Type |
|-------|------|
| `name` | CharField |
| `email_or_phone` | CharField |
| `description` | TextField |
| `category` | CharField (choices) |
| `created_at` | DateTimeField (auto) |
| `updated_at` | DateTimeField (auto) |
### Composition
- `name` (CharField): Name of the composition
- `description` (TextField): Description
- `image` (ImageField): Composition image
- `created_at` (DateTimeField): Creation timestamp
- `updated_at` (DateTimeField): Last update timestamp
| Field | Type |
|-------|------|
| `name` | CharField |
| `description` | TextField |
| `image` | ImageField (legacy) |
| `created_at` | DateTimeField (auto) |
| `updated_at` | DateTimeField (auto) |
### CompositionImage
| Field | Type |
|-------|------|
| `composition` | ForeignKey → Composition |
| `image` | ImageField |
| `is_main` | BooleanField |
| `created_at` | DateTimeField (auto) |
DB constraint: only one `is_main=True` per composition.
### Campaign
- `name` (CharField): Campaign name
- `description` (TextField): Campaign description
- `start_time` (DateTimeField): Campaign start time
- `end_time` (DateTimeField): Campaign end time
- `image` (ImageField): Campaign image
- `created_at` (DateTimeField): Creation timestamp
- `updated_at` (DateTimeField): Last update timestamp
## Admin Panel
| Field | Type |
|-------|------|
| `name` | CharField |
| `description` | TextField |
| `start_time` | DateTimeField |
| `end_time` | DateTimeField |
| `image` | ImageField |
| `created_at` | DateTimeField (auto) |
| `updated_at` | DateTimeField (auto) |
Access the Django admin panel at `http://localhost:8000/admin/` after creating a superuser.
## CORS Configuration
CORS is configured to allow requests from:
- `http://localhost:5173` (Vite default)
- `http://localhost:3000`
- `http://127.0.0.1:5173`
- `http://127.0.0.1:3000`
---
## Media Files
Uploaded images are stored in the `media/` directory:
- Composition images: `media/compositions/`
- Campaign images: `media/campaigns/`
| Content | Path |
|---------|------|
| Composition images | `media/compositions/` |
| Campaign images | `media/campaigns/` |
## Development Notes
---
- The backend uses Django REST Framework for API endpoints
- PostgreSQL is used as the database
- Image uploads are handled via multipart/form-data
- All models include proper indexing for performance
- Serializers include validation and proper error handling
- Viewsets provide both public read access and authenticated write access
## Postman Collection
Import `backend/Zoneco_ORG_API.postman_collection.json` into Postman.
Set the `base_url` collection variable to your server address.
---
## Persian Documentation
Full Persian API documentation: `backend/API_DOCS_FA.md`
---
## HTTP Status Codes
| Code | Meaning |
|------|---------|
| 200 | OK |
| 201 | Created |
| 204 | Deleted (no body) |
| 400 | Invalid data |
| 403 | Forbidden (login required) |
| 404 | Not found |