Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Image Upload, Fixes #15

Merged
merged 7 commits into from
Mar 22, 2023
Merged
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
3 changes: 2 additions & 1 deletion backend/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.env
/node_modules
/dist
/dist
/tmp
4,520 changes: 4,144 additions & 376 deletions backend/package-lock.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,21 @@
"author": "",
"license": "ISC",
"dependencies": {
"@aws-sdk/client-s3": "^3.294.0",
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"express-async-errors": "^3.1.1",
"google-auth-library": "^8.7.0",
"jsonwebtoken": "^9.0.0",
"mongoose": "^6.9.1"
"mongoose": "^6.9.1",
"multer": "^1.4.5-lts.1"
},
"devDependencies": {
"@types/cors": "^2.8.13",
"@types/express": "^4.17.17",
"@types/jsonwebtoken": "^9.0.1",
"@types/multer": "^1.4.7",
"@types/node": "^18.13.0",
"tsc-watch": "^6.0.0",
"typescript": "^4.9.5"
Expand Down
1 change: 0 additions & 1 deletion backend/src/controllers/award.controller.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Request,Response } from "express"
import awardService from "../services/award.service";
import badgeService from "../services/badge.service";

interface AwardBody{
recipients:[UserDetail]
Expand Down
24 changes: 20 additions & 4 deletions backend/src/controllers/badge.controller.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Request, Response } from "express";
import { NextFunction, Request, Response } from "express";
import badgeService from "../services/badge.service";

const createBadge=async (req:Request,res:Response)=>{
let {title,desc}=req.body
const createBadge=async (req:Request,res:Response,next:NextFunction)=>{
let {title,desc,template}=req.body
if(!template || !template.id) next(new Error("Invalid image"))
let {orgId}=res.locals;
let badgeId=await badgeService.createBadge(title,desc,orgId)
let badgeId=await badgeService.createBadge(title,desc,orgId,template.id,template.type)
res.status(200).send({
success:true,
message:"Badge/Cert created successfully!",
Expand Down Expand Up @@ -35,8 +36,23 @@ const badgeDetail=async(req:Request,res:Response)=>{
})
}

const templateUpload=async(req:Request,res:Response)=>{
if(!req.file) throw(new Error("No File"))
res.status(200).send({
success:true,
message:"Upload Success!",
data:{
file:{
id:req.file.filename,
type:req.file.mimetype.split("/")[1]
}
}
})
}

export default {
createBadge,
listBadge,
templateUpload,
badgeDetail
}
7 changes: 4 additions & 3 deletions backend/src/middlewares/auth.middleware.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { NextFunction,Request,Response } from "express";
import { APIError } from "../utils/error";
import jwt from "../utils/jwt"

const userAuth=(req:Request,res:Response,next:NextFunction)=>{
if(!req.headers.authorization||typeof req.headers.authorization!=="string"){
return next(new Error("Invalid Auth header"))
return next(new APIError("Invalid Auth Header",401))
}
let token=req.headers.authorization!.split(" ")[1]
jwt.checkToken(token)
Expand All @@ -18,15 +19,15 @@ const userAuth=(req:Request,res:Response,next:NextFunction)=>{
}
next()
})
.catch((err) => next(new Error("Invalid Token")));
.catch((err) => next(new APIError(err.message,401)));
}

const orgAuth=(req:Request,res:Response,next:NextFunction)=>{
if(res.locals.orgLogin){
next();
}
else{
next(new Error("Please login to an org!"))
next(new APIError("Please login to an org!",403))
}
}

Expand Down
4 changes: 3 additions & 1 deletion backend/src/middlewares/error.middleware.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { ErrorRequestHandler } from "express";

