Redesign database and implement voting
parent
a0b65c7d5b
commit
80e9bc1bde
|
@ -1,10 +1,11 @@
|
||||||
CREATE TABLE states (
|
CREATE TABLE states (
|
||||||
state varchar UNIQUE NOT NULL,
|
state varchar UNIQUE NOT NULL,
|
||||||
|
active bool NOT NULL DEFAULT TRUE, -- Whether request can be considered "active"
|
||||||
PRIMARY KEY(state)
|
PRIMARY KEY(state)
|
||||||
);
|
);
|
||||||
|
|
||||||
INSERT INTO states (state) VALUES
|
INSERT INTO states (state,active) VALUES
|
||||||
('Requested'),
|
('Requested',TRUE),
|
||||||
('Rejected'),
|
('Rejected',FALSE),
|
||||||
('Accepted'),
|
('Accepted',TRUE),
|
||||||
('Learned');
|
('Learned',FALSE);
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
CREATE TABLE config (
|
||||||
|
rowlock bool DEFAULT TRUE UNIQUE NOT NULL CHECK (rowlock = TRUE),
|
||||||
|
normaluservotepoints int NOT NULL,
|
||||||
|
followervotepoints int NOT NULL,
|
||||||
|
subscribervotepoints int NOT NULL,
|
||||||
|
PRIMARY KEY (rowLock)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO config (normalUserVotePoints,followerVotePoints,subscriberVotePoints)
|
||||||
|
VALUES (10,50,100);
|
|
@ -1,9 +0,0 @@
|
||||||
CREATE TABLE requests (
|
|
||||||
id int GENERATED ALWAYS AS IDENTITY,
|
|
||||||
url varchar UNIQUE,
|
|
||||||
requester varchar NOT NULL,
|
|
||||||
score int DEFAULT 0,
|
|
||||||
state varchar NOT NULL DEFAULT 'Requested',
|
|
||||||
PRIMARY KEY(id),
|
|
||||||
FOREIGN KEY (state) REFERENCES states(state)
|
|
||||||
);
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
CREATE TABLE users (
|
||||||
|
userId int NOT NULL,
|
||||||
|
displayName varchar NOT NULL,
|
||||||
|
imageUrl varchar,
|
||||||
|
isFollower boolean NOT NULL DEFAULT FALSE,
|
||||||
|
isSubscriber boolean NOT NULL DEFAULT FALSE,
|
||||||
|
PRIMARY KEY (userId)
|
||||||
|
);
|
|
@ -0,0 +1,9 @@
|
||||||
|
CREATE TABLE requests (
|
||||||
|
url varchar NOT NULL UNIQUE,
|
||||||
|
requester int NOT NULL,
|
||||||
|
state varchar NOT NULL DEFAULT 'Requested',
|
||||||
|
reqTimestamp timestamptz NOT NULL DEFAULT NOW(),
|
||||||
|
PRIMARY KEY (url),
|
||||||
|
FOREIGN KEY (requester) REFERENCES users(userid),
|
||||||
|
FOREIGN KEY (state) REFERENCES states(state)
|
||||||
|
);
|
|
@ -0,0 +1,7 @@
|
||||||
|
CREATE TABLE votes (
|
||||||
|
requestUrl varchar,
|
||||||
|
userId int,
|
||||||
|
PRIMARY KEY (requestUrl,userId),
|
||||||
|
FOREIGN KEY (requestUrl) REFERENCES requests(url),
|
||||||
|
FOREIGN KEY (userId) REFERENCES users(userId)
|
||||||
|
);
|
|
@ -0,0 +1,7 @@
|
||||||
|
CREATE TABLE scores (
|
||||||
|
url varchar,
|
||||||
|
baseScore int NOT NULL DEFAULT 0,
|
||||||
|
score int NOT NULL DEFAULT 0,
|
||||||
|
PRIMARY KEY (url),
|
||||||
|
FOREIGN KEY (url) REFERENCES requests(url)
|
||||||
|
);
|
114
db/90-views.sql
114
db/90-views.sql
|
@ -1,5 +1,111 @@
|
||||||
CREATE VIEW requests_vw AS
|
CREATE OR REPLACE FUNCTION get_requests()
|
||||||
SELECT * FROM requests WHERE state = 'Requested' ORDER BY score DESC, id ASC;
|
RETURNS TABLE (
|
||||||
|
url varchar,
|
||||||
|
requester varchar,
|
||||||
|
state varchar,
|
||||||
|
score int,
|
||||||
|
reqTimestamp timestamptz
|
||||||
|
)
|
||||||
|
LANGUAGE SQL
|
||||||
|
AS $$
|
||||||
|
SELECT url,displayName AS requester,state,score,reqTimestamp FROM requests
|
||||||
|
JOIN scores USING (url)
|
||||||
|
JOIN users ON requests.requester = users.userid
|
||||||
|
WHERE state IN (SELECT state FROM states WHERE active)
|
||||||
|
ORDER BY score DESC, reqTimestamp ASC;
|
||||||
|
$$;
|
||||||
|
|
||||||
CREATE VIEW requests_all_vw AS
|
CREATE OR REPLACE FUNCTION get_requests_all()
|
||||||
SELECT * FROM requests ORDER BY score DESC, id ASC;
|
RETURNS TABLE (
|
||||||
|
url varchar,
|
||||||
|
requester varchar,
|
||||||
|
state varchar,
|
||||||
|
score int,
|
||||||
|
reqTimestamp timestamptz
|
||||||
|
)
|
||||||
|
LANGUAGE SQL
|
||||||
|
AS $$
|
||||||
|
SELECT url,displayName AS requester,state,score,reqTimestamp FROM requests
|
||||||
|
JOIN scores USING (url)
|
||||||
|
JOIN users ON requests.requester = users.userid
|
||||||
|
ORDER BY score DESC, reqTimestamp ASC;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION get_requests_voted(votinguserid int)
|
||||||
|
RETURNS TABLE (
|
||||||
|
url varchar,
|
||||||
|
requester varchar,
|
||||||
|
state varchar,
|
||||||
|
score int,
|
||||||
|
reqTimestamp timestamptz,
|
||||||
|
voted bool
|
||||||
|
)
|
||||||
|
LANGUAGE SQL
|
||||||
|
AS $$
|
||||||
|
SELECT url,displayName AS requester,state,score,reqTimestamp,
|
||||||
|
(CASE WHEN votes.userid IS NULL THEN FALSE ELSE TRUE END) AS voted
|
||||||
|
FROM requests
|
||||||
|
JOIN scores USING (url)
|
||||||
|
JOIN users ON requests.requester = users.userid
|
||||||
|
LEFT JOIN votes ON (requests.url = votes.requesturl AND votes.userid = votinguserid)
|
||||||
|
WHERE state IN (SELECT state FROM states WHERE active)
|
||||||
|
ORDER BY score DESC, reqTimestamp ASC;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION get_requests_all_voted(votinguserid int)
|
||||||
|
RETURNS TABLE (
|
||||||
|
url varchar,
|
||||||
|
requester varchar,
|
||||||
|
state varchar,
|
||||||
|
score int,
|
||||||
|
reqTimestamp timestamptz,
|
||||||
|
voted bool
|
||||||
|
)
|
||||||
|
LANGUAGE SQL
|
||||||
|
AS $$
|
||||||
|
SELECT url,displayName AS requester,state,score,reqTimestamp,
|
||||||
|
(CASE WHEN votes.userid IS NULL THEN FALSE ELSE TRUE END) AS voted
|
||||||
|
FROM requests
|
||||||
|
JOIN scores USING (url)
|
||||||
|
JOIN users ON requests.requester = users.userid
|
||||||
|
LEFT JOIN votes ON (requests.url = votes.requesturl AND votes.userid = votinguserid)
|
||||||
|
ORDER BY score DESC, reqTimestamp ASC;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Views to determine the score added by votes from different classes of users.
|
||||||
|
In order for songs with no votes to show up in this view (and thus have their
|
||||||
|
scores properly calculated), the query must be JOINed to requests, the URL must
|
||||||
|
be selected as requests.url rather than votes.requesturl (which is null in the
|
||||||
|
case of no votes), and users.is{follower,subscriber} must be COALESCEd to FALSE
|
||||||
|
as in the case of no votes, user.* will be NULL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
CREATE OR REPLACE VIEW vote_score_normal_vw AS
|
||||||
|
SELECT requests.url,COUNT(votes.requesturl),(COUNT(votes.requesturl) * (SELECT normaluservotepoints FROM config)) AS votescore FROM requests
|
||||||
|
LEFT JOIN votes ON votes.requesturl = requests.url
|
||||||
|
LEFT JOIN users on votes.userid = users.userid
|
||||||
|
WHERE COALESCE(users.isfollower,FALSE) = FALSE AND COALESCE(users.issubscriber,FALSE) = FALSE
|
||||||
|
GROUP BY requests.url;
|
||||||
|
|
||||||
|
CREATE OR REPLACE VIEW vote_score_follower_vw AS
|
||||||
|
SELECT requests.url,COUNT(votes.requesturl),(COUNT(votes.requesturl) * (SELECT followervotepoints FROM config)) AS votescore FROM requests
|
||||||
|
LEFT JOIN votes ON votes.requesturl = requests.url
|
||||||
|
LEFT JOIN users on votes.userid = users.userid
|
||||||
|
WHERE COALESCE(users.isfollower,FALSE) = TRUE AND COALESCE(users.issubscriber,FALSE) = FALSE
|
||||||
|
GROUP BY requests.url;
|
||||||
|
|
||||||
|
CREATE OR REPLACE VIEW vote_score_subscriber_vw AS
|
||||||
|
SELECT requests.url,COUNT(votes.requesturl),(COUNT(votes.requesturl) * (SELECT subscribervotepoints FROM config)) AS votescore FROM requests
|
||||||
|
LEFT JOIN votes ON votes.requesturl = requests.url
|
||||||
|
LEFT JOIN users on votes.userid = users.userid
|
||||||
|
WHERE COALESCE(users.issubscriber,FALSE) = TRUE
|
||||||
|
GROUP BY requests.url;
|
||||||
|
|
||||||
|
CREATE OR REPLACE VIEW vote_score_all_vw AS
|
||||||
|
SELECT url,SUM(count) AS count, SUM(votescore) AS votescore FROM
|
||||||
|
(SELECT * FROM vote_score_normal_vw
|
||||||
|
UNION ALL SELECT * FROM vote_score_follower_vw
|
||||||
|
UNION ALL SELECT * FROM vote_score_subscriber_vw
|
||||||
|
) AS union_vote_score
|
||||||
|
GROUP BY url;
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
CREATE OR REPLACE PROCEDURE update_scores()
|
||||||
|
LANGUAGE SQL
|
||||||
|
AS $$
|
||||||
|
UPDATE scores SET score = basescore + votescore
|
||||||
|
FROM vote_score_all_vw
|
||||||
|
WHERE scores.url = vote_score_all_vw.url;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
CREATE OR REPLACE PROCEDURE add_request(url varchar,requester int)
|
||||||
|
LANGUAGE SQL
|
||||||
|
AS $$
|
||||||
|
INSERT INTO requests (url,requester) VALUES (url,requester);
|
||||||
|
INSERT INTO scores (url) VALUES (url);
|
||||||
|
INSERT INTO votes (requesturl,userid) VALUES (url,requester);
|
||||||
|
CALL update_scores();
|
||||||
|
$$;
|
||||||
|
|
||||||
|
CREATE OR REPLACE PROCEDURE add_vote(url varchar,voteuser int)
|
||||||
|
LANGUAGE SQL
|
||||||
|
AS $$
|
||||||
|
INSERT INTO votes (requesturl,userid) VALUES (url,voteuser);
|
||||||
|
CALL update_scores();
|
||||||
|
$$;
|
||||||
|
|
||||||
|
CREATE OR REPLACE PROCEDURE delete_vote(url varchar,voteuser int)
|
||||||
|
LANGUAGE SQL
|
||||||
|
AS $$
|
||||||
|
DELETE FROM votes WHERE requesturl = url AND userid = voteuser;
|
||||||
|
CALL update_scores();
|
||||||
|
$$;
|
|
@ -1,5 +1,22 @@
|
||||||
INSERT INTO requests (url,requester,score,state) VALUES
|
INSERT INTO users (userId,displayName,isFollower,isSubscriber) VALUES
|
||||||
('https://www.youtube.com/watch?v=dQw4w9WgXcQ','virtualdxs',0,'Rejected'),
|
(001,'user',false,false),
|
||||||
('https://www.youtube.com/watch?v=C5oeWHngDS4','virtualdxs',500,'Requested'),
|
(002,'follower',true,false),
|
||||||
('https://www.youtube.com/watch?v=vXaJGBLRA_o','virtualdxs',0,'Requested');
|
(003,'subscriber',true,true),
|
||||||
|
(004,'subnonfollower',false,true);
|
||||||
|
|
||||||
|
/* INSERT INTO requests (url,requester,state) VALUES
|
||||||
|
('https://www.youtube.com/watch?v=dQw4w9WgXcQ',001,'Rejected'),
|
||||||
|
('https://www.youtube.com/watch?v=C5oeWHngDS4',002,'Requested'),
|
||||||
|
('https://www.youtube.com/watch?v=vXaJGBLRA_o',003,'Requested'); */
|
||||||
|
|
||||||
|
CALL add_request('https://www.youtube.com/watch?v=dQw4w9WgXcQ',001);
|
||||||
|
CALL add_request('https://www.youtube.com/watch?v=C5oeWHngDS4',002);
|
||||||
|
CALL add_request('https://www.youtube.com/watch?v=vXaJGBLRA_o',003);
|
||||||
|
|
||||||
|
UPDATE requests SET state = 'Rejected' WHERE url = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ';
|
||||||
|
|
||||||
|
INSERT INTO votes (requestUrl,userid) VALUES
|
||||||
|
('https://www.youtube.com/watch?v=C5oeWHngDS4',001),
|
||||||
|
('https://www.youtube.com/watch?v=C5oeWHngDS4',003);
|
||||||
|
|
||||||
|
CALL update_scores();
|
||||||
|
|
|
@ -15,13 +15,21 @@ function getRequests(count,allRequests) {
|
||||||
|
|
||||||
function buildTable(requests,allRequests) {
|
function buildTable(requests,allRequests) {
|
||||||
var requestsDivHTML = '<table><tr><th class="request-url">URL</th><th class="request-requester">Requester</th><th class="request-score">Score</th>';
|
var requestsDivHTML = '<table><tr><th class="request-url">URL</th><th class="request-requester">Requester</th><th class="request-score">Score</th>';
|
||||||
if (allRequests) requestsDivHTML += `<th class="request-state">State</td></tr>`;
|
if (allRequests) requestsDivHTML += `<th class="request-state">State</td>`;
|
||||||
|
if (window.loggedIn) requestsDivHTML += `<th class="request-vote">Vote</td>`;
|
||||||
requestsDivHTML += "</tr>";
|
requestsDivHTML += "</tr>";
|
||||||
for (request of requests) {
|
for (request of requests) {
|
||||||
requestsDivHTML += `<tr><td class="request-url"><a href="${request.url}">${request.url}</a></td>\
|
requestsDivHTML += `<tr><td class="request-url"><a href="${request.url}">${request.url}</a></td>\
|
||||||
<td class="request-requester">${request.requester}</td>\
|
<td class="request-requester">${request.requester}</td>\
|
||||||
<td class="request-score">${request.score}</td>`;
|
<td class="request-score">${request.score}</td>`;
|
||||||
if (allRequests) requestsDivHTML += `<td class="request-state">${request.state}</td></tr>`;
|
if (allRequests) requestsDivHTML += `<td class="request-state">${request.state}</td>`;
|
||||||
|
if (window.loggedIn) {
|
||||||
|
if (request.voted) {
|
||||||
|
requestsDivHTML += `<td class="request-vote"><button onclick="deleteVote('${request.url}')">Unvote</button></td>`;
|
||||||
|
} else {
|
||||||
|
requestsDivHTML += `<td class="request-vote"><button onclick="addVote('${request.url}')">Vote</button></td>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
requestsDivHTML += "</tr>";
|
requestsDivHTML += "</tr>";
|
||||||
}
|
}
|
||||||
requestsDivHTML += "</table>";
|
requestsDivHTML += "</table>";
|
||||||
|
@ -84,15 +92,41 @@ function validateAndSubmitRequest() {
|
||||||
}
|
}
|
||||||
fetch("/api/addRequest", { method: 'POST', body: new URLSearchParams({
|
fetch("/api/addRequest", { method: 'POST', body: new URLSearchParams({
|
||||||
url: url
|
url: url
|
||||||
|
})})
|
||||||
|
.then(response => {
|
||||||
|
updateTable();
|
||||||
|
document.getElementById("addRequestUrl").value = "";
|
||||||
|
response.text().then((message) => {
|
||||||
|
closeAddRequestModal();
|
||||||
|
showMessage(message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addVote(url) {
|
||||||
|
fetch("/api/addVote", { method: 'POST', body: new URLSearchParams({
|
||||||
|
url: url
|
||||||
|
})})
|
||||||
|
.then(response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
response.text().then(addRequestErr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateTable();
|
||||||
|
response.text().then(showMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteVote(url) {
|
||||||
|
fetch("/api/deleteVote", { method: 'POST', body: new URLSearchParams({
|
||||||
|
url: url
|
||||||
})})
|
})})
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
response.text().then(addRequestErr);
|
response.text().then(addRequestErr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
closeAddRequestModal();
|
|
||||||
updateTable();
|
updateTable();
|
||||||
document.getElementById("addRequestUrl").value = "";
|
|
||||||
response.text().then(showMessage);
|
response.text().then(showMessage);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,3 +142,7 @@ div#nav-userpic {
|
||||||
.modalClose a {
|
.modalClose a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.request-vote > button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
80
src/app.ts
80
src/app.ts
|
@ -23,15 +23,31 @@ app.use(session({
|
||||||
|
|
||||||
// API
|
// API
|
||||||
app.get("/api/getRequests", async (request, response) => {
|
app.get("/api/getRequests", async (request, response) => {
|
||||||
|
if (!request.session) {
|
||||||
|
throw new Error ("Missing request.session")
|
||||||
|
}
|
||||||
var requestCount = ( request.query.count ? parseInt(request.query.count as string, 10) : 5 );
|
var requestCount = ( request.query.count ? parseInt(request.query.count as string, 10) : 5 );
|
||||||
|
if (request.session.user) {
|
||||||
|
requests.getRequestsVoted(requestCount,request.session.user.id).then((val: Array<any>) => response.send(val))
|
||||||
|
.catch((e: any) => errorHandler(request,response,e));
|
||||||
|
} else {
|
||||||
requests.getRequests(requestCount).then((val: Array<any>) => response.send(val))
|
requests.getRequests(requestCount).then((val: Array<any>) => response.send(val))
|
||||||
.catch((e: any) => errorHandler(request,response,e));
|
.catch((e: any) => errorHandler(request,response,e));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get("/api/getAllRequests", async (request, response) => {
|
app.get("/api/getAllRequests", async (request, response) => {
|
||||||
|
if (!request.session) {
|
||||||
|
throw new Error ("Missing request.session")
|
||||||
|
}
|
||||||
var requestCount = ( request.query.count ? parseInt(request.query.count as string, 10) : 5 );
|
var requestCount = ( request.query.count ? parseInt(request.query.count as string, 10) : 5 );
|
||||||
|
if (request.session.user) {
|
||||||
|
requests.getAllRequestsVoted(requestCount,request.session.user.id).then((val: Array<any>) => response.send(val))
|
||||||
|
.catch((e: any) => errorHandler(request,response,e));
|
||||||
|
} else {
|
||||||
requests.getAllRequests(requestCount).then((val: Array<any>) => response.send(val))
|
requests.getAllRequests(requestCount).then((val: Array<any>) => response.send(val))
|
||||||
.catch((e: any) => errorHandler(request,response,e));
|
.catch((e: any) => errorHandler(request,response,e));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/api/addRequest", async (request, response) => {
|
app.post("/api/addRequest", async (request, response) => {
|
||||||
|
@ -44,10 +60,10 @@ app.post("/api/addRequest", async (request, response) => {
|
||||||
if (!request.body.url) {
|
if (!request.body.url) {
|
||||||
response.status(400);
|
response.status(400);
|
||||||
response.send("Missing url");
|
response.send("Missing url");
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
var url = request.body.url as string;
|
var url = request.body.url as string;
|
||||||
var requester = request.session.user.display_name;
|
var requester = request.session.user.id;
|
||||||
requests.addRequest(url,requester).then((val: [number,string]) => {
|
requests.addRequest(url,requester).then((val: [number,string]) => {
|
||||||
response.status(val[0]);
|
response.status(val[0]);
|
||||||
response.send(val[1]);
|
response.send(val[1]);
|
||||||
|
@ -60,12 +76,12 @@ app.post("/api/updateRequestState", async (request, response) => {
|
||||||
if (!request.body.url) {
|
if (!request.body.url) {
|
||||||
response.status(400);
|
response.status(400);
|
||||||
response.send("Missing url");
|
response.send("Missing url");
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
if (!request.body.state) {
|
if (!request.body.state) {
|
||||||
response.status(400);
|
response.status(400);
|
||||||
response.send("Missing scoreDiff");
|
response.send("Missing scoreDiff");
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
var url = request.body.url as string;
|
var url = request.body.url as string;
|
||||||
var state = request.body.state as string;
|
var state = request.body.state as string;
|
||||||
|
@ -81,12 +97,12 @@ app.post("/api/updateRequestScore", async (request, response) => {
|
||||||
if (!request.body.url) {
|
if (!request.body.url) {
|
||||||
response.status(400);
|
response.status(400);
|
||||||
response.send("Missing url");
|
response.send("Missing url");
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
if (!request.body.scoreDiff) {
|
if (!request.body.scoreDiff) {
|
||||||
response.status(400);
|
response.status(400);
|
||||||
response.send("Missing scoreDiff");
|
response.send("Missing scoreDiff");
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
var url = request.body.url as string;
|
var url = request.body.url as string;
|
||||||
var scoreDiff = parseInt(request.body.scoreDiff as string, 10);
|
var scoreDiff = parseInt(request.body.scoreDiff as string, 10);
|
||||||
|
@ -102,7 +118,7 @@ app.post("/api/deleteRequest", async (request, response) => {
|
||||||
if (!request.body.url) {
|
if (!request.body.url) {
|
||||||
response.status(400);
|
response.status(400);
|
||||||
response.send("Missing url");
|
response.send("Missing url");
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
var url = request.body.url as string;
|
var url = request.body.url as string;
|
||||||
requests.deleteRequest(url).then((val: [number,string]) => {
|
requests.deleteRequest(url).then((val: [number,string]) => {
|
||||||
|
@ -112,6 +128,48 @@ app.post("/api/deleteRequest", async (request, response) => {
|
||||||
.catch((e: any) => errorHandler(request,response,e));
|
.catch((e: any) => errorHandler(request,response,e));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.post("/api/addVote", async (request,response) => {
|
||||||
|
response.type('text/plain');
|
||||||
|
if (!request.session || !request.session.user) {
|
||||||
|
response.status(401);
|
||||||
|
response.send("Must be logged in");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!request.body.url) {
|
||||||
|
response.status(400);
|
||||||
|
response.send("Missing url");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var url = request.body.url as string;
|
||||||
|
var user = request.session.user.id;
|
||||||
|
requests.addVote(url,user).then((val: [number,string]) => {
|
||||||
|
response.status(val[0]);
|
||||||
|
response.send(val[1]);
|
||||||
|
})
|
||||||
|
.catch((e: any) => errorHandler(request,response,e));
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/api/deleteVote", async (request,response) => {
|
||||||
|
response.type('text/plain');
|
||||||
|
if (!request.session || !request.session.user) {
|
||||||
|
response.status(401);
|
||||||
|
response.send("Must be logged in");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!request.body.url) {
|
||||||
|
response.status(400);
|
||||||
|
response.send("Missing url");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var url = request.body.url as string;
|
||||||
|
var user = request.session.user.id;
|
||||||
|
requests.deleteVote(url,user).then((val: [number,string]) => {
|
||||||
|
response.status(val[0]);
|
||||||
|
response.send(val[1]);
|
||||||
|
})
|
||||||
|
.catch((e: any) => errorHandler(request,response,e));
|
||||||
|
});
|
||||||
|
|
||||||
// Twitch callback
|
// Twitch callback
|
||||||
app.get("/callback", async (request, response) => {
|
app.get("/callback", async (request, response) => {
|
||||||
if (request.query.error) {
|
if (request.query.error) {
|
||||||
|
@ -131,6 +189,14 @@ app.get("/callback", async (request, response) => {
|
||||||
if (typeof tokenResponse == 'undefined') throw new Error('tokenResponse is undefined');
|
if (typeof tokenResponse == 'undefined') throw new Error('tokenResponse is undefined');
|
||||||
request.session.tokenpair = { access_token: tokenResponse.access_token, refresh_token: tokenResponse.refresh_token };
|
request.session.tokenpair = { access_token: tokenResponse.access_token, refresh_token: tokenResponse.refresh_token };
|
||||||
request.session.user = (await twitch.apiRequest(request.session.tokenpair,"GET","/users")).data[0];
|
request.session.user = (await twitch.apiRequest(request.session.tokenpair,"GET","/users")).data[0];
|
||||||
|
|
||||||
|
const updateUserQuery = {
|
||||||
|
name: "updateUser",
|
||||||
|
text: "INSERT INTO users (userid,displayName,imageUrl) VALUES ($1,$2,$3)\
|
||||||
|
ON CONFLICT (userid) DO UPDATE SET displayName = $2, imageUrl = $3"
|
||||||
|
}
|
||||||
|
var query = Object.assign(updateUserQuery,{ values: [request.session.user.id,request.session.user.display_name,request.session.user.profile_image_url] });
|
||||||
|
db.query(query);
|
||||||
response.redirect(307, '/');
|
response.redirect(307, '/');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import db from "./db";
|
||||||
// getRequests
|
// getRequests
|
||||||
const getRequestsQuery = {
|
const getRequestsQuery = {
|
||||||
name: "getRequests",
|
name: "getRequests",
|
||||||
text: "SELECT * FROM requests_vw LIMIT $1"
|
text: "SELECT * FROM get_requests() LIMIT $1"
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getRequests(count: number) {
|
export async function getRequests(count: number) {
|
||||||
|
@ -16,7 +16,7 @@ export async function getRequests(count: number) {
|
||||||
// getAllRequests
|
// getAllRequests
|
||||||
const getAllRequestsQuery = {
|
const getAllRequestsQuery = {
|
||||||
name: "getAllRequests",
|
name: "getAllRequests",
|
||||||
text: "SELECT * FROM requests_all_vw LIMIT $1"
|
text: "SELECT * FROM get_requests_all() LIMIT $1"
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAllRequests(count: number) {
|
export async function getAllRequests(count: number) {
|
||||||
|
@ -25,6 +25,30 @@ export async function getAllRequests(count: number) {
|
||||||
.then((result: pg.QueryResult) => result.rows);
|
.then((result: pg.QueryResult) => result.rows);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// getRequestsVoted
|
||||||
|
const getRequestsVotedQuery = {
|
||||||
|
name: "getRequestsVoted",
|
||||||
|
text: "SELECT * FROM get_requests_voted($2) LIMIT $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getRequestsVoted(count: number, user: number) {
|
||||||
|
var query = Object.assign(getRequestsVotedQuery, { values: [count,user] });
|
||||||
|
return db.query(query)
|
||||||
|
.then((result: pg.QueryResult) => result.rows);
|
||||||
|
};
|
||||||
|
|
||||||
|
// getAllRequestsVoted
|
||||||
|
const getAllRequestsVotedQuery = {
|
||||||
|
name: "getAllRequestsVoted",
|
||||||
|
text: "SELECT * FROM get_requests_all_voted($2) LIMIT $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAllRequestsVoted(count: number,user: number) {
|
||||||
|
var query = Object.assign(getAllRequestsVotedQuery, { values: [count,user] });
|
||||||
|
return db.query(query)
|
||||||
|
.then((result: pg.QueryResult) => result.rows);
|
||||||
|
};
|
||||||
|
|
||||||
// addRequest
|
// addRequest
|
||||||
const validUrlRegexes = [
|
const validUrlRegexes = [
|
||||||
/^https:\/\/www\.youtube\.com\/watch\?v=[a-zA-Z0-9_-]{11}$/
|
/^https:\/\/www\.youtube\.com\/watch\?v=[a-zA-Z0-9_-]{11}$/
|
||||||
|
@ -37,7 +61,7 @@ const checkRequestExistsQuery = {
|
||||||
|
|
||||||
const addRequestQuery = {
|
const addRequestQuery = {
|
||||||
name: "addRequest",
|
name: "addRequest",
|
||||||
text: "INSERT INTO requests (url,requester) VALUES ($1,$2)"
|
text: "CALL add_request($1,$2)"
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function addRequest(url: string, requester: string): Promise<[number,string]> {
|
export async function addRequest(url: string, requester: string): Promise<[number,string]> {
|
||||||
|
@ -105,3 +129,23 @@ export async function deleteRequest(url: string): Promise<[number,string]> {
|
||||||
return db.query(query)
|
return db.query(query)
|
||||||
.then(() => [200,"Song request deleted."] as [number,string]);
|
.then(() => [200,"Song request deleted."] as [number,string]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const checkVoteExistsQuery = {
|
||||||
|
name: "checkVoteExists",
|
||||||
|
text: "SELECT * FROM votes WHERE requesturl = $1 AND userid = $2"
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function addVote(url: string, user: string): Promise<[number,string]> {
|
||||||
|
var query = Object.assign(checkVoteExistsQuery, { values: [url,user] });
|
||||||
|
var result = await db.query(query);
|
||||||
|
if (result.rowCount > 0) {
|
||||||
|
return [200,`Song already voted on`]
|
||||||
|
}
|
||||||
|
return db.query("CALL add_vote($1,$2)",[url,user])
|
||||||
|
.then(() => [201,"Successfully cast vote."] as [number,string]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function deleteVote(url: string, user: string): Promise<[number,string]> {
|
||||||
|
return db.query("CALL delete_vote($1,$2)",[url,user])
|
||||||
|
.then(() => [201,"Successfully retracted vote."] as [number,string]);
|
||||||
|
};
|
||||||
|
|
|
@ -3,13 +3,14 @@
|
||||||
<head>
|
<head>
|
||||||
<link rel=stylesheet href=style.css />
|
<link rel=stylesheet href=style.css />
|
||||||
<title>Learn Request Queue</title>
|
<title>Learn Request Queue</title>
|
||||||
|
<script>window.loggedIn = <%= it.loggedIn %></script>
|
||||||
<script src="main.js" defer></script>
|
<script src="main.js" defer></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="topbar">
|
<div id="topbar">
|
||||||
<div id="logo">Learn Request Queue</div>
|
<div id="logo">Learn Request Queue</div>
|
||||||
<div id="nav-requests"><a href="/">Requests</a></div>
|
<div id="nav-requests"><a href="/">Requests</a></div>
|
||||||
<%- if (it.userName) { // Logged in -%>
|
<%- if (it.loggedIn) { -%>
|
||||||
<div id="nav-addrequest"><a href="#" onclick="openAddRequestModal()">Add Request</a></div>
|
<div id="nav-addrequest"><a href="#" onclick="openAddRequestModal()">Add Request</a></div>
|
||||||
<div id="nav-userpic"><img src="<%= it.userProfilePicture %>" /></div>
|
<div id="nav-userpic"><img src="<%= it.userProfilePicture %>" /></div>
|
||||||
<div id="nav-username"><%= it.userName %></div>
|
<div id="nav-username"><%= it.userName %></div>
|
||||||
|
|
Loading…
Reference in New Issue