Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

more user queries, new role attribute, jwt returns role #13

Merged
merged 5 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions controllers/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,36 +15,37 @@ export default {

const newUser = await authService.register(username, password);
const token = await authService.createTokenSession(newUser, password);

if (token === null) {
throw new Error('Error creating token session');
}

return response.status(201).json({ token });
} catch (error) {
console.error('Error during register:', error);
return response.status(500).send('An error occurred while registering');
return response.status(500).send('An unexpected error occurred while registering');
}
},

login: async (req: Request, res: Response) => {
login: async (request: Request, response: Response) => {
try {
const { username, password } = req.body;
const { username, password } = request.body;

const user = await userService.getUserByUsername(username);

if (user === null) {
return res.status(400).json({ error: 'User not found' });
return response.status(400).json({ error: 'User not found' });
}

const token = await authService.createTokenSession(user, password);
if (token === null) {
return res.status(400).json({ error: 'Invalid password' });
return response.status(400).json({ error: 'Invalid password' });
}

return res.status(200).json({ token });
return response.status(200).json({ token });
} catch (error) {
console.error('Error during login:', error);
return res.status(500).send('An error occurred while logging in');
return response.status(500).send('An unexpected error occurred while logging in');
}
}
}
38 changes: 34 additions & 4 deletions controllers/user.controller.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Request, Response } from "express";
import { executeQuery } from "../database/connection";
import userService from "../services/user.service";
import { AuthenticatedRequest } from "../middleware/authenticate";

