Skip to content

Commit 4784d8a

Browse files
committed
feat(api): added rewards route to get and post rewards
1 parent 7808969 commit 4784d8a

3 files changed

Lines changed: 189 additions & 0 deletions

File tree

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import System from '../../../models/schemas/System.js';
2+
3+
/**
4+
* Fetches rewards/rewards from the system.
5+
* Supports:
6+
* - All rewards
7+
* - Filtering by type (coupon, giftcard, voucher)
8+
* - Fetching a specific reward by its ID/code
9+
*/
10+
export const getRewards = async (req, res) => {
11+
try {
12+
const key = req.headers.key;
13+
const { id } = req.params;
14+
const { type } = req.query;
15+
16+
// Authorization check
17+
// if (!key || key !== process.env.ACCESS_KEY) {
18+
// return res.status(401).json({ message: 'Unauthorized' });
19+
// }
20+
21+
// Fetch only the rewards field
22+
const system = await System.findById('system').select('rewards');
23+
24+
if (!system || !Array.isArray(system.rewards)) {
25+
return res.status(404).json({ message: 'No rewards found' });
26+
}
27+
28+
// If an ID is provided, return that specific reward
29+
if (id) {
30+
const reward = system.rewards.find(r => r._id === id);
31+
if (!reward) {
32+
return res.status(404).json({ message: 'Reward not found' });
33+
}
34+
return res.status(200).json(reward);
35+
}
36+
37+
// Otherwise, return all (optionally filtered by type)
38+
let rewards = system.rewards.filter(r => r.isActive);
39+
if (type) {
40+
rewards = rewards.filter(r => r.type === type);
41+
}
42+
43+
return res.status(200).json({
44+
count: rewards.length,
45+
rewards,
46+
});
47+
} catch (error) {
48+
console.error('Error fetching rewards:', error);
49+
return res.status(500).json({ message: 'Internal Server Error' });
50+
}
51+
};
52+
53+
/**
54+
* Creates a new reward (giftcard, coupon, voucher) and adds it to the system.
55+
*/
56+
export const createReward = async (req, res) => {
57+
try {
58+
const key = req.headers.key;
59+
60+
// Authorization check
61+
if (!key || key !== process.env.ACCESS_KEY) {
62+
return res.status(401).json({ message: 'Unauthorized' });
63+
}
64+
65+
const { id, type, description, discount, appliesTo, usage, validFrom, validUntil, isActive = true } = req.body;
66+
67+
if (!id || !type || !discount || !discount.type || discount.value === undefined) {
68+
return res.status(400).json({ message: 'Missing required reward fields' });
69+
}
70+
71+
const system = await System.findById('system');
72+
if (!system) {
73+
return res.status(404).json({ message: 'System not found' });
74+
}
75+
76+
// Check for existing reward with same ID
77+
const exists = system.rewards.find(r => r._id === id);
78+
if (exists) {
79+
return res.status(409).json({ message: 'Reward with this ID already exists' });
80+
}
81+
82+
const newReward = {
83+
_id: id,
84+
type,
85+
description,
86+
discount,
87+
appliesTo: appliesTo || {},
88+
usage: usage || {},
89+
validFrom,
90+
validUntil,
91+
isActive,
92+
};
93+
94+
system.rewards.push(newReward);
95+
await system.save();
96+
97+
return res.status(201).json({
98+
message: 'Reward created successfully',
99+
reward: newReward,
100+
});
101+
} catch (error) {
102+
console.error('Error creating reward:', error);
103+
return res.status(500).json({ message: 'Internal Server Error' });
104+
}
105+
};

src/routes/v4/index.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1256,6 +1256,21 @@ import pageRoutes from './internal/pages.js';
12561256
*/
12571257
router.use('/pages', pageRoutes);
12581258

