Skip to content

Commit

Permalink
Merge pull request #34 from brandonnorsworthy/updates
Browse files Browse the repository at this point in the history
Updates
  • Loading branch information
brandonnorsworthy authored Sep 21, 2024
2 parents be843c5 + 2beab8b commit 5e1aca6
Show file tree
Hide file tree
Showing 11 changed files with 104 additions and 59 deletions.
22 changes: 20 additions & 2 deletions controllers/suggestion.controller .ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export default {

convertSuggestionIntoQuest: async (request: Request, response: Response) => {
try {
const { userId } = (request as AuthenticatedRequest).tokenData;
const { userId, role } = (request as AuthenticatedRequest).tokenData;
const { suggestionId } = request.params;
let { title, description, objectives, categoryId, image_url } = request.body;

Expand All @@ -62,6 +62,10 @@ export default {
return response.status(404).send('Suggestion not found');
}

if (role !== "admin" || suggestion.user_id !== userId) {
return response.status(403).send('You are not allowed to convert this suggestion into a quest');
}

const newQuest = await questService.createQuest(title, description, objectives, categoryId, suggestion.user_id, image_url);
await suggestionService.deleteSuggestion(Number(suggestionId), userId);

Expand All @@ -70,5 +74,19 @@ export default {
console.error(error);
return response.status(500).send('An error occurred while converting the suggestion into a quest');
}
}
},

deleteSuggestion: async (request: Request, response: Response) => {
try {
const { suggestionId } = request.params;
const { userId } = (request as AuthenticatedRequest).tokenData;

await suggestionService.deleteSuggestion(Number(suggestionId), userId);

return response.send('Suggestion deleted');
} catch (error) {
console.error(error);
return response.status(500).send('An error occurred while deleting the suggestion');
}
},
};
14 changes: 7 additions & 7 deletions database/init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,13 @@ INSERT INTO categories (name) VALUES
('Building');

-- Insert 10 rows into users table
INSERT INTO users (username, role_id, completed_quests, metadata, password, approved_suggestions) VALUES
('zcog', 3, ARRAY[1, 3, 6, 8], '{"sound": false}', '$2b$10$ita5UtzrE2JBh.275g5i8ebBnnM99D9wZhRmcqfZYfgTjbt.baNyG', 0),
('notacoconut', 3, ARRAY[7, 9], '{"sound": true}', '$2b$10$ita5UtzrE2JBh.275g5i8ebBnnM99D9wZhRmcqfZYfgTjbt.baNyG', 0),
('demouser1', 2, ARRAY[2, 4, 7], '{"sound": true}', '$2b$10$ita5UtzrE2JBh.275g5i8ebBnnM99D9wZhRmcqfZYfgTjbt.baNyG', 2),
('demouser2', 2, ARRAY[1, 3, 4], '{"sound": true}', '$2b$10$ita5UtzrE2JBh.275g5i8ebBnnM99D9wZhRmcqfZYfgTjbt.baNyG', 1),
('demouser3', 1, ARRAY[5, 6], '{"sound": false}', '$2b$10$ita5UtzrE2JBh.275g5i8ebBnnM99D9wZhRmcqfZYfgTjbt.baNyG', 1),
('demouser4', 1, ARRAY[1, 2, 8], '{"sound": true}', '$2b$10$ita5UtzrE2JBh.275g5i8ebBnnM99D9wZhRmcqfZYfgTjbt.baNyG', 1);
INSERT INTO users (username, role_id, completed_quests, metadata, password, approved_suggestions, last_login) VALUES
('zcog', 3, ARRAY[1, 3, 6, 8], '{"sound": false}', '$2b$10$ita5UtzrE2JBh.275g5i8ebBnnM99D9wZhRmcqfZYfgTjbt.baNyG', 0, NOW()),
('notacoconut', 3, ARRAY[7, 9], '{"sound": true}', '$2b$10$ita5UtzrE2JBh.275g5i8ebBnnM99D9wZhRmcqfZYfgTjbt.baNyG', 0, NOW()),
('demouser1', 2, ARRAY[2, 4, 7], '{"sound": true}', '$2b$10$ita5UtzrE2JBh.275g5i8ebBnnM99D9wZhRmcqfZYfgTjbt.baNyG', 2, NOW()),
('demouser2', 2, ARRAY[1, 3, 4], '{"sound": true}', '$2b$10$ita5UtzrE2JBh.275g5i8ebBnnM99D9wZhRmcqfZYfgTjbt.baNyG', 1, NOW()),
('demouser3', 1, ARRAY[5, 6], '{"sound": false}', '$2b$10$ita5UtzrE2JBh.275g5i8ebBnnM99D9wZhRmcqfZYfgTjbt.baNyG', 1, NOW()),
('demouser4', 1, ARRAY[1, 2, 8], '{"sound": true}', '$2b$10$ita5UtzrE2JBh.275g5i8ebBnnM99D9wZhRmcqfZYfgTjbt.baNyG', 1, NOW());

-- Insert 10 rows into suggestions table without objectives
INSERT INTO suggestions (user_id, title, description) VALUES
Expand Down
18 changes: 0 additions & 18 deletions middleware/isAdmin.ts

This file was deleted.

30 changes: 30 additions & 0 deletions middleware/isRole.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Request, Response, NextFunction } from 'express';
import { AuthenticatedRequest } from './authenticate';

export const isModerator = (request: Request, response: Response, next: NextFunction) => {
const { role } = (request as AuthenticatedRequest).tokenData;

if (!role) {
return response.status(401).json({ error: 'Access denied, \"role\" missing.' });
}

if (role !== "admin" && role !== "moderator") {
return response.status(403).json({ error: 'Access denied, Admins only.' });
}

return next();
};

