added the link backfilling functionality

minor improvements/corrections
This commit is contained in:
Kaushik Narayan R 2024-08-01 23:15:38 +05:30
parent 149965a15b
commit b20b15a29a
10 changed files with 285 additions and 71 deletions

View File

@ -1,6 +1,6 @@
const accountsAPIURL = 'https://accounts.spotify.com'; const accountsAPIURL = 'https://accounts.spotify.com';
const baseAPIURL = 'https://api.spotify.com/v1'; const baseAPIURL = 'https://api.spotify.com/v1';
const sessionName = 'spotify-manager';
const stateKey = 'spotify_auth_state'; const stateKey = 'spotify_auth_state';
const scopes = { const scopes = {
@ -19,6 +19,7 @@ const scopes = {
module.exports = { module.exports = {
accountsAPIURL, accountsAPIURL,
baseAPIURL, baseAPIURL,
sessionName,
stateKey, stateKey,
scopes scopes
} }

View File

@ -86,7 +86,7 @@ const callback = async (req, res) => {
/** @type {typedefs.User} */ /** @type {typedefs.User} */
req.session.user = { req.session.user = {
username: userResponse.data.display_name, username: userResponse.data.display_name,
uri: userResponse.data.uri, id: userResponse.data.id,
}; };
return res.sendStatus(200); return res.sendStatus(200);
@ -116,7 +116,6 @@ const refresh = async (req, res) => {
if (response.status === 200) { if (response.status === 200) {
req.session.accessToken = response.data.access_token; req.session.accessToken = response.data.access_token;
req.session.refreshToken = response.data.refresh_token ?? req.session.refreshToken; // refresh token rotation req.session.refreshToken = response.data.refresh_token ?? req.session.refreshToken; // refresh token rotation
req.session.cookie.maxAge = 7 * 24 * 60 * 60 * 1000 // 1 week
logger.info(`Access token refreshed${(response.data.refresh_token !== null) ? ' and refresh token updated' : ''}.`); logger.info(`Access token refreshed${(response.data.refresh_token !== null) ? ' and refresh token updated' : ''}.`);
return res.sendStatus(200); return res.sendStatus(200);

View File

@ -3,8 +3,7 @@ const logger = require("../utils/logger")(module);
const { axiosInstance } = require("../utils/axios"); const { axiosInstance } = require("../utils/axios");
const myGraph = require("../utils/graph"); const myGraph = require("../utils/graph");
const { parseSpotifyUri, parseSpotifyLink } = require("../utils/spotifyUriTransformer"); const { parseSpotifyLink } = require("../utils/spotifyURITransformer");
const { Op } = require("sequelize"); const { Op } = require("sequelize");
/** @type {typedefs.Model} */ /** @type {typedefs.Model} */
@ -20,28 +19,28 @@ const Links = require("../models").links;
const updateUser = async (req, res) => { const updateUser = async (req, res) => {
try { try {
let currentPlaylists = []; let currentPlaylists = [];
const userURI = parseSpotifyUri(req.session.user.uri); const uID = req.session.user.id;
// get first 50 // get first 50
const response = await axiosInstance.get( const response = await axiosInstance.get(
`/users/${userURI.id}/playlists`, `/users/${uID}/playlists`,
{ {
params: { params: {
offset: 0, offset: 0,
limit: 50, limit: 50,
}, },
headers: { headers: req.sessHeaders
...req.authHeader
}
} }
); );
if (response.status >= 400 && response.status < 500) if (response.status >= 400 && response.status < 500)
return res.status(response.status).send(response.data); return res.status(response.status).send(response.data);
else if (response.status >= 500)
return res.sendStatus(response.status);
currentPlaylists = response.data.items.map(playlist => { currentPlaylists = response.data.items.map(playlist => {
return { return {
playlistID: parseSpotifyUri(playlist.uri).id, playlistID: playlist.id,
playlistName: playlist.name playlistName: playlist.name
} }
}); });
@ -51,19 +50,17 @@ const updateUser = async (req, res) => {
while (nextURL) { while (nextURL) {
const nextResponse = await axiosInstance.get( const nextResponse = await axiosInstance.get(
nextURL, // absolute URL from previous response which has params nextURL, // absolute URL from previous response which has params
{ { headers: req.sessHeaders }
headers: {
...req.authHeader
}
}
); );
if (response.status >= 400 && response.status < 500) if (response.status >= 400 && response.status < 500)
return res.status(response.status).send(response.data); return res.status(response.status).send(response.data);
else if (response.status >= 500)
return res.sendStatus(response.status);
currentPlaylists.push( currentPlaylists.push(
...nextResponse.data.items.map(playlist => { ...nextResponse.data.items.map(playlist => {
return { return {
playlistID: parseSpotifyUri(playlist.uri).id, playlistID: playlist.id,
playlistName: playlist.name playlistName: playlist.name
} }
}) })
@ -76,7 +73,7 @@ const updateUser = async (req, res) => {
attributes: ["playlistID"], attributes: ["playlistID"],
raw: true, raw: true,
where: { where: {
userID: userURI.id userID: uID
}, },
}); });
@ -86,6 +83,7 @@ const updateUser = async (req, res) => {
const currentSet = new Set(currentPlaylists.map(pl => pl.playlistID)); const currentSet = new Set(currentPlaylists.map(pl => pl.playlistID));
const oldSet = new Set(oldPlaylists.map(pl => pl.playlistID)); const oldSet = new Set(oldPlaylists.map(pl => pl.playlistID));
// TODO: update playlist name
toAdd = currentPlaylists.filter(current => !oldSet.has(current.playlistID)); toAdd = currentPlaylists.filter(current => !oldSet.has(current.playlistID));
toRemove = oldPlaylists.filter(old => !currentSet.has(old.playlistID)); toRemove = oldPlaylists.filter(old => !currentSet.has(old.playlistID));
} else { } else {
@ -101,7 +99,7 @@ const updateUser = async (req, res) => {
removedLinks = await Links.destroy({ removedLinks = await Links.destroy({
where: { where: {
[Op.and]: [ [Op.and]: [
{ userID: userURI.id }, { userID: uID },
{ {
[Op.or]: [ [Op.or]: [
{ from: { [Op.in]: toRemoveIDs } }, { from: { [Op.in]: toRemoveIDs } },
@ -124,7 +122,7 @@ const updateUser = async (req, res) => {
if (toAdd.length) { if (toAdd.length) {
const updatedUser = await Playlists.bulkCreate( const updatedUser = await Playlists.bulkCreate(
toAdd.map(pl => { return { ...pl, userID: userURI.id } }), toAdd.map(pl => { return { ...pl, userID: uID } }),
{ validate: true } { validate: true }
); );
if (updatedUser.length !== toAdd.length) { if (updatedUser.length !== toAdd.length) {
@ -147,13 +145,13 @@ const updateUser = async (req, res) => {
*/ */
const fetchUser = async (req, res) => { const fetchUser = async (req, res) => {
try { try {
const userURI = parseSpotifyUri(req.session.user.uri); const uID = req.session.user.id;
const currentPlaylists = await Playlists.findAll({ const currentPlaylists = await Playlists.findAll({
attributes: ["playlistID", "playlistName"], attributes: ["playlistID", "playlistName"],
raw: true, raw: true,
where: { where: {
userID: userURI.id userID: uID
}, },
}); });
@ -161,7 +159,7 @@ const fetchUser = async (req, res) => {
attributes: ["from", "to"], attributes: ["from", "to"],
raw: true, raw: true,
where: { where: {
userID: userURI.id userID: uID
}, },
}); });
@ -182,14 +180,14 @@ const fetchUser = async (req, res) => {
*/ */
const createLink = async (req, res) => { const createLink = async (req, res) => {
try { try {
const userURI = parseSpotifyUri(req.session.user.uri); const uID = req.session.user.id;
let fromPl, toPl; let fromPl, toPl;
try { try {
fromPl = parseSpotifyLink(req.body["from"]); fromPl = parseSpotifyLink(req.body["from"]);
toPl = parseSpotifyLink(req.body["to"]); toPl = parseSpotifyLink(req.body["to"]);
if (fromPl.type !== "playlist" || toPl.type !== "playlist") { if (fromPl.type !== "playlist" || toPl.type !== "playlist") {
return res.status(400).send({ message: "Invalid Spotify playlist link" }); return res.status(400).send({ message: "Link is not a playlist" });
} }
} catch (error) { } catch (error) {
logger.error("parseSpotifyLink", { error }); logger.error("parseSpotifyLink", { error });
@ -199,7 +197,7 @@ const createLink = async (req, res) => {
let playlists = await Playlists.findAll({ let playlists = await Playlists.findAll({
attributes: ["playlistID"], attributes: ["playlistID"],
raw: true, raw: true,
where: { userID: userURI.id } where: { userID: uID }
}); });
playlists = playlists.map(pl => pl.playlistID); playlists = playlists.map(pl => pl.playlistID);
@ -213,7 +211,7 @@ const createLink = async (req, res) => {
const existingLink = await Links.findOne({ const existingLink = await Links.findOne({
where: { where: {
[Op.and]: [ [Op.and]: [
{ userID: userURI.id }, { userID: uID },
{ from: fromPl.id }, { from: fromPl.id },
{ to: toPl.id } { to: toPl.id }
] ]
@ -227,7 +225,7 @@ const createLink = async (req, res) => {
const allLinks = await Links.findAll({ const allLinks = await Links.findAll({
attributes: ["from", "to"], attributes: ["from", "to"],
raw: true, raw: true,
where: { userID: userURI.id } where: { userID: uID }
}); });
const newGraph = new myGraph(playlists, [...allLinks, { from: fromPl.id, to: toPl.id }]); const newGraph = new myGraph(playlists, [...allLinks, { from: fromPl.id, to: toPl.id }]);
@ -238,7 +236,7 @@ const createLink = async (req, res) => {
} }
const newLink = await Links.create({ const newLink = await Links.create({
userID: userURI.id, userID: uID,
from: fromPl.id, from: fromPl.id,
to: toPl.id to: toPl.id
}); });
@ -262,14 +260,14 @@ const createLink = async (req, res) => {
*/ */
const removeLink = async (req, res) => { const removeLink = async (req, res) => {
try { try {
const userURI = parseSpotifyUri(req.session.user.uri); const uID = req.session.user.id;
let fromPl, toPl; let fromPl, toPl;
try { try {
fromPl = parseSpotifyLink(req.body["from"]); fromPl = parseSpotifyLink(req.body["from"]);
toPl = parseSpotifyLink(req.body["to"]); toPl = parseSpotifyLink(req.body["to"]);
if (fromPl.type !== "playlist" || toPl.type !== "playlist") { if (fromPl.type !== "playlist" || toPl.type !== "playlist") {
return res.status(400).send({ message: "Invalid Spotify playlist link" }); return res.status(400).send({ message: "Link is not a playlist" });
} }
} catch (error) { } catch (error) {
logger.error("parseSpotifyLink", { error }); logger.error("parseSpotifyLink", { error });
@ -280,7 +278,7 @@ const removeLink = async (req, res) => {
const existingLink = await Links.findOne({ const existingLink = await Links.findOne({
where: { where: {
[Op.and]: [ [Op.and]: [
{ userID: userURI.id }, { userID: uID },
{ from: fromPl.id }, { from: fromPl.id },
{ to: toPl.id } { to: toPl.id }
] ]
@ -294,7 +292,7 @@ const removeLink = async (req, res) => {
const removedLink = await Links.destroy({ const removedLink = await Links.destroy({
where: { where: {
[Op.and]: [ [Op.and]: [
{ userID: userURI.id }, { userID: uID },
{ from: fromPl.id }, { from: fromPl.id },
{ to: toPl.id } { to: toPl.id }
] ]
@ -312,9 +310,220 @@ const removeLink = async (req, res) => {
} }
} }
/**
* Add tracks to the link-head playlist,
* that are present in the link-tail playlist but not in the link-head playlist,
* in the order that they are present in the link-tail playlist.
*
* eg.
*
* pl_a has tracks: a, b, c
*
* pl_b has tracks: e, b, d
*
* link from pl_a to pl_b exists
*
* after populateMissingInLink, pl_a will have tracks: a, b, c, e, d
*
* @param {typedefs.Req} req
* @param {typedefs.Res} res
*/
const populateMissingInLink = async (req, res) => {
try {
const uID = req.session.user.id;
let fromPl, toPl;
try {
fromPl = parseSpotifyLink(req.body["from"]);
toPl = parseSpotifyLink(req.body["to"]);
if (fromPl.type !== "playlist" || toPl.type !== "playlist") {
return res.status(400).send({ message: "Link is not a playlist" });
}
} catch (error) {
logger.error("parseSpotifyLink", { error });
return res.status(400).send({ message: "Invalid Spotify playlist link" });
}
// check if exists
const existingLink = await Links.findOne({
where: {
[Op.and]: [
{ userID: uID },
{ from: fromPl.id },
{ to: toPl.id }
]
}
});
if (!existingLink) {
logger.error("link does not exist");
return res.sendStatus(409);
}
let checkFields = ["collaborative", "owner(id)"];
const checkFromData = await axiosInstance.get(
`/playlists/${fromPl.id}/`,
{
params: {
fields: checkFields.join()
},
headers: req.sessHeaders
}
);
if (checkFromData.status >= 400 && checkFromData.status < 500)
return res.status(checkFromData.status).send(checkFromData.data);
else if (checkFromData.status >= 500)
return res.sendStatus(checkFromData.status);
// editable = collaborative || user is owner
if (checkFromData.data.collaborative !== true &&
checkFromData.data.owner.id !== uID) {
logger.error("user cannot edit target playlist");
return res.status(403).send({
message: "You cannot edit this playlist, you must be owner/ playlist must be collaborative"
});
}
let initialFields = ["tracks(next,items(is_local,track(uri)))"];
let mainFields = ["next", "items(is_local,track(uri))"];
const fromData = await axiosInstance.get(
`/playlists/${fromPl.id}/`,
{
params: {
fields: initialFields.join()
},
headers: req.sessHeaders
}
);
if (fromData.status >= 400 && fromData.status < 500)
return res.status(fromData.status).send(fromData.data);
else if (fromData.status >= 500)
return res.sendStatus(fromData.status);
let fromPlaylist = {};
// varying fields again smh
if (fromData.data.tracks.next) {
fromPlaylist.next = new URL(fromData.data.tracks.next);
fromPlaylist.next.searchParams.set("fields", mainFields.join());
fromPlaylist.next = fromPlaylist.next.href;
}
fromPlaylist.tracks = fromData.data.tracks.items.map((playlist_item) => {
return {
is_local: playlist_item.is_local,
uri: playlist_item.track.uri
}
});
// keep getting batches of 50 till exhausted
while (fromPlaylist.next) {
const nextResponse = await axiosInstance.get(
fromPlaylist.next, // absolute URL from previous response which has params
{ headers: req.sessHeaders }
);
if (nextResponse.status >= 400 && nextResponse.status < 500)
return res.status(nextResponse.status).send(nextResponse.data);
else if (nextResponse.status >= 500)
return res.sendStatus(nextResponse.status);
fromPlaylist.tracks.push(
...nextResponse.data.items.map((playlist_item) => {
return {
is_local: playlist_item.is_local,
uri: playlist_item.track.uri
}
})
);
fromPlaylist.next = nextResponse.data.next;
}
delete fromPlaylist.next;
const toData = await axiosInstance.get(
`/playlists/${toPl.id}/`,
{
params: {
fields: initialFields.join()
},
headers: req.sessHeaders
}
);
if (toData.status >= 400 && toData.status < 500)
return res.status(toData.status).send(toData.data);
else if (toData.status >= 500)
return res.sendStatus(toData.status);
let toPlaylist = {};
// varying fields again smh
if (toData.data.tracks.next) {
toPlaylist.next = new URL(toData.data.tracks.next);
toPlaylist.next.searchParams.set("fields", mainFields.join());
toPlaylist.next = toPlaylist.next.href;
}
toPlaylist.tracks = toData.data.tracks.items.map((playlist_item) => {
return {
is_local: playlist_item.is_local,
uri: playlist_item.track.uri
}
});
// keep getting batches of 50 till exhausted
while (toPlaylist.next) {
const nextResponse = await axiosInstance.get(
toPlaylist.next, // absolute URL from previous response which has params
{ headers: req.sessHeaders }
);
if (nextResponse.status >= 400 && nextResponse.status < 500)
return res.status(nextResponse.status).send(nextResponse.data);
else if (nextResponse.status >= 500)
return res.sendStatus(nextResponse.status);
toPlaylist.tracks.push(
...nextResponse.data.items.map((playlist_item) => {
return {
is_local: playlist_item.is_local,
uri: playlist_item.track.uri
}
})
);
toPlaylist.next = nextResponse.data.next;
}
delete toPlaylist.next;
let fromURIs = fromPlaylist.tracks.map(track => track.uri);
let toURIs = toPlaylist.tracks.
filter(track => !track.is_local). // API doesn't support adding local files to playlists yet
map(track => track.uri).
filter(track => !fromURIs.includes(track)); // only ones missing from the 'from' playlist
// add in batches of 100
while (toURIs.length) {
const nextBatch = toURIs.splice(0, 100);
const addResponse = await axiosInstance.post(
`/playlists/${fromPl.id}/tracks`,
{ uris: nextBatch },
{ headers: req.sessHeaders }
);
if (addResponse.status >= 400 && addResponse.status < 500)
return res.status(addResponse.status).send(addResponse.data);
else if (addResponse.status >= 500)
return res.sendStatus(addResponse.status);
}
return res.sendStatus(200);
} catch (error) {
logger.error('populateMissingInLink', { error });
return res.sendStatus(500);
}
}
module.exports = { module.exports = {
updateUser, updateUser,
fetchUser, fetchUser,
createLink, createLink,
removeLink removeLink,
populateMissingInLink,
}; };

View File

@ -2,7 +2,7 @@ const logger = require("../utils/logger")(module);
const typedefs = require("../typedefs"); const typedefs = require("../typedefs");
const { axiosInstance } = require('../utils/axios'); const { axiosInstance } = require('../utils/axios');
const { parseSpotifyURI, parseSpotifyLink } = require("../utils/spotifyURITransformer"); const { parseSpotifyLink } = require("../utils/spotifyURITransformer");
/** /**
* Retrieve list of all of user's playlists * Retrieve list of all of user's playlists
@ -15,20 +15,20 @@ const getUserPlaylists = async (req, res) => {
// get first 50 // get first 50
const response = await axiosInstance.get( const response = await axiosInstance.get(
`/users/${parseSpotifyURI(req.session.user.uri).id}/playlists`, `/users/${req.session.user.id}/playlists`,
{ {
params: { params: {
offset: 0, offset: 0,
limit: 50, limit: 50,
}, },
headers: { headers: req.sessHeaders
...req.authHeader
}
} }
); );
if (response.status >= 400 && response.status < 500) if (response.status >= 400 && response.status < 500)
return res.status(response.status).send(response.data); return res.status(response.status).send(response.data);
else if (response.status >= 500)
return res.sendStatus(response.status);
userPlaylists.total = response.data.total; userPlaylists.total = response.data.total;
@ -48,14 +48,12 @@ const getUserPlaylists = async (req, res) => {
while (userPlaylists.next) { while (userPlaylists.next) {
const nextResponse = await axiosInstance.get( const nextResponse = await axiosInstance.get(
userPlaylists.next, // absolute URL from previous response which has params userPlaylists.next, // absolute URL from previous response which has params
{ { headers: req.sessHeaders }
headers: {
...req.authHeader
}
}
); );
if (response.status >= 400 && response.status < 500) if (response.status >= 400 && response.status < 500)
return res.status(response.status).send(response.data); return res.status(response.status).send(response.data);
else if (response.status >= 500)
return res.sendStatus(response.status);
userPlaylists.items.push( userPlaylists.items.push(
...nextResponse.data.items.map((playlist) => { ...nextResponse.data.items.map((playlist) => {
@ -98,7 +96,7 @@ const getPlaylistDetails = async (req, res) => {
try { try {
uri = parseSpotifyLink(req.query.playlist_link) uri = parseSpotifyLink(req.query.playlist_link)
if (uri.type !== "playlist") { if (uri.type !== "playlist") {
return res.status(400).send({ message: "Invalid Spotify playlist link" }); return res.status(400).send({ message: "Link is not a playlist" });
} }
} catch (error) { } catch (error) {
logger.error("parseSpotifyLink", { error }); logger.error("parseSpotifyLink", { error });
@ -111,11 +109,13 @@ const getPlaylistDetails = async (req, res) => {
params: { params: {
fields: initialFields.join() fields: initialFields.join()
}, },
headers: { ...req.authHeader } headers: req.sessHeaders
} }
); );
if (response.status >= 400 && response.status < 500) if (response.status >= 400 && response.status < 500)
return res.status(response.status).send(response.data); return res.status(response.status).send(response.data);
else if (response.status >= 500)
return res.sendStatus(response.status);
// TODO: this whole section needs to be DRYer // TODO: this whole section needs to be DRYer
// look into serializr // look into serializr
@ -151,15 +151,13 @@ const getPlaylistDetails = async (req, res) => {
while (playlist.next) { while (playlist.next) {
const nextResponse = await axiosInstance.get( const nextResponse = await axiosInstance.get(
playlist.next, // absolute URL from previous response which has params playlist.next, // absolute URL from previous response which has params
{ { headers: req.sessHeaders }
headers: {
...req.authHeader
}
}
); );
if (nextResponse.status >= 400 && nextResponse.status < 500) if (nextResponse.status >= 400 && nextResponse.status < 500)
return res.status(nextResponse.status).send(nextResponse.data); return res.status(nextResponse.status).send(nextResponse.data);
else if (nextResponse.status >= 500)
return res.sendStatus(nextResponse.status);
playlist.tracks.push( playlist.tracks.push(
...nextResponse.data.items.map((playlist_item) => { ...nextResponse.data.items.map((playlist_item) => {

View File

@ -10,6 +10,8 @@ const helmet = require("helmet");
const SQLiteStore = require("connect-sqlite3")(session); const SQLiteStore = require("connect-sqlite3")(session);
const db = require("./models"); const db = require("./models");
const { sessionName } = require('./constants');
const { isAuthenticated } = require('./middleware/authCheck');
const logger = require("./utils/logger")(module); const logger = require("./utils/logger")(module);
@ -26,6 +28,7 @@ const sqliteStore = new SQLiteStore({
// Configure session middleware // Configure session middleware
app.use(session({ app.use(session({
name: sessionName,
store: sqliteStore, store: sqliteStore,
secret: process.env.SESSION_SECRET, secret: process.env.SESSION_SECRET,
resave: false, resave: false,
@ -51,8 +54,8 @@ app.use(express.static(__dirname + '/static'));
// Routes // Routes
app.use("/api/auth/", require("./routes/auth")); app.use("/api/auth/", require("./routes/auth"));
app.use("/api/playlists", require("./routes/playlists")); app.use("/api/playlists", isAuthenticated, require("./routes/playlists"));
app.use("/api/operations", require("./routes/operations")); app.use("/api/operations", isAuthenticated, require("./routes/operations"));
// Fallbacks // Fallbacks
app.use((_req, res) => { app.use((_req, res) => {

View File

@ -1,3 +1,4 @@
const { sessionName } = require("../constants");
const typedefs = require("../typedefs"); const typedefs = require("../typedefs");
const logger = require("../utils/logger")(module); const logger = require("../utils/logger")(module);
@ -9,7 +10,10 @@ const logger = require("../utils/logger")(module);
*/ */
const isAuthenticated = (req, res, next) => { const isAuthenticated = (req, res, next) => {
if (req.session.accessToken) { if (req.session.accessToken) {
req.authHeader = { 'Authorization': `Bearer ${req.session.accessToken}` }; req.sessHeaders = {
'Authorization': `Bearer ${req.session.accessToken}`,
// 'X-RateLimit-SessID': `${req.sessionID}_${req.session.user.username}`
};
next(); next();
} else { } else {
const delSession = req.session.destroy((err) => { const delSession = req.session.destroy((err) => {
@ -18,7 +22,7 @@ const isAuthenticated = (req, res, next) => {
return res.sendStatus(500); return res.sendStatus(500);
} else { } else {
logger.info("Session invalid, destroyed.", { sessionID: delSession.id }); logger.info("Session invalid, destroyed.", { sessionID: delSession.id });
res.clearCookie("connect.sid"); res.clearCookie(sessionName);
return res.sendStatus(401); return res.sendStatus(401);
} }
}); });

View File

@ -1,25 +1,21 @@
const router = require('express').Router(); const router = require('express').Router();
const { updateUser, fetchUser, createLink, removeLink } = require('../controllers/operations'); const { updateUser, fetchUser, createLink, removeLink, populateMissingInLink } = require('../controllers/operations');
const { isAuthenticated } = require('../middleware/authCheck');
const { validate } = require('../validators'); const { validate } = require('../validators');
const { createLinkValidator, removeLinkValidator } = require('../validators/operations'); const { createLinkValidator, removeLinkValidator, populateMissingInLinkValidator } = require('../validators/operations');
router.put( router.put(
"/update", "/update",
isAuthenticated,
updateUser updateUser
); );
router.get( router.get(
"/fetch", "/fetch",
isAuthenticated,
fetchUser fetchUser
); );
router.post( router.post(
"/link", "/link",
isAuthenticated,
createLinkValidator, createLinkValidator,
validate, validate,
createLink createLink
@ -27,10 +23,16 @@ router.post(
router.delete( router.delete(
"/link", "/link",
isAuthenticated,
removeLinkValidator, removeLinkValidator,
validate, validate,
removeLink removeLink
); );
router.put(
"/populate/link",
populateMissingInLinkValidator,
validate,
populateMissingInLink
);
module.exports = router; module.exports = router;

View File

@ -1,19 +1,16 @@
const router = require('express').Router(); const router = require('express').Router();
const { getUserPlaylists, getPlaylistDetails } = require('../controllers/playlists'); const { getUserPlaylists, getPlaylistDetails } = require('../controllers/playlists');
const { isAuthenticated } = require('../middleware/authCheck');
const { getPlaylistDetailsValidator } = require('../validators/playlists'); const { getPlaylistDetailsValidator } = require('../validators/playlists');
const { validate } = require("../validators"); const { validate } = require("../validators");
router.get( router.get(
"/me", "/me",
isAuthenticated,
getUserPlaylists getUserPlaylists
); );
router.get( router.get(
"/details", "/details",
isAuthenticated,
getPlaylistDetailsValidator, getPlaylistDetailsValidator,
validate, validate,
getPlaylistDetails getPlaylistDetails

View File

@ -20,13 +20,13 @@ const axiosInstance = axios.default.create({
}, },
}); });
axiosInstance.interceptors.request.use(request => { axiosInstance.interceptors.request.use(config => {
logger.http("API call", { logger.http("API call", {
url: request.url, url: config.url,
method: request.method, method: config.method,
params: request.params ?? {}, params: config.params ?? {},
}); });
return request; return config;
}); });
axiosInstance.interceptors.response.use( axiosInstance.interceptors.response.use(

View File

@ -25,5 +25,6 @@ const createLinkValidator = async (req, res, next) => {
module.exports = { module.exports = {
createLinkValidator, createLinkValidator,
removeLinkValidator: createLinkValidator removeLinkValidator: createLinkValidator,
populateMissingInLinkValidator: createLinkValidator
} }