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

Prep exercise week3 #29

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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
28 changes: 28 additions & 0 deletions assignments/hackyourtemperature/__tests__/app.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import app from '../server.js';
import supertest from 'supertest';

const request = supertest(app);

describe('POST /weather', () => {
it('should return 400 if cityName is missing', async () => {
const response = await request.post('/weather').send({});
expect(response.status).toBe(400);
expect(response.body.error).toBe('cityName is required');
});

it('should return 404 if cityName is invalid', async () => {
const response = await request
.post('/weather')
.send({ cityName: 'invalidcity' });
expect(response.status).toBe(404);
expect(response.body.error).toBe('city not found');
});

it('should return temperature if cityName is valid', async () => {
const response = await request
.post('/weather')
.send({ cityName: 'London' });
expect(response.status).toBe(200);
expect(response.body.weatherText).toContain('The temperature in London is');
});
});
12 changes: 12 additions & 0 deletions assignments/hackyourtemperature/babel.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
node: 'current',
},
},
],
],
};
7 changes: 7 additions & 0 deletions assignments/hackyourtemperature/jest.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
transform: {
'^.+\\.js$': 'babel-jest',
},
transformIgnorePatterns: ['node_modules/(?!node-fetch)'],
testEnvironment: 'node',
};
90 changes: 90 additions & 0 deletions assignments/hackyourtemperature/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
{
"name": "hackyourtemperature",
"version": "1.0.0",
"description": "",
"main": "server.js",
"dependencies": {
"accepts": "^1.3.8",
"array-flatten": "^1.1.1",
"body-parser": "^1.20.3",
"bytes": "^3.1.2",
"call-bind": "^1.0.7",
"content-disposition": "^0.5.4",
"content-type": "^1.0.5",
"cookie": "^0.6.0",
"cookie-signature": "^1.0.6",
"data-uri-to-buffer": "^4.0.1",
"debug": "^2.6.9",
"define-data-property": "^1.1.4",
"depd": "^2.0.0",
"destroy": "^1.2.0",
"ee-first": "^1.1.1",
"encodeurl": "^2.0.0",
"epxress": "^0.0.1-security",
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"escape-html": "^1.0.3",
"etag": "^1.8.1",
"express": "^4.21.0",
"express-handlers": "^1.0.0",
"fetch": "^1.1.0",
"fetch-blob": "^3.2.0",
"finalhandler": "^1.3.1",
"formdata-polyfill": "^4.0.10",
"forwarded": "^0.2.0",
"fresh": "^0.5.2",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.2",
"has-proto": "^1.0.3",
"has-symbols": "^1.0.3",
"hasown": "^2.0.2",
"http-errors": "^2.0.0",
"iconv-lite": "^0.4.24",
"inherits": "^2.0.4",
"ipaddr.js": "^1.9.1",
"media-typer": "^0.3.0",
"merge-descriptors": "^1.0.3",
"methods": "^1.1.2",
"mime": "^1.6.0",
"mime-db": "^1.52.0",
"mime-types": "^2.1.35",
"ms": "^2.0.0",
"negotiator": "^0.6.3",
"node-domexception": "^1.0.0",
"node-fetch": "^3.3.2",
"object-inspect": "^1.13.2",
"on-finished": "^2.4.1",
"parseurl": "^1.3.3",
"path-to-regexp": "^0.1.10",
"proxy-addr": "^2.0.7",
"qs": "^6.13.0",
"range-parser": "^1.2.1",
"raw-body": "^2.5.2",
"safe-buffer": "^5.2.1",
"safer-buffer": "^2.1.2",
"send": "^0.19.0",
"serve-static": "^1.16.2",
"set-function-length": "^1.2.2",
"setprototypeof": "^1.2.0",
"side-channel": "^1.0.6",
"statuses": "^2.0.1",
"toidentifier": "^1.0.1",
"type-is": "^1.6.18",
"undici-types": "^6.19.8",
"unpipe": "^1.0.0",
"utils-merge": "^1.0.1",
"vary": "^1.1.2",
"web-streams-polyfill": "^3.3.3"
},
"scripts": {
"test": "jest",
"start": "node server.js"

},
"keywords": [],
"author": "",
"license": "ISC",
"type": "module"
}
37 changes: 37 additions & 0 deletions assignments/hackyourtemperature/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import fetch from 'node-fetch';
import { API_KEY } from './source/key.js';
import express from 'express';

