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
534 changes: 524 additions & 10 deletions package-lock.json

Large diffs are not rendered by default.

12 changes: 11 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,27 @@
"description": "",
"main": "index.js",
"scripts": {
"start": "nodemon src/app.ts",
"start": "nodemon src/server.ts",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/cors": "^2.8.19",
"@types/express": "^5.0.3",
"@types/morgan": "^1.9.10",
"@types/multer": "^1.4.13",
"@types/node": "^24.0.1",
"express": "^5.1.0",
"ts-node": "^10.9.2",
"typescript": "^5.8.3"
},
"dependencies": {
"cors": "^2.8.5",
"dotenv": "^16.5.0",
"mongoose": "^8.15.2",
"morgan": "^1.10.0",
"multer": "^2.0.1"
}
}
32 changes: 16 additions & 16 deletions src/account.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
export const accounts = [
{
id: 1,
username: "Omar",
funds: 30,
},
{
id: 2,
username: "Zainab",
funds: 0,
},
{
id: 3,
username: "Salwa",
funds: 100,
},
];
{
id: 1,
username: "Omar",
funds: 30,
},
{
id: 2,
username: "Zainab",
funds: 0,
},
{
id: 3,
username: "Salwa",
funds: 100,
},
];
167 changes: 130 additions & 37 deletions src/api/accounts/accounts.controller.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,141 @@
import { Request, Response } from 'express';
import { accounts } from '../../account';
import { NextFunction, Request, Response } from "express";
import { accounts } from "../../account";
import Account from "../../model/Account";

