Skip to content
This repository was archived by the owner on Nov 16, 2023. It is now read-only.

Commit 7c154e0

Browse files
Merge pull request #3 from developerplace/develop
CRUD Reusable
2 parents 76527df + 388810c commit 7c154e0

8 files changed

Lines changed: 423 additions & 16 deletions

File tree

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"prepare": "husky install"
4646
},
4747
"dependencies": {
48+
"bcrypt": "5.0.1",
4849
"body-parser": "1.19.0",
4950
"cookie-parser": "1.4.5",
5051
"cors": "2.8.5",
@@ -54,6 +55,7 @@
5455
"express-session": "1.17.2",
5556
"express-socket.io-session": "^1.3.5",
5657
"folder-logger": "1.0.9",
58+
"helmet": "^4.6.0",
5759
"moment": "2.29.1",
5860
"mongoose": "6.0.12",
5961
"socket.io": "^4.3.1"
@@ -63,15 +65,18 @@
6365
"@commitlint/config-conventional": "^14.1.0",
6466
"@semantic-release/changelog": "6.0.1",
6567
"@semantic-release/git": "10.0.1",
68+
"@types/bcrypt": "5.0.0",
6669
"@types/body-parser": "1.19.1",
6770
"@types/chai": "^4.2.22",
6871
"@types/cookie-parser": "1.4.2",
6972
"@types/cors": "2.8.12",
7073
"@types/dotenv": "8.2.0",
7174
"@types/express": "^4.17.13",
7275
"@types/express-session": "1.17.4",
76+
"@types/helmet": "^4.0.0",
7377
"@types/mocha": "^9.0.0",
7478
"@types/mongoose": "5.11.97",
79+
"@types/node": "^16.11.6",
7580
"@types/socket.io": "^3.0.2",
7681
"@typescript-eslint/eslint-plugin": "^5.3.0",
7782
"@typescript-eslint/parser": "^5.3.0",

src/config/Api.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import express, { Application, NextFunction, Request, Response } from "express";
2+
import helmet from "helmet";
23
import path from "path";
34
import cors from "cors";
45
import cookieParser from "cookie-parser";
@@ -24,6 +25,7 @@ export default class Api {
2425
* @private
2526
*/
2627
private middlewares() {
28+
this.app.use(helmet());
2729
this.app.use(express.json());
2830
this.app.use(cookieParser());
2931
this.app.use(cors({ origin: true, credentials: true }));

src/controllers/rest/PublicController.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import { NextFunction, Request, Response } from "express";
22
import { iResponse } from "../../interfaces/IResponse";
3+
import UserModel from "../../models/UserModel";
4+
import UserRepository from "../../repositories/UserRepository";
5+
import { Logger } from "../../utils/Logger";
36

47
class PublicController {
58

@@ -11,6 +14,13 @@ class PublicController {
1114
*/
1215
public async indexAction(req: Request, res: Response, next: NextFunction): Promise<void> {
1316
try {
17+
const usersRepository: UserRepository = new UserRepository(UserModel);
18+
await usersRepository.createUserAccount({
19+
username: "admin",
20+
email: "admin@domain.com",
21+
avatar: "",
22+
password: "admin"
23+
});
1424
const response: iResponse = {
1525
error: false,
1626
statusCode: 200,
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import {Document} from 'mongoose';
2+
3+
export interface iUser {
4+
_id?: string,
5+
username: string,
6+
email: string,
7+
password: string,
8+
avatar?: string,
9+
roles?: string[],
10+
active?: boolean,
11+
enabled?: boolean,
12+
otpCode?: number,
13+
lastLogin?: Date,
14+
createdAt?: Date,
15+
updatedAt?: Date
16+
}

src/models/UserModel.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { Schema, model } from "mongoose";
2+
import { iUser } from "../interfaces/models/IUserModel";
3+
4+
const UserSchema = new Schema({
5+
username: {
6+
type: String,
7+
required: true,
8+
unique: true,
9+
index: true,
10+
},
11+
email: {
12+
type: String,
13+
required: true,
14+
unique: true,
15+
index: true,
16+
},
17+
password: {
18+
type: String,
19+
required: true,
20+
},
21+
avatar: {
22+
type: String,
23+
required: false,
24+
},
25+
roles: {
26+
type: Array,
27+
required: true,
28+
default: ["ROLE_USER"],
29+
},
30+
active: {
31+
type: Boolean,
32+
required: true,
33+
default: false,
34+
},
35+
enabled: {
36+
type: Boolean,
37+
required: true,
38+
default: false,
39+
},
40+
otpCode: {
41+
type: Number,
42+
required: false,
43+
},
44+
lastLogin: {
45+
type: Date,
46+
required: false,
47+
},
48+
}, {
49+
timestamps: true,
50+
});
51+
52+
export default model<iUser>("User", UserSchema);

src/repositories/BaseRepository.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { Model } from "mongoose";
2+
3+
export default class BaseRepository<T> {
4+
5+
private readonly entityInstance: Model<T>;
6+
7+
// Constructor
8+
constructor(entity: Model<T>) {
9+
this.entityInstance = entity;
10+
}
11+
12+
protected createDocument<I>(documentObject: I): Promise<Document> {
13+
return new Promise(async (resolve, reject): Promise<void> => {
14+
try {
15+
const newDocumentInstance = new this.entityInstance(documentObject);
16+
await newDocumentInstance.save();
17+
resolve(newDocumentInstance._id);
18+
} catch (e) {
19+
reject(e);
20+
}
21+
});
22+
}
23+
24+
// Método encargado de obtener todos los documentos de una entidad
25+
protected getAllDocuments() {
26+
return new Promise(async (resolve, reject) => {
27+
try {
28+
const documents = await this.entityInstance.find();
29+
resolve(documents);
30+
} catch (e) {
31+
reject(e);
32+
}
33+
});
34+
}
35+
36+
// Método encargado de obtener un documento en base a parámetros
37+
protected getOneDocumentByParameters(parameters: object): Promise<object> {
38+
return new Promise(async (resolve, reject) => {
39+
try {
40+
const document = await this.entityInstance.findOne(parameters);
41+
resolve(document);
42+
} catch (e) {
43+
reject(e);
44+
}
45+
});
46+
}
47+
48+
// Método encargado de obtener todos los documentos de una entidad en base a parámetros
49+
protected getAllDocumentsByParameters(parameters: object = {}) {
50+
return new Promise(async (resolve, reject) => {
51+
try {
52+
const documents = await this.entityInstance.find(parameters);
53+
resolve(documents);
54+
} catch (e) {
55+
reject(e);
56+
}
57+
});
58+
}
59+
60+
// Método encargado de actualizar un documento de una entidad
61+
protected updateDocument(documentId: string, document: object = {}) {
62+
return new Promise(async (resolve, reject) => {
63+
try {
64+
const documentUpdated = await this.entityInstance.findByIdAndUpdate(documentId, document);
65+
resolve(documentUpdated);
66+
} catch (e) {
67+
reject(e);
68+
}
69+
});
70+
}
71+
72+
// Método encargado de eliminar un documento de una entidad
73+
protected deleteDocument(documentId: string) {
74+
return new Promise(async (resolve, reject) => {
75+
try {
76+
await this.entityInstance.findByIdAndDelete(documentId);
77+
resolve(true);
78+
} catch (e) {
79+
reject(e);
80+
}
81+
});
82+
}
83+
84+
}

src/repositories/UserRepository.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { genSaltSync, hashSync } from "bcrypt";
2+
import { iUser } from "../interfaces/models/IUserModel";
3+
import BaseRepository from "./BaseRepository";
4+
5+
export default class UserRepository extends BaseRepository<iUser> {
6+
7+
public countUsers () {
8+
return this.getAllDocuments();
9+
}
10+
11+
public async createUserAccount(userData: iUser) {
12+
try {
13+
let response = {
14+
exist: true,
15+
user: {}
16+
};
17+
const salt = genSaltSync(10);
18+
userData.password = hashSync(userData.password, salt);
19+
userData.otpCode = (userData.hasOwnProperty('otpCode')) ? userData.otpCode : 0;
20+
userData.active = (userData.hasOwnProperty('active')) ? userData.active : false;
21+
userData.enabled = (userData.hasOwnProperty('enabled')) ? userData.enabled : false;
22+
userData.roles = (userData.hasOwnProperty('roles')) ? userData.roles : ["ROLE_USER"];
23+
await this.createDocument(userData);
24+
const createdUser = await this.getOneDocumentByParameters({email: userData.email});
25+
// TODO - Enviar correo electrónico notificando el OTP para activar la cuenta
26+
response.exist = false;
27+
response.user = createdUser;
28+
return response;
29+
} catch (e) {
30+
console.log(e);
31+
throw new Error(e);
32+
}
33+
}
34+
35+
}

0 commit comments

Comments
 (0)