mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-10 02:54:15 +00:00
rewrite controller, including Endpoint
class model
This commit is contained in:
parent
02cc20cd34
commit
22aeb48634
@ -0,0 +1,22 @@
|
||||
import passport from "passport"
|
||||
import { Token } from "@lib"
|
||||
|
||||
export default {
|
||||
method: "POST",
|
||||
route: "/login",
|
||||
fn: async (req, res) => {
|
||||
passport.authenticate("local", { session: false }, async (error, user, options) => {
|
||||
if (error) {
|
||||
return res.status(500).json(`Error validating user > ${error.message}`)
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return res.status(401).json("Invalid credentials")
|
||||
}
|
||||
|
||||
const token = await Token.createNewAuthToken(user, options)
|
||||
|
||||
return res.json({ token: token })
|
||||
})(req, res)
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
import { Session } from "@models"
|
||||
|
||||
export default {
|
||||
method: "POST",
|
||||
route: "/logout",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const { token, user_id } = req.body
|
||||
|
||||
if (typeof user_id === "undefined") {
|
||||
return res.status(400).json("No user_id provided")
|
||||
}
|
||||
if (typeof token === "undefined") {
|
||||
return res.status(400).json("No token provided")
|
||||
}
|
||||
|
||||
const session = await Session.findOneAndDelete({ user_id, token })
|
||||
|
||||
if (session) {
|
||||
return res.json("done")
|
||||
}
|
||||
|
||||
return res.status(404).json("not found")
|
||||
},
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
import { Schematized } from "@lib"
|
||||
|
||||
import createUser from "../methods/createUser"
|
||||
|
||||
export default {
|
||||
method: "POST",
|
||||
route: "/register",
|
||||
fn: Schematized({
|
||||
required: ["username", "email", "password"],
|
||||
select: ["username", "email", "password", "fullName"],
|
||||
}, async (req, res) => {
|
||||
const result = await createUser(req.selection).catch((err) => {
|
||||
return res.status(500).json(err.message)
|
||||
})
|
||||
|
||||
return res.json(result)
|
||||
})
|
||||
}
|
9
packages/server/src/controllers/AuthController/index.js
Normal file
9
packages/server/src/controllers/AuthController/index.js
Normal file
@ -0,0 +1,9 @@
|
||||
import { Controller } from "linebridge/dist/server"
|
||||
import generateEndpointsFromDir from "linebridge/dist/server/lib/generateEndpointsFromDir"
|
||||
|
||||
export default class AuthController extends Controller {
|
||||
static refName = "AuthController"
|
||||
static useRoute = "/auth"
|
||||
|
||||
httpEndpoints = generateEndpointsFromDir(__dirname + "/endpoints")
|
||||
}
|
54
packages/server/src/controllers/AuthController/methods/createUser.js
Executable file
54
packages/server/src/controllers/AuthController/methods/createUser.js
Executable file
@ -0,0 +1,54 @@
|
||||
import { User } from "@models"
|
||||
import Avatars from "dicebar_lib"
|
||||
import bcrypt from "bcrypt"
|
||||
|
||||
export default async function (payload) {
|
||||
let { username, password, email, fullName, roles, avatar } = payload
|
||||
|
||||
// if username has capital letters, throw error
|
||||
if (username !== username.toLowerCase()) {
|
||||
throw new Error("Username must be lowercase")
|
||||
}
|
||||
|
||||
// make sure the username has no spaces
|
||||
if (username.includes(" ")) {
|
||||
throw new Error("Username cannot contain spaces")
|
||||
}
|
||||
|
||||
// make sure the username has no valid characters. Only letters, numbers, and underscores
|
||||
if (!/^[a-z0-9_]+$/.test(username)) {
|
||||
throw new Error("Username can only contain letters, numbers, and underscores")
|
||||
}
|
||||
|
||||
// check if username is already taken
|
||||
const existentUser = await User.findOne({ username: username })
|
||||
|
||||
if (existentUser) {
|
||||
throw new Error("User already exists")
|
||||
}
|
||||
|
||||
// check if the email is already in use
|
||||
const existentEmail = await User.findOne({ email: email })
|
||||
|
||||
if (existentEmail) {
|
||||
throw new Error("Email already in use")
|
||||
}
|
||||
|
||||
// hash the password
|
||||
const hash = bcrypt.hashSync(password, parseInt(process.env.BCRYPT_ROUNDS ?? 3))
|
||||
|
||||
// create the doc
|
||||
let user = new User({
|
||||
username: username,
|
||||
password: hash,
|
||||
email: email,
|
||||
fullName: fullName,
|
||||
avatar: avatar ?? Avatars.generate({ seed: username, type: "initials" }).uri,
|
||||
roles: roles,
|
||||
createdAt: new Date().getTime(),
|
||||
})
|
||||
|
||||
await user.save()
|
||||
|
||||
return user
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
import { Badge } from "@models"
|
||||
|
||||
export default {
|
||||
method: "DELETE",
|
||||
route: "/badge/:badge_id",
|
||||
middlewares: ["withAuthentication", "onlyAdmin"],
|
||||
fn: async (req, res) => {
|
||||
const badge = await Badge.findById(req.params.badge_id).catch((err) => {
|
||||
res.status(500).json({ error: err })
|
||||
return false
|
||||
})
|
||||
|
||||
if (!badge) {
|
||||
return res.status(404).json({ error: "No badge founded" })
|
||||
}
|
||||
|
||||
badge.remove()
|
||||
|
||||
return res.json(badge)
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
import { Schematized } from "@lib"
|
||||
import { Badge } from "@models"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/",
|
||||
fn: Schematized({
|
||||
select: ["_id", "name", "label"],
|
||||
}, async (req, res) => {
|
||||
let badges = []
|
||||
|
||||
if (req.selection._id) {
|
||||
badges = await Badge.find({
|
||||
_id: { $in: req.selection._id },
|
||||
})
|
||||
|
||||
badges = badges.map(badge => badge.toObject())
|
||||
} else {
|
||||
badges = await Badge.find(req.selection).catch((err) => {
|
||||
res.status(500).json({ error: err })
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
if (badges) {
|
||||
return res.json(badges)
|
||||
}
|
||||
})
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
import { User, Badge } from "@models"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/user",
|
||||
fn: async (req, res) => {
|
||||
const user = await User.findOne({ _id: req.query.user_id ?? req.user._id })
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: "User not found" })
|
||||
}
|
||||
|
||||
const badges = await Badge.find({
|
||||
name: { $in: user.badges },
|
||||
})
|
||||
|
||||
return res.json(badges)
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
import { Badge, User } from "@models"
|
||||
import { Schematized } from "@lib"
|
||||
|
||||
export default {
|
||||
method: "POST",
|
||||
route: "/badge/:badge_id/giveToUser",
|
||||
middlewares: ["withAuthentication", "onlyAdmin"],
|
||||
fn: Schematized({
|
||||
required: ["user_id"],
|
||||
select: ["user_id"],
|
||||
}, async (req, res) => {
|
||||
const badge = await Badge.findById(req.params.badge_id).catch((err) => {
|
||||
res.status(500).json({ error: err })
|
||||
return false
|
||||
})
|
||||
|
||||
if (!badge) {
|
||||
return res.status(404).json({ error: "No badge founded" })
|
||||
}
|
||||
|
||||
const user = await User.findById(req.selection.user_id).catch((err) => {
|
||||
res.status(500).json({ error: err })
|
||||
return false
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: "No user founded" })
|
||||
}
|
||||
|
||||
// check if user already have this badge
|
||||
if (user.badges.includes(badge._id)) {
|
||||
return res.status(409).json({ error: "User already have this badge" })
|
||||
}
|
||||
|
||||
user.badges.push(badge._id.toString())
|
||||
|
||||
user.save()
|
||||
|
||||
return res.json(user)
|
||||
})
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
import { Badge } from "@models"
|
||||
import { Schematized } from "@lib"
|
||||
|
||||
export default {
|
||||
method: "PUT",
|
||||
route: "/",
|
||||
middlewares: ["withAuthentication", "onlyAdmin"],
|
||||
fn: Schematized({
|
||||
select: ["badge_id", "name", "label", "description", "icon", "color"],
|
||||
}, async (req, res) => {
|
||||
let badge = await Badge.findById(req.selection.badge_id).catch((err) => null)
|
||||
|
||||
if (!badge) {
|
||||
badge = new Badge()
|
||||
}
|
||||
|
||||
badge.name = req.selection.name || badge.name
|
||||
badge.label = req.selection.label || badge.label
|
||||
badge.description = req.selection.description || badge.description
|
||||
badge.icon = req.selection.icon || badge.icon
|
||||
badge.color = req.selection.color || badge.color
|
||||
|
||||
badge.save()
|
||||
|
||||
return res.json(badge)
|
||||
})
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
import { Schematized } from "@lib"
|
||||
import { Badge, User } from "@models"
|
||||
|
||||
export default {
|
||||
method: "DELETE",
|
||||
route: "/badge/:badge_id/removeFromUser",
|
||||
middlewares: ["withAuthentication", "onlyAdmin"],
|
||||
fn: Schematized({
|
||||
required: ["user_id"],
|
||||
select: ["user_id"],
|
||||
}, async (req, res) => {
|
||||
const badge = await Badge.findById(req.params.badge_id).catch((err) => {
|
||||
res.status(500).json({ error: err })
|
||||
return false
|
||||
})
|
||||
|
||||
if (!badge) {
|
||||
return res.status(404).json({ error: "No badge founded" })
|
||||
}
|
||||
|
||||
const user = await User.findById(req.selection.user_id).catch((err) => {
|
||||
res.status(500).json({ error: err })
|
||||
return false
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: "No user founded" })
|
||||
}
|
||||
|
||||
// check if user already have this badge
|
||||
if (!user.badges.includes(badge._id)) {
|
||||
return res.status(409).json({ error: "User don't have this badge" })
|
||||
}
|
||||
|
||||
user.badges = user.badges.filter(b => b !== badge._id.toString())
|
||||
|
||||
user.save()
|
||||
|
||||
return res.json(user)
|
||||
})
|
||||
}
|
@ -1,198 +1,9 @@
|
||||
import { Controller } from "linebridge/dist/server"
|
||||
import { Badge, User } from "../../models"
|
||||
import { Schematized } from "../../lib"
|
||||
import generateEndpointsFromDir from "linebridge/dist/server/lib/generateEndpointsFromDir"
|
||||
|
||||
export default class BadgesController extends Controller {
|
||||
static refName = "BadgesController"
|
||||
static useRoute = "/badge"
|
||||
|
||||
get = {
|
||||
"/badges": Schematized({
|
||||
select: ["_id", "name", "label"],
|
||||
}, async (req, res) => {
|
||||
let badges = []
|
||||
|
||||
if (req.selection._id) {
|
||||
badges = await Badge.find({
|
||||
_id: { $in: req.selection._id },
|
||||
})
|
||||
|
||||
badges = badges.map(badge => badge.toObject())
|
||||
} else {
|
||||
badges = await Badge.find(req.selection).catch((err) => {
|
||||
res.status(500).json({ error: err })
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
if (badges) {
|
||||
return res.json(badges)
|
||||
}
|
||||
}),
|
||||
"/user/badges": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const user = await User.findOne({ _id: req.query.user_id ?? req.user._id })
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: "User not found" })
|
||||
}
|
||||
|
||||
const badges = await Badge.find({
|
||||
name: { $in: user.badges },
|
||||
})
|
||||
|
||||
return res.json(badges)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post = {
|
||||
"/badge": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
required: ["name"],
|
||||
select: ["name", "label", "description", "icon", "color"],
|
||||
}, async (req, res) => {
|
||||
await Badge.findOne(req.selection).then((data) => {
|
||||
if (data) {
|
||||
return res.status(409).json({
|
||||
error: "This badge is already created",
|
||||
})
|
||||
}
|
||||
|
||||
let badge = new Badge({
|
||||
name: req.selection.name,
|
||||
label: req.selection.label,
|
||||
description: req.selection.description,
|
||||
icon: req.selection.icon,
|
||||
color: req.selection.color,
|
||||
})
|
||||
|
||||
badge.save()
|
||||
|
||||
return res.json(badge)
|
||||
})
|
||||
})
|
||||
},
|
||||
"/badge/:badge_id/giveToUser": {
|
||||
middlewares: ["withAuthentication", "onlyAdmin"],
|
||||
fn: Schematized({
|
||||
required: ["user_id"],
|
||||
select: ["user_id"],
|
||||
}, async (req, res) => {
|
||||
const badge = await Badge.findById(req.params.badge_id).catch((err) => {
|
||||
res.status(500).json({ error: err })
|
||||
return false
|
||||
})
|
||||
|
||||
if (!badge) {
|
||||
return res.status(404).json({ error: "No badge founded" })
|
||||
}
|
||||
|
||||
const user = await User.findById(req.selection.user_id).catch((err) => {
|
||||
res.status(500).json({ error: err })
|
||||
return false
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: "No user founded" })
|
||||
}
|
||||
|
||||
// check if user already have this badge
|
||||
if (user.badges.includes(badge._id)) {
|
||||
return res.status(409).json({ error: "User already have this badge" })
|
||||
}
|
||||
|
||||
user.badges.push(badge._id.toString())
|
||||
|
||||
user.save()
|
||||
|
||||
return res.json(user)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
put = {
|
||||
"/badge/:badge_id": {
|
||||
middlewares: ["withAuthentication", "onlyAdmin"],
|
||||
fn: Schematized({
|
||||
select: ["name", "label", "description", "icon", "color"],
|
||||
}, async (req, res) => {
|
||||
const badge = await Badge.findById(req.params.badge_id).catch((err) => {
|
||||
res.status(500).json({ error: err })
|
||||
return false
|
||||
})
|
||||
|
||||
if (!badge) {
|
||||
return res.status(404).json({ error: "No badge founded" })
|
||||
}
|
||||
|
||||
badge.name = req.selection.name || badge.name
|
||||
badge.label = req.selection.label || badge.label
|
||||
badge.description = req.selection.description || badge.description
|
||||
badge.icon = req.selection.icon || badge.icon
|
||||
badge.color = req.selection.color || badge.color
|
||||
|
||||
badge.save()
|
||||
|
||||
return res.json(badge)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
delete = {
|
||||
"/badge/:badge_id": {
|
||||
middlewares: ["withAuthentication", "onlyAdmin"],
|
||||
fn: async (req, res) => {
|
||||
const badge = await Badge.findById(req.params.badge_id).catch((err) => {
|
||||
res.status(500).json({ error: err })
|
||||
return false
|
||||
})
|
||||
|
||||
if (!badge) {
|
||||
return res.status(404).json({ error: "No badge founded" })
|
||||
}
|
||||
|
||||
badge.remove()
|
||||
|
||||
return res.json(badge)
|
||||
}
|
||||
},
|
||||
"/badge/:badge_id/removeFromUser": {
|
||||
middlewares: ["withAuthentication", "onlyAdmin"],
|
||||
fn: Schematized({
|
||||
required: ["user_id"],
|
||||
select: ["user_id"],
|
||||
}, async (req, res) => {
|
||||
const badge = await Badge.findById(req.params.badge_id).catch((err) => {
|
||||
res.status(500).json({ error: err })
|
||||
return false
|
||||
})
|
||||
|
||||
if (!badge) {
|
||||
return res.status(404).json({ error: "No badge founded" })
|
||||
}
|
||||
|
||||
const user = await User.findById(req.selection.user_id).catch((err) => {
|
||||
res.status(500).json({ error: err })
|
||||
return false
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: "No user founded" })
|
||||
}
|
||||
|
||||
// check if user already have this badge
|
||||
if (!user.badges.includes(badge._id)) {
|
||||
return res.status(409).json({ error: "User don't have this badge" })
|
||||
}
|
||||
|
||||
user.badges = user.badges.filter(b => b !== badge._id.toString())
|
||||
|
||||
user.save()
|
||||
|
||||
return res.json(user)
|
||||
})
|
||||
}
|
||||
}
|
||||
httpEndpoints = generateEndpointsFromDir(__dirname + "/endpoints")
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
import { Schematized } from "@lib"
|
||||
import newComment from "../methods/newComment"
|
||||
|
||||
export default {
|
||||
method: "POST",
|
||||
route: "/post/:post_id",
|
||||
fn: Schematized({
|
||||
required: ["message"],
|
||||
select: ["message"],
|
||||
}, async (req, res) => {
|
||||
const { post_id } = req.params
|
||||
const { message } = req.selection
|
||||
|
||||
try {
|
||||
const comment = newComment({
|
||||
user_id: req.user._id.toString(),
|
||||
parent_id: post_id,
|
||||
message: message,
|
||||
})
|
||||
|
||||
return res.json(comment)
|
||||
} catch (error) {
|
||||
return res.status(400).json({
|
||||
error: error.message,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
import deleteComment from "../methods/deleteComment"
|
||||
|
||||
export default {
|
||||
method: "DELETE",
|
||||
route: "/post/:post_id/:comment_id",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const result = await deleteComment({
|
||||
comment_id: req.params.comment_id,
|
||||
issuer_id: req.user._id.toString(),
|
||||
}).catch((err) => {
|
||||
res.status(500).json({ message: err.message })
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
if (result) {
|
||||
return res.json(result)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
import getComments from "../methods/getComments"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/post/:post_id",
|
||||
fn: async (req, res) => {
|
||||
const { post_id } = req.params
|
||||
|
||||
const comments = await getComments({ parent_id: post_id }).catch((err) => {
|
||||
res.status(400).json({
|
||||
error: err.message,
|
||||
})
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
if (!comments) return
|
||||
|
||||
return res.json(comments)
|
||||
}
|
||||
}
|
@ -1,77 +1,9 @@
|
||||
import { Controller } from "linebridge/dist/server"
|
||||
import { Schematized } from "../../lib"
|
||||
|
||||
import getComments from "./methods/getComments"
|
||||
import newComment from "./methods/newComment"
|
||||
import deleteComment from "./methods/deleteComment"
|
||||
import generateEndpointsFromDir from "linebridge/dist/server/lib/generateEndpointsFromDir"
|
||||
|
||||
export default class CommentsController extends Controller {
|
||||
static refName = "CommentsController"
|
||||
static useRoute = "/comments"
|
||||
|
||||
get = {
|
||||
"/posts/:post_id/comments": {
|
||||
fn: async (req, res) => {
|
||||
const { post_id } = req.params
|
||||
|
||||
const comments = await getComments({ parent_id: post_id }).catch((err) => {
|
||||
res.status(400).json({
|
||||
error: err.message,
|
||||
})
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
if (!comments) return
|
||||
|
||||
return res.json(comments)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post = {
|
||||
"/posts/:post_id/comment": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
required: ["message"],
|
||||
select: ["message"],
|
||||
}, async (req, res) => {
|
||||
const { post_id } = req.params
|
||||
const { message } = req.selection
|
||||
|
||||
try {
|
||||
const comment = newComment({
|
||||
user_id: req.user._id.toString(),
|
||||
parent_id: post_id,
|
||||
message: message,
|
||||
})
|
||||
|
||||
return res.json(comment)
|
||||
} catch (error) {
|
||||
return res.status(400).json({
|
||||
error: error.message,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
delete = {
|
||||
"/posts/:post_id/comment/:comment_id": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const result = await deleteComment({
|
||||
comment_id: req.params.comment_id,
|
||||
issuer_id: req.user._id.toString(),
|
||||
}).catch((err) => {
|
||||
res.status(500).json({ message: err.message })
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
if (result) {
|
||||
return res.json(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
httpEndpoints = generateEndpointsFromDir(__dirname + "/endpoints")
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
import { Controller } from "linebridge/dist/server"
|
||||
|
||||
export default class ConfigController extends Controller {
|
||||
static refName = "ConfigController"
|
||||
static useMiddlewares = ["withAuthentication", "onlyAdmin"]
|
||||
|
||||
post = {
|
||||
"/update_config": async (req, res) => {
|
||||
|
||||
},
|
||||
}
|
||||
}
|
@ -1,61 +1,63 @@
|
||||
import { Controller } from "linebridge/dist/server"
|
||||
import { FeaturedEvent } from "../../models"
|
||||
|
||||
import createFeaturedEvent from "./methods/createFeaturedEvent"
|
||||
import { FeaturedEvent } from "@models"
|
||||
import createFeaturedEvent from "./services/createFeaturedEvent"
|
||||
|
||||
// TODO: Migrate to new linebridge 0.15 endpoint classes instead of this
|
||||
|
||||
export default class FeaturedEventsController extends Controller {
|
||||
get = {
|
||||
"/featured_event/:id": async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const featuredEvent = await FeaturedEvent.findById(id)
|
||||
|
||||
return res.json(featuredEvent)
|
||||
},
|
||||
"/featured_events": async (req, res) => {
|
||||
let query = {
|
||||
expired: false
|
||||
}
|
||||
|
||||
if (req.query.includeExpired) {
|
||||
delete query.expired
|
||||
}
|
||||
|
||||
const featuredEvents = await FeaturedEvent.find(query)
|
||||
|
||||
return res.json(featuredEvents)
|
||||
}
|
||||
}
|
||||
|
||||
post = {
|
||||
"/featured_event": {
|
||||
middlewares: ["withAuthentication", "onlyAdmin"],
|
||||
fn: async (req, res) => {
|
||||
const result = await createFeaturedEvent(req.body).catch((err) => {
|
||||
res.status(500).json({
|
||||
error: err.message
|
||||
})
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
if (result) {
|
||||
return res.json(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete = {
|
||||
"/featured_event/:id": {
|
||||
middlewares: ["withAuthentication", "onlyAdmin"],
|
||||
fn: async (req, res) => {
|
||||
httpEndpoints = {
|
||||
get: {
|
||||
"/featured_event/:id": async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const featuredEvent = await FeaturedEvent.findByIdAndDelete(id)
|
||||
const featuredEvent = await FeaturedEvent.findById(id)
|
||||
|
||||
return res.json(featuredEvent)
|
||||
},
|
||||
"/featured_events": async (req, res) => {
|
||||
let query = {
|
||||
expired: false
|
||||
}
|
||||
|
||||
if (req.query.includeExpired) {
|
||||
delete query.expired
|
||||
}
|
||||
|
||||
const featuredEvents = await FeaturedEvent.find(query)
|
||||
|
||||
return res.json(featuredEvents)
|
||||
}
|
||||
}
|
||||
},
|
||||
post: {
|
||||
"/featured_event": {
|
||||
middlewares: ["withAuthentication", "onlyAdmin"],
|
||||
fn: async (req, res) => {
|
||||
const result = await createFeaturedEvent(req.body).catch((err) => {
|
||||
res.status(500).json({
|
||||
error: err.message
|
||||
})
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
if (result) {
|
||||
return res.json(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
delete: {
|
||||
"/featured_event/:id": {
|
||||
middlewares: ["withAuthentication", "onlyAdmin"],
|
||||
fn: async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const featuredEvent = await FeaturedEvent.findByIdAndDelete(id)
|
||||
|
||||
return res.json(featuredEvent)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
@ -1,61 +1,63 @@
|
||||
import { Controller } from "linebridge/dist/server"
|
||||
|
||||
import getPosts from "./methods/getPosts"
|
||||
import getPlaylists from "./methods/getPlaylists"
|
||||
import getPosts from "./services/getPosts"
|
||||
import getPlaylists from "./services/getPlaylists"
|
||||
|
||||
export default class FeedController extends Controller {
|
||||
static refName = "FeedController"
|
||||
static useRoute = "/feed"
|
||||
|
||||
get = {
|
||||
"/posts": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const for_user_id = req.user?._id.toString()
|
||||
httpEndpoints = {
|
||||
get: {
|
||||
"/posts": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const for_user_id = req.user?._id.toString()
|
||||
|
||||
if (!for_user_id) {
|
||||
return res.status(400).json({
|
||||
error: "Invalid user id"
|
||||
if (!for_user_id) {
|
||||
return res.status(400).json({
|
||||
error: "Invalid user id"
|
||||
})
|
||||
}
|
||||
|
||||
let feed = []
|
||||
|
||||
// fetch posts
|
||||
const posts = await getPosts({
|
||||
for_user_id,
|
||||
limit: req.query?.limit,
|
||||
skip: req.query?.trim,
|
||||
})
|
||||
|
||||
feed = feed.concat(posts)
|
||||
|
||||
return res.json(feed)
|
||||
}
|
||||
},
|
||||
"/playlists": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const for_user_id = req.user?._id.toString()
|
||||
|
||||
let feed = []
|
||||
if (!for_user_id) {
|
||||
return res.status(400).json({
|
||||
error: "Invalid user id"
|
||||
})
|
||||
}
|
||||
|
||||
// fetch posts
|
||||
const posts = await getPosts({
|
||||
for_user_id,
|
||||
limit: req.query?.limit,
|
||||
skip: req.query?.trim,
|
||||
})
|
||||
let feed = []
|
||||
|
||||
feed = feed.concat(posts)
|
||||
|
||||
return res.json(feed)
|
||||
}
|
||||
},
|
||||
"/playlists": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const for_user_id = req.user?._id.toString()
|
||||
|
||||
if (!for_user_id) {
|
||||
return res.status(400).json({
|
||||
error: "Invalid user id"
|
||||
// fetch playlists
|
||||
const playlists = await getPlaylists({
|
||||
for_user_id,
|
||||
limit: req.query?.limit,
|
||||
skip: req.query?.trim,
|
||||
})
|
||||
|
||||
feed = feed.concat(playlists)
|
||||
|
||||
return res.json(feed)
|
||||
}
|
||||
|
||||
let feed = []
|
||||
|
||||
// fetch playlists
|
||||
const playlists = await getPlaylists({
|
||||
for_user_id,
|
||||
limit: req.query?.limit,
|
||||
skip: req.query?.trim,
|
||||
})
|
||||
|
||||
feed = feed.concat(playlists)
|
||||
|
||||
return res.json(feed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,28 @@
|
||||
import { Controller } from "linebridge/dist/server"
|
||||
|
||||
import uploadBodyFiles from "./methods/uploadBodyFiles"
|
||||
import uploadBodyFiles from "./services/uploadBodyFiles"
|
||||
|
||||
export default class FilesController extends Controller {
|
||||
post = {
|
||||
"/upload": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const results = await uploadBodyFiles({
|
||||
req,
|
||||
}).catch((err) => {
|
||||
res.status(400).json({
|
||||
error: err.message,
|
||||
static refName = "FilesController"
|
||||
|
||||
httpEndpoints = {
|
||||
post: {
|
||||
"/upload": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const results = await uploadBodyFiles({
|
||||
req,
|
||||
}).catch((err) => {
|
||||
res.status(400).json({
|
||||
error: err.message,
|
||||
})
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
if (results) {
|
||||
return res.json(results)
|
||||
if (results) {
|
||||
return res.json(results)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,17 @@
|
||||
import { UserFollow } from "@models"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/user/:user_id/is_followed",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const isFollowed = await UserFollow.findOne({
|
||||
user_id: req.user._id.toString(),
|
||||
to: req.params.user_id,
|
||||
}).catch(() => false)
|
||||
|
||||
return res.json({
|
||||
isFollowed: Boolean(isFollowed),
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
import { User, UserFollow } from "@lib"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/user/:user_id/followers",
|
||||
fn: async (req, res) => {
|
||||
const { limit = 30, offset } = req.query
|
||||
|
||||
let followers = []
|
||||
|
||||
const follows = await UserFollow.find({
|
||||
to: req.params.user_id,
|
||||
})
|
||||
.limit(limit)
|
||||
.skip(offset)
|
||||
|
||||
for await (const follow of follows) {
|
||||
const user = await User.findById(follow.user_id)
|
||||
|
||||
if (!user) {
|
||||
continue
|
||||
}
|
||||
|
||||
followers.push(user.toObject())
|
||||
}
|
||||
|
||||
return res.json(followers)
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
import { Schematized } from "@lib"
|
||||
import { User, UserFollow } from "@models"
|
||||
|
||||
import followUser from "../services/followUser"
|
||||
import unfollowUser from "../services/unfollowUser"
|
||||
|
||||
export default {
|
||||
method: "POST",
|
||||
route: "/user/toogle",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
select: ["user_id", "username"],
|
||||
}, async (req, res) => {
|
||||
const selfUserId = req.user._id.toString()
|
||||
let targetUserId = null
|
||||
let result = null
|
||||
|
||||
if (typeof req.selection.user_id === "undefined" && typeof req.selection.username === "undefined") {
|
||||
return res.status(400).json({ message: "No user_id or username provided" })
|
||||
}
|
||||
|
||||
if (typeof req.selection.user_id !== "undefined") {
|
||||
targetUserId = req.selection.user_id
|
||||
} else {
|
||||
const user = await User.findOne({ username: req.selection.username })
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ message: "User not found" })
|
||||
}
|
||||
|
||||
targetUserId = user._id.toString()
|
||||
}
|
||||
|
||||
// check if already following
|
||||
const isFollowed = await UserFollow.findOne({
|
||||
user_id: selfUserId,
|
||||
to: targetUserId,
|
||||
})
|
||||
|
||||
// if already following, delete
|
||||
if (isFollowed) {
|
||||
result = await unfollowUser({
|
||||
user_id: selfUserId,
|
||||
to: targetUserId,
|
||||
}).catch((error) => {
|
||||
return res.status(500).json({ message: error.message })
|
||||
})
|
||||
} else {
|
||||
result = await followUser({
|
||||
user_id: selfUserId,
|
||||
to: targetUserId,
|
||||
}).catch((error) => {
|
||||
return res.status(500).json({ message: error.message })
|
||||
})
|
||||
}
|
||||
|
||||
return res.json(result)
|
||||
})
|
||||
}
|
9
packages/server/src/controllers/FollowController/index.js
Executable file
9
packages/server/src/controllers/FollowController/index.js
Executable file
@ -0,0 +1,9 @@
|
||||
import { Controller } from "linebridge/dist/server"
|
||||
import generateEndpointsFromDir from "linebridge/dist/server/lib/generateEndpointsFromDir"
|
||||
|
||||
export default class FollowController extends Controller {
|
||||
static refName = "FollowController"
|
||||
static useRoute = "/follow"
|
||||
|
||||
httpEndpoints = generateEndpointsFromDir(__dirname + "/endpoints")
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
import { User, UserFollow } from "@models"
|
||||
|
||||
export default async (payload) => {
|
||||
if (typeof payload.user_id === "undefined") {
|
||||
throw new Error("No user_id provided")
|
||||
}
|
||||
if (typeof payload.to === "undefined") {
|
||||
throw new Error("No to provided")
|
||||
}
|
||||
|
||||
const user = await User.findById(payload.user_id)
|
||||
|
||||
if (!user) {
|
||||
throw new Error("User not found")
|
||||
}
|
||||
|
||||
const follow = await UserFollow.findOne({
|
||||
user_id: payload.user_id,
|
||||
to: payload.to,
|
||||
})
|
||||
|
||||
if (follow) {
|
||||
throw new Error("Already following")
|
||||
}
|
||||
|
||||
const newFollow = await UserFollow.create({
|
||||
user_id: payload.user_id,
|
||||
to: payload.to,
|
||||
})
|
||||
|
||||
await newFollow.save()
|
||||
|
||||
global.wsInterface.io.emit(`user.follow`, {
|
||||
...user.toObject(),
|
||||
})
|
||||
global.wsInterface.io.emit(`user.follow.${payload.user_id}`, {
|
||||
...user.toObject(),
|
||||
})
|
||||
|
||||
const followers = await UserFollow.find({
|
||||
to: payload.to,
|
||||
})
|
||||
|
||||
return {
|
||||
following: true,
|
||||
followers: followers,
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
import { User, UserFollow } from "@models"
|
||||
|
||||
export default async (payload) => {
|
||||
if (typeof payload.user_id === "undefined") {
|
||||
throw new Error("No user_id provided")
|
||||
}
|
||||
if (typeof payload.to === "undefined") {
|
||||
throw new Error("No to provided")
|
||||
}
|
||||
|
||||
const user = await User.findById(payload.user_id)
|
||||
|
||||
if (!user) {
|
||||
throw new Error("User not found")
|
||||
}
|
||||
|
||||
const follow = await UserFollow.findOne({
|
||||
user_id: payload.user_id,
|
||||
to: payload.to,
|
||||
})
|
||||
|
||||
if (!follow) {
|
||||
throw new Error("Not following")
|
||||
}
|
||||
|
||||
await follow.remove()
|
||||
|
||||
global.wsInterface.io.emit(`user.unfollow`, {
|
||||
...user.toObject(),
|
||||
})
|
||||
global.wsInterface.io.emit(`user.unfollow.${payload.user_id}`, {
|
||||
...user.toObject(),
|
||||
})
|
||||
|
||||
const followers = await UserFollow.find({
|
||||
to: payload.to,
|
||||
})
|
||||
|
||||
return {
|
||||
following: false,
|
||||
followers: followers,
|
||||
}
|
||||
}
|
@ -1,213 +0,0 @@
|
||||
import { Controller } from "linebridge/dist/server"
|
||||
|
||||
import { User, UserFollow } from "../../models"
|
||||
import { Schematized } from "../../lib"
|
||||
|
||||
export default class FollowerController extends Controller {
|
||||
methods = {
|
||||
follow: async (payload) => {
|
||||
if (typeof payload.user_id === "undefined") {
|
||||
throw new Error("No user_id provided")
|
||||
}
|
||||
if (typeof payload.to === "undefined") {
|
||||
throw new Error("No to provided")
|
||||
}
|
||||
|
||||
const user = await User.findById(payload.user_id)
|
||||
|
||||
if (!user) {
|
||||
throw new Error("User not found")
|
||||
}
|
||||
|
||||
const follow = await UserFollow.findOne({
|
||||
user_id: payload.user_id,
|
||||
to: payload.to,
|
||||
})
|
||||
|
||||
if (follow) {
|
||||
throw new Error("Already following")
|
||||
}
|
||||
|
||||
const newFollow = await UserFollow.create({
|
||||
user_id: payload.user_id,
|
||||
to: payload.to,
|
||||
})
|
||||
|
||||
await newFollow.save()
|
||||
|
||||
global.wsInterface.io.emit(`user.follow`, {
|
||||
...user.toObject(),
|
||||
})
|
||||
global.wsInterface.io.emit(`user.follow.${payload.user_id}`, {
|
||||
...user.toObject(),
|
||||
})
|
||||
|
||||
const followers = await UserFollow.find({
|
||||
to: payload.to,
|
||||
})
|
||||
|
||||
return {
|
||||
following: true,
|
||||
followers: followers,
|
||||
}
|
||||
},
|
||||
unfollow: async (payload) => {
|
||||
if (typeof payload.user_id === "undefined") {
|
||||
throw new Error("No user_id provided")
|
||||
}
|
||||
if (typeof payload.to === "undefined") {
|
||||
throw new Error("No to provided")
|
||||
}
|
||||
|
||||
const user = await User.findById(payload.user_id)
|
||||
|
||||
if (!user) {
|
||||
throw new Error("User not found")
|
||||
}
|
||||
|
||||
const follow = await UserFollow.findOne({
|
||||
user_id: payload.user_id,
|
||||
to: payload.to,
|
||||
})
|
||||
|
||||
if (!follow) {
|
||||
throw new Error("Not following")
|
||||
}
|
||||
|
||||
await follow.remove()
|
||||
|
||||
global.wsInterface.io.emit(`user.unfollow`, {
|
||||
...user.toObject(),
|
||||
})
|
||||
global.wsInterface.io.emit(`user.unfollow.${payload.user_id}`, {
|
||||
...user.toObject(),
|
||||
})
|
||||
|
||||
const followers = await UserFollow.find({
|
||||
to: payload.to,
|
||||
})
|
||||
|
||||
return {
|
||||
following: false,
|
||||
followers: followers,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
post = {
|
||||
"/follow_user": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
select: ["user_id", "username"],
|
||||
}, async (req, res) => {
|
||||
const selfUserId = req.user._id.toString()
|
||||
let targetUserId = null
|
||||
let result = null
|
||||
|
||||
if (typeof req.selection.user_id === "undefined" && typeof req.selection.username === "undefined") {
|
||||
return res.status(400).json({ message: "No user_id or username provided" })
|
||||
}
|
||||
|
||||
if (typeof req.selection.user_id !== "undefined") {
|
||||
targetUserId = req.selection.user_id
|
||||
} else {
|
||||
const user = await User.findOne({ username: req.selection.username })
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ message: "User not found" })
|
||||
}
|
||||
|
||||
targetUserId = user._id.toString()
|
||||
}
|
||||
|
||||
// check if already following
|
||||
const isFollowed = await UserFollow.findOne({
|
||||
user_id: selfUserId,
|
||||
to: targetUserId,
|
||||
})
|
||||
|
||||
// if already following, delete
|
||||
if (isFollowed) {
|
||||
result = await this.methods.unfollow({
|
||||
user_id: selfUserId,
|
||||
to: targetUserId,
|
||||
}).catch((error) => {
|
||||
return res.status(500).json({ message: error.message })
|
||||
})
|
||||
} else {
|
||||
result = await this.methods.follow({
|
||||
user_id: selfUserId,
|
||||
to: targetUserId,
|
||||
}).catch((error) => {
|
||||
return res.status(500).json({ message: error.message })
|
||||
})
|
||||
}
|
||||
|
||||
return res.json(result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
get = {
|
||||
"/user/:user_id/followers": async (req, res) => {
|
||||
const { limit = 30, offset } = req.query
|
||||
|
||||
let followers = []
|
||||
|
||||
const follows = await UserFollow.find({
|
||||
to: req.params.user_id,
|
||||
})
|
||||
.limit(limit)
|
||||
.skip(offset)
|
||||
|
||||
for await (const follow of follows) {
|
||||
const user = await User.findById(follow.user_id)
|
||||
|
||||
if (!user) {
|
||||
continue
|
||||
}
|
||||
|
||||
followers.push(user.toObject())
|
||||
}
|
||||
|
||||
return res.json(followers)
|
||||
},
|
||||
"/followers": Schematized({
|
||||
required: ["user_id"],
|
||||
select: ["user_id"],
|
||||
}, async (req, res) => {
|
||||
let followers = []
|
||||
const follows = await UserFollow.find({
|
||||
to: req.selection.user_id,
|
||||
})
|
||||
|
||||
for await (const follow of follows) {
|
||||
const user = await User.findById(follow.user_id)
|
||||
|
||||
if (!user) {
|
||||
continue
|
||||
}
|
||||
|
||||
followers.push(user.toObject())
|
||||
}
|
||||
|
||||
return res.json(followers)
|
||||
}),
|
||||
"/is_followed": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
required: ["user_id"],
|
||||
select: ["user_id"]
|
||||
}, async (req, res) => {
|
||||
const isFollowed = await UserFollow.findOne({
|
||||
user_id: req.user._id.toString(),
|
||||
to: req.selection.user_id,
|
||||
}).catch(() => false)
|
||||
|
||||
return res.json({
|
||||
isFollowed: Boolean(isFollowed),
|
||||
})
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
@ -1,49 +1,18 @@
|
||||
import { Controller } from "linebridge/dist/server"
|
||||
import { Schematized } from "../../lib"
|
||||
import { Schematized } from "@lib"
|
||||
|
||||
import publishPlaylist from "./methods/publishPlaylist"
|
||||
import getPlaylist from "./methods/getPlaylist"
|
||||
import publishPlaylist from "./services/publishPlaylist"
|
||||
import getPlaylist from "./services/getPlaylist"
|
||||
|
||||
export default class PlaylistsController extends Controller {
|
||||
//static useMiddlewares = ["withAuthentication"]
|
||||
static refName = "PlaylistsController"
|
||||
static useRoute = "/playlist"
|
||||
|
||||
get = {
|
||||
"/playlist/:id": async (req, res) => {
|
||||
const result = await getPlaylist({
|
||||
_id: req.params.id
|
||||
}).catch((err) => {
|
||||
res.status(500).json({
|
||||
error: err.message
|
||||
})
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
if (result) {
|
||||
return res.json(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post = {
|
||||
"/playlist/publish": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
required: ["title", "list"],
|
||||
select: ["title", "description", "thumbnail", "list"],
|
||||
}, async (req, res) => {
|
||||
if (typeof req.body.list === "undefined") {
|
||||
return res.status(400).json({
|
||||
error: "list is required"
|
||||
})
|
||||
}
|
||||
|
||||
// parse
|
||||
req.selection.list = JSON.parse(req.selection.list)
|
||||
|
||||
const result = await publishPlaylist({
|
||||
user_id: req.user._id.toString(),
|
||||
...req.selection
|
||||
httpEndpoints = {
|
||||
get: {
|
||||
"/:id": async (req, res) => {
|
||||
const result = await getPlaylist({
|
||||
_id: req.params.id
|
||||
}).catch((err) => {
|
||||
res.status(500).json({
|
||||
error: err.message
|
||||
@ -55,7 +24,41 @@ export default class PlaylistsController extends Controller {
|
||||
if (result) {
|
||||
return res.json(result)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
post: {
|
||||
"/publish": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
required: ["title", "list"],
|
||||
select: ["title", "description", "thumbnail", "list"],
|
||||
}, async (req, res) => {
|
||||
if (typeof req.body.list === "undefined") {
|
||||
return res.status(400).json({
|
||||
error: "list is required"
|
||||
})
|
||||
}
|
||||
|
||||
// parse
|
||||
req.selection.list = JSON.parse(req.selection.list)
|
||||
|
||||
const result = await publishPlaylist({
|
||||
user_id: req.user._id.toString(),
|
||||
...req.selection
|
||||
}).catch((err) => {
|
||||
res.status(500).json({
|
||||
error: err.message
|
||||
})
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
if (result) {
|
||||
return res.json(result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
import { Schematized } from "@lib"
|
||||
import { CreatePost } from "../methods"
|
||||
|
||||
export default {
|
||||
method: "POST",
|
||||
route: "/new",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
required: ["timestamp"],
|
||||
select: ["message", "attachments", "type", "data", "timestamp"],
|
||||
}, async (req, res) => {
|
||||
const post = await CreatePost({
|
||||
user_id: req.user.id,
|
||||
message: req.selection.message,
|
||||
timestamp: req.selection.timestamp,
|
||||
attachments: req.selection.attachments,
|
||||
type: req.selection.type,
|
||||
data: req.selection.data,
|
||||
})
|
||||
|
||||
return res.json(post)
|
||||
})
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
import { DeletePost } from "../methods"
|
||||
|
||||
export default {
|
||||
method: "DELETE",
|
||||
route: "/:post_id",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const post = await DeletePost({
|
||||
post_id: req.params.post_id,
|
||||
by_user_id: req.user._id.toString(),
|
||||
}).catch((err) => {
|
||||
res.status(400).json({
|
||||
error: err.message
|
||||
})
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
if (!post) return
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
post
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
import { Schematized } from "@lib"
|
||||
import { GetPostData } from "../methods"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/explore",
|
||||
middlewares: ["withOptionalAuthentication"],
|
||||
fn: Schematized({
|
||||
select: ["user_id"]
|
||||
}, async (req, res) => {
|
||||
let posts = await GetPostData({
|
||||
limit: req.query?.limit,
|
||||
skip: req.query?.trim,
|
||||
from_user_id: req.query?.user_id,
|
||||
for_user_id: req.user?._id.toString(),
|
||||
})
|
||||
|
||||
return res.json(posts)
|
||||
})
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
import { GetPostData } from "../methods"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/:post_id",
|
||||
middlewares: ["withOptionalAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
let post = await GetPostData({
|
||||
post_id: req.params.post_id,
|
||||
for_user_id: req.user?._id.toString(),
|
||||
}).catch((error) => {
|
||||
res.status(404).json({ error: error.message })
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
if (!post) return
|
||||
|
||||
return res.json(post)
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
import { GetPostData } from "../methods"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/user/:user_id",
|
||||
middlewares: ["withOptionalAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
let posts = await GetPostData({
|
||||
limit: req.query?.limit,
|
||||
skip: req.query?.trim,
|
||||
for_user_id: req.user?._id.toString(),
|
||||
from_user_id: req.params.user_id,
|
||||
})
|
||||
|
||||
return res.json(posts)
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
import { Schematized } from "@lib"
|
||||
import { GetPostData } from "../methods"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/saved",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
select: ["user_id"]
|
||||
}, async (req, res) => {
|
||||
let posts = await GetPostData({
|
||||
limit: req.query?.limit,
|
||||
skip: req.query?.trim,
|
||||
for_user_id: req.user?._id.toString(),
|
||||
savedOnly: true,
|
||||
})
|
||||
|
||||
return res.json(posts)
|
||||
})
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
import { Schematized } from "@lib"
|
||||
import { ToogleLike } from "../methods"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/:post_id/toogle_like",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
select: ["to"],
|
||||
}, async (req, res) => {
|
||||
const post = await ToogleLike({
|
||||
user_id: req.user._id.toString(),
|
||||
post_id: req.params.post_id,
|
||||
to: req.selection.to,
|
||||
}).catch((err) => {
|
||||
res.status(400).json({
|
||||
error: err.message
|
||||
})
|
||||
return false
|
||||
})
|
||||
|
||||
if (!post) return
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
post
|
||||
})
|
||||
})
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
import { ToogleSavePost } from "../methods"
|
||||
|
||||
export default {
|
||||
method: "POST",
|
||||
route: "/:post_id/toogle_save",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const post = await ToogleSavePost({
|
||||
user_id: req.user._id.toString(),
|
||||
post_id: req.params.post_id,
|
||||
}).catch((err) => {
|
||||
res.status(400).json({
|
||||
error: err.message
|
||||
})
|
||||
return false
|
||||
})
|
||||
|
||||
if (!post) return
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
post
|
||||
})
|
||||
}
|
||||
}
|
@ -1,173 +1,19 @@
|
||||
import { Controller } from "linebridge/dist/server"
|
||||
import { Schematized } from "../../lib"
|
||||
|
||||
import { CreatePost, ToogleLike, GetPostData, DeletePost, ToogleSavePost } from "./methods"
|
||||
import generateEndpointsFromDir from "linebridge/dist/server/lib/generateEndpointsFromDir"
|
||||
|
||||
export default class PostsController extends Controller {
|
||||
static refName = "PostsController"
|
||||
static useRoute = "/posts"
|
||||
|
||||
get = {
|
||||
"/explore": {
|
||||
middlewares: ["withOptionalAuthentication"],
|
||||
fn: Schematized({
|
||||
select: ["user_id"]
|
||||
}, async (req, res) => {
|
||||
let posts = await GetPostData({
|
||||
limit: req.query?.limit,
|
||||
skip: req.query?.trim,
|
||||
from_user_id: req.query?.user_id,
|
||||
for_user_id: req.user?._id.toString(),
|
||||
})
|
||||
httpEndpoints = generateEndpointsFromDir(__dirname + "/endpoints")
|
||||
|
||||
return res.json(posts)
|
||||
})
|
||||
},
|
||||
"/saved": {
|
||||
middlewares: ["withOptionalAuthentication"],
|
||||
fn: Schematized({
|
||||
select: ["user_id"]
|
||||
}, async (req, res) => {
|
||||
let posts = await GetPostData({
|
||||
limit: req.query?.limit,
|
||||
skip: req.query?.trim,
|
||||
for_user_id: req.user?._id.toString(),
|
||||
savedOnly: true,
|
||||
})
|
||||
|
||||
return res.json(posts)
|
||||
})
|
||||
},
|
||||
"/user/:user_id": {
|
||||
middlewares: ["withOptionalAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
let posts = await GetPostData({
|
||||
limit: req.query?.limit,
|
||||
skip: req.query?.trim,
|
||||
for_user_id: req.user?._id.toString(),
|
||||
from_user_id: req.params.user_id,
|
||||
})
|
||||
|
||||
return res.json(posts)
|
||||
}
|
||||
},
|
||||
"/:post_id": {
|
||||
middlewares: ["withOptionalAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
let post = await GetPostData({
|
||||
post_id: req.params.post_id,
|
||||
for_user_id: req.user?._id.toString(),
|
||||
}).catch((error) => {
|
||||
res.status(404).json({ error: error.message })
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
if (!post) return
|
||||
|
||||
return res.json(post)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
put = {
|
||||
"/:post_id": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: (req, res) => {
|
||||
// TODO: Implement Post update
|
||||
return res.status(501).json({ error: "Not implemented" })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post = {
|
||||
"/new": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
required: ["timestamp"],
|
||||
select: ["message", "attachments", "type", "data", "timestamp"],
|
||||
}, async (req, res) => {
|
||||
const post = await CreatePost({
|
||||
user_id: req.user.id,
|
||||
message: req.selection.message,
|
||||
timestamp: req.selection.timestamp,
|
||||
attachments: req.selection.attachments,
|
||||
type: req.selection.type,
|
||||
data: req.selection.data,
|
||||
})
|
||||
|
||||
return res.json(post)
|
||||
})
|
||||
},
|
||||
"/:post_id/toogle_like": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
select: ["to"],
|
||||
}, async (req, res) => {
|
||||
const post = await ToogleLike({
|
||||
user_id: req.user._id.toString(),
|
||||
post_id: req.params.post_id,
|
||||
to: req.selection.to,
|
||||
}).catch((err) => {
|
||||
res.status(400).json({
|
||||
error: err.message
|
||||
})
|
||||
return false
|
||||
})
|
||||
|
||||
if (!post) return
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
post
|
||||
})
|
||||
})
|
||||
},
|
||||
"/:post_id/toogle_save": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const post = await ToogleSavePost({
|
||||
user_id: req.user._id.toString(),
|
||||
post_id: req.params.post_id,
|
||||
}).catch((err) => {
|
||||
res.status(400).json({
|
||||
error: err.message
|
||||
})
|
||||
return false
|
||||
})
|
||||
|
||||
if (!post) return
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
post
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete = {
|
||||
"/:post_id": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const post = await DeletePost({
|
||||
post_id: req.params.post_id,
|
||||
by_user_id: req.user._id.toString(),
|
||||
}).catch((err) => {
|
||||
res.status(400).json({
|
||||
error: err.message
|
||||
})
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
if (!post) return
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
post
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
// put = {
|
||||
// "/:post_id": {
|
||||
// middlewares: ["withAuthentication"],
|
||||
// fn: (req, res) => {
|
||||
// // TODO: Implement Post update
|
||||
// return res.status(501).json({ error: "Not implemented" })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
import { FeaturedWallpaper } from "../../../models"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/featured_wallpapers",
|
||||
fn: async (req, res) => {
|
||||
const featuredWallpapers = await FeaturedWallpaper.find({})
|
||||
.sort({ date: -1 })
|
||||
.limit(10)
|
||||
.catch(err => {
|
||||
return res.status(500).json({
|
||||
error: err.message
|
||||
}).end()
|
||||
})
|
||||
|
||||
return res.json(featuredWallpapers)
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
import { Schematized } from "../../../lib"
|
||||
import IndecentPrediction from "../../../utils/indecent-prediction"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/indecent_prediction",
|
||||
fn: Schematized({
|
||||
select: ["url"],
|
||||
required: ["url"],
|
||||
}, async (req, res) => {
|
||||
const { url } = req.selection
|
||||
|
||||
const predictions = await IndecentPrediction({
|
||||
url,
|
||||
}).catch((err) => {
|
||||
res.status(500).json({
|
||||
error: err.message,
|
||||
})
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
if (predictions) {
|
||||
return res.json(predictions)
|
||||
}
|
||||
})
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
import { Schematized } from "@lib"
|
||||
import { FeaturedWallpaper } from "@models"
|
||||
|
||||
export default {
|
||||
method: "POST",
|
||||
route: "/featured_wallpaper",
|
||||
middlewares: ["withAuthentication", "onlyAdmin"],
|
||||
fn: Schematized({
|
||||
select: ["url", "date", "author"],
|
||||
required: ["url"],
|
||||
}, async (req, res) => {
|
||||
const newFeaturedWallpaper = new FeaturedWallpaper(req.selection)
|
||||
|
||||
const result = await newFeaturedWallpaper.save().catch((err) => {
|
||||
res.status(400).json({ message: err.message })
|
||||
return null
|
||||
})
|
||||
|
||||
if (result) {
|
||||
return res.json(newFeaturedWallpaper)
|
||||
}
|
||||
})
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/posting_policy",
|
||||
middlewares: ["withOptionalAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
// TODO: Use `PermissionsAPI` to get the user's permissions and return the correct policy, by now it will return the default policy
|
||||
return res.json(global.DEFAULT_POSTING_POLICY)
|
||||
}
|
||||
}
|
@ -1,86 +1,8 @@
|
||||
import { Controller } from "linebridge/dist/server"
|
||||
import { Schematized } from "../../lib"
|
||||
|
||||
import { FeaturedWallpaper } from "../../models"
|
||||
|
||||
import IndecentPrediction from "../../utils/indecent-prediction"
|
||||
import generateEndpointsFromDir from "linebridge/dist/server/lib/generateEndpointsFromDir"
|
||||
|
||||
export default class PublicController extends Controller {
|
||||
static refName = "PublicController"
|
||||
|
||||
get = {
|
||||
"/indecent_prediction": {
|
||||
fn: Schematized({
|
||||
select: ["url"],
|
||||
required: ["url"],
|
||||
}, async (req, res) => {
|
||||
const { url } = req.selection
|
||||
|
||||
const predictions = await IndecentPrediction({
|
||||
url,
|
||||
}).catch((err) => {
|
||||
res.status(500).json({
|
||||
error: err.message,
|
||||
})
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
if (predictions) {
|
||||
return res.json(predictions)
|
||||
}
|
||||
})
|
||||
},
|
||||
"/posting_policy": {
|
||||
middlewares: ["withOptionalAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
// TODO: Use `PermissionsAPI` to get the user's permissions and return the correct policy, by now it will return the default policy
|
||||
return res.json(global.DEFAULT_POSTING_POLICY)
|
||||
}
|
||||
},
|
||||
"/featured_wallpapers": {
|
||||
fn: async (req, res) => {
|
||||
const featuredWallpapers = await FeaturedWallpaper.find({})
|
||||
.sort({ date: -1 })
|
||||
.limit(10)
|
||||
.catch(err => {
|
||||
return res.status(500).json({
|
||||
error: err.message
|
||||
}).end()
|
||||
})
|
||||
|
||||
return res.json(featuredWallpapers)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post = {
|
||||
"/only_managers_test": {
|
||||
middlewares: ["withAuthentication", "permissions"],
|
||||
fn: (req, res) => {
|
||||
return res.json({
|
||||
message: "Congrats!, Only managers can access this route (or you are an admin)",
|
||||
assertedPermissions: req.assertedPermissions
|
||||
})
|
||||
},
|
||||
},
|
||||
"/new_featured_wallpaper": {
|
||||
middlewares: ["withAuthentication", "onlyAdmin"],
|
||||
fn: Schematized({
|
||||
select: ["url", "date", "author"],
|
||||
required: ["url"],
|
||||
}, async (req, res) => {
|
||||
const newFeaturedWallpaper = new FeaturedWallpaper(req.selection)
|
||||
|
||||
const result = await newFeaturedWallpaper.save().catch((err) => {
|
||||
res.status(400).json({ message: err.message })
|
||||
return null
|
||||
})
|
||||
|
||||
if (result) {
|
||||
return res.json(newFeaturedWallpaper)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
httpEndpoints = generateEndpointsFromDir(__dirname + "/endpoints")
|
||||
}
|
@ -1,113 +1,115 @@
|
||||
import { Controller } from "linebridge/dist/server"
|
||||
import { Role, User } from "../../models"
|
||||
import { Schematized } from "../../lib"
|
||||
import { Role, User } from "@models"
|
||||
import { Schematized } from "@lib"
|
||||
|
||||
export default class RolesController extends Controller {
|
||||
static refName = "RolesController"
|
||||
static useMiddlewares = ["roles"]
|
||||
|
||||
get = {
|
||||
"/roles": Schematized({
|
||||
select: ["user_id", "username"],
|
||||
}, async (req, res) => {
|
||||
const roles = await Role.find()
|
||||
|
||||
return res.json(roles)
|
||||
}),
|
||||
"/user_roles": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
select: ["username"],
|
||||
httpEndpoints = {
|
||||
get: {
|
||||
"/roles": Schematized({
|
||||
select: ["user_id", "username"],
|
||||
}, async (req, res) => {
|
||||
const user = await User.findOne(req.selection)
|
||||
const roles = await Role.find()
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: "No user founded" })
|
||||
}
|
||||
|
||||
return res.json(user.roles)
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
post = {
|
||||
"/role": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
required: ["name"],
|
||||
select: ["name", "description"],
|
||||
}, async (req, res) => {
|
||||
await Role.findOne(req.selection).then((data) => {
|
||||
if (data) {
|
||||
return res.status(409).json("This role is already created")
|
||||
}
|
||||
|
||||
let role = new Role({
|
||||
name: req.selection.name,
|
||||
description: req.selection.description,
|
||||
})
|
||||
|
||||
role.save()
|
||||
|
||||
return res.json(role)
|
||||
})
|
||||
})
|
||||
},
|
||||
"/update_user_roles": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
required: ["update"],
|
||||
select: ["update"],
|
||||
}, async (req, res) => {
|
||||
// check if issuer user is admin
|
||||
if (!req.isAdmin()) {
|
||||
return res.status(403).json("You do not have administrator permission")
|
||||
}
|
||||
|
||||
if (!Array.isArray(req.selection.update)) {
|
||||
return res.status(400).json("Invalid update request")
|
||||
}
|
||||
|
||||
req.selection.update.forEach(async (update) => {
|
||||
const user = await User.findById(update._id).catch(err => {
|
||||
return false
|
||||
})
|
||||
|
||||
console.log(update.roles)
|
||||
|
||||
if (user) {
|
||||
user.roles = update.roles
|
||||
|
||||
await user.save()
|
||||
}
|
||||
})
|
||||
|
||||
return res.json("done")
|
||||
return res.json(roles)
|
||||
}),
|
||||
},
|
||||
}
|
||||
"/user_roles": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
select: ["username"],
|
||||
}, async (req, res) => {
|
||||
const user = await User.findOne(req.selection)
|
||||
|
||||
delete = {
|
||||
"/role": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
required: ["name"],
|
||||
select: ["name"],
|
||||
}, async (req, res) => {
|
||||
if (req.selection.name === "admin") {
|
||||
return res.status(409).json("You can't delete admin role")
|
||||
}
|
||||
|
||||
await Role.findOne(req.selection).then((data) => {
|
||||
if (!data) {
|
||||
return res.status(404).json("This role is not found")
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: "No user founded" })
|
||||
}
|
||||
|
||||
data.remove()
|
||||
|
||||
return res.json(data)
|
||||
return res.json(user.roles)
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
post: {
|
||||
"/role": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
required: ["name"],
|
||||
select: ["name", "description"],
|
||||
}, async (req, res) => {
|
||||
await Role.findOne(req.selection).then((data) => {
|
||||
if (data) {
|
||||
return res.status(409).json("This role is already created")
|
||||
}
|
||||
|
||||
let role = new Role({
|
||||
name: req.selection.name,
|
||||
description: req.selection.description,
|
||||
})
|
||||
|
||||
role.save()
|
||||
|
||||
return res.json(role)
|
||||
})
|
||||
})
|
||||
},
|
||||
"/update_user_roles": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
required: ["update"],
|
||||
select: ["update"],
|
||||
}, async (req, res) => {
|
||||
// check if issuer user is admin
|
||||
if (!req.isAdmin()) {
|
||||
return res.status(403).json("You do not have administrator permission")
|
||||
}
|
||||
|
||||
if (!Array.isArray(req.selection.update)) {
|
||||
return res.status(400).json("Invalid update request")
|
||||
}
|
||||
|
||||
req.selection.update.forEach(async (update) => {
|
||||
const user = await User.findById(update._id).catch(err => {
|
||||
return false
|
||||
})
|
||||
|
||||
console.log(update.roles)
|
||||
|
||||
if (user) {
|
||||
user.roles = update.roles
|
||||
|
||||
await user.save()
|
||||
}
|
||||
})
|
||||
|
||||
return res.json("done")
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
||||
delete: {
|
||||
"/role": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
required: ["name"],
|
||||
select: ["name"],
|
||||
}, async (req, res) => {
|
||||
if (req.selection.name === "admin") {
|
||||
return res.status(409).json("You can't delete admin role")
|
||||
}
|
||||
|
||||
await Role.findOne(req.selection).then((data) => {
|
||||
if (!data) {
|
||||
return res.status(404).json("This role is not found")
|
||||
}
|
||||
|
||||
data.remove()
|
||||
|
||||
return res.json(data)
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
@ -1,30 +1,36 @@
|
||||
import { Controller } from "linebridge/dist/server"
|
||||
import { User, Post } from "../../models"
|
||||
|
||||
import { User, Post } from "@models"
|
||||
|
||||
export default class SearchController extends Controller {
|
||||
get = {
|
||||
"/search": {
|
||||
middlewares: ["withOptionalAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const { keywords = "" } = req.query
|
||||
static refName = "SearchController"
|
||||
static useRoute = "/search"
|
||||
|
||||
let suggestions = {}
|
||||
httpEndpoints = {
|
||||
get: {
|
||||
"/": {
|
||||
middlewares: ["withOptionalAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const { keywords = "" } = req.query
|
||||
|
||||
// search users by username or name
|
||||
const users = await User.find({
|
||||
$or: [
|
||||
{ username: { $regex: keywords, $options: "i" } },
|
||||
{ fullName: { $regex: keywords, $options: "i" } },
|
||||
],
|
||||
})
|
||||
.limit(5)
|
||||
.select("username fullName avatar verified")
|
||||
let suggestions = {}
|
||||
|
||||
if (users.length > 0) {
|
||||
suggestions["users"] = users
|
||||
// search users by username or name
|
||||
const users = await User.find({
|
||||
$or: [
|
||||
{ username: { $regex: keywords, $options: "i" } },
|
||||
{ fullName: { $regex: keywords, $options: "i" } },
|
||||
],
|
||||
})
|
||||
.limit(5)
|
||||
.select("username fullName avatar verified")
|
||||
|
||||
if (users.length > 0) {
|
||||
suggestions["users"] = users
|
||||
}
|
||||
|
||||
return res.json(suggestions)
|
||||
}
|
||||
|
||||
return res.json(suggestions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
export default {
|
||||
method: "DELETE",
|
||||
route: "/all",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const user_id = req.user._id.toString()
|
||||
|
||||
const allSessions = await Session.deleteMany({ user_id })
|
||||
|
||||
if (allSessions) {
|
||||
return res.json("done")
|
||||
}
|
||||
|
||||
return res.status(404).json("not found")
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
import { Session } from "@models"
|
||||
|
||||
export default {
|
||||
method: "DELETE",
|
||||
route: "/",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const { token } = req.body
|
||||
const user_id = req.user._id.toString()
|
||||
|
||||
if (typeof token === "undefined") {
|
||||
return res.status(400).json("No token provided")
|
||||
}
|
||||
|
||||
const session = await Session.findOneAndDelete({ user_id, token })
|
||||
|
||||
if (session) {
|
||||
return res.json({
|
||||
message: "done",
|
||||
})
|
||||
}
|
||||
|
||||
return res.status(404).json({
|
||||
error: "Session not found",
|
||||
})
|
||||
},
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/current",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
return res.json(req.currentSession)
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
import { Session } from "@models"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/all",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const sessions = await Session.find({ user_id: req.user._id.toString() }, { token: 0 })
|
||||
.sort({ date: -1 })
|
||||
|
||||
return res.json(sessions)
|
||||
},
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
import { Token } from "@lib"
|
||||
|
||||
export default {
|
||||
method: "POST",
|
||||
route: "/regenerate",
|
||||
middlewares: ["useJwtStrategy"],
|
||||
fn: async (req, res) => {
|
||||
const { expiredToken, refreshToken } = req.body
|
||||
|
||||
const token = await Token.regenerateSession(expiredToken, refreshToken).catch((error) => {
|
||||
res.status(400).json({ error: error.message })
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
if (!token) return
|
||||
|
||||
return res.json({ token })
|
||||
},
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
import jwt from "jsonwebtoken"
|
||||
|
||||
import { Session } from "@models"
|
||||
|
||||
export default {
|
||||
method: "POST",
|
||||
route: "/validate",
|
||||
middlewares: ["useJwtStrategy"],
|
||||
fn: async (req, res) => {
|
||||
const token = req.body.session
|
||||
|
||||
let result = {
|
||||
expired: false,
|
||||
valid: true
|
||||
}
|
||||
|
||||
await jwt.verify(token, req.jwtStrategy.secretOrKey, async (err, decoded) => {
|
||||
if (err) {
|
||||
result.valid = false
|
||||
result.error = err.message
|
||||
|
||||
if (err.message === "jwt expired") {
|
||||
result.expired = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
result = { ...result, ...decoded }
|
||||
|
||||
const sessions = await Session.find({ user_id: result.user_id })
|
||||
const sessionsTokens = sessions.map((session) => {
|
||||
if (session.user_id === result.user_id) {
|
||||
return session.token
|
||||
}
|
||||
})
|
||||
|
||||
if (!sessionsTokens.includes(token)) {
|
||||
result.valid = false
|
||||
result.error = "Session token not found"
|
||||
} else {
|
||||
result.valid = true
|
||||
}
|
||||
})
|
||||
|
||||
return res.json(result)
|
||||
},
|
||||
}
|
@ -1,130 +1,9 @@
|
||||
import { Controller } from "linebridge/dist/server"
|
||||
import jwt from "jsonwebtoken"
|
||||
|
||||
import { Session } from "../../models"
|
||||
import { Token } from "../../lib"
|
||||
import generateEndpointsFromDir from "linebridge/dist/server/lib/generateEndpointsFromDir"
|
||||
|
||||
export default class SessionController extends Controller {
|
||||
static refName = "SessionController"
|
||||
static useRoute = "/session"
|
||||
|
||||
get = {
|
||||
"/sessions": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
// get current session _id
|
||||
const { _id } = req.user
|
||||
|
||||
const sessions = await Session.find({ user_id: _id }, { token: 0 })
|
||||
.sort({ date: -1 })
|
||||
|
||||
return res.json(sessions)
|
||||
},
|
||||
},
|
||||
"/current_session": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
return res.json(req.currentSession)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
post = {
|
||||
"/validate_session": {
|
||||
middlewares: ["useJwtStrategy"],
|
||||
fn: async (req, res) => {
|
||||
const token = req.body.session
|
||||
|
||||
let result = {
|
||||
expired: false,
|
||||
valid: true
|
||||
}
|
||||
|
||||
await jwt.verify(token, req.jwtStrategy.secretOrKey, async (err, decoded) => {
|
||||
if (err) {
|
||||
result.valid = false
|
||||
result.error = err.message
|
||||
|
||||
if (err.message === "jwt expired") {
|
||||
result.expired = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
result = { ...result, ...decoded }
|
||||
|
||||
const sessions = await Session.find({ user_id: result.user_id })
|
||||
const sessionsTokens = sessions.map((session) => {
|
||||
if (session.user_id === result.user_id) {
|
||||
return session.token
|
||||
}
|
||||
})
|
||||
|
||||
if (!sessionsTokens.includes(token)) {
|
||||
result.valid = false
|
||||
result.error = "Session token not found"
|
||||
} else {
|
||||
result.valid = true
|
||||
}
|
||||
})
|
||||
|
||||
return res.json(result)
|
||||
},
|
||||
},
|
||||
"/regenerate_session_token": {
|
||||
middlewares: ["useJwtStrategy"],
|
||||
fn: async (req, res) => {
|
||||
const { expiredToken, refreshToken } = req.body
|
||||
|
||||
const token = await Token.regenerateSession(expiredToken, refreshToken).catch((error) => {
|
||||
res.status(400).json({ error: error.message })
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
if (!token) return
|
||||
|
||||
return res.json({ token })
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
delete = {
|
||||
"/session": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const { token, user_id } = req.body
|
||||
|
||||
if (typeof user_id === "undefined") {
|
||||
return res.status(400).json("No user_id provided")
|
||||
}
|
||||
if (typeof token === "undefined") {
|
||||
return res.status(400).json("No token provided")
|
||||
}
|
||||
|
||||
const session = await Session.findOneAndDelete({ user_id, token })
|
||||
if (session) {
|
||||
return res.json("done")
|
||||
}
|
||||
|
||||
return res.status(404).json("not found")
|
||||
},
|
||||
},
|
||||
"/sessions": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const { user_id } = req.body
|
||||
|
||||
if (typeof user_id === "undefined") {
|
||||
return res.status(400).json("No user_id provided")
|
||||
}
|
||||
|
||||
const allSessions = await Session.deleteMany({ user_id })
|
||||
if (allSessions) {
|
||||
return res.json("done")
|
||||
}
|
||||
|
||||
return res.status(404).json("not found")
|
||||
}
|
||||
},
|
||||
}
|
||||
httpEndpoints = generateEndpointsFromDir(__dirname + "/endpoints")
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/stream/addresses",
|
||||
middlewares: ["withOptionalAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const addresses = {
|
||||
api: process.env.STREAMING_INGEST_SERVER,
|
||||
ingest: process.env.STREAMING_API_SERVER,
|
||||
}
|
||||
|
||||
if (req.user) {
|
||||
addresses.liveURL = `${addresses.api}/live/${req.user.username}`
|
||||
addresses.ingestURL = `${addresses.ingest}/${req.user.username}`
|
||||
|
||||
addresses.hlsURL = `${addresses.liveURL}/src.m3u8`
|
||||
addresses.flvURL = `${addresses.liveURL}/src.flv`
|
||||
}
|
||||
|
||||
return res.json(addresses)
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
import { StreamingInfo, User, StreamingCategory } from "@models"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/stream/info",
|
||||
middleware: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
let user_id = req.query.user_id
|
||||
|
||||
if (!req.query.username && !req.query.user_id) {
|
||||
return res.status(400).json({
|
||||
error: "Invalid request, missing username"
|
||||
})
|
||||
}
|
||||
|
||||
if (!user_id) {
|
||||
user_id = await User.findOne({
|
||||
username: req.query.username,
|
||||
})
|
||||
|
||||
user_id = user_id["_id"].toString()
|
||||
}
|
||||
|
||||
let info = await StreamingInfo.findOne({
|
||||
user_id,
|
||||
})
|
||||
|
||||
if (!info) {
|
||||
info = new StreamingInfo({
|
||||
user_id,
|
||||
})
|
||||
|
||||
await info.save()
|
||||
}
|
||||
|
||||
const category = await StreamingCategory.findOne({
|
||||
key: info.category
|
||||
}).catch((err) => {
|
||||
console.error(err)
|
||||
return {}
|
||||
}) ?? {}
|
||||
|
||||
return res.json({
|
||||
...info.toObject(),
|
||||
["category"]: {
|
||||
key: category?.key ?? "unknown",
|
||||
label: category?.label ?? "Unknown",
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
import { StreamingCategory } from "@models"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/streaming/categories",
|
||||
fn: async (req, res) => {
|
||||
const categories = await StreamingCategory.find()
|
||||
|
||||
return res.json(categories)
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
import { StreamingKey } from "@models"
|
||||
import generateStreamingKey from "../services/generateStreamingKey"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/streaming/key",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
let streamingKey = await StreamingKey.findOne({
|
||||
user_id: req.user._id.toString()
|
||||
})
|
||||
|
||||
if (!streamingKey) {
|
||||
const newKey = await generateStreamingKey(req.user._id.toString()).catch(err => {
|
||||
res.status(500).json({
|
||||
error: `Cannot generate a new key: ${err.message}`,
|
||||
})
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
if (!newKey) {
|
||||
return false
|
||||
}
|
||||
|
||||
return res.json(newKey)
|
||||
} else {
|
||||
return res.json(streamingKey)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
import fetchStreamsFromAPI from "../services/fetchStreamsFromAPI"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/streams",
|
||||
fn: async (req, res) => {
|
||||
const remoteStreams = await fetchStreamsFromAPI()
|
||||
|
||||
return res.json(remoteStreams)
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
import generateStreamDataFromStreamingKey from "../services/generateStreamDataFromStreamingKey"
|
||||
|
||||
// This endpoint is used by the streaming server to check if a stream is valid and to notify the clients that a stream has started
|
||||
|
||||
export default {
|
||||
method: "POST",
|
||||
route: "/stream/publish",
|
||||
fn: async (req, res) => {
|
||||
const { stream } = req.body
|
||||
|
||||
const streaming = await generateStreamDataFromStreamingKey(stream).catch((err) => {
|
||||
console.error(err)
|
||||
|
||||
res.status(500).json({
|
||||
error: `Cannot generate stream: ${err.message}`,
|
||||
})
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
if (streaming) {
|
||||
global.wsInterface.io.emit(`streaming.new`, streaming)
|
||||
|
||||
global.wsInterface.io.emit(`streaming.new.${streaming.username}`, streaming)
|
||||
|
||||
return res.json({
|
||||
code: 0,
|
||||
status: "ok"
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
import generateStreamDataFromStreamingKey from "../services/generateStreamDataFromStreamingKey"
|
||||
|
||||
export default {
|
||||
method: "POST",
|
||||
route: "/stream/unpublish",
|
||||
fn: async (req, res) => {
|
||||
const { stream } = req.body
|
||||
|
||||
const streaming = await generateStreamDataFromStreamingKey(stream).catch((err) => {
|
||||
console.error(err)
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
if (streaming) {
|
||||
global.wsInterface.io.emit(`streaming.end`, streaming)
|
||||
|
||||
global.wsInterface.io.emit(`streaming.end.${streaming.username}`, streaming)
|
||||
|
||||
return res.json({
|
||||
code: 0,
|
||||
status: "ok"
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
import { StreamingKey } from "@models"
|
||||
import generateStreamingKey from "../services/generateStreamingKey"
|
||||
|
||||
export default {
|
||||
method: "POST",
|
||||
route: "/streaming/key/regenerate",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
// check if the user already has a key
|
||||
let streamingKey = await StreamingKey.findOne({
|
||||
user_id: req.user._id.toString()
|
||||
})
|
||||
|
||||
// if exists, delete it
|
||||
|
||||
if (streamingKey) {
|
||||
await streamingKey.remove()
|
||||
}
|
||||
|
||||
// generate a new key
|
||||
const newKey = await generateStreamingKey(req.user._id.toString()).catch(err => {
|
||||
res.status(500).json({
|
||||
error: `Cannot generate a new key: ${err.message}`,
|
||||
})
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
if (!newKey) {
|
||||
return false
|
||||
}
|
||||
|
||||
return res.json(newKey)
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
import handleStreamInfoUpdate from "../services/handleStreamInfoUpdate"
|
||||
|
||||
export default {
|
||||
method: "POST",
|
||||
route: "/stream/info",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const { title, description, category, thumbnail } = req.body
|
||||
|
||||
const info = await handleStreamInfoUpdate({
|
||||
user_id: req.user._id.toString(),
|
||||
title,
|
||||
description,
|
||||
category,
|
||||
thumbnail
|
||||
}).catch((err) => {
|
||||
console.error(err)
|
||||
|
||||
res.status(500).json({
|
||||
error: `Cannot update info: ${err.message}`,
|
||||
})
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
if (info) {
|
||||
return res.json(info)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,391 +1,39 @@
|
||||
import { Controller } from "linebridge/dist/server"
|
||||
import { nanoid } from "nanoid"
|
||||
import lodash from "lodash"
|
||||
import axios from "axios"
|
||||
|
||||
import { Schematized } from "../../lib"
|
||||
import { User, StreamingKey, StreamingInfo, StreamingCategory } from "../../models"
|
||||
|
||||
const streamingIngestServer = process.env.STREAMING_INGEST_SERVER ?? ""
|
||||
const streamingServerAPIAddress = process.env.STREAMING_API_SERVER ?? ""
|
||||
|
||||
const streamingServerAPIUri = `${streamingServerAPIAddress.startsWith("https") ? "https" : "http"}://${streamingServerAPIAddress.split("://")[1]}`
|
||||
|
||||
const FILTER_KEYS = ["stream"]
|
||||
import generateEndpointsFromDir from "linebridge/dist/server/lib/generateEndpointsFromDir"
|
||||
|
||||
export default class StreamingController extends Controller {
|
||||
static refName = "StreamingController"
|
||||
static useRoute = "/tv"
|
||||
|
||||
methods = {
|
||||
genereteKey: async (user_id) => {
|
||||
// this will generate a new key for the user
|
||||
// if the user already has a key, it will be regenerated
|
||||
|
||||
// get username from user_id
|
||||
const userData = await User.findById(user_id)
|
||||
|
||||
const streamingKey = new StreamingKey({
|
||||
user_id,
|
||||
username: userData.username,
|
||||
key: nanoid()
|
||||
})
|
||||
|
||||
await streamingKey.save()
|
||||
|
||||
return streamingKey
|
||||
},
|
||||
fetchStreams: async () => {
|
||||
// fetch all streams from api
|
||||
let { data } = await axios.get(`${streamingServerAPIUri}/api/v1/streams`).catch((err) => {
|
||||
console.error(err)
|
||||
return false
|
||||
})
|
||||
|
||||
let streamings = []
|
||||
|
||||
if (!data) return streamings
|
||||
|
||||
streamings = data.streams
|
||||
|
||||
streamings = streamings.map(async (stream) => {
|
||||
const { video, audio, clients } = stream
|
||||
|
||||
stream = await this.methods.generateStreamFromStreamkey(stream.name)
|
||||
|
||||
let info = await StreamingInfo.findOne({
|
||||
user_id: stream.user_id
|
||||
})
|
||||
|
||||
if (info) {
|
||||
stream.info = info.toObject()
|
||||
|
||||
stream.info.category = await StreamingCategory.findOne({
|
||||
key: stream.info.category
|
||||
})
|
||||
}
|
||||
|
||||
stream.video = video
|
||||
stream.audio = audio
|
||||
stream.connectedClients = clients ?? 0
|
||||
|
||||
return stream
|
||||
})
|
||||
|
||||
streamings = await Promise.all(streamings)
|
||||
|
||||
return streamings.map((stream) => {
|
||||
return lodash.omit(stream, FILTER_KEYS)
|
||||
})
|
||||
},
|
||||
generateStreamFromStreamkey: async (streamKey) => {
|
||||
// generate a stream from a streamkey
|
||||
const streamingKey = await StreamingKey.findOne({
|
||||
key: streamKey
|
||||
})
|
||||
|
||||
if (!streamingKey) return false
|
||||
|
||||
const streaming = {
|
||||
user_id: streamingKey.user_id,
|
||||
username: streamingKey.username,
|
||||
sources: {
|
||||
rtmp: `${streamingIngestServer}/live/${streamingKey.username}`,
|
||||
hls: `${streamingServerAPIAddress}/live/${streamingKey.username}/src.m3u8`,
|
||||
flv: `${streamingServerAPIAddress}/live/${streamingKey.username}/src.flv`,
|
||||
}
|
||||
}
|
||||
|
||||
return streaming
|
||||
},
|
||||
handleInfoUpdate: async (payload) => {
|
||||
let info = await StreamingInfo.findOne({
|
||||
user_id: payload.user_id
|
||||
}).catch((err) => {
|
||||
return false
|
||||
})
|
||||
|
||||
const payloadValues = {
|
||||
title: payload.title,
|
||||
description: payload.description,
|
||||
category: payload.category,
|
||||
thumbnail: payload.thumbnail,
|
||||
}
|
||||
|
||||
if (!info) {
|
||||
// create new info
|
||||
info = new StreamingInfo({
|
||||
user_id: payload.user_id,
|
||||
...payloadValues
|
||||
})
|
||||
}
|
||||
|
||||
// merge data
|
||||
info = lodash.merge(info, {
|
||||
title: payload.title,
|
||||
description: payload.description,
|
||||
category: payload.category,
|
||||
thumbnail: payload.thumbnail,
|
||||
})
|
||||
|
||||
await info.save()
|
||||
|
||||
global.wsInterface.io.emit(`streaming.info_update.${payload.user_id}`, info)
|
||||
|
||||
return info
|
||||
}
|
||||
}
|
||||
|
||||
get = {
|
||||
"/streaming/categories": async (req, res) => {
|
||||
const categories = await StreamingCategory.find()
|
||||
|
||||
return res.json(categories)
|
||||
},
|
||||
"/streams": async (req, res) => {
|
||||
const remoteStreams = await this.methods.fetchStreams()
|
||||
|
||||
return res.json(remoteStreams)
|
||||
},
|
||||
"/stream/info": {
|
||||
middleware: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
let user_id = req.query.user_id
|
||||
|
||||
if (!req.query.username && !req.query.user_id) {
|
||||
return res.status(400).json({
|
||||
error: "Invalid request, missing username"
|
||||
})
|
||||
}
|
||||
|
||||
if (!user_id) {
|
||||
user_id = await User.findOne({
|
||||
username: req.query.username,
|
||||
})
|
||||
|
||||
user_id = user_id["_id"].toString()
|
||||
}
|
||||
|
||||
let info = await StreamingInfo.findOne({
|
||||
user_id,
|
||||
})
|
||||
|
||||
if (!info) {
|
||||
info = new StreamingInfo({
|
||||
user_id,
|
||||
})
|
||||
|
||||
await info.save()
|
||||
}
|
||||
|
||||
const category = await StreamingCategory.findOne({
|
||||
key: info.category
|
||||
}).catch((err) => {
|
||||
console.error(err)
|
||||
return {}
|
||||
}) ?? {}
|
||||
|
||||
return res.json({
|
||||
...info.toObject(),
|
||||
["category"]: {
|
||||
key: category?.key ?? "unknown",
|
||||
label: category?.label ?? "Unknown",
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
"/streaming/addresses": {
|
||||
middlewares: ["withOptionalAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const addresses = {
|
||||
api: streamingServerAPIAddress,
|
||||
ingest: streamingIngestServer,
|
||||
}
|
||||
|
||||
if (req.user) {
|
||||
addresses.liveURL = `${addresses.api}/live/${req.user.username}`
|
||||
addresses.ingestURL = `${addresses.ingest}/${req.user.username}`
|
||||
|
||||
addresses.hlsURL = `${addresses.liveURL}/src.m3u8`
|
||||
addresses.flvURL = `${addresses.liveURL}/src.flv`
|
||||
}
|
||||
|
||||
return res.json(addresses)
|
||||
}
|
||||
},
|
||||
"/streaming/:username": async (req, res) => {
|
||||
const { username } = req.params
|
||||
|
||||
const streamings = await this.methods.fetchStreams()
|
||||
|
||||
// search on this.streamings
|
||||
const streaming = streamings.find((streaming) => streaming.username === username)
|
||||
|
||||
if (streaming) {
|
||||
return res.json(lodash.omit(streaming, FILTER_KEYS))
|
||||
}
|
||||
|
||||
return res.status(404).json({
|
||||
error: "Stream not found"
|
||||
})
|
||||
},
|
||||
"/streaming_key": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
let streamingKey = await StreamingKey.findOne({
|
||||
user_id: req.user._id.toString()
|
||||
})
|
||||
|
||||
if (!streamingKey) {
|
||||
const newKey = await this.methods.genereteKey(req.user._id.toString()).catch(err => {
|
||||
res.status(500).json({
|
||||
error: `Cannot generate a new key: ${err.message}`,
|
||||
})
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
if (!newKey) {
|
||||
return false
|
||||
}
|
||||
|
||||
return res.json(newKey)
|
||||
} else {
|
||||
return res.json(streamingKey)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
put = {
|
||||
"/streaming/category": {
|
||||
middlewares: ["withAuthentication", "onlyAdmin"],
|
||||
fn: Schematized({
|
||||
required: ["key", "label"]
|
||||
}, async (req, res) => {
|
||||
const { key, label } = req.selection
|
||||
|
||||
const existingCategory = await StreamingCategory.findOne({
|
||||
key
|
||||
})
|
||||
|
||||
if (existingCategory) {
|
||||
return res.status(400).json({
|
||||
error: "Category already exists"
|
||||
})
|
||||
}
|
||||
|
||||
const category = new StreamingCategory({
|
||||
key,
|
||||
label,
|
||||
})
|
||||
|
||||
await category.save()
|
||||
|
||||
return res.json(category)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
post = {
|
||||
"/streaming/update_info": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const { title, description, category, thumbnail } = req.body
|
||||
|
||||
const info = await this.methods.handleInfoUpdate({
|
||||
user_id: req.user._id.toString(),
|
||||
title,
|
||||
description,
|
||||
category,
|
||||
thumbnail
|
||||
}).catch((err) => {
|
||||
console.error(err)
|
||||
|
||||
res.status(500).json({
|
||||
error: `Cannot update info: ${err.message}`,
|
||||
})
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
if (info) {
|
||||
return res.json(info)
|
||||
}
|
||||
}
|
||||
},
|
||||
"/streaming/publish": async (req, res) => {
|
||||
const { stream } = req.body
|
||||
|
||||
const streaming = await this.methods.generateStreamFromStreamkey(stream).catch((err) => {
|
||||
console.error(err)
|
||||
|
||||
res.status(500).json({
|
||||
error: `Cannot generate stream: ${err.message}`,
|
||||
})
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
if (streaming) {
|
||||
global.wsInterface.io.emit(`streaming.new`, streaming)
|
||||
|
||||
global.wsInterface.io.emit(`streaming.new.${streaming.username}`, streaming)
|
||||
|
||||
return res.json({
|
||||
code: 0,
|
||||
status: "ok"
|
||||
})
|
||||
}
|
||||
},
|
||||
"/streaming/unpublish": async (req, res) => {
|
||||
const { stream } = req.body
|
||||
|
||||
const streaming = await this.methods.generateStreamFromStreamkey(stream).catch((err) => {
|
||||
console.error(err)
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
if (streaming) {
|
||||
global.wsInterface.io.emit(`streaming.end`, streaming)
|
||||
|
||||
global.wsInterface.io.emit(`streaming.end.${streaming.username}`, streaming)
|
||||
|
||||
return res.json({
|
||||
code: 0,
|
||||
status: "ok"
|
||||
})
|
||||
}
|
||||
},
|
||||
"/regenerate_streaming_key": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
// check if the user already has a key
|
||||
let streamingKey = await StreamingKey.findOne({
|
||||
user_id: req.user._id.toString()
|
||||
})
|
||||
|
||||
// if exists, delete it
|
||||
|
||||
if (streamingKey) {
|
||||
await streamingKey.remove()
|
||||
}
|
||||
|
||||
// generate a new key
|
||||
const newKey = await this.methods.genereteKey(req.user._id.toString()).catch(err => {
|
||||
res.status(500).json({
|
||||
error: `Cannot generate a new key: ${err.message}`,
|
||||
})
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
if (!newKey) {
|
||||
return false
|
||||
}
|
||||
|
||||
return res.json(newKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
httpEndpoints = generateEndpointsFromDir(__dirname + "/endpoints")
|
||||
|
||||
// put = {
|
||||
// "/streaming/category": {
|
||||
// middlewares: ["withAuthentication", "onlyAdmin"],
|
||||
// fn: Schematized({
|
||||
// required: ["key", "label"]
|
||||
// }, async (req, res) => {
|
||||
// const { key, label } = req.selection
|
||||
|
||||
// const existingCategory = await StreamingCategory.findOne({
|
||||
// key
|
||||
// })
|
||||
|
||||
// if (existingCategory) {
|
||||
// return res.status(400).json({
|
||||
// error: "Category already exists"
|
||||
// })
|
||||
// }
|
||||
|
||||
// const category = new StreamingCategory({
|
||||
// key,
|
||||
// label,
|
||||
// })
|
||||
|
||||
// await category.save()
|
||||
|
||||
// return res.json(category)
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
import axios from "axios"
|
||||
import lodash from "lodash"
|
||||
|
||||
import { StreamingCategory } from "@models"
|
||||
import generateStreamDataFromStreamingKey from "./generateStreamDataFromStreamingKey"
|
||||
|
||||
const streamingServerAPIAddress = process.env.STREAMING_API_SERVER ?? ""
|
||||
|
||||
const streamingServerAPIUri = `${streamingServerAPIAddress.startsWith("https") ? "https" : "http"}://${streamingServerAPIAddress.split("://")[1]}`
|
||||
|
||||
const FILTER_KEYS = ["stream"]
|
||||
|
||||
export default async () => {
|
||||
// fetch all streams from api
|
||||
let { data } = await axios.get(`${streamingServerAPIUri}/api/v1/streams`).catch((err) => {
|
||||
console.error(err)
|
||||
return false
|
||||
})
|
||||
|
||||
let streamings = []
|
||||
|
||||
if (!data) return streamings
|
||||
|
||||
streamings = data.streams
|
||||
|
||||
streamings = streamings.map(async (stream) => {
|
||||
const { video, audio, clients } = stream
|
||||
|
||||
stream = await generateStreamDataFromStreamingKey(stream.name)
|
||||
|
||||
let info = await StreamingInfo.findOne({
|
||||
user_id: stream.user_id
|
||||
})
|
||||
|
||||
if (info) {
|
||||
stream.info = info.toObject()
|
||||
|
||||
stream.info.category = await StreamingCategory.findOne({
|
||||
key: stream.info.category
|
||||
})
|
||||
}
|
||||
|
||||
stream.video = video
|
||||
stream.audio = audio
|
||||
stream.connectedClients = clients ?? 0
|
||||
|
||||
return stream
|
||||
})
|
||||
|
||||
streamings = await Promise.all(streamings)
|
||||
|
||||
return streamings.map((stream) => {
|
||||
return lodash.omit(stream, FILTER_KEYS)
|
||||
})
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
import { StreamingKey } from "@models"
|
||||
|
||||
export default async (key) => {
|
||||
// generate a stream from a streamkey
|
||||
const streamingKey = await StreamingKey.findOne({
|
||||
key: key
|
||||
})
|
||||
|
||||
if (!streamingKey) return false
|
||||
|
||||
const streaming = {
|
||||
user_id: streamingKey.user_id,
|
||||
username: streamingKey.username,
|
||||
sources: {
|
||||
rtmp: `${streamingIngestServer}/live/${streamingKey.username}`,
|
||||
hls: `${streamingServerAPIAddress}/live/${streamingKey.username}/src.m3u8`,
|
||||
flv: `${streamingServerAPIAddress}/live/${streamingKey.username}/src.flv`,
|
||||
}
|
||||
}
|
||||
|
||||
return streaming
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
import { nanoid } from "nanoid"
|
||||
|
||||
import { StreamingKey, User } from "@models"
|
||||
|
||||
export default async (user_id) => {
|
||||
// this will generate a new key for the user
|
||||
// if the user already has a key, it will be regenerated
|
||||
|
||||
// get username from user_id
|
||||
const userData = await User.findById(user_id)
|
||||
|
||||
const streamingKey = new StreamingKey({
|
||||
user_id,
|
||||
username: userData.username,
|
||||
key: nanoid()
|
||||
})
|
||||
|
||||
await streamingKey.save()
|
||||
|
||||
return streamingKey
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
import { StreamingInfo } from "@models"
|
||||
|
||||
import lodash from "lodash"
|
||||
|
||||
export default async (payload) => {
|
||||
let info = await StreamingInfo.findOne({
|
||||
user_id: payload.user_id
|
||||
}).catch((err) => {
|
||||
return false
|
||||
})
|
||||
|
||||
const payloadValues = {
|
||||
title: payload.title,
|
||||
description: payload.description,
|
||||
category: payload.category,
|
||||
thumbnail: payload.thumbnail,
|
||||
}
|
||||
|
||||
if (!info) {
|
||||
// create new info
|
||||
info = new StreamingInfo({
|
||||
user_id: payload.user_id,
|
||||
...payloadValues
|
||||
})
|
||||
}
|
||||
|
||||
// merge data
|
||||
info = lodash.merge(info, {
|
||||
title: payload.title,
|
||||
description: payload.description,
|
||||
category: payload.category,
|
||||
thumbnail: payload.thumbnail,
|
||||
})
|
||||
|
||||
await info.save()
|
||||
|
||||
global.wsInterface.io.emit(`streaming.info_update.${payload.user_id}`, info)
|
||||
|
||||
return info
|
||||
}
|
@ -4,107 +4,109 @@ import SecureSyncEntry from "./classes/secureSyncEntry"
|
||||
import axios from "axios"
|
||||
|
||||
export default class SyncController extends Controller {
|
||||
static refName = "SyncController"
|
||||
static useRoute = "/sync"
|
||||
|
||||
post = {
|
||||
"/spotify/auth": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const { code, redirect_uri } = req.body
|
||||
httpEndpoints = {
|
||||
post: {
|
||||
"/spotify/auth": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const { code, redirect_uri } = req.body
|
||||
|
||||
if (!code) {
|
||||
return res.status(400).json({
|
||||
message: "Missing code",
|
||||
})
|
||||
}
|
||||
|
||||
if (!redirect_uri) {
|
||||
return res.status(400).json({
|
||||
message: "Missing redirect_uri",
|
||||
})
|
||||
}
|
||||
|
||||
const response = await axios({
|
||||
url: "https://accounts.spotify.com/api/token",
|
||||
method: "post",
|
||||
params: {
|
||||
grant_type: "authorization_code",
|
||||
code: code,
|
||||
redirect_uri: redirect_uri
|
||||
},
|
||||
headers: {
|
||||
'Authorization': `Basic ${(Buffer.from(process.env.SPOTIFY_CLIENT_ID + ":" + process.env.SPOTIFY_CLIENT_SECRET).toString("base64"))}`,
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
if (!code) {
|
||||
return res.status(400).json({
|
||||
message: "Missing code",
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
if (!response) {
|
||||
return res.status(400).json({
|
||||
message: "Missing data",
|
||||
if (!redirect_uri) {
|
||||
return res.status(400).json({
|
||||
message: "Missing redirect_uri",
|
||||
})
|
||||
}
|
||||
|
||||
const response = await axios({
|
||||
url: "https://accounts.spotify.com/api/token",
|
||||
method: "post",
|
||||
params: {
|
||||
grant_type: "authorization_code",
|
||||
code: code,
|
||||
redirect_uri: redirect_uri
|
||||
},
|
||||
headers: {
|
||||
"Authorization": `Basic ${(Buffer.from(process.env.SPOTIFY_CLIENT_ID + ":" + process.env.SPOTIFY_CLIENT_SECRET).toString("base64"))}`,
|
||||
"Content-Type": "application/x-www-form-urlencoded"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
await SecureSyncEntry.set(req.user._id.toString(), "spotify_access_token", response.data.access_token)
|
||||
await SecureSyncEntry.set(req.user._id.toString(), "spotify_refresh_token", response.data.refresh_token)
|
||||
if (!response) {
|
||||
return res.status(400).json({
|
||||
message: "Missing data",
|
||||
})
|
||||
}
|
||||
|
||||
return res.json({
|
||||
message: "ok"
|
||||
})
|
||||
}
|
||||
},
|
||||
"/spotify/unlink": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
await SecureSyncEntry.delete(req.user._id.toString(), "spotify_access_token", "")
|
||||
await SecureSyncEntry.delete(req.user._id.toString(), "spotify_refresh_token", "")
|
||||
await SecureSyncEntry.set(req.user._id.toString(), "spotify_access_token", response.data.access_token)
|
||||
await SecureSyncEntry.set(req.user._id.toString(), "spotify_refresh_token", response.data.refresh_token)
|
||||
|
||||
return res.json({
|
||||
message: "ok"
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get = {
|
||||
"/spotify/client_id": async (req, res) => {
|
||||
return res.json({
|
||||
client_id: process.env.SPOTIFY_CLIENT_ID,
|
||||
})
|
||||
},
|
||||
"/spotify/is_authorized": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const user_id = req.user._id.toString()
|
||||
const authToken = await SecureSyncEntry.get(user_id, "spotify_access_token")
|
||||
|
||||
if (!authToken) {
|
||||
return res.json({
|
||||
is_authorized: false,
|
||||
message: "ok"
|
||||
})
|
||||
}
|
||||
},
|
||||
"/spotify/unlink": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
await SecureSyncEntry.delete(req.user._id.toString(), "spotify_access_token", "")
|
||||
await SecureSyncEntry.delete(req.user._id.toString(), "spotify_refresh_token", "")
|
||||
|
||||
return res.json({
|
||||
is_authorized: true,
|
||||
})
|
||||
return res.json({
|
||||
message: "ok"
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
"/spotify/data": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const user_id = req.user._id.toString()
|
||||
const authToken = await SecureSyncEntry.get(user_id, "spotify_access_token")
|
||||
|
||||
if (!authToken) {
|
||||
return res.status(400).json({
|
||||
message: "Missing auth token",
|
||||
get: {
|
||||
"/spotify/client_id": async (req, res) => {
|
||||
return res.json({
|
||||
client_id: process.env.SPOTIFY_CLIENT_ID,
|
||||
})
|
||||
},
|
||||
"/spotify/is_authorized": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const user_id = req.user._id.toString()
|
||||
const authToken = await SecureSyncEntry.get(user_id, "spotify_access_token")
|
||||
|
||||
if (!authToken) {
|
||||
return res.json({
|
||||
is_authorized: false,
|
||||
})
|
||||
}
|
||||
|
||||
return res.json({
|
||||
is_authorized: true,
|
||||
})
|
||||
}
|
||||
},
|
||||
"/spotify/data": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const user_id = req.user._id.toString()
|
||||
const authToken = await SecureSyncEntry.get(user_id, "spotify_access_token")
|
||||
|
||||
const response = await axios.get("https://api.spotify.com/v1/me", {
|
||||
headers: {
|
||||
"Authorization": `Bearer ${authToken}`
|
||||
},
|
||||
}).catch((error) => {
|
||||
if (!authToken) {
|
||||
return res.status(400).json({
|
||||
message: "Missing auth token",
|
||||
})
|
||||
}
|
||||
|
||||
const response = await axios.get("https://api.spotify.com/v1/me", {
|
||||
headers: {
|
||||
"Authorization": `Bearer ${authToken}`
|
||||
},
|
||||
}).catch((error) => {
|
||||
console.error(error.response.data)
|
||||
|
||||
res.status(error.response.status).json(error.response.data)
|
||||
@ -112,39 +114,40 @@ export default class SyncController extends Controller {
|
||||
return null
|
||||
})
|
||||
|
||||
if (response) {
|
||||
return res.json(response.data)
|
||||
if (response) {
|
||||
return res.json(response.data)
|
||||
}
|
||||
}
|
||||
},
|
||||
"/spotify/currently_playing": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const user_id = req.user._id.toString()
|
||||
const authToken = await SecureSyncEntry.get(user_id, "spotify_access_token")
|
||||
|
||||
if (!authToken) {
|
||||
return res.status(400).json({
|
||||
message: "Missing auth token",
|
||||
})
|
||||
}
|
||||
|
||||
const response = await axios.get("https://api.spotify.com/v1/me/player", {
|
||||
headers: {
|
||||
"Authorization": `Bearer ${authToken}`
|
||||
},
|
||||
}).catch((error) => {
|
||||
console.error(error.response.data)
|
||||
|
||||
res.status(error.response.status).json(error.response.data)
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
if (response) {
|
||||
return res.json(response.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/spotify/currently_playing": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const user_id = req.user._id.toString()
|
||||
const authToken = await SecureSyncEntry.get(user_id, "spotify_access_token")
|
||||
|
||||
if (!authToken) {
|
||||
return res.status(400).json({
|
||||
message: "Missing auth token",
|
||||
})
|
||||
}
|
||||
|
||||
const response = await axios.get("https://api.spotify.com/v1/me/player", {
|
||||
headers: {
|
||||
"Authorization": `Bearer ${authToken}`
|
||||
},
|
||||
}).catch((error) => {
|
||||
console.error(error.response.data)
|
||||
|
||||
res.status(error.response.status).json(error.response.data)
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
if (response) {
|
||||
return res.json(response.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import { User } from "@models"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/username_available",
|
||||
fn: async (req, res) => {
|
||||
const user = await User.findOne({
|
||||
username: req.query.username,
|
||||
})
|
||||
|
||||
return res.json({
|
||||
available: !user,
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
import getConnectedUsersFollowing from "../methods/getConnectedUsersFollowing"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/connected_following_users",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const users = await getConnectedUsersFollowing({
|
||||
from_user_id: req.user._id.toString(),
|
||||
})
|
||||
|
||||
return res.json(users)
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
import lodash from "lodash"
|
||||
import { User } from "@models"
|
||||
|
||||
const AllowedAnonPublicGetters = [
|
||||
"_id",
|
||||
"username",
|
||||
"fullName",
|
||||
"avatar",
|
||||
"roles"
|
||||
]
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/public_data",
|
||||
middlewares: ["withOptionalAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
let user = req.query?.username ?? req.user.username
|
||||
|
||||
if (!user) {
|
||||
return res.status(400).json({
|
||||
error: "No user provided",
|
||||
})
|
||||
}
|
||||
|
||||
user = await User.findOne({
|
||||
username: user,
|
||||
}).catch(() => null)
|
||||
|
||||
if (!user) {
|
||||
return res.json({
|
||||
user: null,
|
||||
})
|
||||
}
|
||||
|
||||
user = lodash.pick(user, AllowedAnonPublicGetters)
|
||||
|
||||
return res.json(user)
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/:user_id/data",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
let user = await User.findOne(req.params.user_id)
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: "User not exists" })
|
||||
}
|
||||
|
||||
return res.json(user)
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
import { Schematized } from "@lib"
|
||||
import { User } from "@models"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/users/data",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
select: ["_id", "username"],
|
||||
}, async (req, res) => {
|
||||
let result = []
|
||||
let selectQueryKeys = []
|
||||
|
||||
if (Array.isArray(req.selection._id)) {
|
||||
for await (let _id of req.selection._id) {
|
||||
const user = await User.findById(_id).catch(err => {
|
||||
return false
|
||||
})
|
||||
if (user) {
|
||||
result.push(user)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = await User.find(req.selection, { username: 1, fullName: 1, _id: 1, roles: 1, avatar: 1 })
|
||||
}
|
||||
|
||||
if (req.query?.select) {
|
||||
try {
|
||||
req.query.select = JSON.parse(req.query.select)
|
||||
} catch (error) {
|
||||
req.query.select = {}
|
||||
}
|
||||
|
||||
selectQueryKeys = Object.keys(req.query.select)
|
||||
}
|
||||
|
||||
if (selectQueryKeys.length > 0) {
|
||||
result = result.filter(user => {
|
||||
let pass = false
|
||||
const selectFilter = req.query.select
|
||||
|
||||
selectQueryKeys.forEach(key => {
|
||||
if (Array.isArray(selectFilter[key]) && Array.isArray(user[key])) {
|
||||
// check if arrays includes any of the values
|
||||
pass = selectFilter[key].some(val => user[key].includes(val))
|
||||
} else if (typeof selectFilter[key] === "object" && typeof user[key] === "object") {
|
||||
// check if objects includes any of the values
|
||||
Object.keys(selectFilter[key]).forEach(objKey => {
|
||||
pass = user[key][objKey] === selectFilter[key][objKey]
|
||||
})
|
||||
}
|
||||
|
||||
// check if strings includes any of the values
|
||||
if (typeof selectFilter[key] === "string" && typeof user[key] === "string") {
|
||||
pass = selectFilter[key].split(",").some(val => user[key].includes(val))
|
||||
}
|
||||
})
|
||||
|
||||
return pass
|
||||
})
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
return res.status(404).json({ error: "Users not found" })
|
||||
}
|
||||
|
||||
return res.json(result)
|
||||
})
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
import UpdateUserData from "../methods/updateUserData"
|
||||
|
||||
export default {
|
||||
method: "DELETE",
|
||||
route: "/self/public_name",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const user_id = req.user._id.toString()
|
||||
|
||||
UpdateUserData.update({
|
||||
user_id: user_id,
|
||||
update: {
|
||||
fullName: undefined
|
||||
}
|
||||
})
|
||||
.then((user) => {
|
||||
return res.json({
|
||||
...user
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
return res.json(500).json({
|
||||
error: err.message
|
||||
})
|
||||
})
|
||||
},
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
import { Schematized } from "@lib"
|
||||
|
||||
import UpdateUserData from "../methods/updateUserData"
|
||||
|
||||
const AllowedPublicUpdateFields = [
|
||||
"fullName",
|
||||
"avatar",
|
||||
"email",
|
||||
"cover",
|
||||
"description",
|
||||
]
|
||||
|
||||
const MaxStringsLengths = {
|
||||
fullName: 120,
|
||||
email: 320,
|
||||
description: 2000,
|
||||
}
|
||||
|
||||
export default {
|
||||
method: "POST",
|
||||
route: "/self/update_data",
|
||||
middlewares: ["withAuthentication", "roles"],
|
||||
fn: Schematized({
|
||||
required: ["update"],
|
||||
select: ["update"],
|
||||
}, async (req, res) => {
|
||||
const user_id = req.user._id.toString()
|
||||
|
||||
let update = {}
|
||||
|
||||
AllowedPublicUpdateFields.forEach((key) => {
|
||||
if (typeof req.selection.update[key] !== "undefined") {
|
||||
// sanitize update
|
||||
// check maximung strings length
|
||||
if (typeof req.selection.update[key] === "string" && MaxStringsLengths[key]) {
|
||||
if (req.selection.update[key].length > MaxStringsLengths[key]) {
|
||||
// create a substring
|
||||
req.selection.update[key] = req.selection.update[key].substring(0, MaxStringsLengths[key])
|
||||
}
|
||||
}
|
||||
|
||||
update[key] = req.selection.update[key]
|
||||
}
|
||||
})
|
||||
|
||||
UpdateUserData.update({
|
||||
user_id: user_id,
|
||||
update: update,
|
||||
}).then((user) => {
|
||||
return res.json({
|
||||
...user
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
return res.json(500).json({
|
||||
error: err.message
|
||||
})
|
||||
})
|
||||
}),
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
import { Schematized } from "@lib"
|
||||
import { User } from "@models"
|
||||
|
||||
import updateUserPassword from "../methods/updateUserPassword"
|
||||
import bcrypt from "bcrypt"
|
||||
|
||||
export default {
|
||||
method: "POST",
|
||||
route: "/self/update_password",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
required: ["currentPassword", "newPassword"],
|
||||
select: ["currentPassword", "newPassword",]
|
||||
}, async (req, res) => {
|
||||
const user = await User.findById(req.user._id).select("+password")
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ message: "User not found" })
|
||||
}
|
||||
|
||||
const isPasswordValid = await bcrypt.compareSync(req.selection.currentPassword, user.password)
|
||||
|
||||
if (!isPasswordValid) {
|
||||
return res.status(401).json({
|
||||
message: "Current password dont match"
|
||||
})
|
||||
}
|
||||
|
||||
const result = await updateUserPassword({
|
||||
user_id: req.user._id,
|
||||
password: req.selection.newPassword,
|
||||
}).catch((error) => {
|
||||
res.status(500).json({ message: error.message })
|
||||
return null
|
||||
})
|
||||
|
||||
if (result) {
|
||||
return res.json(result)
|
||||
}
|
||||
})
|
||||
}
|
@ -1,369 +1,9 @@
|
||||
import { Controller } from "linebridge/dist/server"
|
||||
import passport from "passport"
|
||||
import lodash from "lodash"
|
||||
import bcrypt from "bcrypt"
|
||||
|
||||
import SessionController from "../SessionController"
|
||||
|
||||
import { User } from "../../models"
|
||||
import { Token, Schematized } from "../../lib"
|
||||
|
||||
import createUser from "./methods/createUser"
|
||||
import updatePassword from "./methods/updatePassword"
|
||||
import getConnectedUsersFollowing from "./methods/getConnectedUsersFollowing"
|
||||
|
||||
const AllowedPublicUpdateFields = [
|
||||
"fullName",
|
||||
"avatar",
|
||||
"email",
|
||||
"cover",
|
||||
"description",
|
||||
]
|
||||
|
||||
const AllowedAnonPublicGetters = [
|
||||
"_id",
|
||||
"username",
|
||||
"fullName",
|
||||
"avatar",
|
||||
"roles"
|
||||
]
|
||||
|
||||
const MaxStringsLengths = {
|
||||
fullName: 120,
|
||||
email: 320,
|
||||
description: 2000,
|
||||
}
|
||||
import generateEndpointsFromDir from "linebridge/dist/server/lib/generateEndpointsFromDir"
|
||||
|
||||
export default class UserController extends Controller {
|
||||
static refName = "UserController"
|
||||
static useRoute = "/user"
|
||||
|
||||
methods = {
|
||||
createNew: async (payload) => {
|
||||
const user = await createUser(payload)
|
||||
|
||||
// maybe for the future can implement a event listener for this
|
||||
|
||||
return user
|
||||
},
|
||||
update: async (payload) => {
|
||||
if (typeof payload.user_id === "undefined") {
|
||||
throw new Error("No user_id provided")
|
||||
}
|
||||
if (typeof payload.update === "undefined") {
|
||||
throw new Error("No update provided")
|
||||
}
|
||||
|
||||
let user = await User.findById(payload.user_id)
|
||||
|
||||
if (!user) {
|
||||
throw new Error("User not found")
|
||||
}
|
||||
|
||||
const updateKeys = Object.keys(payload.update)
|
||||
|
||||
updateKeys.forEach((key) => {
|
||||
user[key] = payload.update[key]
|
||||
})
|
||||
|
||||
await user.save()
|
||||
|
||||
global.wsInterface.io.emit(`user.update`, {
|
||||
...user.toObject(),
|
||||
})
|
||||
global.wsInterface.io.emit(`user.update.${payload.user_id}`, {
|
||||
...user.toObject(),
|
||||
})
|
||||
|
||||
return user.toObject()
|
||||
},
|
||||
}
|
||||
|
||||
get = {
|
||||
"/user/public_data": {
|
||||
middlewares: ["withOptionalAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
let user = req.query?.username ?? req.user.username
|
||||
|
||||
if (!user) {
|
||||
return res.status(400).json({
|
||||
error: "No user provided",
|
||||
})
|
||||
}
|
||||
|
||||
user = await User.findOne({
|
||||
username: user,
|
||||
}).catch(() => null)
|
||||
|
||||
if (!user) {
|
||||
return res.json({
|
||||
user: null,
|
||||
})
|
||||
}
|
||||
|
||||
user = lodash.pick(user, AllowedAnonPublicGetters)
|
||||
|
||||
return res.json(user)
|
||||
}
|
||||
},
|
||||
"/self": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
return res.json(req.user)
|
||||
},
|
||||
},
|
||||
"/connected_users_following": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const users = await getConnectedUsersFollowing({
|
||||
from_user_id: req.user._id.toString(),
|
||||
})
|
||||
|
||||
return res.json(users)
|
||||
}
|
||||
},
|
||||
"/user/username-available": async (req, res) => {
|
||||
const user = await User.findOne({
|
||||
username: req.query.username,
|
||||
})
|
||||
|
||||
return res.json({
|
||||
available: !user,
|
||||
})
|
||||
},
|
||||
"/user/email-available": async (req, res) => {
|
||||
const user = await User.findOne({
|
||||
email: req.query.email,
|
||||
})
|
||||
|
||||
return res.json({
|
||||
available: !user,
|
||||
})
|
||||
},
|
||||
"/user": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
select: ["_id", "username"],
|
||||
}, async (req, res) => {
|
||||
let user = await User.findOne(req.selection)
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: "User not exists" })
|
||||
}
|
||||
|
||||
return res.json(user)
|
||||
}),
|
||||
},
|
||||
"/users": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
select: ["_id", "username"],
|
||||
}, async (req, res) => {
|
||||
let result = []
|
||||
let selectQueryKeys = []
|
||||
|
||||
if (Array.isArray(req.selection._id)) {
|
||||
for await (let _id of req.selection._id) {
|
||||
const user = await User.findById(_id).catch(err => {
|
||||
return false
|
||||
})
|
||||
if (user) {
|
||||
result.push(user)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = await User.find(req.selection, { username: 1, fullName: 1, _id: 1, roles: 1, avatar: 1 })
|
||||
}
|
||||
|
||||
if (req.query?.select) {
|
||||
try {
|
||||
req.query.select = JSON.parse(req.query.select)
|
||||
} catch (error) {
|
||||
req.query.select = {}
|
||||
}
|
||||
|
||||
selectQueryKeys = Object.keys(req.query.select)
|
||||
}
|
||||
|
||||
if (selectQueryKeys.length > 0) {
|
||||
result = result.filter(user => {
|
||||
let pass = false
|
||||
const selectFilter = req.query.select
|
||||
|
||||
selectQueryKeys.forEach(key => {
|
||||
if (Array.isArray(selectFilter[key]) && Array.isArray(user[key])) {
|
||||
// check if arrays includes any of the values
|
||||
pass = selectFilter[key].some(val => user[key].includes(val))
|
||||
} else if (typeof selectFilter[key] === "object" && typeof user[key] === "object") {
|
||||
// check if objects includes any of the values
|
||||
Object.keys(selectFilter[key]).forEach(objKey => {
|
||||
pass = user[key][objKey] === selectFilter[key][objKey]
|
||||
})
|
||||
}
|
||||
|
||||
// check if strings includes any of the values
|
||||
if (typeof selectFilter[key] === "string" && typeof user[key] === "string") {
|
||||
pass = selectFilter[key].split(",").some(val => user[key].includes(val))
|
||||
}
|
||||
})
|
||||
|
||||
return pass
|
||||
})
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
return res.status(404).json({ error: "Users not found" })
|
||||
}
|
||||
|
||||
return res.json(result)
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
post = {
|
||||
"/login": async (req, res) => {
|
||||
passport.authenticate("local", { session: false }, async (error, user, options) => {
|
||||
if (error) {
|
||||
return res.status(500).json(`Error validating user > ${error.message}`)
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return res.status(401).json("Invalid credentials")
|
||||
}
|
||||
|
||||
const token = await Token.createNewAuthToken(user, options)
|
||||
|
||||
return res.json({ token: token })
|
||||
})(req, res)
|
||||
},
|
||||
"/logout": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res, next) => {
|
||||
req.body = {
|
||||
user_id: req.decodedToken.user_id,
|
||||
token: req.jwtToken
|
||||
}
|
||||
|
||||
return SessionController.delete(req, res, next)
|
||||
},
|
||||
},
|
||||
"/register": Schematized({
|
||||
required: ["username", "email", "password"],
|
||||
select: ["username", "email", "password", "fullName"],
|
||||
}, async (req, res) => {
|
||||
const result = await this.methods.createNew(req.selection).catch((err) => {
|
||||
return res.status(500).json(err.message)
|
||||
})
|
||||
|
||||
return res.json(result)
|
||||
}),
|
||||
"/self/update_password": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
required: ["currentPassword", "newPassword"],
|
||||
select: ["currentPassword", "newPassword",]
|
||||
}, async (req, res) => {
|
||||
const user = await User.findById(req.user._id).select("+password")
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ message: "User not found" })
|
||||
}
|
||||
|
||||
const isPasswordValid = await bcrypt.compareSync(req.selection.currentPassword, user.password)
|
||||
|
||||
if (!isPasswordValid) {
|
||||
return res.status(401).json({
|
||||
message: "Current password dont match"
|
||||
})
|
||||
}
|
||||
|
||||
const result = await updatePassword({
|
||||
user_id: req.user._id,
|
||||
password: req.selection.newPassword,
|
||||
}).catch((error) => {
|
||||
res.status(500).json({ message: error.message })
|
||||
return null
|
||||
})
|
||||
|
||||
if (result) {
|
||||
return res.json(result)
|
||||
}
|
||||
})
|
||||
},
|
||||
"/update_user": {
|
||||
middlewares: ["withAuthentication", "roles"],
|
||||
fn: Schematized({
|
||||
required: ["_id", "update"],
|
||||
select: ["_id", "update"],
|
||||
}, async (req, res) => {
|
||||
if (!req.selection.user_id) {
|
||||
req.selection.user_id = req.user._id.toString()
|
||||
}
|
||||
|
||||
if ((req.selection.user_id !== req.user._id.toString()) && (req.hasRole("admin") === false)) {
|
||||
return res.status(403).json({ error: "You are not allowed to update this user" })
|
||||
}
|
||||
|
||||
let update = {}
|
||||
|
||||
AllowedPublicUpdateFields.forEach((key) => {
|
||||
if (typeof req.selection.update[key] !== "undefined") {
|
||||
// sanitize update
|
||||
// check maximung strings length
|
||||
if (typeof req.selection.update[key] === "string" && MaxStringsLengths[key]) {
|
||||
if (req.selection.update[key].length > MaxStringsLengths[key]) {
|
||||
// create a substring
|
||||
req.selection.update[key] = req.selection.update[key].substring(0, MaxStringsLengths[key])
|
||||
}
|
||||
}
|
||||
|
||||
update[key] = req.selection.update[key]
|
||||
}
|
||||
})
|
||||
|
||||
this.methods.update({
|
||||
user_id: req.selection.user_id,
|
||||
update: update,
|
||||
}).then((user) => {
|
||||
return res.json({
|
||||
...user
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
return res.json(500).json({
|
||||
error: err.message
|
||||
})
|
||||
})
|
||||
}),
|
||||
},
|
||||
"/unset_public_name": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
select: ["user_id", "roles"],
|
||||
}, async (req, res) => {
|
||||
if (!req.selection.user_id) {
|
||||
req.selection.user_id = req.user._id.toString()
|
||||
}
|
||||
|
||||
if ((req.selection.user_id !== req.user._id.toString()) && (req.hasRole("admin") === false)) {
|
||||
return res.status(403).json({ error: "You are not allowed to update this user" })
|
||||
}
|
||||
|
||||
this.methods.update({
|
||||
user_id: req.selection.user_id,
|
||||
update: {
|
||||
fullName: undefined
|
||||
}
|
||||
}).then((user) => {
|
||||
return res.json({
|
||||
...user
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
return res.json(500).json({
|
||||
error: err.message
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
httpEndpoints = generateEndpointsFromDir(__dirname + "/endpoints")
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { User } from "../../../models"
|
||||
import { User } from "@models"
|
||||
import Avatars from "dicebar_lib"
|
||||
import bcrypt from "bcrypt"
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { UserFollow } from "../../../models"
|
||||
import { UserFollow } from "@models"
|
||||
|
||||
export default async (payload = {}) => {
|
||||
const { from_user_id } = payload
|
||||
|
@ -0,0 +1,33 @@
|
||||
import { User } from "@models"
|
||||
|
||||
export default async (payload) => {
|
||||
if (typeof payload.user_id === "undefined") {
|
||||
throw new Error("No user_id provided")
|
||||
}
|
||||
if (typeof payload.update === "undefined") {
|
||||
throw new Error("No update provided")
|
||||
}
|
||||
|
||||
let user = await User.findById(payload.user_id)
|
||||
|
||||
if (!user) {
|
||||
throw new Error("User not found")
|
||||
}
|
||||
|
||||
const updateKeys = Object.keys(payload.update)
|
||||
|
||||
updateKeys.forEach((key) => {
|
||||
user[key] = payload.update[key]
|
||||
})
|
||||
|
||||
await user.save()
|
||||
|
||||
global.wsInterface.io.emit(`user.update`, {
|
||||
...user.toObject(),
|
||||
})
|
||||
global.wsInterface.io.emit(`user.update.${payload.user_id}`, {
|
||||
...user.toObject(),
|
||||
})
|
||||
|
||||
return user.toObject()
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { User } from "@models"
|
||||
import bcrypt from "bcrypt"
|
||||
import { User } from "../../../models"
|
||||
|
||||
export default async function (payload) {
|
||||
const { user_id, password } = payload
|
31
packages/server/src/controllers/index.js
Executable file → Normal file
31
packages/server/src/controllers/index.js
Executable file → Normal file
@ -1,16 +1,21 @@
|
||||
export { default as ConfigController } from "./ConfigController"
|
||||
export { default as RolesController } from "./RolesController"
|
||||
export { default as SessionController } from "./SessionController"
|
||||
export { default as UserController } from "./UserController"
|
||||
export { default as FollowerController } from "./FollowerController"
|
||||
export { default as FilesController } from "./FilesController"
|
||||
export { default as PublicController } from "./PublicController"
|
||||
|
||||
export { default as UserController } from "./UserController"
|
||||
export { default as AuthController } from "./AuthController"
|
||||
export { default as SessionController } from "./SessionController"
|
||||
|
||||
export { default as FollowController } from "./FollowController"
|
||||
|
||||
export { default as PostsController } from "./PostsController"
|
||||
export { default as StreamingController } from "./StreamingController"
|
||||
export { default as BadgesController } from "./BadgesController"
|
||||
export { default as CommentsController } from "./CommentsController"
|
||||
export { default as SearchController } from "./SearchController"
|
||||
export { default as FeaturedEventsController } from "./FeaturedEventsController"
|
||||
export { default as PlaylistsController } from "./PlaylistsController"
|
||||
export { default as FeedController } from "./FeedController"
|
||||
export { default as SyncController } from "./SyncController"
|
||||
export { default as FeedController } from "./FeedController" // Needs to migrate to lb 0.15
|
||||
|
||||
export { default as StreamingController } from "./StreamingController"
|
||||
|
||||
export { default as BadgesController } from "./BadgesController"
|
||||
export { default as FeaturedEventsController } from "./FeaturedEventsController" // Needs to migrate to lb 0.15
|
||||
|
||||
export { default as PlaylistsController } from "./PlaylistsController" // Needs to migrate to lb 0.15
|
||||
export { default as FilesController } from "./FilesController" // Needs to migrate to lb 0.15
|
||||
export { default as RolesController } from "./RolesController" // Needs to migrate to lb 0.15
|
||||
export { default as SearchController } from "./SearchController" // Needs to migrate to lb 0.15
|
Loading…
x
Reference in New Issue
Block a user