mirror of
https://github.com/20kaushik02/spotify-manager.git
synced 2025-12-06 07: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");
|
||||
module.exports = {
|
||||
"config": path.resolve("config", "sequelize.js")
|
||||
import { resolve } from "path";
|
||||
export default {
|
||||
"config": resolve("config", "sequelize.js")
|
||||
};
|
||||
|
||||
18
api/axios.js
18
api/axios.js
@ -1,10 +1,11 @@
|
||||
const axios = require("axios");
|
||||
const rateLimit = require("axios-rate-limit");
|
||||
import axios from "axios";
|
||||
import rateLimit from "axios-rate-limit";
|
||||
|
||||
const { baseAPIURL, accountsAPIURL } = require("../constants");
|
||||
const logger = require("../utils/logger")(module);
|
||||
import { baseAPIURL, accountsAPIURL } from "../constants.js";
|
||||
import curriedLogger from "../utils/logger.js";
|
||||
const logger = curriedLogger(import.meta);
|
||||
|
||||
const authInstance = axios.create({
|
||||
export const authInstance = axios.create({
|
||||
baseURL: accountsAPIURL,
|
||||
timeout: 20000,
|
||||
headers: {
|
||||
@ -21,7 +22,7 @@ const uncappedAxiosInstance = axios.create({
|
||||
},
|
||||
});
|
||||
|
||||
const axiosInstance = rateLimit(uncappedAxiosInstance, {
|
||||
export const axiosInstance = rateLimit(uncappedAxiosInstance, {
|
||||
maxRequests: 10,
|
||||
perMilliseconds: 5000,
|
||||
});
|
||||
@ -50,8 +51,3 @@ axiosInstance.interceptors.response.use(
|
||||
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: ";
|
||||
|
||||
@ -17,7 +18,7 @@ const logPrefix = "Spotify API: ";
|
||||
* @param {any} data request body
|
||||
* @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;
|
||||
config.headers = { ...config.headers, ...req.sessHeaders };
|
||||
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,
|
||||
"GET", "/me",
|
||||
{ headers: { Authorization: `Bearer ${req.session.accessToken}` } }
|
||||
@ -70,7 +71,7 @@ const getUserProfile = async (req, res) => {
|
||||
return res.headersSent ? null : response.data;
|
||||
}
|
||||
|
||||
const getUserPlaylistsFirstPage = async (req, res) => {
|
||||
export const getUserPlaylistsFirstPage = async (req, res) => {
|
||||
const response = await singleRequest(req, res,
|
||||
"GET",
|
||||
`/users/${req.session.user.id}/playlists`,
|
||||
@ -83,13 +84,13 @@ const getUserPlaylistsFirstPage = async (req, res) => {
|
||||
return res.headersSent ? null : response.data;
|
||||
}
|
||||
|
||||
const getUserPlaylistsNextPage = async (req, res, nextURL) => {
|
||||
export const getUserPlaylistsNextPage = async (req, res, nextURL) => {
|
||||
const response = await singleRequest(
|
||||
req, res, "GET", nextURL);
|
||||
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,
|
||||
"GET",
|
||||
`/playlists/${playlistID}/`,
|
||||
@ -101,13 +102,13 @@ const getPlaylistDetailsFirstPage = async (req, res, initialFields, playlistID)
|
||||
return res.headersSent ? null : response.data;
|
||||
}
|
||||
|
||||
const getPlaylistDetailsNextPage = async (req, res, nextURL) => {
|
||||
export const getPlaylistDetailsNextPage = async (req, res, nextURL) => {
|
||||
const response = await singleRequest(
|
||||
req, res, "GET", nextURL);
|
||||
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,
|
||||
"POST",
|
||||
`/playlists/${playlistID}/tracks`,
|
||||
@ -117,7 +118,7 @@ const addItemsToPlaylist = async (req, res, nextBatch, playlistID) => {
|
||||
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
|
||||
// but see here: https://github.com/spotipy-dev/spotipy/issues/95#issuecomment-2263634801
|
||||
const response = await singleRequest(req, res,
|
||||
@ -130,7 +131,7 @@ const removeItemsFromPlaylist = async (req, res, nextBatch, playlistID, snapshot
|
||||
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)"];
|
||||
|
||||
const checkFromData = await getPlaylistDetailsFirstPage(req, res, checkFields.join(), playlistID);
|
||||
@ -150,15 +151,3 @@ const checkPlaylistEditable = async (req, res, playlistID, userID) => {
|
||||
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.Res} res
|
||||
*/
|
||||
const __controller_func = async (req, res) => {
|
||||
export const __controller_func = async (req, res) => {
|
||||
try {
|
||||
|
||||
} catch (error) {
|
||||
@ -15,7 +16,3 @@ const __controller_func = async (req, res) => {
|
||||
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(
|
||||
|
||||
@ -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.Res} res
|
||||
* @param {typedefs.Next} next
|
||||
*/
|
||||
const __validator_func = async (req, res, next) => {
|
||||
export const __validator_func = async (req, res, next) => {
|
||||
await body("field_name")
|
||||
.notEmpty()
|
||||
.withMessage("field_name not defined in body")
|
||||
@ -15,7 +15,3 @@ const __validator_func = async (req, res, 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 = {
|
||||
development: {
|
||||
@ -8,7 +9,7 @@ const connConfigs = {
|
||||
host: process.env.DB_HOST || "127.0.0.1",
|
||||
port: process.env.DB_PORT || 5432,
|
||||
},
|
||||
staging: {
|
||||
test: {
|
||||
use_env_variable: "DB_URL", // use connection string for non-dev env
|
||||
},
|
||||
production: {
|
||||
@ -25,4 +26,4 @@ for (const conf in connConfigs) {
|
||||
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";
|
||||
const baseAPIURL = "https://api.spotify.com/v1";
|
||||
const sessionName = "spotify-manager";
|
||||
const stateKey = "spotify_auth_state";
|
||||
export const accountsAPIURL = "https://accounts.spotify.com";
|
||||
export const baseAPIURL = "https://api.spotify.com/v1";
|
||||
export const sessionName = "spotify-manager";
|
||||
export const stateKey = "spotify_auth_state";
|
||||
|
||||
const scopes = {
|
||||
export const scopes = {
|
||||
// ImageUpload: "ugc-image-upload",
|
||||
AccessPrivatePlaylists: "playlist-read-private",
|
||||
AccessCollaborativePlaylists: "playlist-read-collaborative",
|
||||
@ -15,11 +15,3 @@ const scopes = {
|
||||
AccessLibrary: "user-library-read",
|
||||
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");
|
||||
const { scopes, stateKey, accountsAPIURL, sessionName } = require("../constants");
|
||||
import * as typedefs from "../typedefs.js";
|
||||
import { scopes, stateKey, accountsAPIURL, sessionName } from "../constants.js";
|
||||
|
||||
const generateRandString = require("../utils/generateRandString");
|
||||
const { getUserProfile } = require("../api/spotify");
|
||||
const logger = require("../utils/logger")(module);
|
||||
import generateRandString from "../utils/generateRandString.js";
|
||||
import { getUserProfile } from "../api/spotify.js";
|
||||
import curriedLogger from "../utils/logger.js";
|
||||
const logger = curriedLogger(import.meta);
|
||||
|
||||
/**
|
||||
* Stateful redirect to Spotify login with credentials
|
||||
* @param {typedefs.Req} req
|
||||
* @param {typedefs.Res} res
|
||||
*/
|
||||
const login = (_req, res) => {
|
||||
export const login = (_req, res) => {
|
||||
try {
|
||||
const state = generateRandString(16);
|
||||
res.cookie(stateKey, state);
|
||||
@ -41,7 +42,7 @@ const login = (_req, res) => {
|
||||
* @param {typedefs.Req} req
|
||||
* @param {typedefs.Res} res
|
||||
*/
|
||||
const callback = async (req, res) => {
|
||||
export const callback = async (req, res) => {
|
||||
try {
|
||||
const { code, state, error } = req.query;
|
||||
const storedState = req.cookies ? req.cookies[stateKey] : null;
|
||||
@ -104,7 +105,7 @@ const callback = async (req, res) => {
|
||||
* @param {typedefs.Req} req
|
||||
* @param {typedefs.Res} res
|
||||
*/
|
||||
const refresh = async (req, res) => {
|
||||
export const refresh = async (req, res) => {
|
||||
try {
|
||||
const authForm = {
|
||||
refresh_token: req.session.refreshToken,
|
||||
@ -139,7 +140,7 @@ const refresh = async (req, res) => {
|
||||
* @param {typedefs.Req} req
|
||||
* @param {typedefs.Res} res
|
||||
*/
|
||||
const logout = async (req, res) => {
|
||||
export const logout = async (req, res) => {
|
||||
try {
|
||||
const delSession = req.session.destroy((error) => {
|
||||
if (Object.keys(error).length) {
|
||||
@ -160,10 +161,3 @@ const logout = async (req, res) => {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
login,
|
||||
callback,
|
||||
refresh,
|
||||
logout
|
||||
};
|
||||
|
||||
@ -1,25 +1,25 @@
|
||||
const typedefs = require("../typedefs");
|
||||
const logger = require("../utils/logger")(module);
|
||||
import * as typedefs from "../typedefs.js";
|
||||
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");
|
||||
const { randomBool, sleep } = require("../utils/flake");
|
||||
const myGraph = require("../utils/graph");
|
||||
import { parseSpotifyLink } from "../utils/spotifyURITransformer.js";
|
||||
import { randomBool, sleep } from "../utils/flake.js";
|
||||
import myGraph from "../utils/graph.js";
|
||||
|
||||
const { Op } = require("sequelize");
|
||||
const { sequelize } = require("../models");
|
||||
/** @type {typedefs.Model} */
|
||||
const Playlists = require("../models").playlists;
|
||||
/** @type {typedefs.Model} */
|
||||
const Links = require("../models").links;
|
||||
import { Op } from "sequelize";
|
||||
|
||||
import models, { sequelize } from "../models/index.js";
|
||||
const Playlists = models.playlists;
|
||||
const Links = models.links;
|
||||
|
||||
/**
|
||||
* Sync user's Spotify data
|
||||
* @param {typedefs.Req} req
|
||||
* @param {typedefs.Res} res
|
||||
*/
|
||||
const updateUser = async (req, res) => {
|
||||
export const updateUser = async (req, res) => {
|
||||
try {
|
||||
let currentPlaylists = [];
|
||||
const uID = req.session.user.id;
|
||||
@ -177,7 +177,7 @@ const updateUser = async (req, res) => {
|
||||
* @param {typedefs.Req} req
|
||||
* @param {typedefs.Res} res
|
||||
*/
|
||||
const fetchUser = async (req, res) => {
|
||||
export const fetchUser = async (req, res) => {
|
||||
try {
|
||||
// if (randomBool(0.5)) {
|
||||
// res.status(404).send({ message: "Not Found" });
|
||||
@ -219,7 +219,7 @@ const fetchUser = async (req, res) => {
|
||||
* @param {typedefs.Req} req
|
||||
* @param {typedefs.Res} res
|
||||
*/
|
||||
const createLink = async (req, res) => {
|
||||
export const createLink = async (req, res) => {
|
||||
try {
|
||||
// await sleep(1000);
|
||||
const uID = req.session.user.id;
|
||||
@ -310,7 +310,7 @@ const createLink = async (req, res) => {
|
||||
* @param {typedefs.Req} req
|
||||
* @param {typedefs.Res} res
|
||||
*/
|
||||
const removeLink = async (req, res) => {
|
||||
export const removeLink = async (req, res) => {
|
||||
try {
|
||||
const uID = req.session.user.id;
|
||||
|
||||
@ -475,7 +475,7 @@ const _populateSingleLinkCore = async (req, res, link) => {
|
||||
* @param {typedefs.Req} req
|
||||
* @param {typedefs.Res} res
|
||||
*/
|
||||
const populateSingleLink = async (req, res) => {
|
||||
export const populateSingleLink = async (req, res) => {
|
||||
try {
|
||||
const uID = req.session.user.id;
|
||||
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.Res} res
|
||||
*/
|
||||
const pruneSingleLink = async (req, res) => {
|
||||
export const pruneSingleLink = async (req, res) => {
|
||||
try {
|
||||
const uID = req.session.user.id;
|
||||
const link = { from: req.body.from, to: req.body.to };
|
||||
@ -643,12 +643,3 @@ const pruneSingleLink = async (req, res) => {
|
||||
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");
|
||||
const { getUserPlaylistsFirstPage, getUserPlaylistsNextPage, getPlaylistDetailsFirstPage, getPlaylistDetailsNextPage } = require("../api/spotify");
|
||||
const { parseSpotifyLink } = require("../utils/spotifyURITransformer");
|
||||
import * as typedefs from "../typedefs.js";
|
||||
import { getUserPlaylistsFirstPage, getUserPlaylistsNextPage, getPlaylistDetailsFirstPage, getPlaylistDetailsNextPage } from "../api/spotify.js";
|
||||
import { parseSpotifyLink } from "../utils/spotifyURITransformer.js";
|
||||
|
||||
/**
|
||||
* Retrieve list of all of user's playlists
|
||||
* @param {typedefs.Req} req
|
||||
* @param {typedefs.Res} res
|
||||
*/
|
||||
const fetchUserPlaylists = async (req, res) => {
|
||||
export const fetchUserPlaylists = async (req, res) => {
|
||||
try {
|
||||
let userPlaylists = {};
|
||||
|
||||
@ -65,7 +66,7 @@ const fetchUserPlaylists = async (req, res) => {
|
||||
* @param {typedefs.Req} req
|
||||
* @param {typedefs.Res} res
|
||||
*/
|
||||
const fetchPlaylistDetails = async (req, res) => {
|
||||
export const fetchPlaylistDetails = async (req, res) => {
|
||||
try {
|
||||
let playlist = {};
|
||||
/** @type {typedefs.URIObject} */
|
||||
@ -152,8 +153,3 @@ const fetchPlaylistDetails = async (req, res) => {
|
||||
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");
|
||||
const express = require("express");
|
||||
const session = require("express-session");
|
||||
import { promisify } from "util";
|
||||
import express from "express";
|
||||
import session from "express-session";
|
||||
|
||||
const cors = require("cors");
|
||||
const cookieParser = require("cookie-parser");
|
||||
const helmet = require("helmet");
|
||||
import cors from "cors";
|
||||
import cookieParser from "cookie-parser";
|
||||
import helmet from "helmet";
|
||||
|
||||
const { createClient } = require('redis');
|
||||
const { RedisStore } = require("connect-redis");
|
||||
import { createClient } from 'redis';
|
||||
import { RedisStore } from "connect-redis";
|
||||
|
||||
const { sessionName } = require("./constants");
|
||||
const db = require("./models");
|
||||
import { sessionName } from "./constants.js";
|
||||
import { sequelize } from "./models/index.js";
|
||||
|
||||
const { isAuthenticated } = require("./middleware/authCheck");
|
||||
const { getUserProfile } = require("./api/spotify");
|
||||
import { isAuthenticated } from "./middleware/authCheck.js";
|
||||
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();
|
||||
|
||||
@ -73,7 +74,7 @@ app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
|
||||
// Static
|
||||
app.use(express.static(__dirname + "/static"));
|
||||
app.use(express.static(import.meta.dirname + "/static"));
|
||||
|
||||
// Healthcheck
|
||||
app.use("/health", (req, res) => {
|
||||
@ -92,11 +93,13 @@ app.use("/auth-health", isAuthenticated, async (req, res) => {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
import authRoutes from "./routes/auth.js";
|
||||
import playlistRoutes from "./routes/playlists.js";
|
||||
import operationRoutes from "./routes/operations.js";
|
||||
// Routes
|
||||
app.use("/api/auth/", require("./routes/auth"));
|
||||
app.use("/api/playlists", isAuthenticated, require("./routes/playlists"));
|
||||
app.use("/api/operations", isAuthenticated, require("./routes/operations"));
|
||||
app.use("/api/auth/", authRoutes);
|
||||
app.use("/api/playlists", isAuthenticated, playlistRoutes);
|
||||
app.use("/api/operations", isAuthenticated, operationRoutes);
|
||||
|
||||
// Fallbacks
|
||||
app.use((req, res) => {
|
||||
@ -119,8 +122,8 @@ const cleanupFunc = (signal) => {
|
||||
|
||||
Promise.allSettled([
|
||||
redisClient.disconnect,
|
||||
db.sequelize.close(),
|
||||
util.promisify(server.close),
|
||||
sequelize.close(),
|
||||
promisify(server.close),
|
||||
]).then(() => {
|
||||
logger.info("Cleaned up, exiting.");
|
||||
process.exit(0);
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
const { sessionName } = require("../constants");
|
||||
const typedefs = require("../typedefs");
|
||||
const logger = require("../utils/logger")(module);
|
||||
import { sessionName } from "../constants.js";
|
||||
import * as typedefs from "../typedefs.js";
|
||||
import curriedLogger from "../utils/logger.js";
|
||||
const logger = curriedLogger(import.meta);
|
||||
|
||||
/**
|
||||
* middleware to check if access token is present
|
||||
@ -8,7 +9,7 @@ const logger = require("../utils/logger")(module);
|
||||
* @param {typedefs.Res} res
|
||||
* @param {typedefs.Next} next
|
||||
*/
|
||||
const isAuthenticated = (req, res, next) => {
|
||||
export const isAuthenticated = (req, res, next) => {
|
||||
if (req.session.accessToken) {
|
||||
req.sessHeaders = {
|
||||
"Authorization": `Bearer ${req.session.accessToken}`,
|
||||
@ -30,7 +31,3 @@ const isAuthenticated = (req, res, next) => {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isAuthenticated,
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
/** @type {import("sequelize-cli").Migration} */
|
||||
module.exports = {
|
||||
async up(queryInterface, Sequelize) {
|
||||
export default {
|
||||
up: async function (queryInterface, Sequelize) {
|
||||
await queryInterface.createTable("playlists", {
|
||||
id: {
|
||||
allowNull: false,
|
||||
@ -28,7 +28,7 @@ module.exports = {
|
||||
}
|
||||
});
|
||||
},
|
||||
async down(queryInterface, Sequelize) {
|
||||
down: async function (queryInterface, Sequelize) {
|
||||
await queryInterface.dropTable("playlists");
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
/** @type {import("sequelize-cli").Migration} */
|
||||
module.exports = {
|
||||
async up(queryInterface, Sequelize) {
|
||||
export default {
|
||||
up: async function (queryInterface, Sequelize) {
|
||||
await queryInterface.createTable("links", {
|
||||
id: {
|
||||
allowNull: false,
|
||||
@ -28,7 +28,7 @@ module.exports = {
|
||||
}
|
||||
});
|
||||
},
|
||||
async down(queryInterface, Sequelize) {
|
||||
down: async function (queryInterface, Sequelize) {
|
||||
await queryInterface.dropTable("links");
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,11 +1,16 @@
|
||||
"use strict";
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const Sequelize = require("sequelize");
|
||||
const logger = require("../utils/logger")(module);
|
||||
const basename = path.basename(__filename);
|
||||
import { readdirSync } from "fs";
|
||||
import { basename as _basename } from "path";
|
||||
const basename = _basename(import.meta.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 config = require(__dirname + "/../config/sequelize.js")[env];
|
||||
const config = seqConfig[env];
|
||||
const db = {};
|
||||
|
||||
let sequelize;
|
||||
@ -26,15 +31,22 @@ if (config.use_env_variable) {
|
||||
})();
|
||||
|
||||
// Read model definitions from folder
|
||||
fs
|
||||
.readdirSync(__dirname)
|
||||
.filter(file => {
|
||||
return (file.indexOf(".") !== 0) && (file !== basename) && (file.slice(-3) === ".js");
|
||||
})
|
||||
.forEach(file => {
|
||||
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
|
||||
db[model.name] = model;
|
||||
});
|
||||
const modelFiles = readdirSync(import.meta.dirname)
|
||||
.filter(
|
||||
(file) => file.indexOf('.') !== 0
|
||||
&& file !== basename
|
||||
&& file.slice(-3) === '.js',
|
||||
);
|
||||
|
||||
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
|
||||
Object.keys(db).forEach(modelName => {
|
||||
@ -43,7 +55,9 @@ Object.keys(db).forEach(modelName => {
|
||||
}
|
||||
});
|
||||
|
||||
// clean ts up
|
||||
db.sequelize = sequelize;
|
||||
db.Sequelize = Sequelize;
|
||||
|
||||
module.exports = db;
|
||||
export { sequelize as sequelize };
|
||||
export { Sequelize as Sequelize };
|
||||
export default db;
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
"use strict";
|
||||
const {
|
||||
Model
|
||||
} = require("sequelize");
|
||||
module.exports = (sequelize, DataTypes) => {
|
||||
import { Model } from "sequelize";
|
||||
export default (sequelize, DataTypes) => {
|
||||
class links extends Model {
|
||||
/**
|
||||
* Helper method for defining associations.
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
"use strict";
|
||||
const {
|
||||
Model
|
||||
} = require("sequelize");
|
||||
module.exports = (sequelize, DataTypes) => {
|
||||
import { Model } from "sequelize";
|
||||
export default (sequelize, DataTypes) => {
|
||||
class playlists extends Model {
|
||||
/**
|
||||
* Helper method for defining associations.
|
||||
|
||||
@ -2,7 +2,8 @@
|
||||
"name": "spotify-manager",
|
||||
"version": "0",
|
||||
"description": "Personal Spotify playlist manager",
|
||||
"main": "index.js",
|
||||
"exports": "./index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"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",
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
const router = require("express").Router();
|
||||
import { Router } from "express";
|
||||
const router = Router();
|
||||
|
||||
const { login, callback, refresh, logout } = require("../controllers/auth");
|
||||
const { isAuthenticated } = require("../middleware/authCheck");
|
||||
const validator = require("../validators");
|
||||
import { login, callback, refresh, logout } from "../controllers/auth.js";
|
||||
import { isAuthenticated } from "../middleware/authCheck.js";
|
||||
import { validate } from "../validators/index.js";
|
||||
|
||||
router.get(
|
||||
"/login",
|
||||
@ -25,4 +26,4 @@ router.get(
|
||||
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");
|
||||
const { validate } = require("../validators");
|
||||
const { createLinkValidator, removeLinkValidator, populateSingleLinkValidator, pruneSingleLinkValidator } = require("../validators/operations");
|
||||
import { updateUser, fetchUser, createLink, removeLink, populateSingleLink, pruneSingleLink } from "../controllers/operations.js";
|
||||
import { createLinkValidator, removeLinkValidator, populateSingleLinkValidator, pruneSingleLinkValidator } from "../validators/operations.js";
|
||||
|
||||
import { validate } from "../validators/index.js";
|
||||
|
||||
router.put(
|
||||
"/update",
|
||||
@ -42,4 +44,4 @@ router.put(
|
||||
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");
|
||||
const { getPlaylistDetailsValidator } = require("../validators/playlists");
|
||||
const { validate } = require("../validators");
|
||||
import { fetchUserPlaylists, fetchPlaylistDetails } from "../controllers/playlists.js";
|
||||
import { getPlaylistDetailsValidator } from "../validators/playlists.js";
|
||||
|
||||
import { validate } from "../validators/index.js";
|
||||
|
||||
router.get(
|
||||
"/me",
|
||||
@ -16,4 +18,4 @@ router.get(
|
||||
fetchPlaylistDetails
|
||||
);
|
||||
|
||||
module.exports = router;
|
||||
export default router;
|
||||
|
||||
@ -64,4 +64,4 @@
|
||||
* }} 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;
|
||||
|
||||
module.exports = {
|
||||
sleep, randomBool
|
||||
};
|
||||
export const randomBool = (chance_of_failure = 0.25) => Math.random() < chance_of_failure;
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
* @param {number} length The length of the string
|
||||
* @return {string} The generated string
|
||||
*/
|
||||
module.exports = (length) => {
|
||||
export default (length) => {
|
||||
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
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.
|
||||
@ -21,7 +22,7 @@ const typedefs = require("../typedefs");
|
||||
* console.log(g.detectCycle()); // true
|
||||
* ```
|
||||
*/
|
||||
class myGraph {
|
||||
export class myGraph {
|
||||
/**
|
||||
* @param {string[]} nodes Graph nodes IDs
|
||||
* @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
|
||||
* @returns {string}
|
||||
*/
|
||||
const getNestedValuesString = (obj, delimiter = ", ") => {
|
||||
export const getNestedValuesString = (obj, delimiter = ", ") => {
|
||||
let values = [];
|
||||
for (key in obj) {
|
||||
if (typeof obj[key] !== "object") {
|
||||
@ -17,7 +17,3 @@ const getNestedValuesString = (obj, 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 typedefs = require("../typedefs");
|
||||
|
||||
const getLabel = (callingModule) => {
|
||||
if (!callingModule.filename) return "repl";
|
||||
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
|
||||
* @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({
|
||||
levels: config.npm.levels,
|
||||
format: combine(
|
||||
@ -49,12 +49,12 @@ const curriedLogger = (callingModule) => {
|
||||
transports: [
|
||||
new transports.Console({ level: "info" }),
|
||||
new transports.File({
|
||||
filename: __dirname + "/../logs/debug.log",
|
||||
filename: import.meta.dirname + "/../logs/debug.log",
|
||||
level: "debug",
|
||||
maxsize: 10485760,
|
||||
}),
|
||||
new transports.File({
|
||||
filename: __dirname + "/../logs/error.log",
|
||||
filename: import.meta.dirname + "/../logs/error.log",
|
||||
level: "error",
|
||||
maxsize: 1048576,
|
||||
}),
|
||||
@ -64,4 +64,4 @@ const curriedLogger = (callingModule) => {
|
||||
return winstonLogger;
|
||||
}
|
||||
|
||||
module.exports = curriedLogger;
|
||||
export default curriedLogger;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
const typedefs = require("../typedefs");
|
||||
import * as typedefs from "../typedefs.js";
|
||||
|
||||
/** @type {RegExp} */
|
||||
const base62Pattern = /^[A-Za-z0-9]+$/;
|
||||
@ -10,7 +10,7 @@ const base62Pattern = /^[A-Za-z0-9]+$/;
|
||||
* @returns {typedefs.URIObject}
|
||||
* @throws {TypeError} If the input is not a valid Spotify URI
|
||||
*/
|
||||
const parseSpotifyURI = (uri) => {
|
||||
export const parseSpotifyURI = (uri) => {
|
||||
const parts = uri.split(":");
|
||||
|
||||
if (parts[0] !== "spotify") {
|
||||
@ -59,7 +59,7 @@ const parseSpotifyURI = (uri) => {
|
||||
* @returns {typedefs.URIObject}
|
||||
* @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 standardPattern = /^https:\/\/open\.spotify\.com\/([^\/]+)\/([^\/?]+)/;
|
||||
|
||||
@ -106,7 +106,7 @@ const parseSpotifyLink = (link) => {
|
||||
* @param {typedefs.URIObject} uriObj
|
||||
* @returns {string}
|
||||
*/
|
||||
const buildSpotifyURI = (uriObj) => {
|
||||
export const buildSpotifyURI = (uriObj) => {
|
||||
if (uriObj.is_local) {
|
||||
const artist = encodeURIComponent(uriObj.artist ?? "");
|
||||
const album = encodeURIComponent(uriObj.album ?? "");
|
||||
@ -122,7 +122,7 @@ const buildSpotifyURI = (uriObj) => {
|
||||
* @param {typedefs.URIObject} uriObj
|
||||
* @returns {string}
|
||||
*/
|
||||
const buildSpotifyLink = (uriObj) => {
|
||||
export const buildSpotifyLink = (uriObj) => {
|
||||
if (uriObj.is_local) {
|
||||
const artist = encodeURIComponent(uriObj.artist ?? "");
|
||||
const album = encodeURIComponent(uriObj.album ?? "");
|
||||
@ -132,10 +132,3 @@ const buildSpotifyLink = (uriObj) => {
|
||||
}
|
||||
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");
|
||||
const logger = require("../utils/logger")(module);
|
||||
|
||||
const typedefs = require("../typedefs");
|
||||
import * as typedefs from "../typedefs.js";
|
||||
import { getNestedValuesString } from "../utils/jsonTransformer.js";
|
||||
import curriedLogger from "../utils/logger.js";
|
||||
const logger = curriedLogger(import.meta);
|
||||
|
||||
/**
|
||||
* 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.Next} next
|
||||
*/
|
||||
const validate = (req, res, next) => {
|
||||
export const validate = (req, res, next) => {
|
||||
const errors = validationResult(req);
|
||||
if (errors.isEmpty()) {
|
||||
return next();
|
||||
@ -40,7 +40,3 @@ const validate = (req, res, next) => {
|
||||
logger.warn("invalid request", { extractedErrors });
|
||||
return;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
validate
|
||||
};
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
const { body, header, param, query } = require("express-validator");
|
||||
|
||||
const typedefs = require("../typedefs");
|
||||
import { body, header, param, query } from "express-validator";
|
||||
import * as typedefs from "../typedefs.js";
|
||||
|
||||
/**
|
||||
* @param {typedefs.Req} req
|
||||
* @param {typedefs.Res} res
|
||||
* @param {typedefs.Next} next
|
||||
*/
|
||||
const createLinkValidator = async (req, res, next) => {
|
||||
export const createLinkValidator = async (req, res, next) => {
|
||||
await body("from")
|
||||
.notEmpty()
|
||||
.withMessage("from not defined in body")
|
||||
@ -23,9 +22,6 @@ const createLinkValidator = async (req, res, next) => {
|
||||
next();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createLinkValidator,
|
||||
removeLinkValidator: createLinkValidator,
|
||||
populateSingleLinkValidator: createLinkValidator,
|
||||
pruneSingleLinkValidator: createLinkValidator,
|
||||
}
|
||||
export { createLinkValidator as removeLinkValidator };
|
||||
export { createLinkValidator as populateSingleLinkValidator };
|
||||
export { createLinkValidator as pruneSingleLinkValidator };
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
const { body, header, param, query } = require("express-validator");
|
||||
|
||||
const typedefs = require("../typedefs");
|
||||
import { body, header, param, query } from "express-validator";
|
||||
import * as typedefs from "../typedefs.js";
|
||||
|
||||
/**
|
||||
* @param {typedefs.Req} req
|
||||
* @param {typedefs.Res} res
|
||||
* @param {typedefs.Next} next
|
||||
*/
|
||||
const getPlaylistDetailsValidator = async (req, res, next) => {
|
||||
export const getPlaylistDetailsValidator = async (req, res, next) => {
|
||||
await query("playlist_link")
|
||||
.notEmpty()
|
||||
.withMessage("playlist_link not defined in query")
|
||||
@ -16,7 +15,3 @@ const getPlaylistDetailsValidator = async (req, res, next) => {
|
||||
.run(req);
|
||||
next();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getPlaylistDetailsValidator
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user