This commit is contained in:
Nat 2024-03-10 13:04:53 -07:00
commit 22fe5913e6
Signed by: nat
GPG Key ID: B53AB05285D710D6
47 changed files with 5276 additions and 0 deletions

9
Dockerfile Normal file
View File

@ -0,0 +1,9 @@
FROM node:14.10.1
WORKDIR /app
COPY package*.json ./
RUN npm install
CMD ["npm", "run", "start"]

BIN
Final-Report.odt Normal file

Binary file not shown.

BIN
Final-Report.pdf Normal file

Binary file not shown.

19
README.md Normal file
View File

@ -0,0 +1,19 @@
# Nat & Samira's Rare Earth Metals
An ecommerce website that may or may not sell rare earth metals
This project was completed late 2023 for COSC304 as a study in web development and database development it uses:
* Express.js on the backend
* Vanilla JS and web technologies on the front end
* Docker for deployment
[See our final report][1] for more technical information and a full walkthrough with screenshots.
## Setup
With docker installed, `cd` into the directory and run:
```
docker compose up
```
[1]: ./Final-Report.pdf

22
auth.js Normal file
View File

@ -0,0 +1,22 @@
const sql = require('mssql');
const auth = {
checkAuthentication: function(req, res) {
let authenticated = false;
if (req.session.authenticatedUser) {
authenticated = true;
}
if (!authenticated) {
let url = req.protocol + '://' + req.get('host') + req.originalUrl;
let loginMessage = "You have not been authorized to access the URL " + url;
req.session.loginMessage = loginMessage;
res.redirect("/login");
}
return authenticated;
}
}
module.exports = auth;

94
ddl/MySQL_WorksOn_DDL.sql Normal file
View File

@ -0,0 +1,94 @@
CREATE DATABASE IF NOT EXISTS workson;
USE workson;
SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS workson;
DROP TABLE IF EXISTS proj;
DROP TABLE IF EXISTS emp;
DROP TABLE IF EXISTS dept;
SET FOREIGN_KEY_CHECKS = 1;
CREATE TABLE emp (
eno CHAR(5) NOT NULL,
ename VARCHAR(30),
bdate DATE,
title CHAR(2),
salary DECIMAL(9,2),
supereno CHAR(5),
dno CHAR(5),
PRIMARY KEY (eno)
) ENGINE = InnoDB;
CREATE TABLE dept (
dno CHAR(5) NOT NULL,
dname VARCHAR(40),
mgreno CHAR(5),
PRIMARY KEY (dno),
CONSTRAINT FK_dept_emp FOREIGN KEY (mgreno) REFERENCES emp (eno) ON DELETE SET NULL) ENGINE = InnoDB;
ALTER TABLE emp ADD CONSTRAINT FK_emp_dept FOREIGN KEY (dno) REFERENCES dept(dno);
CREATE TABLE proj (
pno CHAR(5) NOT NULL,
pname VARCHAR(40),
budget DECIMAL(9,2),
dno CHAR(5),
PRIMARY KEY (pno),
CONSTRAINT FK_proj_dept FOREIGN KEY (dno) REFERENCES dept(dno) ON DELETE SET NULL) ENGINE = InnoDB;
CREATE TABLE workson (
eno CHAR(5) NOT NULL,
pno CHAR(5) NOT NULL,
resp VARCHAR(20),
hours SMALLINT,
CONSTRAINT PK_workson PRIMARY KEY (eno,pno),
CONSTRAINT FK_workson_emp FOREIGN KEY (eno) REFERENCES emp (eno),
CONSTRAINT FK_workson_proj FOREIGN KEY (pno) REFERENCES proj (pno)) ENGINE = InnoDB;
INSERT INTO dept VALUES ('D1','Management',NULL);
INSERT INTO dept VALUES ('D2','Consulting',NULL);
INSERT INTO dept VALUES ('D3','Accounting',NULL);
INSERT INTO dept VALUES ('D4','Development',NULL);
INSERT INTO proj VALUES ('P1','Instruments',150000,'D1');
INSERT INTO proj VALUES ('P2','DB Develop',135000,'D2');
INSERT INTO proj VALUES ('P3','Budget',250000,'D3');
INSERT INTO proj VALUES ('P4','Maintenance',310000,'D2');
INSERT INTO proj VALUES ('P5','CAD/CAM',500000,'D2');
INSERT INTO emp VALUES ('E1','J. Doe','1975-01-05','EE',30000,null,null);
INSERT INTO emp VALUES ('E2','M. Smith','1966-06-04','SA',50000,null,'D3');
INSERT INTO emp VALUES ('E3','A. Lee','1966-07-05','ME',40000,null,'D2');
INSERT INTO emp VALUES ('E4','J. Miller','1950-09-01','PR',20000,null,'D3');
INSERT INTO emp VALUES ('E5','B. Casey','1971-12-25','SA',50000,null,'D3');
INSERT INTO emp VALUES ('E6','L. Chu','1965-11-30','EE',30000,null,'D2');
INSERT INTO emp VALUES ('E7','R. Davis','1977-09-08','ME',40000,null,'D1');
INSERT INTO emp VALUES ('E8','J. Jones','1972-10-11','SA',50000,null,'D1');
UPDATE emp SET supereno = 'E2' WHERE eno = 'E1';
UPDATE emp SET supereno = 'E5' WHERE eno = 'E2';
UPDATE emp SET supereno = 'E7' WHERE eno = 'E3';
UPDATE emp SET supereno = 'E6' WHERE eno = 'E4';
UPDATE emp SET supereno = 'E8' WHERE eno = 'E5';
UPDATE emp SET supereno = 'E7' WHERE eno = 'E6';
UPDATE emp SET supereno = 'E8' WHERE eno = 'E7';
Update dept SET mgreno = 'E8' WHERE dno = 'D1';
Update dept SET mgreno = 'E7' WHERE dno = 'D2';
Update dept SET mgreno = 'E5' WHERE dno = 'D3';
INSERT INTO workson VALUES ('E1','P1','Manager',12);
INSERT INTO workson VALUES ('E2','P1','Analyst',24);
INSERT INTO workson VALUES ('E2','P2','Analyst',6);
INSERT INTO workson VALUES ('E3','P3','Consultant',10);
INSERT INTO workson VALUES ('E3','P4','Engineer',48);
INSERT INTO workson VALUES ('E4','P2','Programmer',18);
INSERT INTO workson VALUES ('E5','P2','Manager',24);
INSERT INTO workson VALUES ('E6','P4','Manager',48);
INSERT INTO workson VALUES ('E7','P3','Engineer',36);

97
ddl/SQLServer_WorksOn.ddl Normal file
View File

@ -0,0 +1,97 @@
CREATE DATABASE workson;
go
USE workson;
go
-- Drop all tables ignoring foreign key constraints
exec sp_MSforeachtable "declare @name nvarchar(max); set @name = parsename('?', 1); exec sp_MSdropconstraints @name";
exec sp_MSforeachtable "drop table ?";
-- DROP TABLE IF EXISTS Emp;
-- DROP TABLE IF EXISTS Dept;
-- DROP TABLE IF EXISTS Proj;
-- DROP TABLE IF EXISTS WorksOn;
CREATE TABLE Emp (
eno CHAR(5) NOT NULL,
ename VARCHAR(30),
bdate DATETIME,
title CHAR(2),
salary DECIMAL(9,2),
supereno CHAR(5),
dno CHAR(5),
PRIMARY KEY (eno),
);
CREATE TABLE Dept (
dno CHAR(5) NOT NULL,
dname VARCHAR(40),
mgreno CHAR(5),
PRIMARY KEY (dno),
CONSTRAINT FK_Dept_Emp FOREIGN KEY (mgreno) REFERENCES Emp (eno));
ALTER TABLE Emp ADD CONSTRAINT FK_Emp_Dept FOREIGN KEY (dno) REFERENCES Dept(dno);
CREATE TABLE Proj (
pno CHAR(5) NOT NULL,
pname VARCHAR(40),
budget DECIMAL(9,2),
dno CHAR(5),
PRIMARY KEY (pno),
CONSTRAINT FK_Proj_Dept FOREIGN KEY (dno) REFERENCES Dept(dno));
CREATE TABLE WorksOn (
eno CHAR(5) NOT NULL,
pno CHAR(5) NOT NULL,
resp VARCHAR(20),
hours SMALLINT,
CONSTRAINT PK_WorksOn PRIMARY KEY (eno,pno),
CONSTRAINT FK_WorksOn_Emp FOREIGN KEY (eno) REFERENCES Emp (eno),
CONSTRAINT FK_WorksOn_Proj FOREIGN KEY (pno) REFERENCES Proj (pno));
INSERT INTO Dept VALUES ('D1','Management',NULL);
INSERT INTO Dept VALUES ('D2','Consulting',NULL);
INSERT INTO Dept VALUES ('D3','Accounting',NULL);
INSERT INTO Dept VALUES ('D4','Development',NULL);
INSERT INTO Proj VALUES ('P1','Instruments',150000,'D1');
INSERT INTO Proj VALUES ('P2','DB Develop',135000,'D2');
INSERT INTO Proj VALUES ('P3','Budget',250000,'D3');
INSERT INTO Proj VALUES ('P4','Maintenance',310000,'D2');
INSERT INTO Proj VALUES ('P5','CAD/CAM',500000,'D2');
INSERT INTO Emp VALUES ('E1','J. Doe','1975-01-05','EE',30000,null,null);
INSERT INTO Emp VALUES ('E2','M. Smith','1966-06-04','SA',50000,null,'D3');
INSERT INTO Emp VALUES ('E3','A. Lee','1966-07-05','ME',40000,null,'D2');
INSERT INTO Emp VALUES ('E4','J. Miller','1950-09-01','PR',20000,null,'D3');
INSERT INTO Emp VALUES ('E5','B. Casey','1971-12-25','SA',50000,null,'D3');
INSERT INTO Emp VALUES ('E6','L. Chu','1965-11-30','EE',30000,null,'D2');
INSERT INTO Emp VALUES ('E7','R. Davis','1977-09-08','ME',40000,null,'D1');
INSERT INTO Emp VALUES ('E8','J. Jones','1972-10-11','SA',50000,null,'D1');
UPDATE Emp SET supereno = 'E2' WHERE eno = 'E1';
UPDATE Emp SET supereno = 'E5' WHERE eno = 'E2';
UPDATE Emp SET supereno = 'E7' WHERE eno = 'E3';
UPDATE Emp SET supereno = 'E6' WHERE eno = 'E4';
UPDATE Emp SET supereno = 'E8' WHERE eno = 'E5';
UPDATE Emp SET supereno = 'E7' WHERE eno = 'E6';
UPDATE Emp SET supereno = 'E8' WHERE eno = 'E7';
Update Dept SET mgreno = 'E8' WHERE dno = 'D1';
Update Dept SET mgreno = 'E7' WHERE dno = 'D2';
Update Dept SET mgreno = 'E5' WHERE dno = 'D3';
INSERT INTO WorksOn VALUES ('E1','P1','Manager',12);
INSERT INTO WorksOn VALUES ('E2','P1','Analyst',24);
INSERT INTO WorksOn VALUES ('E2','P2','Analyst',6);
INSERT INTO WorksOn VALUES ('E3','P3','Consultant',10);
INSERT INTO WorksOn VALUES ('E3','P4','Engineer',48);
INSERT INTO WorksOn VALUES ('E4','P2','Programmer',18);
INSERT INTO WorksOn VALUES ('E5','P2','Manager',24);
INSERT INTO WorksOn VALUES ('E6','P4','Manager',48);
INSERT INTO WorksOn VALUES ('E7','P3','Engineer',36);

