mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-10 02:54:15 +00:00
369 lines
12 KiB
JavaScript
Executable File
369 lines
12 KiB
JavaScript
Executable File
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,
|
|
}
|
|
|
|
export default class UserController extends Controller {
|
|
static refName = "UserController"
|
|
|
|
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
|
|
})
|
|
})
|
|
})
|
|
}
|
|
}
|
|
} |