export default {
getUsers: async (request: Request, response: Response) => {
try {
const users = await executeQuery('SELECT id, username, completed_quests, metadata FROM users');

console.log(users)
const users = await userService.getAllUsers();

if (users.length === 0) {
response.status(404).send('No users found');
Expand All @@ -18,5 +18,35 @@ export default {
console.error(error);
response.status(500).send('An error occurred while fetching users');
}
}
},

getUserByUsername: async (request: Request, response: Response) => {
try {
const { username } = request.params;
const user = await userService.getUserByUsername(username);

if (!user) {
response.status(404).send('User not found');
return;
}

delete(user.password);
response.send(user);
} catch (error) {
console.error(error);
response.status(500).send('An error occurred while fetching user');
}
},

getCompletedQuests: async (request: Request, response: Response) => {
try {
const { userId } = (request as AuthenticatedRequest).tokenData;
const completedQuests = await userService.getCompletedQuests(parseInt(userId));

response.send(completedQuests);
} catch (error) {
console.error(error);
response.status(500).send('An error occurred while fetching completed quests');
}
},
}
6 changes: 5 additions & 1 deletion database/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ if (DATABASE.CONNECTION_STRING) {
* @param expectSingleRow If true, will return the first row of the result; otherwise, will return all rows.
* @returns A Promise that resolves to the result of the query.
*/
export const executeQuery = async (queryString: string, queryValues: any[] = [], expectSingleRow: boolean = false): Promise<any[] | any> => {
export const executeQuery = async (
queryString: string,
queryValues: any[] = [],
expectSingleRow: boolean = false
) => {
try {
const result = await pool.query(queryString, queryValues);

Expand Down
27 changes: 14 additions & 13 deletions database/init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ CREATE TABLE categories (
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
completed_quests INTEGER[] NOT NULL DEFAULT ARRAY[]::INTEGER[],
metadata JSONB NOT NULL DEFAULT '{}',
role VARCHAR(10) NOT NULL DEFAULT 'user',
password VARCHAR(255),
metadata JSONB NOT NULL DEFAULT '{}',
completed_quests INTEGER[] NOT NULL DEFAULT ARRAY[]::INTEGER[],
oauth_provider VARCHAR(50), -- For OAuth integration (e.g., Google, Facebook)
oauth_id VARCHAR(255) -- ID from the OAuth provider
);
Expand Down Expand Up @@ -46,17 +47,17 @@ INSERT INTO categories (name) VALUES
('Trade');

-- Insert 10 rows into users table
INSERT INTO users (username, completed_quests, metadata, password, oauth_provider, oauth_id) VALUES
('zcog', ARRAY[1, 3, 6, 8], '{"sound": false}', '$2b$10$XEzscKa8BkSW5EKw5dLkFOlLa6IYRkoykOG2LlYImtIYDG4.6tfu6', NULL, NULL),
('littefoot', ARRAY[2, 4, 7], '{"sound": true}', '$2b$10$XEzscKa8BkSW5EKw5dLkFOlLa6IYRkoykOG2LlYImtIYDG4.6tfu6.', 'google', 'google-id-123'),
('Cattasaurus', ARRAY[5, 6], '{"sound": false}', '$2b$10$XEzscKa8BkSW5EKw5dLkFOlLa6IYRkoykOG2LlYImtIYDG4.6tfu6', NULL, NULL),
('HandsomeJack', ARRAY[1, 2, 8], '{"sound": true}', '$2b$10$XEzscKa8BkSW5EKw5dLkFOlLa6IYRkoykOG2LlYImtIYDG4.6tfu6', NULL, NULL),
('luna', ARRAY[3, 5, 7], '{"sound": false}', '$2b$10$XEzscKa8BkSW5EKw5dLkFOlLa6IYRkoykOG2LlYImtIYDG4.6tfu6', 'facebook', 'fb-id-456'),
('SugarCubeBlood', ARRAY[4, 6], '{"sound": true}', '$2b$10$XEzscKa8BkSW5EKw5dLkFOlLa6IYRkoykOG2LlYImtIYDG4.6tfu6', NULL, NULL),
('CaptainSparklez', ARRAY[7, 8], '{"sound": false}', '$2b$10$XEzscKa8BkSW5EKw5dLkFOlLa6IYRkoykOG2LlYImtIYDG4.6tfu6', 'twitter', 'tw-id-789'),
('zezima', ARRAY[1, 3, 4], '{"sound": true}', '$2b$10$XEzscKa8BkSW5EKw5dLkFOlLa6IYRkoykOG2LlYImtIYDG4.6tfu6', NULL, NULL),
('pageinabook', ARRAY[2, 5, 6], '{"sound": false}', '$2b$10$XEzscKa8BkSW5EKw5dLkFOlLa6IYRkoykOG2LlYImtIYDG4.6tfu6', NULL, NULL),
('notacoconut', ARRAY[7, 9], '{"sound": true}', '$2b$10$XEzscKa8BkSW5EKw5dLkFOlLa6IYRkoykOG2LlYImtIYDG4.6tfu6', 'google', 'google-id-234');
INSERT INTO users (username, role, completed_quests, metadata, password, oauth_provider, oauth_id) VALUES
('zcog', 'admin', ARRAY[1, 3, 6, 8], '{"sound": false}', '$2b$10$2iWlcbpf1MYROBYodo/4yufNXOpGzwiQenhLjyK3I/D4Y.q3V4aEu', NULL, NULL),
('littefoot', 'user', ARRAY[2, 4, 7], '{"sound": true}', '$2b$10$2iWlcbpf1MYROBYodo/4yufNXOpGzwiQenhLjyK3I/D4Y.q3V4aEu.', 'google', 'google-id-123'),
('Cattasaurus', 'user', ARRAY[5, 6], '{"sound": false}', '$2b$10$2iWlcbpf1MYROBYodo/4yufNXOpGzwiQenhLjyK3I/D4Y.q3V4aEu', NULL, NULL),
('HandsomeJack', 'user', ARRAY[1, 2, 8], '{"sound": true}', '$2b$10$2iWlcbpf1MYROBYodo/4yufNXOpGzwiQenhLjyK3I/D4Y.q3V4aEu', NULL, NULL),
('luna', 'user', ARRAY[3, 5, 7], '{"sound": false}', '$2b$10$2iWlcbpf1MYROBYodo/4yufNXOpGzwiQenhLjyK3I/D4Y.q3V4aEu', 'facebook', 'fb-id-456'),
('SugarCubeBlood', 'user', ARRAY[4, 6], '{"sound": true}', '$2b$10$2iWlcbpf1MYROBYodo/4yufNXOpGzwiQenhLjyK3I/D4Y.q3V4aEu', NULL, NULL),
('CaptainSparklez', 'user', ARRAY[7, 8], '{"sound": false}', '$2b$10$2iWlcbpf1MYROBYodo/4yufNXOpGzwiQenhLjyK3I/D4Y.q3V4aEu', 'twitter', 'tw-id-789'),
('zezima', 'admin', ARRAY[1, 3, 4], '{"sound": true}', '$2b$10$2iWlcbpf1MYROBYodo/4yufNXOpGzwiQenhLjyK3I/D4Y.q3V4aEu', NULL, NULL),
('pageinabook', 'user', ARRAY[2, 5, 6], '{"sound": false}', '$2b$10$2iWlcbpf1MYROBYodo/4yufNXOpGzwiQenhLjyK3I/D4Y.q3V4aEu', NULL, NULL),
('notacoconut', 'admin', ARRAY[7, 9], '{"sound": true}', '$2b$10$2iWlcbpf1MYROBYodo/4yufNXOpGzwiQenhLjyK3I/D4Y.q3V4aEu', 'google', 'google-id-234');

-- Insert 10 rows into suggestions table
INSERT INTO suggestions (user_id, title, description) VALUES
Expand Down
13 changes: 4 additions & 9 deletions middleware/authenticate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@ import jwt from 'jsonwebtoken';
import { JWT_SECRET } from '../config';

interface DecodedJWT {
id: string;
userId: string;
username: string;
role: string;
iat: number;
}

export interface AuthenticatedRequest extends Request {
tokenData: {
userId: string;
username: string;
};
tokenData: DecodedJWT;
}

const authenticate = (request: Request, response: Response, next: NextFunction) => {
Expand All @@ -29,10 +27,7 @@ const authenticate = (request: Request, response: Response, next: NextFunction)
return response.status(401).json({ message: 'Unauthorized' });
}

(request as AuthenticatedRequest).tokenData = {
userId: decoded.id,
username: decoded.username,
};
(request as AuthenticatedRequest).tokenData = { ...decoded };

next();
} catch (error) {
Expand Down
18 changes: 18 additions & 0 deletions middleware/isAdmin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Request, Response, NextFunction } from 'express';
import { AuthenticatedRequest } from './authenticate';

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();
};

export default isAdmin;
Loading
Loading