Skip to content
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
7 changes: 7 additions & 0 deletions semana18/Cookenu/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules
build

.env

.vscode
requests.rest
2,860 changes: 2,860 additions & 0 deletions semana18/Cookenu/package-lock.json

Large diffs are not rendered by default.

35 changes: 35 additions & 0 deletions semana18/Cookenu/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "to-do-list",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "tsc && node --inspect ./build/index.js",
"dev-start": "ts-node-dev ./src/index.ts",
"migrations": "tsc && node ./build/migrations/index.js",
"test": "ts-node-dev ./src/services/authenticator.ts"
},
"author": "Labenu",
"license": "ISC",
"dependencies": {
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"jsonwebtoken": "^8.5.1",
"knex": "^0.21.5",
"mysql": "^2.18.1",
"uuid": "^8.3.2"
},
"devDependencies": {
"@types/bcryptjs": "^2.4.2",
"@types/cors": "^2.8.8",
"@types/express": "^4.17.8",
"@types/jsonwebtoken": "^8.5.1",
"@types/knex": "^0.16.1",
"@types/node": "^14.11.2",
"@types/uuid": "^8.3.0",
"ts-loader": "^8.1.0",
"ts-node-dev": "^1.0.0-pre.63",
"typescript": "^4.2.4"
}
}
18 changes: 18 additions & 0 deletions semana18/Cookenu/src/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import express from "express";
import cors from "cors";
import { AddressInfo } from "net";

const app = express();

app.use(express.json());
app.use(cors());

const server = app.listen(process.env.PORT || 3003, () => {
if (server) {
const address = server.address() as AddressInfo;
console.log(`Server is running in http://localhost: ${address.port}`);
} else {
console.error(`Failure upon starting server.`);
}
});
export default app;
16 changes: 16 additions & 0 deletions semana18/Cookenu/src/connection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import knex from "knex";
import dotenv from "dotenv";

dotenv.config();

export const connection = knex({
client: "mysql",
connection: {
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_SCHEMA,
port: 3306,
multipleStatements: true,
},
});
4 changes: 4 additions & 0 deletions semana18/Cookenu/src/data/TableName.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

export const userTableName = "cookenu_users";

export const recipeTableName = "cookenu_recipes";
35 changes: 35 additions & 0 deletions semana18/Cookenu/src/endpoints/recipes/createRecipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { getTokenData } from "./../../services/authenticator";
import { Request, Response } from "express";
import { connection } from "../../connection";
import { recipeTableName } from "../../data/TableName";
import { generateId } from "../../services/idGenerator";

export async function createRecipe(req: Request, res: Response): Promise<void> {
try {
const token: string = req.headers.authorization!;
const { title, description } = req.body;

const tokenData = getTokenData(token);

const id: string = generateId();

const createdAt = new Date();

await connection(recipeTableName).insert({
id,
title,
description,
created_at: createdAt,
author_id: tokenData!.id,
});

res.send("You just created your recipe!");
} catch (error) {
console.log(error.message);
if (res.statusCode === 200) {
res.status(500).send("Internal server error");
} else {
res.send(error.message);
}
}
}
23 changes: 23 additions & 0 deletions semana18/Cookenu/src/endpoints/recipes/getRecipeById.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Request, Response } from "express";
import { connection } from "../../connection";
import { recipeTableName } from "../../data/TableName";

export async function getRecipeById(
req: Request,
res: Response
): Promise<void> {
try {
const [recipe] = await connection(recipeTableName).where({
id: req.params.id,
});

res.send({ recipe });
} catch (error) {
console.log(error.message);
if (res.statusCode === 200) {
res.status(500).send("Internal server error.");
} else {
res.send(error.message);
}
}
}
25 changes: 25 additions & 0 deletions semana18/Cookenu/src/endpoints/users/getProfile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Request, Response } from "express";
import { connection } from "../../connection";
import { userTableName } from "../../data/TableName";
import { getTokenData } from "../../services/authenticator";

export async function getProfile(req: Request, res: Response) {
try {
const token: string = req.headers.authorization!;

const tokenData = getTokenData(token);

const [user] = await connection(userTableName).where({ id: tokenData?.id });

const { id, email, name } = user;

res.send({ id, name, email });
} catch (error) {
console.log(error.message);
if (res.statusCode === 200) {
res.status(500).send("Internal server error.");
} else {
res.send(error.message);
}
}
}
25 changes: 25 additions & 0 deletions semana18/Cookenu/src/endpoints/users/getUserById.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Request, Response } from "express";
import { connection } from "../../connection";
import { userTableName } from "../../data/TableName";
import { getTokenData } from "../../services/authenticator";

