├── .gitignore ├── README.md ├── docker-compose.yml ├── linuxfest-backend ├── .env.db.test ├── .env.dev.test ├── Dockerfile ├── mails │ ├── form.html │ ├── password.html │ └── register.html ├── package-lock.json ├── package.json └── src │ ├── app.js │ ├── config │ ├── index.js │ └── logger.js │ ├── db │ └── mongoose.js │ ├── express_middlewares │ ├── adminAuth.js │ └── userAuth.js │ ├── index.js │ ├── models │ ├── Company.js │ ├── Discount.js │ ├── Static.js │ ├── SuperUser.js │ ├── Teacher.js │ ├── User.js │ └── Workshop.js │ ├── routers │ ├── company.js │ ├── discount.js │ ├── files.js │ ├── static.js │ ├── superuser.js │ ├── teacher.js │ ├── user.js │ └── workshop.js │ └── utils │ └── utils.js └── nginx ├── Dockerfile └── nginx.conf /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode 3 | mongodb 4 | mongodb-data 5 | .env* 6 | !.env*test 7 | .eslintrc.json 8 | uploads 9 | *.orig 10 | *.log 11 | logs 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LinuxFest-Website-backend 2 | back-end of LinuxFest website 3 | 4 | To use docker, the file structure should be like below: 5 | ``` 6 | . 7 | ├── docker-compose.yml 8 | ├── backend 9 | │ └── linuxfest-backend 10 | │ ├── Dockerfile 11 | │ ├── .env.db 12 | │ ├── .env.dev 13 | │ └── src/ 14 | └── nginx 15 | ├── Dockerfile 16 | ├── nginx.conf 17 | ├── front/ 18 | └── panel/ 19 | ``` 20 | NOTE: Rename .env.dev.test to .env.dev and also .env.db.test to .env.db 21 | 22 | And to build and create a container run these lines: 23 | ``` 24 | docker-compose --env-file ./backend/linuxfest-backend/.env.db -f docker-compose.yml build 25 | docker-compose --env-file ./backend/linuxfest-backend/.env.db -f docker-compose.yml up -d 26 | ``` 27 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | nginx: 5 | container_name: nginx 6 | ports: 7 | - "81:80" 8 | build: 9 | context: ./nginx/ 10 | networks: 11 | - nginx-net 12 | depends_on: 13 | - node-app 14 | 15 | node-app: 16 | container_name: node-app 17 | build: 18 | context: ./backend/linuxfest-backend/ 19 | args: 20 | NODE_ENV: production 21 | volumes: 22 | - upload-files:/app/uploads 23 | environment: 24 | - NODE_ENV=production 25 | - MONGO_USER=${MONGO_USER} 26 | - MONGO_PASSWORD=${MONGO_PASSWORD} 27 | - PWD=/app 28 | env_file: 29 | - ./backend/linuxfest-backend/.env.dev 30 | networks: 31 | - api-net 32 | - nginx-net 33 | depends_on: 34 | - mongo 35 | command: node ./src/index.js 36 | 37 | mongo: 38 | image: mongo:4.4 39 | container_name: mongodb 40 | volumes: 41 | - mongo-db:/data/db 42 | networks: 43 | - api-net 44 | environment: 45 | - MONGO_INITDB_ROOT_USERNAME=${MONGO_INITDB_ROOT_USERNAME} 46 | - MONGO_INITDB_ROOT_PASSWORD=${MONGO_INITDB_ROOT_PASSWORD} 47 | 48 | volumes: 49 | upload-files: 50 | mongo-db: 51 | 52 | networks: 53 | api-net: {} 54 | nginx-net: {} 55 | -------------------------------------------------------------------------------- /linuxfest-backend/.env.db.test: -------------------------------------------------------------------------------- 1 | MONGO_USER=alinow 2 | MONGO_PASSWORD=123456 3 | MONGO_INITDB_ROOT_USERNAME=alinow 4 | MONGO_INITDB_ROOT_PASSWORD=123456 -------------------------------------------------------------------------------- /linuxfest-backend/.env.dev.test: -------------------------------------------------------------------------------- 1 | MONGOURL=mongodb://localhost:27017/linux-fest-test 2 | PORT=5000 3 | BASEURL=api 4 | MAILHOST=smtp.gmail.com 5 | MAILUSER=mailuser 6 | MAILPASS=mailpass 7 | ALLOWED_HOSTS=http://localhost:8080 http://localhost:8081 8 | SIGN_TOKEN=randomstr 9 | SUPER_TOKEN_SIGN=randomstr 10 | SITE_VERSION=v_2022 11 | FRONTURL=http://localhost:8080 12 | ZARIN=merchantID 13 | RANDOM=123 14 | MONGO_USER=alinow 15 | MONGO_PASSWORD=123456 16 | MONGO_INITDB_ROOT_USERNAME=alinow 17 | MONGO_INITDB_ROOT_PASSWORD=123456 -------------------------------------------------------------------------------- /linuxfest-backend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16.10.0-alpine 2 | 3 | WORKDIR /app 4 | COPY package.json . 5 | 6 | ARG NODE_ENV 7 | RUN if [ "$NODE_ENV" = "production" ]; \ 8 | then npm install --only=production; \ 9 | else npm install; \ 10 | fi 11 | 12 | COPY . ./ 13 | 14 | 15 | ENV PORT 5000 16 | EXPOSE $PORT 17 | -------------------------------------------------------------------------------- /linuxfest-backend/mails/form.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 7 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 111 | 112 | 113 | 116 | 117 | 118 | 313 | 314 | 315 |
119 | 120 | 121 | 122 |
123 |
125 |
127 | 128 | 129 | 130 |
132 |
134 | 135 |
137 | 138 | 139 | 141 | 142 | 143 | 165 | 166 | 167 |
145 | 146 | 148 | 149 | 150 | 160 | 161 | 162 |
152 | 153 | 158 | 159 |
163 | 164 |
168 | 169 | 170 |
171 | 172 |
173 |
174 | 175 | 176 |
177 |
178 |
179 | 180 | 181 | 182 |
183 |
185 |
187 | 188 | 189 | 190 |
192 |
194 | 195 |
197 | 198 | 199 | 201 | 202 | 203 | 235 | 236 | 237 |
205 | 206 |
208 |

210 | با سلام و احترام؛

211 |

213 |  

214 |

216 | ثبت نام شما در سایت سیزدهمین دوره جشنواره لینوکس 217 | امیرکبیر با موفقیت انجام شد. 218 |

219 | @ceit_ssc 220 | 221 | 222 |

224 |  

225 |

227 | انجمن علمی دانشکده مهندسی کامپیوتر

228 |

230 | دانشگاه صنعتی امیرکبیر - تهران

231 | 232 |
233 | 234 |
238 | 239 | 240 |
241 | 242 |
243 |
244 | 245 | 246 |
247 |
248 |
249 | 250 | 251 | 252 |
253 |
255 |
257 | 258 | 259 | 260 |
262 |
264 | 265 |
267 | 268 | 269 | 271 | 272 | 273 | 295 | 296 | 297 |
275 | 276 | 278 | 279 | 280 | 290 | 291 | 292 |
282 | 283 | 288 | 289 |
293 | 294 |
298 | 299 | 300 |
301 | 302 |
303 |
304 | 305 | 306 |
307 |
308 |
309 | 310 | 311 | 312 |
316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | -------------------------------------------------------------------------------- /linuxfest-backend/mails/password.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 7 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 111 | 112 | 113 | 116 | 117 | 118 | 316 | 317 | 318 |
119 | 120 | 121 | 122 |
123 |
125 |
127 | 128 | 129 | 130 |
132 |
134 | 135 |
137 | 138 | 139 | 141 | 142 | 143 | 165 | 166 | 167 |
145 | 146 | 148 | 149 | 150 | 160 | 161 | 162 |
152 | 153 | 158 | 159 |
163 | 164 |
168 | 169 | 170 |
171 | 172 |
173 |
174 | 175 | 176 |
177 |
178 |
179 | 180 | 181 | 182 |
183 |
185 |
187 | 188 | 189 | 190 |
192 |
194 | 195 |
197 | 198 | 199 | 201 | 202 | 203 | 238 | 239 | 240 |
205 | 206 |
208 |

210 | با سلام و احترام؛

211 |

213 |  

214 |

216 | برای تغییر رمز عبور خود میتوانید از لینک زیر استفاده کنید: 217 |

218 | Reset Password 219 |
220 |
221 | @ceit_ssc 223 | 224 | 225 |

227 |  

228 |

230 | انجمن علمی دانشکده مهندسی کامپیوتر

231 |

233 | دانشگاه صنعتی امیرکبیر - تهران

234 | 235 |
236 | 237 |
241 | 242 | 243 |
244 | 245 |
246 |
247 | 248 | 249 |
250 |
251 |
252 | 253 | 254 | 255 |
256 |
258 |
260 | 261 | 262 | 263 |
265 |
267 | 268 |
270 | 271 | 272 | 274 | 275 | 276 | 298 | 299 | 300 |
278 | 279 | 281 | 282 | 283 | 293 | 294 | 295 |
285 | 286 | 291 | 292 |
296 | 297 |
301 | 302 | 303 |
304 | 305 |
306 |
307 | 308 | 309 |
310 |
311 |
312 | 313 | 314 | 315 |
319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | -------------------------------------------------------------------------------- /linuxfest-backend/mails/register.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 7 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 111 | 112 | 113 | 116 | 117 | 118 | 313 | 314 | 315 |
119 | 120 | 121 | 122 |
123 |
125 |
127 | 128 | 129 | 130 |
132 |
134 | 135 |
137 | 138 | 139 | 141 | 142 | 143 | 165 | 166 | 167 |
145 | 146 | 148 | 149 | 150 | 160 | 161 | 162 |
152 | 153 | 158 | 159 |
163 | 164 |
168 | 169 | 170 |
171 | 172 |
173 |
174 | 175 | 176 |
177 |
178 |
179 | 180 | 181 | 182 |
183 |
185 |
187 | 188 | 189 | 190 |
192 |
194 | 195 |
197 | 198 | 199 | 201 | 202 | 203 | 235 | 236 | 237 |
205 | 206 |
208 |

210 | با سلام و احترام؛

211 |

213 |  

214 |

216 | ثبت نام شما در سایت سیزدهمین دوره جشنواره لینوکس امیرکبیر با موفقیت انجام شد. 217 |

218 | @ceit_ssc 220 | 221 | 222 |

224 |  

225 |

227 | انجمن علمی دانشکده مهندسی کامپیوتر

228 |

230 | دانشگاه صنعتی امیرکبیر - تهران