250
ddl/SQLServer_orderdb.ddl Normal file

File diff suppressed because one or more lines are too long

141
ddl/ShipmentMySQL.sql Normal file
View File

@ -0,0 +1,141 @@
CREATE DATABASE IF NOT EXISTS shipment;
USE shipment;
SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS customer;
DROP TABLE IF EXISTS product;
DROP TABLE IF EXISTS shipment;
DROP TABLE IF EXISTS shippedproduct;
SET FOREIGN_KEY_CHECKS = 1;
CREATE TABLE customer (
cid integer,
cname varchar(30),
address varchar(50),
city varchar(30),
state varchar(2),
primary key (cid)
) ENGINE=InnoDB;
CREATE TABLE product (
pid integer,
pname varchar(30),
price decimal(9,2),
inventory integer,
primary key (pid)
) ENGINE=InnoDB;
CREATE TABLE shipment (
sid integer,
cid integer,
shipdate datetime,
primary key (sid),
foreign key (cid) REFERENCES customer(cid)
) ENGINE=InnoDB;
CREATE TABLE shippedproduct (
sid integer,
pid integer,
amount integer,
primary key (sid, pid),
foreign key (sid) REFERENCES shipment(sid),
foreign key (pid) REFERENCES product(pid)
) ENGINE=InnoDB;
INSERT INTO customer VALUES (1,'Fred Smith','101 Evergreen Terrace','Springfield','IL');
INSERT INTO customer VALUES (2,'Joe Smithsonian','245 Straight Street','Iowa City','IA');
INSERT INTO customer VALUES (3,'Steve Stevenson','24 Michigan Ave.','Chicago','IL');
INSERT INTO customer VALUES (4,'Russell Johnson','1 Hollywood Drive','Hollywood','CA');
INSERT INTO customer VALUES (5,'John Doe','34 Dead End Lane','Detroit','MI');
INSERT INTO customer VALUES (6,'Scott Charles','748 Mayflower','Huntington Beach','CA');
INSERT INTO customer VALUES (7,'Robert Dean','234 Greenwood Drive','Morristown','NJ');
INSERT INTO customer VALUES (8,'Shannon Rose',null,'Wyandotte','MI');
INSERT INTO customer VALUES (9,'Beth Rosebud','1 First Street','Muscatine','IA');
INSERT INTO customer VALUES (10,'Suzanne May','2 Second Street','Iowa City','IA');
INSERT INTO customer VALUES (11,'Aiden Adams','324 2A Street','Kelowna','BC');
INSERT INTO customer VALUES (12,'Betty Bains',null,'Kelowna','BC');
INSERT INTO customer VALUES (13,'Cindy Champion','1 1st Street','Calgary','AB');
INSERT INTO customer VALUES (14,'David Denter','23456 Main Street','Vernon','BC');
INSERT INTO customer VALUES (15,'Elish Elias','3445 Hwy 97 North','Lake Country','BC');
INSERT INTO product VALUES (1,'Swiss Chocolate',32.99,40);
INSERT INTO product VALUES (2,'Wooden Chair',99.99,12);
INSERT INTO product VALUES (3,'Teddy Bear',12.99,12);
INSERT INTO product VALUES (4,'Chocolate Bar',5.95,40);
INSERT INTO product VALUES (5,'Desk',250.99,100);
INSERT INTO product VALUES (6,'Table',500,44);
INSERT INTO product VALUES (7,'Deluxe Sweet Collection',32.65,83);
INSERT INTO product VALUES (8,'Table',2.99,156);
INSERT INTO product VALUES (9,'Sports Car',123500,0);
INSERT INTO product VALUES (10,'Textbook',250,23);
INSERT INTO shipment VALUES (1,15,'2021-07-05 00:00:00');
INSERT INTO shipment VALUES (2,15,'2021-07-12 00:00:00');
INSERT INTO shipment VALUES (3,2,'2021-09-05 00:00:00');
INSERT INTO shipment VALUES (4,3,'2022-07-13 00:00:00');
INSERT INTO shipment VALUES (5,4,'2022-07-17 00:00:00');
INSERT INTO shipment VALUES (6,4,'2022-08-02 00:00:00');
INSERT INTO shipment VALUES (7,4,'2021-09-04 00:00:00');
INSERT INTO shipment VALUES (8,4,'2021-08-19 00:00:00');
INSERT INTO shipment VALUES (9,2,'2021-07-07 00:00:00');
INSERT INTO shipment VALUES (10,2,'2021-08-09 00:00:00');
INSERT INTO shipment VALUES (11,6,'2022-09-04 00:00:00');
INSERT INTO shipment VALUES (12,7,'2022-07-05 00:00:00');
INSERT INTO shipment VALUES (13,6,'2022-08-24 00:00:00');
INSERT INTO shipment VALUES (14,8,'2021-09-22 00:00:00');
INSERT INTO shipment VALUES (15,9,'2022-08-24 00:00:00');
INSERT INTO shipment VALUES (16,10,'2022-08-26 00:00:00');
INSERT INTO shipment VALUES (17,11,'2022-09-24 00:00:00');
INSERT INTO shipment VALUES (18,12,'2021-08-28 00:00:00');
INSERT INTO shipment VALUES (19,13,'2022-08-24 00:00:00');
INSERT INTO shipment VALUES (20,13,'2022-09-12 00:00:00');
INSERT INTO shipment VALUES (21,15,'2022-08-24 00:00:00');
INSERT INTO shipment VALUES (22,12,'2021-08-13 00:00:00');
INSERT INTO shipment VALUES (23,12,'2022-08-24 00:00:00');
INSERT INTO shippedproduct VALUES (1,10,1);
INSERT INTO shippedproduct VALUES (1,9,2);
INSERT INTO shippedproduct VALUES (1,7,5);
INSERT INTO shippedproduct VALUES (1,4,6);
INSERT INTO shippedproduct VALUES (2,1,3);
INSERT INTO shippedproduct VALUES (2,3,8);
INSERT INTO shippedproduct VALUES (2,10,1);
INSERT INTO shippedproduct VALUES (3,1,20);
INSERT INTO shippedproduct VALUES (4,1,5);
INSERT INTO shippedproduct VALUES (5,2,13);
INSERT INTO shippedproduct VALUES (6,1,4);
INSERT INTO shippedproduct VALUES (6,2,1);
INSERT INTO shippedproduct VALUES (7,1,3);
INSERT INTO shippedproduct VALUES (8,4,25);
INSERT INTO shippedproduct VALUES (9,2,32);
INSERT INTO shippedproduct VALUES (10,2,2);
INSERT INTO shippedproduct VALUES (11,2,5);
INSERT INTO shippedproduct VALUES (12,3,1);
INSERT INTO shippedproduct VALUES (12,4,10);
INSERT INTO shippedproduct VALUES (13,1,5);
INSERT INTO shippedproduct VALUES (13,4,2);
INSERT INTO shippedproduct VALUES (13,2,7);
INSERT INTO shippedproduct VALUES (14,10,1);
INSERT INTO shippedproduct VALUES (14,3,2);
INSERT INTO shippedproduct VALUES (15,6,5);
INSERT INTO shippedproduct VALUES (15,8,25);
INSERT INTO shippedproduct VALUES (16,1,4);
INSERT INTO shippedproduct VALUES (16,10,2);
INSERT INTO shippedproduct VALUES (17,3,4);
INSERT INTO shippedproduct VALUES (17,4,5);
INSERT INTO shippedproduct VALUES (17,5,6);
INSERT INTO shippedproduct VALUES (18,7,5);
INSERT INTO shippedproduct VALUES (19,8,12);
INSERT INTO shippedproduct VALUES (20,9,1);
INSERT INTO shippedproduct VALUES (20,10,3);
INSERT INTO shippedproduct VALUES (20,2,5);
INSERT INTO shippedproduct VALUES (21,1,3);
INSERT INTO shippedproduct VALUES (21,2,7);
INSERT INTO shippedproduct VALUES (22,4,11);
INSERT INTO shippedproduct VALUES (22,6,13);
INSERT INTO shippedproduct VALUES (23,7,1);
INSERT INTO shippedproduct VALUES (23,8,1);
INSERT INTO shippedproduct VALUES (23,9,1);

View File