export default async function getUserById(req: Request, res: Response) {
try {
const token: string = req.headers.authorization!;

getTokenData(token);

const [user] = await connection(userTableName).where({ id: req.params.id });

const { id, name, email } = user;

res.send({ id, name, email });
} catch (error) {
console.log(error.message);
if (res.statusCode === 200) {
res.status(500).send("Internal server error.");
} else {
res.send(error.message);
}
}
}
28 changes: 28 additions & 0 deletions semana18/Cookenu/src/endpoints/users/login.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { generateToken } from "./../../services/authenticator";
import { Request, Response } from "express";
import { connection } from "../../connection";
import { userTableName } from "../../data/TableName";
import { compareHash } from "../../services/hashManager";

export async function login(req: Request, res: Response): Promise<void> {
try {
const { email, password } = req.body;
const [user] = await connection(userTableName).where({ email });
const passwordIsCorrect: boolean = compareHash(password, user.password);

if (!passwordIsCorrect) {
res.statusCode = 401;
throw new Error("Invalid credentials");
}

const token = generateToken({ id: user.id });
res.send(token);
} catch (error) {
console.log(error.message);
if (res.statusCode === 200) {
res.status(500).send("Internal server error.");
} else {
res.send(error.message);
}
}
}
27 changes: 27 additions & 0 deletions semana18/Cookenu/src/endpoints/users/signup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { generateToken } from "./../../services/authenticator";
import { Request, Response } from "express";
import { connection } from "../../connection";
import { userTableName } from "../../data/TableName";
import { generateHash } from "../../services/hashManager";
import { generateId } from "../../services/idGenerator";

export async function signup(req: Request, res: Response): Promise<void> {
try {
const { name, email, password } = req.body;
const id: string = generateId();
const cypherPassword: string = generateHash(password);
const token: string = generateToken({ id });

await connection(userTableName).insert({
id,
name,
email,
password: cypherPassword,
});

res.send({ token });
} catch (error) {
console.log(error.message);
res.status(500).send("Internal server error");
}
}
15 changes: 15 additions & 0 deletions semana18/Cookenu/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import app from "./app";
import { login } from "./endpoints/users/login";
import { signup } from "./endpoints/users/signup";
import { getProfile } from "./endpoints/users/getProfile";
import getUserById from "./endpoints/users/getUserById";
import { getRecipeById } from "./endpoints/recipes/getRecipeById";
import { createRecipe } from "./endpoints/recipes/createRecipe";

app.post("/users/signup", signup);
app.post("/users/login", login);
app.get("/users/profile", getProfile);
app.get("users/:id/profile", getUserById);

app.post("recipe", createRecipe);
app.get("/recipe/:id", getRecipeById);
26 changes: 26 additions & 0 deletions semana18/Cookenu/src/migrations/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { userTableName, recipeTableName } from "./../data/TableName";
import { connection } from "../connection";

connection
.raw(
`
CREATE TABLE IF NOT EXISTS ${userTableName}(
id VARCHAR(255) PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL
);

CREATE TABLE IF NOT EXISTS ${recipeTableName}(
id VARCHAR(255) PRIMARY KEY,
title VARCHAR(255) NOT NULL,
description VARCHAR(15555) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
author_id VARCHAR(255),
FOREIGN KEY (author_id) REFERENCES ${userTableName} (id)
);
`
)
.then(() => console.log("MySQL tables created!"))
.catch((error) => console.log(error.message))
.finally(() => connection.destroy());
19 changes: 19 additions & 0 deletions semana18/Cookenu/src/services/authenticator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { config } from "dotenv";
import { sign, verify } from "jsonwebtoken";
import { authenticationData } from "../types";

config();

const { JWT_KEY } = process.env;

export const generateToken = (payload: authenticationData): string =>
sign(payload, JWT_KEY!, { expiresIn: "1h" });

export const getTokenData = (token: string): authenticationData | null => {
try {
const { id } = verify(token, JWT_KEY!) as authenticationData;
return { id };
} catch (error) {
return null;
}
};
15 changes: 15 additions & 0 deletions semana18/Cookenu/src/services/hashManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { hashSync, compareSync, genSaltSync } from "bcryptjs";
import { config } from "dotenv";

config();

export const generateHash = (plainText: string): string => {
const rounds: number = Number(process.env.BCRYPT_COST);

const salt: string = genSaltSync(rounds);

return hashSync(plainText, salt);
};

export const compareHash = (plainText: string, cypherText: string): boolean =>
compareSync(plainText, cypherText);
5 changes: 5 additions & 0 deletions semana18/Cookenu/src/services/idGenerator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { v4 } from "uuid";

export function generateId(): string {
return v4();
}
19 changes: 19 additions & 0 deletions semana18/Cookenu/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export type authenticationData = {
id: string;
// role: string;
};

export type user = {
id: string;
name: string;
email: string;
password: string;
};

export type recipe = {
id: string;
title: string;
description: string;
createdAt: string;
authorId: string;
};
14 changes: 14 additions & 0 deletions semana18/Cookenu/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"outDir": "./build",
"rootDir": "./src",
"removeComments": true,
"strict": true,
"noImplicitAny": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
Loading