1259+
import rewardsRoutes from './internal/rewards.js';
1260+
/**
1261+
* @api {use} Mount Rewards Routes
1262+
* @apiDescription Mount the rewards-related routes for handling interactions.
1263+
* @apiName UseRewardsRoutes
1264+
* @apiGroup Routes
1265+
*
1266+
* @apiSuccess {Object} routes Rewards-related routes mounted on the parent router.
1267+
*
1268+
* @function createRewardsRoutes
1269+
* @description Creates and returns a set of routes for handling interactions related to rewards.
1270+
* @returns {Object} Rewards-related routes.
1271+
*/
1272+
router.use('/rewards', rewardsRoutes);
1273+
12591274
/**
12601275
* Exporting the router for use in other parts of the application.
12611276
* @exports {Router} router - Express Router instance with mounted routes.

src/routes/v4/internal/rewards.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { Router } from 'express';
2+
import { createReward, getRewards } from '../../../controllers/v4/internal/rewards.js';
3+
import createRateLimiter from '../../../middlewares/rateLimit.js';
4+
5+
const router = Router();
6+
7+
router
8+
.route('/')
9+
/**
10+
* @api {get} v4/rewards Get All Rewards
11+
* @apiDescription Fetch all available rewards (coupons, vouchers, giftcards).
12+
* @apiName getRewards
13+
* @apiGroup Rewards
14+
* @apiPermission internal
15+
*
16+
* @apiHeader {String} Key Internal access token
17+
*
18+
* @apiSuccess {Array} rewards List of available rewards.
19+
* @apiError (Unauthorized 401) Unauthorized Invalid or missing access key.
20+
* @apiError (Internal Server Error 500) InternalServerError An error occurred while fetching rewards.
21+
*/
22+
.get(createRateLimiter(), getRewards)
23+
/**
24+
* @api {post} v4/rewards Create a New Reward
25+
* @apiDescription Create a new reward entry for use as a giftcard, coupon, or voucher.
26+
* @apiName createReward
27+
* @apiGroup Rewards
28+
* @apiPermission admin
29+
*
30+
* @apiHeader {String} Key Internal access token
31+
*
32+
* @apiBody {String} _id Unique ID of the reward.
33+
* @apiBody {String} type Reward type (coupon, giftcard, voucher).
34+
* @apiBody {Object} discount Discount details.
35+
* @apiBody {Object} appliesTo (optional) Role/plan filters.
36+
* @apiBody {Object} usage (optional) Max usage/conditions.
37+
* @apiBody {Date} validFrom (optional) Validity start.
38+
* @apiBody {Date} validUntil (optional) Validity end.
39+
* @apiBody {Boolean} isActive Whether reward is currently active.
40+
*
41+
* @apiSuccess {Object} reward Created reward object.
42+
* @apiError (Bad Request 400) BadRequest Missing or invalid fields.
43+
* @apiError (Conflict 409) Conflict Reward with this ID already exists.
44+
* @apiError (Unauthorized 401) Unauthorized Invalid or missing access key.
45+
* @apiError (Internal Server Error 500) InternalServerError An error occurred while creating reward.
46+
*/
47+
.post(createRateLimiter(), createReward);
48+
49+
router
50+
.route('/:id')
51+
/**
52+
* @api {get} v4/rewards/:id Get Specific Reward
53+
* @apiDescription Fetch a specific reward by its unique ID.
54+
* @apiName getSingleReward
55+
* @apiGroup Rewards
56+
* @apiPermission internal
57+
*
58+
* @apiHeader {String} Key Internal access token
59+
*
60+
* @apiParam {String} id Reward's unique identifier.
61+
*
62+
* @apiSuccess {Object} reward Reward details.
63+
* @apiError (Not Found 404) NotFound Reward with the given ID does not exist.
64+
* @apiError (Unauthorized 401) Unauthorized Invalid or missing access key.
65+
* @apiError (Internal Server Error 500) InternalServerError An error occurred while fetching reward.
66+
*/
67+
.get(createRateLimiter(), getRewards);
68+
69+
export default router;

0 commit comments

Comments
 (0)