diff --git a/Backend/src/index.ts b/Backend/src/index.ts index 79173b3..b387dd9 100644 --- a/Backend/src/index.ts +++ b/Backend/src/index.ts @@ -15,6 +15,7 @@ import {errorHandler} from './middleware/error.middleware'; import {notFoundHandler} from './middleware/notFound.middleware'; import {usersRouter} from './models/users/users.router'; import {pricealarmsRouter} from './models/pricealarms/pricealarms.router'; +import {contactpersonsRouter} from './models/contact_persons/contact_persons.router'; const cookieParser = require('cookie-parser'); @@ -49,6 +50,7 @@ app.use('/prices', pricesRouter); app.use('/users', usersRouter); app.use('/vendors', vendorsRouter); app.use('/pricealarms', pricealarmsRouter); +app.use('/contactpersons', contactpersonsRouter); app.use(errorHandler); app.use(notFoundHandler); diff --git a/Backend/src/models/contact_persons/contact_person.interface.ts b/Backend/src/models/contact_persons/contact_person.interface.ts new file mode 100644 index 0000000..e777a40 --- /dev/null +++ b/Backend/src/models/contact_persons/contact_person.interface.ts @@ -0,0 +1,9 @@ +export interface Contact_Person { + contact_person_id: number; + first_name: string; + last_name: string; + gender: string; + email: string; + phone: string; + vendor_id: number; +} diff --git a/Backend/src/models/contact_persons/contact_persons.interface.ts b/Backend/src/models/contact_persons/contact_persons.interface.ts new file mode 100644 index 0000000..97f8393 --- /dev/null +++ b/Backend/src/models/contact_persons/contact_persons.interface.ts @@ -0,0 +1,5 @@ +import {Contact_Person} from './contact_person.interface'; + +export interface Contact_Persons { + [key: number]: Contact_Person; +} diff --git a/Backend/src/models/contact_persons/contact_persons.router.ts b/Backend/src/models/contact_persons/contact_persons.router.ts new file mode 100644 index 0000000..bb2d1a0 --- /dev/null +++ b/Backend/src/models/contact_persons/contact_persons.router.ts @@ -0,0 +1,129 @@ +/** + * Required External Modules and Interfaces + */ + +import express, {Request, Response} from 'express'; +import * as ContactPersonService from './contact_persons.service'; +import {Contact_Person} from './contact_person.interface'; +import {Contact_Persons} from './contact_persons.interface'; +import * as UserService from '../users/users.service'; +import * as PriceService from '../prices/prices.service'; + + +/** + * Router Definition + */ + +export const contactpersonsRouter = express.Router(); + + +/** + * Controller Definitions + */ + +// GET contactpersons/ +contactpersonsRouter.get('/', async (req: Request, res: Response) => { + try { + const contacts: Contact_Persons = await ContactPersonService.findAll(); + + res.status(200).send(contacts); + } catch (e) { + console.log('Error handling a request: ' + e.message); + res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'})); + } +}); + +// GET contactpersons/:id +contactpersonsRouter.get('/:id', async (req: Request, res: Response) => { + const id: number = parseInt(req.params.id, 10); + + if (!id) { + res.status(400).send('Missing parameters.'); + return; + } + + try { + const contact: Contact_Person = await ContactPersonService.find(id); + + res.status(200).send(contact); + } catch (e) { + console.log('Error handling a request: ' + e.message); + res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'})); + } +}); + +// GET contactpersons/byvendor/:id +contactpersonsRouter.get('/byvendor/:id', async (req: Request, res: Response) => { + const id: number = parseInt(req.params.id, 10); + + if (!id) { + res.status(400).send('Missing parameters.'); + return; + } + + try { + const contacts: Contact_Persons = await ContactPersonService.findByVendor(id); + + res.status(200).send(contacts); + } catch (e) { + console.log('Error handling a request: ' + e.message); + res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'})); + } +}); + +// POST contactpersons/ +contactpersonsRouter.post('/', async (req: Request, res: Response) => { + try { + // Authenticate user + const user_ip = req.connection.remoteAddress ?? ''; + const user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip); + + // Get required parameters + const vendor_id = req.body.vendor_id; + const first_name = req.body.first_name; + const last_name = req.body.last_name; + const gender = req.body.gender; + const email = req.body.email; + const phone = req.body.phone; + + const success = await ContactPersonService.createContactEntry(user.user_id, vendor_id, first_name, last_name, gender, email, phone); + + if (success) { + res.sendStatus(200); + } else { + res.sendStatus(500); + } + } catch (e) { + console.log('Error handling a request: ' + e.message); + res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'})); + } +}); + +// PUT contactpersons/:id +contactpersonsRouter.put('/:id', async (req: Request, res: Response) => { + try { + // Authenticate user + const user_ip = req.connection.remoteAddress ?? ''; + const user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip); + + // Get required parameters + const contact_person_id = parseInt(req.params.id, 10); + const vendor_id = req.body.vendor_id; + const first_name = req.body.first_name; + const last_name = req.body.last_name; + const gender = req.body.gender; + const email = req.body.email; + const phone = req.body.phone; + + const success = await ContactPersonService.updateContactEntry(user.user_id, contact_person_id, vendor_id, first_name, last_name, gender, email, phone); + + if (success) { + res.sendStatus(200); + } else { + res.sendStatus(500); + } + } catch (e) { + console.log('Error handling a request: ' + e.message); + res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'})); + } +}); diff --git a/Backend/src/models/contact_persons/contact_persons.service.ts b/Backend/src/models/contact_persons/contact_persons.service.ts new file mode 100644 index 0000000..7e23191 --- /dev/null +++ b/Backend/src/models/contact_persons/contact_persons.service.ts @@ -0,0 +1,175 @@ +import * as dotenv from 'dotenv'; + +dotenv.config(); + +const mariadb = require('mariadb'); +const pool = mariadb.createPool({ + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + database: process.env.DB_DATABASE, + connectionLimit: 5 +}); + +/** + * Data Model Interfaces + */ + +import {Contact_Person} from './contact_person.interface'; +import {Contact_Persons} from './contact_persons.interface'; + + +/** + * Service Methods + */ + +/** + * Fetches and returns all known contact persons + */ +export const findAll = async (): Promise => { + let conn; + let contRows = []; + try { + conn = await pool.getConnection(); + const rows = await conn.query('SELECT contact_person_id, first_name, last_name, gender, email, phone, vendor_id FROM contact_persons'); + for (let row in rows) { + if (row !== 'meta') { + contRows.push(rows[row]); + } + } + + } catch (err) { + throw err; + } finally { + if (conn) { + conn.end(); + } + } + + return contRows; +}; + +/** + * Fetches and returns the contact person with the specified id + * @param id The id of the contact person to fetch + */ +export const find = async (id: number): Promise => { + let conn; + let cont: any; + try { + conn = await pool.getConnection(); + const rows = await conn.query('SELECT contact_person_id, first_name, last_name, gender, email, phone, vendor_id FROM contact_persons WHERE contact_person_id = ?', id); + for (let row in rows) { + if (row !== 'meta') { + cont = rows[row]; + } + } + + } catch (err) { + throw err; + } finally { + if (conn) { + conn.end(); + } + } + + return cont; +}; + +/** + * Fetches and returns the contact persons for the specified vendor + * @param id The id of the vendor to fetch contact persons for + */ +export const findByVendor = async (id: number): Promise => { + let conn; + let contRows = []; + try { + conn = await pool.getConnection(); + const rows = await conn.query('SELECT contact_person_id, first_name, last_name, gender, email, phone, vendor_id FROM contact_persons WHERE vendor_id = ?', id); + for (let row in rows) { + if (row !== 'meta') { + contRows.push(rows[row]); + } + } + + } catch (err) { + throw err; + } finally { + if (conn) { + conn.end(); + } + } + + return contRows; +}; + +/** + * Creates a contact entry record + * @param user_id The user id of the issuing user + * @param vendor_id The vendor id of the vendor to create the record for + * @param first_name The first name of the contact person + * @param last_name The last name of the contact person + * @param gender The gender of the contact person + * @param email The email of the contact person + * @param phone The phone number of the contact person + */ +export const createContactEntry = async (user_id: number, vendor_id: number, first_name: string, last_name: string, gender: string, email: string, phone: string): Promise => { + let conn; + try { + conn = await pool.getConnection(); + + // Check if the user is authorized to manage the requested vendor + const user_vendor_rows = await conn.query('SELECT vendor_id FROM vendors WHERE vendor_id = ? AND admin_id = ?', [vendor_id, user_id]); + if (user_vendor_rows.length !== 1) { + return false; + } + + // Create contact person entry + const res = await conn.query('INSERT INTO contact_persons (first_name, last_name, gender, email, phone, vendor_id) VALUES (?, ?, ?, ?, ?, ?)', [first_name, last_name, gender, email, phone, vendor_id]); + + // If there are more / less than 1 affected rows, return false + return res.affectedRows === 1; + } catch (err) { + throw err; + } finally { + if (conn) { + conn.end(); + } + } +}; + +/** + * Updates a contact entry record + * @param user_id The user id of the issuing user + * @param contact_person_id The id of the record to update + * @param vendor_id The vendor id of the vendor to create the record for + * @param first_name The first name of the contact person + * @param last_name The last name of the contact person + * @param gender The gender of the contact person + * @param email The email of the contact person + * @param phone The phone number of the contact person + */ +export const updateContactEntry = async (user_id: number, contact_person_id: number, vendor_id: number, first_name: string, last_name: string, gender: string, email: string, phone: string): Promise => { + let conn; + try { + conn = await pool.getConnection(); + + // Check if the user is authorized to manage the requested vendor + const user_vendor_rows = await conn.query('SELECT vendor_id FROM vendors WHERE vendor_id = ? AND admin_id = ?', [vendor_id, user_id]); + if (user_vendor_rows.length !== 1) { + return false; + } + + // Create contact person entry + const res = await conn.query('UPDATE contact_persons SET first_name = ?, last_name = ?, gender = ?, email = ?, phone = ? WHERE contact_person_id = ? AND vendor_id = ?', [first_name, last_name, gender, email, phone, contact_person_id, vendor_id]); + + // If there are more / less than 1 affected rows, return false + return res.affectedRows === 1; + } catch (err) { + throw err; + } finally { + if (conn) { + conn.end(); + } + } +}; diff --git a/Backend/src/models/manufacturers/manufacturers.router.ts b/Backend/src/models/manufacturers/manufacturers.router.ts index e78815c..99b0875 100644 --- a/Backend/src/models/manufacturers/manufacturers.router.ts +++ b/Backend/src/models/manufacturers/manufacturers.router.ts @@ -19,7 +19,7 @@ export const manufacturersRouter = express.Router(); * Controller Definitions */ -// GET items/ +// GET manufacturers/ manufacturersRouter.get('/', async (req: Request, res: Response) => { try { const manufacturers: Manufacturers = await ManufacturerService.findAll(); @@ -31,7 +31,7 @@ manufacturersRouter.get('/', async (req: Request, res: Response) => { } }); -// GET items/:id +// GET manufacturers/:id manufacturersRouter.get('/:id', async (req: Request, res: Response) => { const id: number = parseInt(req.params.id, 10); @@ -50,7 +50,7 @@ manufacturersRouter.get('/:id', async (req: Request, res: Response) => { } }); -// GET items/:term +// GET manufacturers/:term manufacturersRouter.get('/search/:term', async (req: Request, res: Response) => { const term: string = req.params.term; diff --git a/Backend/src/models/prices/prices.router.ts b/Backend/src/models/prices/prices.router.ts index 12ffaf1..047ce1b 100644 --- a/Backend/src/models/prices/prices.router.ts +++ b/Backend/src/models/prices/prices.router.ts @@ -6,6 +6,7 @@ import express, {Request, Response} from 'express'; import * as PriceService from './prices.service'; import {Price} from './price.interface'; import {Prices} from './prices.interface'; +import * as UserService from '../users/users.service'; /** @@ -100,3 +101,28 @@ pricesRouter.get('/byProduct/list/:ids', async (req: Request, res: Response) => res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'})); } }); + +// POST prices/ +pricesRouter.post('/', async (req: Request, res: Response) => { + try { + // Authenticate user + const user_ip = req.connection.remoteAddress ?? ''; + const user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip); + + // Get required parameters + const vendor_id = req.body.vendor_id; + const product_id = req.body.product_id; + const price_in_cents = req.body.price_in_cents; + + const success = await PriceService.createPriceEntry(user.user_id, vendor_id, product_id, price_in_cents); + + if (success) { + res.sendStatus(200); + } else { + res.sendStatus(500); + } + } catch (e) { + console.log('Error handling a request: ' + e.message); + res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'})); + } +}); diff --git a/Backend/src/models/prices/prices.service.ts b/Backend/src/models/prices/prices.service.ts index 405cfd4..0a5ab2a 100644 --- a/Backend/src/models/prices/prices.service.ts +++ b/Backend/src/models/prices/prices.service.ts @@ -376,3 +376,29 @@ export const findListByProducts = async (productIds: [number]): Promise return priceRows; }; + +export const createPriceEntry = async (user_id: number, vendor_id: number, product_id: number, price_in_cents: number): Promise => { + let conn; + try { + conn = await pool.getConnection(); + + // Check if the user is authorized to manage the requested vendor + const user_vendor_rows = await conn.query('SELECT vendor_id FROM vendors WHERE vendor_id = ? AND admin_id = ?', [vendor_id, user_id]); + if (user_vendor_rows.length !== 1) { + return false; + } + + // Create price entry + const res = await conn.query('INSERT INTO prices (product_id, vendor_id, price_in_cents) VALUES (?,?,?)', [product_id, vendor_id, price_in_cents]); + + // If there are more / less than 1 affected rows, return false + return res.affectedRows === 1; + + } catch (err) { + throw err; + } finally { + if (conn) { + conn.end(); + } + } +}; diff --git a/Backend/src/models/products/products.router.ts b/Backend/src/models/products/products.router.ts index f0ea9a3..dd332e5 100644 --- a/Backend/src/models/products/products.router.ts +++ b/Backend/src/models/products/products.router.ts @@ -87,3 +87,22 @@ productsRouter.get('/list/:ids', async (req: Request, res: Response) => { res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'})); } }); + +// GET products/vendor/:id +productsRouter.get('/vendor/:id', async (req: Request, res: Response) => { + const id: number = parseInt(req.params.id, 10); + + if (!id) { + res.status(400).send('Missing parameters.'); + return; + } + + try { + const products: Products = await ProductService.findByVendor(id); + + res.status(200).send(products); + } catch (e) { + console.log('Error handling a request: ' + e.message); + res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'})); + } +}); diff --git a/Backend/src/models/products/products.service.ts b/Backend/src/models/products/products.service.ts index c0bee72..7d397ab 100644 --- a/Backend/src/models/products/products.service.ts +++ b/Backend/src/models/products/products.service.ts @@ -159,3 +159,41 @@ export const findList = async (ids: [number]): Promise => { return prodRows; }; + +/** + * Fetches and returns the products that the given vendor has price entries for + * @param id The id of the vendor to fetch the products for + */ +export const findByVendor = async (id: number): Promise => { + let conn; + let prodRows = []; + try { + conn = await pool.getConnection(); + + // Get the relevant product ids + let relevant_prod_ids = []; + const relevantProds = await conn.query('SELECT product_id FROM prices WHERE vendor_id = ? GROUP BY product_id', id); + for (let row in relevantProds) { + if (row !== 'meta') { + relevant_prod_ids.push(relevantProds[row].product_id); + } + } + + // Fetch products + const rows = await conn.query('SELECT product_id, name, asin, is_active, short_description, long_description, image_guid, date_added, last_modified, manufacturer_id, selling_rank, category_id FROM products WHERE product_id IN (?)', [relevant_prod_ids]); + for (let row in rows) { + if (row !== 'meta') { + prodRows.push(rows[row]); + } + } + + } catch (err) { + throw err; + } finally { + if (conn) { + conn.end(); + } + } + + return prodRows; +}; diff --git a/Backend/src/models/vendors/vendors.router.ts b/Backend/src/models/vendors/vendors.router.ts index 7ca180d..7440f4f 100644 --- a/Backend/src/models/vendors/vendors.router.ts +++ b/Backend/src/models/vendors/vendors.router.ts @@ -99,7 +99,7 @@ vendorsRouter.put('/manage/deactivatelisting', async (req: Request, res: Respons const success = await VendorService.deactivateListing(user.user_id, vendor_id, product_id); - if(success) { + if (success) { res.sendStatus(200); } else { res.sendStatus(500); diff --git a/Backend/src/models/vendors/vendors.service.ts b/Backend/src/models/vendors/vendors.service.ts index 1dc2220..39fee7d 100644 --- a/Backend/src/models/vendors/vendors.service.ts +++ b/Backend/src/models/vendors/vendors.service.ts @@ -171,10 +171,7 @@ export const deactivateListing = async (user_id: number, vendor_id: number, prod const status = await conn.query('UPDATE prices SET active_listing = false WHERE vendor_id = ? and product_id = ?', [vendor_id, product_id]); - if(status.affectedRows > 0){ - return true; - } - return false; + return status.affectedRows > 0; } catch (err) { throw err; } finally {