Skip to content

Commit 5194fb7

Browse files
committed
First commit
1 parent 6d4a354 commit 5194fb7

35 files changed

+1235
-0
lines changed

restApi/.env

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#main path to static file and upload file
2+
PATH_PUBLIC = public
3+
4+
#server configuration
5+
SERVER_PORT=5000
6+
SERVER_HOST=localhost
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { index, store, show, update, destroy } from "#service/userService";
2+
3+
export async function listUsers(req,res){
4+
const usersList = await index();
5+
res.json(usersList);
6+
}
7+
8+
export async function getUser(req,res){
9+
const userInfo = await show(req.params.id);
10+
userInfo ? res.json(userInfo) : res.json({msg:"User not found..."});
11+
}
12+
13+
export async function deleteUser(req,res){
14+
const userDelete = await destroy(req.body.id);
15+
userDelete ? res.json({msg:"user deleted"}) : res.json({msg:"user don`t exist"});
16+
}
17+
18+
export async function newUser(req,res){
19+
const createUser = await store(req.body);
20+
createUser ? res.json({msg:"User created"}) : res.json({msg:"user not created"});
21+
}
22+
23+
export async function updateUser(req,res){
24+
const userUpdate = await update(req.body.id,req.body);
25+
userUpdate ? res.json({msg:"User updated"}) : res.json({msg:"User dont updated"});
26+
}

restApi/app/middlewares/authApi.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export function auth(req, res, next) {
2+
const isAuth = req.headers.authorization && req.headers.authorization.includes("Bearer") ? true : false
3+
4+
if (isAuth) {
5+
next();
6+
} else {
7+
res.json({message:"You`re almost hacked this API ,you is embryo soldier!, token Bearer required"});
8+
}
9+
}

restApi/app/models/user.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
const user = [
2+
{
3+
"id":"255261",
4+
"name":"John",
5+
"email":"[email protected]",
6+
"country":"Canada",
7+
"photo":"https://randomuser.me/api/portraits/men/77.jpg"
8+
},
9+
{
10+
"id":"226195",
11+
"name":"David",
12+
"email":"[email protected]",
13+
"country":"Japan",
14+
"photo":"https://randomuser.me/api/portraits/men/68.jpg"
15+
},
16+
{
17+
"id":"493672",
18+
"name":"Alice",
19+
"email":"[email protected]",
20+
"country":"Japan",
21+
"photo":"https://randomuser.me/api/portraits/women/42.jpg"
22+
},
23+
{
24+
"id":"703642",
25+
"name":"Daniel",
26+
"email":"[email protected]",
27+
"country":"Canada",
28+
"photo":"https://randomuser.me/api/portraits/women/55.jpg"
29+
},
30+
{
31+
"id":"775456",
32+
"name":"Emily",
33+
"email":"[email protected]",
34+
"country":"Canada",
35+
"photo":"https://randomuser.me/api/portraits/men/88.jpg"
36+
},
37+
{
38+
"id":"989409",
39+
"name":"Michael",
40+
"email":"[email protected]",
41+
"country":"Australia",
42+
"photo":"https://randomuser.me/api/portraits/women/46.jpg"
43+
}
44+
]
45+
46+
export default user;

restApi/app/services/userService.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import user from "#model/user";
2+
3+
async function index() {
4+
return user;
5+
}
6+
7+
async function store(data) {
8+
user.push(data);
9+
return data;
10+
}
11+
12+
async function show(id) {
13+
const item = user.find(item => item.id === id);
14+
return item;
15+
}
16+
17+
async function update(id, data) {
18+
const indexToUpdate = user.findIndex(item => item.id === id);
19+
if (indexToUpdate !== -1) {
20+
user[indexToUpdate] = { ...user[indexToUpdate], ...data };
21+
return user[indexToUpdate];
22+
}
23+
return null;
24+
}
25+
26+
async function destroy(id) {
27+
const indexToDelete = user.findIndex(item => item.id === id);
28+
if (indexToDelete !== -1) {
29+
const deletedItem = user.splice(indexToDelete, 1);
30+
return deletedItem[0];
31+
}
32+
return null;
33+
}
34+
35+
export {
36+
index,
37+
store,
38+
show,
39+
update,
40+
destroy,
41+
};

