CJS -> ESM

This commit is contained in:
Kaushik Narayan R 2025-03-05 12:19:35 -07:00
parent 5a8611afbc
commit bcc39d5f38
33 changed files with 228 additions and 282 deletions

View File

@ -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")
};

View File

@ -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
};

View File

@ -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,
}

View File

@ -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
};

View File

@ -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;

View File

@ -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
View File

@ -0,0 +1,3 @@
// https://github.com/motdotla/dotenv/issues/133#issuecomment-255298822
import DotenvFlow from "dotenv-flow";
export default DotenvFlow.config();

View File

@ -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;

View File

@ -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
};

View File

@ -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,10 +140,10 @@ 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) {
if (Object.keys(error).length) {
res.status(500).send({ message: "Internal Server Error" });
logger.error("Error while logging out", { error });
return;
@ -160,10 +161,3 @@ const logout = async (req, res) => {
return;
}
}
module.exports = {
login,
callback,
refresh,
logout
};

View File

@ -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,
};

View File

@ -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
};

View File

@ -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);

View File

@ -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,
}

View File

@ -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");
}
};

View File

@ -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");
}
};

View File

@ -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;

View File

@ -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.
@ -22,4 +20,4 @@ module.exports = (sequelize, DataTypes) => {
modelName: "links",
});
return links;
};
};

View File

@ -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.
@ -22,4 +20,4 @@ module.exports = (sequelize, DataTypes) => {
modelName: "playlists",
});
return playlists;
};
};

View File

@ -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",

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -1,10 +1,10 @@
/**
* @typedef {import("module")} Module
*
*
* @typedef {import("express").Request} Req
* @typedef {import("express").Response} Res
* @typedef {import("express").NextFunction} Next
*
*
* @typedef {{
* type: string,
* is_local: boolean,
@ -14,25 +14,25 @@
* title?: string,
* duration?: number
* }} URIObject
*
*
* @typedef {{
* username: string,
* uri: string
* }} User
*
*
* @typedef {{
* name: string,
* uri: string,
* }} SimplifiedPlaylist
*
*
* @typedef {{
* name: string
* }} Album
*
*
* @typedef {{
* name: string
* }} Artist
*
*
* @typedef {{
* uri: string,
* name: string,
@ -40,18 +40,18 @@
* album: Album,
* is_local: boolean,
* }} Track
*
*
* @typedef {{
* added_at: string,
* track: Track,
* }} PlaylistTrack
*
*
* @typedef {{
* url: string,
* height: number,
* width: number
* }} ImageObject
*
*
* @typedef {{
* uri: string,
* name: string,
@ -64,4 +64,4 @@
* }} PlaylistDetails
*/
exports.unused = {};
export default {};

View File

@ -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;

View File

@ -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 = "";

View File

@ -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;

View File

@ -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
}

View File

@ -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;

View File

@ -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
}

View File

@ -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
};

View File

@ -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 };

View File

@ -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
}