diff --git a/controllers/auth.controller.js b/controllers/auth.controller.js index efbfa14..307fd79 100644 --- a/controllers/auth.controller.js +++ b/controllers/auth.controller.js @@ -7,7 +7,7 @@ import { } from '../utils/responseCodes.js'; import { hash_password, getJwt } from '../utils/password.js'; import User from '../models/user.model.js'; -import verifycaptcha from '../utils/recaptcha.js'; +import { verifycaptcha } from '../utils/recaptcha.js'; import validator from 'validator'; import { OAuth2Client } from 'google-auth-library'; const client = new OAuth2Client( @@ -64,7 +64,7 @@ export async function signUp(req, res) { if (checkUser) return response_400(res, 'Email already in use'); if (!verifycaptcha(recaptcha_token)) return response_400(res, 'Captcha was found incorrect'); - + const password = await hash_password(req.body.password); let newUser = User({ email, diff --git a/controllers/form.controller.js b/controllers/form.controller.js index e00ce4e..7406c8c 100644 --- a/controllers/form.controller.js +++ b/controllers/form.controller.js @@ -5,12 +5,13 @@ import { response_401, response_500, } from '../utils/responseCodes.js'; -import verifycaptcha from '../utils/recaptcha.js'; +import { verifycaptcha } from '../utils/recaptcha.js'; import { hash_password, encryptString } from '../utils/password.js'; import Form from '../models/form.model.js'; import Project from '../models/project.model.js'; import { generateRandomString } from '../utils/generateRandomString.js'; import { prisma } from '../config/sql.config.js'; + export async function updateForm(req, res) { const id = req.params.id; @@ -195,27 +196,41 @@ export async function getForm(req, res) { ]); form = form[0]; if (!form) return response_400(res, 'Form not found'); + return response_200(res, 'OK', form); + } catch (error) { + console.log(error); + return response_500(res, 'Server Error', error); + } +} + +export async function getFormSubmissions(req, res) { + try { + const { formId } = req.params; + const { limit, skip } = req.query; + const formSubmissions = await prisma.formSubmission.findMany({ - select: { - id: true, - data: true, - createdAt: true, + where: { + formId: formId, }, orderBy: { createdAt: 'desc', }, - where: { - formId: formId, + select: { + id: true, + data: true, + createdAt: true, }, + take: limit, + skip: skip, }); - form.submission = formSubmissions; - console.log(form); - return response_200(res, 'OK', form); + + return response_200(res, 'OK', formSubmissions); } catch (error) { console.log(error); return response_500(res, 'Server Error', error); } } + export async function deleteForm(req, res) { try { const id = req.body.id; diff --git a/controllers/formSubmission.controller.js b/controllers/formSubmission.controller.js index d77f3fd..f51a520 100644 --- a/controllers/formSubmission.controller.js +++ b/controllers/formSubmission.controller.js @@ -9,29 +9,73 @@ import { response_500, } from '../utils/responseCodes.js'; import Form from '../models/form.model.js'; +import { createFile } from '../utils/fileUpload.js'; +import { verifySubmissionRecaptcha } from '../utils/recaptcha.js'; + export async function createFormSubmission(req, res) { try { + + const grcToken = req.body['grc-token']; const encryptedStr = req.query.formRef; const decryptedStr = dcryptString(encryptedStr); - console.log(decryptedStr); const { formId, submisssionLinkGeneratedAt } = JSON.parse(decryptedStr); - const form = await Form.findOne({ formId: formId }); + + const form = await Form.findOne({ formId: formId }).populate('project'); if (!form) return response_400(res, 'Form not found'); + + let incomingTime = new Date(form.submisssionLinkGeneratedAt).getTime(); - console.log(incomingTime); - console.log(submisssionLinkGeneratedAt); if (incomingTime !== submisssionLinkGeneratedAt) return response_400(res, 'Link expired'); + + + if (form.project.allowRecaptcha) { + if (!grcToken) return response_401(res, 'Recaptcha token not found'); + const recaptcha = await verifySubmissionRecaptcha( + grcToken, + form.project.recaptchaSecretKey, + ); + if (!recaptcha) return response_401(res, 'Recaptcha verification failed'); + } + + + const allowedOrigins = form.project.allowedOrigins; + if (allowedOrigins.length > 0) { + const origin = req.headers.origin; + if (!allowedOrigins.includes(origin)) + return response_401(res, 'Origin not allowed'); + } + const schema = form.schema; const submissionData = req.body; + + if (form.hasFileField) { + if (req.files.length > 1) { + return response_400(res, 'Too many files'); + } + + if (req.files.length === 1) { + let fieldName = req.files[0].fieldname; + submissionData[fieldName] = fieldName; + } + } + const isValid = validateSchema(schema, submissionData); if (!isValid) return response_400(res, 'Invalid data'); + + if (form.hasFileField) { + const fileUrl = (await createFile(req.files[0])).url; + if (!fileUrl) return response_500(res, 'File upload failed'); + submissionData[req.files[0].fieldname] = fileUrl; + } + const submission = await prisma.formSubmission.create({ data: { formId: formId, data: submissionData, }, }); + return response_201(res, submission); } catch (err) { return response_500(res, err); diff --git a/controllers/project.controller.js b/controllers/project.controller.js index 4794c5e..17682f7 100644 --- a/controllers/project.controller.js +++ b/controllers/project.controller.js @@ -2,7 +2,7 @@ import Project from '../models/project.model.js'; import User from '../models/user.model.js'; import Form from '../models/form.model.js'; import Collaborators from '../models/invitedCollaborators.model.js'; -import verifycaptcha from '../utils/recaptcha.js'; +import { verifycaptcha } from '../utils/recaptcha.js'; import { sendCollabInvitationLink } from '../utils/mailer.js'; import { getJwt, hash_password } from '../utils/password.js'; import { diff --git a/controllers/user.controller.js b/controllers/user.controller.js index 3ab4e5c..7e1c4d2 100644 --- a/controllers/user.controller.js +++ b/controllers/user.controller.js @@ -1,7 +1,7 @@ import { sendVerificationLink } from '../utils/mailer.js'; import { getJwt, hash_password } from '../utils/password.js'; import { response_200, response_400 } from '../utils/responseCodes.js'; -import verifycaptcha from '../utils/recaptcha.js'; +import { verifycaptcha } from '../utils/recaptcha.js'; import validator from 'validator'; import jwt from 'jsonwebtoken'; import User from '../models/user.model.js'; diff --git a/routes/form.routes.js b/routes/form.routes.js index 4eb163b..3bb1c6e 100644 --- a/routes/form.routes.js +++ b/routes/form.routes.js @@ -7,6 +7,7 @@ import { deleteForm, updateForm, getForm, + getFormSubmissions, } from '../controllers/form.controller.js'; const router = Router(); @@ -16,4 +17,5 @@ router.post('/new/:projectId', verifiedMiddleware, createForm); router.patch('/update/:id', verifiedMiddleware, updateForm); router.delete('/', verifiedMiddleware, deleteForm); router.get('/dashboard/:formId', verifiedMiddleware, getForm); +router.get('/submissions/:formId', verifiedMiddleware, getFormSubmissions); export default router; diff --git a/routes/formSubmission.routes.js b/routes/formSubmission.routes.js index 1d0cdf0..dfc69ba 100644 --- a/routes/formSubmission.routes.js +++ b/routes/formSubmission.routes.js @@ -2,6 +2,8 @@ import { greet } from '../controllers/auth.controller.js'; import verifiedMiddleware from '../middlewares/verify.middleware.js'; import { Router } from 'express'; import { createFormSubmission } from '../controllers/formSubmission.controller.js'; +import upload from '../config/multer.config.js'; + const router = Router(); -router.post('/submit', createFormSubmission); +router.post('/submit', upload.any(), createFormSubmission); export default router; diff --git a/utils/fileUpload.js b/utils/fileUpload.js index e877924..fa11051 100644 --- a/utils/fileUpload.js +++ b/utils/fileUpload.js @@ -4,6 +4,7 @@ import { GetObjectCommand, } from '@aws-sdk/client-s3'; import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; +import crypto from 'crypto'; import dotenv from 'dotenv'; dotenv.config(); @@ -50,6 +51,6 @@ export const getFile = async (fileName) => { Key: fileName, }; const command = new GetObjectCommand(getObjectParams); - const url = await getSignedUrl(s3, command, ); + const url = await getSignedUrl(s3, command); return { url }; }; diff --git a/utils/recaptcha.js b/utils/recaptcha.js index 7d08bd6..758c458 100644 --- a/utils/recaptcha.js +++ b/utils/recaptcha.js @@ -1,4 +1,4 @@ -export default async function verifycaptcha(token) { +export async function verifycaptcha(token) { if (process.env.ENV === 'dev') return true; var secretKey = process.env.GOOGLE_RECAPTCHA_SECRET_KEY; var userKey = token; @@ -16,3 +16,21 @@ export default async function verifycaptcha(token) { return false; } + +export async function verifySubmissionRecaptcha(token, secret) { + var secretKey = secret; + var userKey = token; + let res = await fetch('https://www.google.com/recaptcha/api/siteverify', { + method: 'POST', + body: { + secret: secretKey, + response: userKey, + }, + }); + + if (res.body.success) { + return true; + } + + return false; +}