Skip to content

Commit

Permalink
Merge branch 'dev' into feat/delete-product-endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
Uzo-Felix authored Jul 22, 2024
2 parents 21d1289 + 588e4fe commit ebd6191
Show file tree
Hide file tree
Showing 12 changed files with 261 additions and 5 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Build, Test, and Deploy for Dev Branch

on:
push:
branch:
branchs:
- dev

jobs:
Expand All @@ -23,7 +23,7 @@ jobs:
run: yarn install

- name: Run Test
run: yarn test --passWithNoTests
run: yarn test

- name: buld the dist
run: yarn build
Expand Down
29 changes: 29 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: CI

on:
pull_request:
branches:
- dev

jobs:
test:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: "20"

- name: Install dependencies
run: yarn install

- name: Run tests
run: yarn test
env:
CI: true
- name: buld the dist
run: yarn build
2 changes: 1 addition & 1 deletion server-script/startappdev.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash

cd /var/www/aihomework/dev/
/usr/bin/yarn prod
/usr/bin/yarn start
2 changes: 1 addition & 1 deletion server-script/startappprod.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash

cd /var/www/aihomework/prod/
/usr/bin/yarn prod
/usr/bin/yarn start
68 changes: 68 additions & 0 deletions src/controllers/HelpController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// src/controllers/UserController.ts
import { Request, Response } from "express";
import { HelpService } from "../services";
import { HttpError } from "../middleware";

class HelpController {
private helpService: HelpService;

constructor() {
this.helpService = new HelpService();
}

async createTopic(req: Request, res: Response): Promise<void> {
try {
const topic = await this.helpService.create(req);
res.status(201).json({
success: true,
message: "Topic Created Successfully",
data: {
article_id: topic.id,
content: topic.content,
author: topic.author,
title: topic.title,
createdAt: topic.createdAt,
updatedAt: topic.updatedAt,
},
status_code: 201,
});
} catch (error) {
if (error instanceof HttpError) {
res.status(error.status_code).json({ message: error.message });
} else {
res
.status(500)
.json({ message: error.message || "Internal Server Error" });
}
}
}

async updateTopic(req: Request, res: Response): Promise<void> {
try {
const topic = await this.helpService.update(req);
res.status(200).json({
success: true,
message: "Topic Updated Successfully",
data: {
article_id: topic.id,
content: topic.content,
author: topic.author,
title: topic.title,
createdAt: topic.createdAt,
updatedAt: topic.updatedAt,
},
status_code: 200,
});
} catch (error) {
if (error instanceof HttpError) {
res.status(error.status_code).json({ message: error.message });
} else {
res
.status(500)
.json({ message: error.message || "Internal Server Error" });
}
}
}
}

export default HelpController;
1 change: 1 addition & 0 deletions src/controllers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from "./AuthController";
export * from "./UserController";
export * from "./ProductController";
export * from "./NotificationController"
export * from "./HelpController";
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import cors from "cors";
import {
userRouter,
authRoute,
helpRouter,
testimonialRoute,
notificationRouter,
smsRouter,
Expand Down Expand Up @@ -44,6 +45,7 @@ server.get("/", (req: Request, res: Response) => {
});
server.use("/api/v1", userRouter, orgRouter);
server.use("/api/v1/auth", authRoute);
server.use("/api/v1/help-center", helpRouter);
server.use("/api/v1/sms", smsRouter);
server.use("/api/v1", testimonialRoute);
server.use("/api/v1/product", productRouter);
Expand Down
10 changes: 10 additions & 0 deletions src/routes/help-center.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// src/routes/help-center.ts
import { Router } from "express";
import HelpController from "../controllers/HelpController";
import { authMiddleware, verifyAdmin } from "../services";

const helpRouter = Router();
const helpController = new HelpController();
helpRouter.post("/topics", authMiddleware, helpController.createTopic.bind(helpController));
helpRouter.patch("/topics/:id", authMiddleware, helpController.updateTopic.bind(helpController));
export { helpRouter };
1 change: 1 addition & 0 deletions src/routes/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// silently ignore
export * from "./auth";
export * from "./user";
export * from "./help-center";
export * from "./testimonial";
export * from "./product";
export * from "./sms";
Expand Down
144 changes: 144 additions & 0 deletions src/services/help.services.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// src/services/HelpService.ts
import { NextFunction, Request, Response } from "express";
import jwt from "jsonwebtoken";
import { HelpCenterTopic } from "../models";
import { User } from "../models";
import AppDataSource from "../data-source";
import { HttpError } from "../middleware";
import config from "../config";

export class HelpService {
public async create(req: Request): Promise<HelpCenterTopic> {
try {
const { title, content, author } = req.body;

//Validate Input
if (!title || !content || !author) {
throw new HttpError(
422,
"Validation failed: Title, content, and author are required"
);
}

//Check for Existing Title
const articleRepository = AppDataSource.getRepository(HelpCenterTopic);
const existingTitle = await articleRepository.findOne({
where: { title },
});
if (existingTitle) {
throw new HttpError(422, "Article already exists");
}

const articleEntity = articleRepository.create({
title,
content,
author,
});
const article = await articleRepository.save(articleEntity);
return article;
} catch (error) {
throw new HttpError(error.status || 500, error.message || error);
}
}

public async update(req: Request): Promise<HelpCenterTopic> {
try {
const { title, content, author } = req.body;
const article_id = req.params.id;

//Get article repo
const articleRepository = AppDataSource.getRepository(HelpCenterTopic);

// Check if article exists
const existingArticle = await articleRepository.findOne({
where: { id: article_id },
});

if (!existingArticle) {
throw new HttpError(404, "Not Found");
}

//Update Article on DB
await articleRepository.update(article_id, { title, content, author });

//Fetch Updated article
const newArticle = await articleRepository.findOne({
where: { id: article_id },
});
return newArticle;
} catch (error) {
console.error(error);
throw new HttpError(error.status || 500, error.message || error);
}
}
}

export const authMiddleware = (
req: Request,
res: Response,
next: NextFunction
) => {
const authHeader = req.headers["authorization"];
const token = authHeader && authHeader.split(" ")[1];

if (!token) {
return res.status(401).json({
success: false,
message: "Access denied. No token provided",
status_code: 401,
});
}

try {
const verified = jwt.verify(token, config.TOKEN_SECRET);
if (verified) {
next();
}
} catch (error) {
res.status(401).json({
success: false,
message: "Access denied. Invalid token",
status_code: 401,
});
}
};

export const verifyAdmin = async (
req: Request,
res: Response,
next: NextFunction
) => {
const authHeader = req.headers["authorization"];
const token = authHeader && authHeader.split(" ")[1];
try {
const decodedToken = jwt.decode(token);

if (typeof decodedToken === "string" || !decodedToken) {
return res.status(401).json({
success: false,
message: "Access denied. Invalid token",
status_code: 401,
});
}

const userRepository = AppDataSource.getRepository(User);
const user = await userRepository.findOne({
where: { id: decodedToken.userId },
});
console.log(user.role);

if (user.role !== "admin") {
return res.status(403).json({
success: false,
message: "Access denied! You are not an admin",
status_code: 403,
});
}

next();
} catch (error) {
res
.status(401)
.json({ status: "error", message: "Access denied. Invalid token" });
}
};
1 change: 1 addition & 0 deletions src/services/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./auth.services";
export * from "./user.services";
export * from "./help.services";
2 changes: 1 addition & 1 deletion tsconfig.jest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
"types": ["jest", "node"]
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules"]
"exclude": ["node_modules", "dist/**/*.ts"]
}

0 comments on commit ebd6191

Please sign in to comment.