231 | 232 |
233 | 234 |
238 | 239 | 240 |
241 | 242 |
243 |
244 | 245 | 246 |
247 |
248 |
249 | 250 | 251 | 252 |
253 |
255 |
257 | 258 | 259 | 260 |
262 |
264 | 265 |
267 | 268 | 269 | 271 | 272 | 273 | 295 | 296 | 297 |
275 | 276 | 278 | 279 | 280 | 290 | 291 | 292 |
282 | 283 | 288 | 289 |
293 | 294 |
298 | 299 | 300 |
301 | 302 |
303 |
304 | 305 | 306 |
307 |
308 |
309 | 310 | 311 | 312 |
316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | -------------------------------------------------------------------------------- /linuxfest-backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "linuxfest-backend", 3 | "version": "1.0.0", 4 | "description": "AUT-CE-SSC LinuxFest website backend", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest", 8 | "dev": "env-cmd -f ./.env.dev nodemon ./src/index.js", 9 | "dev_db": "..\\mongodb\\bin\\mongod.exe --dbpath ..\\mongodb-data", 10 | "start": "env-cmd -f ./.env.dev node ./src/index.js" 11 | }, 12 | "devDependencies": { 13 | "env-cmd": "^10.0.1", 14 | "eslint": "^8.10.0", 15 | "jest": "^27.5.1", 16 | "nodemon": "^2.0.15" 17 | }, 18 | "dependencies": { 19 | "@fast-csv/format": "^4.3.5", 20 | "acorn": "^7.4.1", 21 | "archiver": "^5.3.0", 22 | "axios": "^0.26.0", 23 | "bcryptjs": "^2.4.3", 24 | "cors": "^2.8.5", 25 | "crypto-js": "^3.3.0", 26 | "dotenv": "^8.2.0", 27 | "express": "^4.17.1", 28 | "jimp": "^0.16.1", 29 | "jsonwebtoken": "^8.5.1", 30 | "mongoose": "^5.11.14", 31 | "multer": "^1.4.2", 32 | "nodemailer": "^6.7.2", 33 | "persianize": "^1.0.2", 34 | "sharp": "^0.30.2", 35 | "validator": "^13.7.0", 36 | "winston": "^3.6.0", 37 | "zarinpal-checkout": "^0.2.7" 38 | }, 39 | "jest": { 40 | "testEnvironment": "node" 41 | }, 42 | "repository": { 43 | "type": "git", 44 | "url": "git+https://github.com/Mahanmmi/LinuxFest-WebSite.git" 45 | }, 46 | "author": "", 47 | "license": "ISC" 48 | } 49 | -------------------------------------------------------------------------------- /linuxfest-backend/src/app.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const cors = require('cors'); 3 | const { ALLOWED_HOSTS, BASEURL } = require('./config/index.js'); 4 | 5 | const app = express(); 6 | 7 | app.use(cors()); 8 | app.use(express.json()); 9 | app.use(express.urlencoded({ extended: false })); 10 | 11 | 12 | app.get("/ping", (req, res) => { 13 | res.send("PONG"); 14 | }); 15 | 16 | 17 | //================================== Routes ==================================\\ 18 | app.use(`/${BASEURL}/almightyone`, require('./routers/superuser')); 19 | app.use(`/${BASEURL}/users`, require('./routers/user')); 20 | app.use(`/${BASEURL}/workshops`, require('./routers/workshop')); 21 | app.use(`/${BASEURL}/teachers`, require('./routers/teacher')); 22 | app.use(`/${BASEURL}/discounts`, require('./routers/discount')); 23 | app.use(`/${BASEURL}/statics`, require('./routers/static')); 24 | app.use(`/${BASEURL}/companies`, require('./routers/company')) 25 | app.use(`/${BASEURL}/overview`, require('./routers/files')) 26 | 27 | module.exports = app; -------------------------------------------------------------------------------- /linuxfest-backend/src/config/index.js: -------------------------------------------------------------------------------- 1 | 2 | require("dotenv").config(); 3 | const path = require('path'); 4 | 5 | const { NODE_ENV, MONGOURL: env_mongo_url } = process.env; 6 | let MONGO_URL; 7 | 8 | if (NODE_ENV === 'production') { 9 | const MONGO_IP = process.env.MONGO_IP || "mongo", 10 | MONGO_PORT = process.env.MONGO_PORT || 27017, 11 | MONGO_USER = process.env.MONGO_USER, 12 | MONGO_PASSWORD = process.env.MONGO_PASSWORD; 13 | 14 | MONGO_URL = `mongodb://${MONGO_USER}:${MONGO_PASSWORD}@${MONGO_IP}:${MONGO_PORT}/linux-fest?authSource=admin`; 15 | } else { 16 | MONGO_URL = env_mongo_url; 17 | } 18 | 19 | module.exports = { 20 | BASEURL: process.env.BASEURL, 21 | PORT: process.env.PORT, 22 | MONGOURL: MONGO_URL, 23 | ALLOWED_HOSTS: String(process.env.ALLOWED_HOSTS).trim().split(" "), 24 | SUPER_TOKEN_SIGN: process.env.SUPER_TOKEN_SIGN, 25 | SIGN_TOKEN: process.env.SIGN_TOKEN, 26 | SITE_VERSION: process.env.SITE_VERSION, 27 | FRONTURL: process.env.FRONTURL, 28 | MAILHOST: process.env.MAILHOST, 29 | MAILUSER: process.env.MAILUSER, 30 | MAILPASS: process.env.MAILPASS, 31 | ZARIN: process.env.ZARIN, 32 | RANDOM: parseInt(process.env.RANDOM), 33 | workingDir: process.env.PWD, 34 | UPLOAD_PATH: path.join(process.env.PWD, "uploads", process.env.SITE_VERSION) 35 | } -------------------------------------------------------------------------------- /linuxfest-backend/src/config/logger.js: -------------------------------------------------------------------------------- 1 | const { createLogger, format, transports, config } = require('winston'); 2 | 3 | const logConfiguration = { 4 | // levels: config.syslog.levels, 5 | transports: 6 | [ 7 | new transports.File({ 8 | level: 'info', 9 | filename: `${__dirname}/../logs/server.log`, 10 | format: format.combine( 11 | format.timestamp({ format: 'MMM-DD-YYYY HH:mm:ss' }), 12 | format.align(), 13 | format.printf(info => `${info.level}: ${[info.timestamp]}: ${info.message}`), 14 | ) 15 | }), 16 | new transports.Console({ 17 | level: 'info', 18 | format: format.combine( 19 | format.timestamp({ format: 'MMM-DD-YYYY HH:mm:ss' }), 20 | format.align(), 21 | format.printf(info => `${info.level}: ${[info.timestamp]}: ${info.message}`), 22 | ) 23 | }), 24 | ], 25 | } 26 | 27 | const logger = createLogger(logConfiguration); 28 | 29 | module.exports = logger; -------------------------------------------------------------------------------- /linuxfest-backend/src/db/mongoose.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const { MONGOURL } = require('./../config/index.js'); 3 | 4 | const connectDB = async () => { 5 | try { 6 | await mongoose.connect(`${MONGOURL}`, { 7 | useNewUrlParser: true, 8 | useCreateIndex: true, 9 | useUnifiedTopology: true 10 | }) 11 | 12 | return Promise.resolve(); 13 | } 14 | catch (ex) { 15 | return Promise.reject(ex); 16 | } 17 | } 18 | 19 | module.exports = connectDB; -------------------------------------------------------------------------------- /linuxfest-backend/src/express_middlewares/adminAuth.js: -------------------------------------------------------------------------------- 1 | const SuperUser = require('../models/SuperUser'); 2 | const jwt = require('jsonwebtoken'); 3 | const {SUPER_TOKEN_SIGN, SIGN_TOKEN} = require('./../config/index.js') 4 | async function authenticateCreateAdmin(req, res, next) { 5 | try { 6 | console.log("Number of admins: ", (await SuperUser.find()).length); 7 | const superManPermissions = [ 8 | { permission: "getUser" }, 9 | { permission: "addUser" }, 10 | { permission: "editUser" }, 11 | { permission: "deleteUser" }, 12 | { permission: "getWorkshop" }, 13 | { permission: "addWorkshop" }, 14 | { permission: "editWorkshop" }, 15 | { permission: "deleteWorkshop" }, 16 | { permission: "getTeacher" }, 17 | { permission: "addTeacher" }, 18 | { permission: "editTeacher" }, 19 | { permission: "deleteTeacher" }, 20 | { permission: "addStatic" }, 21 | { permission: "editStatic" }, 22 | { permission: "deleteStatic" }, 23 | { permission: "addCompany" }, 24 | { permission: "editCompany" }, 25 | { permission: "deleteCompany" }, 26 | { permission: "getAdmin" }, 27 | { permission: "addAdmin" }, 28 | { permission: "editAdmin" }, 29 | { permission: "deleteAdmin" }, 30 | ]; 31 | 32 | if ((await SuperUser.find()).length === 0) { 33 | const token = req.header('Authorization').replace('Bearer ', ''); 34 | if (token === SUPER_TOKEN_SIGN) { 35 | const admin = new SuperUser({ 36 | permissions: superManPermissions 37 | }); 38 | console.log(admin) 39 | req.newAdmin = admin; 40 | next(); 41 | } else { 42 | throw new Error(); 43 | } 44 | } else { 45 | console.log(req.body.admin.permissions) 46 | await authenticateAdmin(req, res, () => { 47 | const admin = new SuperUser({ 48 | permissions: req.body.admin.permissions 49 | }); 50 | req.newAdmin = admin; 51 | 52 | next(); 53 | }); 54 | } 55 | } catch (err) { 56 | res.status(401).send({ error: err.message }); 57 | } 58 | } 59 | async function authCheckAdmin(req) { 60 | try { 61 | const token = req.header('Authorization').replace('Bearer ', ''); 62 | const decoded = jwt.verify(token, SIGN_TOKEN); 63 | const admin = await SuperUser.findOne({ _id: decoded._id, 'tokens.token': token }); 64 | 65 | if (!admin) { 66 | return false; 67 | } 68 | 69 | // eslint-disable-next-line require-atomic-updates 70 | req.token = token; 71 | // eslint-disable-next-line require-atomic-updates 72 | req.admin = admin; 73 | 74 | return true; 75 | } catch (err) { 76 | return false; 77 | } 78 | } 79 | 80 | async function authenticateAdmin(req, res, next) { 81 | if (await authCheckAdmin(req)) { 82 | next(); 83 | } else { 84 | res.status(401).send({ error: 'Please authenticate as admin' }); 85 | } 86 | } 87 | 88 | module.exports = { 89 | authenticateCreateAdmin, 90 | authenticateAdmin, 91 | }; -------------------------------------------------------------------------------- /linuxfest-backend/src/express_middlewares/userAuth.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | const User = require('../models/User'); 3 | const {SIGN_TOKEN} = require('./../config/index.js') 4 | async function authCheckUser(req) { 5 | try { 6 | const token = req.header('Authorization').replace('Bearer ', ''); 7 | const decoded = jwt.verify(token, SIGN_TOKEN); 8 | const user = await User.findOne({ _id: decoded._id, 'tokens.token': token }); 9 | 10 | if (!user) { 11 | return false; 12 | } 13 | 14 | // eslint-disable-next-line require-atomic-updates 15 | req.token = token; 16 | // eslint-disable-next-line require-atomic-updates 17 | req.user = user; 18 | return true; 19 | } catch (error) { 20 | return false; 21 | } 22 | } 23 | 24 | const userAuth = async (req, res, next) => { 25 | if (await authCheckUser(req)) { 26 | next(); 27 | } else { 28 | res.status(401).send('Please authenticate'); 29 | } 30 | } 31 | 32 | module.exports = userAuth; -------------------------------------------------------------------------------- /linuxfest-backend/src/index.js: -------------------------------------------------------------------------------- 1 | 2 | const app = require('./app'); 3 | const connectDB = require('./db/mongoose.js'); 4 | const { PORT } = require('./config/index.js'); 5 | const logger = require('./config/logger.js'); 6 | 7 | let attempt = 0; 8 | const MAX_ATTEMPT = 5; 9 | 10 | const runAPI = () => { 11 | attempt += 1; 12 | 13 | connectDB().then(() => { 14 | logger.info('DB connected'); 15 | 16 | app.listen(PORT, () => { 17 | logger.info('Server is up and running on port: ' + PORT); 18 | }); 19 | }).catch((err)=>{ 20 | logger.error(`${err} mongoDB didn't connect!`); 21 | if (attempt < MAX_ATTEMPT) { 22 | setTimeout(runAPI, 10000); 23 | } else { 24 | logger.error("Exiting the process!"); 25 | process.exit(1); 26 | } 27 | }) 28 | } 29 | 30 | 31 | runAPI() -------------------------------------------------------------------------------- /linuxfest-backend/src/models/Company.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const {SITE_VERSION} = require('./../config/index.js') 3 | 4 | const schema = new mongoose.Schema({ 5 | name: { 6 | type: String, 7 | required: true 8 | }, 9 | description: { 10 | type: String, 11 | required: true 12 | }, 13 | logo:{ 14 | type: String 15 | }, 16 | job_opportunities:[{ 17 | jobName: { 18 | type: String, 19 | required: true 20 | }, 21 | jobDescription: { 22 | type: String, 23 | required: true 24 | }, 25 | jlink: { 26 | type: String, 27 | required: true 28 | } 29 | }] 30 | }, { 31 | timestamps: true 32 | }); 33 | 34 | 35 | 36 | schema.methods.toJSON = function () { 37 | const company = this; 38 | const companyObject = company.toObject(); 39 | 40 | const url = `/uploads/${SITE_VERSION}/companies/${companyObject._id}/companyLogo.png`; 41 | if (companyObject.logo) { 42 | delete companyObject.logo; 43 | companyObject.picUrl = url; 44 | } 45 | return companyObject; 46 | }; 47 | 48 | 49 | 50 | const Company = new mongoose.model("Company", schema); 51 | 52 | module.exports = Company; -------------------------------------------------------------------------------- /linuxfest-backend/src/models/Discount.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const schema = mongoose.Schema({ 4 | percentage: { 5 | type: Number, 6 | required: true, 7 | validate(value) { 8 | if(value > 100 || value < 0){ 9 | throw new Error(); 10 | } 11 | } 12 | }, 13 | code: { 14 | type: String, 15 | required: true, 16 | unique: true 17 | }, 18 | users: [{ 19 | user: { 20 | type: mongoose.Types.ObjectId, 21 | required: true, 22 | }, 23 | isUsed: { 24 | type: Boolean, 25 | default: false 26 | }, 27 | }], 28 | count: { 29 | type: Number, 30 | required: true 31 | } 32 | }, { 33 | timestamps: true 34 | }); 35 | 36 | 37 | schema.statics.findByCode = async function (code,userid) { 38 | var discount = await Discount.findOne({$and:[{ code }, { users : { $elemMatch : { user:userid , isUsed:false} } }]}); 39 | return discount; 40 | }; 41 | 42 | const Discount = new mongoose.model('Discount', schema); 43 | 44 | module.exports = Discount; -------------------------------------------------------------------------------- /linuxfest-backend/src/models/Static.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const schema = new mongoose.Schema({ 4 | name: { 5 | type: String, 6 | required: true, 7 | unique: true 8 | }, 9 | type: { 10 | type: String, 11 | required: true 12 | }, 13 | description: { 14 | type: String, 15 | required: true 16 | } 17 | }, { 18 | timestamps: true 19 | }); 20 | 21 | const Static = new mongoose.model("Static", schema); 22 | 23 | module.exports = Static; -------------------------------------------------------------------------------- /linuxfest-backend/src/models/SuperUser.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const bcrypt = require('bcryptjs'); 3 | const jwt = require('jsonwebtoken'); 4 | const { SIGN_TOKEN } = require('./../config/index.js') 5 | 6 | const superUserSchema = new mongoose.Schema({ 7 | username: { 8 | type: String, 9 | required: true, 10 | unique: true, 11 | trim: true, 12 | toLower: true, 13 | minlength: 5 14 | }, 15 | password: { 16 | type: String, 17 | required: true 18 | }, 19 | permissions: [{ 20 | permission: { 21 | type: String, 22 | required: true 23 | } 24 | }], 25 | tokens: [{ 26 | token: { 27 | type: String, 28 | required: true 29 | } 30 | }] 31 | }, { 32 | timestamps: true 33 | }); 34 | 35 | superUserSchema.statics.findByCredentials = async (username, password) => { 36 | const admin = await SuperUser.findOne({ username }); 37 | 38 | if (!admin) { 39 | throw new Error('Unable to login'); 40 | } 41 | 42 | const isMatch = await bcrypt.compare(password, admin.password); 43 | 44 | if (!isMatch) { 45 | throw new Error('Unable to login'); 46 | } 47 | 48 | return admin; 49 | } 50 | 51 | superUserSchema.methods.generateAuthToken = async function () { 52 | const admin = this; 53 | const token = jwt.sign({ _id: admin._id.toString() }, SIGN_TOKEN, { expiresIn: '7 days' }); 54 | 55 | admin.tokens = admin.tokens.concat({ token }); 56 | await admin.save(); 57 | 58 | return token; 59 | } 60 | 61 | superUserSchema.pre('save', async function (next) { 62 | const superUser = this; 63 | 64 | if (superUser.isModified('password')) { 65 | superUser.password = await bcrypt.hash(superUser.password, 8); 66 | } 67 | 68 | next(); 69 | }); 70 | 71 | superUserSchema.methods.toJSON = function () { 72 | const admin = this; 73 | const adminObject = admin.toObject(); 74 | 75 | delete adminObject.password; 76 | delete adminObject.tokens; 77 | 78 | return adminObject; 79 | } 80 | 81 | const SuperUser = new mongoose.model('SuperUser', superUserSchema); 82 | 83 | module.exports = SuperUser; -------------------------------------------------------------------------------- /linuxfest-backend/src/models/Teacher.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const persianize = require('persianize'); 3 | const { BASEURL } = require('./../config/index.js') 4 | 5 | const schema = mongoose.Schema({ 6 | fullName: { 7 | type: String, 8 | required: true, 9 | unique: true, 10 | trim: true, 11 | validate(values) { 12 | for (const value of values.split(' ')) { 13 | if (!persianize.validator().alpha(value)) { 14 | throw new Error('نام معتبر نمیباشد') 15 | } 16 | } 17 | } 18 | }, 19 | fullName_en: { 20 | type: String, 21 | required: true, 22 | trim: true, 23 | }, 24 | affiliation: { 25 | type: String 26 | }, 27 | affiliation_en: { 28 | type: String 29 | }, 30 | degree: { 31 | type: String 32 | }, 33 | degree_en: { 34 | type: String 35 | }, 36 | description: { 37 | type: String 38 | }, 39 | description_en: { 40 | type: String 41 | }, 42 | lecture_title: { 43 | type: String 44 | }, 45 | lecture_title_en: { 46 | type: String 47 | }, 48 | lecture_abstract: { 49 | type: String 50 | }, 51 | lecture_abstract_en: { 52 | type: String 53 | }, 54 | imagePath: { 55 | type: String 56 | }, 57 | resume: { 58 | type: String 59 | } 60 | }, { 61 | timestamps: true 62 | }); 63 | 64 | schema.virtual('workshops', { 65 | ref: 'Workshop', 66 | localField: '_id', 67 | foreignField: 'teachers.teacher' 68 | }) 69 | 70 | schema.methods.toJSON = function () { 71 | const teacher = this; 72 | const teacherObject = teacher.toObject(); 73 | 74 | const url = `/${BASEURL}/teachers/pic/${teacherObject._id}`; 75 | if (teacherObject.imagePath) { 76 | delete teacherObject.imagePath; 77 | teacherObject.picUrl = url; 78 | } 79 | 80 | const resume_url = `/${BASEURL}/teachers/resume/${teacherObject._id}`; 81 | if (teacherObject.resume) { 82 | delete teacherObject.imagePath; 83 | teacherObject.resume = resume_url; 84 | } 85 | 86 | return teacherObject; 87 | }; 88 | 89 | const Teacher = new mongoose.model('Teacher', schema); 90 | 91 | module.exports = Teacher; -------------------------------------------------------------------------------- /linuxfest-backend/src/models/User.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const validator = require("validator"); 3 | const bcrypt = require("bcryptjs"); 4 | const jwt = require("jsonwebtoken"); 5 | const persianize = require("persianize"); 6 | const { SIGN_TOKEN } = require('./../config/index.js') 7 | 8 | const schema = new mongoose.Schema({ 9 | firstName: { 10 | type: String, 11 | required: true, 12 | trim: true, 13 | validate(value) { 14 | const parts = value.split(" "); 15 | for (const part of parts) { 16 | if (!persianize.validator().alpha(part)) { 17 | throw new Error('نام معتبر نمیباشد') 18 | } 19 | } 20 | } 21 | }, 22 | lastName: { 23 | type: String, 24 | required: true, 25 | trim: true, 26 | validate(value) { 27 | const parts = value.split(" "); 28 | for (const part of parts) { 29 | if (!persianize.validator().alpha(part)) { 30 | throw new Error('نام خانوادگی معتبر نمیباشد') 31 | } 32 | } 33 | } 34 | }, 35 | email: { 36 | type: String, 37 | required: true, 38 | unique: true, 39 | trim: true, 40 | lowercase: true, 41 | validate(value) { 42 | if (!validator.isEmail(value)) { 43 | throw new Error("ایمیل معتبر نمی‌باشد"); 44 | } 45 | } 46 | }, 47 | password: { 48 | type: String, 49 | required: true, 50 | minlength: 8, 51 | validate(value) { 52 | if (validator.isAlpha(value) || validator.isNumeric(value)) { 53 | throw new Error("رمزعبور ضعیف می‌باشد"); 54 | } 55 | } 56 | }, 57 | phoneNumber: { 58 | type: String, 59 | minlength: 11, 60 | maxlength: 12, 61 | validate(value) { 62 | if (!validator.isNumeric(value) || !value.startsWith("09")) { 63 | throw new Error("شماره تماس نامعتبر است"); 64 | } 65 | }, 66 | required: true 67 | }, 68 | studentNumber: { 69 | type: String, 70 | minlength: 7, 71 | maxlength: 8, 72 | unique: true, 73 | sparse: true, 74 | validate(value) { 75 | if (!validator.isNumeric(value)) { 76 | throw new Error("شماره دانشجویی نامعتبر است"); 77 | } 78 | } 79 | }, 80 | age: { 81 | type: Number, 82 | min: 16, 83 | max: 99 84 | }, 85 | tokens: [ 86 | { 87 | token: { 88 | type: String, 89 | required: true 90 | } 91 | } 92 | ], 93 | workshops: [{ 94 | workshop: { 95 | type: mongoose.Types.ObjectId, 96 | required: true 97 | } 98 | }], 99 | forgotTokens: [{ 100 | forgotToken: { 101 | type: String, 102 | required: true 103 | } 104 | }], 105 | orderIDs: [{ 106 | idNumber: { 107 | type: Number, 108 | required: true, 109 | unique: true, 110 | sparse: true 111 | }, 112 | workshopId: [{ 113 | type: mongoose.Types.ObjectId, 114 | required: true 115 | }] 116 | }], 117 | orders: [{ 118 | ResCode: { 119 | type: Number, 120 | required: true 121 | }, 122 | Amount: { 123 | type: Number, 124 | required: true 125 | }, 126 | OrderId: { 127 | type: Number, 128 | required: true 129 | }, 130 | Authority:{ 131 | type: String, 132 | required: true 133 | }, 134 | WorkshopIds: [{ 135 | type: mongoose.Types.ObjectId, 136 | required: true 137 | }] 138 | }] 139 | }, { 140 | timestamps: true 141 | }); 142 | 143 | 144 | 145 | schema.statics.findByCredentials = async function (email, password) { 146 | const user = await User.findOne({ email }); 147 | 148 | if (!user || !(await bcrypt.compare(password, user.password))) { 149 | throw new Error("اطلاعات واردشده معتبر نمی‌باشد"); 150 | } 151 | 152 | 153 | return user; 154 | }; 155 | 156 | schema.methods.generateAuthToken = async function () { 157 | const user = this; 158 | const token = jwt.sign({ _id: user._id.toString() }, SIGN_TOKEN, { 159 | expiresIn: "7 days" 160 | }); 161 | 162 | user.tokens = user.tokens.concat({ token }); 163 | await user.save(); 164 | 165 | return token; 166 | }; 167 | 168 | schema.methods.generateForgotToken = async function (email) { 169 | const user = this; 170 | const forgotToken = jwt.sign({ email }, SIGN_TOKEN, { 171 | expiresIn: "2 days" 172 | }); 173 | user.forgotTokens = user.forgotTokens.concat({ forgotToken }); 174 | 175 | await user.save(); 176 | 177 | return forgotToken; 178 | }; 179 | 180 | schema.methods.toJSON = function () { 181 | const user = this; 182 | const userObject = user.toObject(); 183 | 184 | delete userObject.password; 185 | delete userObject.tokens; 186 | delete userObject.forgotTokens; 187 | // delete userObject.orderIDs; 188 | 189 | return userObject; 190 | }; 191 | 192 | schema.pre("save", async function (next) { 193 | const user = this; 194 | 195 | if (user.isModified("password")) { 196 | user.password = await bcrypt.hash(user.password, 8); 197 | } 198 | next(); 199 | }); 200 | 201 | const User = new mongoose.model("User", schema); 202 | 203 | module.exports = User; 204 | -------------------------------------------------------------------------------- /linuxfest-backend/src/models/Workshop.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Teacher = require('./Teacher'); 3 | const { SITE_VERSION } = require('./../config/index.js') 4 | 5 | const schema = mongoose.Schema({ 6 | capacity: { 7 | type: Number, 8 | required: true, 9 | validate(value) { 10 | if (value < 0) { 11 | throw new Error('ظرفیت نمیتواند منفی باشد'); 12 | } 13 | } 14 | }, 15 | title: { 16 | type: String, 17 | required: true, 18 | trim: true 19 | }, 20 | price: { 21 | type: Number, 22 | required: true 23 | }, 24 | isRegOpen: { 25 | type: Boolean, 26 | default: false 27 | }, 28 | picPath: { 29 | type: String 30 | }, 31 | album: [{ 32 | albumPicPath: { 33 | type: String 34 | } 35 | }], 36 | description: { 37 | type: String, 38 | required: true 39 | }, 40 | prerequisites: { 41 | type: String, 42 | }, 43 | times: [{ 44 | startTime: { 45 | type: Date, 46 | required: true 47 | }, 48 | endTime: { 49 | type: Date, 50 | required: true 51 | } 52 | }], 53 | teachers: [{ 54 | id: { 55 | type: mongoose.Types.ObjectId, 56 | required: true 57 | }, 58 | name: { 59 | type: String, 60 | } 61 | }] 62 | }); 63 | 64 | schema.virtual('participants', { 65 | ref: 'User', 66 | localField: '_id', 67 | foreignField: 'workshops.workshop' 68 | }); 69 | 70 | schema.virtual('participantsCount').get(async function() { 71 | // console.log(await this.populate('participants').execPopulate().participants); 72 | return (await this.populate('participants').execPopulate()).participants.length; 73 | }); 74 | 75 | schema.pre("save", async function (next) { 76 | const workshop = this; 77 | 78 | if (workshop.isModified("teachers")) { 79 | for (const obj of workshop.teachers) { 80 | const id = obj.id; 81 | const teacher = await Teacher.findById(id); 82 | obj.name = teacher.fullName 83 | } 84 | } 85 | if (!this.isNew) { 86 | if ((await workshop.participantsCount) >= workshop.capacity) { 87 | workshop.isRegOpen = false; 88 | } 89 | } 90 | next(); 91 | }); 92 | 93 | schema.methods.toJSON = function () { 94 | const workshop = this; 95 | const workshopObject = workshop.toObject(); 96 | //TODO: FIX HERE TO GET WORKSHOP PICTURE 97 | const url = `/uploads/${SITE_VERSION}/workshops/${workshopObject._id}`; 98 | if (workshopObject.picPath) { 99 | workshopObject.picUrl = url + '/mainPic.png'; 100 | } 101 | if (workshopObject.album.length) { 102 | workshopObject.albumUrls = []; 103 | for (const pic of workshop.album) { 104 | workshopObject.albumUrls = workshopObject.albumUrls.concat(url + "/album/" + pic._id + ".png"); 105 | } 106 | } 107 | 108 | delete workshopObject.picPath; 109 | delete workshopObject.album; 110 | return workshopObject; 111 | }; 112 | 113 | const Workshop = new mongoose.model('Workshop', schema); 114 | 115 | module.exports = Workshop; -------------------------------------------------------------------------------- /linuxfest-backend/src/routers/company.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const multer = require('multer'); 3 | const path = require('path'); 4 | const sharp = require('sharp'); 5 | const Jimp = require('jimp'); 6 | 7 | const fs = require('fs'); 8 | const Company = require('../models/Company'); 9 | const { checkPermission } = require('../utils/utils'); 10 | const { authenticateAdmin } = require('../express_middlewares/adminAuth'); 11 | const { UPLOAD_PATH } = require('./../config/index.js'); 12 | 13 | const router = new express.Router(); 14 | 15 | router.post("/", authenticateAdmin, async (req, res) => { 16 | try { 17 | if (!checkPermission(req.admin, 'addCompany', res)) { 18 | return; 19 | } 20 | 21 | const company = new Company(req.body); 22 | await company.save(); 23 | res.status(201).send(company); 24 | } catch (err) { 25 | res.status(400).send(err.message); 26 | } 27 | }); 28 | 29 | router.patch("/manage/insert/:id",authenticateAdmin,async (req, res)=>{ 30 | try { 31 | if (!checkPermission(req.admin, 'editCompany', res)) { 32 | return; 33 | } 34 | 35 | const company = await Company.findById(req.params.id); 36 | 37 | if (!company) { 38 | res.status(404).send({message:"Company Not Found!"}); 39 | return; 40 | } 41 | 42 | company.job_opportunities = company.job_opportunities.concat(res.body.job_opportunities) 43 | try { 44 | await company.save(); 45 | res.send(company); 46 | return; 47 | } catch (err){ 48 | res.status(400).send(err.message); 49 | return; 50 | } 51 | } catch (err) { 52 | res.status(500).send(err.message); 53 | } 54 | }) 55 | 56 | router.get("/", async (req, res) => { 57 | try { 58 | const companies = await Company.find(); 59 | res.send(companies); 60 | } catch (err) { 61 | res.status(500).send(err.message); 62 | } 63 | }); 64 | 65 | router.patch("/manage/:id",authenticateAdmin, async (req, res)=>{ 66 | try { 67 | if (!checkPermission(req.admin, 'editCompany', res)) { 68 | return; 69 | } 70 | 71 | const company = await Company.findById(req.params.id); 72 | 73 | if (!company) { 74 | res.status(404).send({message:"Company Not Found!"}); 75 | return; 76 | } 77 | 78 | const updates = Object.keys(req.body); 79 | let allowedUpdates = ['name', 'description', 'logo','job_opportunities']; 80 | updates.forEach((update) => { 81 | if (allowedUpdates.includes(update)) { 82 | company[update] = req.body[update]; 83 | } 84 | }); 85 | try { 86 | await company.save(); 87 | res.send(company); 88 | return; 89 | } catch (err){ 90 | res.status(400).send(err.message); 91 | return; 92 | } 93 | } catch (err) { 94 | res.status(500).send(err.message); 95 | } 96 | }) 97 | 98 | router.delete("/manage/:id",authenticateAdmin, async (req, res)=>{ 99 | try { 100 | if (!checkPermission(req.admin, 'deleteCompany', res)) { 101 | return; 102 | } 103 | const company = await Company.findById(req.params.id); 104 | if (!company) { 105 | res.status(404).send({message:"Company Not Found"}); 106 | return; 107 | } 108 | await Company.deleteOne(company); 109 | res.status(204).end() 110 | } catch (err) { 111 | res.status(400).send({ error: err.message }); 112 | } 113 | }) 114 | 115 | 116 | router.get("/find/:id", async (req, res) => { 117 | try { 118 | const company = await Company.findById(req.params.id); 119 | if (!company) { 120 | res.status(404).send({message:"Company Not Found"}); 121 | return; 122 | } 123 | res.status(200).send(company); 124 | } catch (err) { 125 | res.status(500).send(err.message); 126 | } 127 | }); 128 | 129 | 130 | router.patch("/find", authenticateAdmin, async (req, res) => { 131 | try { 132 | if (!checkPermission(req.admin, 'editCompany', res)) { 133 | return; 134 | } 135 | const name = req.query.name; 136 | let company; 137 | if(!name){ 138 | res.status(400).send({message:"Wrong input"}) 139 | return; 140 | } 141 | company = await Company.findOne({name}) 142 | 143 | if (!company) { 144 | res.status(404).send({message:"Company Not Found!"}); 145 | return; 146 | } 147 | 148 | const updates = Object.keys(req.body); 149 | let allowedUpdates = ['name', 'description', 'logo','job_opportunities']; 150 | updates.forEach((update) => { 151 | if (allowedUpdates.includes(update)) { 152 | company[update] = req.body[update]; 153 | } 154 | }); 155 | try { 156 | await company.save(); 157 | res.send(company); 158 | return; 159 | } catch (err){ 160 | res.status(400).send(err.message); 161 | return; 162 | } 163 | } catch (err) { 164 | res.status(500).send(err.message); 165 | } 166 | }); 167 | 168 | router.delete("/find", authenticateAdmin, async (req, res) => { 169 | try { 170 | if (!checkPermission(req.admin, 'deleteCompany', res)) { 171 | return; 172 | } 173 | const name = req.query.name; 174 | let company; 175 | if(!name){ 176 | res.status(400).send({message:"Wrong input"}) 177 | return; 178 | } 179 | company = await Company.findOne({name}) 180 | if (!company) { 181 | res.status(404).send({message:"Company Not Found"}); 182 | return; 183 | } 184 | await Company.deleteOne(company); 185 | res.status(204).end() 186 | } catch (err) { 187 | res.status(400).send({ error: err.message }); 188 | } 189 | }); 190 | 191 | 192 | 193 | 194 | const upload = multer({ 195 | limits: { 196 | fileSize: 10000000 197 | }, 198 | fileFilter(req, file, cb) { 199 | if (!file.originalname.match(/\.(jpg|jpeg|png)$/)) { 200 | cb(new Error('لطفا یک تصویر را آپلود کنید')); 201 | } 202 | cb(undefined, true); 203 | } 204 | }); 205 | 206 | 207 | //TODO : Add picture get route 208 | 209 | router.get('/pic/:id',async(req,res)=>{ 210 | try{ 211 | const filePath = path.join(UPLOAD_PATH, "companies", req.params.id, "companyLogo.png") 212 | if (fs.existsSync(filePath)) 213 | { 214 | return res.status(200).sendFile(filePath); 215 | } 216 | else 217 | { 218 | return res.status(404).send({message:"File Not Found"}) 219 | } 220 | }catch(error){ 221 | return res.status(400).send({message:"Internal error"}) 222 | } 223 | }) 224 | 225 | router.post('/pic/:id', authenticateAdmin, upload.single('companyLogo'), async (req, res) => { 226 | if (!checkPermission(req.admin, 'editCompany', res)) { 227 | return; 228 | } 229 | try { 230 | const company = await Company.findById(req.params.id); 231 | if (!company) { 232 | res.status(404).send({message:"Company Not Found!"}); 233 | return; 234 | } 235 | 236 | const filePath = path.join(UPLOAD_PATH, "companies", req.params.id); 237 | if (!fs.existsSync(filePath)) { 238 | fs.mkdirSync(filePath, { recursive: true }, (err) => { 239 | if (err) { 240 | throw new Error(err); 241 | } 242 | }); 243 | } 244 | 245 | // const buffer = await sharp(req.file.buffer).png().toBuffer(); 246 | // fs.writeFileSync(path.join(filePath, "companyLogo.png"), buffer, (err) => { 247 | // if (err) { 248 | // throw new Error(err); 249 | // } 250 | // }); 251 | 252 | const image = await Jimp.read(req.file.buffer) 253 | 254 | const imagePath = path.join(filePath, "companyLogo.png") 255 | await image.writeAsync(imagePath); // Returns Promise 256 | 257 | company.logo = imagePath 258 | await company.save(); 259 | 260 | res.status(200).send(company); 261 | } catch (error) { 262 | console.log(error) 263 | res.status(500).send({ error: error.message }); 264 | } 265 | }, (err, req, res) => { 266 | res.status(400).send({ error: err.message }); 267 | }); 268 | 269 | 270 | router.delete('/pic/:id', authenticateAdmin, async (req, res) => { 271 | if (!checkPermission(req.admin, 'editCompany', res)) { 272 | return; 273 | } 274 | 275 | try { 276 | const company = await Company.findById(req.params.id); 277 | 278 | if (!company || !company.logo) { 279 | res.status(404).send({message:"Not Found"}); 280 | return; 281 | } 282 | const filePath = path.join(UPLOAD_PATH, "companies", req.params.id, "companyLogo.png"); 283 | fs.unlink(filePath, (err) => { 284 | if (err) { 285 | console.log(err); 286 | } 287 | }); 288 | 289 | company.logo = ''; 290 | await company.save(); 291 | 292 | return res.send(company); 293 | } catch (err) { 294 | return res.status(500).send({ error: err.message }); 295 | } 296 | }); 297 | 298 | 299 | 300 | 301 | module.exports = router; -------------------------------------------------------------------------------- /linuxfest-backend/src/routers/discount.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | 4 | const Discount = require('../models/Discount'); 5 | 6 | const { checkPermission } = require('../utils/utils'); 7 | const { authenticateAdmin } = require('../express_middlewares/adminAuth'); 8 | 9 | const router = new express.Router(); 10 | 11 | router.get('/', authenticateAdmin, async (req, res) => { 12 | if (!checkPermission(req.admin, 'getWorkshop', res)) { 13 | return; 14 | } 15 | try { 16 | res.send((await Discount.find({}))); 17 | } catch (err) { 18 | res.status(500).send(err.message); 19 | } 20 | }); 21 | 22 | router.get('/:id',authenticateAdmin, async(req,res)=>{ 23 | if(!checkPermission(req.admin,'getWorkshop',res)){ 24 | return; 25 | } 26 | try { 27 | res.send((await Discount.findById(req.params.id))); 28 | } catch (err) { 29 | res.status(500).send(err.message); 30 | } 31 | }) 32 | 33 | router.post('/', authenticateAdmin, async (req, res) => { 34 | if (!checkPermission(req.admin, 'addWorkshop', res)) { 35 | return; 36 | } 37 | try { 38 | const discount = new Discount(req.body); 39 | await discount.save(); 40 | res.status(201).send(discount); 41 | } catch (err) { 42 | res.status(500).send(err.message); 43 | } 44 | }); 45 | 46 | router.patch('/:id', authenticateAdmin, async (req, res) => { 47 | if (!checkPermission(req.admin, 'editWorkshop', res)) { 48 | return; 49 | } 50 | const updates = Object.keys(req.body); 51 | const allowedUpdates = ['percentage', 'code', 'count','users']; 52 | const isValidOperation = updates.every((update) => allowedUpdates.includes(update)); 53 | if (!isValidOperation) { 54 | return res.status(400).send({ error: 'invalid updates' }); 55 | } 56 | try { 57 | const discount = await Discount.findById(req.params.id); 58 | updates.forEach((update) => discount[update] = req.body[update]); 59 | await discount.save(); 60 | res.send(discount); 61 | } catch (err) { 62 | res.status(400).send(err.message); 63 | } 64 | }); 65 | 66 | router.delete('/:id', authenticateAdmin, async (req, res) => { 67 | if (!checkPermission(req.admin, 'deleteWorkshop', res)) { 68 | return; 69 | } 70 | try { 71 | const discount = Discount.findById(req.params.id); 72 | await Discount.deleteOne(discount); 73 | res.status(204).end() 74 | } catch (err) { 75 | res.status(500).send(err.message); 76 | } 77 | }); 78 | 79 | 80 | module.exports = router; -------------------------------------------------------------------------------- /linuxfest-backend/src/routers/files.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const path = require('path') 3 | const router = express.Router() 4 | 5 | router.get('/',(req, res)=>{ 6 | res.sendFile(path.join(__dirname, '../docs/LinuxFest-2023.pdf')) 7 | }) 8 | 9 | module.exports = router -------------------------------------------------------------------------------- /linuxfest-backend/src/routers/static.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const Static = require('../models/Static'); 4 | const { checkPermission } = require('../utils/utils'); 5 | const { authenticateAdmin } = require('../express_middlewares/adminAuth'); 6 | 7 | const router = new express.Router(); 8 | 9 | router.post("/", authenticateAdmin, async (req, res) => { 10 | try { 11 | if (!checkPermission(req.admin, 'addStatic', res)) { 12 | return; 13 | } 14 | 15 | const static = new Static(req.body); 16 | await static.save(); 17 | res.status(201).send(static); 18 | } catch (err) { 19 | res.status(400).send(err.message); 20 | } 21 | }); 22 | 23 | router.get("/", async (req, res) => { 24 | try { 25 | const statics = await Static.find(); 26 | res.send(statics); 27 | } catch (err) { 28 | res.status(500).send(err.message); 29 | } 30 | }); 31 | 32 | router.get("/find", async (req, res) => { 33 | try { 34 | const id = req.query.id; 35 | const name = req.query.name; 36 | const type = req.query.type; 37 | let statics; 38 | if (id) { 39 | statics = await Static.findById(id); 40 | } else if (name) { 41 | statics = await Static.findOne({ name }); 42 | } else if (type) { 43 | statics = await Static.find({ type }); 44 | } 45 | if (!statics) { 46 | res.status(404).send(); 47 | return; 48 | } 49 | res.send(statics); 50 | } catch (err) { 51 | res.status(500).send(err.message); 52 | } 53 | }); 54 | 55 | router.patch("/find", authenticateAdmin, async (req, res) => { 56 | try { 57 | if (!checkPermission(req.admin, 'editStatic', res)) { 58 | return; 59 | } 60 | 61 | const id = req.query.id; 62 | const name = req.query.name; 63 | let static; 64 | if (id) { 65 | static = await Static.findById(id); 66 | } else if (name) { 67 | static = await Static.findOne({ name }); 68 | } 69 | if (!static) { 70 | res.status(404).send(); 71 | return; 72 | } 73 | 74 | const updates = Object.keys(req.body); 75 | let allowedUpdates = ['name', 'type', 'description']; 76 | updates.forEach((update) => { 77 | if (allowedUpdates.includes(update)) { 78 | static[update] = req.body[update]; 79 | } 80 | }); 81 | try { 82 | await static.save(); 83 | res.send(static); 84 | return; 85 | } catch (err){ 86 | res.status(400).send(err.message); 87 | return; 88 | } 89 | } catch (err) { 90 | res.status(500).send(err.message); 91 | } 92 | }); 93 | 94 | router.delete("/find", authenticateAdmin, async (req, res) => { 95 | try { 96 | if (!checkPermission(req.admin, 'deleteStatic', res)) { 97 | return; 98 | } 99 | 100 | const id = req.query.id; 101 | const name = req.query.name; 102 | let static; 103 | if (id) { 104 | static = await Static.findById(id); 105 | } else if (name) { 106 | static = await Static.findOne({ name }); 107 | } 108 | if (!static) { 109 | res.status(404).send(); 110 | return; 111 | } 112 | 113 | await Static.deleteOne(static); 114 | res.send(204).end(); 115 | } catch (err) { 116 | res.status(500).send(err.message); 117 | } 118 | }); 119 | 120 | module.exports = router; -------------------------------------------------------------------------------- /linuxfest-backend/src/routers/superuser.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const SuperUser = require('../models/SuperUser'); 4 | const Workshop = require('../models/Workshop'); 5 | const User = require('../models/User'); 6 | const { checkPermission,sendEmail } = require('../utils/utils'); 7 | const { 8 | authenticateCreateAdmin, 9 | authenticateAdmin 10 | } = require('../express_middlewares/adminAuth'); 11 | 12 | 13 | const router = new express.Router(); 14 | 15 | 16 | //TODO : FIX HERE 17 | router.post('/', authenticateCreateAdmin, async (req, res) => { 18 | console.log('ok') 19 | try { 20 | 21 | if (req.admin && !checkPermission(req.admin, 'addAdmin', res)) { 22 | return; 23 | } 24 | if (req.admin && !req.body.admin.permissions.every(perm => typeof perm === 'string')) { 25 | res.status(400).send(); 26 | return; 27 | } 28 | 29 | req.newAdmin.username = req.body.username; 30 | req.newAdmin.password = req.body.password; 31 | if (req.admin) { 32 | const forbiddenPerms = ['addAdmin', 'editAdmin', 'deleteAdmin', 'getAdmin']; 33 | const perms = req.body.admin.permissions.filter((element) => { 34 | return !forbiddenPerms.includes(element); 35 | }); 36 | req.newAdmin.permissions = perms.map(element => { return { permission: element } }); 37 | console.log(req.newAdmin.permissions) 38 | } 39 | await req.newAdmin.save(); 40 | 41 | const token = await req.newAdmin.generateAuthToken(); 42 | 43 | res.status(201).send({ admin: req.newAdmin, token }); 44 | } catch (err) { 45 | res.status(500).send({ error: err.message }); 46 | } 47 | }); 48 | 49 | 50 | router.post('/login', async (req, res) => { 51 | try { 52 | const admin = await SuperUser.findByCredentials(req.body.username, req.body.password); 53 | const token = await admin.generateAuthToken(); 54 | res.send({ admin, token }); 55 | } catch (err) { 56 | res.status(400).send({ error: err.message }); 57 | } 58 | }); 59 | 60 | router.post('/logout', authenticateAdmin, async (req, res) => { 61 | try { 62 | req.admin.tokens = req.admin.tokens.filter((token) => token.token !== req.token); 63 | await req.admin.save(); 64 | 65 | res.send(); 66 | } catch (err) { 67 | res.status(500).send(); 68 | } 69 | }); 70 | 71 | router.post('/logoutall', authenticateAdmin, async (req, res) => { 72 | try { 73 | req.admin.tokens = []; 74 | await req.admin.save(); 75 | 76 | res.send(); 77 | } catch (err) { 78 | res.status(500).send(); 79 | } 80 | }); 81 | 82 | router.get('/admin', authenticateAdmin, async (req, res) => { 83 | res.send(req.admin); 84 | }); 85 | 86 | router.get('/admin/all', authenticateAdmin, async (req, res) => { 87 | try { 88 | if (!checkPermission(req.admin, 'getAdmin', res)) { 89 | return; 90 | } 91 | const admins = await SuperUser.find({}); 92 | res.send(admins); 93 | } catch (err) { 94 | res.status(500).send({ error: err.message }); 95 | } 96 | }); 97 | 98 | 99 | router.get('/search/user', authenticateAdmin, async (req, res) => { 100 | try { 101 | let users; 102 | if (!Object.keys(req.query).length) { 103 | return res.send({ 104 | error: 'قسمت جستوجو خالی میباشد' 105 | }); 106 | } 107 | 108 | const queries = {} 109 | Object.keys(req.query).filter((v) => v != 'sorted').map(v => { queries[v] = { $regex: new RegExp(req.query[v]) } }); 110 | 111 | let pipeline = User.find(queries); 112 | if (req.query.sorted) { 113 | let sort = { timestamps: -1 }; 114 | if (req.query.sorted == 'ascending') { 115 | sort.timestamps = 1; 116 | } 117 | pipeline = pipeline.sort(sort); 118 | } 119 | 120 | 121 | users = await pipeline.exec(); 122 | if (!users) { 123 | res.status(404).send(); 124 | } 125 | res.send(users); 126 | } catch (err) { 127 | res.status(500).send({ error: err.message }); 128 | } 129 | }); 130 | 131 | router.get('/search/workshop', authenticateAdmin, async (req, res) => { 132 | try { 133 | let workshops; 134 | if (!Object.keys(req.query).length) { 135 | return res.send({ 136 | error: 'قسمت جستوجو خالی میباشد' 137 | }); 138 | } 139 | const queries = {}; 140 | Object.keys(req.query).filter((v) => v != 'sorted' && v != 'sort').map(v => { queries[v] = { $regex: new RegExp(req.query[v]) } }); 141 | let pipeline = Workshop.find(queries); 142 | let sortBy = {}; 143 | if (req.query.sort) { 144 | sortBy[req.query.sort] = -1; 145 | } 146 | //console.log(req.query.sort); 147 | 148 | if (req.query.sorted) { 149 | let sort = { startTime: -1 }; 150 | if (req.query.sorted == 'ascending') { 151 | sort.startTime = 1; 152 | if (req.query.sort) { 153 | sortBy[req.query.sort] = 1; 154 | } 155 | } 156 | if (!req.query.sort) { 157 | pipeline = pipeline.sort(sort); 158 | 159 | } 160 | else { 161 | pipeline = pipeline.sort(sortBy); 162 | } 163 | 164 | } 165 | 166 | workshops = await pipeline.exec(); 167 | if (!workshops) { 168 | res.status(404).send(); 169 | } 170 | res.send(workshops) 171 | 172 | } catch (err) { 173 | res.status(500).send({ error: err.message }); 174 | } 175 | }); 176 | 177 | router.patch('/admin/:id', authenticateAdmin, async (req, res) => { 178 | try { 179 | if (!checkPermission(req.admin, 'editAdmin', res)) { 180 | return; 181 | } 182 | const admin = await SuperUser.findById(req.params.id); 183 | if (!admin) { 184 | res.status(404).send(); 185 | return; 186 | } 187 | 188 | const validFields = ['username', 'password', 'permissions']; 189 | for (const element in req.body) { 190 | if (!validFields.includes(element)) { 191 | res.status(400).send(); 192 | return; 193 | } 194 | } 195 | 196 | Object.keys(req.body).forEach((update) => { 197 | if (update === 'permissions') { 198 | const forbiddenPerms = ['addAdmin', 'editAdmin', 'deleteAdmin', 'getAdmin']; 199 | const perm = req.body.permissions.filter(element => !forbiddenPerms.includes(element)); 200 | admin.permissions = perm.map(element => { return { permission: element } }); 201 | } else { 202 | admin[update] = req.body[update]; 203 | } 204 | }); 205 | await admin.save(); 206 | 207 | res.send(admin); 208 | } catch (err) { 209 | res.status(500).send({ error: err.message }); 210 | } 211 | }); 212 | 213 | 214 | router.delete('/admin/:id', authenticateAdmin, async (req, res) => { 215 | try { 216 | if (!checkPermission(req.admin, 'deleteAdmin', res)) { 217 | return; 218 | } 219 | const admin = await SuperUser.findById(req.params.id); 220 | if (!admin) { 221 | res.status(404).send(); 222 | } 223 | 224 | if(req.admin == admin){ //same admin shouldn't remove him self 225 | res.status(403).send({message:"Same User"}) 226 | return; 227 | } 228 | 229 | await SuperUser.deleteOne(admin); 230 | // await admin.save(); 231 | res.status(204).end() 232 | } catch (err) { 233 | res.status(500).send({ error: err.message }); 234 | } 235 | }); 236 | 237 | async function asyncForEach(array,callback) { 238 | for (let index = 0; index < array.length; index++) { 239 | await callback(array[index], index, array); 240 | } 241 | } 242 | 243 | router.post('/mailit',authenticateAdmin,async (req,res)=>{ 244 | const emails = req.body['mails'] 245 | const subject = req.body['title'] 246 | const html = req.body['html'] 247 | 248 | const start = async () => { 249 | await asyncForEach(emails, async (email) => { 250 | var temp = await sendEmail(email,subject,html) 251 | console.log(temp); 252 | }); 253 | } 254 | start().then(()=>{ 255 | res.send(emails) 256 | }).catch((err)=>{ 257 | console.log(err) 258 | res.status(500).send(`Something is wrong with mails - ${err}`) 259 | }); 260 | }) 261 | 262 | module.exports = router; -------------------------------------------------------------------------------- /linuxfest-backend/src/routers/teacher.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const express = require('express'); 4 | const multer = require('multer'); 5 | const sharp = require('sharp'); 6 | const Jimp = require('jimp'); 7 | 8 | const Teacher = require('../models/Teacher'); 9 | const Workshop = require('../models/Workshop'); 10 | const { checkPermission } = require('../utils/utils'); 11 | const { authenticateAdmin } = require('../express_middlewares/adminAuth'); 12 | const { UPLOAD_PATH } = require('./../config/index.js'); 13 | 14 | 15 | const router = new express.Router(); 16 | 17 | // *************************** Mangement routes ******************** // 18 | 19 | router.post('/manage', authenticateAdmin, async (req, res) => { 20 | try { 21 | if (!checkPermission(req.admin, 'addTeacher', res)) { 22 | return; 23 | } 24 | 25 | const validFields = ["fullName", "fullName_en", "description", "description_en", "affiliation", "affiliation_en", "degree", "degree_en", "lecture_title", "lecture_title_en", "lecture_abstract", "lecture_abstract_en"]; 26 | const finalBody = {}; 27 | validFields.forEach(field => { 28 | finalBody[field] = req.body[field]; 29 | }); 30 | const teacher = new Teacher(finalBody); 31 | await teacher.save(); 32 | return res.send(teacher); 33 | 34 | } catch (err) { 35 | return res.status(400).send({ error: err.message }); 36 | } 37 | }); 38 | 39 | router.get('/manage', authenticateAdmin, async (req, res) => { 40 | try { 41 | if (!checkPermission(req.admin, 'getTeacher', res)) { 42 | return res.status(403).end(); 43 | } 44 | const teachers = await Teacher.find({}); 45 | 46 | let result = []; 47 | for (const teacher of teachers) { 48 | await teacher.populate('workshops').execPopulate() 49 | result = result.concat({ 50 | teacher, 51 | workshops: teacher.workshops 52 | }); 53 | } 54 | 55 | return res.send(result); 56 | } catch (err) { 57 | return res.status(400).send({ error: err.message }); 58 | } 59 | }); 60 | 61 | router.get('/manage/:id', authenticateAdmin, async (req, res) => { 62 | try { 63 | if (!checkPermission(req.admin, 'getTeacher', res)) { 64 | return res.status(403).end(); 65 | } 66 | const teacher = await Teacher.findById(req.params.id); 67 | 68 | if (!teacher) { 69 | return res.status(404).send(); 70 | } 71 | await teacher.populate('workshops').execPopulate(); 72 | 73 | return res.send({ teacher, workshops: teacher.workshops }); 74 | } catch (err) { 75 | return res.status(400).send({ error: err.message }); 76 | } 77 | }) 78 | 79 | router.patch('/manage/:id', authenticateAdmin, async (req, res) => { 80 | try { 81 | if (!checkPermission(req.admin, 'editTeacher', res)) { 82 | return res.status(403).end(); 83 | } 84 | const teacher = await Teacher.findById(req.params.id); 85 | 86 | if (!teacher) { 87 | return res.status(404).send(); 88 | } 89 | const validUpdates = ["fullName", "fullName_en", "description", "description_en", "affiliation", "affiliation_en", "degree", "degree_en", "lecture_title", "lecture_title_en", "lecture_abstract", "lecture_abstract_en"]; 90 | const updates = Object.keys(req.body); 91 | console.log(updates) 92 | const isValidOperation = validUpdates.every((update) => updates.includes(update)); 93 | 94 | if (!isValidOperation) { 95 | return res.status(400).send({ error: 'Invalid updates' }); 96 | } 97 | validUpdates.forEach(update => teacher[update] = req.body[update]); 98 | await teacher.save(); 99 | return res.send(teacher); 100 | } catch (err) { 101 | return res.status(400).send({ error: err.message }); 102 | } 103 | }); 104 | 105 | router.delete('/manage/:id', authenticateAdmin, async (req, res) => { 106 | try { 107 | if (!checkPermission(req.admin, 'deleteTeacher', res)) { 108 | return res.status(403).end(); 109 | } 110 | const teacher = await Teacher.findById(req.params.id); 111 | if (!teacher) { 112 | return res.status(404).send(); 113 | } 114 | await Teacher.deleteOne(teacher); 115 | return res.status(204).end() 116 | } catch (err) { 117 | return res.status(400).send({ error: err.message }); 118 | } 119 | }) 120 | 121 | 122 | const upload = multer({ 123 | limits: { 124 | fileSize: 10000000 125 | }, 126 | fileFilter(req, file, cb) { 127 | if (!file.originalname.match(/\.(jpg|jpeg|png)$/)) { 128 | cb(new Error('لطفا یک تصویر را آپلود کنید')); 129 | } 130 | cb(undefined, true); 131 | } 132 | }); 133 | 134 | 135 | router.post('/manage/pic/:id', authenticateAdmin, upload.single('mainPic'), async (req, res) => { 136 | if (!checkPermission(req.admin, 'editTeacher', res)) { 137 | return res.status(403).end(); 138 | } 139 | try { 140 | const teacher = await Teacher.findById(req.params.id); 141 | if (!teacher) { 142 | return res.status(404).send(); 143 | } 144 | 145 | const filePath = path.join(UPLOAD_PATH, "teachers", req.params.id); 146 | if (!fs.existsSync(filePath)) { 147 | fs.mkdirSync(filePath, { recursive: true }, (err) => { 148 | if (err) { 149 | throw new Error(err); 150 | } 151 | }); 152 | } 153 | 154 | const image = await Jimp.read(req.file.buffer) 155 | 156 | const imagePath = path.join(filePath, "mainPic.png") 157 | await image.writeAsync(imagePath); // Returns Promise 158 | 159 | teacher.imagePath = imagePath 160 | 161 | await teacher.save(); 162 | return res.status(200).send(teacher); 163 | } catch (error) { 164 | console.log(error) 165 | return res.status(500).send({ error: error.message }); 166 | } 167 | }, (err, req, res) => { 168 | res.status(400).send({ error: err.message }); 169 | }); 170 | 171 | router.delete('/manage/pic/:id', authenticateAdmin, async (req, res) => { 172 | if (!checkPermission(req.admin, 'editTeacher', res)) { 173 | return res.status(403).end(); 174 | } 175 | 176 | try { 177 | const teacher = await Teacher.findById(req.params.id); 178 | 179 | if (!teacher || !teacher.imagePath) { 180 | return res.status(404).send(); 181 | } 182 | const filePath = path.join(UPLOAD_PATH, "teachers", req.params.id, "mainPic.png"); 183 | fs.unlink(filePath, (err) => { 184 | if (err) { 185 | console.log(err); 186 | } 187 | }); 188 | 189 | teacher.imagePath = ''; 190 | await teacher.save(); 191 | 192 | return res.send(teacher); 193 | } catch (err) { 194 | return res.status(500).send({ error: err.message }); 195 | } 196 | }); 197 | 198 | 199 | const uploadResume = multer({ 200 | limits: { 201 | fileSize: 10000000 202 | }, 203 | fileFilter(req, file, cb) { 204 | if (!file.originalname.match(/\.(pdf)$/)) { 205 | cb(new Error('لطفا فایل پی‌دی‌اف را آپلود نمایید')); 206 | } 207 | cb(undefined, true); 208 | } 209 | }); 210 | 211 | router.post('/manage/resume/:id', authenticateAdmin, uploadResume.single('mainPic'), async (req, res) => { 212 | if (!checkPermission(req.admin, 'editTeacher', res)) { 213 | return res.status(403).end(); 214 | } 215 | try { 216 | const teacher = await Teacher.findById(req.params.id); 217 | if (!teacher) { 218 | return res.status(404).send(); 219 | } 220 | 221 | const dirPath = path.join(UPLOAD_PATH, "teachers", req.params.id); 222 | if (!fs.existsSync(dirPath)) { 223 | fs.mkdirSync(dirPath, { recursive: true }, (err) => { 224 | if (err) { 225 | throw new Error(err); 226 | } 227 | }); 228 | } 229 | const filePath = path.join(dirPath, "resume.pdf") 230 | fs.writeFileSync(filePath, req.file.buffer, (err) => { 231 | if (err) { 232 | throw new Error(err); 233 | } 234 | }); 235 | 236 | teacher.resume = filePath 237 | await teacher.save(); 238 | 239 | return res.status(200).send(teacher); 240 | } catch (error) { 241 | console.log(error) 242 | return res.status(500).send({ error: error.message }); 243 | } 244 | }, (err, req, res) => { 245 | res.status(400).send({ error: err.message }); 246 | }); 247 | 248 | 249 | // *********************** ordinary routes ****************** // 250 | 251 | router.get('/', async (req, res) => { 252 | try { 253 | const teachers = await Teacher.find({}); 254 | 255 | let result = []; 256 | for (const teacher of teachers) { 257 | const workshops = await Workshop.find({ 'teachers.id': { $in: [teacher.id] } }) 258 | result = result.concat({ 259 | teacher, 260 | workshops: workshops 261 | }); 262 | } 263 | return res.status(200).send(result); 264 | } catch (err) { 265 | return res.status(400).send({ error: err.message }); 266 | } 267 | }) 268 | 269 | router.get('/pic/:id', async (req, res) => { 270 | try { 271 | const filePath = path.join(UPLOAD_PATH, "teachers", req.params.id, "mainPic.png"); 272 | if (fs.existsSync(filePath)) { 273 | return res.status(200).sendFile(filePath); 274 | } 275 | else { 276 | return res.status(404).send({ message: "File Not Found" }) 277 | } 278 | } catch (error) { 279 | return res.status(400).send({ message: "Internal error" }) 280 | } 281 | }) 282 | 283 | 284 | router.get('/resume/:id', async (req, res) => { 285 | try { 286 | const filePath = path.join(UPLOAD_PATH, "teachers", req.params.id, "resume.pdf"); 287 | if (fs.existsSync(filePath)) { 288 | return res.download(filePath); 289 | } 290 | else { 291 | return res.status(404).send({ message: "File Not Found" }) 292 | } 293 | } catch (error) { 294 | return res.status(400).send({ message: "Internal error" }) 295 | } 296 | }) 297 | 298 | 299 | module.exports = router; 300 | -------------------------------------------------------------------------------- /linuxfest-backend/src/routers/user.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const jwt = require('jsonwebtoken'); 3 | const { ZARIN, SIGN_TOKEN, FRONTURL, RANDOM} = require('./../config/index.js') 4 | const ZarinpalCheckout = require('zarinpal-checkout'); 5 | const zarinpal = ZarinpalCheckout.create(ZARIN, false); 6 | const User = require('../models/User'); 7 | const Discount = require('../models/Discount'); 8 | const auth = require('../express_middlewares/userAuth'); 9 | const Workshop = require('../models/Workshop'); 10 | const { checkPermission, sendWelcomeEmail, sendForgetPasswordEmail } = require('../utils/utils'); 11 | const { authenticateAdmin } = require('../express_middlewares/adminAuth'); 12 | const { response } = require('express'); 13 | const logger = require('./../config/logger.js') 14 | const router = new express.Router(); 15 | 16 | 17 | router.get('/verifyPayment',async(req,res)=>{ 18 | try{ 19 | zarinpal.PaymentVerification({ 20 | Amount: req.query.amount, 21 | Authority: req.query.Authority 22 | }).then( async(response)=>{ 23 | if (response.status == 101 || response.status == 100) { 24 | logger.info("Verified! Ref ID: " + response.RefID); 25 | const user = await User.findOne({ 26 | "orderIDs.idNumber": req.query.order_id 27 | }); 28 | if (!user) { 29 | res.status(404).send({message:'Order id not found'}) 30 | return; 31 | } 32 | else 33 | { 34 | //insert workshops 35 | let order; 36 | for (const oi of user.orderIDs) { 37 | if (oi.idNumber == req.query.order_id) { 38 | order = oi; 39 | break; 40 | } 41 | } 42 | for (const workshop of order.workshopId) { 43 | user.workshops = user.workshops.concat({ workshop: workshop }); 44 | } 45 | 46 | //insert to orders history 47 | user.orders = user.orders.concat({ResCode:response.status,Amount:req.query.amount,OrderId:req.query.order_id,Authority:req.query.Authority,WorkshopIds:order.workshopId}) 48 | 49 | 50 | user.orderIDs.splice(user.orderIDs.indexOf(order), 1); 51 | try{ 52 | await user.save(); 53 | for (const workshop of order.workshopId) { 54 | const workshopObj = await Workshop.findById(workshop); 55 | await workshopObj.save(); 56 | } 57 | res.status(200).send({message:'OK',order:order}) 58 | } catch(err) { 59 | res.status(500).send({message:'Something is wrong',order:order}); 60 | console.error(JSON.stringify(err)); 61 | return; 62 | } 63 | } 64 | } else { 65 | console.log(response); 66 | res.status(406).send({message:'Failed payment'}) 67 | } 68 | }).catch(function (err) { 69 | console.log(err); 70 | res.status(504).send({message:'Timeout'}) 71 | }) 72 | }catch(err){ 73 | res.status(500).send({message:err}) 74 | } 75 | }) 76 | 77 | 78 | //==================== Unverified Transactions 79 | router.get('/payment/unverifiedtrans',authenticateAdmin,async(req,res)=>{ 80 | if (!checkPermission(req.admin, "editUser", res)) { 81 | res.status(401).send(); 82 | return; 83 | } 84 | console.log("Verifing old payments") 85 | zarinpal.UnverifiedTransactions().then(response=>{ 86 | if(response.status == 100){ 87 | console.log(response.status) 88 | res.status(200).send(response.authorities) 89 | }else{ 90 | console.log(JSON.stringify(response)) 91 | } 92 | // TODO: check this line 93 | }).catch(err=>{ 94 | console.log(err) 95 | res.status(400).send(err) 96 | }) 97 | }) 98 | 99 | async function createUser(req, res) { 100 | const validFields = ["firstName", "lastName", "email", "password", "phoneNumber", "studentNumber"]; 101 | const finalBody = {}; 102 | validFields.forEach(field => { 103 | finalBody[field] = req.body[field]; 104 | }); 105 | const user = new User(finalBody); 106 | 107 | try { 108 | await user.save(); 109 | 110 | const token = await user.generateAuthToken(); 111 | 112 | sendWelcomeEmail(user); 113 | res.status(201).send({ user, token }); 114 | } catch (error) { 115 | console.log(error) 116 | res.status(400).send(error); 117 | } 118 | } 119 | 120 | router.post("/", async (req, res) => { 121 | await createUser(req, res); 122 | }); 123 | 124 | router.post('/ac', authenticateAdmin, async (req, res) => { 125 | if (!checkPermission(req.admin, "addUser", res)) { 126 | return; 127 | } 128 | await createUser(req, res); 129 | }); 130 | 131 | router.post('/login', async (req, res) => { 132 | try { 133 | const user = await User.findByCredentials(req.body.email, req.body.password); 134 | const token = await user.generateAuthToken(); 135 | res.send({ user, token }); 136 | } catch (error) { 137 | console.log(error); 138 | 139 | res.status(400).send({ error: error.message }); 140 | } 141 | }); 142 | 143 | router.post('/me/logout', auth, async (req, res) => { 144 | try { 145 | req.user.tokens = req.user.tokens.filter((token) => token.token !== req.token); 146 | await req.user.save(); 147 | 148 | res.send(); 149 | } catch (error) { 150 | res.status(500).send(); 151 | } 152 | }); 153 | 154 | router.post('/me/logoutAll', auth, async (req, res) => { 155 | try { 156 | req.user.tokens = []; 157 | 158 | await req.user.save(); 159 | res.send(); 160 | } catch (error) { 161 | res.status(500).send(); 162 | } 163 | }); 164 | 165 | router.get("/", authenticateAdmin, async (req, res) => { 166 | if (!checkPermission(req.admin, "getUser", res)) { 167 | return; 168 | } 169 | try { 170 | const users = await User.find(); 171 | res.send(users); 172 | } catch (err) { 173 | res.status(500).send({ error: err.message }); 174 | } 175 | }); 176 | 177 | router.get('/me', auth, async (req, res) => { 178 | let workshops = []; 179 | for (const workshop of req.user.workshops) { 180 | workshops = workshops.concat(await Workshop.findById(workshop.workshop)); 181 | } 182 | res.send({ user: req.user, workshops }); 183 | }); 184 | 185 | 186 | router.post('/forget', async (req, res) => { 187 | try { 188 | const user = await User.findOne({ email: req.body.email }); 189 | 190 | if (!user) { 191 | res.status(404).send(); 192 | return; 193 | } 194 | const forgotToken = await user.generateForgotToken(req.body.email); 195 | 196 | sendForgetPasswordEmail(user, forgotToken); 197 | 198 | res.status(200).send({message:'Email Sent'}); 199 | 200 | } catch (error) { 201 | res.status(500).send({ error: error.message }); 202 | } 203 | }); 204 | 205 | 206 | router.get("/:id", authenticateAdmin, async (req, res) => { 207 | if (!checkPermission(req.admin, "getUser", res)) { 208 | return; 209 | } 210 | try { 211 | const user = await User.findById(req.params.id); 212 | let workshops = []; 213 | for (const workshop of user.workshops) { 214 | workshops = workshops.concat(await Workshop.findById(workshop.workshop)); 215 | } 216 | res.send({ user, workshops }); 217 | } catch (err) { 218 | res.status(500).send({ error: err.message }); 219 | } 220 | }); 221 | 222 | async function userPatch(user, req, res, isAdmin) { 223 | const updates = Object.keys(req.body); 224 | let allowedUpdates = ['firstName', 'lastName', 'email', 'password', 'age', 'phoneNumber']; 225 | if (isAdmin) { 226 | allowedUpdates += 'studentNumber'; 227 | } 228 | const isValidOperation = updates.every((update) => allowedUpdates.includes(update)); 229 | 230 | if (!isValidOperation) { 231 | return res.status(400).send({ error: 'invalid updates' }); 232 | } 233 | try { 234 | updates.forEach((update) => user[update] = req.body[update]); 235 | 236 | await user.save(); 237 | 238 | res.send(user); 239 | } catch (error) { 240 | res.status(400).send(error); 241 | } 242 | } 243 | 244 | router.patch('/me', auth, async (req, res) => { 245 | await userPatch(req.user, req, res, false); 246 | }); 247 | 248 | router.patch('/:id', authenticateAdmin, async (req, res) => { 249 | if (!checkPermission(req.admin, "editUser", res)) { 250 | res.status(401).send(); 251 | return; 252 | } 253 | const user = await User.findById(req.params.id); 254 | if (!user) { 255 | res.status(404).send(); 256 | } 257 | await userPatch(user, req, res, true); 258 | }); 259 | 260 | router.patch('/forget/:token', async (req, res) => { 261 | try { 262 | const decodedEmail = jwt.verify(req.params.token, SIGN_TOKEN).email; 263 | const user = await User.findOne({ email: decodedEmail, 'forgotTokens.forgotToken': req.params.token }); 264 | if (!user) { 265 | res.status(404).send({message:'User Not Found!'}); 266 | return; 267 | } 268 | user.password = req.body.password; 269 | user.forgotTokens.splice(user.forgotTokens.indexOf(user.forgotTokens.find(x => x.forgotToken === req.params.token)), 1); 270 | await user.save(); 271 | 272 | res.status(200).send(user); 273 | } catch (error) { 274 | res.status(400).send({ error: error.message }); 275 | } 276 | }); 277 | 278 | async function userDelete(user, req, res) { 279 | try { 280 | await User.deleteOne(user); 281 | res.status(204).end() 282 | } catch (error) { 283 | console.log(error) 284 | res.status(500).send({message:"Something is wrong with deleting user"}); 285 | } 286 | } 287 | 288 | router.delete('/me', auth, async (req, res) => { 289 | await userDelete(req.user, req, res); 290 | }); 291 | 292 | router.delete('/:id', authenticateAdmin, async (req, res) => { 293 | if (!checkPermission(req.admin, "deleteUser", res)) { 294 | res.status(401).send(); 295 | return; 296 | } 297 | const user = await User.findById(req.params.id); 298 | if (!user) { 299 | res.status(404).send({message:"User Not Found"}); 300 | } 301 | await userDelete(user, req, res); 302 | res.status(204).end() 303 | }); 304 | //======================= Payment =======================\\ 305 | async function initPayment(user, workshops,workshopsIds, discountCode, res) { 306 | return new Promise(async(resolve,reject)=>{ 307 | const rand = Math.floor(Math.random() * RANDOM); 308 | const orderId = parseInt(user._id, 16) % rand; 309 | user.orderIDs = user.orderIDs.concat({ workshopId:workshopsIds, idNumber: orderId }); 310 | await user.save(); 311 | let price = 0; 312 | workshops.forEach(workshop => { 313 | price += workshop.price; 314 | }); 315 | try { 316 | if (discountCode) { 317 | const discount = await Discount.findByCode(discountCode,user._id); 318 | if (!discount) { 319 | throw new Error("Discount not found"); 320 | } 321 | if (discount.count > 0 || discount.count === -1) { 322 | if (discount.count > 0) { 323 | discount.count--; 324 | //TODO : move this part into verifypayment 325 | for(var i=0; i { 385 | let workshops = []; 386 | try { 387 | for (const workshopId of req.body.workshopIds) { 388 | const workshop = await Workshop.findById(workshopId); 389 | if (!workshop) { 390 | return res.status(404).send(`${workshopId} not found`); 391 | } 392 | try { 393 | // flag shows workshop should be added to workshops list or not 394 | let flag = true; 395 | //Check capacity 396 | await workshop.populate('participants').execPopulate(); 397 | if (workshop.participants.length >= workshop.capacity) { 398 | workshop.isRegOpen = false; 399 | await workshop.save(); 400 | } 401 | if (!workshop.isRegOpen) { 402 | flag = false; 403 | } 404 | //Check already in or not 405 | for (const wsId of req.user.workshops) { 406 | // TODO check below condition 407 | if (wsId.workshop == workshopId) { 408 | flag = false; 409 | } 410 | } 411 | if (flag) { 412 | workshops = workshops.concat(workshop); 413 | } 414 | } catch (err) { 415 | return res.status(500).send({ msg: err.message, err }); 416 | } 417 | } 418 | } catch (err) { 419 | return res.status(400).send(err.message); 420 | } 421 | if (workshops.length !== 0) { 422 | try{ 423 | const urlToRedirect = await initPayment(req.user, workshops, req.body.workshopIds, req.body.discount, res) 424 | console.log("\n\n URL == " + JSON.stringify(urlToRedirect) + "\t\t FOR USER == " + req.user._id + " \t\t WORKSHOPS == "+req.body.workshopIds+"\n\n") 425 | return res.status(200).send(urlToRedirect) 426 | }catch(err){ 427 | // TODO: check this line 428 | return res.status(400).send("Theres a problem to register to workshops"); 429 | } 430 | } else { 431 | return res.status(400).send("No available workshop to register"); 432 | } 433 | }) 434 | 435 | 436 | module.exports = router; -------------------------------------------------------------------------------- /linuxfest-backend/src/routers/workshop.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const express = require('express'); 4 | const multer = require('multer'); 5 | const sharp = require('sharp'); 6 | const Jimp = require('jimp'); 7 | const { format } = require('@fast-csv/format'); 8 | const archiver = require('archiver'); 9 | 10 | const mongoose = require('mongoose'); 11 | 12 | const Workshop = require('../models/Workshop'); 13 | const Teacher = require('../models/Teacher'); 14 | const User = require('../models/User'); 15 | const { checkPermission } = require('../utils/utils'); 16 | const { authenticateAdmin } = require('../express_middlewares/adminAuth'); 17 | const { SITE_VERSION, UPLOAD_PATH, workingDir } = require('./../config/index.js') 18 | 19 | 20 | const router = new express.Router(); 21 | 22 | router.post('/', authenticateAdmin, async (req, res) => { 23 | try { 24 | if (!checkPermission(req.admin, 'addWorkshop', res)) { 25 | return; 26 | } 27 | 28 | const validFields = ["capacity", "title", "price", "isRegOpen", "description", "times", "teachers", "prerequisites"]; 29 | const finalBody = {}; 30 | validFields.forEach(field => { 31 | finalBody[field] = req.body[field]; 32 | }); 33 | const workshop = new Workshop(finalBody); 34 | for (const obj of workshop.teachers) { 35 | const id = obj.id; 36 | const teacher = await Teacher.findById(id); 37 | if (!teacher) { 38 | return res.status(404).send("Teacher not found"); 39 | } 40 | obj.name = teacher.fullName; 41 | } 42 | await workshop.save(); 43 | 44 | return res.status(201).send(workshop) 45 | } catch (err) { 46 | return res.status(500).send({ error: err.message }); 47 | } 48 | }); 49 | 50 | router.get("/manage", authenticateAdmin, async (req, res) => { 51 | try { 52 | if (!checkPermission(req.admin, 'getWorkshop', res)) { 53 | return; 54 | } 55 | const workshops = await Workshop.find({}); 56 | 57 | let result = []; 58 | for (const workshop of workshops) { 59 | await workshop.populate('participants').execPopulate() 60 | const count = await workshop.participantsCount; 61 | result = result.concat({ 62 | workshop, 63 | participants: workshop.participants, 64 | participantsCount: count 65 | }); 66 | } 67 | 68 | return res.send(result); 69 | } catch (err) { 70 | return res.status(500).send({ error: err.message }); 71 | } 72 | }); 73 | 74 | 75 | router.get("/manage/dump", authenticateAdmin, async (req, res) => { 76 | 77 | try { 78 | if (!checkPermission(req.admin, 'getWorkshop', res)) { 79 | return; 80 | } 81 | const workshops = await Workshop.find({}); 82 | 83 | const dump_dir = path.join(workingDir, "workshop_dump"); 84 | console.log(dump_dir) 85 | if (fs.existsSync(dump_dir)){ 86 | console.log("dump exists. try to rm folder"); 87 | fs.rmdirSync(dump_dir, { recursive: true }); 88 | } 89 | console.log("create new workshop_dump dir"); 90 | fs.mkdirSync(dump_dir); 91 | 92 | 93 | for (const workshop of workshops) { 94 | await workshop.populate('participants').execPopulate(); 95 | 96 | console.log(workshop.title) 97 | const file_name = workshop.title + "_" + workshop._id + '.csv'; 98 | const fileName = path.join(dump_dir, file_name); 99 | const csvFile = fs.createWriteStream(fileName); 100 | 101 | const stream = format({ headers:true }); 102 | stream.pipe(csvFile); 103 | 104 | let participants = workshop.participants; 105 | let res = []; 106 | for(let i=0; i { 138 | try { 139 | const workshops = await Workshop.find({}); 140 | return res.send(workshops); 141 | } catch (err) { 142 | return res.status(500).send({ err: err.message }); 143 | } 144 | }); 145 | 146 | router.get('/manage/:id', authenticateAdmin, async (req, res) => { 147 | try { 148 | if (!checkPermission(req.admin, 'getWorkshop', res)) { 149 | return; 150 | } 151 | 152 | const workshop = await Workshop.findById(req.params.id); 153 | if (!workshop) { 154 | return res.status(404).send(); 155 | } 156 | 157 | await workshop.populate('participants').execPopulate(); 158 | let teachers = []; 159 | for (const teacher of workshop.teachers) { 160 | teachers = teachers.concat(await Teacher.findById(teacher.id)); 161 | } 162 | 163 | const count = await workshop.participantsCount; 164 | return res.send({ workshop, participants: workshop.participants, teachers, participantsCount: count }); 165 | } catch (err) { 166 | return res.status(500).send({ error: err.message }); 167 | } 168 | }); 169 | 170 | router.get("/:id", async (req, res) => { 171 | const workshop = await Workshop.findById(req.params.id); 172 | if (!workshop) { 173 | return res.status(404).send(); 174 | } 175 | let teachers = []; 176 | for (const teacher of workshop.teachers) { 177 | teachers = teachers.concat(await Teacher.findById(teacher.id)); 178 | } 179 | return res.send({ workshop, teachers }); 180 | }) 181 | 182 | router.patch('/manage/:id', authenticateAdmin, async (req, res) => { 183 | try { 184 | if (!checkPermission(req.admin, 'editWorkshop', res)) { 185 | return; 186 | } 187 | 188 | const workshop = await Workshop.findById(req.params.id); 189 | if (!workshop) { 190 | return res.status(404).send(); 191 | } 192 | 193 | const validUpdates = ['capacity', 'title', 'isRegOpen', 'description', 'teachers', 'price', 'times', 'prerequisites']; 194 | const updates = Object.keys(req.body); 195 | if (!updates.every(element => validUpdates.includes(element))) { 196 | return res.status(400).send(); 197 | } 198 | 199 | updates.forEach(update => workshop[update] = req.body[update]); 200 | await workshop.save() 201 | return res.send(workshop); 202 | } catch (err) { 203 | return res.status(500).send({ error: err.message }); 204 | } 205 | }); 206 | 207 | router.delete('/manage/:id', authenticateAdmin, async (req, res) => { 208 | if (!checkPermission(req.admin, 'deleteWorkshop', res)) { 209 | return; 210 | } 211 | const workshop = await Workshop.findById(req.params.id); 212 | if (!workshop) { 213 | return res.status(404).send(); 214 | } 215 | await workshop.populate('participants').execPopulate(); 216 | for (const participant of workshop.participants) { 217 | participant.workshops.splice(participant.workshops.indexOf({ workshop: req.params.id }), 1); 218 | await participant.save(); 219 | } 220 | await Workshop.deleteOne(workshop); 221 | return res.status(204).end() 222 | }); 223 | 224 | router.put('/manage/:workshopId/user/:userId', authenticateAdmin, async (req, res) => { 225 | if (!checkPermission(req.admin, 'editWorkshop', res)) { 226 | return; 227 | } 228 | try { 229 | const workshop = await Workshop.findById(req.params.workshopId); 230 | if (!workshop) { 231 | return res.status(404).send(); 232 | } 233 | const user = await User.findById(req.params.userId); 234 | if (!user) { 235 | return res.status(404).send(); 236 | } 237 | if (workshop.isRegOpen) { 238 | user.workshops = user.workshops.concat({ workshop: workshop._id }); 239 | await user.save(); 240 | await workshop.save(); 241 | } 242 | return res.status(200).send(); 243 | } catch (err) { 244 | return res.status(500).send(err); 245 | } 246 | }); 247 | 248 | router.delete('/manage/:workshopId/user/:userId', authenticateAdmin, async (req, res) => { 249 | if (!checkPermission(req.admin, 'editWorkshop', res)) { 250 | return; 251 | } 252 | try { 253 | const workshop = await Workshop.findById(req.params.workshopId); 254 | if (!workshop) { 255 | return res.status(404).send(); 256 | } 257 | const user = await User.findById(req.params.userId); 258 | if (!user) { 259 | return res.status(404).send(); 260 | } 261 | 262 | user.workshops = user.workshops.filter(val => { 263 | return val._id === workshop._id; 264 | }); 265 | 266 | await user.save(); 267 | return res.status(200).send(); 268 | } catch (err) { 269 | return res.status(500).send(err.message); 270 | } 271 | }); 272 | 273 | 274 | //Upload file endpoint(s) 275 | const upload = multer({ 276 | limits: { 277 | fileSize: 10000000 278 | }, 279 | fileFilter(req, file, cb) { 280 | if (!file.originalname.match(/\.(jpg|jpeg|png)$/)) { 281 | cb(new Error('لطفا تصویر آپلود کنید')); 282 | } 283 | cb(undefined, true); 284 | } 285 | }); 286 | 287 | router.get('/pic/:id',async(req,res)=>{ 288 | try{ 289 | const filePath = path.join(UPLOAD_PATH, "workshops", req.params.id, "mainPic.png") 290 | if (fs.existsSync(filePath)) 291 | { 292 | return res.status(200).sendFile(filePath); 293 | } 294 | else 295 | { 296 | return res.status(404).send({message:"File Not Found"}) 297 | } 298 | }catch(error){ 299 | return res.status(400).send({message:"Internal error"}) 300 | } 301 | }) 302 | 303 | router.get('/pic/:workshop/:id',async(req,res)=>{ 304 | try{ 305 | const filePath = path.join(UPLOAD_PATH, "workshops", req.params.workshop, req.params.id +".png") 306 | if (fs.existsSync(filePath)) 307 | { 308 | return res.status(200).sendFile(filePath); 309 | } 310 | else 311 | { 312 | return res.status(404).send({message:"File Not Found"}) 313 | } 314 | }catch(error){ 315 | return res.status(400).send({message:"Internal error"}) 316 | } 317 | }) 318 | 319 | router.post('/pic/album/:id', authenticateAdmin, upload.array('pictures'), async (req, res) => { 320 | if (!checkPermission(req.admin, 'editWorkshop', res)) { 321 | return; 322 | } 323 | 324 | try { 325 | const workshop = await Workshop.findById(req.params.id); 326 | if (!workshop) { 327 | return res.status(404).send(); 328 | } 329 | 330 | for (const file of req.files) { 331 | const filePath = path.join(UPLOAD_PATH, "workshops", req.params.id, "album"); 332 | 333 | if (!fs.existsSync(filePath)) { 334 | fs.mkdirSync(filePath, { recursive: true }, (err) => { 335 | if (err) { 336 | throw new Error(err); 337 | } 338 | }); 339 | } 340 | 341 | const picId = new mongoose.Types.ObjectId(); 342 | 343 | const image = await Jimp.read(file.buffer).resize(1280, 960) 344 | 345 | const imagePath = path.join(filePath, picId.toHexString() + ".png") 346 | await image.writeAsync(imagePath); // Returns Promise 347 | 348 | 349 | // const buffer = await sharp(file.buffer).resize({ width: 1280, height: 960 }).png().toBuffer(); 350 | // fs.writeFileSync(path.join(filePath, picId.toHexString() + ".png"), buffer, (err) => { 351 | // if (err) { 352 | // throw new Error(err); 353 | // } 354 | // }); 355 | 356 | workshop.album = workshop.album.concat({ 357 | _id: picId, 358 | albumPicPath: imagePath 359 | }); 360 | } 361 | 362 | await workshop.save(); 363 | 364 | return res.send(workshop); 365 | } catch (error) { 366 | return res.status(500).send({ error: error.message }); 367 | } 368 | }, (err, req, res) => { 369 | return res.status(400).send({ error: err.message }); 370 | }); 371 | 372 | router.delete('/pic/album/:id/:picid', authenticateAdmin, async (req, res) => { 373 | if (!checkPermission(req.admin, 'editWorkshop', res)) { 374 | return; 375 | } 376 | 377 | try { 378 | const workshop = await Workshop.findOne({ _id: req.params.id, 'album._id': req.params.picid }); 379 | if (!workshop) { 380 | return res.status(404).send(); 381 | } 382 | const filePath = path.join(UPLOAD_PATH, "workshops", req.params.id, "album", req.params.picid + '.png') 383 | fs.unlinkSync(filePath, (err) => { 384 | if (err) { 385 | throw new Error(err); 386 | } 387 | }); 388 | 389 | workshop.album = workshop.album.filter((picObj) => { 390 | return picObj._id.toHexString() !== req.params.picid; 391 | }); 392 | await workshop.save(); 393 | 394 | return res.send(workshop); 395 | } catch (error) { 396 | return res.status(500).send({ error: error.message }); 397 | } 398 | }); 399 | 400 | router.post('/pic/:id', authenticateAdmin, upload.single('mainPic'), async (req, res) => { 401 | if (!checkPermission(req.admin, 'editWorkshop', res)) { 402 | return; 403 | } 404 | try { 405 | const workshop = await Workshop.findById(req.params.id); 406 | if (!workshop) { 407 | return res.status(404).send(); 408 | } 409 | 410 | const filePath = path.join(UPLOAD_PATH, "workshops", req.params.id); 411 | if (!fs.existsSync(filePath)) { 412 | fs.mkdirSync(filePath, { recursive: true }, (err) => { 413 | if (err) { 414 | throw new Error(err); 415 | } 416 | }); 417 | } 418 | 419 | // const buffer = await sharp(req.file.buffer).resize({ width: 1280, height: 960 }).png().toBuffer(); 420 | // fs.writeFileSync(path.join(filePath, "mainPic.png"), buffer, (err) => { 421 | // if (err) { 422 | // throw new Error(err); 423 | // } 424 | // }); 425 | 426 | const image = await Jimp.read(req.file.buffer).resize(1280, 960) 427 | const imagePath = path.join(filePath, "mainPic.png") 428 | await image.writeAsync(imagePath); // Returns Promise 429 | 430 | workshop.picPath = imagePath; 431 | await workshop.save(); 432 | 433 | return res.send(workshop); 434 | } catch (error) { 435 | return res.status(500).send({ error: error.message }); 436 | } 437 | }, (err, req, res) => { 438 | return res.status(400).send({ error: err.message }); 439 | }); 440 | 441 | router.delete('/pic/:id', authenticateAdmin, async (req, res) => { 442 | if (!checkPermission(req.admin, 'editWorkshop', res)) { 443 | return; 444 | } 445 | 446 | try { 447 | const workshop = await Workshop.findById(req.params.id); 448 | if (!workshop || !workshop.picPath) { 449 | return res.status(404).send(); 450 | } 451 | const filePath = path.join(UPLOAD_PATH, "workshops", req.params.id, "mainPic.png"); 452 | fs.unlink(filePath, (err) => { 453 | if (err) { 454 | throw new Error(err); 455 | } 456 | }); 457 | 458 | workshop.picPath = ''; 459 | await workshop.save(); 460 | 461 | return res.send(workshop); 462 | 463 | } catch (err) { 464 | return res.status(500).send({ error: err.message }); 465 | } 466 | }); 467 | 468 | module.exports = router; -------------------------------------------------------------------------------- /linuxfest-backend/src/utils/utils.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const nodemailer = require("nodemailer"); 3 | const { MAILHOST, MAILUSER, MAILPASS, FRONTURL } = require('./../config/index.js') 4 | 5 | const transporter = nodemailer.createTransport({ 6 | host: MAILHOST, 7 | port: 587, 8 | auth: { 9 | user: MAILUSER, 10 | pass: MAILPASS, 11 | } 12 | }); 13 | 14 | 15 | function checkPermission(admin, perm, res) { 16 | console.log(admin.permissions.filter(permission => permission.permission === perm)); 17 | if (!admin.permissions.filter(permission => permission.permission === perm).length) { 18 | res.status(401).send({ error: "You don't have permission to do that" }); 19 | return false; 20 | } else { 21 | return true; 22 | } 23 | } 24 | 25 | async function sendWelcomeEmail(user) { 26 | let html; 27 | try { 28 | html = fs.readFileSync(`${__dirname}/../../mails/register.html`).toString(); 29 | } catch (err) { 30 | console.log(err) 31 | html = "Welcome to linuxfest"; 32 | } 33 | const mailOptions = { 34 | from: '"CEIT Linux Festival" ', 35 | to: user.email, 36 | subject: 'Welcome to Linux Festival!', 37 | html: html 38 | }; 39 | transporter.sendMail(mailOptions, (error, info) => { 40 | if (error) { 41 | return console.log(error); 42 | } 43 | console.log('Message sent: %s', info.messageId); 44 | }); 45 | } 46 | 47 | 48 | async function sendEmail(email,subject,html) 49 | { 50 | return new Promise(async(resolve,reject)=>{ 51 | const mailOptions = { 52 | from: '"CEIT Linux Festival" ', 53 | to: email, 54 | subject: subject, 55 | html: html 56 | }; 57 | transporter.sendMail(mailOptions, (error, info) => { 58 | if (error) { 59 | reject(error) 60 | // return console.log(error) 61 | } 62 | resolve(email) 63 | // return email 64 | }); 65 | }) 66 | 67 | } 68 | 69 | async function sendForgetPasswordEmail(user, token) { 70 | 71 | let html; 72 | const link = `${FRONTURL}/user/forget/${token}` 73 | try { 74 | html = fs.readFileSync(`${__dirname}/../../mails/password.html`).toString(); 75 | html = html.replace("<<>>", link); 76 | } catch (err) { 77 | html = link; 78 | } 79 | 80 | const mailOptions = { 81 | from: '"CEIT Linux Festival" ', 82 | to: user.email, 83 | subject: 'Password reset', 84 | html: html 85 | }; 86 | 87 | transporter.sendMail(mailOptions, (error, info) => { 88 | if (error) { 89 | return console.log(error); 90 | } 91 | console.log('Message sent: %s', info.messageId); 92 | }); 93 | } 94 | 95 | module.exports = { 96 | checkPermission, 97 | sendWelcomeEmail, 98 | sendForgetPasswordEmail, 99 | sendEmail 100 | } -------------------------------------------------------------------------------- /nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | # ############# # 2 | # panel builder # 3 | # ############# # 4 | 5 | FROM node:14.7.0-alpine as panel_builder 6 | 7 | # make the 'app' folder the current working directory 8 | WORKDIR /app 9 | 10 | # copy both 'package.json' and 'package-lock.json' (if available) 11 | COPY ./panel/package*.json ./ 12 | 13 | # install project dependencies 14 | RUN npm install 15 | 16 | # copy project files and folders to the current working directory (i.e. 'app' folder) 17 | COPY ./panel/ ./ 18 | 19 | # build app for production with minification 20 | RUN npm run build 21 | 22 | 23 | # ############# # 24 | # front builder # 25 | # ############# # 26 | 27 | FROM node:14.7.0-alpine as front_builder 28 | 29 | # make the 'app' folder the current working directory 30 | WORKDIR /app 31 | 32 | # copy both 'package.json' and 'package-lock.json' (if available) 33 | COPY ./front/package*.json ./ 34 | 35 | # install project dependencies 36 | RUN npm install 37 | 38 | # copy project files and folders to the current working directory (i.e. 'app' folder) 39 | COPY ./front . 40 | 41 | # build app for production with minification 42 | RUN npm run build 43 | 44 | 45 | 46 | # ############# # 47 | # NGINX builder # 48 | # ############# # 49 | 50 | FROM nginx:1.21-alpine 51 | 52 | # remove default nginx index 53 | RUN rm -rf /usr/share/nginx/html/* 54 | WORKDIR /usr/share/nginx/html 55 | 56 | RUN mkdir front 57 | COPY --from=front_builder /app/dist ./front/ 58 | 59 | RUN mkdir panel 60 | COPY --from=panel_builder /app/dist ./panel/ 61 | 62 | # first remove the current existing config 63 | RUN rm /etc/nginx/conf.d/default.conf 64 | # then copy the config file to the container 65 | COPY nginx.conf /etc/nginx/conf.d -------------------------------------------------------------------------------- /nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | upstream web_app { 2 | server node-app:5000; 3 | } 4 | 5 | server { 6 | 7 | listen 81; 8 | listen [::]:81; 9 | client_max_body_size 20M; 10 | 11 | location /api { 12 | proxy_pass http://web_app; 13 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 14 | proxy_set_header X-Forwarded-Proto $scheme; 15 | proxy_set_header Host $host; 16 | proxy_redirect off; 17 | } 18 | 19 | location ^~ /admin { 20 | alias /usr/share/nginx/html/panel/; 21 | index index.html; 22 | try_files $uri $uri/ /index.html; 23 | } 24 | 25 | location / { 26 | root /usr/share/nginx/html/front/; 27 | index index.html; 28 | try_files $uri $uri/ /index.html; 29 | } 30 | 31 | error_page 500 502 503 504 /50x.html; 32 | location = /50x.html { 33 | root /usr/share/nginx/html/front; 34 | } 35 | } --------------------------------------------------------------------------------