mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 18:44: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 pkg from "../package.json"
|
||||
|
||||
export default class Server {
|
||||
static useMiddlewaresOrder = ["useLogger", "useCors", "useAuth"]
|
||||
static useMiddlewaresOrder = ["useLogger", "useCors", "useAuth", "useErrorHandler"]
|
||||
|
||||
eventBus = global.eventBus = new EventEmitter()
|
||||
|
||||
@ -166,15 +165,15 @@ export default class Server {
|
||||
await this.storage.initialize()
|
||||
|
||||
// register controllers & middlewares
|
||||
this.server.use(express.json({ extended: false }))
|
||||
this.server.use(express.urlencoded({ extended: true }))
|
||||
|
||||
await this.__registerControllers()
|
||||
await this.__registerInternalMiddlewares()
|
||||
await this.__registerInternalRoutes()
|
||||
|
||||
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)
|
||||
|
||||
// calculate elapsed time
|
||||
|
@ -67,7 +67,7 @@ export default async (req, res) => {
|
||||
|
||||
console.log(track)
|
||||
|
||||
if (!track.lyricsEnabled){
|
||||
if (!track.lyricsEnabled) {
|
||||
return res.status(403).json({
|
||||
error: "Lyrics disabled for this track",
|
||||
})
|
||||
|
@ -1,11 +1,18 @@
|
||||
import path from "path"
|
||||
import createRoutesFromDirectory from "@utils/createRoutesFromDirectory"
|
||||
import getMiddlewares from "@utils/getMiddlewares"
|
||||
|
||||
export default (router) => {
|
||||
export default async (router) => {
|
||||
// create a file based router
|
||||
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 {
|
||||
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",
|
||||
collection: "tracks",
|
||||
schema: {
|
||||
user_id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
@ -44,6 +40,10 @@ export default {
|
||||
lyricsEnabled: {
|
||||
type: Boolean,
|
||||
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
|
||||
|
||||
// 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`)
|
||||
})
|
||||
|
||||
|
@ -1,5 +1,24 @@
|
||||
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) {
|
||||
const files = fs.readdirSync(directoryPath)
|
||||
|
||||
@ -39,7 +58,7 @@ function createRoutesFromDirectory(startFrom, directoryPath, router) {
|
||||
|
||||
handler = handler.default || handler
|
||||
|
||||
router[method](route, handler)
|
||||
router[method](route, createRouteHandler(route, handler))
|
||||
|
||||
router.routes.push({
|
||||
method,
|
||||
|
Loading…
x
Reference in New Issue
Block a user