Skip to content

Commit 300e36f

Browse files
Merge pull request #3 from kennie-larkson/authentication
Authentication
2 parents 7ee11d8 + 4327034 commit 300e36f

18 files changed

+320
-68
lines changed

src/app.ts

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import cors from "cors";
44
import errorMiddleware from "./middleware/error.middleware";
55
import IController from "./interfaces/controller.interface";
66
import AppDataSource from "./data-source";
7-
import { userController } from "./entity/users/user.controller";
87

98
class App {
109
public app: express.Application;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import express, { Request, Response, NextFunction } from "express";
2+
import AuthenticationService from "./authentication.service";
3+
import { ICreateUser } from "./../entity/users/user.interface";
4+
import { validateUserForm } from "./../middleware/validation.middleware";
5+
6+
export default class AuthController {
7+
public path = "/auth";
8+
public router = express.Router();
9+
private authService = new AuthenticationService();
10+
11+
constructor() {
12+
this.initializeRoutes();
13+
}
14+
15+
private initializeRoutes() {
16+
this.router.post(
17+
`${this.path}/register`,
18+
validateUserForm,
19+
this.registration
20+
);
21+
}
22+
23+
private registration = async (
24+
request: express.Request,
25+
response: express.Response,
26+
next: express.NextFunction
27+
) => {
28+
const userData: ICreateUser = request.body;
29+
try {
30+
const { cookie, user } = await this.authService.register(userData);
31+
console.log({ user, cookie });
32+
response.setHeader("Set-Cookie", [cookie]);
33+
response.send(user);
34+
} catch (error) {
35+
next(error);
36+
}
37+
};
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import bcrypt from "bcrypt";
2+
import { ICreateUser } from "entity/users/user.interface";
3+
import jwt from "jsonwebtoken";
4+
import AppDataSource from "./../data-source";
5+
import User from "./../entity/users/user.entity";
6+
import ITokenData from "./../interfaces/tokenData.interface";
7+
import IDataStoredInToken from "./../interfaces/dataStoredInToken.interface";
8+
9+
export default class AuthenticationService {
10+
public async register(userData: ICreateUser) {
11+
if (
12+
await AppDataSource.manager
13+
.getRepository(User)
14+
.findOneBy({ email: userData.email })
15+
) {
16+
throw new Error(
17+
"Sorry there is a user with that email already. Please, try another."
18+
);
19+
}
20+
21+
const hashedPassword = await bcrypt.hash(userData.password, 10);
22+
23+
const user = AppDataSource.manager.getRepository(User).create({
24+
...userData,
25+
password: hashedPassword,
26+
});
27+
await AppDataSource.manager.getRepository(User).save(user);
28+
delete user.password;
29+
const tokenData = this.createToken(user);
30+
const cookie = this.createCookie(tokenData);
31+
32+
return { user, cookie };
33+
}
34+
35+
public createToken(user: User): ITokenData {
36+
const expiresIn = 60 * 60;
37+
const secret = process.env.JWT_SECRET;
38+
const dataStoredInToken: IDataStoredInToken = { id: user.id };
39+
40+
return { expiresIn, token: jwt.sign(dataStoredInToken, secret, {}) };
41+
}
42+
43+
public createCookie(tokenData: ITokenData) {
44+
return `Authorization=${tokenData.token}; HttpOnly; Max-Age=${tokenData.expiresIn}`;
45+
}
46+
}

src/data-source.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import "reflect-metadata";
1+
import "dotenv/config";
2+
//import "reflect-metadata";
23
import { DataSource } from "typeorm";
34
import User from "./entity/users/user.entity";
45
import Post from "./entity/posts/post.entity";

src/entity/posts/post.controller.ts

+68-7
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,94 @@
11
import express from "express";
22
import IPost from "./post.interface";
3+
import { validatePostForm } from "./../../middleware/validation.middleware";
34

45
import AppDataSource from "./../../data-source";
56
import { NextFunction, Request, Response } from "express";
67
import Post from "./post.entity";
78

89
export default class PostController {
9-
public path = "/post";
10+
public path = "/posts";
1011
public router = express.Router();
1112

12-
async all(request: Request, response: Response, next: NextFunction) {
13-
return AppDataSource.manager.find(Post);
13+
constructor() {
14+
this.initializeRoutes();
1415
}
1516

16-
async one(request: Request, response: Response, next: NextFunction) {
17+
private initializeRoutes() {
18+
this.router.get(this.path, this.getAllPosts);
19+
this.router.get(`${this.path}/:id`, this.getPostById);
20+
this.router.post(`${this.path}`, validatePostForm, this.createPost);
21+
this.router.delete(`${this.path}/:id`, this.removePost);
22+
this.router.patch(`${this.path}/:id`, this.updatePost);
23+
}
24+
25+
public async getAllPosts(
26+
request: Request,
27+
response: Response,
28+
next: NextFunction
29+
) {
30+
try {
31+
const posts = await AppDataSource.manager
32+
.getRepository(Post)
33+
.find({ relations: { author: true } });
34+
return response.json(posts);
35+
} catch (error) {
36+
return response.json(error);
37+
}
38+
}
39+
40+
async getPostById(request: Request, response: Response, next: NextFunction) {
1741
const { id } = request.params;
1842
return AppDataSource.manager.findOneBy(Post, { id: Number(id) });
1943
}
2044

21-
async save(request: Request, response: Response, next: NextFunction) {
45+
async createPost(request: Request, response: Response, next: NextFunction) {
2246
const post: IPost = request.body;
23-
return AppDataSource.manager.save(post);
47+
//const post = request.body;
48+
try {
49+
const createdPost = AppDataSource.manager
50+
.getRepository(Post)
51+
.create(post);
52+
const result = await AppDataSource.manager
53+
.getRepository(Post)
54+
.save(createdPost);
55+
return response.json(result);
56+
} catch (error) {
57+
return response.json(error);
58+
}
2459
}
2560

26-
async remove(request: Request, response: Response, next: NextFunction) {
61+
async removePost(request: Request, response: Response, next: NextFunction) {
2762
const { id } = request.params;
2863
let postToRemove = await AppDataSource.manager.findOneBy(Post, {
2964
id: Number(id),
3065
});
3166
await AppDataSource.manager.remove(postToRemove);
3267
}
68+
69+
async updatePost(request: Request, response: Response, next: NextFunction) {
70+
const { id } = request.params;
71+
const update = request.body;
72+
console.log(update);
73+
74+
try {
75+
const postToUpdate = await AppDataSource.getRepository(Post).findOneBy({
76+
id: Number(id),
77+
});
78+
if (!postToUpdate) {
79+
return response.json({ message: `Unable to find post with id ${id}` });
80+
}
81+
AppDataSource.getRepository(Post).merge(postToUpdate, update);
82+
const updatedPost = await AppDataSource.getRepository(Post).save(
83+
postToUpdate
84+
);
85+
86+
return response.json({
87+
message: `Successfully updated data for post with id: ${id}.`,
88+
updatedPost,
89+
});
90+
} catch (error) {
91+
return response.json(error);
92+
}
93+
}
3394
}

src/entity/posts/post.entity.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
1-
import { Entity, Column, PrimaryGeneratedColumn } from "typeorm";
1+
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from "typeorm";
22
import "reflect-metadata";
3+
import User from "../users/user.entity";
34

45
@Entity()
56
class Post {
67
@PrimaryGeneratedColumn()
78
public id?: number;
89

910
@Column()
10-
public author?: string;
11+
public content: string;
1112

1213
@Column()
13-
public content?: string;
14+
public title: string;
1415

15-
@Column()
16-
public title?: string;
16+
@ManyToOne(() => User, (author) => author.posts)
17+
public author: User;
1718
}
1819

1920
export default Post;

src/entity/posts/post.interface.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import User from "entity/users/user.entity";
2+
13
interface IPost {
2-
id: number;
3-
author: string;
44
content: string;
55
title: string;
6+
author: Partial<User>;
67
}
78

89
export default IPost;

src/entity/topic/topic.controller.ts

+91-16
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,103 @@
11
import AppDataSource from "src/data-source";
2-
import { NextFunction, Request, Response } from "express";
2+
import express, { NextFunction, Request, Response } from "express";
33
import Topic from "./topic.entity";
4-
import ITopic from "./topic.interface";
4+
import { ICreateTopic } from "./topic.interface";
55

66
export default class TopicController {
7-
async all(request: Request, response: Response, next: NextFunction) {
8-
return AppDataSource.manager.find(Topic);
7+
public path = "/topics";
8+
public router = express.Router();
9+
10+
constructor() {
11+
this.initializeRoutes();
12+
}
13+
14+
private initializeRoutes() {
15+
this.router.get(this.path, this.getAllTopics);
16+
this.router.get(`${this.path}/:id`, this.getTopicById);
17+
this.router.post(`${this.path}`, this.createTopic);
18+
this.router.delete(`${this.path}/:id`, this.removeTopic);
19+
this.router.patch(`${this.path}/:id`, this.updateTopic);
920
}
1021

11-
async one(request: Request, response: Response, next: NextFunction) {
12-
return AppDataSource.manager.findOneBy(Topic, {
13-
id: Number(request.params.id),
14-
});
22+
async getAllTopics(request: Request, response: Response, next: NextFunction) {
23+
try {
24+
const topics = await AppDataSource.manager.getRepository(Topic).find();
25+
return response.json(topics);
26+
} catch (error) {
27+
return response.json(error);
28+
}
1529
}
1630

17-
async save(request: Request, response: Response, next: NextFunction) {
18-
const topic: ITopic = request.body;
19-
return AppDataSource.manager.save(topic);
31+
async getTopicById(request: Request, response: Response, next: NextFunction) {
32+
const { id } = request.params;
33+
try {
34+
const topic = AppDataSource.manager.getRepository(Topic).findOneBy({
35+
id: Number(id),
36+
});
37+
if (!topic) {
38+
return response.json({ message: `Unable to find user with id ${id}` });
39+
}
40+
return response.json(topic);
41+
} catch (error) {
42+
return response.json(error);
43+
}
44+
}
45+
46+
async createTopic(request: Request, response: Response, next: NextFunction) {
47+
const topic: ICreateTopic = request.body;
48+
49+
try {
50+
const createdTopic = AppDataSource.getRepository(Topic).create(topic);
51+
const results = await AppDataSource.getRepository(Topic).save(
52+
createdTopic
53+
);
54+
return response.send(results);
55+
} catch (error) {
56+
return response.json(error);
57+
}
2058
}
2159

22-
async remove(request: Request, response: Response, next: NextFunction) {
23-
let topicToRemove = await AppDataSource.manager.findOneBy(Topic, {
24-
id: Number(request.params.id),
25-
});
26-
await AppDataSource.manager.remove(topicToRemove);
60+
async removeTopic(request: Request, response: Response, next: NextFunction) {
61+
const { id } = request.params;
62+
63+
try {
64+
const removedUser = await AppDataSource.getRepository(Topic).delete(id);
65+
if (removedUser.affected === 0) {
66+
return response.json(
67+
`Unable to find topic with id: ${id} for deletion.`
68+
);
69+
}
70+
return response.json({
71+
message: `Successfully deleted records of topic with id: ${id}.`,
72+
});
73+
} catch (error) {
74+
return response.json(error);
75+
}
76+
}
77+
78+
async updateTopic(request: Request, response: Response, next: NextFunction) {
79+
const { id } = request.params;
80+
const update: Partial<Topic> = request.body;
81+
console.log(update);
82+
83+
try {
84+
const topicToUpdate = await AppDataSource.getRepository(Topic).findOneBy({
85+
id: Number(id),
86+
});
87+
if (!topicToUpdate) {
88+
return response.json({ message: `Unable to find topic with id ${id}` });
89+
}
90+
AppDataSource.getRepository(Topic).merge(topicToUpdate, update);
91+
const updatedUser = await AppDataSource.getRepository(Topic).save(
92+
topicToUpdate
93+
);
94+
95+
return response.json({
96+
message: `Successfully updated data for topic with id: ${id}.`,
97+
updatedUser,
98+
});
99+
} catch (error) {
100+
return response.json(error);
101+
}
27102
}
28103
}

src/entity/topic/topic.entity.ts

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { Entity, Column, PrimaryGeneratedColumn } from "typeorm";
2-
//import "reflect-metadata";
32

43
@Entity()
54
class Topic {

src/entity/topic/topic.interface.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
interface ITopic {
1+
export interface ICreateTopic {
22
topic: string;
33
domain: string;
44
}
5-
6-
export default ITopic;

0 commit comments

Comments
 (0)