restApi/config/headers.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export function configHeaders(request, response){
2+
3+
response.setHeader('Access-Control-Allow-Origin', '*');
4+
//here add more headers...
5+
}

restApi/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import { runApp } from "#app"
2+
runApp();

restApi/lib/core/handlers.js

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// handler.js
2+
import parseRequestBody from "./parseRequestBody.js";
3+
import { routes, get, post, put, delet } from "./routes.js";
4+
import { logger } from "#utils/logs";
5+
import { handleNotFound, handleBadRequest, handleMethodNotAllowed } from "#status";
6+
import { renderFile } from "./renderFile.js";
7+
import { configHeaders } from "#config/headers";
8+
9+
async function handleRequest(request, response) {
10+
const { url, method } = request;
11+
12+
renderFile(request,response);
13+
configHeaders(request,response);
14+
15+
if (method === 'OPTIONS') {
16+
response.writeHead(200, {
17+
'Access-Control-Allow-Origin': '*',
18+
'Access-Control-Allow-Headers': 'Authorization, Content-Type,X-Requested-With'
19+
});
20+
response.end();
21+
return;
22+
}
23+
24+
async function status(body){
25+
response.statusCode = body;
26+
}
27+
28+
async function log(...messages) {
29+
console.log(...messages);
30+
}
31+
32+
async function send(body) {
33+
response.end(body.toString());
34+
}
35+
36+
async function setHeader(body,value){
37+
response.setHeader(body,value);
38+
}
39+
40+
async function go(path){
41+
response.writeHead(302, { Location:path});
42+
response.end();
43+
}
44+
45+
46+
47+
async function json(data) {
48+
response.writeHead(200, { "Content-Type": "application/json"});
49+
response.end(JSON.stringify(data));
50+
}
51+
52+
53+
const customResponse = { log, send, json, setHeader, status, go};
54+
55+
let routeFound = false;
56+
for (const route of routes) {
57+
const match = url.match(route.regexPath);
58+
59+
if (match) {
60+
routeFound = true;
61+
if (route.method === method) {
62+
63+
if (method === "POST" || method === "PUT" || method === "DELETE") {
64+
try {
65+
const body = await parseRequestBody(request);
66+
request.body = body.data;
67+
} catch (error) {
68+
await logger.error("Error parsing request body:" + error);
69+
return handleBadRequest(request, customResponse);
70+
}
71+
}
72+
73+
const param = {};
74+
for (let i = 0; i < route.paramNames.length; i++) {
75+
const paramName = route.paramNames[i];
76+
param[paramName] = match[i + 1];
77+
}
78+
79+
request.params = param;
80+
request.cookies = "";
81+
82+
await runMiddlewares(request, customResponse, route.handlers);
83+
return;
84+
} else if (method === "GET" && route.method === "POST" || route.method === "DELETE" || route.method === "PUT") {
85+
return await handleMethodNotAllowed(request, customResponse, route.method);
86+
}else if (method === "POST" || method === "DELETE" || method === "PUT" && route.method === "GET") {
87+
return await handleMethodNotAllowed(request, customResponse, route.method);
88+
}
89+
}
90+
}
91+
92+
if (!routeFound) {
93+
await handleNotFound(request, customResponse);
94+
}
95+
}
96+
97+
async function runMiddlewares(request, customResponse, handlers) {
98+
if (!handlers || handlers.length === 0) {
99+
return;
100+
}
101+
102+
const currentHandler = handlers[0];
103+
104+
await currentHandler(request, customResponse, async () => {
105+
await runMiddlewares(request, customResponse, handlers.slice(1));
106+
});
107+
}
108+
109+
export { handleRequest, get, post, put, delet };

