mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-11 03:24:16 +00:00
move routes from main api
This commit is contained in:
parent
2bab67da15
commit
ff49cadde0
@ -12,11 +12,10 @@ import StorageClient from "@classes/StorageClient"
|
|||||||
|
|
||||||
import RoomServer from "./roomsServer"
|
import RoomServer from "./roomsServer"
|
||||||
|
|
||||||
|
|
||||||
import pkg from "../package.json"
|
import pkg from "../package.json"
|
||||||
|
|
||||||
export default class Server {
|
export default class Server {
|
||||||
static useMiddlewaresOrder = ["useLogger", "useCors", "useAuth"]
|
static useMiddlewaresOrder = ["useLogger", "useCors", "useAuth", "useErrorHandler"]
|
||||||
|
|
||||||
eventBus = global.eventBus = new EventEmitter()
|
eventBus = global.eventBus = new EventEmitter()
|
||||||
|
|
||||||
@ -166,15 +165,15 @@ export default class Server {
|
|||||||
await this.storage.initialize()
|
await this.storage.initialize()
|
||||||
|
|
||||||
// register controllers & middlewares
|
// register controllers & middlewares
|
||||||
|
this.server.use(express.json({ extended: false }))
|
||||||
|
this.server.use(express.urlencoded({ extended: true }))
|
||||||
|
|
||||||
await this.__registerControllers()
|
await this.__registerControllers()
|
||||||
await this.__registerInternalMiddlewares()
|
await this.__registerInternalMiddlewares()
|
||||||
await this.__registerInternalRoutes()
|
await this.__registerInternalRoutes()
|
||||||
|
|
||||||
this.server.use(this.internalRouter)
|
this.server.use(this.internalRouter)
|
||||||
|
|
||||||
this.server.use(express.json({ extended: false }))
|
|
||||||
this.server.use(express.urlencoded({ extended: true }))
|
|
||||||
|
|
||||||
await this._http.listen(this.options.listenPort, this.options.listenHost)
|
await this._http.listen(this.options.listenPort, this.options.listenHost)
|
||||||
|
|
||||||
// calculate elapsed time
|
// calculate elapsed time
|
||||||
|
@ -67,7 +67,7 @@ export default async (req, res) => {
|
|||||||
|
|
||||||
console.log(track)
|
console.log(track)
|
||||||
|
|
||||||
if (!track.lyricsEnabled){
|
if (!track.lyricsEnabled) {
|
||||||
return res.status(403).json({
|
return res.status(403).json({
|
||||||
error: "Lyrics disabled for this track",
|
error: "Lyrics disabled for this track",
|
||||||
})
|
})
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
import path from "path"
|
import path from "path"
|
||||||
import createRoutesFromDirectory from "@utils/createRoutesFromDirectory"
|
import createRoutesFromDirectory from "@utils/createRoutesFromDirectory"
|
||||||
|
import getMiddlewares from "@utils/getMiddlewares"
|
||||||
|
|
||||||
export default (router) => {
|
export default async (router) => {
|
||||||
// create a file based router
|
// create a file based router
|
||||||
const routesPath = path.resolve(__dirname, "routes")
|
const routesPath = path.resolve(__dirname, "routes")
|
||||||
|
|
||||||
// router = createRoutesFromDirectory("routes", routesPath, router)
|
const middlewares = await getMiddlewares(["withOptionalAuth"])
|
||||||
|
|
||||||
|
for (const middleware of middlewares) {
|
||||||
|
router.use(middleware)
|
||||||
|
}
|
||||||
|
|
||||||
|
router = createRoutesFromDirectory("routes", routesPath, router)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
path: "/playlists",
|
path: "/playlists",
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
import { Playlist, Track } from "@models"
|
||||||
|
import { NotFoundError } from "@classes/Errors"
|
||||||
|
|
||||||
|
export default async (req, res) => {
|
||||||
|
const { playlist_id } = req.params
|
||||||
|
|
||||||
|
let playlist = await Playlist.findOne({
|
||||||
|
_id: playlist_id,
|
||||||
|
}).catch((err) => {
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
playlist = playlist.toObject()
|
||||||
|
|
||||||
|
if (playlist.public === false) {
|
||||||
|
if (req.session) {
|
||||||
|
if (req.session.user_id !== playlist.user_id) {
|
||||||
|
playlist = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
playlist = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!playlist) {
|
||||||
|
return new NotFoundError(req, res, "Playlist not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
const orderedIds = playlist.list
|
||||||
|
|
||||||
|
playlist.list = await Track.find({
|
||||||
|
_id: [...playlist.list],
|
||||||
|
public: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
playlist.list = playlist.list.sort((a, b) => {
|
||||||
|
return orderedIds.findIndex((id) => id === a._id.toString()) - orderedIds.findIndex((id) => id === b._id.toString())
|
||||||
|
})
|
||||||
|
|
||||||
|
return res.json(playlist)
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
import { Playlist, Track } from "@models"
|
||||||
|
|
||||||
|
export default async (req, res) => {
|
||||||
|
const { keywords, limit = 5, offset = 0 } = req.query
|
||||||
|
|
||||||
|
let results = {
|
||||||
|
playlists: [],
|
||||||
|
artists: [],
|
||||||
|
albums: [],
|
||||||
|
tracks: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
let searchQuery = {
|
||||||
|
public: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keywords) {
|
||||||
|
searchQuery = {
|
||||||
|
...searchQuery,
|
||||||
|
title: {
|
||||||
|
$regex: keywords,
|
||||||
|
$options: "i",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let playlists = await Playlist.find(searchQuery)
|
||||||
|
.limit(limit)
|
||||||
|
.skip(offset)
|
||||||
|
|
||||||
|
if (playlists) {
|
||||||
|
results.playlists = playlists
|
||||||
|
}
|
||||||
|
|
||||||
|
let tracks = await Track.find(searchQuery)
|
||||||
|
.limit(limit)
|
||||||
|
.skip(offset)
|
||||||
|
|
||||||
|
if (tracks) {
|
||||||
|
results.tracks = tracks
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.json(results)
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
import { Playlist, Track } from "@models"
|
||||||
|
import { AuthorizationError, NotFoundError } from "@classes/Errors"
|
||||||
|
|
||||||
|
export default async (req, res) => {
|
||||||
|
if (!req.session) {
|
||||||
|
return new AuthorizationError(req, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { keywords, limit = 10, offset = 0 } = req.query
|
||||||
|
const user_id = req.session.user_id.toString()
|
||||||
|
|
||||||
|
let searchQuery = {
|
||||||
|
user_id,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keywords) {
|
||||||
|
searchQuery = {
|
||||||
|
...searchQuery,
|
||||||
|
title: {
|
||||||
|
$regex: keywords,
|
||||||
|
$options: "i",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let playlists = await Playlist.find(searchQuery)
|
||||||
|
.catch((err) => false)
|
||||||
|
//.limit(limit)
|
||||||
|
//.skip(offset)
|
||||||
|
|
||||||
|
if (!playlists) {
|
||||||
|
return new NotFoundError("Playlists not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
playlists = await Promise.all(playlists.map(async (playlist) => {
|
||||||
|
playlist.list = await Track.find({
|
||||||
|
_id: [
|
||||||
|
...playlist.list,
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
return playlist
|
||||||
|
}))
|
||||||
|
|
||||||
|
return res.json(playlists)
|
||||||
|
}
|
@ -0,0 +1,131 @@
|
|||||||
|
import { Playlist, Track } from "@models"
|
||||||
|
import { AuthorizationError, NotFoundError, PermissionError, BadRequestError } from "@classes/Errors"
|
||||||
|
|
||||||
|
const PlaylistAllowedUpdateFields = [
|
||||||
|
"title",
|
||||||
|
"cover",
|
||||||
|
"album",
|
||||||
|
"artist",
|
||||||
|
"description",
|
||||||
|
"public",
|
||||||
|
]
|
||||||
|
|
||||||
|
const TrackAllowedUpdateFields = [
|
||||||
|
"title",
|
||||||
|
"album",
|
||||||
|
"artist",
|
||||||
|
"cover",
|
||||||
|
"explicit",
|
||||||
|
"metadata",
|
||||||
|
"public",
|
||||||
|
"spotifyId",
|
||||||
|
"lyricsEnabled",
|
||||||
|
"public",
|
||||||
|
]
|
||||||
|
|
||||||
|
async function createOrUpdateTrack(payload) {
|
||||||
|
if (!payload.title || !payload.source || !payload.user_id) {
|
||||||
|
throw new Error("title and source and user_id are required")
|
||||||
|
}
|
||||||
|
|
||||||
|
let track = null
|
||||||
|
|
||||||
|
if (payload._id) {
|
||||||
|
track = await Track.findById(payload._id)
|
||||||
|
|
||||||
|
if (!track) {
|
||||||
|
throw new Error("track not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
TrackAllowedUpdateFields.forEach((field) => {
|
||||||
|
if (typeof payload[field] !== "undefined") {
|
||||||
|
track[field] = payload[field]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
track = await Track.findByIdAndUpdate(payload._id, track)
|
||||||
|
|
||||||
|
if (!track) {
|
||||||
|
throw new Error("Failed to update track")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
track = new Track(payload)
|
||||||
|
|
||||||
|
await track.save()
|
||||||
|
}
|
||||||
|
|
||||||
|
return track
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async (req, res) => {
|
||||||
|
if (!req.session) {
|
||||||
|
return new AuthorizationError(req, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!req.body.title || !req.body.list) {
|
||||||
|
return new BadRequestError(req, res, "title and list are required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Array.isArray(req.body.list)) {
|
||||||
|
return new BadRequestError(req, res, "list must be an array")
|
||||||
|
}
|
||||||
|
|
||||||
|
let playlist = null
|
||||||
|
|
||||||
|
if (!req.body._id) {
|
||||||
|
playlist = new Playlist({
|
||||||
|
user_id: req.session.user_id.toString(),
|
||||||
|
created_at: Date.now(),
|
||||||
|
title: req.body.title ?? "Untitled",
|
||||||
|
description: req.body.description,
|
||||||
|
cover: req.body.cover,
|
||||||
|
explicit: req.body.explicit,
|
||||||
|
public: req.body.public,
|
||||||
|
list: req.body.list,
|
||||||
|
})
|
||||||
|
|
||||||
|
await playlist.save()
|
||||||
|
} else {
|
||||||
|
playlist = await Playlist.findById(req.body._id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!playlist) {
|
||||||
|
return new NotFoundError(req, res, "Playlist not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (playlist.user_id !== req.session.user_id.toString()) {
|
||||||
|
return new PermissionError(req, res, "You don't have permission to edit this playlist")
|
||||||
|
}
|
||||||
|
|
||||||
|
playlist = playlist.toObject()
|
||||||
|
|
||||||
|
playlist.list = await Promise.all(req.body.list.map(async (track, index) => {
|
||||||
|
if (typeof track !== "object") {
|
||||||
|
return track
|
||||||
|
}
|
||||||
|
|
||||||
|
track.user_id = req.session.user_id.toString()
|
||||||
|
|
||||||
|
const result = await createOrUpdateTrack(track)
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
return result._id.toString()
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
PlaylistAllowedUpdateFields.forEach((field) => {
|
||||||
|
if (typeof req.body[field] !== "undefined") {
|
||||||
|
playlist[field] = req.body[field]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
playlist = await Playlist.findByIdAndUpdate(req.body._id, playlist)
|
||||||
|
|
||||||
|
if (!playlist) {
|
||||||
|
return new NotFoundError(req, res, "Playlist not updated")
|
||||||
|
}
|
||||||
|
|
||||||
|
global.eventBus.emit(`playlist.${playlist._id}.updated`, playlist)
|
||||||
|
|
||||||
|
return res.json(playlist)
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
import { Track } 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()
|
||||||
|
|
||||||
|
track.artist = track.artist ?? "Unknown artist"
|
||||||
|
|
||||||
|
return track
|
||||||
|
}
|
21
packages/music_server/src/controllers/tracks/index.js
Normal file
21
packages/music_server/src/controllers/tracks/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: "/tracks",
|
||||||
|
router,
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
import { Track } from "@models"
|
||||||
|
import { NotFoundError } from "@classes/Errors"
|
||||||
|
|
||||||
|
export default async (req, res) => {
|
||||||
|
const { track_id } = req.params
|
||||||
|
|
||||||
|
let track = await Track.findOne({
|
||||||
|
_id: track_id,
|
||||||
|
public: true,
|
||||||
|
}).catch((err) => {
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!track) {
|
||||||
|
return new NotFoundError(req, res, "Track not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.json(track)
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
import { Track } from "@models"
|
||||||
|
import { NotFoundError, InternalServerError } from "@classes/Errors"
|
||||||
|
|
||||||
|
import mimetypes from "mime-types"
|
||||||
|
|
||||||
|
export default async (req, res) => {
|
||||||
|
const { track_id } = req.params
|
||||||
|
|
||||||
|
let track = await Track.findOne({
|
||||||
|
_id: track_id,
|
||||||
|
public: true,
|
||||||
|
}).catch((err) => {
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!track) {
|
||||||
|
return new NotFoundError(req, res, "Track not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
track = track.toObject()
|
||||||
|
|
||||||
|
if (typeof track.stream_source === "undefined") {
|
||||||
|
return new NotFoundError(req, res, "Track doesn't have stream source")
|
||||||
|
}
|
||||||
|
|
||||||
|
global.storage.getObject(process.env.S3_BUCKET, `tracks/${track.stream_source}`, (err, dataStream) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(err)
|
||||||
|
return new InternalServerError(req, res, "Error while getting file from storage")
|
||||||
|
}
|
||||||
|
|
||||||
|
const extname = mimetypes.lookup(track.stream_source)
|
||||||
|
|
||||||
|
// send chunked response
|
||||||
|
res.status(200)
|
||||||
|
|
||||||
|
// set headers
|
||||||
|
res.setHeader("Content-Type", extname)
|
||||||
|
res.setHeader("Accept-Ranges", "bytes")
|
||||||
|
|
||||||
|
return dataStream.pipe(res)
|
||||||
|
})
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
import { Track } from "@models"
|
||||||
|
|
||||||
|
export default async (req, res) => {
|
||||||
|
const { ids, limit = 20, offset = 0 } = req.query
|
||||||
|
|
||||||
|
if (!ids) {
|
||||||
|
return res.status(400).json({
|
||||||
|
message: "IDs is required",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let tracks = await Track.find({
|
||||||
|
_id: [...ids],
|
||||||
|
public: true,
|
||||||
|
})
|
||||||
|
.limit(limit)
|
||||||
|
.skip(offset)
|
||||||
|
.catch((err) => {
|
||||||
|
return []
|
||||||
|
})
|
||||||
|
|
||||||
|
return res.json(tracks)
|
||||||
|
}
|
@ -2,10 +2,6 @@ export default {
|
|||||||
name: "Track",
|
name: "Track",
|
||||||
collection: "tracks",
|
collection: "tracks",
|
||||||
schema: {
|
schema: {
|
||||||
user_id: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
title: {
|
title: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
@ -44,6 +40,10 @@ export default {
|
|||||||
lyricsEnabled: {
|
lyricsEnabled: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
}
|
},
|
||||||
|
publisher: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,25 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
@ -7,6 +7,11 @@ export default (req, res, next) => {
|
|||||||
|
|
||||||
res._responseTimeMs = elapsedTimeInMs
|
res._responseTimeMs = elapsedTimeInMs
|
||||||
|
|
||||||
|
// cut req.url if is too long
|
||||||
|
if (req.url.length > 100) {
|
||||||
|
req.url = req.url.substring(0, 100) + "..."
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`${req.method} ${res._status_code ?? res.statusCode ?? 200} ${req.url} ${elapsedTimeInMs}ms`)
|
console.log(`${req.method} ${res._status_code ?? res.statusCode ?? 200} ${req.url} ${elapsedTimeInMs}ms`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1,5 +1,24 @@
|
|||||||
import fs from "fs"
|
import fs from "fs"
|
||||||
|
|
||||||
|
function createRouteHandler(route, fn) {
|
||||||
|
if (typeof route !== "string") {
|
||||||
|
fn = route
|
||||||
|
route = "Unknown route"
|
||||||
|
}
|
||||||
|
|
||||||
|
return async (req, res) => {
|
||||||
|
try {
|
||||||
|
await fn(req, res)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`[ERROR] (${route}) >`, error)
|
||||||
|
|
||||||
|
return res.status(500).json({
|
||||||
|
error: error.message,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function createRoutesFromDirectory(startFrom, directoryPath, router) {
|
function createRoutesFromDirectory(startFrom, directoryPath, router) {
|
||||||
const files = fs.readdirSync(directoryPath)
|
const files = fs.readdirSync(directoryPath)
|
||||||
|
|
||||||
@ -39,7 +58,7 @@ function createRoutesFromDirectory(startFrom, directoryPath, router) {
|
|||||||
|
|
||||||
handler = handler.default || handler
|
handler = handler.default || handler
|
||||||
|
|
||||||
router[method](route, handler)
|
router[method](route, createRouteHandler(route, handler))
|
||||||
|
|
||||||
router.routes.push({
|
router.routes.push({
|
||||||
method,
|
method,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user