const app = express();
app.use(express.json());

app.get('/', (req, res) => {
res.send('hello from backend to frontend');
});

app.post('/weather', async (req, res) => {
const cityName = req.body.cityName;
const url = `http://api.openweathermap.org/data/2.5/weather?q=${cityName}&appid=${API_KEY}&units=metric`;
if (!cityName) {
return res.status(400).json({ error: 'cityName is required' });
}
try {
const response = await fetch(url);
const data = await response.json();

if (data.cod === '404') {
return res.status(404).json({ error: 'city not found' });
}
const temperature = data.main.temp;

res.status(200).json({
weatherText: `The temperature in ${cityName} is ${temperature}°C`,
});
} catch (error) {
res.status(500).json({ error: 'server error' });
}
});

app.listen(3000, () => {
console.log('Server is running on port 3000');
});
1 change: 1 addition & 0 deletions assignments/hackyourtemperature/source/key.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const AIP_KEY = '5b880f4ee3e43bab62bd1d03019f09c2';
7 changes: 3 additions & 4 deletions week3/prep-exercise/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "build-with-students",
"version": "1.0.0",
"description": "",
"description": "In this exercise, you will build a secure authentication and authorization system using Node.js and Express.js with four main endpoints: `register`, `login`, `getProfile`, and `logout`. The system will utilize JWT (JSON Web Tokens) for managing user sessions.",
"main": "app.js",
"types": "module",
"scripts": {
Expand All @@ -15,13 +15,12 @@
"license": "ISC",
"dependencies": {
"bcrypt": "^5.1.1",
"express": "^4.18.2",
"lokijs": "^1.5.12",
"express": "^4.21.0",
"jsonwebtoken": "^9.0.2",
"lokijs": "^1.5.12",
"uuid": "^9.0.1"
},
"devDependencies": {
"nodemon": "^3.1.0"
}
}

13 changes: 9 additions & 4 deletions week3/prep-exercise/server/app.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import express from 'express';
// TODO Use below import statement for importing middlewares from users.js for your routes
// TODO import { ....... } from "./users.js";

import { register, login, getProfile, logout } from './users.js';
let app = express();

app.use(express.json());
// TODO: Create routes here, e.g. app.post("/register", .......)

app.post('/auth/register', register);

app.post('/auth/login', login);

app.get('/auth/profile', getProfile);

app.post('/auth/logout', logout);

// Serve the front-end application from the `client` folder
app.use(express.static('client'));
Expand Down
66 changes: 62 additions & 4 deletions week3/prep-exercise/server/users.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,70 @@
import newDatabase from './database.js'
import newDatabase from './database.js';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';

// Change this boolean to true if you wish to keep your
// users between restart of your application
const isPersistent = false
const database = newDatabase({isPersistent})

const isPersistent = true;
const database = newDatabase({ isPersistent });
const secretKey = 'super-secret-key';
// Create middlewares required for routes defined in app.js

//register
// export const register = async (req, res) => {};
export const register = async (req, res) => {
const { username, password } = req.body;
if (!username || !password) {
return res.status(400).send('Username and password are required');
}
try {
const hashedPassword = await bcrypt.hash(password, 10);
const newUser = database.create({ username, password: hashedPassword });
res.status(201).json({ id: newUser.id, username: newUser.username });
} catch (error) {
res.status(500).send('An error occurred while registering the user');
}
};

//login
export const login = async (req, res) => {
const { username, password } = req.body;
const users = database.getAll();
const user = users.find((user) => user.username === username);

if (!user) {
return res.status(404).send('User not found');
}
const isPasswordCorrect = await bcrypt.compare(password, user.password);
if (!isPasswordCorrect) {
return res.status(401).json({ message: 'Invalid password' });
}
const token = jwt.sign({ id: user.id }, secretKey, { expiresIn: '1h' });
return res.status(200).json({ token });
};

//getProfile

export const getProfile = async (req, res) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ message: 'Unauthorized' });
}
try {
const decoded = jwt.verify(token, secretKey);
const user = database.getById(decoded.id);
if (!user) {
res.status(404).json({ message: 'User not found' });
}
res.status(200).json({ id: user.id, username: user.username });
} catch (error) {
res.status(401).json({ message: 'Unauthorized' });
}
};

// You can also create helper functions in this file to help you implement logic
// inside middlewares
//logout
export const logout = async (req, res) => {
res.status(204).send('Logged out successfully');
};