mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-10 02:54:15 +00:00
implement music_server
This commit is contained in:
parent
e9deddafec
commit
0911883453
@ -6,32 +6,107 @@ import http from "http"
|
||||
import EventEmitter from "@foxify/events"
|
||||
|
||||
import ComtyClient from "@classes/ComtyClient"
|
||||
|
||||
import routes from "./routes"
|
||||
import DbManager from "@classes/DbManager"
|
||||
import RedisClient from "@classes/RedisClient"
|
||||
import StorageClient from "@classes/StorageClient"
|
||||
|
||||
import RoomServer from "./roomsServer"
|
||||
|
||||
export default class Server {
|
||||
constructor(options = {}) {
|
||||
this.app = express()
|
||||
this.httpServer = http.createServer(this.app)
|
||||
|
||||
this.websocketServer = new RoomServer(this.httpServer)
|
||||
import pkg from "../package.json"
|
||||
|
||||
export default class Server {
|
||||
static useMiddlewaresOrder = ["useLogger", "useCors", "useAuth"]
|
||||
|
||||
eventBus = global.eventBus = new EventEmitter()
|
||||
|
||||
internalRouter = express.Router()
|
||||
|
||||
constructor(options = {}) {
|
||||
this.server = express()
|
||||
this._http = http.createServer(this.server)
|
||||
|
||||
this.websocketServer = new RoomServer(this._http)
|
||||
|
||||
this.options = {
|
||||
listenHost: process.env.LISTEN_HOST || "0.0.0.0",
|
||||
listenPort: process.env.LISTEN_PORT || 3050,
|
||||
listenHost: process.env.HTTP_LISTEN_IP ?? "0.0.0.0",
|
||||
listenPort: process.env.HTTP_LISTEN_PORT ?? 3050,
|
||||
...options
|
||||
}
|
||||
}
|
||||
|
||||
comty = global.comty = ComtyClient()
|
||||
|
||||
eventBus = global.eventBus = new EventEmitter()
|
||||
db = new DbManager()
|
||||
|
||||
redis = global.redis = RedisClient()
|
||||
|
||||
storage = global.storage = StorageClient()
|
||||
|
||||
async __registerControllers() {
|
||||
let controllersPath = fs.readdirSync(path.resolve(__dirname, "controllers"))
|
||||
|
||||
this.internalRouter.routes = []
|
||||
|
||||
for await (const controllerPath of controllersPath) {
|
||||
const controller = require(path.resolve(__dirname, "controllers", controllerPath)).default
|
||||
|
||||
if (!controller) {
|
||||
console.error(`Controller ${controllerPath} not found.`)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
const handler = await controller(express.Router())
|
||||
|
||||
if (!handler) {
|
||||
console.error(`Controller ${controllerPath} returning not valid handler.`)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// let middlewares = []
|
||||
|
||||
// if (Array.isArray(handler.useMiddlewares)) {
|
||||
// middlewares = await getMiddlewares(handler.useMiddlewares)
|
||||
// }
|
||||
|
||||
// for (const middleware of middlewares) {
|
||||
// handler.router.use(middleware)
|
||||
// }
|
||||
|
||||
this.internalRouter.use(handler.path ?? "/", handler.router)
|
||||
|
||||
this.internalRouter.routes.push({
|
||||
path: handler.path ?? "/",
|
||||
routers: handler.router.routes
|
||||
})
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
async __registerInternalMiddlewares() {
|
||||
let middlewaresPath = fs.readdirSync(path.resolve(__dirname, "useMiddlewares"))
|
||||
|
||||
// sort middlewares
|
||||
if (this.constructor.useMiddlewaresOrder) {
|
||||
middlewaresPath = middlewaresPath.sort((a, b) => {
|
||||
const aIndex = this.constructor.useMiddlewaresOrder.indexOf(a.replace(".js", ""))
|
||||
const bIndex = this.constructor.useMiddlewaresOrder.indexOf(b.replace(".js", ""))
|
||||
|
||||
if (aIndex === -1) {
|
||||
return 1
|
||||
}
|
||||
|
||||
if (bIndex === -1) {
|
||||
return -1
|
||||
}
|
||||
|
||||
return aIndex - bIndex
|
||||
})
|
||||
}
|
||||
|
||||
for await (const middlewarePath of middlewaresPath) {
|
||||
const middleware = require(path.resolve(__dirname, "useMiddlewares", middlewarePath)).default
|
||||
|
||||
@ -41,31 +116,42 @@ export default class Server {
|
||||
continue
|
||||
}
|
||||
|
||||
this.app.use(middleware)
|
||||
this.server.use(middleware)
|
||||
}
|
||||
}
|
||||
|
||||
registerRoutes() {
|
||||
routes.forEach((route) => {
|
||||
const order = []
|
||||
__registerInternalRoutes() {
|
||||
this.internalRouter.get("/", (req, res) => {
|
||||
return res.status(200).json({
|
||||
name: pkg.name,
|
||||
version: pkg.version,
|
||||
})
|
||||
})
|
||||
|
||||
if (route.middlewares) {
|
||||
route.middlewares.forEach((middleware) => {
|
||||
order.push(middleware)
|
||||
})
|
||||
}
|
||||
this.internalRouter.get("/_routes", (req, res) => {
|
||||
return res.status(200).json(this.__getRegisteredRoutes(this.internalRouter.routes))
|
||||
})
|
||||
|
||||
order.push(route.routes)
|
||||
|
||||
this.app.use(route.use, ...order)
|
||||
this.internalRouter.get("*", (req, res) => {
|
||||
return res.status(404).json({
|
||||
error: "Not found",
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async registerBaseRoute() {
|
||||
await this.app.get("/", async (req, res) => {
|
||||
return res.json({
|
||||
uptimeMinutes: Math.floor(process.uptime() / 60),
|
||||
})
|
||||
__getRegisteredRoutes(router) {
|
||||
return router.map((entry) => {
|
||||
if (Array.isArray(entry.routers)) {
|
||||
return {
|
||||
path: entry.path,
|
||||
routes: this.__getRegisteredRoutes(entry.routers),
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
method: entry.method,
|
||||
path: entry.path,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -74,15 +160,22 @@ export default class Server {
|
||||
|
||||
await this.websocketServer.initialize()
|
||||
|
||||
// initialize clients
|
||||
await this.db.initialize()
|
||||
await this.redis.initialize()
|
||||
await this.storage.initialize()
|
||||
|
||||
// register controllers & middlewares
|
||||
await this.__registerControllers()
|
||||
await this.__registerInternalMiddlewares()
|
||||
await this.__registerInternalRoutes()
|
||||
|
||||
this.app.use(express.json({ extended: false }))
|
||||
this.app.use(express.urlencoded({ extended: true }))
|
||||
this.server.use(this.internalRouter)
|
||||
|
||||
await this.registerBaseRoute()
|
||||
await this.registerRoutes()
|
||||
this.server.use(express.json({ extended: false }))
|
||||
this.server.use(express.urlencoded({ extended: true }))
|
||||
|
||||
await this.httpServer.listen(this.options.listenPort, this.options.listenHost)
|
||||
await this._http.listen(this.options.listenPort, this.options.listenHost)
|
||||
|
||||
// calculate elapsed time
|
||||
const elapsedHrTime = process.hrtime(startHrTime)
|
||||
|
21
packages/music_server/src/controllers/lyrics/index.js
Normal file
21
packages/music_server/src/controllers/lyrics/index.js
Normal file
@ -0,0 +1,21 @@
|
||||
import path from "path"
|
||||
import createRoutesFromDirectory from "@utils/createRoutesFromDirectory"
|
||||
import getMiddlewares from "@utils/getMiddlewares"
|
||||
|
||||
export default async (router) => {
|
||||
// create a file based router
|
||||
const routesPath = path.resolve(__dirname, "routes")
|
||||
|
||||
const middlewares = await getMiddlewares(["withOptionalAuth"])
|
||||
|
||||
for (const middleware of middlewares) {
|
||||
router.use(middleware)
|
||||
}
|
||||
|
||||
router = createRoutesFromDirectory("routes", routesPath, router)
|
||||
|
||||
return {
|
||||
path: "/lyrics",
|
||||
router,
|
||||
}
|
||||
}
|
@ -0,0 +1,196 @@
|
||||
const syncLyricsProvider = `https://spotify-lyric-api.herokuapp.com`
|
||||
const canvasProvider = `https://api.delitefully.com/api/canvas`
|
||||
|
||||
import { Track } from "@models"
|
||||
import axios from "axios"
|
||||
|
||||
const clearQueryRegexs = [
|
||||
// remove titles with (feat. Something)
|
||||
new RegExp(/\(feat\..*\)/, "gi"),
|
||||
// remplace $ with S
|
||||
new RegExp(/\$/, "gi"),
|
||||
// remove special characters
|
||||
new RegExp(/[\(\)\[\]\$\&\*\#\@\!\%\+\=\_\-\:\;\'\"\,\.]/, "gi"),
|
||||
// remove words like "official video", "official audio", "official music video"
|
||||
new RegExp(/official\s(video|audio|music\svideo)/, "gi"),
|
||||
]
|
||||
|
||||
async function findSpotifyTrack({
|
||||
title,
|
||||
artist,
|
||||
sessionToken,
|
||||
} = {}) {
|
||||
let query = `${title} artist:${artist}`
|
||||
|
||||
// run clear query regexs
|
||||
for (const regex of clearQueryRegexs) {
|
||||
query = query.replace(regex, "")
|
||||
}
|
||||
|
||||
const { data } = await global.comty.instances.default({
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": `Bearer ${sessionToken}`,
|
||||
},
|
||||
params: {
|
||||
query: query,
|
||||
type: "track",
|
||||
},
|
||||
url: "/sync/spotify/search",
|
||||
}).catch((error) => {
|
||||
console.error(error.response.data)
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
if (!data) {
|
||||
return null
|
||||
}
|
||||
|
||||
return data.tracks.items[0]
|
||||
}
|
||||
|
||||
export default async (req, res) => {
|
||||
const noCache = req.query["no-cache"] === "true"
|
||||
|
||||
let track = await Track.findOne({
|
||||
_id: req.params.track_id,
|
||||
}).catch((error) => {
|
||||
return null
|
||||
})
|
||||
|
||||
if (!track) {
|
||||
return res.status(404).json({
|
||||
error: "Track not found",
|
||||
})
|
||||
}
|
||||
|
||||
console.log(track)
|
||||
|
||||
if (!track.lyricsEnabled){
|
||||
return res.status(403).json({
|
||||
error: "Lyrics disabled for this track",
|
||||
})
|
||||
}
|
||||
|
||||
//console.log("Found track", track)
|
||||
|
||||
track = track.toObject()
|
||||
|
||||
let lyricData = {
|
||||
syncType: null,
|
||||
lines: null,
|
||||
canvas_url: null,
|
||||
}
|
||||
|
||||
let cachedData = null
|
||||
|
||||
try {
|
||||
if (!noCache) {
|
||||
cachedData = await global.redis.get(`lyrics:${track._id}`)
|
||||
|
||||
if (cachedData) {
|
||||
lyricData = JSON.parse(cachedData)
|
||||
}
|
||||
|
||||
if (track.videoCanvas) {
|
||||
lyricData.canvas_url = track.videoCanvas
|
||||
}
|
||||
}
|
||||
|
||||
if (!cachedData) {
|
||||
// no cache, recosntruct lyrics data
|
||||
|
||||
// first check if track has spotify id to fetch the lyrics
|
||||
// if not present, try to search from spotify api and update the track with the spotify id
|
||||
if (!track.spotifyId) {
|
||||
if (!req.session) {
|
||||
throw new Error("Session not found and track has no spotify id")
|
||||
}
|
||||
|
||||
console.log("Fetching spotify track")
|
||||
|
||||
const spotifyTrack = await findSpotifyTrack({
|
||||
title: track.title,
|
||||
artist: track.artist,
|
||||
sessionToken: req.sessionToken,
|
||||
})
|
||||
|
||||
console.log(spotifyTrack)
|
||||
|
||||
if (spotifyTrack.id) {
|
||||
track.spotifyId = spotifyTrack.id
|
||||
|
||||
console.log("Updating track with spotify id")
|
||||
|
||||
const result = await Track.findOneAndUpdate({
|
||||
_id: track._id.toString(),
|
||||
}, {
|
||||
spotifyId: spotifyTrack.id,
|
||||
})
|
||||
|
||||
console.log(result)
|
||||
} else {
|
||||
throw new Error("Failed to search spotify id")
|
||||
}
|
||||
}
|
||||
|
||||
// ok now we have the spotify id, try to fetch the lyrics
|
||||
console.log("Fetching lyrics from sync provider, ID:", track.spotifyId)
|
||||
|
||||
let { data } = await axios.get(`${syncLyricsProvider}/?trackid=${track.spotifyId}`)
|
||||
|
||||
lyricData.syncType = data.syncType
|
||||
lyricData.lines = data.lines
|
||||
|
||||
// so we have the lyrics, now check if track has videoCanvas
|
||||
// if not present, try to fetch from canvas provider and update the track with the videoCanvas
|
||||
// handle errors silently
|
||||
if (track.videoCanvas) {
|
||||
lyricData.canvas_url = track.videoCanvas
|
||||
} else {
|
||||
try {
|
||||
console.log("Fetching canvas for id", track.spotifyId)
|
||||
|
||||
const { data } = await axios.get(`${canvasProvider}/${track.spotifyId}`)
|
||||
|
||||
lyricData.canvas_url = data.canvas_url
|
||||
|
||||
console.log("Updating track with canvas url")
|
||||
|
||||
await Track.findOneAndUpdate({
|
||||
_id: track._id.toString(),
|
||||
}, {
|
||||
videoCanvas: data.canvas_url,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
// force rewrite cache
|
||||
await global.redis.set(`lyrics:${track._id}`, JSON.stringify(data))
|
||||
|
||||
// check
|
||||
// const _cachedData = await global.redis.get(`lyrics:${track._id}`)
|
||||
|
||||
// console.log("Cached data", _cachedData, data)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
|
||||
return res.status(500).json({
|
||||
error: `Failed to generate lyrics for track ${track._id}`,
|
||||
})
|
||||
}
|
||||
|
||||
if (!lyricData.lines) {
|
||||
return res.status(404).json({
|
||||
error: "Lyrics not found",
|
||||
})
|
||||
}
|
||||
|
||||
//console.log("Lyrics data", lyricData)
|
||||
|
||||
return res.json(lyricData)
|
||||
}
|
14
packages/music_server/src/controllers/playlists/index.js
Normal file
14
packages/music_server/src/controllers/playlists/index.js
Normal file
@ -0,0 +1,14 @@
|
||||
import path from "path"
|
||||
import createRoutesFromDirectory from "@utils/createRoutesFromDirectory"
|
||||
|
||||
export default (router) => {
|
||||
// create a file based router
|
||||
const routesPath = path.resolve(__dirname, "routes")
|
||||
|
||||
// router = createRoutesFromDirectory("routes", routesPath, router)
|
||||
|
||||
return {
|
||||
path: "/playlists",
|
||||
router,
|
||||
}
|
||||
}
|
@ -9,7 +9,10 @@ global.isProduction = process.env.NODE_ENV === "production"
|
||||
import path from "path"
|
||||
import { registerBaseAliases } from "linebridge/dist/server"
|
||||
|
||||
globalThis["__root"] = path.resolve(__dirname)
|
||||
|
||||
const customAliases = {
|
||||
"root": globalThis["__root"],
|
||||
"@services": path.resolve(__dirname, "services"),
|
||||
}
|
||||
|
||||
@ -57,6 +60,32 @@ async function main() {
|
||||
const api = new API()
|
||||
|
||||
await api.initialize()
|
||||
|
||||
// kill on process exit
|
||||
process.on("exit", () => {
|
||||
api.server.close()
|
||||
process.exit(0)
|
||||
})
|
||||
|
||||
// kill on ctrl+c
|
||||
process.on("SIGINT", () => {
|
||||
api.server.close()
|
||||
process.exit(0)
|
||||
})
|
||||
|
||||
// kill on uncaught exceptions
|
||||
process.on("uncaughtException", (error) => {
|
||||
console.error(`🆘 [FATAL ERROR] >`, error)
|
||||
api.server.close()
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
// kill on unhandled rejections
|
||||
process.on("unhandledRejection", (error) => {
|
||||
console.error(`🆘 [FATAL ERROR] >`, error)
|
||||
api.server.close()
|
||||
process.exit(1)
|
||||
})
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
|
25
packages/music_server/src/middlewares/withAuth/index.js
Normal file
25
packages/music_server/src/middlewares/withAuth/index.js
Normal file
@ -0,0 +1,25 @@
|
||||
export default async function (req, res, next) {
|
||||
// extract authentification header
|
||||
let auth = req.headers.authorization
|
||||
|
||||
if (!auth) {
|
||||
return res.status(401).json({ error: "Unauthorized, missing token" })
|
||||
}
|
||||
|
||||
auth = auth.replace("Bearer ", "")
|
||||
|
||||
// check if authentification is valid
|
||||
const validation = await comty.rest.session.validateToken(auth).catch((error) => {
|
||||
return {
|
||||
valid: false,
|
||||
}
|
||||
})
|
||||
|
||||
if (!validation.valid) {
|
||||
return res.status(401).json({ error: "Unauthorized" })
|
||||
}
|
||||
|
||||
req.session = validation.session
|
||||
|
||||
return next()
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
export default async function (req, res, next) {
|
||||
// extract authentification header
|
||||
let auth = req.headers.authorization
|
||||
|
||||
if (!auth) {
|
||||
return next()
|
||||
}
|
||||
|
||||
auth = auth.replace("Bearer ", "")
|
||||
|
||||
// check if authentification is valid
|
||||
const validation = await comty.rest.session.validateToken(auth).catch((error) => {
|
||||
return {
|
||||
valid: false,
|
||||
}
|
||||
})
|
||||
|
||||
if (!validation.valid) {
|
||||
return next()
|
||||
}
|
||||
|
||||
req.sessionToken = auth
|
||||
req.session = validation.session
|
||||
|
||||
return next()
|
||||
}
|
19
packages/music_server/src/models/index.js
Executable file
19
packages/music_server/src/models/index.js
Executable file
@ -0,0 +1,19 @@
|
||||
import mongoose, { Schema } from "mongoose"
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
|
||||
function generateModels() {
|
||||
let models = {}
|
||||
|
||||
const dirs = fs.readdirSync(__dirname).filter(file => file !== "index.js")
|
||||
|
||||
dirs.forEach((file) => {
|
||||
const model = require(path.join(__dirname, file)).default
|
||||
|
||||
models[model.name] = mongoose.model(model.name, new Schema(model.schema), model.collection)
|
||||
})
|
||||
|
||||
return models
|
||||
}
|
||||
|
||||
module.exports = generateModels()
|
34
packages/music_server/src/models/playlist/index.js
Executable file
34
packages/music_server/src/models/playlist/index.js
Executable file
@ -0,0 +1,34 @@
|
||||
export default {
|
||||
name: "Playlist",
|
||||
collection: "playlists",
|
||||
schema: {
|
||||
user_id: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
description: {
|
||||
type: String
|
||||
},
|
||||
list: {
|
||||
type: Object,
|
||||
default: [],
|
||||
required: true
|
||||
},
|
||||
thumbnail: {
|
||||
type: String,
|
||||
default: "https://storage.ragestudio.net/comty-static-assets/default_song.png"
|
||||
},
|
||||
created_at: {
|
||||
type: Date,
|
||||
required: true
|
||||
},
|
||||
public: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
}
|
||||
}
|
49
packages/music_server/src/models/track/index.js
Executable file
49
packages/music_server/src/models/track/index.js
Executable file
@ -0,0 +1,49 @@
|
||||
export default {
|
||||
name: "Track",
|
||||
collection: "tracks",
|
||||
schema: {
|
||||
user_id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
album: {
|
||||
type: String,
|
||||
},
|
||||
artist: {
|
||||
type: String,
|
||||
},
|
||||
source: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
metadata: {
|
||||
type: Object,
|
||||
},
|
||||
explicit: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
public: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
thumbnail: {
|
||||
type: String,
|
||||
default: "https://storage.ragestudio.net/comty-static-assets/default_song.png"
|
||||
},
|
||||
videoCanvas: {
|
||||
type: String,
|
||||
},
|
||||
spotifyId: {
|
||||
type: String,
|
||||
},
|
||||
lyricsEnabled: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
export default [
|
||||
|
||||
]
|
25
packages/music_server/src/services/getTrackById.js
Normal file
25
packages/music_server/src/services/getTrackById.js
Normal file
@ -0,0 +1,25 @@
|
||||
import { Track, User } from "@models"
|
||||
|
||||
export default async (_id) => {
|
||||
if (!_id) {
|
||||
throw new Error("Missing _id")
|
||||
}
|
||||
|
||||
let track = await Track.findById(_id).catch((err) => false)
|
||||
|
||||
if (!track) {
|
||||
throw new Error("Track not found")
|
||||
}
|
||||
|
||||
track = track.toObject()
|
||||
|
||||
if (!track.metadata) {
|
||||
// TODO: Get metadata from source
|
||||
}
|
||||
|
||||
const userData = await User.findById(track.user_id).catch((err) => false)
|
||||
|
||||
track.artist = track.artist ?? userData?.fullName ?? userData?.username ?? "Unknown artist"
|
||||
|
||||
return track
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
import fs from "fs"
|
||||
|
||||
function createRoutesFromDirectory(startFrom, directoryPath, router) {
|
||||
const files = fs.readdirSync(directoryPath)
|
||||
|
||||
if (typeof router.routes !== "object") {
|
||||
router.routes = []
|
||||
}
|
||||
|
||||
files.forEach((file) => {
|
||||
const filePath = `${directoryPath}/${file}`
|
||||
|
||||
const stat = fs.statSync(filePath)
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
createRoutesFromDirectory(startFrom, filePath, router)
|
||||
} else if (file.endsWith(".js") || file.endsWith(".jsx") || file.endsWith(".ts") || file.endsWith(".tsx")) {
|
||||
let splitedFilePath = filePath.split("/")
|
||||
|
||||
// slice the startFrom path
|
||||
splitedFilePath = splitedFilePath.slice(splitedFilePath.indexOf(startFrom) + 1)
|
||||
|
||||
const method = splitedFilePath[0]
|
||||
|
||||
let route = splitedFilePath.slice(1, splitedFilePath.length).join("/")
|
||||
|
||||
route = route.replace(".jsx", "")
|
||||
route = route.replace(".js", "")
|
||||
route = route.replace(".ts", "")
|
||||
route = route.replace(".tsx", "")
|
||||
|
||||
if (route === "index") {
|
||||
route = "/"
|
||||
} else {
|
||||
route = `/${route}`
|
||||
}
|
||||
|
||||
let handler = require(filePath)
|
||||
|
||||
handler = handler.default || handler
|
||||
|
||||
router[method](route, handler)
|
||||
|
||||
router.routes.push({
|
||||
method,
|
||||
path: route,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return router
|
||||
}
|
||||
|
||||
export default createRoutesFromDirectory
|
46
packages/music_server/src/utils/getMiddlewares/index.js
Normal file
46
packages/music_server/src/utils/getMiddlewares/index.js
Normal file
@ -0,0 +1,46 @@
|
||||
import fs from "node:fs"
|
||||
import path from "node:path"
|
||||
|
||||
export default async (middlewares, middlewaresPath) => {
|
||||
if (typeof middlewaresPath === "undefined") {
|
||||
middlewaresPath = path.resolve(globalThis["__root"], "middlewares")
|
||||
}
|
||||
|
||||
if (!fs.existsSync(middlewaresPath)) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (typeof middlewares === "string") {
|
||||
middlewares = [middlewares]
|
||||
}
|
||||
|
||||
let fns = []
|
||||
|
||||
for await (const middlewareName of middlewares) {
|
||||
const middlewarePath = path.resolve(middlewaresPath, middlewareName)
|
||||
|
||||
if (!fs.existsSync(middlewarePath)) {
|
||||
console.error(`Middleware ${middlewareName} not found.`)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
const middleware = require(middlewarePath).default
|
||||
|
||||
if (!middleware) {
|
||||
console.error(`Middleware ${middlewareName} not valid export.`)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if (typeof middleware !== "function") {
|
||||
console.error(`Middleware ${middlewareName} not valid function.`)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
fns.push(middleware)
|
||||
}
|
||||
|
||||
return fns
|
||||
}
|
20
packages/music_server/src/utils/resolveUrl/index.js
Normal file
20
packages/music_server/src/utils/resolveUrl/index.js
Normal file
@ -0,0 +1,20 @@
|
||||
export default (from, to) => {
|
||||
const resolvedUrl = new URL(to, new URL(from, "resolve://"))
|
||||
|
||||
if (resolvedUrl.protocol === "resolve:") {
|
||||
let { pathname, search, hash } = resolvedUrl
|
||||
|
||||
if (to.includes("@")) {
|
||||
const fromUrl = new URL(from)
|
||||
const toUrl = new URL(to, fromUrl.origin)
|
||||
|
||||
pathname = toUrl.pathname
|
||||
search = toUrl.search
|
||||
hash = toUrl.hash
|
||||
}
|
||||
|
||||
return pathname + search + hash
|
||||
}
|
||||
|
||||
return resolvedUrl.toString()
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user