Compare commits
	
		
			2 Commits
		
	
	
		
			kevin/revi
			...
			b210c5a3e2
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| b210c5a3e2 | |||
| e6d26011cb | 
							
								
								
									
										2
									
								
								api/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -2,5 +2,3 @@ node_modules/
 | 
			
		||||
dist/
 | 
			
		||||
package-lock.json
 | 
			
		||||
.env
 | 
			
		||||
test-report.html
 | 
			
		||||
uploads/
 | 
			
		||||
@@ -1,36 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
	"$schema": "https://biomejs.dev/schemas/1.7.3/schema.json",
 | 
			
		||||
	"files": {
 | 
			
		||||
		"ignore": [".expo/", ".vscode/", "node_modules/", "dist/", "*.json"]
 | 
			
		||||
	},
 | 
			
		||||
	"organizeImports": {
 | 
			
		||||
		"enabled": true
 | 
			
		||||
	},
 | 
			
		||||
	"linter": {
 | 
			
		||||
		"enabled": false,
 | 
			
		||||
		"rules": {
 | 
			
		||||
			"recommended": true
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	"formatter": {
 | 
			
		||||
		"enabled": true,
 | 
			
		||||
		"formatWithErrors": false,
 | 
			
		||||
		"ignore": [],
 | 
			
		||||
		"attributePosition": "auto",
 | 
			
		||||
		"indentStyle": "tab",
 | 
			
		||||
		"indentWidth": 2,
 | 
			
		||||
		"lineEnding": "lf",
 | 
			
		||||
		"lineWidth": 120
 | 
			
		||||
	},
 | 
			
		||||
	"javascript": {
 | 
			
		||||
		"formatter": {
 | 
			
		||||
			"arrowParentheses": "always",
 | 
			
		||||
			"bracketSameLine": true,
 | 
			
		||||
			"bracketSpacing": true,
 | 
			
		||||
			"jsxQuoteStyle": "double",
 | 
			
		||||
			"quoteProperties": "asNeeded",
 | 
			
		||||
			"semicolons": "always",
 | 
			
		||||
			"trailingComma": "all"
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -59,7 +59,6 @@
 | 
			
		||||
		"eslint": "^8.3.0",
 | 
			
		||||
		"http": "^0.0.1-security",
 | 
			
		||||
		"jest": "^29.7.0",
 | 
			
		||||
		"jest-html-reporter": "^3.10.2",
 | 
			
		||||
		"mocha": "^9.1.3",
 | 
			
		||||
		"mongodb-memory-server": "^9.2.0",
 | 
			
		||||
		"npm-run-all": "^4.1.5",
 | 
			
		||||
@@ -77,12 +76,6 @@
 | 
			
		||||
		"testEnvironment": "node",
 | 
			
		||||
		"setupFilesAfterEnv": [
 | 
			
		||||
			"./tests/_setupFile.ts"
 | 
			
		||||
		],
 | 
			
		||||
		"reporters": [
 | 
			
		||||
			"default",
 | 
			
		||||
			["./node_modules/jest-html-reporter", {
 | 
			
		||||
				"pageTitle": "Test Report"
 | 
			
		||||
			}]
 | 
			
		||||
		]
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,29 +1,29 @@
 | 
			
		||||
import express from "express";
 | 
			
		||||
import morgan from "morgan";
 | 
			
		||||
import path from "path";
 | 
			
		||||
import cors from "cors";
 | 
			
		||||
import cookieParser from "cookie-parser";
 | 
			
		||||
import path from 'path'
 | 
			
		||||
import cors from 'cors'
 | 
			
		||||
import cookieParser from 'cookie-parser'
 | 
			
		||||
import { router as routes } from "./routes";
 | 
			
		||||
//import { router as middlewares } from './middlewares'
 | 
			
		||||
import env from "./config/environment";
 | 
			
		||||
import env from './config/environment'
 | 
			
		||||
 | 
			
		||||
export let corsWhitelist: Array<string>;
 | 
			
		||||
if (env.CORS_WHITELIST != "undefined") {
 | 
			
		||||
	corsWhitelist = [...["http://localhost:8080", "http://localhost:6040"], ...env.CORS_WHITELIST.split(";")];
 | 
			
		||||
export let corsWhitelist: Array<string>
 | 
			
		||||
if (env.CORS_WHITELIST != 'undefined') {
 | 
			
		||||
	corsWhitelist = [...['http://localhost:8080', 'http://localhost:6040'], ...env.CORS_WHITELIST.split(';')]
 | 
			
		||||
} else {
 | 
			
		||||
	corsWhitelist = ["http://localhost:8080", "http://localhost:6040"];
 | 
			
		||||
	corsWhitelist = ['http://localhost:8080', 'http://localhost:6040']
 | 
			
		||||
}
 | 
			
		||||
const corsOptions = {
 | 
			
		||||
	origin: function (origin: any, callback: any) {
 | 
			
		||||
		if (!origin || corsWhitelist.indexOf(origin) !== -1) {
 | 
			
		||||
			callback(null, true);
 | 
			
		||||
			callback(null, true)
 | 
			
		||||
		} else {
 | 
			
		||||
			callback(new Error("Not allowed by CORS"));
 | 
			
		||||
			callback(new Error('Not allowed by CORS'))
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	optionsSuccessStatus: 200,
 | 
			
		||||
	credentials: true,
 | 
			
		||||
};
 | 
			
		||||
	credentials: true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const app = express();
 | 
			
		||||
 | 
			
		||||
@@ -31,13 +31,12 @@ export const app = express();
 | 
			
		||||
//app.use(middlewares)
 | 
			
		||||
//app.set('view engine', 'ejs')
 | 
			
		||||
//app.set('views', path.join(__dirname, 'views'))
 | 
			
		||||
app.use(cors(corsOptions));
 | 
			
		||||
app.use(cors(corsOptions))
 | 
			
		||||
app.use(morgan("dev"));
 | 
			
		||||
app.use(express.urlencoded({ extended: true }));
 | 
			
		||||
app.use(express.json());
 | 
			
		||||
app.use(express.static(path.join(__dirname, "public")));
 | 
			
		||||
app.use('/public/uploads', express.static(path.join(__dirname, '../uploads')));
 | 
			
		||||
app.use(cookieParser());
 | 
			
		||||
app.use(express.static(path.join(__dirname, 'public')))
 | 
			
		||||
app.use(cookieParser())
 | 
			
		||||
 | 
			
		||||
// Routes
 | 
			
		||||
app.use(routes);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,13 @@
 | 
			
		||||
import { Request, Response } from "express";
 | 
			
		||||
import bcrypt from "bcrypt";
 | 
			
		||||
import jwt from "jsonwebtoken";
 | 
			
		||||
import env from "../config/environment";
 | 
			
		||||
import Docs from "../services/docsService";
 | 
			
		||||
import User from "../models/User";
 | 
			
		||||
import { Log } from "nork";
 | 
			
		||||
import { IUser, signupExam, ISignin, signinExam } from "../validators/authValidator";
 | 
			
		||||
import { Request, Response } from 'express';
 | 
			
		||||
import bcrypt from 'bcrypt';
 | 
			
		||||
import jwt from 'jsonwebtoken';
 | 
			
		||||
import env from '../config/environment';
 | 
			
		||||
import Docs from '../services/docsService';
 | 
			
		||||
import User from '../models/User';
 | 
			
		||||
import {Log} from 'nork'
 | 
			
		||||
import { IUser, signupExam, ISignin, signinExam } from '../validators/authValidator';
 | 
			
		||||
 | 
			
		||||
new Docs("user", "signup", "/api/v1/auth/signup", "POST", "user signup api", undefined, signupExam, "status object");
 | 
			
		||||
new Docs('user', 'signup', '/api/v1/auth/signup', 'POST', 'user signup api', undefined, signupExam, 'status object');
 | 
			
		||||
export async function signup_post(req: Request, res: Response) {
 | 
			
		||||
	try {
 | 
			
		||||
		const payload: IUser = req.body;
 | 
			
		||||
@@ -16,27 +16,27 @@ export async function signup_post(req: Request, res: Response) {
 | 
			
		||||
		const user = new User(payload);
 | 
			
		||||
		await user.save();
 | 
			
		||||
 | 
			
		||||
		res.status(201).json(Log.info(201, "user was successfully signed up"));
 | 
			
		||||
		res.status(201).json(Log.info(201, 'user was successfully signed up'));
 | 
			
		||||
	} catch (err: any) {
 | 
			
		||||
		if (err.code == 11000) {
 | 
			
		||||
			res.status(400).json(Log.error(400, "this user already exists"));
 | 
			
		||||
			res.status(400).json(Log.error(400, 'this user already exists'));
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		Log.error(500, err);
 | 
			
		||||
		res.status(500).json(Log.error(500, "something went wrong"));
 | 
			
		||||
		res.status(500).json(Log.error(500, 'something went wrong'));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
new Docs("user", "signin", "/api/v1/auth/signin", "POST", "user signin api", undefined, signinExam, "status object");
 | 
			
		||||
new Docs('user', 'signin', '/api/v1/auth/signin', 'POST', 'user signin api', undefined, signinExam, 'status object');
 | 
			
		||||
export async function signin_post(req: Request, res: Response) {
 | 
			
		||||
	try {
 | 
			
		||||
		const payload: ISignin = req.body;
 | 
			
		||||
 | 
			
		||||
		const user = await User.findOne({ email: payload.email });
 | 
			
		||||
		if (!user) {
 | 
			
		||||
			res.cookie("jwt", "", { httpOnly: true, maxAge: 0 });
 | 
			
		||||
			res.cookie("auth", false, { httpOnly: false, maxAge: 0 });
 | 
			
		||||
			res.status(401).json(Log.error(401, "email or password is wrong"));
 | 
			
		||||
			res.cookie('jwt', '', { httpOnly: true, maxAge: 0 });
 | 
			
		||||
			res.cookie('auth', false, { httpOnly: false, maxAge: 0 });
 | 
			
		||||
			res.status(401).json(Log.error(401, 'email or password is wrong'));
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -44,47 +44,38 @@ export async function signin_post(req: Request, res: Response) {
 | 
			
		||||
			const maxAge = 3 * 24 * 60 * 60; // 3 days in seconds
 | 
			
		||||
			const createToken = (id: any) => {
 | 
			
		||||
				return jwt.sign({ id }, env.JWT_SECRET, {
 | 
			
		||||
					expiresIn: maxAge,
 | 
			
		||||
					expiresIn: maxAge
 | 
			
		||||
				});
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			const token = createToken(user._id);
 | 
			
		||||
			res.cookie("jwt", token, { httpOnly: true, maxAge: maxAge * 1000 });
 | 
			
		||||
			res.cookie("auth", true, { httpOnly: false, maxAge: maxAge * 1000 });
 | 
			
		||||
			res.cookie('jwt', token, { httpOnly: true, maxAge: maxAge * 1000 });
 | 
			
		||||
			res.cookie('auth', true, { httpOnly: false, maxAge: maxAge * 1000 });
 | 
			
		||||
 | 
			
		||||
			res.json(Log.info(200, "user is logged in", { jwt: token }));
 | 
			
		||||
			res.json(Log.info(200, 'user is logged in', {jwt: token}));
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		res.cookie("jwt", "", { httpOnly: true, maxAge: 0 });
 | 
			
		||||
		res.cookie("auth", false, { httpOnly: false, maxAge: 0 });
 | 
			
		||||
		res.status(401).json(Log.error(401, "email or password is wrong"));
 | 
			
		||||
		res.cookie('jwt', '', { httpOnly: true, maxAge: 0 });
 | 
			
		||||
		res.cookie('auth', false, { httpOnly: false, maxAge: 0 });
 | 
			
		||||
		res.status(401).json(Log.error(401, 'email or password is wrong'));
 | 
			
		||||
	} catch (err: any) {
 | 
			
		||||
		Log.error(500, err);
 | 
			
		||||
		res.status(500).json(Log.error(500, "something went wrong"));
 | 
			
		||||
		res.status(500).json(Log.error(500, 'something went wrong'));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
new Docs("user", "logout", "/api/v1/auth/logout", "POST", "user logout api", undefined, {}, "status object");
 | 
			
		||||
new Docs('user', 'logout', '/api/v1/auth/logout', 'POST', 'user logout api', undefined, {}, 'status object');
 | 
			
		||||
export function logout_post(req: Request, res: Response) {
 | 
			
		||||
	res.cookie("jwt", "", { httpOnly: true, maxAge: 0 });
 | 
			
		||||
	res.cookie("auth", false, { httpOnly: false, maxAge: 0 });
 | 
			
		||||
	res.json(Log.info(200, "user was logged out"));
 | 
			
		||||
	res.cookie('jwt', '', { httpOnly: true, maxAge: 0 });
 | 
			
		||||
	res.cookie('auth', false, { httpOnly: false, maxAge: 0 });
 | 
			
		||||
	res.json(Log.info(200, 'user was logged out'));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
new Docs(
 | 
			
		||||
	"user",
 | 
			
		||||
	"status",
 | 
			
		||||
	"/api/v1/auth/status",
 | 
			
		||||
	"GET",
 | 
			
		||||
	"user login status api",
 | 
			
		||||
	undefined,
 | 
			
		||||
	undefined,
 | 
			
		||||
	"status code | user object",
 | 
			
		||||
);
 | 
			
		||||
new Docs('user', 'status', '/api/v1/auth/status', 'GET', 'user login status api', undefined, undefined, 'status code | user object');
 | 
			
		||||
export function status_get(req: Request, res: Response) {
 | 
			
		||||
	let userObject = res.locals.user;
 | 
			
		||||
	userObject.password = undefined;
 | 
			
		||||
	userObject.__v = undefined;
 | 
			
		||||
	res.status(200).json(Log.info(200, "user is logged in", userObject));
 | 
			
		||||
	res.status(200).json(Log.info(200, 'user is logged in', userObject));
 | 
			
		||||
}
 | 
			
		||||
@@ -1,22 +1,13 @@
 | 
			
		||||
import { Request, Response } from "express";
 | 
			
		||||
import Beer from "../models/Beer";
 | 
			
		||||
import { isValidObjectId, Types } from "mongoose";
 | 
			
		||||
import fs from "fs";
 | 
			
		||||
import path from "path";
 | 
			
		||||
import { Log } from "nork";
 | 
			
		||||
import Docs from "../services/docsService";
 | 
			
		||||
import { addExam, delExam, editExam, IBeer } from "../validators/beerValidator";
 | 
			
		||||
import { Request, Response } from 'express';
 | 
			
		||||
import Beer from '../models/Beer';
 | 
			
		||||
import { isValidObjectId, Types } from 'mongoose';
 | 
			
		||||
import fs from 'fs';
 | 
			
		||||
import path from 'path';
 | 
			
		||||
import {Log} from 'nork'
 | 
			
		||||
import Docs from '../services/docsService';
 | 
			
		||||
import { addExam, delExam, editExam, IBeer } from '../validators/beerValidator';
 | 
			
		||||
 | 
			
		||||
new Docs(
 | 
			
		||||
	"beer",
 | 
			
		||||
	"add",
 | 
			
		||||
	"/api/v1/beer/add",
 | 
			
		||||
	"POST",
 | 
			
		||||
	"beer add api",
 | 
			
		||||
	undefined,
 | 
			
		||||
	{ ...addExam, photos: "optional field | max 4 images | formData" },
 | 
			
		||||
	"status object | beer object",
 | 
			
		||||
);
 | 
			
		||||
new Docs('beer', 'add', '/api/v1/beer/add', 'POST', 'beer add api', undefined, { ...addExam, photos: 'optional field | max 4 images | formData' }, 'status object | beer object');
 | 
			
		||||
export async function add_post(req: Request, res: Response) {
 | 
			
		||||
	try {
 | 
			
		||||
		if (req.files) {
 | 
			
		||||
@@ -28,37 +19,28 @@ export async function add_post(req: Request, res: Response) {
 | 
			
		||||
		}
 | 
			
		||||
		const beer = new Beer(req.body);
 | 
			
		||||
		await beer.save();
 | 
			
		||||
		res.status(201).json(Log.info(201, "beer was created", beer));
 | 
			
		||||
		res.status(201).json(Log.info(201, 'beer was created', beer));
 | 
			
		||||
	} catch (err: any) {
 | 
			
		||||
		Log.error(500, "error in add_post", err);
 | 
			
		||||
		res.status(500).json(Log.error(500, "something went wrong"));
 | 
			
		||||
		Log.error(500, 'error in add_post', err);
 | 
			
		||||
		res.status(500).json(Log.error(500, 'something went wrong'));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
new Docs(
 | 
			
		||||
	"beer",
 | 
			
		||||
	"get",
 | 
			
		||||
	"/api/v1/beer/get",
 | 
			
		||||
	"GET",
 | 
			
		||||
	"beer get api",
 | 
			
		||||
	undefined,
 | 
			
		||||
	undefined,
 | 
			
		||||
	"status object | array of beer objects",
 | 
			
		||||
);
 | 
			
		||||
new Docs('beer', 'get', '/api/v1/beer/get', 'GET', 'beer get api', undefined, undefined, 'status object | array of beer objects');
 | 
			
		||||
export async function get_get(req: Request, res: Response) {
 | 
			
		||||
	try {
 | 
			
		||||
		const beer = await Beer.find({}, "-__v");
 | 
			
		||||
		res.status(200).json(Log.info(200, "beers fetched", beer));
 | 
			
		||||
		const beer = await Beer.find({}, '-__v');
 | 
			
		||||
		res.status(200).json(Log.info(200, 'beers fetched', beer));
 | 
			
		||||
	} catch (err: any) {
 | 
			
		||||
		Log.error(500, "error in get_get", err);
 | 
			
		||||
		res.status(500).json(Log.error(500, "something went wrong"));
 | 
			
		||||
		Log.error(500, 'error in get_get', err);
 | 
			
		||||
		res.status(500).json(Log.error(500, 'something went wrong'));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
new Docs("beer", "del", "/api/v1/beer/del", "POST", "beer del api", undefined, delExam, "status object");
 | 
			
		||||
new Docs('beer', 'del', '/api/v1/beer/del', 'POST', 'beer del api', undefined, delExam, 'status object');
 | 
			
		||||
export async function del_post(req: Request, res: Response) {
 | 
			
		||||
	try {
 | 
			
		||||
		if (!isValidObjectId(req.body._id)) throw Log.error(400, "this is not valid _id");
 | 
			
		||||
		if (!isValidObjectId(req.body._id)) throw Log.error(400, 'this is not valid _id');
 | 
			
		||||
 | 
			
		||||
		const beer = await Beer.deleteOne(new Types.ObjectId(req.body._id));
 | 
			
		||||
 | 
			
		||||
@@ -72,39 +54,30 @@ export async function del_post(req: Request, res: Response) {
 | 
			
		||||
			res.status(err.code).json(err);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		Log.error(500, "error in del_post", err);
 | 
			
		||||
		res.status(500).json(Log.error(500, "something went wrong"));
 | 
			
		||||
		Log.error(500, 'error in del_post', err);
 | 
			
		||||
		res.status(500).json(Log.error(500, 'something went wrong'));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
new Docs(
 | 
			
		||||
	"beer",
 | 
			
		||||
	"edit",
 | 
			
		||||
	"/api/v1/beer/edit",
 | 
			
		||||
	"POST",
 | 
			
		||||
	"beer edit api",
 | 
			
		||||
	undefined,
 | 
			
		||||
	{ ...editExam, photos: "optional field | max 4 images | formData" },
 | 
			
		||||
	"status object |  beer data",
 | 
			
		||||
);
 | 
			
		||||
new Docs('beer', 'edit', '/api/v1/beer/edit', 'POST', 'beer edit api', undefined, { ...editExam, photos: 'optional field | max 4 images | formData' }, 'status object |  beer data');
 | 
			
		||||
export async function edit_post(req: Request, res: Response) {
 | 
			
		||||
	try {
 | 
			
		||||
		if (!isValidObjectId(req.body._id)) throw Log.error(400, "this is not valid _id");
 | 
			
		||||
		if (!isValidObjectId(req.body._id)) throw Log.error(400, 'this is not valid _id');
 | 
			
		||||
 | 
			
		||||
		if (req.files) {
 | 
			
		||||
			if (!req.body.imgs) {
 | 
			
		||||
				req.body.imgs = [];
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (typeof req.body.imgs == "string") {
 | 
			
		||||
			if (typeof req.body.imgs == 'string') {
 | 
			
		||||
				req.body.imgs = [req.body.imgs];
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (req.body.imgs.length + req.files.length > 4) {
 | 
			
		||||
				req.body.imgs.forEach((el: string[]) => {
 | 
			
		||||
					fs.rmSync(path.join(__dirname, "../../uploads/" + el));
 | 
			
		||||
					fs.rmSync(path.join(__dirname, '../../uploads/' + el));
 | 
			
		||||
				});
 | 
			
		||||
				throw Log.error(400, "exceeds the 4 image limit");
 | 
			
		||||
				throw Log.error(400, 'exceeds the 4 image limit');
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			const files: any = req.files;
 | 
			
		||||
@@ -117,11 +90,11 @@ export async function edit_post(req: Request, res: Response) {
 | 
			
		||||
		const beer = await Beer.findOneAndUpdate(new Types.ObjectId(req.body._id), payload, { new: true });
 | 
			
		||||
		res.json(Log.info(200, `beer ${req.body._id} edited`, beer));
 | 
			
		||||
	} catch (err: any) {
 | 
			
		||||
		if (err.code && typeof err.code == "number") {
 | 
			
		||||
		if (err.code && typeof err.code == 'number') {
 | 
			
		||||
			res.status(err.code).json(err);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		Log.error(500, "error in del_post", err);
 | 
			
		||||
		res.status(500).json(Log.error(500, "something went wrong"));
 | 
			
		||||
		Log.error(500, 'error in del_post', err);
 | 
			
		||||
		res.status(500).json(Log.error(500, 'something went wrong'));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,14 +1,14 @@
 | 
			
		||||
import { Request, Response } from "express";
 | 
			
		||||
import path from "path";
 | 
			
		||||
import fs from "fs";
 | 
			
		||||
import { Log } from "nork";
 | 
			
		||||
import { Docs } from "../services/docsService";
 | 
			
		||||
import { Request, Response } from 'express';
 | 
			
		||||
import path from 'path';
 | 
			
		||||
import fs from 'fs';
 | 
			
		||||
import {Log} from 'nork'
 | 
			
		||||
import { Docs } from '../services/docsService';
 | 
			
		||||
 | 
			
		||||
new Docs("docs", "get_all", "/api/v1", "GET", "Get docs json", undefined, undefined, "docs json");
 | 
			
		||||
new Docs('docs', 'get_all', '/api/v1', 'GET', 'Get docs json', undefined, undefined, 'docs json');
 | 
			
		||||
export function docs_get(req: Request, res: Response) {
 | 
			
		||||
	try {
 | 
			
		||||
		res.json(JSON.parse(fs.readFileSync(path.join(__dirname, "../public/api.json")).toString()));
 | 
			
		||||
		res.json(JSON.parse(fs.readFileSync(path.join(__dirname, '../public/api.json')).toString()));
 | 
			
		||||
	} catch (err: any) {
 | 
			
		||||
		res.status(500).json(Log.error(500, "api.json docs file does not exists under public folder"));
 | 
			
		||||
		res.status(500).json(Log.error(500, 'api.json docs file does not exists under public folder'));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,71 +0,0 @@
 | 
			
		||||
import { Request, Response } from "express";
 | 
			
		||||
import Review from "../models/Review";
 | 
			
		||||
import { isValidObjectId, Types } from "mongoose";
 | 
			
		||||
import { Log } from "nork";
 | 
			
		||||
import Docs from "../services/docsService";
 | 
			
		||||
import { addExam, delExam, IReview } from "../validators/reviewValidator";
 | 
			
		||||
 | 
			
		||||
new Docs(
 | 
			
		||||
	"review",
 | 
			
		||||
	"add",
 | 
			
		||||
	"/api/v1/review/add",
 | 
			
		||||
	"POST",
 | 
			
		||||
	"review add api",
 | 
			
		||||
	undefined,
 | 
			
		||||
	addExam,
 | 
			
		||||
	"status object | review object",
 | 
			
		||||
);
 | 
			
		||||
export async function add_post(req: Request, res: Response) {
 | 
			
		||||
	try {
 | 
			
		||||
		const data: IReview = req.body;
 | 
			
		||||
		data.user_id = res.locals.user._id
 | 
			
		||||
		const review = new Review(data);
 | 
			
		||||
		await review.save();
 | 
			
		||||
		res.status(201).json(Log.info(201, "review was added", review));
 | 
			
		||||
	} catch (err) {
 | 
			
		||||
		Log.error(500, "error while adding review", err);
 | 
			
		||||
		res.status(500).json(Log.error(500, "something went wrong"));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
new Docs(
 | 
			
		||||
	"review",
 | 
			
		||||
	"get",
 | 
			
		||||
	"/api/v1/review/get",
 | 
			
		||||
	"GET",
 | 
			
		||||
	"review get api",
 | 
			
		||||
	undefined,
 | 
			
		||||
	undefined,
 | 
			
		||||
	"status object | array of review objects",
 | 
			
		||||
);
 | 
			
		||||
export async function get_get(req: Request, res: Response) {
 | 
			
		||||
	try {
 | 
			
		||||
		const review = await Review.find({}, "-__v");
 | 
			
		||||
		res.status(200).json(Log.info(200, "reviews fetched", review));
 | 
			
		||||
	} catch (err) {
 | 
			
		||||
		Log.error(500, "error while geting reviews", err);
 | 
			
		||||
		res.status(500).json(Log.error(500, "something went wrong"));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
new Docs("review", "del", "/api/v1/review/del", "POST", "review del api", undefined, delExam, "status object");
 | 
			
		||||
export async function del_post(req: Request, res: Response) {
 | 
			
		||||
	try {
 | 
			
		||||
		if (!isValidObjectId(req.body._id)) throw Log.error(400, "this is not valid _id");
 | 
			
		||||
 | 
			
		||||
		const review = await Review.deleteOne(new Types.ObjectId(req.body._id));
 | 
			
		||||
 | 
			
		||||
		if (review.deletedCount > 0) {
 | 
			
		||||
			res.status(200).json(Log.info(200, `review ${req.body._id} deleted`));
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		throw Log.error(400, `review ${req.body._id} does not exist`);
 | 
			
		||||
	} catch (err: any) {
 | 
			
		||||
		if (err.code) {
 | 
			
		||||
			res.status(err.code).json(err);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		Log.error(500, "error in del_post", err);
 | 
			
		||||
		res.status(500).json(Log.error(500, "something went wrong"));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
import { Request, Response, NextFunction } from "express";
 | 
			
		||||
import jwt from "jsonwebtoken";
 | 
			
		||||
import env from "../config/environment";
 | 
			
		||||
import { Log } from "nork";
 | 
			
		||||
import User from "../models/User";
 | 
			
		||||
import { isValidObjectId } from "mongoose";
 | 
			
		||||
import { Request, Response, NextFunction } from 'express';
 | 
			
		||||
import jwt from 'jsonwebtoken';
 | 
			
		||||
import env from '../config/environment';
 | 
			
		||||
import { Log } from 'nork';
 | 
			
		||||
import User from '../models/User';
 | 
			
		||||
import { isValidObjectId } from 'mongoose';
 | 
			
		||||
 | 
			
		||||
export function requireAuth(req: Request, res: Response, next: NextFunction) {
 | 
			
		||||
	const token = req.cookies?.jwt;
 | 
			
		||||
@@ -12,40 +12,40 @@ export function requireAuth(req: Request, res: Response, next: NextFunction) {
 | 
			
		||||
		jwt.verify(token, env.JWT_SECRET, async (err: any, decodedToken: any) => {
 | 
			
		||||
			if (err) {
 | 
			
		||||
				// console.error(err.message)
 | 
			
		||||
				res.status(401).send(Log.error(401, "user is not authenticated"));
 | 
			
		||||
				res.status(401).send(Log.error(401, 'user is not authenticated'));
 | 
			
		||||
			}
 | 
			
		||||
			if (!err) {
 | 
			
		||||
				const user = await User.findById(decodedToken.id);
 | 
			
		||||
				if (user === null) {
 | 
			
		||||
					res.status(401).send(Log.error(401, "user is not authenticated"));
 | 
			
		||||
					res.status(401).send(Log.error(401, 'user is not authenticated'));
 | 
			
		||||
					return;
 | 
			
		||||
				}
 | 
			
		||||
				res.locals.user = user;
 | 
			
		||||
				Log.info(100, "user is authenticated");
 | 
			
		||||
				Log.info(100, 'user is authenticated');
 | 
			
		||||
				next();
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!token) {
 | 
			
		||||
		res.status(401).send(Log.error(401, "user is not authenticated"));
 | 
			
		||||
		res.status(401).send(Log.error(401, 'user is not authenticated'));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function requireVerified(req: Request, res: Response, next: NextFunction) {
 | 
			
		||||
	if (res.locals.user._id) {
 | 
			
		||||
		if (res.locals.user.verified) {
 | 
			
		||||
			Log.info(100, "user is verified");
 | 
			
		||||
			Log.info(100, 'user is verified');
 | 
			
		||||
			next();
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		res.status(403).json(Log.error(403, "user is not verified"));
 | 
			
		||||
		res.status(403).json(Log.error(403, 'user is not verified'));
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!res.locals.user._id) {
 | 
			
		||||
		res.status(401).send(Log.error(401, "user is not authenticated"));
 | 
			
		||||
		res.status(401).send(Log.error(401, 'user is not authenticated'));
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -53,33 +53,33 @@ export function requireVerified(req: Request, res: Response, next: NextFunction)
 | 
			
		||||
export class requireRole {
 | 
			
		||||
	static Admin(req: Request, res: Response, next: NextFunction) {
 | 
			
		||||
		if (res.locals.user.admin) {
 | 
			
		||||
			Log.info(100, "user is admin");
 | 
			
		||||
			Log.info(100, 'user is admin');
 | 
			
		||||
			next();
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		res.status(403).json(Log.error(403, "insufficient permissions"));
 | 
			
		||||
		res.status(403).json(Log.error(403, 'insufficient permissions'));
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	static Owner(req: Request, res: Response, next: NextFunction) {
 | 
			
		||||
		try {
 | 
			
		||||
			if (!isValidObjectId(req.body.domain_id)) {
 | 
			
		||||
				throw Log.error(400, "neznámé domain_id");
 | 
			
		||||
				throw Log.error(400, 'neznámé domain_id');
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			const domain = res.locals.user.domains.filter((domain: any) => domain.domain_id == req.body.domain_id);
 | 
			
		||||
			console.log(domain);
 | 
			
		||||
 | 
			
		||||
			if (domain.length < 1) {
 | 
			
		||||
				throw Log.error(400, "neznámé domain_id");
 | 
			
		||||
				throw Log.error(400, 'neznámé domain_id');
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (domain[0].role == 1) {
 | 
			
		||||
				Log.info(100, "user is owner");
 | 
			
		||||
				Log.info(100, 'user is owner');
 | 
			
		||||
				next();
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			res.status(403).json(Log.error(403, "insufficient permissions"));
 | 
			
		||||
			res.status(403).json(Log.error(403, 'insufficient permissions'));
 | 
			
		||||
			return;
 | 
			
		||||
		} catch (err: any) {
 | 
			
		||||
			res.status(400).json(err);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
import multer from "multer";
 | 
			
		||||
import { Log } from "nork";
 | 
			
		||||
import { Request, Response, NextFunction } from "express";
 | 
			
		||||
import multer from 'multer';
 | 
			
		||||
import {Log} from 'nork'
 | 
			
		||||
import { Request, Response, NextFunction } from 'express';
 | 
			
		||||
 | 
			
		||||
const validateMulterRequestMiddleware = async (err: any, req: Request, res: Response, next: NextFunction) => {
 | 
			
		||||
	if (err instanceof multer.MulterError) {
 | 
			
		||||
		Log.error(500, "error while processing uploaded files", JSON.stringify(err));
 | 
			
		||||
		res.status(400).json(Log.error(400, "error while processing uploaded files"));
 | 
			
		||||
		Log.error(500, 'error while processing uploaded files', JSON.stringify(err));
 | 
			
		||||
		res.status(400).json(Log.error(400, 'error while processing uploaded files'));
 | 
			
		||||
		return;
 | 
			
		||||
	} else {
 | 
			
		||||
		next();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import { Request, Response, NextFunction } from "express";
 | 
			
		||||
import { object, AnySchema } from "yup";
 | 
			
		||||
import { Log } from "nork";
 | 
			
		||||
import { Request, Response, NextFunction } from 'express';
 | 
			
		||||
import { object, AnySchema } from 'yup';
 | 
			
		||||
import { Log } from 'nork';
 | 
			
		||||
 | 
			
		||||
const validate = (schema: AnySchema) => async (req: Request, res: Response, next: NextFunction) => {
 | 
			
		||||
	try {
 | 
			
		||||
@@ -8,7 +8,7 @@ const validate = (schema: AnySchema) => async (req: Request, res: Response, next
 | 
			
		||||
 | 
			
		||||
		next();
 | 
			
		||||
	} catch (err: any) {
 | 
			
		||||
		return res.status(400).json(Log.error(400, "validation error", err));
 | 
			
		||||
		return res.status(400).json(Log.error(400, 'validation error', err));
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,34 +1,34 @@
 | 
			
		||||
import path from "path";
 | 
			
		||||
import { Schema, model } from "mongoose";
 | 
			
		||||
import { IBeer } from "../validators/beerValidator";
 | 
			
		||||
import path from 'path';
 | 
			
		||||
import { Schema, model } from 'mongoose';
 | 
			
		||||
import { IBeer } from '../validators/beerValidator';
 | 
			
		||||
 | 
			
		||||
const schema = new Schema<IBeer | any>(
 | 
			
		||||
	{
 | 
			
		||||
		name: {
 | 
			
		||||
			type: String,
 | 
			
		||||
			required: true,
 | 
			
		||||
			required: true
 | 
			
		||||
		},
 | 
			
		||||
		degree: {
 | 
			
		||||
			type: Number,
 | 
			
		||||
			required: true,
 | 
			
		||||
			required: true
 | 
			
		||||
		},
 | 
			
		||||
		packaging: {
 | 
			
		||||
			type: String,
 | 
			
		||||
			required: true,
 | 
			
		||||
			required: true
 | 
			
		||||
		},
 | 
			
		||||
		brand: {
 | 
			
		||||
			type: String,
 | 
			
		||||
			required: true,
 | 
			
		||||
			required: true
 | 
			
		||||
		},
 | 
			
		||||
		imgs: {
 | 
			
		||||
			type: Array,
 | 
			
		||||
			required: false,
 | 
			
		||||
			default: [],
 | 
			
		||||
		},
 | 
			
		||||
			default: []
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		timestamps: true,
 | 
			
		||||
	},
 | 
			
		||||
		timestamps: true
 | 
			
		||||
	}
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export default model(path.basename(__filename).split(".")[0], schema);
 | 
			
		||||
export default model(path.basename(__filename).split('.')[0], schema);
 | 
			
		||||
@@ -1,45 +0,0 @@
 | 
			
		||||
import path from "path";
 | 
			
		||||
import { Schema, model } from "mongoose";
 | 
			
		||||
import { IReview } from "../validators/reviewValidator";
 | 
			
		||||
 | 
			
		||||
const schema = new Schema<IReview | any>(
 | 
			
		||||
	{
 | 
			
		||||
		foam: {
 | 
			
		||||
			type: Number,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		bitter_sweetness: {
 | 
			
		||||
			type: Number,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		taste: {
 | 
			
		||||
			type: Number,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		packaging: {
 | 
			
		||||
			type: Number,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		sourness: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		would_again: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		beer_id: {
 | 
			
		||||
			type: String,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		user_id: {
 | 
			
		||||
			type: String,
 | 
			
		||||
			required: true,
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		timestamps: true,
 | 
			
		||||
	},
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export default model(path.basename(__filename).split(".")[0], schema);
 | 
			
		||||
@@ -1,26 +1,26 @@
 | 
			
		||||
import path from "path";
 | 
			
		||||
import { Schema, model } from "mongoose";
 | 
			
		||||
import { IUser } from "../validators/authValidator";
 | 
			
		||||
import path from 'path';
 | 
			
		||||
import { Schema, model } from 'mongoose';
 | 
			
		||||
import { IUser } from '../validators/authValidator';
 | 
			
		||||
 | 
			
		||||
const schema = new Schema<IUser>(
 | 
			
		||||
	{
 | 
			
		||||
		username: {
 | 
			
		||||
			type: String,
 | 
			
		||||
			required: true,
 | 
			
		||||
			required: true
 | 
			
		||||
		},
 | 
			
		||||
		email: {
 | 
			
		||||
			type: String,
 | 
			
		||||
			required: true,
 | 
			
		||||
			unique: true,
 | 
			
		||||
			unique: true
 | 
			
		||||
		},
 | 
			
		||||
		password: {
 | 
			
		||||
			type: String,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
			required: true
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		timestamps: true,
 | 
			
		||||
	},
 | 
			
		||||
		timestamps: true
 | 
			
		||||
	}
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export default model(path.basename(__filename).split(".")[0], schema);
 | 
			
		||||
export default model(path.basename(__filename).split('.')[0], schema);
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +1 @@
 | 
			
		||||
{"version":"2.0.0","endpoints":{"user":{"signup":{"name":"user","operation":"signup","route":"/api/v1/auth/signup","method":"POST","description":"user signup api","body":{"username":"testuser","email":"text@example.com","password":"Test1234"},"response":"status object"},"signin":{"name":"user","operation":"signin","route":"/api/v1/auth/signin","method":"POST","description":"user signin api","body":{"email":"text@example.com","password":"Test1234"},"response":"status object"},"logout":{"name":"user","operation":"logout","route":"/api/v1/auth/logout","method":"POST","description":"user logout api","body":{},"response":"status object"},"status":{"name":"user","operation":"status","route":"/api/v1/auth/status","method":"GET","description":"user login status api","response":"status code | user object"}},"beer":{"add":{"name":"beer","operation":"add","route":"/api/v1/beer/add","method":"POST","description":"beer add api","body":{"brand":"Pilsner Urqell","name":"Kozel","degree":11,"packaging":"can","photos":"optional field | max 4 images | formData"},"response":"status object | beer object"},"get":{"name":"beer","operation":"get","route":"/api/v1/beer/get","method":"GET","description":"beer get api","response":"status object | array of beer objects"},"del":{"name":"beer","operation":"del","route":"/api/v1/beer/del","method":"POST","description":"beer del api","body":{"_id":"6352b303b71cb62222f39895"},"response":"status object"},"edit":{"name":"beer","operation":"edit","route":"/api/v1/beer/edit","method":"POST","description":"beer edit api","body":{"_id":"6355b95dc03fad77bc380146","brand":"Pilsner Urqell","name":"Radegast","degree":12,"packaging":"bottle","imgs":[],"photos":"optional field | max 4 images | formData"},"response":"status object |  beer data"}},"docs":{"get_all":{"name":"docs","operation":"get_all","route":"/api/v1","method":"GET","description":"Get docs json","response":"docs json"}},"review":{"add":{"name":"review","operation":"add","route":"/api/v1/review/add","method":"POST","description":"review add api","body":{"beer_id":"6352b303b71cb62222f39895","foam":3,"bitter_sweetness":2,"taste":5,"packaging":3,"sourness":false,"would_again":true},"response":"status object | review object"},"get":{"name":"review","operation":"get","route":"/api/v1/review/get","method":"GET","description":"review get api","response":"status object | array of review objects"},"del":{"name":"review","operation":"del","route":"/api/v1/review/del","method":"POST","description":"review del api","body":{"_id":"6352b303b71cb62222f39895"},"response":"status object"}}}}
 | 
			
		||||
{"version":"2.0.0","endpoints":{"user":{"signup":{"name":"user","operation":"signup","route":"/api/v1/auth/signup","method":"POST","description":"user signup api","body":{"username":"testuser","email":"text@example.com","password":"Test1234"},"response":"status object"},"signin":{"name":"user","operation":"signin","route":"/api/v1/auth/signin","method":"POST","description":"user signin api","body":{"email":"text@example.com","password":"Test1234"},"response":"status object"},"logout":{"name":"user","operation":"logout","route":"/api/v1/auth/logout","method":"POST","description":"user logout api","body":{},"response":"status object"},"status":{"name":"user","operation":"status","route":"/api/v1/auth/status","method":"GET","description":"user login status api","response":"status code | user object"}},"beer":{"add":{"name":"beer","operation":"add","route":"/api/v1/beer/add","method":"POST","description":"beer add api","body":{"brand":"Pilsner Urqell","name":"Kozel","degree":11,"packaging":"can","photos":"optional field | max 4 images | formData"},"response":"status object | beer object"},"get":{"name":"beer","operation":"get","route":"/api/v1/beer/get","method":"GET","description":"beer get api","response":"status object | array of beer objects"},"del":{"name":"beer","operation":"del","route":"/api/v1/beer/del","method":"POST","description":"beer del api","body":{"_id":"6352b303b71cb62222f39895"},"response":"status object"},"edit":{"name":"beer","operation":"edit","route":"/api/v1/beer/edit","method":"POST","description":"beer edit api","body":{"_id":"6355b95dc03fad77bc380146","brand":"Pilsner Urqell","name":"Radegast","degree":12,"packaging":"bottle","imgs":[],"photos":"optional field | max 4 images | formData"},"response":"status object |  beer data"}},"docs":{"get_all":{"name":"docs","operation":"get_all","route":"/api/v1","method":"GET","description":"Get docs json","response":"docs json"}}}}
 | 
			
		||||
@@ -1,42 +1,29 @@
 | 
			
		||||
import { Router } from "express";
 | 
			
		||||
import multer from "multer";
 | 
			
		||||
import path from "path";
 | 
			
		||||
import multer from 'multer';
 | 
			
		||||
import path from 'path'
 | 
			
		||||
import * as authController from "../controllers/authController";
 | 
			
		||||
import * as beerController from "../controllers/beerController";
 | 
			
		||||
import * as docsController from "../controllers/docsController";
 | 
			
		||||
import * as reviewController from "../controllers/reviewController";
 | 
			
		||||
import * as beerController from "../controllers/beerController"
 | 
			
		||||
import * as docsController from "../controllers/docsController"
 | 
			
		||||
import { requireAuth } from "../middlewares/authMiddleware";
 | 
			
		||||
import validate from "../middlewares/validateRequest";
 | 
			
		||||
import valMulter from "../middlewares/validateMulterRequest";
 | 
			
		||||
import * as AuthVal from "../validators/authValidator";
 | 
			
		||||
import * as BVal from "../validators/beerValidator";
 | 
			
		||||
import * as RVal from "../validators/reviewValidator";
 | 
			
		||||
import validate from '../middlewares/validateRequest'
 | 
			
		||||
import valMulter from '../middlewares/validateMulterRequest';
 | 
			
		||||
import * as AuthVal from '../validators/authValidator'
 | 
			
		||||
import * as BVal from '../validators/beerValidator';
 | 
			
		||||
 | 
			
		||||
const upload = multer({ dest: path.resolve(__dirname, "../../uploads") });
 | 
			
		||||
const upload = multer({ dest: path.resolve(__dirname, '../../uploads') });
 | 
			
		||||
 | 
			
		||||
const router = Router();
 | 
			
		||||
 | 
			
		||||
router.get("/", docsController.docs_get);
 | 
			
		||||
router.get('/', docsController.docs_get);
 | 
			
		||||
 | 
			
		||||
router.post("/auth/signup",validate(AuthVal.signup) , authController.signup_post);
 | 
			
		||||
router.post("/auth/signin",validate(AuthVal.signin) , authController.signin_post);
 | 
			
		||||
router.post("/auth/logout", requireAuth, authController.logout_post);
 | 
			
		||||
router.get("/auth/status", requireAuth, authController.status_get);
 | 
			
		||||
 | 
			
		||||
router.post(
 | 
			
		||||
	"/beer/add",
 | 
			
		||||
	[requireAuth, upload.array("photos", 4), valMulter, validate(BVal.add)],
 | 
			
		||||
	beerController.add_post,
 | 
			
		||||
);
 | 
			
		||||
router.get("/beer/get", [requireAuth], beerController.get_get);
 | 
			
		||||
router.post("/beer/del", [requireAuth, validate(BVal.del)], beerController.del_post);
 | 
			
		||||
router.post(
 | 
			
		||||
	"/beer/edit",
 | 
			
		||||
	[requireAuth, upload.array("photos", 4), valMulter, validate(BVal.edit)],
 | 
			
		||||
	beerController.edit_post,
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
router.post("/review/add", [requireAuth, validate(RVal.add)], reviewController.add_post);
 | 
			
		||||
router.get("/review/get", requireAuth, reviewController.get_get);
 | 
			
		||||
router.post('/beer/add', [requireAuth, upload.array('photos', 4), valMulter, validate(BVal.add)], beerController.add_post);
 | 
			
		||||
router.get('/beer/get', [requireAuth], beerController.get_get);
 | 
			
		||||
router.post('/beer/del', [requireAuth, validate(BVal.del)], beerController.del_post);
 | 
			
		||||
router.post('/beer/edit', [requireAuth, upload.array('photos', 4), valMulter, validate(BVal.edit)], beerController.edit_post);
 | 
			
		||||
 | 
			
		||||
export default router;
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
import http from "http";
 | 
			
		||||
import { app } from "./app";
 | 
			
		||||
import env from "./config/environment";
 | 
			
		||||
import mongoose from "mongoose"; // TODO: dopsat nork module pro db
 | 
			
		||||
import mongoose from 'mongoose' // TODO: dopsat nork module pro db
 | 
			
		||||
import { Log } from "nork";
 | 
			
		||||
 | 
			
		||||
const port: number = env.APP_PORT || 8080;
 | 
			
		||||
@@ -23,11 +23,13 @@ export const server = http.createServer(app);
 | 
			
		||||
//	runServer()
 | 
			
		||||
//}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
(async () => {
 | 
			
		||||
	if (!process.env.DOCS_GEN) {
 | 
			
		||||
		try {
 | 
			
		||||
			await mongoose.connect(env.DB_URI);
 | 
			
		||||
			Log.info(200, "connected to db");
 | 
			
		||||
			Log.info(200, 'connected to db');
 | 
			
		||||
			server.listen(port, () => {
 | 
			
		||||
				Log.info(200, `Server is listening on http://localhost:${port}`);
 | 
			
		||||
			});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
import fs from "fs";
 | 
			
		||||
import path from "path";
 | 
			
		||||
import fs from 'fs';
 | 
			
		||||
import path from 'path';
 | 
			
		||||
 | 
			
		||||
export type HttpMethods = "POST" | "GET" | "PUT" | "DELETE";
 | 
			
		||||
export type ApiResponse = "status object" | Object | string;
 | 
			
		||||
export type HttpMethods = 'POST' | 'GET' | 'PUT' | 'DELETE';
 | 
			
		||||
export type ApiResponse = 'status object' | Object | string;
 | 
			
		||||
 | 
			
		||||
export class Docs {
 | 
			
		||||
	private name: string;
 | 
			
		||||
@@ -14,16 +14,7 @@ export class Docs {
 | 
			
		||||
	private body?: Object;
 | 
			
		||||
	private response?: ApiResponse;
 | 
			
		||||
 | 
			
		||||
	public constructor(
 | 
			
		||||
		name: string,
 | 
			
		||||
		operation: string,
 | 
			
		||||
		route: string,
 | 
			
		||||
		method: HttpMethods,
 | 
			
		||||
		description?: string,
 | 
			
		||||
		parameters?: Object[],
 | 
			
		||||
		body?: Object,
 | 
			
		||||
		response?: ApiResponse,
 | 
			
		||||
	) {
 | 
			
		||||
	public constructor(name: string, operation: string, route: string, method: HttpMethods, description?: string, parameters?: Object[], body?: Object, response?: ApiResponse) {
 | 
			
		||||
		this.name = name;
 | 
			
		||||
		this.operation = operation;
 | 
			
		||||
		this.route = route;
 | 
			
		||||
@@ -41,14 +32,14 @@ export class Docs {
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const jsonPath = path.join(__dirname, "../public/api.json");
 | 
			
		||||
		const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "../../package.json")).toString());
 | 
			
		||||
		const jsonPath = path.join(__dirname, '../public/api.json');
 | 
			
		||||
		const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '../../package.json')).toString());
 | 
			
		||||
 | 
			
		||||
		const jsonAPI = () => JSON.parse(fs.readFileSync(jsonPath).toString());
 | 
			
		||||
		const genJsonAPI = () => fs.writeFileSync(jsonPath, JSON.stringify({ version: pkg.version, endpoints: {} }));
 | 
			
		||||
 | 
			
		||||
		if (!fs.existsSync(path.join(__dirname, "../public"))) {
 | 
			
		||||
			fs.mkdirSync(path.join(__dirname, "../public"));
 | 
			
		||||
		if (!fs.existsSync(path.join(__dirname, '../public'))) {
 | 
			
		||||
			fs.mkdirSync(path.join(__dirname, '../public'));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!fs.existsSync(jsonPath)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import * as shell from "shelljs";
 | 
			
		||||
import * as shell from 'shelljs';
 | 
			
		||||
 | 
			
		||||
// Copy all the view templates
 | 
			
		||||
//shell.cp('-R', 'src/views', 'dist/')
 | 
			
		||||
shell.cp("-R", "src/public", "dist/");
 | 
			
		||||
shell.cp("-u", "src/.env", "dist/");
 | 
			
		||||
shell.cp('-R', 'src/public', 'dist/');
 | 
			
		||||
shell.cp('-u', 'src/.env', 'dist/');
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
const mongoose = require("mongoose");
 | 
			
		||||
const { MongoMemoryServer } = require("mongodb-memory-server");
 | 
			
		||||
const mongoose = require('mongoose');
 | 
			
		||||
const { MongoMemoryServer } = require('mongodb-memory-server');
 | 
			
		||||
 | 
			
		||||
let mongo: any = null;
 | 
			
		||||
 | 
			
		||||
const connectDB = async () => {
 | 
			
		||||
	mongo = await MongoMemoryServer.create({ binary: { os: { os: "linux", dist: "ubuntu", release: "18.04" } } }); // TODO: check that host OS is Void Linux, else remove the argument
 | 
			
		||||
	mongo = await MongoMemoryServer.create({ binary: { os: { os: 'linux', dist: 'ubuntu', release: '18.04' } } }); // TODO: check that host OS is Void Linux, else remove the argument 
 | 
			
		||||
	const uri = mongo.getUri();
 | 
			
		||||
 | 
			
		||||
	await mongoose.connect(uri);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
import * as yup from "yup";
 | 
			
		||||
import YupPassword from "yup-password";
 | 
			
		||||
import * as yup from 'yup';
 | 
			
		||||
import YupPassword from 'yup-password';
 | 
			
		||||
YupPassword(yup);
 | 
			
		||||
import { Schema } from "mongoose";
 | 
			
		||||
import { Schema } from 'mongoose';
 | 
			
		||||
 | 
			
		||||
interface mongooseAddition {
 | 
			
		||||
	_id?: Schema.Types.ObjectId;
 | 
			
		||||
@@ -13,22 +13,22 @@ interface mongooseAddition {
 | 
			
		||||
export const signup = yup.object({
 | 
			
		||||
	username: yup.string().required(),
 | 
			
		||||
	email: yup.string().email().required(),
 | 
			
		||||
	password: yup.string().min(8).minLowercase(1).minUppercase(1).minNumbers(1).required(),
 | 
			
		||||
	password: yup.string().min(8).minLowercase(1).minUppercase(1).minNumbers(1).required()
 | 
			
		||||
});
 | 
			
		||||
export interface IUser extends yup.InferType<typeof signup>, mongooseAddition {}
 | 
			
		||||
export const signupExam: IUser = {
 | 
			
		||||
	username: "testuser",
 | 
			
		||||
	email: "text@example.com",
 | 
			
		||||
	password: "Test1234",
 | 
			
		||||
	username: 'testuser',
 | 
			
		||||
	email: 'text@example.com',
 | 
			
		||||
	password: 'Test1234'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// SignIn
 | 
			
		||||
export const signin = yup.object({
 | 
			
		||||
	email: yup.string().email().required(),
 | 
			
		||||
	password: yup.string().min(8).minLowercase(1).minUppercase(1).minNumbers(1).required(),
 | 
			
		||||
	password: yup.string().min(8).minLowercase(1).minUppercase(1).minNumbers(1).required()
 | 
			
		||||
});
 | 
			
		||||
export interface ISignin extends yup.InferType<typeof signin>, mongooseAddition {}
 | 
			
		||||
export const signinExam: ISignin = {
 | 
			
		||||
	email: "text@example.com",
 | 
			
		||||
	password: "Test1234",
 | 
			
		||||
	email: 'text@example.com',
 | 
			
		||||
	password: 'Test1234'
 | 
			
		||||
};
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
import * as yup from "yup";
 | 
			
		||||
import YupPassword from "yup-password";
 | 
			
		||||
import * as yup from 'yup';
 | 
			
		||||
import YupPassword from 'yup-password';
 | 
			
		||||
YupPassword(yup);
 | 
			
		||||
import { Schema } from "mongoose";
 | 
			
		||||
import { Schema } from 'mongoose';
 | 
			
		||||
 | 
			
		||||
interface mongooseAddition {
 | 
			
		||||
	_id?: Schema.Types.ObjectId;
 | 
			
		||||
@@ -14,23 +14,23 @@ export const add = yup.object({
 | 
			
		||||
	brand: yup.string().required(),
 | 
			
		||||
	name: yup.string().required(),
 | 
			
		||||
	degree: yup.number().required(),
 | 
			
		||||
	packaging: yup.string().required(),
 | 
			
		||||
	packaging: yup.string().required()
 | 
			
		||||
});
 | 
			
		||||
export interface IBeer extends yup.InferType<typeof add>, mongooseAddition {}
 | 
			
		||||
export const addExam: IBeer = {
 | 
			
		||||
	brand: "Pilsner Urqell",
 | 
			
		||||
	name: "Kozel",
 | 
			
		||||
	brand: 'Pilsner Urqell',
 | 
			
		||||
	name: 'Kozel',
 | 
			
		||||
	degree: 11,
 | 
			
		||||
	packaging: "can",
 | 
			
		||||
	packaging: 'can'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Remove
 | 
			
		||||
export const del = yup.object({
 | 
			
		||||
	_id: yup.string().required(),
 | 
			
		||||
	_id: yup.string().required()
 | 
			
		||||
});
 | 
			
		||||
export interface IDel extends yup.InferType<typeof del> {}
 | 
			
		||||
export const delExam: IDel = {
 | 
			
		||||
	_id: "6352b303b71cb62222f39895",
 | 
			
		||||
	_id: '6352b303b71cb62222f39895'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Update
 | 
			
		||||
@@ -45,13 +45,8 @@ export const edit = yup.object({
 | 
			
		||||
  //  Array.isArray(imgs) ? schema.array() : schema.string()
 | 
			
		||||
  //)
 | 
			
		||||
 | 
			
		||||
	imgs: yup
 | 
			
		||||
		.mixed()
 | 
			
		||||
		.test(
 | 
			
		||||
			"is-array-or-string",
 | 
			
		||||
			"imgs must be either an array or a string",
 | 
			
		||||
			(value) => Array.isArray(value) || typeof value === "string",
 | 
			
		||||
		),
 | 
			
		||||
  imgs: yup.mixed().test('is-array-or-string', 'imgs must be either an array or a string', value =>  
 | 
			
		||||
    Array.isArray(value) || typeof value === 'string')
 | 
			
		||||
 | 
			
		||||
	//imgs: yup.mixed().when('isArray', {
 | 
			
		||||
	//	is: Array.isArray,
 | 
			
		||||
@@ -61,10 +56,10 @@ export const edit = yup.object({
 | 
			
		||||
});
 | 
			
		||||
export interface IEdit extends yup.InferType<typeof edit> {}
 | 
			
		||||
export const editExam: IEdit = {
 | 
			
		||||
	_id: "6355b95dc03fad77bc380146",
 | 
			
		||||
	brand: "Pilsner Urqell",
 | 
			
		||||
	name: "Radegast",
 | 
			
		||||
	_id: '6355b95dc03fad77bc380146',
 | 
			
		||||
	brand: 'Pilsner Urqell',
 | 
			
		||||
	name: 'Radegast',
 | 
			
		||||
	degree: 12,
 | 
			
		||||
	packaging: "bottle",
 | 
			
		||||
	imgs: [],
 | 
			
		||||
	packaging: 'bottle',
 | 
			
		||||
	imgs: []
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,43 +0,0 @@
 | 
			
		||||
import * as yup from "yup";
 | 
			
		||||
import mongoose, { Schema } from "mongoose";
 | 
			
		||||
 | 
			
		||||
interface mongooseAddition {
 | 
			
		||||
	_id?: Schema.Types.ObjectId;
 | 
			
		||||
	createdAt?: Schema.Types.Date;
 | 
			
		||||
	updatedAt?: Schema.Types.Date;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let objectIdSchema = yup
 | 
			
		||||
	.string()
 | 
			
		||||
	.test("is-objectId", "Invalid ObjectId", (value: any) => mongoose.Types.ObjectId.isValid(value));
 | 
			
		||||
 | 
			
		||||
// Add
 | 
			
		||||
export const add = yup.object({
 | 
			
		||||
	beer_id: objectIdSchema,
 | 
			
		||||
	foam: yup.number().min(1).max(3).required(),
 | 
			
		||||
	bitter_sweetness: yup.number().min(1).max(5).required(),
 | 
			
		||||
	taste: yup.number().min(1).max(5).required(),
 | 
			
		||||
	packaging: yup.number().min(1).max(5).required(),
 | 
			
		||||
	sourness: yup.boolean().required(),
 | 
			
		||||
	would_again: yup.boolean().required(),
 | 
			
		||||
	user_id: yup.string().notRequired()
 | 
			
		||||
});
 | 
			
		||||
export interface IReview extends yup.InferType<typeof add>, mongooseAddition {}
 | 
			
		||||
export const addExam: IReview = {
 | 
			
		||||
	beer_id: "6352b303b71cb62222f39895",
 | 
			
		||||
	foam: 3,
 | 
			
		||||
	bitter_sweetness: 2,
 | 
			
		||||
	taste: 5,
 | 
			
		||||
	packaging: 3,
 | 
			
		||||
	sourness: false,
 | 
			
		||||
	would_again: true,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Remove
 | 
			
		||||
export const del = yup.object({
 | 
			
		||||
	_id: objectIdSchema.required(),
 | 
			
		||||
});
 | 
			
		||||
export interface IDel extends yup.InferType<typeof del> {}
 | 
			
		||||
export const delExam: IDel = {
 | 
			
		||||
	_id: "6352b303b71cb62222f39895",
 | 
			
		||||
};
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { connectDB, dropDB, dropCollections } from "../src/utils/test_mongodb";
 | 
			
		||||
import { connectDB, dropDB, dropCollections } from '../src/utils/test_mongodb';
 | 
			
		||||
 | 
			
		||||
beforeAll(async () => {
 | 
			
		||||
	await connectDB();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,22 +1,22 @@
 | 
			
		||||
import supertest from "supertest";
 | 
			
		||||
import { app } from "../src/app";
 | 
			
		||||
import { connectDB, dropDB, dropCollections } from "../src/utils/test_mongodb";
 | 
			
		||||
import supertest from 'supertest';
 | 
			
		||||
import { app } from '../src/app';
 | 
			
		||||
import { connectDB, dropDB, dropCollections } from '../src/utils/test_mongodb';
 | 
			
		||||
 | 
			
		||||
const request = supertest(app);
 | 
			
		||||
 | 
			
		||||
export const getJWT = async () => {
 | 
			
		||||
	try {
 | 
			
		||||
		const resReg: any = await request.post("/api/v1/auth/signup").send({
 | 
			
		||||
			email: "test@example.local",
 | 
			
		||||
			password: "admin1234",
 | 
			
		||||
			username: "Test Test",
 | 
			
		||||
		const resReg: any = await request.post('/api/v1/auth/signup').send({
 | 
			
		||||
			email: 'test@example.local',
 | 
			
		||||
			password: 'admin1234',
 | 
			
		||||
			username: 'Test Test'
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		const resLog: any = await request.post("/api/auth/login").send({
 | 
			
		||||
			email: "test@example.local",
 | 
			
		||||
			password: "admin1234",
 | 
			
		||||
		const resLog: any = await request.post('/api/auth/login').send({
 | 
			
		||||
			email: 'test@example.local',
 | 
			
		||||
			password: 'admin1234'
 | 
			
		||||
		});
 | 
			
		||||
		if (resLog.statusCode != 200) throw "error while logging in";
 | 
			
		||||
		if (resLog.statusCode != 200) throw 'error while logging in';
 | 
			
		||||
 | 
			
		||||
		const body = JSON.parse(resLog.text);
 | 
			
		||||
		return Promise.resolve(body.data.jwt);
 | 
			
		||||
@@ -31,194 +31,194 @@ export const getJWT = async () => {
 | 
			
		||||
 * @returns JWT cookie
 | 
			
		||||
 */
 | 
			
		||||
export async function login(): Promise<string> {
 | 
			
		||||
	const res = await request.post("/api/v1/auth/signin").send({
 | 
			
		||||
		email: "thisistest@host.local",
 | 
			
		||||
		password: "Admin1234",
 | 
			
		||||
	const res = await request.post('/api/v1/auth/signin').send({
 | 
			
		||||
		email: 'thisistest@host.local',
 | 
			
		||||
		password: 'Admin1234'
 | 
			
		||||
	});
 | 
			
		||||
	return res.headers["set-cookie"];
 | 
			
		||||
	return res.headers['set-cookie'];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function signup(): Promise<boolean> {
 | 
			
		||||
	const res = await request.post("/api/v1/auth/signup").send({
 | 
			
		||||
		email: "thisistest@host.local",
 | 
			
		||||
		password: "Admin1234",
 | 
			
		||||
		username: "Test Test",
 | 
			
		||||
	const res = await request.post('/api/v1/auth/signup').send({
 | 
			
		||||
		email: 'thisistest@host.local',
 | 
			
		||||
		password: 'Admin1234',
 | 
			
		||||
		username: 'Test Test'
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	if (res.statusCode == 201) return true;
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
describe("POST /api/v1/auth/signup", () => {
 | 
			
		||||
	describe("should drop validation error", () => {
 | 
			
		||||
		it("should drop 400 (empty request))", async () => {
 | 
			
		||||
			const res: any = await request.post("/api/v1/auth/signup").send({});
 | 
			
		||||
describe('POST /api/v1/auth/signup', () => {
 | 
			
		||||
	describe('should drop validation error', () => {
 | 
			
		||||
		it('should drop 400 (empty request))', async () => {
 | 
			
		||||
			const res: any = await request.post('/api/v1/auth/signup').send({});
 | 
			
		||||
			expect(res.statusCode).toBe(400);
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		it("should drop 400 (email))", async () => {
 | 
			
		||||
			const res: any = await request.post("/api/v1/auth/signup").send({
 | 
			
		||||
				email: "",
 | 
			
		||||
				username: "User Admin",
 | 
			
		||||
				password: "Admin1234",
 | 
			
		||||
		it('should drop 400 (email))', async () => {
 | 
			
		||||
			const res: any = await request.post('/api/v1/auth/signup').send({
 | 
			
		||||
				email: '',
 | 
			
		||||
				username: 'User Admin',
 | 
			
		||||
				password: 'Admin1234'
 | 
			
		||||
			});
 | 
			
		||||
			console.log(res);
 | 
			
		||||
            console.log(res)
 | 
			
		||||
			const body = JSON.parse(res.text);
 | 
			
		||||
			expect(res.statusCode).toBe(400);
 | 
			
		||||
			expect(body.data.path).toBe("email");
 | 
			
		||||
			expect(body.data.path).toBe('email');
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		it("should drop 400 (username))", async () => {
 | 
			
		||||
			const res: any = await request.post("/api/v1/auth/signup").send({
 | 
			
		||||
				email: "admin@localhost.local",
 | 
			
		||||
				username: "",
 | 
			
		||||
				password: "Admin1234",
 | 
			
		||||
		it('should drop 400 (username))', async () => {
 | 
			
		||||
			const res: any = await request.post('/api/v1/auth/signup').send({
 | 
			
		||||
				email: 'admin@localhost.local',
 | 
			
		||||
				username: '',
 | 
			
		||||
				password: 'Admin1234'
 | 
			
		||||
			});
 | 
			
		||||
			const body = JSON.parse(res.text);
 | 
			
		||||
 | 
			
		||||
			expect(res.statusCode).toBe(400);
 | 
			
		||||
			expect(body.data.path).toBe("username");
 | 
			
		||||
			expect(body.data.path).toBe('username');
 | 
			
		||||
		});
 | 
			
		||||
		it("should drop 400 (password))", async () => {
 | 
			
		||||
			const res: any = await request.post("/api/v1/auth/signup").send({
 | 
			
		||||
				email: "admin@localhost.local",
 | 
			
		||||
				username: "User Admin",
 | 
			
		||||
				password: "",
 | 
			
		||||
		it('should drop 400 (password))', async () => {
 | 
			
		||||
			const res: any = await request.post('/api/v1/auth/signup').send({
 | 
			
		||||
				email: 'admin@localhost.local',
 | 
			
		||||
				username: 'User Admin',
 | 
			
		||||
				password: ''
 | 
			
		||||
			});
 | 
			
		||||
			const body = JSON.parse(res.text);
 | 
			
		||||
 | 
			
		||||
			expect(res.statusCode).toBe(400);
 | 
			
		||||
			expect(body.data.path).toBe("password");
 | 
			
		||||
			expect(body.data.path).toBe('password');
 | 
			
		||||
		});
 | 
			
		||||
		it("should drop 400 (password - min 8 chars", async () => {
 | 
			
		||||
			const res = await request.post("/api/v1/auth/signup").send({
 | 
			
		||||
				email: "admin@localhost.local",
 | 
			
		||||
				username: "User Admin",
 | 
			
		||||
				password: "Admin12",
 | 
			
		||||
		it('should drop 400 (password - min 8 chars', async () => {
 | 
			
		||||
			const res = await request.post('/api/v1/auth/signup').send({
 | 
			
		||||
				email: 'admin@localhost.local',
 | 
			
		||||
				username: 'User Admin',
 | 
			
		||||
				password: 'Admin12'
 | 
			
		||||
			});
 | 
			
		||||
			const body = JSON.parse(res.text);
 | 
			
		||||
 | 
			
		||||
			expect(res.statusCode).toBe(400);
 | 
			
		||||
			expect(body.data.path).toBe("password");
 | 
			
		||||
			expect(body.data.path).toBe('password');
 | 
			
		||||
		});
 | 
			
		||||
		it("should drop 400 (password - min 1 number", async () => {
 | 
			
		||||
			const res = await request.post("/api/v1/auth/signup").send({
 | 
			
		||||
				email: "admin@localhost.local",
 | 
			
		||||
				username: "User Admin",
 | 
			
		||||
				password: "Adminadmin",
 | 
			
		||||
		it('should drop 400 (password - min 1 number', async () => {
 | 
			
		||||
			const res = await request.post('/api/v1/auth/signup').send({
 | 
			
		||||
				email: 'admin@localhost.local',
 | 
			
		||||
				username: 'User Admin',
 | 
			
		||||
				password: 'Adminadmin'
 | 
			
		||||
			});
 | 
			
		||||
			const body = JSON.parse(res.text);
 | 
			
		||||
 | 
			
		||||
			expect(res.statusCode).toBe(400);
 | 
			
		||||
			expect(body.data.path).toBe("password");
 | 
			
		||||
			expect(body.data.path).toBe('password');
 | 
			
		||||
		});
 | 
			
		||||
		it("should drop 400 (password - min 1 uppercase", async () => {
 | 
			
		||||
			const res = await request.post("/api/v1/auth/signup").send({
 | 
			
		||||
				email: "admin@localhost.local",
 | 
			
		||||
				username: "User Admin",
 | 
			
		||||
				password: "admin1234",
 | 
			
		||||
		it('should drop 400 (password - min 1 uppercase', async () => {
 | 
			
		||||
			const res = await request.post('/api/v1/auth/signup').send({
 | 
			
		||||
				email: 'admin@localhost.local',
 | 
			
		||||
				username: 'User Admin',
 | 
			
		||||
				password: 'admin1234'
 | 
			
		||||
			});
 | 
			
		||||
			const body = JSON.parse(res.text);
 | 
			
		||||
 | 
			
		||||
			expect(res.statusCode).toBe(400);
 | 
			
		||||
			expect(body.data.path).toBe("password");
 | 
			
		||||
			expect(body.data.path).toBe('password');
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should register an user", async () => {
 | 
			
		||||
		const res: any = await request.post("/api/v1/auth/signup").send({
 | 
			
		||||
			email: "thisistest@host.local",
 | 
			
		||||
			password: "Admin1234",
 | 
			
		||||
			username: "Test Test",
 | 
			
		||||
	test('should register an user', async () => {
 | 
			
		||||
		const res: any = await request.post('/api/v1/auth/signup').send({
 | 
			
		||||
			email: 'thisistest@host.local',
 | 
			
		||||
			password: 'Admin1234',
 | 
			
		||||
			username: 'Test Test'
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(201);
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe("POST /api/v1/auth/signin", () => {
 | 
			
		||||
	const url = "/api/v1/auth/signin";
 | 
			
		||||
describe('POST /api/v1/auth/signin', () => {
 | 
			
		||||
	const url = '/api/v1/auth/signin';
 | 
			
		||||
 | 
			
		||||
	describe("should drop an validation error", () => {
 | 
			
		||||
		it("should drop 400 (empty)", async () => {
 | 
			
		||||
	describe('should drop an validation error', () => {
 | 
			
		||||
		it('should drop 400 (empty)', async () => {
 | 
			
		||||
			const res = await request.post(url).send();
 | 
			
		||||
 | 
			
		||||
			expect(res.statusCode).toBe(400);
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		it("should drop 400 (email)", async () => {
 | 
			
		||||
		it('should drop 400 (email)', async () => {
 | 
			
		||||
			const res = await request.post(url).send({
 | 
			
		||||
				password: "Admin1234",
 | 
			
		||||
				password: 'Admin1234'
 | 
			
		||||
			});
 | 
			
		||||
			const body = JSON.parse(res.text);
 | 
			
		||||
 | 
			
		||||
			expect(res.statusCode).toBe(400);
 | 
			
		||||
			expect(body.data.path).toBe("email");
 | 
			
		||||
			expect(body.data.path).toBe('email');
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		it("should drop 400 (password)", async () => {
 | 
			
		||||
		it('should drop 400 (password)', async () => {
 | 
			
		||||
			const res = await request.post(url).send({
 | 
			
		||||
				email: "thisistest@host.local",
 | 
			
		||||
				email: 'thisistest@host.local'
 | 
			
		||||
			});
 | 
			
		||||
			const body = JSON.parse(res.text);
 | 
			
		||||
 | 
			
		||||
			expect(res.statusCode).toBe(400);
 | 
			
		||||
			expect(body.data.path).toBe("password");
 | 
			
		||||
			expect(body.data.path).toBe('password');
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 401", async () => {
 | 
			
		||||
	test('should drop 401', async () => {
 | 
			
		||||
		const res = await request.post(url).send({
 | 
			
		||||
			email: "thisistest@host.local",
 | 
			
		||||
			password: "Test12365465132",
 | 
			
		||||
			email: 'thisistest@host.local',
 | 
			
		||||
			password: 'Test12365465132'
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(401);
 | 
			
		||||
		expect(res.header["set-cookie"][0]).toContain("jwt=; Max-Age=0");
 | 
			
		||||
		expect(res.header["set-cookie"][1]).toContain("auth=false");
 | 
			
		||||
		expect(res.header['set-cookie'][0]).toContain("jwt=; Max-Age=0")
 | 
			
		||||
		expect(res.header['set-cookie'][1]).toContain("auth=false")
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should login an user", async () => {
 | 
			
		||||
	test('should login an user', async () => {
 | 
			
		||||
		const res: any = await request.post(url).send({
 | 
			
		||||
			email: "thisistest@host.local",
 | 
			
		||||
			password: "Admin1234",
 | 
			
		||||
			email: 'thisistest@host.local',
 | 
			
		||||
			password: 'Admin1234'
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(200);
 | 
			
		||||
		expect(res.header["set-cookie"][0]).toContain("jwt=");
 | 
			
		||||
		expect(res.header["set-cookie"][1]).toContain("auth=true");
 | 
			
		||||
		expect(res.header['set-cookie'][0]).toContain("jwt=")
 | 
			
		||||
		expect(res.header['set-cookie'][1]).toContain("auth=true")
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe("POST /api/v1/auth/logout", () => {
 | 
			
		||||
	const url = "/api/v1/auth/logout";
 | 
			
		||||
	test("should drop 401 error", async () => {
 | 
			
		||||
describe('POST /api/v1/auth/logout', () => {
 | 
			
		||||
	const url = '/api/v1/auth/logout';
 | 
			
		||||
	test('should drop 401 error', async () => {
 | 
			
		||||
		const res = await request.post(url).send({});
 | 
			
		||||
		expect(res.statusCode).toBe(401);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should logout an user", async () => {
 | 
			
		||||
	test('should logout an user', async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
		const res = await request.post(url).set("Cookie", jwt).send();
 | 
			
		||||
		const res = await request.post(url).set('Cookie', jwt).send();
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(200);
 | 
			
		||||
		expect(res.header["set-cookie"][0]).toContain("jwt=; Max-Age=0");
 | 
			
		||||
		expect(res.header["set-cookie"][1]).toContain("auth=false");
 | 
			
		||||
		expect(res.header['set-cookie'][0]).toContain("jwt=; Max-Age=0")
 | 
			
		||||
		expect(res.header['set-cookie'][1]).toContain("auth=false")
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe("GET /api/v1/auth/status", () => {
 | 
			
		||||
	const url = "/api/v1/auth/status";
 | 
			
		||||
	test("should return login status 401", async () => {
 | 
			
		||||
describe('GET /api/v1/auth/status', () => {
 | 
			
		||||
	const url = '/api/v1/auth/status';
 | 
			
		||||
	test('should return login status 401', async () => {
 | 
			
		||||
		const res = await request.get(url).send();
 | 
			
		||||
		expect(res.statusCode).toBe(401);
 | 
			
		||||
	});
 | 
			
		||||
	test("should return login status 200", async () => {
 | 
			
		||||
	test('should return login status 200', async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
		const res = await request.get(url).set("Cookie", jwt).send();
 | 
			
		||||
		const res = await request.get(url).set('Cookie', jwt).send();
 | 
			
		||||
		expect(res.statusCode).toBe(200);
 | 
			
		||||
		expect(res.body.data.username).toBe("Test Test");
 | 
			
		||||
		expect(res.body.data.email).toBe("thisistest@host.local");
 | 
			
		||||
		expect(res.body.data.password).toBeUndefined();
 | 
			
		||||
		expect(res.body.data.username).toBe("Test Test")
 | 
			
		||||
		expect(res.body.data.email).toBe("thisistest@host.local")
 | 
			
		||||
		expect(res.body.data.password).toBeUndefined()
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
@@ -1,193 +1,166 @@
 | 
			
		||||
import supertest from "supertest";
 | 
			
		||||
import { app } from "../src/app";
 | 
			
		||||
import { login } from "./auth.test";
 | 
			
		||||
import { addExam, delExam, editExam } from "../src/validators/beerValidator";
 | 
			
		||||
import supertest from 'supertest';
 | 
			
		||||
import { app } from '../src/app';
 | 
			
		||||
import { login } from './auth.test';
 | 
			
		||||
import { addExam, delExam, editExam } from '../src/validators/beerValidator';
 | 
			
		||||
 | 
			
		||||
const request = supertest(app);
 | 
			
		||||
 | 
			
		||||
describe("POST /api/v1/beer/add", () => {
 | 
			
		||||
	const url = "/api/v1/beer/add";
 | 
			
		||||
	test("should drop 401 error", async () => {
 | 
			
		||||
describe('POST /api/v1/beer/add', () => {
 | 
			
		||||
	const url = '/api/v1/beer/add';
 | 
			
		||||
	test('should drop 401 error', async () => {
 | 
			
		||||
		const res = await request.post(url).send({});
 | 
			
		||||
		expect(res.statusCode).toBe(401);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 400 ()", async () => {
 | 
			
		||||
	test('should drop 400 ()', async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
		const res = await request.post(url).set("Cookie", jwt).send({});
 | 
			
		||||
		const res = await request.post(url).set('Cookie', jwt).send({});
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(400);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 400 (name)", async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
		const body: any = { ...addExam };
 | 
			
		||||
		delete body.name;
 | 
			
		||||
		const res = await request.post(url).set("Cookie", jwt).send(body);
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(400);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 400 (degree)", async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
		const body: any = { ...addExam };
 | 
			
		||||
		delete body.degree;
 | 
			
		||||
		const res = await request.post(url).set("Cookie", jwt).send(body);
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(400);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 400 (packaging)", async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
		const body: any = { ...addExam };
 | 
			
		||||
		delete body.packaging;
 | 
			
		||||
		const res = await request.post(url).set("Cookie", jwt).send(body);
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(400);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 400 (brand)", async () => {
 | 
			
		||||
	test('should drop 400 (brand)', async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
		const body: any = { ...addExam };
 | 
			
		||||
		delete body.brand;
 | 
			
		||||
		const res = await request.post(url).set("Cookie", jwt).send(body);
 | 
			
		||||
		const res = await request.post(url).set('Cookie', jwt).send(body);
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(400);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 201", async () => {
 | 
			
		||||
	test('should drop 201', async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
		const res = await request.post(url).set("Cookie", jwt).send(addExam);
 | 
			
		||||
		const res = await request.post(url).set('Cookie', jwt).send(addExam);
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(201);
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe("GET /api/v1/beer/get", () => {
 | 
			
		||||
	const url = "/api/v1/beer/get";
 | 
			
		||||
describe('GET /api/v1/beer/get', () => {
 | 
			
		||||
	const url = '/api/v1/beer/get';
 | 
			
		||||
 | 
			
		||||
	test("should drop 401", async () => {
 | 
			
		||||
	test('should drop 401', async () => {
 | 
			
		||||
		const res = await request.get(url).send();
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(401);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 200", async () => {
 | 
			
		||||
	test('should drop 200', async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
		const res = await request.get(url).set("Cookie", jwt).send();
 | 
			
		||||
		const res = await request.get(url).set('Cookie', jwt).send();
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(200);
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe("POST /api/v1/beer/del", () => {
 | 
			
		||||
	const url = "/api/v1/beer/del";
 | 
			
		||||
describe('POST /api/v1/beer/del', () => {
 | 
			
		||||
	const url = '/api/v1/beer/del';
 | 
			
		||||
 | 
			
		||||
	test("should drop 401", async () => {
 | 
			
		||||
	test('should drop 401', async () => {
 | 
			
		||||
		const res = await request.post(url).send();
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(401);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 400", async () => {
 | 
			
		||||
	test('should drop 400', async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
		const res = await request.post(url).set("Cookie", jwt).send(delExam);
 | 
			
		||||
		const res = await request.post(url).set('Cookie', jwt).send(delExam);
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(400);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 400", async () => {
 | 
			
		||||
	test('should drop 400', async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
		const res = await request.post(url).set("Cookie", jwt).send({
 | 
			
		||||
			_id: "thisWillNotWork",
 | 
			
		||||
		const res = await request.post(url).set('Cookie', jwt).send({
 | 
			
		||||
			_id: 'thisWillNotWork'
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(400);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 200", async () => {
 | 
			
		||||
	test('should drop 200', async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
		const req = await request.post("/api/v1/beer/add").set("Cookie", jwt).send(addExam);
 | 
			
		||||
		const req = await request.post('/api/v1/beer/add').set('Cookie', jwt).send(addExam);
 | 
			
		||||
		const id = req.body.data._id;
 | 
			
		||||
		const res = await request.post(url).set("Cookie", jwt).send({
 | 
			
		||||
			_id: id,
 | 
			
		||||
		const res = await request.post(url).set('Cookie', jwt).send({
 | 
			
		||||
			_id: id
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(200);
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe("POST /api/v1/beer/edit", () => {
 | 
			
		||||
	const url = "/api/v1/beer/edit";
 | 
			
		||||
describe('POST /api/v1/beer/edit', () => {
 | 
			
		||||
	const url = '/api/v1/beer/edit';
 | 
			
		||||
 | 
			
		||||
	test("should drop 401", async () => {
 | 
			
		||||
	test('should drop 401', async () => {
 | 
			
		||||
		const res = await request.post(url).send();
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(401);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 400", async () => {
 | 
			
		||||
	test('should drop 400', async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
 | 
			
		||||
		const payload: any = { ...editExam };
 | 
			
		||||
		delete payload._id;
 | 
			
		||||
 | 
			
		||||
		const res = await request.post(url).set("Cookie", jwt).send(payload);
 | 
			
		||||
		const res = await request.post(url).set('Cookie', jwt).send(payload);
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(400);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 200", async () => {
 | 
			
		||||
	test('should drop 200', async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
 | 
			
		||||
		const payload: any = { ...editExam };
 | 
			
		||||
		delete payload.name;
 | 
			
		||||
 | 
			
		||||
		const res = await request.post(url).set("Cookie", jwt).send(payload);
 | 
			
		||||
		const res = await request.post(url).set('Cookie', jwt).send(payload);
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(200);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 200", async () => {
 | 
			
		||||
	test('should drop 200', async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
 | 
			
		||||
		const payload: any = { ...editExam };
 | 
			
		||||
		delete payload.degree;
 | 
			
		||||
 | 
			
		||||
		const res = await request.post(url).set("Cookie", jwt).send(payload);
 | 
			
		||||
		const res = await request.post(url).set('Cookie', jwt).send(payload);
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(200);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 200", async () => {
 | 
			
		||||
	test('should drop 200', async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
 | 
			
		||||
		const payload: any = { ...editExam };
 | 
			
		||||
		delete payload.packaging;
 | 
			
		||||
 | 
			
		||||
		const res = await request.post(url).set("Cookie", jwt).send(payload);
 | 
			
		||||
		const res = await request.post(url).set('Cookie', jwt).send(payload);
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(200);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 200", async () => {
 | 
			
		||||
	test('should drop 200', async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
 | 
			
		||||
		const payload: any = { ...editExam };
 | 
			
		||||
		delete payload.brand;
 | 
			
		||||
 | 
			
		||||
		const res = await request.post(url).set("Cookie", jwt).send(payload);
 | 
			
		||||
		const res = await request.post(url).set('Cookie', jwt).send(payload);
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(200);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 200", async () => {
 | 
			
		||||
	test('should drop 200', async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
		const req = await request.post("/api/v1/beer/add").set("Cookie", jwt).send(addExam);
 | 
			
		||||
		const req = await request.post('/api/v1/beer/add').set('Cookie', jwt).send(addExam);
 | 
			
		||||
		const _id = req.body.data._id;
 | 
			
		||||
		const payload = { ...editExam, _id: _id };
 | 
			
		||||
 | 
			
		||||
		let res = await request.post(url).set("Cookie", jwt).send(payload);
 | 
			
		||||
		let res = await request.post(url).set('Cookie', jwt).send(payload);
 | 
			
		||||
 | 
			
		||||
		delete res.body.data._id;
 | 
			
		||||
		delete res.body.data.__v;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
import request from "supertest";
 | 
			
		||||
import { app } from "../src/app";
 | 
			
		||||
import request from 'supertest';
 | 
			
		||||
import { app } from '../src/app';
 | 
			
		||||
 | 
			
		||||
describe("GET /api/v1", () => {
 | 
			
		||||
	describe("should return json with docs", () => {
 | 
			
		||||
		test("should respond with a 200 status code", async () => {
 | 
			
		||||
			const response = await request(app).get("/api/v1").send({});
 | 
			
		||||
			expect(response.headers["content-type"]).toMatch(/json/);
 | 
			
		||||
describe('GET /api/v1', () => {
 | 
			
		||||
	describe('should return json with docs', () => {
 | 
			
		||||
		test('should respond with a 200 status code', async () => {
 | 
			
		||||
			const response = await request(app).get('/api/v1').send({});
 | 
			
		||||
			expect(response.headers['content-type']).toMatch(/json/);
 | 
			
		||||
			expect(response.statusCode).toBe(200);
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,99 +0,0 @@
 | 
			
		||||
import supertest from "supertest";
 | 
			
		||||
import { app } from "../src/app";
 | 
			
		||||
import { login } from "./auth.test";
 | 
			
		||||
import { addExam, delExam } from "../src/validators/reviewValidator";
 | 
			
		||||
 | 
			
		||||
const request = supertest(app);
 | 
			
		||||
 | 
			
		||||
describe("POST /api/v1/review/add", () => {
 | 
			
		||||
	const url = "/api/v1/review/add";
 | 
			
		||||
	test("should drop 401 error", async () => {
 | 
			
		||||
		const res = await request.post(url).send({});
 | 
			
		||||
		expect(res.statusCode).toBe(401);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 400 ()", async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
		const res = await request.post(url).set("Cookie", jwt).send({});
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(400);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 400 (foam)", async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
		const body: any = { ...addExam };
 | 
			
		||||
		delete body.foam;
 | 
			
		||||
		const res = await request.post(url).set("Cookie", jwt).send(body);
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(400);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 400 (bitter_sweetness)", async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
		const body: any = { ...addExam };
 | 
			
		||||
		delete body.bitter_sweetness;
 | 
			
		||||
		const res = await request.post(url).set("Cookie", jwt).send(body);
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(400);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 400 (taste)", async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
		const body: any = { ...addExam };
 | 
			
		||||
		delete body.taste;
 | 
			
		||||
		const res = await request.post(url).set("Cookie", jwt).send(body);
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(400);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 400 (packaging)", async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
		const body: any = { ...addExam };
 | 
			
		||||
		delete body.packaging;
 | 
			
		||||
		const res = await request.post(url).set("Cookie", jwt).send(body);
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(400);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 400 (sourness)", async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
		const body: any = { ...addExam };
 | 
			
		||||
		delete body.sourness;
 | 
			
		||||
		const res = await request.post(url).set("Cookie", jwt).send(body);
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(400);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 400 (would_again)", async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
		const body: any = { ...addExam };
 | 
			
		||||
		delete body.would_again;
 | 
			
		||||
		const res = await request.post(url).set("Cookie", jwt).send(body);
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(400);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 201", async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
		const res = await request.post(url).set("Cookie", jwt).send(addExam);
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(201);
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe("GET /api/v1/review/get", () => {
 | 
			
		||||
	const url = "/api/v1/review/get";
 | 
			
		||||
 | 
			
		||||
	test("should drop 401", async () => {
 | 
			
		||||
		const res = await request.get(url).send();
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(401);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	test("should drop 200", async () => {
 | 
			
		||||
		const jwt = await login();
 | 
			
		||||
		const res = await request.get(url).set("Cookie", jwt).send();
 | 
			
		||||
 | 
			
		||||
		expect(res.statusCode).toBe(200);
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										1
									
								
								frontend/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -37,4 +37,3 @@ yarn-error.*
 | 
			
		||||
.env
 | 
			
		||||
 | 
			
		||||
.vscode/
 | 
			
		||||
*.swp
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ export default function TabLayout() {
 | 
			
		||||
					}}
 | 
			
		||||
				/>
 | 
			
		||||
				<Tabs.Screen
 | 
			
		||||
					name="review/index"
 | 
			
		||||
					name="review"
 | 
			
		||||
					options={{
 | 
			
		||||
						title: "Reviews",
 | 
			
		||||
						tabBarIcon: ({ color }) => (
 | 
			
		||||
@@ -66,10 +66,6 @@ export default function TabLayout() {
 | 
			
		||||
					name="beer/add"
 | 
			
		||||
					options={{ href: null, title: "Add beer" }}
 | 
			
		||||
				/>
 | 
			
		||||
				<Tabs.Screen
 | 
			
		||||
					name="review/add/[beer_id]"
 | 
			
		||||
					options={{ href: null, title: "Add review" }}
 | 
			
		||||
				/>
 | 
			
		||||
			</Tabs>
 | 
			
		||||
		</View>
 | 
			
		||||
	);
 | 
			
		||||
 
 | 
			
		||||
@@ -49,7 +49,7 @@ export default function BeerAdd() {
 | 
			
		||||
		console.log(result);
 | 
			
		||||
 | 
			
		||||
		if (!result.canceled) {
 | 
			
		||||
			setImage(result.assets[0]);
 | 
			
		||||
			setImage(result.assets[0].uri);
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
@@ -68,7 +68,7 @@ export default function BeerAdd() {
 | 
			
		||||
		console.log(result);
 | 
			
		||||
 | 
			
		||||
		if (!result.canceled) {
 | 
			
		||||
			setImage(result.assets[0]);
 | 
			
		||||
			setImage(result.assets[0].uri);
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
@@ -87,61 +87,30 @@ export default function BeerAdd() {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	function dataURItoBlob(dataURI) {
 | 
			
		||||
		// convert base64/URLEncoded data component to raw binary data held in a string
 | 
			
		||||
		var byteString;
 | 
			
		||||
		if (dataURI.split(",")[0].indexOf("base64") >= 0)
 | 
			
		||||
			byteString = atob(dataURI.split(",")[1]);
 | 
			
		||||
		else byteString = unescape(dataURI.split(",")[1]);
 | 
			
		||||
 | 
			
		||||
		// separate out the mime component
 | 
			
		||||
		var mimeString = dataURI.split(",")[0].split(":")[1].split(";")[0];
 | 
			
		||||
 | 
			
		||||
		// write the bytes of the string to a typed array
 | 
			
		||||
		var ia = new Uint8Array(byteString.length);
 | 
			
		||||
		for (var i = 0; i < byteString.length; i++) {
 | 
			
		||||
			ia[i] = byteString.charCodeAt(i);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return new Blob([ia], { type: mimeString });
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	async function addBeer() {
 | 
			
		||||
		// TODO: after the request - redirect to /beer/{new_beer_id}?; plus some modal about successful state
 | 
			
		||||
 | 
			
		||||
		const data = new FormData();
 | 
			
		||||
		if (Platform.OS == "web") {
 | 
			
		||||
			// TODO: On phone its imposibble to upload an image
 | 
			
		||||
			data.append("photos", dataURItoBlob(image.uri));
 | 
			
		||||
		}
 | 
			
		||||
		data.append("brand", b_brand);
 | 
			
		||||
		data.append("name", b_name);
 | 
			
		||||
		data.append("degree", b_degree);
 | 
			
		||||
		data.append("packaging", "can");
 | 
			
		||||
 | 
			
		||||
		try {
 | 
			
		||||
		const req = await fetch(`${process.env.EXPO_PUBLIC_API_URL}/beer/add`, {
 | 
			
		||||
			method: "POST",
 | 
			
		||||
			credentials: "include",
 | 
			
		||||
				body: data,
 | 
			
		||||
			headers: { "Content-Type": "application/json" },
 | 
			
		||||
			body: JSON.stringify({
 | 
			
		||||
				brand: b_brand,
 | 
			
		||||
				name: b_name,
 | 
			
		||||
				degree: b_degree,
 | 
			
		||||
				packaging: b_packaging,
 | 
			
		||||
				photos: null,
 | 
			
		||||
			}),
 | 
			
		||||
		});
 | 
			
		||||
		const res = await req.json();
 | 
			
		||||
 | 
			
		||||
		if (res.code == 201 && res.data._id) {
 | 
			
		||||
				// TODO: reditect using expo router
 | 
			
		||||
				// window.location.href = `/beer/${res.data._id}`;
 | 
			
		||||
				alert("Added");
 | 
			
		||||
			window.location.href = `/beer/${res.data._id}`;
 | 
			
		||||
		} else {
 | 
			
		||||
			alert(
 | 
			
		||||
				"Beer was not added successfully. Please check your data and try again.",
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
		} catch (err) {
 | 
			
		||||
			alert(
 | 
			
		||||
				"Beer was not added successfully. Please check your data and try again.",
 | 
			
		||||
			);
 | 
			
		||||
			console.error(err);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (
 | 
			
		||||
@@ -185,7 +154,6 @@ export default function BeerAdd() {
 | 
			
		||||
					theme="DropdownTheme"
 | 
			
		||||
					//searchable={true} //maybe we can use it later...
 | 
			
		||||
				/>
 | 
			
		||||
 | 
			
		||||
				<View style={styles.imageContainer}>
 | 
			
		||||
					<Button
 | 
			
		||||
						title="Open gallery"
 | 
			
		||||
@@ -196,16 +164,17 @@ export default function BeerAdd() {
 | 
			
		||||
 | 
			
		||||
					{Platform.OS != "web" ? (
 | 
			
		||||
						<Button
 | 
			
		||||
							title="Open camera"
 | 
			
		||||
							onPress={openCamera}
 | 
			
		||||
							title={"Open camera"}
 | 
			
		||||
							buttonStyle={styles.imageButton}
 | 
			
		||||
							textStyle={styles.imageTextButton}
 | 
			
		||||
						/>
 | 
			
		||||
					) : (
 | 
			
		||||
						false
 | 
			
		||||
					)}
 | 
			
		||||
 | 
			
		||||
					{image && <Image source={{ uri: image }} style={styles.image} />}
 | 
			
		||||
				</View>
 | 
			
		||||
				{image && <Image source={{ uri: image.uri }} style={styles.image} />}
 | 
			
		||||
				<Button title="Add beer" color={colors.gold} onPress={addBeer} />
 | 
			
		||||
			</View>
 | 
			
		||||
		</View>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,20 +1,11 @@
 | 
			
		||||
import {
 | 
			
		||||
	StyleSheet,
 | 
			
		||||
	View,
 | 
			
		||||
	FlatList,
 | 
			
		||||
	Dimensions,
 | 
			
		||||
	StatusBar,
 | 
			
		||||
	Image,
 | 
			
		||||
} from "react-native";
 | 
			
		||||
import { StyleSheet, View, FlatList } from "react-native";
 | 
			
		||||
import Text from "@components/Text";
 | 
			
		||||
import Button from "@components/Button";
 | 
			
		||||
import { colors } from "@components/style";
 | 
			
		||||
import { router } from "expo-router";
 | 
			
		||||
import { useEffect, useState } from "react";
 | 
			
		||||
// import { FlashList } from "@shopify/flash-list";
 | 
			
		||||
 | 
			
		||||
export default function Tab() {
 | 
			
		||||
	const API_HOST = process.env.EXPO_PUBLIC_API_URL.replace("/api/v1", "");
 | 
			
		||||
	const [data, setData] = useState([]);
 | 
			
		||||
	useEffect(() => {
 | 
			
		||||
		fetchData();
 | 
			
		||||
@@ -37,61 +28,24 @@ export default function Tab() {
 | 
			
		||||
	return (
 | 
			
		||||
		<View style={styles.container}>
 | 
			
		||||
			<Button
 | 
			
		||||
				title="Add new beer"
 | 
			
		||||
				title="Add Beer"
 | 
			
		||||
				color={colors.gold}
 | 
			
		||||
				onPress={() => {
 | 
			
		||||
					router.replace("/beer/add");
 | 
			
		||||
				}}
 | 
			
		||||
			/>
 | 
			
		||||
 | 
			
		||||
			{/* 				<FlashList
 | 
			
		||||
					data={data}
 | 
			
		||||
					estimatedItemSize={100}
 | 
			
		||||
					keyExtractor={(item) => String(item._id)}
 | 
			
		||||
					renderItem={({ item }) => (
 | 
			
		||||
						<View style={styles.item}>
 | 
			
		||||
							<Text>Name: {item.name}</Text>
 | 
			
		||||
							<Text>Brand: {item.brand}</Text>
 | 
			
		||||
							<Text>Degree: {item.degree}</Text>
 | 
			
		||||
							<Text>Packaging: {item.packaging}</Text>
 | 
			
		||||
						</View>
 | 
			
		||||
					)}
 | 
			
		||||
				/> */}
 | 
			
		||||
 | 
			
		||||
			<FlatList
 | 
			
		||||
				data={data}
 | 
			
		||||
				style={styles.beerList}
 | 
			
		||||
				keyExtractor={(item) => String(item._id)}
 | 
			
		||||
				renderItem={({ item }) => (
 | 
			
		||||
					<View style={styles.item}>
 | 
			
		||||
						<Image
 | 
			
		||||
							source={
 | 
			
		||||
								item.imgs[0]
 | 
			
		||||
									? {
 | 
			
		||||
											uri: `${API_HOST}/public/uploads/${item.imgs[0]}`,
 | 
			
		||||
										}
 | 
			
		||||
									: {
 | 
			
		||||
											uri: "https://imagesvc.meredithcorp.io/v3/mm/image?url=https:%2F%2Fstatic.onecms.io%2Fwp-content%2Fuploads%2Fsites%2F44%2F2020%2F09%2F29%2Flight-beer.jpg",
 | 
			
		||||
										}
 | 
			
		||||
							}
 | 
			
		||||
							style={styles.itemImg}
 | 
			
		||||
						/>
 | 
			
		||||
						<View style={styles.itemDesc}>
 | 
			
		||||
						<Text>Name: {item.name}</Text>
 | 
			
		||||
						<Text>Brand: {item.brand}</Text>
 | 
			
		||||
						<Text>Degree: {item.degree}</Text>
 | 
			
		||||
						<Text>Packaging: {item.packaging}</Text>
 | 
			
		||||
					</View>
 | 
			
		||||
						<View style={styles.itemAddReview}>
 | 
			
		||||
							<Button
 | 
			
		||||
								title="Add review"
 | 
			
		||||
								color={colors.gold}
 | 
			
		||||
								onPress={() => {
 | 
			
		||||
									router.push(`/review/add/${item._id}`);
 | 
			
		||||
								}}
 | 
			
		||||
							/>
 | 
			
		||||
						</View>
 | 
			
		||||
					</View>
 | 
			
		||||
				)}
 | 
			
		||||
			/>
 | 
			
		||||
		</View>
 | 
			
		||||
@@ -103,12 +57,12 @@ export const styles = StyleSheet.create({
 | 
			
		||||
		flex: 1,
 | 
			
		||||
		justifyContent: "center",
 | 
			
		||||
		alignItems: "center",
 | 
			
		||||
		marginTop: "2%",
 | 
			
		||||
		marginTop: "5%",
 | 
			
		||||
	},
 | 
			
		||||
	beerList: {
 | 
			
		||||
		width: "100%",
 | 
			
		||||
		paddingHorizontal: "15%",
 | 
			
		||||
		marginTop: "2%",
 | 
			
		||||
		marginTop: "5%",
 | 
			
		||||
	},
 | 
			
		||||
	item: {
 | 
			
		||||
		borderColor: "gray",
 | 
			
		||||
@@ -117,12 +71,4 @@ export const styles = StyleSheet.create({
 | 
			
		||||
		padding: 13,
 | 
			
		||||
		marginBottom: "5%",
 | 
			
		||||
	},
 | 
			
		||||
	itemImg: {
 | 
			
		||||
		height: 300,
 | 
			
		||||
		resizeMode: "contain",
 | 
			
		||||
	},
 | 
			
		||||
	itemDesc: {
 | 
			
		||||
		alignItems: "center",
 | 
			
		||||
		paddingBottom: "2%",
 | 
			
		||||
	},
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								frontend/app/(app)/(tabs)/review.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,10 @@
 | 
			
		||||
import { View } from "react-native";
 | 
			
		||||
import Text from "@components/Text";
 | 
			
		||||
 | 
			
		||||
export default function Tab() {
 | 
			
		||||
	return (
 | 
			
		||||
		<View style={{ justifyContent: "center", alignItems: "center", flex: 1 }}>
 | 
			
		||||
			<Text>Tab REVIEW</Text>
 | 
			
		||||
		</View>
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,521 +0,0 @@
 | 
			
		||||
import { StyleSheet, TextInput, View, Image } from "react-native";
 | 
			
		||||
import { useCallback, useState } from "react";
 | 
			
		||||
import Button from "@components/Button";
 | 
			
		||||
import Text from "@components/Text";
 | 
			
		||||
import { colors } from "@components/style";
 | 
			
		||||
import * as ImagePicker from "expo-image-picker";
 | 
			
		||||
import DropDownPicker from "react-native-dropdown-picker";
 | 
			
		||||
const DropdownTheme = require("@components/DropdownTheme");
 | 
			
		||||
import { Platform } from "react-native";
 | 
			
		||||
import { useLocalSearchParams } from "expo-router";
 | 
			
		||||
 | 
			
		||||
export default function reviewAdd() {
 | 
			
		||||
	// States for each dropdown
 | 
			
		||||
	const routeParams = useLocalSearchParams();
 | 
			
		||||
	const [openFoam, setOpenFoam] = useState(false);
 | 
			
		||||
	const [openBitterSweetness, setOpenBitterSweetness] = useState(false);
 | 
			
		||||
	const [openTaste, setOpenTaste] = useState(false);
 | 
			
		||||
	const [openPackaging, setOpenPackaging] = useState(false);
 | 
			
		||||
	const [openSourness, setOpenSourness] = useState(false);
 | 
			
		||||
	const [openAgain, setOpenAgain] = useState(false);
 | 
			
		||||
 | 
			
		||||
	// pěna
 | 
			
		||||
	const [itemFoam, setFoamValue] = useState(null);
 | 
			
		||||
	const [foam, setFoam] = useState([
 | 
			
		||||
		{
 | 
			
		||||
			label: "Bad",
 | 
			
		||||
			value: "1",
 | 
			
		||||
			icon: () => (
 | 
			
		||||
				<Image
 | 
			
		||||
					source={require("@assets/smileys/smiley-x-eyes.png")}
 | 
			
		||||
					style={styles.iconStyle}
 | 
			
		||||
				/>
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			label: "Medium",
 | 
			
		||||
			value: "2",
 | 
			
		||||
			icon: () => (
 | 
			
		||||
				<Image
 | 
			
		||||
					source={require("@assets/smileys/smiley-meh.png")}
 | 
			
		||||
					style={styles.iconStyle}
 | 
			
		||||
				/>
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			label: "Excellent",
 | 
			
		||||
			value: "3",
 | 
			
		||||
			icon: () => (
 | 
			
		||||
				<Image
 | 
			
		||||
					source={require("@assets/smileys/smiley.png")}
 | 
			
		||||
					style={styles.iconStyle}
 | 
			
		||||
				/>
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
	]);
 | 
			
		||||
 | 
			
		||||
	// hořkost / sladkost
 | 
			
		||||
	const [itemBitter_sweetness, setBitter_sweetnessValue] = useState(null);
 | 
			
		||||
	const [bitter_sweetness, setBitter_sweetness] = useState([
 | 
			
		||||
		{
 | 
			
		||||
			label: "Bad",
 | 
			
		||||
			value: "1",
 | 
			
		||||
			icon: () => (
 | 
			
		||||
				<Image
 | 
			
		||||
					source={require("@assets/smileys/smiley-x-eyes.png")}
 | 
			
		||||
					style={styles.iconStyle}
 | 
			
		||||
				/>
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			label: "Medium",
 | 
			
		||||
			value: "2",
 | 
			
		||||
			icon: () => (
 | 
			
		||||
				<Image
 | 
			
		||||
					source={require("@assets/smileys/smiley-meh.png")}
 | 
			
		||||
					style={styles.iconStyle}
 | 
			
		||||
				/>
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			label: "Excellent",
 | 
			
		||||
			value: "3",
 | 
			
		||||
			icon: () => (
 | 
			
		||||
				<Image
 | 
			
		||||
					source={require("@assets/smileys/smiley.png")}
 | 
			
		||||
					style={styles.iconStyle}
 | 
			
		||||
				/>
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
	]);
 | 
			
		||||
 | 
			
		||||
	//chuť
 | 
			
		||||
	const [itemTaste, setTasteValue] = useState(null);
 | 
			
		||||
	const [taste, setTaste] = useState([
 | 
			
		||||
		{
 | 
			
		||||
			label: "Disgust",
 | 
			
		||||
			value: "1",
 | 
			
		||||
			icon: () => (
 | 
			
		||||
				<Image
 | 
			
		||||
					source={require("@assets/smileys/smiley-blank.png")}
 | 
			
		||||
					style={styles.iconStyle}
 | 
			
		||||
				/>
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			label: "Not great, not terrible",
 | 
			
		||||
			value: "2",
 | 
			
		||||
			icon: () => (
 | 
			
		||||
				<Image
 | 
			
		||||
					source={require("@assets/smileys/smiley-nervous.png")}
 | 
			
		||||
					style={styles.iconStyle}
 | 
			
		||||
				/>
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			label: "Good",
 | 
			
		||||
			value: "3",
 | 
			
		||||
			icon: () => (
 | 
			
		||||
				<Image
 | 
			
		||||
					source={require("@assets/smileys/smiley-meh.png")}
 | 
			
		||||
					style={styles.iconStyle}
 | 
			
		||||
				/>
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			label: "Why not",
 | 
			
		||||
			value: "4",
 | 
			
		||||
			icon: () => (
 | 
			
		||||
				<Image
 | 
			
		||||
					source={require("@assets/smileys/smiley-wink.png")}
 | 
			
		||||
					style={styles.iconStyle}
 | 
			
		||||
				/>
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			label: "Excellent!",
 | 
			
		||||
			value: "5",
 | 
			
		||||
			icon: () => (
 | 
			
		||||
				<Image
 | 
			
		||||
					source={require("@assets/smileys/smiley.png")}
 | 
			
		||||
					style={styles.iconStyle}
 | 
			
		||||
				/>
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
	]);
 | 
			
		||||
 | 
			
		||||
	// packaging
 | 
			
		||||
	const [itemPackaging, setPackagingValue] = useState(null);
 | 
			
		||||
	const [packaging, setPackaging] = useState([
 | 
			
		||||
		{
 | 
			
		||||
			label: "Disgust",
 | 
			
		||||
			value: "1",
 | 
			
		||||
			icon: () => (
 | 
			
		||||
				<Image
 | 
			
		||||
					source={require("@assets/smileys/smiley-blank.png")}
 | 
			
		||||
					style={styles.iconStyle}
 | 
			
		||||
				/>
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			label: "Not great, not terrible",
 | 
			
		||||
			value: "2",
 | 
			
		||||
			icon: () => (
 | 
			
		||||
				<Image
 | 
			
		||||
					source={require("@assets/smileys/smiley-nervous.png")}
 | 
			
		||||
					style={styles.iconStyle}
 | 
			
		||||
				/>
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			label: "Good",
 | 
			
		||||
			value: "3",
 | 
			
		||||
			icon: () => (
 | 
			
		||||
				<Image
 | 
			
		||||
					source={require("@assets/smileys/smiley-meh.png")}
 | 
			
		||||
					style={styles.iconStyle}
 | 
			
		||||
				/>
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			label: "Why not",
 | 
			
		||||
			value: "4",
 | 
			
		||||
			icon: () => (
 | 
			
		||||
				<Image
 | 
			
		||||
					source={require("@assets/smileys/smiley-wink.png")}
 | 
			
		||||
					style={styles.iconStyle}
 | 
			
		||||
				/>
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			label: "Excellent!",
 | 
			
		||||
			value: "5",
 | 
			
		||||
			icon: () => (
 | 
			
		||||
				<Image
 | 
			
		||||
					source={require("@assets/smileys/smiley.png")}
 | 
			
		||||
					style={styles.iconStyle}
 | 
			
		||||
				/>
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
	]);
 | 
			
		||||
 | 
			
		||||
	//kyselost
 | 
			
		||||
	const [itemSourness, setSournessValue] = useState(null);
 | 
			
		||||
	const [sourness, setSourness] = useState([
 | 
			
		||||
		{
 | 
			
		||||
			label: "True",
 | 
			
		||||
			value: true,
 | 
			
		||||
			icon: () => (
 | 
			
		||||
				<Image
 | 
			
		||||
					source={require("@assets/smileys/smiley-blank.png")}
 | 
			
		||||
					style={styles.iconStyle}
 | 
			
		||||
				/>
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			label: "False",
 | 
			
		||||
			value: false,
 | 
			
		||||
			icon: () => (
 | 
			
		||||
				<Image
 | 
			
		||||
					source={require("@assets/smileys/smiley-nervous.png")}
 | 
			
		||||
					style={styles.iconStyle}
 | 
			
		||||
				/>
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
	]);
 | 
			
		||||
 | 
			
		||||
	//dal bych si znovu?
 | 
			
		||||
	const [itemAgain, setAgainValue] = useState(null);
 | 
			
		||||
	const [again, setAgain] = useState([
 | 
			
		||||
		{
 | 
			
		||||
			label: "Yes",
 | 
			
		||||
			value: true,
 | 
			
		||||
			icon: () => (
 | 
			
		||||
				<Image
 | 
			
		||||
					source={require("@assets/smileys/smiley.png")}
 | 
			
		||||
					style={styles.iconStyle}
 | 
			
		||||
				/>
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			label: "No",
 | 
			
		||||
			value: false,
 | 
			
		||||
			icon: () => (
 | 
			
		||||
				<Image
 | 
			
		||||
					source={require("@assets/smileys/smiley-x-eyes.png")}
 | 
			
		||||
					style={styles.iconStyle}
 | 
			
		||||
				/>
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
	]);
 | 
			
		||||
 | 
			
		||||
	//podmínky pro zavření ostatních dropdownů, pokud je jiný otevřený
 | 
			
		||||
	const onOpenFoam = useCallback(() => {
 | 
			
		||||
		setOpenBitterSweetness(false);
 | 
			
		||||
		setOpenTaste(false);
 | 
			
		||||
		setOpenPackaging(false);
 | 
			
		||||
		setOpenSourness(false);
 | 
			
		||||
		setOpenAgain(false);
 | 
			
		||||
		setOpenFoam(true);
 | 
			
		||||
	}, []);
 | 
			
		||||
 | 
			
		||||
	const onOpenBitterSweetness = useCallback(() => {
 | 
			
		||||
		setOpenFoam(false);
 | 
			
		||||
		setOpenTaste(false);
 | 
			
		||||
		setOpenPackaging(false);
 | 
			
		||||
		setOpenSourness(false);
 | 
			
		||||
		setOpenAgain(false);
 | 
			
		||||
		setOpenBitterSweetness(true);
 | 
			
		||||
	}, []);
 | 
			
		||||
 | 
			
		||||
	const onOpenTaste = useCallback(() => {
 | 
			
		||||
		setOpenFoam(false);
 | 
			
		||||
		setOpenBitterSweetness(false);
 | 
			
		||||
		setOpenPackaging(false);
 | 
			
		||||
		setOpenSourness(false);
 | 
			
		||||
		setOpenAgain(false);
 | 
			
		||||
		setOpenTaste(true);
 | 
			
		||||
	}, []);
 | 
			
		||||
 | 
			
		||||
	const onOpenPackaging = useCallback(() => {
 | 
			
		||||
		setOpenFoam(false);
 | 
			
		||||
		setOpenBitterSweetness(false);
 | 
			
		||||
		setOpenTaste(false);
 | 
			
		||||
		setOpenSourness(false);
 | 
			
		||||
		setOpenAgain(false);
 | 
			
		||||
		setOpenPackaging(true);
 | 
			
		||||
	}, []);
 | 
			
		||||
 | 
			
		||||
	const onOpenSourness = useCallback(() => {
 | 
			
		||||
		setOpenFoam(false);
 | 
			
		||||
		setOpenBitterSweetness(false);
 | 
			
		||||
		setOpenTaste(false);
 | 
			
		||||
		setOpenPackaging(false);
 | 
			
		||||
		setOpenAgain(false);
 | 
			
		||||
		setOpenSourness(true);
 | 
			
		||||
	}, []);
 | 
			
		||||
 | 
			
		||||
	const onOpenAgain = useCallback(() => {
 | 
			
		||||
		setOpenFoam(false);
 | 
			
		||||
		setOpenBitterSweetness(false);
 | 
			
		||||
		setOpenTaste(false);
 | 
			
		||||
		setOpenPackaging(false);
 | 
			
		||||
		setOpenSourness(false);
 | 
			
		||||
		setOpenAgain(true);
 | 
			
		||||
	}, []);
 | 
			
		||||
 | 
			
		||||
	DropDownPicker.addTheme("DropdownTheme", DropdownTheme);
 | 
			
		||||
	DropDownPicker.setTheme("DropdownTheme");
 | 
			
		||||
 | 
			
		||||
	async function addBeer() {
 | 
			
		||||
		const req = await fetch(`${process.env.EXPO_PUBLIC_API_URL}/review/add`, {
 | 
			
		||||
			method: "POST",
 | 
			
		||||
			credentials: "include",
 | 
			
		||||
			headers: { "Content-Type": "application/json" },
 | 
			
		||||
			body: JSON.stringify({
 | 
			
		||||
				beer_id: routeParams.beer_id,
 | 
			
		||||
				foam: itemFoam,
 | 
			
		||||
				bitter_sweetness: itemBitter_sweetness,
 | 
			
		||||
				taste: itemTaste,
 | 
			
		||||
				packaging: itemPackaging,
 | 
			
		||||
				sourness: itemSourness,
 | 
			
		||||
				would_again: itemAgain,
 | 
			
		||||
			}),
 | 
			
		||||
		});
 | 
			
		||||
		const res = await req.json();
 | 
			
		||||
 | 
			
		||||
		if (res.code == 201 && res.data._id) {
 | 
			
		||||
			// window.location.href = `/review/${res.data._id}`;
 | 
			
		||||
			// TODO: use react router for redirect
 | 
			
		||||
			alert("Review was added!");
 | 
			
		||||
		} else {
 | 
			
		||||
			alert(
 | 
			
		||||
				"Review was not added successfully. Please check your data and try again.",
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (
 | 
			
		||||
		<View style={styles.container}>
 | 
			
		||||
			<View style={styles.form}>
 | 
			
		||||
				<Text style={styles.text}>
 | 
			
		||||
					How does your beer taste? Write a review!
 | 
			
		||||
				</Text>
 | 
			
		||||
				<Text style={styles.dropdownText} zIndex={6000} zIndexInverse={1000}>
 | 
			
		||||
					How does the foam look like?
 | 
			
		||||
				</Text>
 | 
			
		||||
				<DropDownPicker
 | 
			
		||||
					open={openFoam}
 | 
			
		||||
					onOpen={onOpenFoam}
 | 
			
		||||
					value={itemFoam}
 | 
			
		||||
					items={foam}
 | 
			
		||||
					setOpen={setOpenFoam}
 | 
			
		||||
					setValue={setFoamValue}
 | 
			
		||||
					setItems={setFoam}
 | 
			
		||||
					placeholder="Please select..."
 | 
			
		||||
					theme="DropdownTheme"
 | 
			
		||||
					zIndex={6000}
 | 
			
		||||
					zIndexInverse={1000}
 | 
			
		||||
				/>
 | 
			
		||||
				<Text style={styles.dropdownText} zIndex={5000} zIndexInverse={2000}>
 | 
			
		||||
					More bitter, or more sweet?
 | 
			
		||||
				</Text>
 | 
			
		||||
				<DropDownPicker
 | 
			
		||||
					open={openBitterSweetness}
 | 
			
		||||
					onOpen={onOpenBitterSweetness}
 | 
			
		||||
					value={itemBitter_sweetness}
 | 
			
		||||
					items={bitter_sweetness}
 | 
			
		||||
					setOpen={setOpenBitterSweetness}
 | 
			
		||||
					setValue={setBitter_sweetnessValue}
 | 
			
		||||
					setItems={setBitter_sweetness}
 | 
			
		||||
					placeholder="Please select..."
 | 
			
		||||
					theme="DropdownTheme"
 | 
			
		||||
					zIndex={5000}
 | 
			
		||||
					zIndexInverse={2000}
 | 
			
		||||
				/>
 | 
			
		||||
 | 
			
		||||
				<Text style={styles.dropdownText} zIndex={4000} zIndexInverse={3000}>
 | 
			
		||||
					How does it taste?
 | 
			
		||||
				</Text>
 | 
			
		||||
				<DropDownPicker
 | 
			
		||||
					open={openTaste}
 | 
			
		||||
					onOpen={onOpenTaste}
 | 
			
		||||
					value={itemTaste}
 | 
			
		||||
					items={taste}
 | 
			
		||||
					setOpen={setOpenTaste}
 | 
			
		||||
					setValue={setTasteValue}
 | 
			
		||||
					setItems={setTaste}
 | 
			
		||||
					placeholder="Please select..."
 | 
			
		||||
					theme="DropdownTheme"
 | 
			
		||||
					zIndex={4000}
 | 
			
		||||
					zIndexInverse={3000}
 | 
			
		||||
				/>
 | 
			
		||||
 | 
			
		||||
				<Text style={styles.dropdownText} zIndex={5000} zIndexInverse={4000}>
 | 
			
		||||
					How do you like the packaging?
 | 
			
		||||
				</Text>
 | 
			
		||||
				<DropDownPicker
 | 
			
		||||
					open={openPackaging}
 | 
			
		||||
					onOpen={onOpenPackaging}
 | 
			
		||||
					value={itemPackaging}
 | 
			
		||||
					items={packaging}
 | 
			
		||||
					setOpen={setOpenPackaging}
 | 
			
		||||
					setValue={setPackagingValue}
 | 
			
		||||
					setItems={setPackaging}
 | 
			
		||||
					placeholder="Please select..."
 | 
			
		||||
					theme="DropdownTheme"
 | 
			
		||||
					zIndex={3000}
 | 
			
		||||
					zIndexInverse={4000}
 | 
			
		||||
				/>
 | 
			
		||||
 | 
			
		||||
				<Text style={styles.dropdownText} zIndex={4000} zIndexInverse={5000}>
 | 
			
		||||
					Is it sour?
 | 
			
		||||
				</Text>
 | 
			
		||||
				<DropDownPicker
 | 
			
		||||
					open={openSourness}
 | 
			
		||||
					onOpen={onOpenSourness}
 | 
			
		||||
					value={itemSourness}
 | 
			
		||||
					items={sourness}
 | 
			
		||||
					setOpen={setOpenSourness}
 | 
			
		||||
					setValue={setSournessValue}
 | 
			
		||||
					setItems={setSourness}
 | 
			
		||||
					placeholder="Please select..."
 | 
			
		||||
					theme="DropdownTheme"
 | 
			
		||||
					zIndex={2000}
 | 
			
		||||
					zIndexInverse={5000}
 | 
			
		||||
				/>
 | 
			
		||||
 | 
			
		||||
				<Text style={styles.dropdownText} zIndex={5000} zIndexInverse={6000}>
 | 
			
		||||
					Would you drink it again?
 | 
			
		||||
				</Text>
 | 
			
		||||
				<DropDownPicker
 | 
			
		||||
					open={openAgain}
 | 
			
		||||
					onOpen={onOpenAgain}
 | 
			
		||||
					value={itemAgain}
 | 
			
		||||
					items={again}
 | 
			
		||||
					setOpen={setOpenAgain}
 | 
			
		||||
					setValue={setAgainValue}
 | 
			
		||||
					setItems={setAgain}
 | 
			
		||||
					placeholder="Please select..."
 | 
			
		||||
					theme="DropdownTheme"
 | 
			
		||||
					zIndex={1000}
 | 
			
		||||
					zIndexInverse={6000}
 | 
			
		||||
				/>
 | 
			
		||||
 | 
			
		||||
				<View style={styles.buttonSend}>
 | 
			
		||||
					<Button title="Add review" color={colors.gold} onPress={addBeer} />
 | 
			
		||||
				</View>
 | 
			
		||||
			</View>
 | 
			
		||||
		</View>
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const styles = StyleSheet.create({
 | 
			
		||||
	container: {
 | 
			
		||||
		width: "100%",
 | 
			
		||||
		height: "100%",
 | 
			
		||||
		alignItems: "center",
 | 
			
		||||
		display: "flex",
 | 
			
		||||
	},
 | 
			
		||||
	form: {
 | 
			
		||||
		gap: 5,
 | 
			
		||||
		width: "80%",
 | 
			
		||||
	},
 | 
			
		||||
	buttonSend: {
 | 
			
		||||
		display: "flex",
 | 
			
		||||
		alignItems: "center",
 | 
			
		||||
		marginTop: "2%",
 | 
			
		||||
	},
 | 
			
		||||
	input: {
 | 
			
		||||
		height: "auto",
 | 
			
		||||
		width: "100%",
 | 
			
		||||
		borderColor: "gray",
 | 
			
		||||
		borderWidth: 1,
 | 
			
		||||
		borderRadius: 10,
 | 
			
		||||
		padding: 13,
 | 
			
		||||
		color: "#fff",
 | 
			
		||||
	},
 | 
			
		||||
	imageContainer: {
 | 
			
		||||
		alignItems: "center",
 | 
			
		||||
		justifyContent: "center",
 | 
			
		||||
		display: "flex",
 | 
			
		||||
		flexDirection: "row",
 | 
			
		||||
		gap: 10,
 | 
			
		||||
	},
 | 
			
		||||
	imageButton: {
 | 
			
		||||
		backgroundColor: colors.dark,
 | 
			
		||||
		borderColor: "gray",
 | 
			
		||||
		borderWidth: 1,
 | 
			
		||||
		borderRadius: 10,
 | 
			
		||||
	},
 | 
			
		||||
	imageTextButton: {
 | 
			
		||||
		color: colors.white,
 | 
			
		||||
	},
 | 
			
		||||
	image: {
 | 
			
		||||
		width: 150,
 | 
			
		||||
		height: 150,
 | 
			
		||||
	},
 | 
			
		||||
	text: {
 | 
			
		||||
		color: colors.white,
 | 
			
		||||
		fontSize: 24,
 | 
			
		||||
		textAlign: "center",
 | 
			
		||||
		paddingBottom: "3%",
 | 
			
		||||
		paddingTop: "10%",
 | 
			
		||||
	},
 | 
			
		||||
	iconStyle: {
 | 
			
		||||
		width: 30,
 | 
			
		||||
		height: 30,
 | 
			
		||||
	},
 | 
			
		||||
	dropdownContainer: {
 | 
			
		||||
		width: "100%",
 | 
			
		||||
	},
 | 
			
		||||
	dropdownText: {
 | 
			
		||||
		color: colors.white,
 | 
			
		||||
		fontSize: 16,
 | 
			
		||||
		paddingBottom: 1,
 | 
			
		||||
		paddingTop: "1%",
 | 
			
		||||
		display: "flex",
 | 
			
		||||
		alignItems: "flex-start",
 | 
			
		||||
		flexDirection: "column",
 | 
			
		||||
	},
 | 
			
		||||
});
 | 
			
		||||
@@ -1,139 +0,0 @@
 | 
			
		||||
import { View, StyleSheet, FlatList, Image } from "react-native";
 | 
			
		||||
import Text from "@components/Text";
 | 
			
		||||
import Button from "@components/Button";
 | 
			
		||||
import { colors } from "@components/style";
 | 
			
		||||
import { router } from "expo-router";
 | 
			
		||||
import { useEffect, useState } from "react";
 | 
			
		||||
import { useAuth } from "@context/AuthContext";
 | 
			
		||||
 | 
			
		||||
export default function Tab() {
 | 
			
		||||
	const { authState } = useAuth();
 | 
			
		||||
	const user = authState.user;
 | 
			
		||||
	const [data, setData] = useState([]);
 | 
			
		||||
	useEffect(() => {
 | 
			
		||||
		fetchData();
 | 
			
		||||
	}, []);
 | 
			
		||||
 | 
			
		||||
	const API_HOST = process.env.EXPO_PUBLIC_API_URL.replace("/api/v1", "");
 | 
			
		||||
 | 
			
		||||
	async function fetchData() {
 | 
			
		||||
		try {
 | 
			
		||||
			const res = await fetch(`${process.env.EXPO_PUBLIC_API_URL}/review/get`, {
 | 
			
		||||
				method: "GET",
 | 
			
		||||
				credentials: "include",
 | 
			
		||||
			});
 | 
			
		||||
			let data = await res.json();
 | 
			
		||||
			// show only logged in user's data
 | 
			
		||||
			data = data.data.filter((review) => review.user_id == user._id);
 | 
			
		||||
 | 
			
		||||
			let beers = await fetch(`${process.env.EXPO_PUBLIC_API_URL}/beer/get`, {
 | 
			
		||||
				method: "GET",
 | 
			
		||||
				credentials: "include",
 | 
			
		||||
			});
 | 
			
		||||
			beers = await beers.json();
 | 
			
		||||
			beers = beers.data;
 | 
			
		||||
			console.log(beers);
 | 
			
		||||
 | 
			
		||||
			async function getBeerParam(search, beers) {
 | 
			
		||||
				for (let i = 0; i < beers.length; i++) {
 | 
			
		||||
					if (beers[i]._id == search) {
 | 
			
		||||
						return beers[i];
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				return null;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			data.forEach(async (el) => {
 | 
			
		||||
				el.beer = await getBeerParam(el.beer_id, beers);
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			console.log("reviews", data);
 | 
			
		||||
			setData(data);
 | 
			
		||||
		} catch (err) {
 | 
			
		||||
			console.error(err);
 | 
			
		||||
			alert("Something went wrong");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	const opt3 = ["Bad", "Medium", "Excellent!"];
 | 
			
		||||
	const opt5 = [
 | 
			
		||||
		"Disgust",
 | 
			
		||||
		"Not great, not terrible",
 | 
			
		||||
		"Good",
 | 
			
		||||
		"Why not?",
 | 
			
		||||
		"Excellent!",
 | 
			
		||||
	];
 | 
			
		||||
	const opt2 = ["Yes", "No"];
 | 
			
		||||
	const sourness = ["Good", "Bad"];
 | 
			
		||||
 | 
			
		||||
	return (
 | 
			
		||||
		<View style={styles.container}>
 | 
			
		||||
			<FlatList
 | 
			
		||||
				data={data}
 | 
			
		||||
				style={styles.reviewList}
 | 
			
		||||
				keyExtractor={(item) => String(item._id)}
 | 
			
		||||
				renderItem={({ item }) => (
 | 
			
		||||
					<View style={styles.itemContainer}>
 | 
			
		||||
						<View>
 | 
			
		||||
							<Text>{item.beer.name}</Text>
 | 
			
		||||
							<Text>{item.beer.brand}</Text>
 | 
			
		||||
							<Text>{item.beer.degree}°</Text>
 | 
			
		||||
							<Text>{item.beer.packaging}</Text>
 | 
			
		||||
							<Image
 | 
			
		||||
								source={
 | 
			
		||||
									item.beer.imgs[0]
 | 
			
		||||
										? {
 | 
			
		||||
												uri: `${API_HOST}/public/uploads/${item.beer.imgs[0]}`,
 | 
			
		||||
											}
 | 
			
		||||
										: {
 | 
			
		||||
												uri: "https://imagesvc.meredithcorp.io/v3/mm/image?url=https:%2F%2Fstatic.onecms.io%2Fwp-content%2Fuploads%2Fsites%2F44%2F2020%2F09%2F29%2Flight-beer.jpg",
 | 
			
		||||
											}
 | 
			
		||||
								}
 | 
			
		||||
								style={styles.itemImg}
 | 
			
		||||
							/>
 | 
			
		||||
						</View>
 | 
			
		||||
						<View>
 | 
			
		||||
							<Text>Foam → {opt3[item.foam - 1]}</Text>
 | 
			
		||||
							<Text>
 | 
			
		||||
								Bitter / Sweetness → {opt3[item.bitter_sweetness - 1]}
 | 
			
		||||
							</Text>
 | 
			
		||||
							<Text>Taste → {opt5[item.taste - 1]}</Text>
 | 
			
		||||
							<Text>Packaging → {opt5[item.packaging - 1]}</Text>
 | 
			
		||||
							<Text>Sourness → {sourness[item.sourness - 1]}</Text>
 | 
			
		||||
							<Text>Would again? → {opt2[item.would_again - 1]}</Text>
 | 
			
		||||
						</View>
 | 
			
		||||
					</View>
 | 
			
		||||
				)}
 | 
			
		||||
			/>
 | 
			
		||||
		</View>
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const styles = StyleSheet.create({
 | 
			
		||||
	container: {
 | 
			
		||||
		flex: 1,
 | 
			
		||||
		justifyContent: "center",
 | 
			
		||||
		alignItems: "center",
 | 
			
		||||
		marginTop: "5%",
 | 
			
		||||
	},
 | 
			
		||||
	reviewList: {
 | 
			
		||||
		width: "100%",
 | 
			
		||||
		paddingHorizontal: "15%",
 | 
			
		||||
		marginTop: "5%",
 | 
			
		||||
	},
 | 
			
		||||
	itemContainer: {
 | 
			
		||||
		borderColor: "gray",
 | 
			
		||||
		borderWidth: 1,
 | 
			
		||||
		borderRadius: 10,
 | 
			
		||||
		padding: 13,
 | 
			
		||||
		marginBottom: "5%",
 | 
			
		||||
		flex: 1,
 | 
			
		||||
		flexDirection: "row",
 | 
			
		||||
		justifyContent: "space-between",
 | 
			
		||||
	},
 | 
			
		||||
	itemImg: {
 | 
			
		||||
		width: 150,
 | 
			
		||||
		aspectRatio: 1,
 | 
			
		||||
		resizeMode: "contain",
 | 
			
		||||
		marginTop: "5%",
 | 
			
		||||
	},
 | 
			
		||||
});
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 7.9 KiB  | 
| 
		 Before Width: | Height: | Size: 8.1 KiB  | 
| 
		 Before Width: | Height: | Size: 8.9 KiB  | 
| 
		 Before Width: | Height: | Size: 8.9 KiB  | 
| 
		 Before Width: | Height: | Size: 11 KiB  | 
| 
		 Before Width: | Height: | Size: 8.9 KiB  | 
| 
		 Before Width: | Height: | Size: 9.0 KiB  | 
							
								
								
									
										244
									
								
								frontend/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						@@ -10,7 +10,6 @@
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@expo/metro-runtime": "~3.2.1",
 | 
			
		||||
        "@react-native-async-storage/async-storage": "^1.23.1",
 | 
			
		||||
        "@shopify/flash-list": "1.6.4",
 | 
			
		||||
        "@types/react": "~18.2.45",
 | 
			
		||||
        "axios": "^1.6.8",
 | 
			
		||||
        "expo": "^51.0.2",
 | 
			
		||||
@@ -26,7 +25,6 @@
 | 
			
		||||
        "react-dom": "18.2.0",
 | 
			
		||||
        "react-native": "0.74.1",
 | 
			
		||||
        "react-native-dropdown-picker": "^5.4.6",
 | 
			
		||||
        "react-native-range-slider-expo": "^1.4.3",
 | 
			
		||||
        "react-native-safe-area-context": "4.10.1",
 | 
			
		||||
        "react-native-screens": "3.31.1",
 | 
			
		||||
        "react-native-web": "~0.19.6"
 | 
			
		||||
@@ -2258,18 +2256,6 @@
 | 
			
		||||
        "node": ">=14.21.3"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@egjs/hammerjs": {
 | 
			
		||||
      "version": "2.0.17",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz",
 | 
			
		||||
      "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==",
 | 
			
		||||
      "peer": true,
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@types/hammerjs": "^2.0.36"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=0.8.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@expo/bunyan": {
 | 
			
		||||
      "version": "4.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@expo/bunyan/-/bunyan-4.0.0.tgz",
 | 
			
		||||
@@ -6317,25 +6303,6 @@
 | 
			
		||||
        "join-component": "^1.1.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@shopify/flash-list": {
 | 
			
		||||
      "version": "1.6.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@shopify/flash-list/-/flash-list-1.6.4.tgz",
 | 
			
		||||
      "integrity": "sha512-M2momcnY7swsvmpHIFDVbdOaFw4aQocJXA/lFP0Gpz+alQjFylqVKvszxl4atYO2SNbjxlb2L6hEP9WEcAknGQ==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "recyclerlistview": "4.2.0",
 | 
			
		||||
        "tslib": "2.4.0"
 | 
			
		||||
      },
 | 
			
		||||
      "peerDependencies": {
 | 
			
		||||
        "@babel/runtime": "*",
 | 
			
		||||
        "react": "*",
 | 
			
		||||
        "react-native": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@shopify/flash-list/node_modules/tslib": {
 | 
			
		||||
      "version": "2.4.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
 | 
			
		||||
      "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@sideway/address": {
 | 
			
		||||
      "version": "4.1.5",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
 | 
			
		||||
@@ -6380,12 +6347,6 @@
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
 | 
			
		||||
      "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@types/hammerjs": {
 | 
			
		||||
      "version": "2.0.45",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.45.tgz",
 | 
			
		||||
      "integrity": "sha512-qkcUlZmX6c4J8q45taBKTL3p+LbITgyx7qhlPYOdOHZB7B31K0mXbP5YA7i7SgDeEGuI9MnumiKPEMrxg8j3KQ==",
 | 
			
		||||
      "peer": true
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@types/istanbul-lib-coverage": {
 | 
			
		||||
      "version": "2.0.6",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
 | 
			
		||||
@@ -7027,12 +6988,6 @@
 | 
			
		||||
        "node": ">= 6"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/boolbase": {
 | 
			
		||||
      "version": "1.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
 | 
			
		||||
      "peer": true
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/bplist-creator": {
 | 
			
		||||
      "version": "0.1.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz",
 | 
			
		||||
@@ -7736,56 +7691,6 @@
 | 
			
		||||
        "hyphenate-style-name": "^1.0.3"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/css-select": {
 | 
			
		||||
      "version": "5.1.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
 | 
			
		||||
      "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
 | 
			
		||||
      "peer": true,
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "boolbase": "^1.0.0",
 | 
			
		||||
        "css-what": "^6.1.0",
 | 
			
		||||
        "domhandler": "^5.0.2",
 | 
			
		||||
        "domutils": "^3.0.1",
 | 
			
		||||
        "nth-check": "^2.0.1"
 | 
			
		||||
      },
 | 
			
		||||
      "funding": {
 | 
			
		||||
        "url": "https://github.com/sponsors/fb55"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/css-tree": {
 | 
			
		||||
      "version": "1.1.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
 | 
			
		||||
      "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
 | 
			
		||||
      "peer": true,
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "mdn-data": "2.0.14",
 | 
			
		||||
        "source-map": "^0.6.1"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=8.0.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/css-tree/node_modules/source-map": {
 | 
			
		||||
      "version": "0.6.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
 | 
			
		||||
      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
 | 
			
		||||
      "peer": true,
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=0.10.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/css-what": {
 | 
			
		||||
      "version": "6.1.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
 | 
			
		||||
      "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
 | 
			
		||||
      "peer": true,
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">= 6"
 | 
			
		||||
      },
 | 
			
		||||
      "funding": {
 | 
			
		||||
        "url": "https://github.com/sponsors/fb55"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/csstype": {
 | 
			
		||||
      "version": "3.1.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
 | 
			
		||||
@@ -8063,61 +7968,6 @@
 | 
			
		||||
        "node": ">=8"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/dom-serializer": {
 | 
			
		||||
      "version": "2.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
 | 
			
		||||
      "peer": true,
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "domelementtype": "^2.3.0",
 | 
			
		||||
        "domhandler": "^5.0.2",
 | 
			
		||||
        "entities": "^4.2.0"
 | 
			
		||||
      },
 | 
			
		||||
      "funding": {
 | 
			
		||||
        "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/domelementtype": {
 | 
			
		||||
      "version": "2.3.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
 | 
			
		||||
      "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
 | 
			
		||||
      "funding": [
 | 
			
		||||
        {
 | 
			
		||||
          "type": "github",
 | 
			
		||||
          "url": "https://github.com/sponsors/fb55"
 | 
			
		||||
        }
 | 
			
		||||
      ],
 | 
			
		||||
      "peer": true
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/domhandler": {
 | 
			
		||||
      "version": "5.0.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
 | 
			
		||||
      "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
 | 
			
		||||
      "peer": true,
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "domelementtype": "^2.3.0"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">= 4"
 | 
			
		||||
      },
 | 
			
		||||
      "funding": {
 | 
			
		||||
        "url": "https://github.com/fb55/domhandler?sponsor=1"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/domutils": {
 | 
			
		||||
      "version": "3.1.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
 | 
			
		||||
      "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
 | 
			
		||||
      "peer": true,
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "dom-serializer": "^2.0.0",
 | 
			
		||||
        "domelementtype": "^2.3.0",
 | 
			
		||||
        "domhandler": "^5.0.3"
 | 
			
		||||
      },
 | 
			
		||||
      "funding": {
 | 
			
		||||
        "url": "https://github.com/fb55/domutils?sponsor=1"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/dotenv": {
 | 
			
		||||
      "version": "16.4.5",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
 | 
			
		||||
@@ -8174,18 +8024,6 @@
 | 
			
		||||
        "once": "^1.4.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/entities": {
 | 
			
		||||
      "version": "4.5.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
 | 
			
		||||
      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
 | 
			
		||||
      "peer": true,
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=0.12"
 | 
			
		||||
      },
 | 
			
		||||
      "funding": {
 | 
			
		||||
        "url": "https://github.com/fb55/entities?sponsor=1"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/env-editor": {
 | 
			
		||||
      "version": "0.4.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/env-editor/-/env-editor-0.4.2.tgz",
 | 
			
		||||
@@ -9365,15 +9203,6 @@
 | 
			
		||||
        "node": ">=8"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/hoist-non-react-statics": {
 | 
			
		||||
      "version": "3.3.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
 | 
			
		||||
      "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
 | 
			
		||||
      "peer": true,
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "react-is": "^16.7.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/hosted-git-info": {
 | 
			
		||||
      "version": "3.0.8",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.8.tgz",
 | 
			
		||||
@@ -11152,12 +10981,6 @@
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/md5hex/-/md5hex-1.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-c2YOUbp33+6thdCUi34xIyOU/a7bvGKj/3DB1iaPMTuPHf/Q2d5s4sn1FaCOO43XkXggnb08y5W2PU8UNYNLKQ=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/mdn-data": {
 | 
			
		||||
      "version": "2.0.14",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
 | 
			
		||||
      "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
 | 
			
		||||
      "peer": true
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/memoize-one": {
 | 
			
		||||
      "version": "5.2.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
 | 
			
		||||
@@ -12023,18 +11846,6 @@
 | 
			
		||||
        "node": ">=4"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/nth-check": {
 | 
			
		||||
      "version": "2.1.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
 | 
			
		||||
      "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
 | 
			
		||||
      "peer": true,
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "boolbase": "^1.0.0"
 | 
			
		||||
      },
 | 
			
		||||
      "funding": {
 | 
			
		||||
        "url": "https://github.com/fb55/nth-check?sponsor=1"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/nullthrows": {
 | 
			
		||||
      "version": "1.1.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz",
 | 
			
		||||
@@ -13075,19 +12886,6 @@
 | 
			
		||||
        "react-native": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/react-native-gesture-handler": {
 | 
			
		||||
      "version": "1.10.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-1.10.3.tgz",
 | 
			
		||||
      "integrity": "sha512-cBGMi1IEsIVMgoox4RvMx7V2r6bNKw0uR1Mu1o7NbuHS6BRSVLq0dP34l2ecnPlC+jpWd3le6Yg1nrdCjby2Mw==",
 | 
			
		||||
      "peer": true,
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@egjs/hammerjs": "^2.0.17",
 | 
			
		||||
        "fbjs": "^3.0.0",
 | 
			
		||||
        "hoist-non-react-statics": "^3.3.0",
 | 
			
		||||
        "invariant": "^2.2.4",
 | 
			
		||||
        "prop-types": "^15.7.2"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/react-native-helmet-async": {
 | 
			
		||||
      "version": "2.0.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/react-native-helmet-async/-/react-native-helmet-async-2.0.4.tgz",
 | 
			
		||||
@@ -13101,15 +12899,6 @@
 | 
			
		||||
        "react": "^16.6.0 || ^17.0.0 || ^18.0.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/react-native-range-slider-expo": {
 | 
			
		||||
      "version": "1.4.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/react-native-range-slider-expo/-/react-native-range-slider-expo-1.4.3.tgz",
 | 
			
		||||
      "integrity": "sha512-EAIXfuCxJYffi6yqOcmCMWzvPNRSJ7zgDhQtVXXe2fokYaXVLG1uq2jM+cpTStMusIIGIo9HGZAja94N+rnNyg==",
 | 
			
		||||
      "peerDependencies": {
 | 
			
		||||
        "react-native-gesture-handler": "^1.6.1",
 | 
			
		||||
        "react-native-svg": "^12.1.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/react-native-safe-area-context": {
 | 
			
		||||
      "version": "4.10.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.10.1.tgz",
 | 
			
		||||
@@ -13132,20 +12921,6 @@
 | 
			
		||||
        "react-native": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/react-native-svg": {
 | 
			
		||||
      "version": "12.5.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-12.5.1.tgz",
 | 
			
		||||
      "integrity": "sha512-c374ENsq2MWCfr+7jC7TGwSeOAuC1Dp0osh2pw8PjpYFxmmB/toFIwcnCLz+SgBd6iLJClRhbATealqM05HOGg==",
 | 
			
		||||
      "peer": true,
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "css-select": "^5.1.0",
 | 
			
		||||
        "css-tree": "^1.1.3"
 | 
			
		||||
      },
 | 
			
		||||
      "peerDependencies": {
 | 
			
		||||
        "react": "*",
 | 
			
		||||
        "react-native": ">=0.50.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/react-native-web": {
 | 
			
		||||
      "version": "0.19.11",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.19.11.tgz",
 | 
			
		||||
@@ -13316,20 +13091,6 @@
 | 
			
		||||
        "node": ">=0.10.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/recyclerlistview": {
 | 
			
		||||
      "version": "4.2.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/recyclerlistview/-/recyclerlistview-4.2.0.tgz",
 | 
			
		||||
      "integrity": "sha512-uuBCi0c+ggqHKwrzPX4Z/mJOzsBbjZEAwGGmlwpD/sD7raXixdAbdJ6BTcAmuWG50Cg4ru9p12M94Njwhr/27A==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "lodash.debounce": "4.0.8",
 | 
			
		||||
        "prop-types": "15.8.1",
 | 
			
		||||
        "ts-object-utils": "0.0.5"
 | 
			
		||||
      },
 | 
			
		||||
      "peerDependencies": {
 | 
			
		||||
        "react": ">= 15.2.1",
 | 
			
		||||
        "react-native": ">= 0.30.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/regenerate": {
 | 
			
		||||
      "version": "1.4.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
 | 
			
		||||
@@ -14576,11 +14337,6 @@
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
 | 
			
		||||
      "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/ts-object-utils": {
 | 
			
		||||
      "version": "0.0.5",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/ts-object-utils/-/ts-object-utils-0.0.5.tgz",
 | 
			
		||||
      "integrity": "sha512-iV0GvHqOmilbIKJsfyfJY9/dNHCs969z3so90dQWsO1eMMozvTpnB1MEaUbb3FYtZTGjv5sIy/xmslEz0Rg2TA=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/tslib": {
 | 
			
		||||
      "version": "2.6.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
 | 
			
		||||
 
 | 
			
		||||
@@ -26,11 +26,9 @@
 | 
			
		||||
    "react-dom": "18.2.0",
 | 
			
		||||
    "react-native": "0.74.1",
 | 
			
		||||
    "react-native-dropdown-picker": "^5.4.6",
 | 
			
		||||
    "react-native-range-slider-expo": "^1.4.3",
 | 
			
		||||
    "react-native-safe-area-context": "4.10.1",
 | 
			
		||||
    "react-native-screens": "3.31.1",
 | 
			
		||||
    "react-native-web": "~0.19.6",
 | 
			
		||||
    "@shopify/flash-list": "1.6.4"
 | 
			
		||||
    "react-native-web": "~0.19.6"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@babel/core": "^7.20.0",
 | 
			
		||||
 
 | 
			
		||||