export const isAdmin = (request: Request, response: Response, next: NextFunction) => {
const { role } = (request as AuthenticatedRequest).tokenData;

if (!role) {
return response.status(401).json({ error: 'Access denied, \"role\" missing.' });
}

if (role !== "admin") {
return response.status(403).json({ error: 'Access denied, Admins only.' });
}

return next();
};
2 changes: 0 additions & 2 deletions routes/category.router.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import express from 'express';
import categoryController from '../controllers/category.controller';
import isAdmin from '../middleware/isAdmin';

const categoryRouter = express.Router();

categoryRouter.get(
'/',
isAdmin,
categoryController.getAllCategories
);

Expand Down
22 changes: 12 additions & 10 deletions routes/quest.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import express from 'express';
import questController from '../controllers/quest.controller';
import { validateBody, validateParams, validateQuery } from '../middleware/validate';
import questSchema from '../validationSchemas/questSchema';
import isAdmin from '../middleware/isAdmin';
import { isAdmin, isModerator } from '../middleware/isRole';

const questRouter = express.Router();

Expand All @@ -22,25 +22,27 @@ questRouter.get(
questController.getQuest,
);

// admin routes
questRouter.post(
'/',
isAdmin,
validateBody(questSchema.create),
questController.createQuest
);
// moderator routes
questRouter.put(
'/:id',
isAdmin,
isModerator,
validateParams(questSchema.idSchema),
validateBody(questSchema.update),
questController.updateQuest
);
questRouter.delete(
'/:id',
isAdmin,
isModerator,
validateParams(questSchema.idSchema),
questController.deleteQuest
);

// admin routes
questRouter.post(
'/',
isAdmin,
validateBody(questSchema.create),
questController.createQuest
);

export default questRouter;
16 changes: 11 additions & 5 deletions routes/suggestion.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import express from 'express';
import suggestionController from '../controllers/suggestion.controller ';
import { validateBody, validateParams, validateQuery } from '../middleware/validate';
import suggestionSchema from '../validationSchemas/suggestionSchema';
import isAdmin from '../middleware/isAdmin';
import { isAdmin, isModerator } from '../middleware/isRole';

const suggestionRouter = express.Router();

Expand All @@ -17,17 +17,23 @@ suggestionRouter.get(
suggestionController.getLeaderboard
)

// admin routes
// moderator routes
suggestionRouter.get(
'/',
isAdmin,
isModerator,
validateQuery(suggestionSchema.allSuggestions),
suggestionController.getSuggestions
);
suggestionRouter.delete(
'/:suggestionId',
isModerator,
validateParams(suggestionSchema.suggestionId),
suggestionController.deleteSuggestion
);
suggestionRouter.post(
'/:suggestionId/quest',
isAdmin,
validateParams(suggestionSchema.convertSuggestionIntoQuest),
isModerator,
validateParams(suggestionSchema.suggestionId),
validateBody(suggestionSchema.convertSuggestionIntoQuestBody),
suggestionController.convertSuggestionIntoQuest
);
Expand Down
2 changes: 1 addition & 1 deletion routes/user.router.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import express from 'express';
import userController from '../controllers/user.controller';
import isAdmin from '../middleware/isAdmin';
import { isAdmin } from '../middleware/isRole';
import { validateBody, validateParams } from '../middleware/validate';
import userSchema from '../validationSchemas/userSchema';

Expand Down
8 changes: 4 additions & 4 deletions services/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export default {
try {
const passwordHash = await hashPassword(password);
const queryString = `
INSERT INTO users (username, password)
VALUES ($1, $2)
INSERT INTO users (username, password, last_login)
VALUES ($1, $2, NOW())
RETURNING *
`;

Expand Down Expand Up @@ -51,8 +51,8 @@ export default {

const queryString = `
WITH inserted_user AS (
INSERT INTO users (username, role_id)
VALUES ($1, (SELECT id FROM roles WHERE name = 'guest' LIMIT 1))
INSERT INTO users (username, role_id, last_login)
VALUES ($1, (SELECT id FROM roles WHERE name = 'guest' LIMIT 1), NOW())
RETURNING *
)
SELECT inserted_user.*, roles.name AS role
Expand Down
27 changes: 18 additions & 9 deletions services/suggestion.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,26 @@ export default {
title: string;
description: string;
username: string;
isLastPage?: boolean;
}[]> => {
const query = `
SELECT s.id,
s.title,
s.description,
u.username
FROM suggestions s
JOIN users u ON s.user_id = u.id
WHERE deleted_by IS NULL
ORDER BY id
LIMIT 20 OFFSET (($1 - 1) * 20);
WITH paginated_suggestions AS (
SELECT s.id,
s.title,
s.description,
u.username
FROM suggestions s
JOIN users u ON s.user_id = u.id
WHERE s.deleted_by IS NULL
ORDER BY s.id
LIMIT 20 OFFSET (($1 - 1) * 20)
)
SELECT *,
CASE
WHEN COUNT(*) OVER () < 20 THEN TRUE
ELSE FALSE
END AS isLastPage
FROM paginated_suggestions;
`;
const values = [offset]

Expand Down
2 changes: 1 addition & 1 deletion validationSchemas/suggestionSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default {
.max(512)
}),

convertSuggestionIntoQuest: Joi.object({
suggestionId: Joi.object({
suggestionId: Joi.number()
.min(1)
.required()
Expand Down

0 comments on commit 5e1aca6

Please sign in to comment.