6.8 KiB
Advertisements Controller Documentation
File: controllers/admin/advertisements.controller.js
Base URL: /api/admin/advertisements
Guards: authenticate → requireAdmin() → adminLimiter
Table of Contents
- Get All Advertisements
- Get Single Advertisement
- Create Advertisement
- Update Advertisement
- Archive Advertisement
- Bulk Archive Advertisements
- Restore Advertisement
- Bulk Restore Advertisements
- Get Archived Advertisements
- Get Field Values
Status Derivation
Status is never trusted as stored — it is recomputed on every read and write:
| Condition | Derived Status |
|---|---|
deletedAt is set |
archived |
is_active = false |
draft |
end_date < now |
expired |
start_date > now |
scheduled |
| Otherwise | active |
archived is the only status that bypasses derivation (set explicitly by archive/restore).
CTAs
Each advertisement supports up to 2 CTAs:
[
{ "label": "Learn More", "link": "/courses", "variant": "default" },
{ "label": "Sign Up", "link": "/register", "variant": "outline" }
]
- First CTA defaults to
"default"variant; second defaults to"outline". - An explicit valid variant from the client (
"default"or"outline") always wins. - Items beyond 2 are silently discarded.
Get All Advertisements
GET /api/admin/advertisements
Returns a paginated list of active (non-deleted) advertisements. Status is resynced on the way out.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| page | number | No | Default: 1 |
| limit | number | No | Default: 10, max: 1000 |
| filters | array | No | JSON array of filter objects |
| sort | array | No | JSON array of sort objects |
Response 200
{
"status": "success",
"message": "Advertisements retrieved.",
"data": [...],
"pagination": { "page": 1, "limit": 10, "total": 5, "totalPages": 1 }
}
Get Single Advertisement
GET /api/admin/advertisements/:advertisementId
Returns one advertisement with its image asset and audit user info.
Response 200
{
"status": "success",
"message": "Advertisement retrieved.",
"data": {
"advertisement_id": 1,
"uuid": "...",
"type": "hero",
"status": "active",
"badge_label": "New",
"headline": "Welcome",
"description": "...",
"image_url": null,
"image_asset_id": 12,
"image": { "asset_id": 12, "display_name": "hero.jpg", "file_url": "...", "thumbnail_url": "..." },
"ctas": [{ "label": "Start", "link": "/start", "variant": "default" }],
"start_date": "2026-01-01T00:00:00.000Z",
"end_date": null,
"order": 0,
"is_active": true,
"size": null,
"click_count": 0,
"creator": { "user_id": 1, "full_name": "Admin User" },
"updater": null,
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z"
}
}
Error Responses
| Status | Message |
|---|---|
400 |
Invalid advertisement ID. |
404 |
Advertisement not found. |
Create Advertisement
POST /api/admin/advertisements
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
type |
string | Yes | hero, banner, popup, sidebar |
createdBy |
number | Yes | User ID of creator |
badge_label |
string | No | Small label shown on the ad |
headline |
string | No | Main heading |
description |
string | No | Body text |
image_url |
string | No | Direct image URL |
image_asset_id |
number | No | FK to assets table |
ctas |
array | No | Up to 2 CTA objects [{ label, link, variant }] |
start_date |
date | No | ISO date string |
end_date |
date | No | ISO date string |
order |
number | No | Display order. Default: 0 |
is_active |
boolean | No | Default: true |
size |
string | No | sm, md, lg — banner only |
Response 201
{
"status": "success",
"message": "Advertisement created.",
"data": { ... }
}
Error Responses
| Status | Message |
|---|---|
400 |
type is required. |
400 |
Invalid type. Must be one of: hero, banner, popup, sidebar |
400 |
createdBy is required. |
400 |
Invalid size. Must be one of: sm, md, lg |
Update Advertisement
PATCH /api/admin/advertisements/:advertisementId
Partial update. Only fields present in the body are changed. Status is recomputed after all fields are applied.
Request Body
Same optional fields as Create. Does not accept type once set. Accepts updatedBy.
Response 200
{ "status": "success", "message": "Advertisement updated.", "data": { ... } }
Archive Advertisement
DELETE /api/admin/advertisements/:advertisementId
Soft-deletes the advertisement (deletedAt set, status → archived).
Response 200
{ "status": "success", "message": "Advertisement archived." }
Bulk Archive Advertisements
DELETE /api/admin/advertisements/bulk
Rate-limited. Soft-deletes multiple advertisements.
Request Body
{ "ids": [1, 2, 3], "deletedBy": 1 }
Response 200
{
"status": "success",
"message": "3 advertisement(s) archived.",
"archived_ids": [1, 2, 3],
"skipped_ids": []
}
Restore Advertisement
PATCH /api/admin/advertisements/:advertisementId/restore
Restores a soft-deleted advertisement.
Response 200
{ "status": "success", "message": "Advertisement restored.", "data": { ... } }
Bulk Restore Advertisements
PATCH /api/admin/advertisements/bulk-restore
Request Body
{ "ids": [1, 2] }
Response 200
{
"status": "success",
"message": "2 advertisement(s) restored.",
"restored_ids": [1, 2],
"skipped_ids": []
}
Get Archived Advertisements
GET /api/admin/advertisements/archived
Returns paginated list of soft-deleted advertisements.
Response 200
{
"status": "success",
"message": "Archived advertisements retrieved.",
"data": [...],
"pagination": { ... }
}
Get Field Values
GET /api/admin/advertisements/field-values
Returns distinct values for filterable advertisement fields. Used by DataTable filter dropdowns.
Response 200
{
"status": "success",
"message": "Field values retrieved.",
"data": { "type": ["hero", "banner"], "status": ["active", "draft"] }
}