@ -0,0 +1,129 @@
CREATE DATABASE IF NOT EXISTS university;
USE university;
SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS student;
DROP TABLE IF EXISTS faculty;
DROP TABLE IF EXISTS course;
DROP TABLE IF EXISTS enrolled;
SET FOREIGN_KEY_CHECKS = 1;
CREATE TABLE student(
snum integer,
sname varchar(30),
major varchar(25),
standing varchar(2),
age integer,
primary key (snum)
) ENGINE=InnoDB;
CREATE TABLE faculty(
fid integer,
fname varchar(30),
deptid integer,
primary key (fid)
) ENGINE=InnoDB;
CREATE TABLE course(
cname varchar(40),
meets_at varchar(20),
room varchar(10),
fid integer,
primary key (cname),
foreign key (fid) references faculty(fid)
) ENGINE=InnoDB;
CREATE TABLE enrolled(
snum integer,
cname varchar(40),
primary key (snum,cname),
foreign key (snum) references student(snum),
foreign key (cname) references course(cname)
) ENGINE=InnoDB;
INSERT INTO student VALUES( 051135593,'Maria White','English','SR',21);
INSERT INTO student VALUES( 060839453,'Charles Harris','Architecture','SR',22);
INSERT INTO student VALUES( 099354543,'Susan Martin','Law','JR',20);
INSERT INTO student VALUES( 112348546,'Joseph Thompson','Computer Science','SO',19);
INSERT INTO student VALUES( 115987938,'Christopher Garcia','Computer Science','JR',20);
INSERT INTO student VALUES( 132977562,'Angela Martinez','History','SR',20);
INSERT INTO student VALUES( 269734834,'Thomas Robinson','Psychology','SO',18);
INSERT INTO student VALUES( 280158572,'Margaret Clark','Animal Science','FR',18);
INSERT INTO student VALUES( 301221823,'Juan Rodriguez','Psychology','JR',20);
INSERT INTO student VALUES( 318548912,'Dorthy Lewis','Finance','FR',18);
INSERT INTO student VALUES( 320874981,'Daniel Lee','Electrical Engineering','FR',17);
INSERT INTO student VALUES( 322654189,'Lisa Walker','Computer Science','SO',17);
INSERT INTO student VALUES( 348121549,'Paul Hall','Computer Science','JR',18);
INSERT INTO student VALUES( 351565322,'Nancy Allen','Accounting','JR',19);
INSERT INTO student VALUES( 451519864,'Mark Young','Finance','FR',18);
INSERT INTO student VALUES( 455798411,'Luis Hernandez','Electrical Engineering','FR',17);
INSERT INTO student VALUES( 462156489,'Donald King','Mechanical Engineering','SO',19);
INSERT INTO student VALUES( 550156548,'George Wright','Education','SR',21);
INSERT INTO student VALUES( 552455318,'Ana Lopez','Computer Engineering','SR',19);
INSERT INTO student VALUES( 556784565,'Kenneth Hill','Civil Engineering','SR',21);
INSERT INTO student VALUES( 567354612,'Karen Scott','Computer Engineering','FR',18);
INSERT INTO student VALUES( 573284895,'Steven Green','Kinesiology','SO',19);
INSERT INTO student VALUES( 574489456,'Betty Adams','Economics','JR',20);
INSERT INTO student VALUES( 578875478,'Edward Baker','Veterinary Medicine','SR',21);
INSERT INTO faculty VALUES( 142519864,'Ivana Teach',20);
INSERT INTO faculty VALUES( 242518965,'James Smith',68);
INSERT INTO faculty VALUES( 141582651,'Mary Johnson',20);
INSERT INTO faculty VALUES( 011564812,'John Williams',68);
INSERT INTO faculty VALUES( 254099823,'Patricia Jones',68);
INSERT INTO faculty VALUES( 356187925,'Robert Brown',12);
INSERT INTO faculty VALUES( 489456522,'Linda Davis',20);
INSERT INTO faculty VALUES( 287321212,'Michael Miller',12);
INSERT INTO faculty VALUES( 248965255,'Barbara Wilson',12);
INSERT INTO faculty VALUES( 159542516,'William Moore',33);
INSERT INTO faculty VALUES( 090873519,'Elizabeth Taylor',11);
INSERT INTO faculty VALUES( 486512566,'David Anderson',20);
INSERT INTO faculty VALUES( 619023588,'Jennifer Thomas',11);
INSERT INTO faculty VALUES( 489221823,'Richard Jackson',33);
INSERT INTO faculty VALUES( 548977562,'Ulysses Teach',20);
INSERT INTO course VALUES('Data Structures','MWF 10','R128',489456522);
INSERT INTO course VALUES('Database Systems','MWF 12:30-1:45','1320 DCL',142519864);
INSERT INTO course VALUES('Operating System Design','TuTh 12-1:20','20 AVW',489456522);
INSERT INTO course VALUES('Archaeology of the Incas','MWF 3-4:15','R128',248965255);
INSERT INTO course VALUES('Aviation Accident Investigation','TuTh 1-2:50','Q3',011564812);
INSERT INTO course VALUES('Air Quality Engineering','TuTh 10:30-11:45','R15',011564812);
INSERT INTO course VALUES('Introductory Latin','MWF 3-4:15','R12',248965255);
INSERT INTO course VALUES('American Political Parties','TuTh 2-3:15','20 AVW',619023588);
INSERT INTO course VALUES('Social Cognition','Tu 6:30-8:40','R15',159542516);
INSERT INTO course VALUES('Perception','MTuWTh 3','Q3',489221823);
INSERT INTO course VALUES('Multivariate Analysis','TuTh 2-3:15','R15',090873519);
INSERT INTO course VALUES('Patent Law','F 1-2:50','R128',090873519);
INSERT INTO course VALUES('Urban Economics','MWF 11','20 AVW',489221823);
INSERT INTO course VALUES('Organic Chemistry','TuTh 12:30-1:45','R12',489221823);
INSERT INTO course VALUES('Marketing Research','MW 10-11:15','1320 DCL',489221823);
INSERT INTO course VALUES('Seminar in American Art','M 4','R15',489221823);
INSERT INTO course VALUES('Orbital Mechanics','MWF 8','1320 DCL',011564812);
INSERT INTO course VALUES('Dairy Herd Management','TuTh 12:30-1:45','R128',356187925);
INSERT INTO course VALUES('Communication Networks','MW 9:30-10:45','20 AVW',141582651);
INSERT INTO course VALUES('Optical Electronics','TuTh 12:30-1:45','R15',254099823);
INSERT INTO course VALUES('Intoduction to Math','TuTh 8-9:30','R128',489221823);
INSERT INTO enrolled VALUES( 112348546,'Database Systems');
INSERT INTO enrolled VALUES(115987938,'Database Systems');
INSERT INTO enrolled VALUES(348121549,'Database Systems');
INSERT INTO enrolled VALUES(322654189,'Database Systems');
INSERT INTO enrolled VALUES(552455318,'Database Systems');
INSERT INTO enrolled VALUES(455798411,'Operating System Design');
INSERT INTO enrolled VALUES(552455318,'Operating System Design');
INSERT INTO enrolled VALUES(567354612,'Operating System Design');
INSERT INTO enrolled VALUES(112348546,'Operating System Design');
INSERT INTO enrolled VALUES(115987938,'Operating System Design');
INSERT INTO enrolled VALUES(322654189,'Operating System Design');
INSERT INTO enrolled VALUES(567354612,'Data Structures');
INSERT INTO enrolled VALUES(552455318,'Communication Networks');
INSERT INTO enrolled VALUES(455798411,'Optical Electronics');
INSERT INTO enrolled VALUES(301221823,'Perception');
INSERT INTO enrolled VALUES(301221823,'Social Cognition');
INSERT INTO enrolled VALUES(301221823,'American Political Parties');
INSERT INTO enrolled VALUES(556784565,'Air Quality Engineering');
INSERT INTO enrolled VALUES(099354543,'Patent Law');
INSERT INTO enrolled VALUES(574489456,'Urban Economics');

13
ddl/user_mydb_create.sql Normal file
View File

@ -0,0 +1,13 @@
CREATE DATABASE IF NOT EXISTS mydb;
USE mydb;
GRANT ALL privileges ON mydb.* TO testuser;
USE workson;
GRANT ALL privileges ON workson.* TO testuser;
USE university;
GRANT ALL privileges ON university.* TO testuser;
USE shipment;
GRANT ALL privileges ON shipment.* TO testuser;

Binary file not shown.

61
docker-compose.yml Normal file
View File

@ -0,0 +1,61 @@
version: '3.8'
services:
node:
build:
context: ./
dockerfile: Dockerfile
image: cosc304-node
container_name: cosc304-node
volumes:
- ./:/app/
- /app/node_modules
networks:
network304:
aliases:
- cosc304_node
ports:
- 80:3000
cosc304-sqlserver:
image: mcr.microsoft.com/mssql/server:2019-latest
container_name: cosc304-sqlserver
restart: always
environment:
ACCEPT_EULA: 'Y'
SA_PASSWORD: '304#sa#pw'
ports:
- '1433:1433'
expose:
- '1433'
volumes:
- cosc304-db:/var/lib/mssql
- ./ddl:/scripts
networks:
network304:
aliases:
- cosc304_sqlserver
command:
- /bin/bash
- -c
- |
/opt/mssql/bin/sqlservr &
pid=$$!
echo "Waiting for MS SQL to be available"
/opt/mssql-tools/bin/sqlcmd -l 30 -S localhost -h-1 -V1 -U sa -P $$SA_PASSWORD -Q "SET NOCOUNT ON SELECT \"YAY WE ARE UP\" , @@servername"
is_up=$$?
while [ $$is_up -ne 0 ] ; do
echo -e $$(date)
/opt/mssql-tools/bin/sqlcmd -l 30 -S localhost -h-1 -V1 -U sa -P $$SA_PASSWORD -Q "SET NOCOUNT ON SELECT \"YAY WE ARE UP\" , @@servername"
is_up=$$?
sleep 5
done
for foo in /scripts/SQLServer*.ddl
do /opt/mssql-tools/bin/sqlcmd -U sa -P $$SA_PASSWORD -l 30 -e -i $$foo
done
trap "kill -15 $$pid" SIGTERM
wait $$pid
exit 0
volumes:
cosc304-db:
mysql-db:
networks:
network304:

2130
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

23
package.json Normal file
View File

@ -0,0 +1,23 @@
{
"name": "cosc304lab7",
"version": "1.0.0",
"description": "COSC 304 Lab 7 Node Variant",
"repository": "N/A",
"main": "server.js",
"scripts": {
"start": "nodemon server.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.17.1",
"express-handlebars": "^5.1.0",
"express-session": "^1.17.1",
"moment": "^2.28.0",
"mssql": "^6.2.1",
"nodemon": "^2.0.4",
"tedious": "^9.2.1",
"multer": "^1.4.5-lts.1"
}
}

81
public/css/styles.css Normal file
View File

