init
|
@ -0,0 +1,9 @@
|
|||
FROM node:14.10.1
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
|
||||
RUN npm install
|
||||
|
||||
CMD ["npm", "run", "start"]
|
|
@ -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
|
|
@ -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;
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
||||
|
|
@ -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');
|
||||
|
|
@ -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;
|
|
@ -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:
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
After Width: | Height: | Size: 56 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 9.3 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 9.2 KiB |
After Width: | Height: | Size: 56 KiB |
After Width: | Height: | Size: 353 KiB |
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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 & 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;
|
||||
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
@ -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>
|
||||
${product.quantity}
|
||||
<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;
|
|
@ -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;
|
||||
|
|
@ -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)
|
|
@ -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/
|
|
@ -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();
|
||||
}
|
||||
};
|
|
@ -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 & 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"> </div>
|
||||
{{/if}}
|
||||
|
||||
<main>
|
||||
{{{content}}}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
|
@ -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>
|