const errorHandler:ErrorRequestHandler=(err,req,res,next)=>{
res.status(500).send({
let statusCode=500
if(err.statusCode) statusCode=err.statusCode
res.status(statusCode).send({
success:false,
message:err.message||"Sorry, Some error occured!",
data:null
Expand Down
30 changes: 30 additions & 0 deletions backend/src/middlewares/upload.middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Request,Response,NextFunction } from "express";
import multer, { FileFilterCallback } from "multer";

const fileFilter=(req:Request,file:Express.Multer.File,cb:FileFilterCallback)=>{
if(!(file.mimetype === "image/png"||file.mimetype === "image/jpeg"||file.mimetype === 'image/jpg')){
cb(null,false)
}
cb(null,true)
}


const handleUpload=(req:Request,res:Response,next:NextFunction)=>{
const upload = multer({
dest:"tmp/uploads/templates",
fileFilter,
limits: {
fileSize: 1005*1000,
},
}).single("template");

upload(req,res,(err)=>{
if(err){
next(new Error(err))
}
next()
})
}

export default handleUpload

4 changes: 4 additions & 0 deletions backend/src/models/badge.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ const badgeSchema = new Schema({
type: Types.ObjectId,
ref: "orgs",
},
template:{
type:String,
required:true
},
assertions:[
{
name:{
Expand Down
3 changes: 3 additions & 0 deletions backend/src/modules.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@ declare namespace NodeJS {
PORT: string;
JWT_SECRET: string;
GOOGLE_AUTH_CLIENT_ID: string;
S3_BUCKET: string;
S3_ACCESS_KEY: string;
S3_ACCESS_SECRET: string;
}
}
2 changes: 2 additions & 0 deletions backend/src/routers/org/badge.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import express from "express";
import awardController from "../../controllers/award.controller";
const router = express.Router();
import badgeController from "../../controllers/badge.controller";
import handleUpload from "../../middlewares/upload.middleware";

router.get("/",badgeController.listBadge)
router.post("/add",badgeController.createBadge)
router.post("/upload",handleUpload, badgeController.templateUpload);

router.get("/:badge_id/detail",badgeController.badgeDetail)
router.get("/:badge_id/assertions", awardController.listAssertions);
Expand Down
4 changes: 3 additions & 1 deletion backend/src/services/assertion.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,16 @@ const assertionDetail=(assertionId:string)=>
Badge.findOne({
assertions:{
$elemMatch: {
id:assertionId
_id:assertionId
}
}
},{
title:1,
desc:1,
template:1,
"assertions.$":1
})
.populate("org","key name")


export default{
Expand Down
1 change: 1 addition & 0 deletions backend/src/services/award.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const listAwardees = (badgeId: string, orgId: string) =>
.select({
assertions: {
user: 1,
_id:1
},
});

Expand Down
46 changes: 30 additions & 16 deletions backend/src/services/badge.service.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,46 @@
import fs from "fs";
import { Types } from "mongoose"
import Badge from "../models/badge.model"
import s3 from "../utils/s3";

const createBadge=(title:string,desc:string,org:string)=>new Promise((resolve,reject)=>{
Badge.create({
title,
desc,
org
const createBadge=(title:string,desc:string,org:string,fileName:string,fileType:string)=>new Promise(async(resolve,reject)=>{
const _id=new Types.ObjectId()
const filePath = "tmp/uploads/templates";
const fileKey=_id+"."+fileType
const s3Response=await s3
.uploadFile(filePath, fileName, "templates/" + fileKey)
.catch((err) => reject(err));
s3Response&&fs.unlink(filePath+"/"+fileName,()=>{
// console.log("deleted",fileName)
})
.then(data=>{
if(data){
resolve(data._id)
}
else{
reject(new Error("Unable to create"))
}
})
.catch(err=>reject(err))
s3Response &&
Badge.create({
_id,
title,
desc,
org,
template: fileKey,
})
.then((data) => {
if (data) {
resolve(data._id);
} else {
reject(new Error("Unable to create"));
}
})
.catch((err) => reject(err));
})

const listBadge=(org:string)=>Badge.find({
org
})
.select("title desc")
.select("title desc template")

const badgeDetail = (org: string,badge:string) =>
Badge.findOne({
org,
_id:badge
}).select("title desc");
}).select("title desc template");

export default {
createBadge,
Expand Down
7 changes: 7 additions & 0 deletions backend/src/utils/error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export class APIError extends Error{
statusCode:number
constructor(message:string,statusCode:number){
super(message)
this.statusCode=statusCode
}
}
38 changes: 38 additions & 0 deletions backend/src/utils/s3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
import fs from "fs";
import { promisify } from "util";
const readFile = promisify(fs.readFile);

const client = new S3Client({
region: process.env.S3_REGION,
credentials: {
accessKeyId: process.env.S3_ACCESS_KEY,
secretAccessKey: process.env.S3_ACCESS_SECRET,
},
});

const uploadFile = (sourcePath: string, sourceName: string, fileKey: string) =>
new Promise(async (resolve, reject) => {
const fileData = await readFile(`${sourcePath}/${sourceName}`).catch(
(err) => reject(err)
);
if (fileData) {
const s3Command = new PutObjectCommand({
Bucket: process.env.S3_BUCKET,
Key: fileKey,
Body: fileData,
});

const s3Response = await client
.send(s3Command)
.catch((err) => reject(err));

resolve(s3Response);
} else {
reject("Error");
}
});

export default {
uploadFile,
};
Binary file added frontend/public/img/thumbnail.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 10 additions & 4 deletions frontend/src/components/AssertionBasicData.jsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
import { Box, Typography } from '@mui/material';
import React from 'react'

const AssertionBasicData = ({detail}) => {
const AssertionBasicData = ({detail,org}) => {
return (
<Box sx={{ mt: 5 }}>
<Box>
<Typography sx={{ fontSize: 14 }} color="text.secondary" gutterBottom>
Awarded to
</Typography>
{detail.name}
{detail&&detail.name}
</Box>
<Box>
<Typography sx={{ fontSize: 14 }} color="text.secondary" gutterBottom>
<Typography sx={{ fontSize: 14, mt:1 }} color="text.secondary" gutterBottom>
Awarded by
</Typography>
{org&&org.name}
</Box>
<Box>
<Typography sx={{ fontSize: 14, mt:2 }} color="text.secondary" gutterBottom>
Assertion ID
</Typography>
{detail._id}
{detail&&detail._id}
</Box>
</Box>
);
Expand Down
Loading