@ -0,0 +1,81 @@
body {
margin: 0;
}
h1 {
text-align: center;
font-weight: normal;
}
nav {
width: 100%;
position: fixed;
top: 0; left: 0;
background-color: white;
border-bottom: 3px double black;
font-size: 1.1em;
}
nav > ul {
padding-left: 10px;
list-style: none;
}
nav > ul > li {
display: inline-block;
padding-right: 20px;
}
li.nav-right {
float: right;
padding-right: 0;
padding-left: 10px;
}
nav > ul:last-child {
padding-right: 10px;
}
main {
width: 80%;
margin-left: auto;
margin-right: auto;
min-height: 100vh;
}
.product-search-container {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
}
.product-search {
text-align: center;
}
.product-search > input[type="text"] {
width: 90%;
margin-bottom: 10px;
}
.product-search > input, .product-search > button {
font-size: 1.1em;
padding: 5px;
}
.product-search > button, .product-search > input[type="submit"] {
padding: 5px;
width: 80px;
text-align: center;
background-color: white;
border-color: black;
}
.product-search > button:hover, .product-search > input[type="submit"]:hover {
padding: 5px;
width: 80px;
text-align: center;
background-color: grey;
border-color: black;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
public/img/1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
public/img/1_a.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

BIN
public/img/2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
public/img/3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

BIN
public/img/4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
public/img/5.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 KiB

44
routes/addcart.js Normal file
View File

@ -0,0 +1,44 @@
const express = require('express');
const router = express.Router();
router.get('/', function(req, res, next) {
res.setHeader('Content-Type', 'text/html');
// If the product list isn't set in the session,
// create a new list.
let productList = false;
if (!req.session.productList) {
productList = [];
} else {
productList = req.session.productList;
}
// Add new product selected
// Get product information
let id = false;
let name = false;
let price = false;
if (req.query.id && req.query.name && req.query.price) {
id = req.query.id;
name = req.query.name;
price = req.query.price;
} else {
res.redirect("/listprod");
}
// Update quantity if add same item to order again
if (productList.some(p => p.id == id)) {
productList = productList.map(p => (p.id == id ? {...p, quantity: p.quantity + 1} : p));
} else {
productList.push({
"id": id,
"name": name,
"price": price,
"quantity": 1
});
}
req.session.productList = productList;
res.redirect("/showcart");
});
module.exports = router;

523
routes/admin.js Normal file
View File

@ -0,0 +1,523 @@
const express = require('express');
const router = express.Router();
const auth = require('../auth');
const sql = require('mssql');
const { getUser } = require('../util.js');
const multer = require('multer');
const storage = multer.memoryStorage();
const uploadFile = multer({ dest: 'public/img' });
const uploadDB = multer({ storage });
router.get('/', async function(req, res, next) {
res.setHeader('Content-Type', 'text/html');
if (!req.session.user.isAdmin) {
res.send(htmlPage(`
<p>404: Not Found</p>
`, `404: Not Found`));
return;
}
let pool;
try {
let pool = await sql.connect(dbConfig);
// TO/DO: Write SQL query that prints out total order amount by day
let results = await pool.request().query(`
select cast(orderDate as date) as dateOfOrder, sum(totalAmount) as valueOfOrders
from ordersummary
group by cast(orderDate as date)
`);
const chartData = JSON.stringify({
type: 'bar',
data: {
labels: results.recordset.map(r => r.dateOfOrder),
datasets: [{
label: 'Income',
data: results.recordset.map(r => r.valueOfOrders),
borderWidth: 1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
res.render('layouts/main', {
loggedIn: getUser(req) != null,
user: getUser(req),
spacer: true,
content: `
<h1>Administrator Panel</h2>
<ul>
<li>
<a href="/admin/listCustomers">
Customer Directory
</a>
</li>
<li>
<a href="/admin/addProduct">
Add New Product
</a>
</li>
<li>
<a href="/admin/productDirectory">
Edit Products
</a>
</li>
</ul>
<h2> Fulfill an order </h2>
<form method="get" action="/ship">
<input type="number" name="orderId" placeholder="Order#"><br>
<input type="submit">
</form>
<h2>Sales By Day</h2>
<table>
<tbody>
<tr>
<th>Order Date</th>
<th>Income</th>
</tr>
${results.recordset.map(row =>`
<tr>
<td>${(new Date(row.dateOfOrder)).toDateString()}</td>
<td>$${Number(row.valueOfOrders).toFixed(2)}</td>
</tr>
`).join("\n")}
</tbody>
</table>
<div>
<canvas id="salesChart"></canvas>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
const chart = document.getElementById("salesChart");
const chartData = JSON.parse('${chartData}');
console.log(chartData);
new Chart(chart, chartData);
</script>
`,
});
} catch(err) {
console.dir(err);
res.write(err + "");
res.end();
} finally {
if (pool) pool.close();
}
});
router.get('/listCustomers', async (req, res) => {
let pool;
try {
let pool = await sql.connect(dbConfig);
let customers = (await pool.request().query(`
select * from customer
`)).recordset;
res.render('layouts/main', {
title: 'Registered Customers',
loggedIn: getUser(req) != null,
user: getUser(req),
spacer: true,
content: `
<h1> Customer Directory </h1>
<table>
<tbody>
<tr>
<th>Name</th>
<th>Username</th>
<th>Email</th>
</tr>
${customers.map(customer =>`
<tr>
<td>
${customer.firstName} ${customer.lastName}
</td>
<td>
${customer.userid}
</td>
<td>
${customer.email}
</td>
</tr>
`).join("\n")}
</tbody>
</table>
`,
});
} catch (err) {
console.error(err);
res.send("Unable to retrieve customer list");
} finally {
if (pool) pool.close();
}
});
router.get('/productDirectory', async (req, res) => {
let pool;
try {
pool = await sql.connect(dbConfig);
const products = (await pool.request().query(`
select * from product
`)).recordset;
res.render('layouts/main', {
title: 'Admin Product Directory',
spacer: true,
loggedIn: true,
user: getUser(req),
content: `
<h1> Admin Product Directory </h1>
<table>
<tbody>
<tr>
<th>ID</th>
<th>Name</th>
</tr>
${products.map(p => `
<tr>
<td>${p.productId}</td>
<td>${p.productName}</td>
<td>
<a href="/admin/productUpdate?id=${p.productId}">
Update
</a>
</td>
<td>
<a href="/admin/productAddImage?id=${p.productId}">
Add Image
</a>
</td>
<td>
<a href="/admin/productDelete?id=${p.productId}">
X
</a>
</td>
</tr>
`).join('\n')}
</tbody>
</table>
`,
});
} catch (err) {
res.send("Could not retrieve categories: " + err.toString());
console.error(err);
} finally {
if (pool) pool.close();
}
});
router.get('/productDelete', async (req, res) => {
let pool;
let stmt;
try {
pool = await sql.connect(dbConfig);
stmt = new sql.PreparedStatement(pool);
stmt.input('productId', sql.VarChar);
await stmt.prepare(`
delete from product where productId = @productId
`);
await stmt.execute({productId: req.query.id});
res.redirect('/admin/productDirectory');
} catch (err) {
res.send("Could not retrieve categories: " + err.toString());
console.error(err);
} finally {
if (stmt) stmt.unprepare();
if (pool) pool.close();
}
});
router.get('/productUpdate', async (req, res) => {
let pool;
let stmt;
try {
pool = await sql.connect(dbConfig);
stmt = new sql.PreparedStatement(pool);
stmt.input('productId', sql.VarChar);
await stmt.prepare(`
select * from product where productId = @productId
`);
const product = (await stmt.execute({productId: req.query.id})).recordset[0];
const categories = (await pool.request().query(`
select * from category
`)).recordset;
res.render('layouts/main', {
title: 'Add a New Product',
loggedIn: true,
user: getUser(req),
spacer: true,
content: `
<h1>Update Product</h1>
<form method="post" action="/admin/productUpdate?id=${product.productId}">
<label for="productName">Name: </label>
<input type="text" name="productName" value="${product.productName}" required><br>
<label for="productPrice">Price: </label>
<input type="number" name="productPrice" value="${product.productPrice}" required><br>
<label for="categoryId">Category: </label>
<select name="categoryId">
${categories.map(cat =>`
<option value="${cat.categoryId}" ${cat.categoryId == product.categoryId ? 'selected="selected"' : ''}>${cat.categoryName}</option>
`).join("\n")}
</select><br>
<label for="productDesc">Description: </label><br>
<textarea name="productDesc">${product.productDesc}</textarea><br>
<input type="submit" value="Submit">
</form>
`,
});
} catch (err) {
res.send("Could not retrieve data: " + err.toString());
console.error(err);
} finally {
if (stmt) stmt.unprepare();
if (pool) pool.close();
}
});
router.post('/productUpdate', async (req, res) => {
let pool;
let stmt;
try {
pool = await sql.connect(dbConfig);
stmt = new sql.PreparedStatement(pool);
stmt.input('productName', sql.VarChar);
stmt.input('productPrice', sql.Decimal(10, 2));
stmt.input('productDesc', sql.VarChar);
stmt.input('categoryId', sql.Int);
stmt.input('productId', sql.Int);
await stmt.prepare(`
update product set
productName = @productName,
productPrice = @productPrice,
productDesc = @productDesc,
categoryId = @categoryId
where productId = @productId
`);
console.log(req.query.id);
await stmt.execute({...req.body, productId: req.query.id});
res.redirect(`/admin/productDirectory`);
} catch (err) {
console.error(err);
res.send("Could not add product: " + err.toString());
} finally {
if (stmt) stmt.unprepare();
if (pool) pool.close();
}
});
router.get('/productAddImage', async (req, res) => {
res.render('layouts/main', {
title: 'Add a New Product',
loggedIn: true,
user: getUser(req),
spacer: true,
content: `
<h1> Add an image to this product</h1>
<form onsubmit="onSubmit(event)" id="imageForm">
<label for="productImage">Image: </label>
<input type="file" name="productImage" id="fileInput"><br>
<p>
Save this file in:
</p>
<input type="radio" id="storeFile" name="storageLocation" value="file" checked>
<label for="storeFile">The filesystem</label><br>
<input type="radio" id="storeDB" name="storageLocation" value="db">
<label for="storeDB">The DB</label><br>
<input type="submit" value="Submit">
</form>
<script>
function onSubmit(e) {
e.preventDefault();
const storageLocation = document.querySelector('input[name="storageLocation"]:checked').value;
const input = document.getElementById('fileInput');
let dest = 'productAddDBImage';
if (storageLocation == "file") {
dest = 'productAddFileImage';
}
const data = new FormData(document.getElementById("imageForm"));
fetch('/admin/' + dest + '?id=' + ${req.query.id}, {
method: 'POST',
body: data
}).then(res => {
window.location = "/admin/productDirectory";
});
return false;
}
</script>
`,
});
});
router.post('/productAddFileImage', uploadFile.single('productImage'), async (req, res) => {
let pool;
let stmt;
try {
pool = await sql.connect(dbConfig);
stmt = new sql.PreparedStatement(pool);
stmt.input('productId', sql.Int);
stmt.input('productImageURL', sql.VarChar);
await stmt.prepare(`
update product set
productImageURL = @productImageURL
where productId = @productId
`);
await stmt.execute({productId: req.query.id, productImageURL: `img/${req.file.filename}`});
res.redirect('/admin/productDirectory');
} catch (err) {
res.send("Could not update product: " + err.toString());
console.error(err);
} finally {
if (stmt) stmt.unprepare();
if (pool) pool.close();
}
});
router.post('/productAddDBImage', uploadDB.single('productImage'), async (req, res) => {
let pool;
let stmt;
try {
pool = await sql.connect(dbConfig);
stmt = new sql.PreparedStatement(pool);
stmt.input('productId', sql.Int);
stmt.input('productImage', sql.VarBinary);
await stmt.prepare(`
update product set
productImage = @productImage
where productId = @productId
`);
await stmt.execute({productId: req.query.id, productImage: req.file.buffer});
res.redirect('/admin/productDirectory');
} catch (err) {
res.send("Could not update product: " + err.toString());
console.error(err);
} finally {
if (stmt) stmt.unprepare();
if (pool) pool.close();
}
});
router.get('/addProduct', async (req, res) => {
let pool;
try {
pool = await sql.connect(dbConfig);
const categories = (await pool.request().query(`
select * from category
`)).recordset;
res.render('layouts/main', {
title: 'Add a New Product',
loggedIn: true,
user: getUser(req),
spacer: true,
content: `
<h1>Add a New Product</h1>
<form method="post" action="/admin/addProduct" enctype="multipart/form-data">
<label for="productName">Name: </label>
<input type="text" name="productName" required><br>
<label for="productPrice">Price: </label>
<input type="number" name="productPrice" required><br>
<label for="categoryId">Category: </label>
<select name="categoryId">
${categories.map(cat =>`
<option value="${cat.categoryId}">${cat.categoryName}</option>
`).join("\n")}
</select><br>
<label for="productImage">Image: </label>
<input type="file" name="productImage"><br>
<label for="productDesc">Description: </label><br>
<textarea name="productDesc"></textarea><br>
<input type="submit" value="Submit">
</form>
`,
});
} catch (err) {
res.send("Could not retrieve categories: " + err.toString());
console.error(err);
} finally {
if (pool) pool.close();
}
});
router.post('/addProduct', uploadDB.single('productImage'), async (req, res) => {
let pool;
let stmt;
try {
pool = await sql.connect(dbConfig);
stmt = new sql.PreparedStatement(pool);
stmt.input('productName', sql.VarChar);
stmt.input('productPrice', sql.Decimal(10, 2));
stmt.input('productDesc', sql.VarChar);
stmt.input('categoryId', sql.Int);
stmt.input('image', sql.VarBinary);
await stmt.prepare(`
insert into product(productName, productPrice, productDesc, productImage, categoryId)
output inserted.productId
values (@productName, @productPrice, @productDesc, @image, @categoryId)
`);
const recordToInsert = {
...req.body,
image: req.body.storageLocation == "file" ? `img/${req.file.filename}` : req.file.buffer,
};
const stmtResults = await stmt.execute(recordToInsert);
res.redirect(`/product/?id=${stmtResults.recordset[0].productId}`);
} catch (err) {
console.error(err);
res.send("Could not add product: " + err.toString());
} finally {
if (stmt) stmt.unprepare();
if (pool) pool.close();
}
});
module.exports = router;

23
routes/checkout.js Normal file
View File

@ -0,0 +1,23 @@
const express = require('express');
const router = express.Router();
const { getUser } = require('../util.js');
router.get('/', function(req, res, next) {
res.render('layouts/main', {
title: 'Grocery Checkout Line',
loggedIn: true,
user: getUser(req),
spacer: true,
content: `
<h1> Verify your credentials to continue. </h1>
<form method="get" action="/order">
<input type="text" name="customerId" size="50" placeholder="Customer ID"><br>
<input type="password" name="password" size="50" placeholder="Password"><br>
<input type="submit" value="Submit"><input type="reset" value="Reset">
</form>
`,
});
});
module.exports = router;

240
routes/customer.js Normal file
View File

@ -0,0 +1,240 @@
const express = require('express');
const router = express.Router();
const sql = require('mssql');
const { getUser, getCustomerRecord } = require('../util.js');
router.get('/', async function(req, res, next) {
res.setHeader('Content-Type', 'text/html');
let pool;
try {
pool = await sql.connect(dbConfig);
const profile = await getCustomerRecord(pool, getUser(req).id);
res.render('layouts/main', {
loggedIn: getUser(req) != null,
user: getUser(req),
spacer: true,
content: `
<h1>${profile.firstName} ${profile.lastName}'s Profile</h1>
<table>
<tbody>
<tr>
<th>ID</th>
<td>${profile.customerId}</td>
</tr>
<tr>
<th>Username</th>
<td>${profile.userid}</td>
</tr>
<tr>
<th>First Name</th>
<td>${profile.firstName}</td>
</tr>
<tr>
<th>Last Name</th>
<td>${profile.lastName}</td>
</tr>
<tr>
<th>email</th>
<td>${profile.email}</td>
</tr>
<tr>
<th>Phone Number</th>
<td>${profile.phonenum}</td>
</tr>
<tr>
<th>Address</th>
<td>${profile.address}</td>
</tr>
<tr>
<th>City</th>
<td>${profile.city}</td>
</tr>
<tr>
<th>State</th>
<td>${profile.state}</td>
</tr>
<tr>
<th>Postal Code</th>
<td>${profile.postalCode}</td>
</tr>
<tr>
<th>Country</th>
<td>${profile.country}</td>
</tr>
</tbody>
</table>
<ul>
<li>
<a href="/listorder">
Show all orders
</a>
</li>
<li>
<a href="/customer/update">
Edit account details
</a>
</li>
<li>
<a href="/logout">
Log out
</a>
</li>
</ul>
`,
});
} catch(err) {
console.dir(err);
res.write(err + "")
res.end();
} finally {
if (pool) pool.close();
}
});
router.get('/update', async (req, res) => {
let pool;
try {
pool = await sql.connect(dbConfig);
const profile = await getCustomerRecord(pool, getUser(req).id);
res.render('layouts/main', {
title: 'Update your Profile',
loggedIn: getUser(req) != null,
user: getUser(req),
spacer: true,
content: `
<h1>${profile.firstName} ${profile.lastName}'s Profile</h1>
<form action='/customer/update' method='post'>
<table>
<tbody>
<tr>
<th>ID</th>
<td>${profile.customerId}</td>
</tr>
<tr>
<th>Username</th>
<td>
<input type='text' name='userid' required minlength="2" maxlength="50" value='${profile.userid}'/>
</td>
</tr>
<tr>
<th>First Name</th>
<td>
<input type='text' name='firstName' required minlength="2" maxlength="50" value='${profile.firstName}'/>
</td>
</tr>
<tr>
<th>Last Name</th>
<td>
<input type='text' name='lastName' required minlength="2" maxlength="50" value='${profile.lastName}'/>
</td>
</tr>
<tr>
<th>email</th>
<td>
<input type='email' name='email' required value='${profile.email}'/>
</td>
</tr>
<tr>
<th>Phone Number</th>
<td>
<input type='tel' name='phonenum' required maxlength="15" value='${profile.phonenum}'/>
</td>
</tr>
<tr>
<th>Address</th>
<td>
<input type='text' name='address' required value='${profile.address}'/>
</td>
</tr>
<tr>
<th>City</th>
<td>
<input type='text' name='city' required value='${profile.city}'/>
</td>
</tr>
<tr>
<th>State</th>
<td>
<input type='text' name='state' required value='${profile.state}'/>
</td>
</tr>
<tr>
<th>Postal Code</th>
<td>
<input type='text' name='postalCode' required value='${profile.postalCode}'/>
</td>
</tr>
<tr>
<th>Country</th>
<td>
<input type='text' name='country' required value='${profile.country}'/>
</td>
</tr>
</tbody>
</table>
<input type="submit" value="Update"/>
<button onclick="e.preventDefault(); history.back();">
Cancel
</button>
</form>
`,
});
} finally {
if (pool) pool.close();
}
});
router.post('/update', async (req, res) => {
let pool;
let stmt;
try {
pool = await sql.connect(dbConfig);
stmt = new sql.PreparedStatement(pool);
stmt.input('userid', sql.VarChar);
stmt.input('firstName', sql.VarChar);
stmt.input('lastName', sql.VarChar);
stmt.input('email', sql.VarChar);
stmt.input('phonenum', sql.VarChar);
stmt.input('address', sql.VarChar);
stmt.input('city', sql.VarChar);
stmt.input('state', sql.VarChar);
stmt.input('postalCode', sql.VarChar);
stmt.input('country', sql.VarChar);
stmt.input('customerId', sql.Int);
await stmt.prepare(`
update customer set
userid = @userid,
firstName = @firstName,
lastName = @lastName,
email = @email,
phonenum = @address,
city = @city,
state = @state,
postalCode = @postalCode,
country = @country
where customerId = @customerId
`);
await stmt.execute({...req.body, customerId: getUser(req).id});
res.redirect('/customer');
} catch (err) {
console.error(err);
res.write("Failed to update user");
} finally {
if (stmt) stmt.unprepare();
if (pool) pool.close();
}
});
module.exports = router;

43
routes/displayImage.js Normal file
View File

@ -0,0 +1,43 @@
const express = require('express');
const router = express.Router();
const sql = require('mssql');
router.get('/', async function(req, res, next) {
res.setHeader('Content-Type', 'image/jpeg');
let id = req.query.id;
let idVal = parseInt(id);
if (isNaN(idVal)) {
res.end();
return;
}
try {
let pool = await sql.connect(dbConfig);
let sqlQuery = "select productName, productImage from product where productId = @id";
result = await pool.request()
.input('id', sql.Int, idVal)
.query(sqlQuery);
if (result.recordset.length === 0) {
console.log("No image record");
res.end();
return;
} else {
let productImage = result.recordset[0].productImage;
res.write(productImage);
}
res.end()
} catch(err) {
console.dir(err);
res.write(err + "")
res.end();
}
});
module.exports = router;

29
routes/index.js Normal file
View File

@ -0,0 +1,29 @@
const express = require('express');
const router = express.Router();
const { getUser } = require('../util.js');
// Rendering the main page
router.get('/', function (req, res) {
console.log(getUser(req));
res.render('layouts/main', {
title: "Nat & Sami's Rare Earth Metals",
loggedIn: getUser(req) != null,
user: getUser(req),
content: `
<form action='/listprod' class="product-search-container">
<div class="product-search">
<h1 align="center">Nat &amp; Samira's Rare Earth Metals</h1>
<input type='text' name='productName' id='productNameInput' placeholder="Find what you're looking for..."/><br>
<input type='submit' value='Search'/>
<button type='button' onclick='document.getElementById("productNameInput").value = "";'>
Reset
</button>
</div>
</form>
`,
});
})
module.exports = router;

110
routes/listorder.js Normal file
View File

@ -0,0 +1,110 @@
const express = require('express');
const router = express.Router();
const sql = require('mssql');
const moment = require('moment');
const { getUser } = require('../util.js');
router.get('/', async (req, res, next) => {
let pool;
let stmt;
try {
pool = await sql.connect(dbConfig);
let orderResults;
if (getUser(req).isAdmin) {
orderResults = await pool.request().query(`
select o.orderId, o.orderDate, o.customerId, c.firstName, c.lastName, o.totalAmount
from ordersummary as o
left join customer as c on o.customerId = c.customerId
`);
} else {
stmt = new sql.PreparedStatement(pool);
stmt.input('customerId', sql.Int);
await stmt.prepare(`
select o.orderId, o.orderDate, o.customerId, c.firstName, c.lastName, o.totalAmount
from ordersummary as o
left join customer as c on o.customerId = c.customerId
where o.customerId = @customerId
`);
orderResults = await stmt.execute({customerId: getUser(req).id});
}
const productResults = await pool.request().query(`
select orderId, productId, quantity, price from orderproduct
`);
res.render('layouts/main', {
loggedIn: getUser(req) != null,
user: getUser(req),
spacer: true,
content: `
<!DOCTYPE html>
<html lang='en'>
<head>
<title>Nat & Samira's Grocery Order List</title>
</head>
<body>
<h1>Order List</h1>
<table>
<tbody>
<tr>
<th>Customer ID</th>
<th>Order Date</th>
<th>Customer ID</th>
<th>Customer Name</th>
<th>Total Amount</th>
</tr>
${orderResults.recordset.map(row => {
const products = productResults.recordset.filter(r => r.orderId == row.orderId);
return `
<tr>
<td>${row.orderId}</td>
<td>${row.orderDate.toDateString()}</td>
<td>${row.customerId}</td>
<td>${row.firstName} ${row.lastName}</td>
<td>$${row.totalAmount.toFixed(2)}</td>
</tr>
<tr>
<td colspan='5'>
<table>
<tbody>
<tr>
<th>Prouct Id</th>
<th>Quantity</th>
<th>Price</th>
</tr>
${products.map(productRow => `
<tr>
<td>${productRow.productId}</td>
<td>${productRow.quantity}</td>
<td>$${productRow.price.toFixed(2)}</td>
</tr>
`).join('\n')}
</tbody>
</table>
</td>
</tr>
`;
}).join('\n')}
</tbody>
</table>
</body>
</html>
`
});
pool.close();
} catch (err) {
res.write(err.toString());
res.end();
} finally {
if (stmt) stmt.unprepare();
if (pool) pool.close();
}
});
module.exports = router;

88
routes/listprod.js Normal file
View File

@ -0,0 +1,88 @@
const express = require('express');
const router = express.Router();
const sql = require('mssql');
const { getUser } = require('../util.js');
router.get('/', async function(req, res, next) {
// Get the product name to search for
let name = req.query.productName;
let content;
let pool;
let stmt;
try {
pool = await sql.connect(dbConfig);
stmt = new sql.PreparedStatement(pool);
stmt.input('name', sql.VarChar)
await stmt.prepare(`select productId, productName, productPrice
from product
where productName like concat('%', @name, '%')
`);
const productResults = await stmt.execute({name:name});
content = `
<h2>All products ${name ? `matching ${name}` : ''}</h2>
<table>
<tbody>
<tr>
<th>Product Name</th>
<th>Price</th>
</tr>
${productResults.recordset.map(row => `
<tr>
<td>
<a href="/product?id=${row.productId}">
${row.productName}
</a>
</td>
<td>$${row.productPrice.toFixed(2)}</td>
<td>
<a href='/addcart?${(new URLSearchParams({id: row.productId, name: row.productName, price: row.productPrice})).toString()}'>
Add to Cart
</a>
</td>
</tr>
`).join('\n')}
</tbody>
</table>
${ productResults.length === 0 ?
`<p>
Nothing to show!
</p>`
: ''
}
`;
} catch (err) {
content = err.toString();
console.error(err);
} finally {
if (stmt) stmt.unprepare();
if (pool) pool.close();
}
res.render('layouts/main', {
loggedIn: getUser(req) != null,
user: getUser(req),
spacer: true,
content: `
<h1>Find what you're looking for</h1>
<form action='/listprod'>
<div class="product-search">
<input type='text' name='productName' id='productNameInput' placeholder="Search..."/><br>
<input type='submit' value='Search'/>
<button type='button' onclick='document.getElementById("productNameInput").value = "";'>
Reset
</button>
</div>
</form>
${content}
`,
});
});
module.exports = router;

48
routes/loaddata.js Normal file
View File

@ -0,0 +1,48 @@
const express = require('express');
const router = express.Router();
const sql = require('mssql');
const fs = require('fs');
const dbConfigWithoutDB = {
server: 'cosc304_sqlserver',
authentication: {
type: 'default',
options: {
userName: 'sa',
password: '304#sa#pw'
}
},
options: {
encrypt: false,
enableArithAbort:false,
}
}
router.get('/', function(req, res, next) {
(async function() {
try {
let pool = await sql.connect(dbConfigWithoutDB);
res.setHeader('Content-Type', 'text/html');
res.write('<title>Data Loader</title>');
res.write('<h1>Connecting to database.</h1><p>');
let data = fs.readFileSync("./ddl/SQLServer_orderdb.ddl", { encoding: 'utf8' });
let commands = data.split(";");
for (let i = 0; i < commands.length; i++) {
let command = commands[i];
let result = await pool.request()
.query(command);
res.write('<p>' + JSON.stringify(result) + '</p>')
}
res.write('"<h2>Database loading complete!</h2>')
res.end()
} catch(err) {
console.dir(err);
res.send(err)
}
})();
});
module.exports = router;

57
routes/login.js Normal file
View File

@ -0,0 +1,57 @@
const express = require('express');
const router = express.Router();
router.get('/', function(req, res, next) {
if (req.session.user) {
res.redirect('/');
}
res.setHeader('Content-Type', 'text/html');
res.render('layouts/main', {
title: "Login Screen",
loggedIn: false,
content: `
<div style="margin:0 auto;text-align:center;display:inline">
<h3>Please Login to System</h3>
<br>
<form name="MyForm" method=post action="/validateLogin">
<table style="display:inline">
<tr>
<td>
<div align="right">
<font face="Arial, Helvetica, sans-serif" size="2">Username:</font>
</div>
</td>
<td><input type="text" name="username" size=10 maxlength=10></td>
</tr>
<tr>
<td>
<div align="right">
<font face="Arial, Helvetica, sans-serif" size="2">Password:</font>
</div>
</td>
<td><input type="password" name="password" size=10 maxlength="10"></td>
</tr>
</table>
<br />
<p>
Don't have an account?
<a href="/register">
Register here.
</a>
</p>
<input class="submit" type="submit" name="Submit2" value="Log In">
</form>
</div>
`,
});
});
module.exports = router;

10
routes/logout.js Normal file
View File

@ -0,0 +1,10 @@
const express = require('express');
const router = express.Router();
router.get('/', function(req, res, next) {
req.session.user = null;
res.redirect("/login");
});
module.exports = router;

196
routes/order.js Normal file
View File

@ -0,0 +1,196 @@
const express = require('express');
const router = express.Router();
const sql = require('mssql');
const moment = require('moment');
const { getUser } = require('../util.js');
router.get('/', async function(req, res, next) {
res.setHeader('Content-Type', 'text/html');
/** Make connection and validate **/
let pool = false;
let customerRequest = false;
let orderStatement = false;
let orderProductStatement = false;
let content;
loadedResources: try {
let productList = false;
if (req.session.productList && req.session.productList.length > 0) {
productList = req.session.productList.filter(p => p != null);
}
/**
Determine if valid customer id was entered
Determine if there are products in the shopping cart
If either are not true, display an error message
**/
if (!productList) {
content = `
<p>
You do not currently have any items in your cart.
</p>
`;
break loadedResources;
}
if (!/^-?\d+$/.test(req.query.customerId)) {
content = `
<p>
The given ID does not match any known customer.
</p>
`;
break loadedResources;
}
pool = await sql.connect(dbConfig);
customerRequest = new sql.PreparedStatement(pool);
customerRequest.input('id', sql.Int);
await customerRequest.prepare(`
select customerId, password from customer where customerId = @id
`);
const customerResults = await customerRequest.execute({id: req.query.customerId});
if (customerResults.recordset.length == 0) {
content = `
<p>
The given ID does not match any known customer.
</p>
`;
break loadedResources;
}
const customer = customerResults.recordset[0];
if (customer.password != req.query.password) {
content = `
<p>
Id or password did not match!
</p>
`;
break loadedResources;
}
/** Save order information to database**/
/**
// Use retrieval of auto-generated keys.
sqlQuery = "INSERT INTO <TABLE> OUTPUT INSERTED.orderId VALUES( ... )";
let result = await pool.request()
.input(...)
.query(sqlQuery);
// Catch errors generated by the query
let orderId = result.recordset[0].orderId;
**/
console.log(productList)
const totalAmount = productList.reduce((acc, next) => acc + next.price * next.quantity, 0);
orderStatement = new sql.PreparedStatement(pool);
orderStatement.input('amount', sql.Decimal(10, 2));
orderStatement.input('address', sql.VarChar);
orderStatement.input('city', sql.VarChar);
orderStatement.input('state', sql.VarChar);
orderStatement.input('country', sql.VarChar);
orderStatement.input('postalCode', sql.VarChar);
orderStatement.input('customerId', sql.Int);
await orderStatement.prepare(`
insert into ordersummary(orderDate, totalAmount, shipToAddress, shipToCity, shipToState, shipToPostalCode, shipToCountry, customerId)
output inserted.orderId
values (getdate(), @amount, @address, @city, @state, @postalCode, @country, @customerId)
`);
const orderResults = await orderStatement.execute({
amount: totalAmount,
address: customer.address,
city: customer.city,
state: customer.state,
country: customer.country,
postalCode: customer.postalCode,
customerId: customer.customerId,
});
const orderId = orderResults.recordset[0].orderId;
/** Insert each item into OrderedProduct table using OrderId from previous INSERT **/
/** Update total amount for order record **/
/** For each entry in the productList is an array with key values: id, name, quantity, price **/
orderProductStatement = new sql.PreparedStatement(pool);
orderProductStatement.input('orderId', sql.Int);
orderProductStatement.input('id', sql.Int);
orderProductStatement.input('quantity', sql.Int);
orderProductStatement.input('price', sql.Decimal(10, 2));
await orderProductStatement.prepare(`
insert into orderproduct(orderId, productId, quantity, price) values (@orderId, @id, @quantity, @price)
`);
await Promise.all(productList.map(product =>
orderProductStatement.execute({
...product,
orderId
})
));
content = `
<table>
<tbody>
<tr>
<th>Product Name</th>
<th>Price</th>
<th>Quantity</th>
</tr>
${productList.map(row => `
<tr>
<td>${row.name}</td>
<td>$${Number(row.price).toFixed(2)}</td>
<td>${row.quantity}</td>
</tr>
`).join('\n')}
</tbody>
</table>
<p>
<strong>Order Total:</strong> $${Number(totalAmount).toFixed(2)}
</p>
<p>
<strong>Tracking Number:</strong> ${orderId}
</p>
`;
} catch (err) {
console.error(err);
content = `
<p>
${err.toString()}
</p>
`;
} finally {
if (customerRequest) customerRequest.unprepare();
if (orderStatement) orderStatement.unprepare();
if (orderProductStatement) orderProductStatement.unprepare();
if (pool) pool.close();
}
/** Print out order summary **/
/** Clear session/cart **/
req.session.productList = [];
res.render('layouts/main', {
title: 'Order Complete',
loggedIn: true,
user: getUser(req),
spacer: true,
content: `
<h1>Your Order</h1>
${content}
`,
});
});
module.exports = router;

86
routes/product.js Normal file
View File

@ -0,0 +1,86 @@
const express = require('express');
const router = express.Router();
const sql = require('mssql');
const { getUser } = require('../util.js');
router.get('/', async function(req, res, next) {
// Get product name to search for
const productId = req.query.id;
let pool;
let stmt;
try {
pool = await sql.connect(dbConfig);
stmt = new sql.PreparedStatement(pool);
stmt.input('productId', sql.Int);
// TO/DO: Retrieve and display info for the product
await stmt.prepare(`
select * from product
join category on product.categoryId = category.categoryId
where productId = @productId
`);
const product = (await stmt.execute({productId})).recordset[0];
console.log(product);
if (!product) {
throw new Error('Product not found!');
}
// TO/DO: If there is a productImageURL, display using IMG tag
// TO/DO: Retrieve any image stored directly in database. Note: Call displayImage.jsp with product id as parameter.
let imageUrl;
if (product.productImageURL) {
imageUrl = product.productImageURL;
} else {
imageUrl = `/displayImage?id=${productId}`;
}
// TO/DO: Add links to Add to Cart and Continue Shopping
res.render('layouts/main', {
title: product.productName,
loggedIn: true,
user: getUser(req),
spacer: true,
content: `
<h1>${product.productName}</h1>
<img src='${imageUrl}' alt='${product.productName}'>
<p>
<strong>Category:</strong> ${product.categoryName}
</p>
<p>
${product.productDesc}
</p>
<p>
<strong>Price:</strong> $${Number(product.productPrice).toFixed(2)}
</p>
<ul>
<li>
<a href="/addcart?${(new URLSearchParams({id: product.productId, name: product.productName, price: product.productPrice})).toString()}">
Add to Cart
</a>
</li>
<li>
<a href="/listprod">Continue Shopping</a>
</li>
</ul>
`,
});
} catch(err) {
console.dir(err);
res.write(err + "")
res.end();
} finally {
if (stmt) stmt.unprepare();
if (pool) pool.close();
}
});
module.exports = router;

164
routes/register.js Normal file
View File

@ -0,0 +1,164 @@
const express = require('express');
const router = express.Router();
const sql = require('mssql');
const { getUser } = require('../util.js');
router.get('/', async (req, res) => {
if (getUser(req) != null) {
res.redirect('/');
}
res.render('layouts/main', {
title: 'Register',
content: `
<div style="margin:0 auto;text-align:center;display:inline">
<h1>Register</h1>
<br>
<form method=post action="/register">
<table style="display:inline">
<tr>
<td>
<div align="right">
<label for="firstName">First name:</label>
</div>
</td>
<td><input type="text" name="firstName" required size=10></td>
</tr>
<tr>
<td>
<div align="right">
<label for="lastName">Last name:</label>
</div>
</td>
<td><input type="text" name="lastName" required size=10></td>
</tr>
<tr>
<td>
<div align="right">
<label for="email">Email:</label>
</div>
</td>
<td><input type="email" name="email" required size=10></td>
</tr>
<tr>
<td>
<div align="right">
<label for="phonenum">Phone Number:</label>
</div>
</td>
<td><input type="tel" name="phonenum" required size=10></td>
</tr>
<tr>
<td>
<div align="right">
<label for="address">Address:</label>
</div>
</td>
<td><input type="text" name="address" required size=10></td>
</tr>
<tr>
<td>
<div align="right">
<label for="city">City:</label>
</div>
</td>
<td><input type="text" name="city" required size=10></td>
</tr>
<tr>
<td>
<div align="right">
<label for="state">State:</label>
</div>
</td>
<td><input type="text" name="state" required size=10></td>
</tr>
<tr>
<td>
<div align="right">
<label for="country">Country:</label>
</div>
</td>
<td><input type="text" name="country" required size=10></td>
</tr>
<tr>
<td>
<div align="right">
<label for="postalCode">postalCode:</label>
</div>
</td>
<td><input type="text" name="postalCode" required size=10></td>
</tr>
<tr>
<td>
<div align="right">
<label for="userid">Username:</font>
</div>
</td>
<td><input type="text" name="userid" required size=10 maxlength=10></td>
</tr>
<tr>
<td>
<div align="right">
<label for="password">Password:</label>
</div>
</td>
<td><input type="password" name="password" required size=10 maxlength="10"></td>
</tr>
</table>
<br />
<p>
Already have an account?
<a href="/login">
Login
</a>
</p>
<input class="submit" type="submit" name="Submit" value="Register">
</form>
</div>
`,
});
});
router.post('/', async (req, res) => {
let pool;
let stmt;
try {
pool = await sql.connect(dbConfig);
stmt = new sql.PreparedStatement(pool);
stmt.input('userid', sql.VarChar);
stmt.input('firstName', sql.VarChar);
stmt.input('lastName', sql.VarChar);
stmt.input('email', sql.VarChar);
stmt.input('phonenum', sql.VarChar);
stmt.input('address', sql.VarChar);
stmt.input('city', sql.VarChar);
stmt.input('state', sql.VarChar);
stmt.input('postalCode', sql.VarChar);
stmt.input('country', sql.VarChar);
stmt.input('password', sql.VarChar);
await stmt.prepare(`
insert into customer(userid, firstName, lastName, email, phonenum, address, city, state, postalCode, country, password)
values (@userid, @firstName, @lastName, @email, @phonenum, @address, @city, @state, @postalCode, @country, @password)
`);
await stmt.execute({...req.body});
res.redirect('/login');
} catch (err) {
console.error(err);
res.send("Failed to register user: " + err.toString());
} finally {
if (stmt) stmt.unprepare();
if (pool) pool.close();
}
});
module.exports = router;

168
routes/ship.js Normal file
View File

@ -0,0 +1,168 @@
const express = require('express');
const router = express.Router();
const sql = require('mssql');
const moment = require('moment');
const { htmlPage } = require('../util.js');
router.get('/', async function(req, res, next) {
res.setHeader('Content-Type', 'text/html');
// TO/DO: Get order id
const orderId = req.query.orderId;
let pool;
let validateIdStatement;
let fetchItemsStatement;
let createShipmentStatement;
let checkInventoryStatement;
let updateInventoryStatement;
let orderSummaryHTML;
let tx;
let failed = false;
try {
pool = await sql.connect(dbConfig);
// TODO: Check if valid order id
validateIdStatement = new sql.PreparedStatement(pool);
validateIdStatement.input('orderId', sql.Int)
await validateIdStatement.prepare(`select * from ordersummary where orderId = @orderId`);
const results = await validateIdStatement.execute({orderId});
if (results.recordset.length === 0) {
throw new Error("The order you're looking for doesn't exist!");
}
const order = results.recordset[0];
// TO/DO: Start a transaction
tx = new sql.Transaction(pool);
await tx.begin();
// TO/DO: Retrieve all items in order with given id
let items;
fetchItemsStatement = new sql.PreparedStatement(tx);
fetchItemsStatement.input('orderId', sql.Int);
await fetchItemsStatement.prepare(`
select * from orderproduct where orderId = @orderId
`);
items = (await fetchItemsStatement.execute({orderId: order.orderId})).recordset;
await fetchItemsStatement.unprepare();
fetchItemsStatement = false;
orderSummaryHTML = `
<ul>
${items.map(item =>`
<li>
${item.quantity} units of product ${item.productId}
</li>
`).join('\n')}
<ul>
`;
// TO/DO: Create a new shipment record.
createShipmentStatement = new sql.PreparedStatement(tx);
createShipmentStatement.input('shipmentDesc', sql.VarChar);
createShipmentStatement.input('warehouseId', sql.Int);
await createShipmentStatement.prepare(`
insert into shipment(shipmentDate, shipmentDesc, warehouseId)
output inserted.shipmentId
values (getdate(), @shipmentDesc, @warehouseId)
`);
const shipmentResults = await createShipmentStatement.execute({
shipmentDate: Date.now(),
shipmentDesc: order.orderId + "",
warehouseId: 1,
});
await createShipmentStatement.unprepare();
createShipmentStatement = false;
for (const item of items) {
// TO/DO: For each item verify sufficient quantity available in warehouse 1.
checkInventoryStatement = new sql.PreparedStatement(tx);
checkInventoryStatement.input('productId', sql.Int);
await checkInventoryStatement.prepare(`
select * from productinventory
where productId = @productId and warehouseId = 1
`);
const inventoryResults = await checkInventoryStatement.execute({productId: item.productId});
const inventory = inventoryResults.recordset[0];
await checkInventoryStatement.unprepare();
checkInventoryStatement = false;
// TO/DO: If any item does not have sufficient inventory, cancel transaction and rollback. Otherwise, update inventory for each item.
if (inventory.quantity < item.quantity) {
let err = new Error('Insufficient inventory');
err.inventory = true;
err.productId = item.productId;
err.orderId = item.orderId;
throw err;
} else {
updateInventoryStatement = new sql.PreparedStatement(tx);
updateInventoryStatement.input('productId', sql.Int);
updateInventoryStatement.input('amount', sql.Int);
await updateInventoryStatement.prepare(`
update productinventory set quantity = (quantity - @amount) where productId = @productId
`);
await updateInventoryStatement.execute({productId: item.productId, amount: item.quantity});
await updateInventoryStatement.unprepare();
updateInventoryStatement = false;
}
}
tx.commit();
res.send(htmlPage(`
<h1>Order ${order.orderId} shipped!</h1>
<p><strong>Tracking number:</strong> ${shipmentResults.recordset[0].shipmentId}</p>
<h2>Order Summary:</h2>
${orderSummaryHTML}
<p>
<a href="/">Return to main page</a>
</p>
`, `Shipment`));
} catch(err) {
console.dir(err);
failed = true;
if (err.inventory) {
res.send(htmlPage(`
<h1>Order ${err.orderId} was not shipped</h1>
<p>
Shipment unsuccessful. Insufficient inventory for product ID: ${err.productId}
</p>
<h2>Order Summary:</h2>
${orderSummaryHTML || "Nothing to show"}
<p>
<a href="/">Return to main page</a>
</p>
`, `Shipment`));
} else {
res.write(err + "")
res.end();
}
} finally {
if (validateIdStatement) await validateIdStatement.unprepare();
if (fetchItemsStatement) await fetchItemsStatement.unprepare();
if (createShipmentStatement) await createShipmentStatement.unprepare();
if (checkInventoryStatement) await checkInventoryStatement.unprepare();
if (updateInventoryStatement) await updateInventoryStatement.unprepare();
if (failed) tx.rollback();
if (pool) pool.close();
}
});
module.exports = router;

78
routes/showcart.js Normal file
View File

@ -0,0 +1,78 @@
const express = require('express');
const router = express.Router();
const { getUser } = require('../util.js');
router.get('/', function(req, res, next) {
if (req.session.productList && req.session.productList.length != 0 && req.query.remove) {
req.session.productList = req.session.productList.filter(p => p.id != req.query.remove);
}
if (req.session.productList && req.session.productList.length != 0 && req.query.id && req.query.inc) {
req.session.productList = req.session.productList.map(p => {
if (p.id == req.query.id) {
const newQuantity = p.quantity + Number(req.query.inc);
return newQuantity === 0 ? null : {...p, quantity: p.quantity + Number(req.query.inc)};
}
return p;
}).filter(p => p !== null);
}
let content;
if (req.session.productList && req.session.productList.length != 0) {
req.session.productList = req.session.productList.filter(p => p);
const productList = req.session.productList;
content = `
<table>
<tr>
<th>Product ID</th>
<th>Product Name</th>
<th>Price</th>
<th>Quantity</th>
</tr>
${productList.map(product => `
<tr>
<td>${product.id}</td>
<td>${product.name}</td>
<td>$${Number(product.price).toFixed(2)}</td>
<td>
<a href='/showcart?${(new URLSearchParams({id: product.id, inc: -1})).toString()}'>-</a>&nbsp;
${product.quantity}&nbsp;
<a href='/showcart?${(new URLSearchParams({id: product.id, inc: 1})).toString()}'>+</a>
</td>
<td align='right'><a href='/showcart?remove=${product.id}'>Delete</a></td>
</tr>
`).join('\n')}
</table>
<p>
<a href='/checkout'>Checkout</a>
</p>
`;
} else{
content = `
<p>
Your shopping cart is empty!
</p>
`;
}
res.render('layouts/main', {
title: 'Your Shopping Cart',
loggedIn: true,
user: getUser(req),
spacer: true,
content: `
<h1>Your Shopping Cart</h1>
${content}
<p>
<a href='/listprod'>Continue Shopping</a>
</p>`,
});
});
module.exports = router;

69
routes/validateLogin.js Normal file
View File

@ -0,0 +1,69 @@
const express = require('express');
const router = express.Router();
const auth = require('../auth.js');
const sql = require('mssql');
router.post('/', async function(req, res) {
// Have to preserve async context since we make an async call
// to the database in the validateLogin function.
let authenticatedUser = await validateLogin(req);
console.log(authenticatedUser);
if (authenticatedUser === false) {
res.redirect("/login");
} else {
req.session.user = authenticatedUser;
res.redirect("/");
}
});
async function validateLogin(req) {
if (!req.body || !req.body.username || !req.body.password) {
return false;
}
let username = req.body.username;
let password = req.body.password;
let authenticatedUser = await (async function() {
let pool;
let stmt;
try {
pool = await sql.connect(dbConfig);
// TO/DO: Check if userId and password match some customer account.
// If so, set authenticatedUser to be the username.
stmt = new sql.PreparedStatement(pool);
stmt.input('userid', sql.VarChar)
await stmt.prepare(`select password, userid, customerId from customer where userid = @userid`);
const queryResults = await stmt.execute({userid: req.body.username});
if (queryResults.recordset.length === 0) {
return false;
}
const foundUser = queryResults.recordset[0];
if (foundUser.password != req.body.password) {
return false;
}
return {
username: foundUser.userid,
isAdmin: foundUser.customerId === 1,
id: foundUser.customerId,
};
} catch(err) {
console.dir(err);
return false;
} finally {
if (stmt) stmt.unprepare();
if (pool) pool.close();
}
})();
return authenticatedUser;
}
module.exports = router;

114
server.js Normal file
View File

@ -0,0 +1,114 @@
const express = require('express');
const exphbs = require('express-handlebars');
const session = require('express-session')
const bodyParser = require('body-parser');
const { getUser } = require('./util.js');
let index = require('./routes/index.js');
let loadData = require('./routes/loaddata.js');
let listOrder = require('./routes/listorder.js');
let listProd = require('./routes/listprod.js');
let addCart = require('./routes/addcart.js');
let showCart = require('./routes/showcart.js');
let checkout = require('./routes/checkout.js');
let order = require('./routes/order.js');
let login = require('./routes/login.js');
let register = require('./routes/register.js');
let validateLogin = require('./routes/validateLogin.js');
let logout = require('./routes/logout.js');
let admin = require('./routes/admin.js');
let product = require('./routes/product.js');
let displayImage = require('./routes/displayImage.js');
let customer = require('./routes/customer.js');
let ship = require('./routes/ship.js');
const app = express();
// Enable parsing of requests for POST requests
app.use(express.json());
app.use(bodyParser.urlencoded({extended: true}));
// This DB Config is accessible globally
dbConfig = {
server: 'cosc304_sqlserver',
database: 'orders',
authentication: {
type: 'default',
options: {
userName: 'sa',
password: '304#sa#pw'
}
},
options: {
encrypt: false,
enableArithAbort:false,
database: 'orders'
}
}
const checkUser = (req, res, next) => {
if (getUser(req) == null) {
// DEBUG
//req.session.user = { username: "arnold", isAdmin: true, id: 1 };
res.redirect('/login');
}
next();
};
const checkAdmin = (req, res, next) => {
if (!getUser(req).isAdmin) {
res.send("404: Page not found");
}
next();
};
// Setting up the session.
// This uses MemoryStorage which is not
// recommended for production use.
app.use(session({
secret: 'COSC 304 Rules!',
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: false,
secure: false,
maxAge: 60000,
}
}))
// Setting up the rendering engine
app.engine('handlebars', exphbs());
app.set('view engine', 'handlebars');
// Setting up where static assets should
// be served from.
app.use(express.static('public'));
// Setting up Express.js routes.
// These present a "route" on the URL of the site.
// Eg: http://127.0.0.1/loaddata
app.use('/login', login);
app.use('/register', register);
app.use('/loaddata', loadData);
app.use('/validateLogin', validateLogin);
app.use('/displayImage', displayImage);
app.use('/ship', ship);
app.use(checkUser);
app.use('/', index);
app.use('/admin', checkAdmin, admin);
app.use('/listorder', listOrder);
app.use('/listprod', listProd);
app.use('/addcart', addCart);
app.use('/showcart', showCart);
app.use('/checkout', checkout);
app.use('/order', order);
app.use('/logout', logout);
app.use('/product', product);
app.use('/customer', customer);
// Starting our Express app
app.listen(3000)

3
sources.txt Normal file
View File

@ -0,0 +1,3 @@
https://stackoverflow.com/questions/36067767/how-do-i-upload-a-file-with-the-js-fetch-api#40826943
https://stackoverflow.com/questions/15839169/how-to-get-the-value-of-a-selected-radio-button#15839451
https://www.chartjs.org/docs/latest/

36
util.js Normal file
View File

@ -0,0 +1,36 @@
const sql = require('mssql');
module.exports.htmlPage = (body, title='Nat & Samira\'s Grocery Services') => `
<!DOCTYPE html>
<html lang='en'>
<head>
<title>${title}</title>
</head>
<body>
${body}
</body>
</html>
`;
module.exports.getUser = (req) => {
return req.session.user;
};
module.exports.getCustomerRecord = async (pool, id) => {
let stmt;
try {
stmt = new sql.PreparedStatement(pool);
stmt.input('id', sql.Int);
await stmt.prepare(`
select * from customer where customerId = @id
`);
const results = await stmt.execute({id: id});
return results.recordset[0];
} catch (err) {
console.error(err);
return null;
} finally {
if (stmt) stmt.unprepare();
}
};

View File

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{title}}</title>
<link rel="stylesheet" type="text/css" href="/css/styles.css">
</head>
<body>
{{#if loggedIn}}
<nav>
<ul>
<li>
Nat &amp; Samira's Rare Earth Metals
</li>
<li>
<a href="/">Home</a>
</li>
<li>
<a href="/listprod">Products</a>
</li>
<li>
<a href="/listorder">Orders</a>
</li>
{{#if user.isAdmin}}
<li class="nav-right">
<a href="/admin">Admin Panel</a>
</li>
{{/if}}
<li class="nav-right">
<a href="/customer">{{{user.username}}}</a>
</li>
</ul>
</nav>
{{/if}}
{{#if spacer}}
<div style="margin-top: 10vh">&nbsp;</div>
{{/if}}
<main>
{{{content}}}
</main>
</body>
</html>

29
views/login.handlebars Normal file
View File

@ -0,0 +1,29 @@
<div style="margin:0 auto;text-align:center;display:inline">
<h3>Please Login to System</h3>
<br>
<form name="MyForm" method=post action="/validateLogin">
<table style="display:inline">
<tr>
<td>
<div align="right">
<font face="Arial, Helvetica, sans-serif" size="2">Username:</font>
</div>
</td>
<td><input type="text" name="username" size=10 maxlength=10></td>
</tr>
<tr>
<td>
<div align="right">
<font face="Arial, Helvetica, sans-serif" size="2">Password:</font>
</div>
</td>
<td><input type="password" name="password" size=10 maxlength="10"></td>
</tr>
</table>
<br />
<input class="submit" type="submit" name="Submit2" value="Log In">
</form>
</div>