export const accountCreate = (req: Request, res: Response) => {
const id = accounts[accounts.length - 1].id + 1;
const newAccount = { ...req.body, funds: 0, id };
accounts.push(newAccount);
res.status(201).json(newAccount);
//creating new account
export const accountCreate = async (
req: Request,
res: Response,
next: NextFunction
) => {
// const id = accounts[accounts.length - 1].id + 1;
// const newAccount = { ...req.body, funds: 0, id };
// accounts.push(newAccount);
// res.status(201).json(newAccount);
try {
const { username, funds, image } = req.body;
// console.log(
// "Creating account with username:",
// username,
// "and funds:",
// funds
// );
let imagePath;
if (req.file) {
imagePath = req.file?.path;
}
const newAcc = await Account.create({ username, funds, image: imagePath }); // Account is my created model / image(key): imagePath(value)
res.status(201).json(newAcc);
} catch (error) {
// res.status(500).json({ message: "Error creating an account.." });
next(error); // Pass the error to the next middleware (error handler)
}
};

export const accountDelete = (req: Request, res: Response) => {
// Deleting an account
export const accountDelete = async (
req: Request,
res: Response,
next: NextFunction
) => {
// const foundAccount = accounts.find((account) => account.id === +accountId);
// if (foundAccount) {
// let newAccounts = accounts.filter((account) => account.id !== +accountId);
// res.status(204).end();
// } else {
// res.status(404).json({ message: "Account not found" });
// }
try {
const { accountId } = req.params;
const foundAccount = accounts.find((account) => account.id === +accountId);
if (foundAccount) {
let newAccounts = accounts.filter((account) => account.id !== +accountId);
res.status(204).end();
const foundACCFromDB = await Account.findByIdAndDelete(accountId);

if (foundACCFromDB) {
res.status(204).end();
} else {
res.status(404).json({ message: 'Account not found' });
res.status(404).json({ message: "Account not found" });
}
} catch (error) {
// res.status(500).json({ message: "Error deleting account..." });
next(error);
}
};

export const accountUpdate = (req: Request, res: Response) => {
const { accountId } = req.params;
const foundAccount = accounts.find((account) => account.id === +accountId);
if (foundAccount) {
foundAccount.funds = req.body.funds;
res.status(204).end();
} else {
res.status(404).json({ message: 'Account not found' });
// Updating an account
export const accountUpdate = async (
req: Request,
res: Response,
next: NextFunction
) => {
const { accountId } = req.params;
const { username, funds } = req.body;
// const foundAccount = accounts.find((account) => account.id === +accountId);
const updateAccInDB = await Account.findByIdAndUpdate(accountId, {
username,
funds,
});
try {
if (updateAccInDB) {
res.status(204).end();
}
} catch (error) {
next(error);
}
// if (updateAccInDB) {
// res.status(204).end();
// } else {
// res.status(404).json({ message: "Account not found, can't update" });
// }
};

export const accountsGet = (req: Request, res: Response) => {
res.json(accounts);
// get all accounts from the database
export const accountsGet = async (
req: Request,
res: Response,
next: NextFunction
) => {
try {
const accountsFromDB = await Account.find();
res.status(200).json(accountsFromDB);
} catch (error) {
// res.status(500).json({ message: "Error fetching accounts..." });
next(error);
}
};
// get one account by username
export const getAccountByUsername = async (req: Request, res: Response) => {
const { username } = req.params;
// const foundAccount = accounts.find(
// (account) => account.username === username
// );
const foundAccByUsername = await Account.findOne({ username });
if (req.query.currency === "usd" && foundAccByUsername) {
const accountInUsd = {
...foundAccByUsername,
funds: foundAccByUsername.funds * 3.31,
};
res.status(201).json(accountInUsd);
} else {
res.status(201).json(foundAccByUsername);
}
};

export const getAccountByUsername = (req: Request, res: Response) => {
const { username } = req.params;
const foundAccount = accounts.find(
(account) => account.username === username
);
if (req.query.currency === 'usd' && foundAccount) {
const accountInUsd = { ...foundAccount, funds: foundAccount.funds * 3.31 };
res.status(201).json(accountInUsd);
} else {
res.status(201).json(foundAccount);
}
};
// get one account by username, abd check for amount above 3000
// export const getAccByUsernameAmount = async (req: Request, res: Response) => {
// const { username } = req.params;
// // const foundAccount = accounts.find(
// // (account) => account.username === username
// // );
// const foundAccByUsername = await Account.find({ username }); // this is an array, find a way to just grap the users with the correct condition
// // check
// if (
// foundAccByUsername &&
// Number(req.query.amount) <= foundAccByUsername?.funds
// ) {
// const accountWithCorrectAmount = {
// username: foundAccByUsername.username,
// funds: foundAccByUsername.funds,
// };
// res.status(201).json(accountWithCorrectAmount);
// } else {
// // res.status(201).json(foundAccByUsername);
// res.status(500).json({ message: "Error fetching accounts..." });
// }
// };
27 changes: 19 additions & 8 deletions src/api/accounts/accounts.routes.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
import express from 'express';
import express from "express";
const accountsRouter = express.Router();
import { accountsGet, accountUpdate, accountDelete, accountCreate, getAccountByUsername } from './accounts.controller';
import {
accountsGet,
accountUpdate,
accountDelete,
accountCreate,
getAccountByUsername,
// getAccByUsernameAmount,
} from "./accounts.controller";
import upload from "../../middlewares/multer";

accountsRouter.get('/', accountsGet);
accountsRouter.get('/:username', getAccountByUsername);
accountsRouter.post('/', accountCreate);
accountsRouter.get("/", accountsGet);
accountsRouter.get("/:username", getAccountByUsername);
accountsRouter.post("/", upload.single("image"), accountCreate); // multer middleware used here

accountsRouter.delete('/:accountId', accountDelete);
accountsRouter.delete("/:accountId", accountDelete);

accountsRouter.put('/:accountId', accountUpdate);
accountsRouter.put("/:accountId", accountUpdate);

export default accountsRouter;
// new route created
// accountsRouter.get("/vip", getAccByUsernameAmount);

export default accountsRouter;
32 changes: 26 additions & 6 deletions src/app.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,32 @@
import express from "express";
import { Request, Response, NextFunction } from "express";
import morgan from "morgan";
import cors from "cors";
import accountsRouter from "./api/accounts/accounts.routes";
import notFoundHandler from "./middlewares/NotFound";
import errorHandler from "./middlewares/ErrorHandler";
import path from "path";

const app = express();
const PORT = 8000;

app.use(express.json());
app.use("/accounts", accountsRouter);
app.use((req: Request, res: Response, next: NextFunction) => {
console.log(new Date().toLocaleString());
next();
});
app.use(morgan("dev")); // Morgan is a thid party logging middleware, it logs every request to the console
app.use(cors()); // CORS is a middleware that allows cross-origin requests, it is used to allow requests from different origins (domains)

app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
app.use(express.json()); // Middleware to parse JSON bodies, every single request with body has to be JSon
// all middleware must be placed before the routes
app.use("/accounts", accountsRouter); // app.use is app level middleware
app.use("./src/uploads", express.static(path.join(__dirname, "uploads"))); // this middleware will fix the image path so we can view it in a browser, joining the root dir with uploads

// ----------- 404 error handler middleware and plug in controllers
app.use(notFoundHandler);

// ----------- 500 error handler middleware and plug in controllers
app.use(errorHandler);

export default app;

// app.ts is where we place middlewares, routes, and other configurations for the Express application.
15 changes: 15 additions & 0 deletions src/middlewares/ErrorHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { NextFunction, Request, Response } from "express";

const errorHandler = (
err: any,
req: Request,
res: Response,
next: NextFunction
) => {
console.log(err);
res
.status(err.status || 500)
.json(`something broke ${err} at path: ${req.path}`);
};

export default errorHandler;
13 changes: 13 additions & 0 deletions src/middlewares/NotFound.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { NextFunction, Request, Response } from "express";

const notFoundHandler = (
err: any,
req: Request,
res: Response,
next: NextFunction
) => {
console.log(err);
res.status(404).json({ message: `Not found at path: ${req.path}` });
};

export default notFoundHandler;
47 changes: 47 additions & 0 deletions src/middlewares/multer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Request } from "express";
import multer from "multer";
import path from "path";

// this middleware is router lever, why? because i only need image/files upload in certain endpoints
// this will store the uploaded files in the /tmp/my-uploads directory
// diskstorage, will store files on my local machine as files
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "./src/uploads"); // this is the directory where the files will be stored;
},
filename: function (req, file, cb) {
// this will make sure that the file name is unique, to avoid naming clashes
const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9);
cb(null, file.fieldname + "-" + uniqueSuffix + "-" + file.originalname);
},
});
// const fileFilter = (req: Request, file: Express.Multer.File, cb: any) => {
// // The function should call `cb` with a boolean
// // to indicate if the file should be accepted

// // To accept only .png files
// if (file.mimetype === "image/png") {
// cb(null, true); // this will accept the file?
// } else {
// cb(Error("only .PNG files accepted"), false);
// }
// };
// task: restrict the user to .png file type
const upload = multer({
storage: storage,
fileFilter: (req, file, cb) => {
const allowedTypes = /jpg|png/;
const extValid = allowedTypes.test(
path.extname(file.originalname).toLowerCase()
);
const mimeValid = allowedTypes.test(file.mimetype);

if (extValid && mimeValid) {
cb(null, true); // Accept the file
} else {
cb(new Error("Only .jpg and .png files are allowed!") as any, false); // Reject the file
}
},
});

export default upload;
Loading