mirror of
https://github.com/20kaushik02/spotify-manager.git
synced 2025-12-06 08:54:07 +00:00
CJS -> ESM
This commit is contained in:
parent
5a8611afbc
commit
bcc39d5f38
@ -1,6 +1,7 @@
|
|||||||
require("dotenv-flow").config();
|
import dotenvFlow from "dotenv-flow";
|
||||||
|
dotenvFlow.config();
|
||||||
|
|
||||||
const path = require("path");
|
import { resolve } from "path";
|
||||||
module.exports = {
|
export default {
|
||||||
"config": path.resolve("config", "sequelize.js")
|
"config": resolve("config", "sequelize.js")
|
||||||
};
|
};
|
||||||
|
|||||||
18
api/axios.js
18
api/axios.js
@ -1,10 +1,11 @@
|
|||||||
const axios = require("axios");
|
import axios from "axios";
|
||||||
const rateLimit = require("axios-rate-limit");
|
import rateLimit from "axios-rate-limit";
|
||||||
|
|
||||||
const { baseAPIURL, accountsAPIURL } = require("../constants");
|
import { baseAPIURL, accountsAPIURL } from "../constants.js";
|
||||||
const logger = require("../utils/logger")(module);
|
import curriedLogger from "../utils/logger.js";
|
||||||
|
const logger = curriedLogger(import.meta);
|
||||||
|
|
||||||
const authInstance = axios.create({
|
export const authInstance = axios.create({
|
||||||
baseURL: accountsAPIURL,
|
baseURL: accountsAPIURL,
|
||||||
timeout: 20000,
|
timeout: 20000,
|
||||||
headers: {
|
headers: {
|
||||||
@ -21,7 +22,7 @@ const uncappedAxiosInstance = axios.create({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const axiosInstance = rateLimit(uncappedAxiosInstance, {
|
export const axiosInstance = rateLimit(uncappedAxiosInstance, {
|
||||||
maxRequests: 10,
|
maxRequests: 10,
|
||||||
perMilliseconds: 5000,
|
perMilliseconds: 5000,
|
||||||
});
|
});
|
||||||
@ -50,8 +51,3 @@ axiosInstance.interceptors.response.use(
|
|||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
authInstance,
|
|
||||||
axiosInstance
|
|
||||||
};
|
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
|
|
||||||
const logger = require("../utils/logger")(module);
|
import curriedLogger from "../utils/logger.js";
|
||||||
|
const logger = curriedLogger(import.meta);
|
||||||
|
|
||||||
const typedefs = require("../typedefs");
|
import * as typedefs from "../typedefs.js";
|
||||||
|
|
||||||
const { axiosInstance } = require("./axios");
|
import { axiosInstance } from "./axios.js";
|
||||||
|
|
||||||
const logPrefix = "Spotify API: ";
|
const logPrefix = "Spotify API: ";
|
||||||
|
|
||||||
@ -17,7 +18,7 @@ const logPrefix = "Spotify API: ";
|
|||||||
* @param {any} data request body
|
* @param {any} data request body
|
||||||
* @param {boolean} inlineData true if data is to be placed inside config
|
* @param {boolean} inlineData true if data is to be placed inside config
|
||||||
*/
|
*/
|
||||||
const singleRequest = async (req, res, method, path, config = {}, data = null, inlineData = false) => {
|
export const singleRequest = async (req, res, method, path, config = {}, data = null, inlineData = false) => {
|
||||||
let resp;
|
let resp;
|
||||||
config.headers = { ...config.headers, ...req.sessHeaders };
|
config.headers = { ...config.headers, ...req.sessHeaders };
|
||||||
try {
|
try {
|
||||||
@ -62,7 +63,7 @@ const singleRequest = async (req, res, method, path, config = {}, data = null, i
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const getUserProfile = async (req, res) => {
|
export const getUserProfile = async (req, res) => {
|
||||||
const response = await singleRequest(req, res,
|
const response = await singleRequest(req, res,
|
||||||
"GET", "/me",
|
"GET", "/me",
|
||||||
{ headers: { Authorization: `Bearer ${req.session.accessToken}` } }
|
{ headers: { Authorization: `Bearer ${req.session.accessToken}` } }
|
||||||
@ -70,7 +71,7 @@ const getUserProfile = async (req, res) => {
|
|||||||
return res.headersSent ? null : response.data;
|
return res.headersSent ? null : response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getUserPlaylistsFirstPage = async (req, res) => {
|
export const getUserPlaylistsFirstPage = async (req, res) => {
|
||||||
const response = await singleRequest(req, res,
|
const response = await singleRequest(req, res,
|
||||||
"GET",
|
"GET",
|
||||||
`/users/${req.session.user.id}/playlists`,
|
`/users/${req.session.user.id}/playlists`,
|
||||||
@ -83,13 +84,13 @@ const getUserPlaylistsFirstPage = async (req, res) => {
|
|||||||
return res.headersSent ? null : response.data;
|
return res.headersSent ? null : response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getUserPlaylistsNextPage = async (req, res, nextURL) => {
|
export const getUserPlaylistsNextPage = async (req, res, nextURL) => {
|
||||||
const response = await singleRequest(
|
const response = await singleRequest(
|
||||||
req, res, "GET", nextURL);
|
req, res, "GET", nextURL);
|
||||||
return res.headersSent ? null : response.data;
|
return res.headersSent ? null : response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getPlaylistDetailsFirstPage = async (req, res, initialFields, playlistID) => {
|
export const getPlaylistDetailsFirstPage = async (req, res, initialFields, playlistID) => {
|
||||||
const response = await singleRequest(req, res,
|
const response = await singleRequest(req, res,
|
||||||
"GET",
|
"GET",
|
||||||
`/playlists/${playlistID}/`,
|
`/playlists/${playlistID}/`,
|
||||||
@ -101,13 +102,13 @@ const getPlaylistDetailsFirstPage = async (req, res, initialFields, playlistID)
|
|||||||
return res.headersSent ? null : response.data;
|
return res.headersSent ? null : response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getPlaylistDetailsNextPage = async (req, res, nextURL) => {
|
export const getPlaylistDetailsNextPage = async (req, res, nextURL) => {
|
||||||
const response = await singleRequest(
|
const response = await singleRequest(
|
||||||
req, res, "GET", nextURL);
|
req, res, "GET", nextURL);
|
||||||
return res.headersSent ? null : response.data;
|
return res.headersSent ? null : response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
const addItemsToPlaylist = async (req, res, nextBatch, playlistID) => {
|
export const addItemsToPlaylist = async (req, res, nextBatch, playlistID) => {
|
||||||
const response = await singleRequest(req, res,
|
const response = await singleRequest(req, res,
|
||||||
"POST",
|
"POST",
|
||||||
`/playlists/${playlistID}/tracks`,
|
`/playlists/${playlistID}/tracks`,
|
||||||
@ -117,7 +118,7 @@ const addItemsToPlaylist = async (req, res, nextBatch, playlistID) => {
|
|||||||
return res.headersSent ? null : response.data;
|
return res.headersSent ? null : response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeItemsFromPlaylist = async (req, res, nextBatch, playlistID, snapshotID) => {
|
export const removeItemsFromPlaylist = async (req, res, nextBatch, playlistID, snapshotID) => {
|
||||||
// API doesn't document this kind of deletion via the 'positions' field
|
// API doesn't document this kind of deletion via the 'positions' field
|
||||||
// but see here: https://github.com/spotipy-dev/spotipy/issues/95#issuecomment-2263634801
|
// but see here: https://github.com/spotipy-dev/spotipy/issues/95#issuecomment-2263634801
|
||||||
const response = await singleRequest(req, res,
|
const response = await singleRequest(req, res,
|
||||||
@ -130,7 +131,7 @@ const removeItemsFromPlaylist = async (req, res, nextBatch, playlistID, snapshot
|
|||||||
return res.headersSent ? null : response.data;
|
return res.headersSent ? null : response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkPlaylistEditable = async (req, res, playlistID, userID) => {
|
export const checkPlaylistEditable = async (req, res, playlistID, userID) => {
|
||||||
let checkFields = ["collaborative", "owner(id)"];
|
let checkFields = ["collaborative", "owner(id)"];
|
||||||
|
|
||||||
const checkFromData = await getPlaylistDetailsFirstPage(req, res, checkFields.join(), playlistID);
|
const checkFromData = await getPlaylistDetailsFirstPage(req, res, checkFields.join(), playlistID);
|
||||||
@ -150,15 +151,3 @@ const checkPlaylistEditable = async (req, res, playlistID, userID) => {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
singleRequest,
|
|
||||||
getUserProfile,
|
|
||||||
getUserPlaylistsFirstPage,
|
|
||||||
getUserPlaylistsNextPage,
|
|
||||||
getPlaylistDetailsFirstPage,
|
|
||||||
getPlaylistDetailsNextPage,
|
|
||||||
addItemsToPlaylist,
|
|
||||||
removeItemsFromPlaylist,
|
|
||||||
checkPlaylistEditable,
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
const logger = require("../utils/logger")(module);
|
import curriedLogger from "../utils/logger.js";
|
||||||
|
const logger = curriedLogger(import.meta);
|
||||||
|
|
||||||
const typedefs = require("../typedefs");
|
import * as typedefs from "../typedefs.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {typedefs.Req} req
|
* @param {typedefs.Req} req
|
||||||
* @param {typedefs.Res} res
|
* @param {typedefs.Res} res
|
||||||
*/
|
*/
|
||||||
const __controller_func = async (req, res) => {
|
export const __controller_func = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -15,7 +16,3 @@ const __controller_func = async (req, res) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
__controller_func
|
|
||||||
};
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
const router = require("express").Router();
|
import { Router } from "express";
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
const { validate } = require("../validators");
|
import { validate } from "../validators/index.js";
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
|
|
||||||
@ -10,4 +11,4 @@ router.post(
|
|||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
module.exports = router;
|
export default router;
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
const { body, header, param, query } = require("express-validator");
|
import { body, header, param, query } from "express-validator";
|
||||||
|
|
||||||
const typedefs = require("../typedefs");
|
import * as typedefs from "../typedefs.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {typedefs.Req} req
|
* @param {typedefs.Req} req
|
||||||
* @param {typedefs.Res} res
|
* @param {typedefs.Res} res
|
||||||
* @param {typedefs.Next} next
|
* @param {typedefs.Next} next
|
||||||
*/
|
*/
|
||||||
const __validator_func = async (req, res, next) => {
|
export const __validator_func = async (req, res, next) => {
|
||||||
await body("field_name")
|
await body("field_name")
|
||||||
.notEmpty()
|
.notEmpty()
|
||||||
.withMessage("field_name not defined in body")
|
.withMessage("field_name not defined in body")
|
||||||
@ -15,7 +15,3 @@ const __validator_func = async (req, res, next) => {
|
|||||||
|
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
__validator_func
|
|
||||||
}
|
|
||||||
|
|||||||
3
config/dotenv.js
Normal file
3
config/dotenv.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// https://github.com/motdotla/dotenv/issues/133#issuecomment-255298822
|
||||||
|
import DotenvFlow from "dotenv-flow";
|
||||||
|
export default DotenvFlow.config();
|
||||||
@ -1,4 +1,5 @@
|
|||||||
const logger = require("../utils/logger")(module);
|
import curriedLogger from "../utils/logger.js";
|
||||||
|
const logger = curriedLogger(import.meta);
|
||||||
|
|
||||||
const connConfigs = {
|
const connConfigs = {
|
||||||
development: {
|
development: {
|
||||||
@ -8,7 +9,7 @@ const connConfigs = {
|
|||||||
host: process.env.DB_HOST || "127.0.0.1",
|
host: process.env.DB_HOST || "127.0.0.1",
|
||||||
port: process.env.DB_PORT || 5432,
|
port: process.env.DB_PORT || 5432,
|
||||||
},
|
},
|
||||||
staging: {
|
test: {
|
||||||
use_env_variable: "DB_URL", // use connection string for non-dev env
|
use_env_variable: "DB_URL", // use connection string for non-dev env
|
||||||
},
|
},
|
||||||
production: {
|
production: {
|
||||||
@ -25,4 +26,4 @@ for (const conf in connConfigs) {
|
|||||||
connConfigs[conf]["dialect"] = process.env.DB_DIALECT || "postgres";
|
connConfigs[conf]["dialect"] = process.env.DB_DIALECT || "postgres";
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = connConfigs;
|
export default connConfigs;
|
||||||
|
|||||||
18
constants.js
18
constants.js
@ -1,9 +1,9 @@
|
|||||||
const accountsAPIURL = "https://accounts.spotify.com";
|
export const accountsAPIURL = "https://accounts.spotify.com";
|
||||||
const baseAPIURL = "https://api.spotify.com/v1";
|
export const baseAPIURL = "https://api.spotify.com/v1";
|
||||||
const sessionName = "spotify-manager";
|
export const sessionName = "spotify-manager";
|
||||||
const stateKey = "spotify_auth_state";
|
export const stateKey = "spotify_auth_state";
|
||||||
|
|
||||||
const scopes = {
|
export const scopes = {
|
||||||
// ImageUpload: "ugc-image-upload",
|
// ImageUpload: "ugc-image-upload",
|
||||||
AccessPrivatePlaylists: "playlist-read-private",
|
AccessPrivatePlaylists: "playlist-read-private",
|
||||||
AccessCollaborativePlaylists: "playlist-read-collaborative",
|
AccessCollaborativePlaylists: "playlist-read-collaborative",
|
||||||
@ -15,11 +15,3 @@ const scopes = {
|
|||||||
AccessLibrary: "user-library-read",
|
AccessLibrary: "user-library-read",
|
||||||
AccessUser: "user-read-private",
|
AccessUser: "user-read-private",
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
accountsAPIURL,
|
|
||||||
baseAPIURL,
|
|
||||||
sessionName,
|
|
||||||
stateKey,
|
|
||||||
scopes
|
|
||||||
};
|
|
||||||
|
|||||||
@ -1,18 +1,19 @@
|
|||||||
const { authInstance } = require("../api/axios");
|
import { authInstance } from "../api/axios.js";
|
||||||
|
|
||||||
const typedefs = require("../typedefs");
|
import * as typedefs from "../typedefs.js";
|
||||||
const { scopes, stateKey, accountsAPIURL, sessionName } = require("../constants");
|
import { scopes, stateKey, accountsAPIURL, sessionName } from "../constants.js";
|
||||||
|
|
||||||
const generateRandString = require("../utils/generateRandString");
|
import generateRandString from "../utils/generateRandString.js";
|
||||||
const { getUserProfile } = require("../api/spotify");
|
import { getUserProfile } from "../api/spotify.js";
|
||||||
const logger = require("../utils/logger")(module);
|
import curriedLogger from "../utils/logger.js";
|
||||||
|
const logger = curriedLogger(import.meta);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stateful redirect to Spotify login with credentials
|
* Stateful redirect to Spotify login with credentials
|
||||||
* @param {typedefs.Req} req
|
* @param {typedefs.Req} req
|
||||||
* @param {typedefs.Res} res
|
* @param {typedefs.Res} res
|
||||||
*/
|
*/
|
||||||
const login = (_req, res) => {
|
export const login = (_req, res) => {
|
||||||
try {
|
try {
|
||||||
const state = generateRandString(16);
|
const state = generateRandString(16);
|
||||||
res.cookie(stateKey, state);
|
res.cookie(stateKey, state);
|
||||||
@ -41,7 +42,7 @@ const login = (_req, res) => {
|
|||||||
* @param {typedefs.Req} req
|
* @param {typedefs.Req} req
|
||||||
* @param {typedefs.Res} res
|
* @param {typedefs.Res} res
|
||||||
*/
|
*/
|
||||||
const callback = async (req, res) => {
|
export const callback = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { code, state, error } = req.query;
|
const { code, state, error } = req.query;
|
||||||
const storedState = req.cookies ? req.cookies[stateKey] : null;
|
const storedState = req.cookies ? req.cookies[stateKey] : null;
|
||||||
@ -104,7 +105,7 @@ const callback = async (req, res) => {
|
|||||||
* @param {typedefs.Req} req
|
* @param {typedefs.Req} req
|
||||||
* @param {typedefs.Res} res
|
* @param {typedefs.Res} res
|
||||||
*/
|
*/
|
||||||
const refresh = async (req, res) => {
|
export const refresh = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const authForm = {
|
const authForm = {
|
||||||
refresh_token: req.session.refreshToken,
|
refresh_token: req.session.refreshToken,
|
||||||
@ -139,10 +140,10 @@ const refresh = async (req, res) => {
|
|||||||
* @param {typedefs.Req} req
|
* @param {typedefs.Req} req
|
||||||
* @param {typedefs.Res} res
|
* @param {typedefs.Res} res
|
||||||
*/
|
*/
|
||||||
const logout = async (req, res) => {
|
export const logout = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const delSession = req.session.destroy((error) => {
|
const delSession = req.session.destroy((error) => {
|
||||||
if(Object.keys(error).length) {
|
if (Object.keys(error).length) {
|
||||||
res.status(500).send({ message: "Internal Server Error" });
|
res.status(500).send({ message: "Internal Server Error" });
|
||||||
logger.error("Error while logging out", { error });
|
logger.error("Error while logging out", { error });
|
||||||
return;
|
return;
|
||||||
@ -160,10 +161,3 @@ const logout = async (req, res) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
login,
|
|
||||||
callback,
|
|
||||||
refresh,
|
|
||||||
logout
|
|
||||||
};
|
|
||||||
|
|||||||
@ -1,25 +1,25 @@
|
|||||||
const typedefs = require("../typedefs");
|
import * as typedefs from "../typedefs.js";
|
||||||
const logger = require("../utils/logger")(module);
|
import curriedLogger from "../utils/logger.js";
|
||||||
|
const logger = curriedLogger(import.meta);
|
||||||
|
|
||||||
const { getUserPlaylistsFirstPage, getUserPlaylistsNextPage, getPlaylistDetailsFirstPage, getPlaylistDetailsNextPage, addItemsToPlaylist, removeItemsFromPlaylist, checkPlaylistEditable } = require("../api/spotify");
|
import { getUserPlaylistsFirstPage, getUserPlaylistsNextPage, getPlaylistDetailsFirstPage, getPlaylistDetailsNextPage, addItemsToPlaylist, removeItemsFromPlaylist, checkPlaylistEditable } from "../api/spotify.js";
|
||||||
|
|
||||||
const { parseSpotifyLink } = require("../utils/spotifyURITransformer");
|
import { parseSpotifyLink } from "../utils/spotifyURITransformer.js";
|
||||||
const { randomBool, sleep } = require("../utils/flake");
|
import { randomBool, sleep } from "../utils/flake.js";
|
||||||
const myGraph = require("../utils/graph");
|
import myGraph from "../utils/graph.js";
|
||||||
|
|
||||||
const { Op } = require("sequelize");
|
import { Op } from "sequelize";
|
||||||
const { sequelize } = require("../models");
|
|
||||||
/** @type {typedefs.Model} */
|
import models, { sequelize } from "../models/index.js";
|
||||||
const Playlists = require("../models").playlists;
|
const Playlists = models.playlists;
|
||||||
/** @type {typedefs.Model} */
|
const Links = models.links;
|
||||||
const Links = require("../models").links;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sync user's Spotify data
|
* Sync user's Spotify data
|
||||||
* @param {typedefs.Req} req
|
* @param {typedefs.Req} req
|
||||||
* @param {typedefs.Res} res
|
* @param {typedefs.Res} res
|
||||||
*/
|
*/
|
||||||
const updateUser = async (req, res) => {
|
export const updateUser = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
let currentPlaylists = [];
|
let currentPlaylists = [];
|
||||||
const uID = req.session.user.id;
|
const uID = req.session.user.id;
|
||||||
@ -177,7 +177,7 @@ const updateUser = async (req, res) => {
|
|||||||
* @param {typedefs.Req} req
|
* @param {typedefs.Req} req
|
||||||
* @param {typedefs.Res} res
|
* @param {typedefs.Res} res
|
||||||
*/
|
*/
|
||||||
const fetchUser = async (req, res) => {
|
export const fetchUser = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
// if (randomBool(0.5)) {
|
// if (randomBool(0.5)) {
|
||||||
// res.status(404).send({ message: "Not Found" });
|
// res.status(404).send({ message: "Not Found" });
|
||||||
@ -219,7 +219,7 @@ const fetchUser = async (req, res) => {
|
|||||||
* @param {typedefs.Req} req
|
* @param {typedefs.Req} req
|
||||||
* @param {typedefs.Res} res
|
* @param {typedefs.Res} res
|
||||||
*/
|
*/
|
||||||
const createLink = async (req, res) => {
|
export const createLink = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
// await sleep(1000);
|
// await sleep(1000);
|
||||||
const uID = req.session.user.id;
|
const uID = req.session.user.id;
|
||||||
@ -310,7 +310,7 @@ const createLink = async (req, res) => {
|
|||||||
* @param {typedefs.Req} req
|
* @param {typedefs.Req} req
|
||||||
* @param {typedefs.Res} res
|
* @param {typedefs.Res} res
|
||||||
*/
|
*/
|
||||||
const removeLink = async (req, res) => {
|
export const removeLink = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const uID = req.session.user.id;
|
const uID = req.session.user.id;
|
||||||
|
|
||||||
@ -475,7 +475,7 @@ const _populateSingleLinkCore = async (req, res, link) => {
|
|||||||
* @param {typedefs.Req} req
|
* @param {typedefs.Req} req
|
||||||
* @param {typedefs.Res} res
|
* @param {typedefs.Res} res
|
||||||
*/
|
*/
|
||||||
const populateSingleLink = async (req, res) => {
|
export const populateSingleLink = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const uID = req.session.user.id;
|
const uID = req.session.user.id;
|
||||||
const link = { from: req.body.from, to: req.body.to };
|
const link = { from: req.body.from, to: req.body.to };
|
||||||
@ -591,7 +591,7 @@ const _pruneSingleLinkCore = async (req, res, link) => {
|
|||||||
* @param {typedefs.Req} req
|
* @param {typedefs.Req} req
|
||||||
* @param {typedefs.Res} res
|
* @param {typedefs.Res} res
|
||||||
*/
|
*/
|
||||||
const pruneSingleLink = async (req, res) => {
|
export const pruneSingleLink = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const uID = req.session.user.id;
|
const uID = req.session.user.id;
|
||||||
const link = { from: req.body.from, to: req.body.to };
|
const link = { from: req.body.from, to: req.body.to };
|
||||||
@ -643,12 +643,3 @@ const pruneSingleLink = async (req, res) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
updateUser,
|
|
||||||
fetchUser,
|
|
||||||
createLink,
|
|
||||||
removeLink,
|
|
||||||
populateSingleLink,
|
|
||||||
pruneSingleLink,
|
|
||||||
};
|
|
||||||
|
|||||||
@ -1,15 +1,16 @@
|
|||||||
const logger = require("../utils/logger")(module);
|
import curriedLogger from "../utils/logger.js";
|
||||||
|
const logger = curriedLogger(import.meta);
|
||||||
|
|
||||||
const typedefs = require("../typedefs");
|
import * as typedefs from "../typedefs.js";
|
||||||
const { getUserPlaylistsFirstPage, getUserPlaylistsNextPage, getPlaylistDetailsFirstPage, getPlaylistDetailsNextPage } = require("../api/spotify");
|
import { getUserPlaylistsFirstPage, getUserPlaylistsNextPage, getPlaylistDetailsFirstPage, getPlaylistDetailsNextPage } from "../api/spotify.js";
|
||||||
const { parseSpotifyLink } = require("../utils/spotifyURITransformer");
|
import { parseSpotifyLink } from "../utils/spotifyURITransformer.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve list of all of user's playlists
|
* Retrieve list of all of user's playlists
|
||||||
* @param {typedefs.Req} req
|
* @param {typedefs.Req} req
|
||||||
* @param {typedefs.Res} res
|
* @param {typedefs.Res} res
|
||||||
*/
|
*/
|
||||||
const fetchUserPlaylists = async (req, res) => {
|
export const fetchUserPlaylists = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
let userPlaylists = {};
|
let userPlaylists = {};
|
||||||
|
|
||||||
@ -65,7 +66,7 @@ const fetchUserPlaylists = async (req, res) => {
|
|||||||
* @param {typedefs.Req} req
|
* @param {typedefs.Req} req
|
||||||
* @param {typedefs.Res} res
|
* @param {typedefs.Res} res
|
||||||
*/
|
*/
|
||||||
const fetchPlaylistDetails = async (req, res) => {
|
export const fetchPlaylistDetails = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
let playlist = {};
|
let playlist = {};
|
||||||
/** @type {typedefs.URIObject} */
|
/** @type {typedefs.URIObject} */
|
||||||
@ -152,8 +153,3 @@ const fetchPlaylistDetails = async (req, res) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
fetchUserPlaylists,
|
|
||||||
fetchPlaylistDetails
|
|
||||||
};
|
|
||||||
|
|||||||
45
index.js
45
index.js
@ -1,23 +1,24 @@
|
|||||||
require("dotenv-flow").config();
|
import _ from "./config/dotenv.js";
|
||||||
|
|
||||||
const util = require("util");
|
import { promisify } from "util";
|
||||||
const express = require("express");
|
import express from "express";
|
||||||
const session = require("express-session");
|
import session from "express-session";
|
||||||
|
|
||||||
const cors = require("cors");
|
import cors from "cors";
|
||||||
const cookieParser = require("cookie-parser");
|
import cookieParser from "cookie-parser";
|
||||||
const helmet = require("helmet");
|
import helmet from "helmet";
|
||||||
|
|
||||||
const { createClient } = require('redis');
|
import { createClient } from 'redis';
|
||||||
const { RedisStore } = require("connect-redis");
|
import { RedisStore } from "connect-redis";
|
||||||
|
|
||||||
const { sessionName } = require("./constants");
|
import { sessionName } from "./constants.js";
|
||||||
const db = require("./models");
|
import { sequelize } from "./models/index.js";
|
||||||
|
|
||||||
const { isAuthenticated } = require("./middleware/authCheck");
|
import { isAuthenticated } from "./middleware/authCheck.js";
|
||||||
const { getUserProfile } = require("./api/spotify");
|
import { getUserProfile } from "./api/spotify.js";
|
||||||
|
|
||||||
const logger = require("./utils/logger")(module);
|
import curriedLogger from "./utils/logger.js";
|
||||||
|
const logger = curriedLogger(import.meta);
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
@ -73,7 +74,7 @@ app.use(express.json());
|
|||||||
app.use(express.urlencoded({ extended: true }));
|
app.use(express.urlencoded({ extended: true }));
|
||||||
|
|
||||||
// Static
|
// Static
|
||||||
app.use(express.static(__dirname + "/static"));
|
app.use(express.static(import.meta.dirname + "/static"));
|
||||||
|
|
||||||
// Healthcheck
|
// Healthcheck
|
||||||
app.use("/health", (req, res) => {
|
app.use("/health", (req, res) => {
|
||||||
@ -92,11 +93,13 @@ app.use("/auth-health", isAuthenticated, async (req, res) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
import authRoutes from "./routes/auth.js";
|
||||||
|
import playlistRoutes from "./routes/playlists.js";
|
||||||
|
import operationRoutes from "./routes/operations.js";
|
||||||
// Routes
|
// Routes
|
||||||
app.use("/api/auth/", require("./routes/auth"));
|
app.use("/api/auth/", authRoutes);
|
||||||
app.use("/api/playlists", isAuthenticated, require("./routes/playlists"));
|
app.use("/api/playlists", isAuthenticated, playlistRoutes);
|
||||||
app.use("/api/operations", isAuthenticated, require("./routes/operations"));
|
app.use("/api/operations", isAuthenticated, operationRoutes);
|
||||||
|
|
||||||
// Fallbacks
|
// Fallbacks
|
||||||
app.use((req, res) => {
|
app.use((req, res) => {
|
||||||
@ -119,8 +122,8 @@ const cleanupFunc = (signal) => {
|
|||||||
|
|
||||||
Promise.allSettled([
|
Promise.allSettled([
|
||||||
redisClient.disconnect,
|
redisClient.disconnect,
|
||||||
db.sequelize.close(),
|
sequelize.close(),
|
||||||
util.promisify(server.close),
|
promisify(server.close),
|
||||||
]).then(() => {
|
]).then(() => {
|
||||||
logger.info("Cleaned up, exiting.");
|
logger.info("Cleaned up, exiting.");
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
const { sessionName } = require("../constants");
|
import { sessionName } from "../constants.js";
|
||||||
const typedefs = require("../typedefs");
|
import * as typedefs from "../typedefs.js";
|
||||||
const logger = require("../utils/logger")(module);
|
import curriedLogger from "../utils/logger.js";
|
||||||
|
const logger = curriedLogger(import.meta);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* middleware to check if access token is present
|
* middleware to check if access token is present
|
||||||
@ -8,7 +9,7 @@ const logger = require("../utils/logger")(module);
|
|||||||
* @param {typedefs.Res} res
|
* @param {typedefs.Res} res
|
||||||
* @param {typedefs.Next} next
|
* @param {typedefs.Next} next
|
||||||
*/
|
*/
|
||||||
const isAuthenticated = (req, res, next) => {
|
export const isAuthenticated = (req, res, next) => {
|
||||||
if (req.session.accessToken) {
|
if (req.session.accessToken) {
|
||||||
req.sessHeaders = {
|
req.sessHeaders = {
|
||||||
"Authorization": `Bearer ${req.session.accessToken}`,
|
"Authorization": `Bearer ${req.session.accessToken}`,
|
||||||
@ -30,7 +31,3 @@ const isAuthenticated = (req, res, next) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
isAuthenticated,
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
/** @type {import("sequelize-cli").Migration} */
|
/** @type {import("sequelize-cli").Migration} */
|
||||||
module.exports = {
|
export default {
|
||||||
async up(queryInterface, Sequelize) {
|
up: async function (queryInterface, Sequelize) {
|
||||||
await queryInterface.createTable("playlists", {
|
await queryInterface.createTable("playlists", {
|
||||||
id: {
|
id: {
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
@ -28,7 +28,7 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async down(queryInterface, Sequelize) {
|
down: async function (queryInterface, Sequelize) {
|
||||||
await queryInterface.dropTable("playlists");
|
await queryInterface.dropTable("playlists");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
/** @type {import("sequelize-cli").Migration} */
|
/** @type {import("sequelize-cli").Migration} */
|
||||||
module.exports = {
|
export default {
|
||||||
async up(queryInterface, Sequelize) {
|
up: async function (queryInterface, Sequelize) {
|
||||||
await queryInterface.createTable("links", {
|
await queryInterface.createTable("links", {
|
||||||
id: {
|
id: {
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
@ -28,7 +28,7 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async down(queryInterface, Sequelize) {
|
down: async function (queryInterface, Sequelize) {
|
||||||
await queryInterface.dropTable("links");
|
await queryInterface.dropTable("links");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,11 +1,16 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
const fs = require("fs");
|
import { readdirSync } from "fs";
|
||||||
const path = require("path");
|
import { basename as _basename } from "path";
|
||||||
const Sequelize = require("sequelize");
|
const basename = _basename(import.meta.filename);
|
||||||
const logger = require("../utils/logger")(module);
|
|
||||||
const basename = path.basename(__filename);
|
import Sequelize from "sequelize";
|
||||||
|
|
||||||
|
import curriedLogger from "../utils/logger.js";
|
||||||
|
const logger = curriedLogger(import.meta);
|
||||||
|
|
||||||
|
import seqConfig from "../config/sequelize.js"
|
||||||
const env = process.env.NODE_ENV || "development";
|
const env = process.env.NODE_ENV || "development";
|
||||||
const config = require(__dirname + "/../config/sequelize.js")[env];
|
const config = seqConfig[env];
|
||||||
const db = {};
|
const db = {};
|
||||||
|
|
||||||
let sequelize;
|
let sequelize;
|
||||||
@ -26,15 +31,22 @@ if (config.use_env_variable) {
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
// Read model definitions from folder
|
// Read model definitions from folder
|
||||||
fs
|
const modelFiles = readdirSync(import.meta.dirname)
|
||||||
.readdirSync(__dirname)
|
.filter(
|
||||||
.filter(file => {
|
(file) => file.indexOf('.') !== 0
|
||||||
return (file.indexOf(".") !== 0) && (file !== basename) && (file.slice(-3) === ".js");
|
&& file !== basename
|
||||||
})
|
&& file.slice(-3) === '.js',
|
||||||
.forEach(file => {
|
);
|
||||||
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
|
|
||||||
db[model.name] = model;
|
await Promise.all(modelFiles.map(async file => {
|
||||||
});
|
const model = await import(`./${file}`);
|
||||||
|
if (!model.default) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const namedModel = model.default(sequelize, Sequelize.DataTypes);
|
||||||
|
db[namedModel.name] = namedModel;
|
||||||
|
}))
|
||||||
|
|
||||||
// Setup defined associations
|
// Setup defined associations
|
||||||
Object.keys(db).forEach(modelName => {
|
Object.keys(db).forEach(modelName => {
|
||||||
@ -43,7 +55,9 @@ Object.keys(db).forEach(modelName => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// clean ts up
|
||||||
db.sequelize = sequelize;
|
db.sequelize = sequelize;
|
||||||
db.Sequelize = Sequelize;
|
db.Sequelize = Sequelize;
|
||||||
|
export { sequelize as sequelize };
|
||||||
module.exports = db;
|
export { Sequelize as Sequelize };
|
||||||
|
export default db;
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
const {
|
import { Model } from "sequelize";
|
||||||
Model
|
export default (sequelize, DataTypes) => {
|
||||||
} = require("sequelize");
|
|
||||||
module.exports = (sequelize, DataTypes) => {
|
|
||||||
class links extends Model {
|
class links extends Model {
|
||||||
/**
|
/**
|
||||||
* Helper method for defining associations.
|
* Helper method for defining associations.
|
||||||
@ -22,4 +20,4 @@ module.exports = (sequelize, DataTypes) => {
|
|||||||
modelName: "links",
|
modelName: "links",
|
||||||
});
|
});
|
||||||
return links;
|
return links;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
const {
|
import { Model } from "sequelize";
|
||||||
Model
|
export default (sequelize, DataTypes) => {
|
||||||
} = require("sequelize");
|
|
||||||
module.exports = (sequelize, DataTypes) => {
|
|
||||||
class playlists extends Model {
|
class playlists extends Model {
|
||||||
/**
|
/**
|
||||||
* Helper method for defining associations.
|
* Helper method for defining associations.
|
||||||
@ -22,4 +20,4 @@ module.exports = (sequelize, DataTypes) => {
|
|||||||
modelName: "playlists",
|
modelName: "playlists",
|
||||||
});
|
});
|
||||||
return playlists;
|
return playlists;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,7 +2,8 @@
|
|||||||
"name": "spotify-manager",
|
"name": "spotify-manager",
|
||||||
"version": "0",
|
"version": "0",
|
||||||
"description": "Personal Spotify playlist manager",
|
"description": "Personal Spotify playlist manager",
|
||||||
"main": "index.js",
|
"exports": "./index.js",
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "cross-env NODE_ENV=development nodemon --delay 2 --exitcrash index.js",
|
"dev": "cross-env NODE_ENV=development nodemon --delay 2 --exitcrash index.js",
|
||||||
"test_setup": "npm i && cross-env NODE_ENV=test npx sequelize-cli db:migrate",
|
"test_setup": "npm i && cross-env NODE_ENV=test npx sequelize-cli db:migrate",
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
const router = require("express").Router();
|
import { Router } from "express";
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
const { login, callback, refresh, logout } = require("../controllers/auth");
|
import { login, callback, refresh, logout } from "../controllers/auth.js";
|
||||||
const { isAuthenticated } = require("../middleware/authCheck");
|
import { isAuthenticated } from "../middleware/authCheck.js";
|
||||||
const validator = require("../validators");
|
import { validate } from "../validators/index.js";
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/login",
|
"/login",
|
||||||
@ -25,4 +26,4 @@ router.get(
|
|||||||
logout
|
logout
|
||||||
);
|
);
|
||||||
|
|
||||||
module.exports = router;
|
export default router;
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
const router = require("express").Router();
|
import { Router } from "express";
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
const { updateUser, fetchUser, createLink, removeLink, populateSingleLink, pruneSingleLink } = require("../controllers/operations");
|
import { updateUser, fetchUser, createLink, removeLink, populateSingleLink, pruneSingleLink } from "../controllers/operations.js";
|
||||||
const { validate } = require("../validators");
|
import { createLinkValidator, removeLinkValidator, populateSingleLinkValidator, pruneSingleLinkValidator } from "../validators/operations.js";
|
||||||
const { createLinkValidator, removeLinkValidator, populateSingleLinkValidator, pruneSingleLinkValidator } = require("../validators/operations");
|
|
||||||
|
import { validate } from "../validators/index.js";
|
||||||
|
|
||||||
router.put(
|
router.put(
|
||||||
"/update",
|
"/update",
|
||||||
@ -42,4 +44,4 @@ router.put(
|
|||||||
pruneSingleLink
|
pruneSingleLink
|
||||||
);
|
);
|
||||||
|
|
||||||
module.exports = router;
|
export default router;
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
const router = require("express").Router();
|
import { Router } from "express";
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
const { fetchUserPlaylists, fetchPlaylistDetails } = require("../controllers/playlists");
|
import { fetchUserPlaylists, fetchPlaylistDetails } from "../controllers/playlists.js";
|
||||||
const { getPlaylistDetailsValidator } = require("../validators/playlists");
|
import { getPlaylistDetailsValidator } from "../validators/playlists.js";
|
||||||
const { validate } = require("../validators");
|
|
||||||
|
import { validate } from "../validators/index.js";
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/me",
|
"/me",
|
||||||
@ -16,4 +18,4 @@ router.get(
|
|||||||
fetchPlaylistDetails
|
fetchPlaylistDetails
|
||||||
);
|
);
|
||||||
|
|
||||||
module.exports = router;
|
export default router;
|
||||||
|
|||||||
22
typedefs.js
22
typedefs.js
@ -1,10 +1,10 @@
|
|||||||
/**
|
/**
|
||||||
* @typedef {import("module")} Module
|
* @typedef {import("module")} Module
|
||||||
*
|
*
|
||||||
* @typedef {import("express").Request} Req
|
* @typedef {import("express").Request} Req
|
||||||
* @typedef {import("express").Response} Res
|
* @typedef {import("express").Response} Res
|
||||||
* @typedef {import("express").NextFunction} Next
|
* @typedef {import("express").NextFunction} Next
|
||||||
*
|
*
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* type: string,
|
* type: string,
|
||||||
* is_local: boolean,
|
* is_local: boolean,
|
||||||
@ -14,25 +14,25 @@
|
|||||||
* title?: string,
|
* title?: string,
|
||||||
* duration?: number
|
* duration?: number
|
||||||
* }} URIObject
|
* }} URIObject
|
||||||
*
|
*
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* username: string,
|
* username: string,
|
||||||
* uri: string
|
* uri: string
|
||||||
* }} User
|
* }} User
|
||||||
*
|
*
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* name: string,
|
* name: string,
|
||||||
* uri: string,
|
* uri: string,
|
||||||
* }} SimplifiedPlaylist
|
* }} SimplifiedPlaylist
|
||||||
*
|
*
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* name: string
|
* name: string
|
||||||
* }} Album
|
* }} Album
|
||||||
*
|
*
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* name: string
|
* name: string
|
||||||
* }} Artist
|
* }} Artist
|
||||||
*
|
*
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* uri: string,
|
* uri: string,
|
||||||
* name: string,
|
* name: string,
|
||||||
@ -40,18 +40,18 @@
|
|||||||
* album: Album,
|
* album: Album,
|
||||||
* is_local: boolean,
|
* is_local: boolean,
|
||||||
* }} Track
|
* }} Track
|
||||||
*
|
*
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* added_at: string,
|
* added_at: string,
|
||||||
* track: Track,
|
* track: Track,
|
||||||
* }} PlaylistTrack
|
* }} PlaylistTrack
|
||||||
*
|
*
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* url: string,
|
* url: string,
|
||||||
* height: number,
|
* height: number,
|
||||||
* width: number
|
* width: number
|
||||||
* }} ImageObject
|
* }} ImageObject
|
||||||
*
|
*
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* uri: string,
|
* uri: string,
|
||||||
* name: string,
|
* name: string,
|
||||||
@ -64,4 +64,4 @@
|
|||||||
* }} PlaylistDetails
|
* }} PlaylistDetails
|
||||||
*/
|
*/
|
||||||
|
|
||||||
exports.unused = {};
|
export default {};
|
||||||
|
|||||||
@ -1,7 +1,3 @@
|
|||||||
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
export const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
|
||||||
const randomBool = (chance_of_failure = 0.25) => Math.random() < chance_of_failure;
|
export const randomBool = (chance_of_failure = 0.25) => Math.random() < chance_of_failure;
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
sleep, randomBool
|
|
||||||
};
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* @param {number} length The length of the string
|
* @param {number} length The length of the string
|
||||||
* @return {string} The generated string
|
* @return {string} The generated string
|
||||||
*/
|
*/
|
||||||
module.exports = (length) => {
|
export default (length) => {
|
||||||
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
let text = "";
|
let text = "";
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
const logger = require("./logger")(module);
|
import curriedLogger from "./logger.js";
|
||||||
|
const logger = curriedLogger(import.meta);
|
||||||
|
|
||||||
const typedefs = require("../typedefs");
|
import * as typedefs from "../typedefs.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Directed graph, may or may not be connected.
|
* Directed graph, may or may not be connected.
|
||||||
@ -21,7 +22,7 @@ const typedefs = require("../typedefs");
|
|||||||
* console.log(g.detectCycle()); // true
|
* console.log(g.detectCycle()); // true
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
class myGraph {
|
export class myGraph {
|
||||||
/**
|
/**
|
||||||
* @param {string[]} nodes Graph nodes IDs
|
* @param {string[]} nodes Graph nodes IDs
|
||||||
* @param {{ from: string, to: string }[]} edges Graph edges b/w nodes
|
* @param {{ from: string, to: string }[]} edges Graph edges b/w nodes
|
||||||
@ -156,4 +157,4 @@ class myGraph {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = myGraph;
|
export default myGraph;
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
* @param {string} delimiter Delimiter of final string
|
* @param {string} delimiter Delimiter of final string
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
const getNestedValuesString = (obj, delimiter = ", ") => {
|
export const getNestedValuesString = (obj, delimiter = ", ") => {
|
||||||
let values = [];
|
let values = [];
|
||||||
for (key in obj) {
|
for (key in obj) {
|
||||||
if (typeof obj[key] !== "object") {
|
if (typeof obj[key] !== "object") {
|
||||||
@ -17,7 +17,3 @@ const getNestedValuesString = (obj, delimiter = ", ") => {
|
|||||||
|
|
||||||
return values.join(delimiter);
|
return values.join(delimiter);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
getNestedValuesString
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
const path = require("path");
|
import path from "path";
|
||||||
|
|
||||||
|
import { createLogger, transports, config, format } from "winston";
|
||||||
|
import * as typedefs from "../typedefs.js";
|
||||||
|
|
||||||
const { createLogger, transports, config, format } = require("winston");
|
|
||||||
const { combine, label, timestamp, printf, errors } = format;
|
const { combine, label, timestamp, printf, errors } = format;
|
||||||
|
|
||||||
const typedefs = require("../typedefs");
|
|
||||||
|
|
||||||
const getLabel = (callingModule) => {
|
const getLabel = (callingModule) => {
|
||||||
if (!callingModule.filename) return "repl";
|
if (!callingModule.filename) return "repl";
|
||||||
const parts = callingModule.filename?.split(path.sep);
|
const parts = callingModule.filename?.split(path.sep);
|
||||||
@ -35,9 +35,9 @@ const logFormat = printf(({ level, message, label, timestamp, ...meta }) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a curried function, and call it with the module in use to get logs with filename
|
* Creates a curried function, and call it with the module in use to get logs with filename
|
||||||
* @param {typedefs.Module} callingModule The module from which the logger is called
|
* @param {typedefs.Module} callingModule The module from which the logger is called (ESM - import.meta)
|
||||||
*/
|
*/
|
||||||
const curriedLogger = (callingModule) => {
|
export const curriedLogger = (callingModule) => {
|
||||||
let winstonLogger = createLogger({
|
let winstonLogger = createLogger({
|
||||||
levels: config.npm.levels,
|
levels: config.npm.levels,
|
||||||
format: combine(
|
format: combine(
|
||||||
@ -49,12 +49,12 @@ const curriedLogger = (callingModule) => {
|
|||||||
transports: [
|
transports: [
|
||||||
new transports.Console({ level: "info" }),
|
new transports.Console({ level: "info" }),
|
||||||
new transports.File({
|
new transports.File({
|
||||||
filename: __dirname + "/../logs/debug.log",
|
filename: import.meta.dirname + "/../logs/debug.log",
|
||||||
level: "debug",
|
level: "debug",
|
||||||
maxsize: 10485760,
|
maxsize: 10485760,
|
||||||
}),
|
}),
|
||||||
new transports.File({
|
new transports.File({
|
||||||
filename: __dirname + "/../logs/error.log",
|
filename: import.meta.dirname + "/../logs/error.log",
|
||||||
level: "error",
|
level: "error",
|
||||||
maxsize: 1048576,
|
maxsize: 1048576,
|
||||||
}),
|
}),
|
||||||
@ -64,4 +64,4 @@ const curriedLogger = (callingModule) => {
|
|||||||
return winstonLogger;
|
return winstonLogger;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = curriedLogger;
|
export default curriedLogger;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
const typedefs = require("../typedefs");
|
import * as typedefs from "../typedefs.js";
|
||||||
|
|
||||||
/** @type {RegExp} */
|
/** @type {RegExp} */
|
||||||
const base62Pattern = /^[A-Za-z0-9]+$/;
|
const base62Pattern = /^[A-Za-z0-9]+$/;
|
||||||
@ -10,7 +10,7 @@ const base62Pattern = /^[A-Za-z0-9]+$/;
|
|||||||
* @returns {typedefs.URIObject}
|
* @returns {typedefs.URIObject}
|
||||||
* @throws {TypeError} If the input is not a valid Spotify URI
|
* @throws {TypeError} If the input is not a valid Spotify URI
|
||||||
*/
|
*/
|
||||||
const parseSpotifyURI = (uri) => {
|
export const parseSpotifyURI = (uri) => {
|
||||||
const parts = uri.split(":");
|
const parts = uri.split(":");
|
||||||
|
|
||||||
if (parts[0] !== "spotify") {
|
if (parts[0] !== "spotify") {
|
||||||
@ -59,7 +59,7 @@ const parseSpotifyURI = (uri) => {
|
|||||||
* @returns {typedefs.URIObject}
|
* @returns {typedefs.URIObject}
|
||||||
* @throws {TypeError} If the input is not a valid Spotify link
|
* @throws {TypeError} If the input is not a valid Spotify link
|
||||||
*/
|
*/
|
||||||
const parseSpotifyLink = (link) => {
|
export const parseSpotifyLink = (link) => {
|
||||||
const localPattern = /^https:\/\/open\.spotify\.com\/local\/([^\/]*)\/([^\/]*)\/([^\/]+)\/(\d+)$/;
|
const localPattern = /^https:\/\/open\.spotify\.com\/local\/([^\/]*)\/([^\/]*)\/([^\/]+)\/(\d+)$/;
|
||||||
const standardPattern = /^https:\/\/open\.spotify\.com\/([^\/]+)\/([^\/?]+)/;
|
const standardPattern = /^https:\/\/open\.spotify\.com\/([^\/]+)\/([^\/?]+)/;
|
||||||
|
|
||||||
@ -106,7 +106,7 @@ const parseSpotifyLink = (link) => {
|
|||||||
* @param {typedefs.URIObject} uriObj
|
* @param {typedefs.URIObject} uriObj
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
const buildSpotifyURI = (uriObj) => {
|
export const buildSpotifyURI = (uriObj) => {
|
||||||
if (uriObj.is_local) {
|
if (uriObj.is_local) {
|
||||||
const artist = encodeURIComponent(uriObj.artist ?? "");
|
const artist = encodeURIComponent(uriObj.artist ?? "");
|
||||||
const album = encodeURIComponent(uriObj.album ?? "");
|
const album = encodeURIComponent(uriObj.album ?? "");
|
||||||
@ -122,7 +122,7 @@ const buildSpotifyURI = (uriObj) => {
|
|||||||
* @param {typedefs.URIObject} uriObj
|
* @param {typedefs.URIObject} uriObj
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
const buildSpotifyLink = (uriObj) => {
|
export const buildSpotifyLink = (uriObj) => {
|
||||||
if (uriObj.is_local) {
|
if (uriObj.is_local) {
|
||||||
const artist = encodeURIComponent(uriObj.artist ?? "");
|
const artist = encodeURIComponent(uriObj.artist ?? "");
|
||||||
const album = encodeURIComponent(uriObj.album ?? "");
|
const album = encodeURIComponent(uriObj.album ?? "");
|
||||||
@ -132,10 +132,3 @@ const buildSpotifyLink = (uriObj) => {
|
|||||||
}
|
}
|
||||||
return `https://open.spotify.com/${uriObj.type}/${uriObj.id}`
|
return `https://open.spotify.com/${uriObj.type}/${uriObj.id}`
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
parseSpotifyURI,
|
|
||||||
parseSpotifyLink,
|
|
||||||
buildSpotifyURI,
|
|
||||||
buildSpotifyLink
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
const { validationResult } = require("express-validator");
|
import { validationResult } from "express-validator";
|
||||||
|
|
||||||
const { getNestedValuesString } = require("../utils/jsonTransformer");
|
import * as typedefs from "../typedefs.js";
|
||||||
const logger = require("../utils/logger")(module);
|
import { getNestedValuesString } from "../utils/jsonTransformer.js";
|
||||||
|
import curriedLogger from "../utils/logger.js";
|
||||||
const typedefs = require("../typedefs");
|
const logger = curriedLogger(import.meta);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refer: https://stackoverflow.com/questions/58848625/access-messages-in-express-validator
|
* Refer: https://stackoverflow.com/questions/58848625/access-messages-in-express-validator
|
||||||
@ -12,7 +12,7 @@ const typedefs = require("../typedefs");
|
|||||||
* @param {typedefs.Res} res
|
* @param {typedefs.Res} res
|
||||||
* @param {typedefs.Next} next
|
* @param {typedefs.Next} next
|
||||||
*/
|
*/
|
||||||
const validate = (req, res, next) => {
|
export const validate = (req, res, next) => {
|
||||||
const errors = validationResult(req);
|
const errors = validationResult(req);
|
||||||
if (errors.isEmpty()) {
|
if (errors.isEmpty()) {
|
||||||
return next();
|
return next();
|
||||||
@ -40,7 +40,3 @@ const validate = (req, res, next) => {
|
|||||||
logger.warn("invalid request", { extractedErrors });
|
logger.warn("invalid request", { extractedErrors });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
validate
|
|
||||||
};
|
|
||||||
|
|||||||
@ -1,13 +1,12 @@
|
|||||||
const { body, header, param, query } = require("express-validator");
|
import { body, header, param, query } from "express-validator";
|
||||||
|
import * as typedefs from "../typedefs.js";
|
||||||
const typedefs = require("../typedefs");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {typedefs.Req} req
|
* @param {typedefs.Req} req
|
||||||
* @param {typedefs.Res} res
|
* @param {typedefs.Res} res
|
||||||
* @param {typedefs.Next} next
|
* @param {typedefs.Next} next
|
||||||
*/
|
*/
|
||||||
const createLinkValidator = async (req, res, next) => {
|
export const createLinkValidator = async (req, res, next) => {
|
||||||
await body("from")
|
await body("from")
|
||||||
.notEmpty()
|
.notEmpty()
|
||||||
.withMessage("from not defined in body")
|
.withMessage("from not defined in body")
|
||||||
@ -23,9 +22,6 @@ const createLinkValidator = async (req, res, next) => {
|
|||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
export { createLinkValidator as removeLinkValidator };
|
||||||
createLinkValidator,
|
export { createLinkValidator as populateSingleLinkValidator };
|
||||||
removeLinkValidator: createLinkValidator,
|
export { createLinkValidator as pruneSingleLinkValidator };
|
||||||
populateSingleLinkValidator: createLinkValidator,
|
|
||||||
pruneSingleLinkValidator: createLinkValidator,
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,13 +1,12 @@
|
|||||||
const { body, header, param, query } = require("express-validator");
|
import { body, header, param, query } from "express-validator";
|
||||||
|
import * as typedefs from "../typedefs.js";
|
||||||
const typedefs = require("../typedefs");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {typedefs.Req} req
|
* @param {typedefs.Req} req
|
||||||
* @param {typedefs.Res} res
|
* @param {typedefs.Res} res
|
||||||
* @param {typedefs.Next} next
|
* @param {typedefs.Next} next
|
||||||
*/
|
*/
|
||||||
const getPlaylistDetailsValidator = async (req, res, next) => {
|
export const getPlaylistDetailsValidator = async (req, res, next) => {
|
||||||
await query("playlist_link")
|
await query("playlist_link")
|
||||||
.notEmpty()
|
.notEmpty()
|
||||||
.withMessage("playlist_link not defined in query")
|
.withMessage("playlist_link not defined in query")
|
||||||
@ -16,7 +15,3 @@ const getPlaylistDetailsValidator = async (req, res, next) => {
|
|||||||
.run(req);
|
.run(req);
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
getPlaylistDetailsValidator
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user