179 lines
6.0 KiB
TypeScript
179 lines
6.0 KiB
TypeScript
import * as queries from "./queries"
|
|
import * as youtube from "./youtube"
|
|
import { log, LogLevel } from "./logging"
|
|
import pg from "pg";
|
|
import db from "./db";
|
|
|
|
export async function getRequests(count: number, offset: number, sort: string) {
|
|
var query = {
|
|
text: "SELECT * FROM requests_vw \
|
|
JOIN states ON requests_vw.state = states.state WHERE active \
|
|
ORDER BY " + sort + " LIMIT $1 OFFSET $2",
|
|
values: [count,offset]
|
|
};
|
|
return db.query(query)
|
|
.then((result: pg.QueryResult) => result.rows);
|
|
};
|
|
|
|
export async function getRequestsVoted(count: number, offset: number, sort: string, user: number) {
|
|
var query = {
|
|
text: "SELECT * FROM get_requests_voted($3) \
|
|
JOIN states ON get_requests_voted.state = states.state WHERE active \
|
|
ORDER BY " + sort + " LIMIT $1 OFFSET $2",
|
|
values: [count,offset,user]
|
|
};
|
|
return db.query(query)
|
|
.then((result: pg.QueryResult) => result.rows);
|
|
};
|
|
|
|
export async function getRequestsTotal() {
|
|
return db.query(queries.getRequestsTotal)
|
|
.then((result: pg.QueryResult) => result.rows[0]["count"]);
|
|
};
|
|
|
|
export async function getAllRequests(count: number, offset: number, sort: string) {
|
|
var query = {
|
|
text: "SELECT * FROM requests_vw \
|
|
ORDER BY " + sort + " LIMIT $1 OFFSET $2",
|
|
values: [count,offset]
|
|
};
|
|
return db.query(query)
|
|
.then((result: pg.QueryResult) => result.rows);
|
|
};
|
|
|
|
export async function getAllRequestsVoted(count: number, offset: number, sort: string, user: number) {
|
|
var query = {
|
|
text: "SELECT * FROM get_requests_voted($3) \
|
|
ORDER BY " + sort + " LIMIT $1 OFFSET $2",
|
|
values: [count,offset,user]
|
|
};
|
|
return db.query(query)
|
|
.then((result: pg.QueryResult) => result.rows);
|
|
};
|
|
|
|
export async function getAllRequestsTotal() {
|
|
return db.query(queries.getAllRequestsTotal)
|
|
.then((result: pg.QueryResult) => result.rows[0]["count"]);
|
|
};
|
|
|
|
const validUrlRegexes = [
|
|
/^https:\/\/www\.youtube\.com\/watch\?v=[a-zA-Z0-9_-]{11}$/
|
|
];
|
|
|
|
async function checkRequestExists(url: string) {
|
|
var query = Object.assign(queries.checkRequestExists, { values: [url] });
|
|
var result = await db.query(query);
|
|
if (result.rowCount > 0) {
|
|
return result;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async function retrieveYoutubeMetadata(url: string) {
|
|
var videoId = url.match(/^https:\/\/www\.youtube\.com\/watch\?v=([a-zA-Z0-9_-]{11})$/)![1];
|
|
try {
|
|
var ytResponse = await youtube.apiRequest('/videos', new URLSearchParams({ id: videoId, part: 'snippet' }));
|
|
if (ytResponse) {
|
|
var title = ytResponse['items'][0]['snippet']['title'];
|
|
db.query(Object.assign(queries.updateRequestMetadata, { values: [url,title] }));
|
|
}
|
|
} catch(e) {
|
|
log(LogLevel.ERROR,"Failed to fetch YouTube metadata");
|
|
log(LogLevel.ERROR,"Video ID: " + videoId);
|
|
log(LogLevel.ERROR,"Error info:");
|
|
log(LogLevel.ERROR,e);
|
|
}
|
|
}
|
|
|
|
export async function addRequest(url: string, requester: string): Promise<[number,string]> {
|
|
// Check that URL is of an accepted format
|
|
var validUrl = false;
|
|
for (var regex of validUrlRegexes) {
|
|
if (regex.test(url)) {
|
|
validUrl = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!validUrl) return [400, "Invalid song URL."];
|
|
|
|
// Check whether the URL has already been requested
|
|
var existsResult = await checkRequestExists(url)
|
|
if (existsResult) {
|
|
console.log(existsResult);
|
|
return [200,`Song already requested by ${existsResult.rows[0].requester}. State: ${existsResult.rows[0].state}`]
|
|
}
|
|
|
|
// Check whether the user has hit their rate limit
|
|
var rateLimitQuery = Object.assign(queries.getRateLimitStatus, { values: [requester] });
|
|
var rateLimitResult = (await db.query(rateLimitQuery)).rows[0];
|
|
if (rateLimitResult.status) {
|
|
return [429,`You have reached your maximum of ${rateLimitResult.max} requests per day.
|
|
Please try again later.\n
|
|
Tip: Removing one of your requests from the past 24 hours by retracting your vote will allow you to replace it with another.`];
|
|
}
|
|
|
|
// Add the request
|
|
var query = Object.assign(queries.addRequest, { values: [url,requester] });
|
|
return db.query(query)
|
|
.then(async () => {
|
|
if (url.includes('youtube')){
|
|
retrieveYoutubeMetadata(url);
|
|
}
|
|
return [201,"Song request added."] as [number,string];
|
|
})
|
|
};
|
|
|
|
export async function updateRequestMetadata(url: string): Promise <[number,string]> {
|
|
if (!checkRequestExists(url)) {
|
|
return [400,"Request does not exist."];
|
|
}
|
|
if (url.includes('youtube')){
|
|
retrieveYoutubeMetadata(url);
|
|
}
|
|
return [200,"Metadata update requested."];
|
|
}
|
|
|
|
export async function updateRequestState(url: string, state: string): Promise<[number,string]> {
|
|
var query = Object.assign(queries.checkValidState, { values: [state] });
|
|
var result = await db.query(query);
|
|
if (result.rowCount == 0) {
|
|
return [400,"Invalid state"]
|
|
}
|
|
|
|
if (!checkRequestExists(url)) {
|
|
return [400,"Request does not exist."];
|
|
}
|
|
|
|
var query = Object.assign(queries.updateRequestState, { values: [url,state] });
|
|
return db.query(query)
|
|
.then(() => [200,"Song request state updated."] as [number,string]);
|
|
};
|
|
|
|
export async function updateRequestScoreModifier(url: string, scoreDiff: number): Promise<[number,string]> {
|
|
var query = Object.assign(queries.updateRequestScoreModifier, { values: [url,scoreDiff] });
|
|
return db.query(query)
|
|
.then(() => [200,"Song request score updated."] as [number,string]);
|
|
};
|
|
|
|
export async function deleteRequest(url: string): Promise<[number,string]> {
|
|
var query = Object.assign(queries.deleteRequest, { values: [url] });
|
|
return db.query(query)
|
|
.then(() => [200,"Song request deleted."] as [number,string]);
|
|
};
|
|
|
|
export async function addVote(url: string, user: string): Promise<[number,string]> {
|
|
var query = Object.assign(queries.checkVoteExists, { 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]);
|
|
};
|