restApi/lib/core/parseRequestBody.js

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import Busboy from "busboy";
2+
import fs from 'fs';
3+
4+
/**
5+
* Parses the request body based on the content type.
6+
* Supports multipart/form-data and application/json content types.
7+
* @param {http.IncomingMessage} req - The request object.
8+
* @returns {Promise} - A promise that resolves to the parsed data.
9+
*/
10+
11+
export default async function parseRequestBody(req) {
12+
return new Promise((resolve, reject) => {
13+
const contentType = req.headers["content-type"];
14+
let data = "";
15+
16+
if (contentType && contentType.startsWith("multipart/form-data")) {
17+
// Parsing multipart/form-data
18+
const boundaryMatch = contentType.match(/boundary=(?:"([^"]+)"|([^;]+))/i);
19+
const boundary = boundaryMatch ? boundaryMatch[1] || boundaryMatch[2] : undefined;
20+
21+
if (!boundary) {
22+
reject(new Error('Boundary not found in the content-type header.'));
23+
return;
24+
}
25+
26+
const busboy = Busboy({ headers: req.headers, boundary });
27+
const formData = { fields: {}, files: {} };
28+
29+
busboy.on('field', (fieldname, val) => {
30+
formData.fields[fieldname] = val;
31+
});
32+
33+
busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
34+
const chunks = [];
35+
file.on('data', (chunk) => {
36+
chunks.push(chunk);
37+
});
38+
39+
file.on('end', () => {
40+
formData.files[fieldname] = {
41+
filename,
42+
encoding,
43+
mimetype,
44+
data: Buffer.concat(chunks),
45+
};
46+
});
47+
});
48+
49+
busboy.on('finish', () => {
50+
const data = formData.fields ? formData.fields : {};
51+
const files = formData.files ? formData.files : {};
52+
resolve({ data: { data, files } });
53+
});
54+
55+
busboy.on('error', (err) => {
56+
reject(err);
57+
});
58+
59+
req.pipe(busboy);
60+
} else if (contentType && contentType.startsWith("application/json")) {
61+
// Parsing application/json
62+
req.on("data", (chunk) => {
63+
data += chunk.toString();
64+
});
65+
66+
req.on("end", () => {
67+
try {
68+
const jsonData = JSON.parse(data);
69+
resolve({ data: jsonData });
70+
} catch (err) {
71+
reject(err);
72+
}
73+
});
74+
} else {
75+
// Unsupported content type
76+
reject(new Error("Unsupported content type."));
77+
}
78+
79+
req.on("error", (err) => {
80+
reject(err);
81+
});
82+
});
83+
}

restApi/lib/core/renderFile.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import fs from 'fs';
2+
import { dirname, extname, join } from 'path';
3+
import { fileURLToPath } from 'url';
4+
5+
/**
6+
* Renders a file in the response based on the request URL.
7+
* @param {http.IncomingMessage} request - The request object.
8+
* @param {http.ServerResponse} response - The response object.
9+
*/
10+
11+
export function renderFile(request, response) {
12+
const currentFileUrl = import.meta.url;
13+
const currentFilePath = dirname(fileURLToPath(currentFileUrl));
14+
const pathPublic = process.env.PATH_PUBLIC;
15+
const publicPath = join(currentFilePath, pathPublic).replace('/lib/core', '');
16+
17+
const filePath = join(publicPath, request.url.replace(/\.\.\//g, ''));
18+
19+
fs.stat(filePath, (err, stats) => {
20+
if (err || !stats.isFile()) {
21+
return;
22+
}
23+
24+
const contentType = getContentType(filePath);
25+
response.writeHead(200, { 'Content-Type': contentType });
26+
fs.createReadStream(filePath).pipe(response);
27+
});
28+
}
29+
30+
/**
31+
* Gets the content type based on the file extension.
32+
* @param {string} filePath - The path to the file.
33+
* @returns {string} - The content type.
34+
*/
35+
36+
function getContentType(filePath) {
37+
const ext = extname(filePath);
38+
switch (ext) {
39+
case '.html':
40+
return 'text/html';
41+
case '.js':
42+
return 'text/javascript';
43+
case '.css':
44+
return 'text/css';
45+
case '.jpeg':
46+
case '.jpg':
47+
return 'image/jpeg';
48+
case '.png':
49+
return 'image/png';
50+
case '.gif':
51+
return 'image/gif';
52+
default:
53+
return 'application/octet-stream';
54+
}
55+
}

0 commit comments

Comments
 (0)