mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-10 02:54:15 +00:00
rewrite for linebridge 0.15
This commit is contained in:
parent
8149889587
commit
82dc237edf
@ -21,7 +21,7 @@
|
||||
"formidable": "^2.1.1",
|
||||
"jimp": "^0.16.2",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"linebridge": "0.15.2",
|
||||
"linebridge": "0.15.4",
|
||||
"luxon": "^3.2.1",
|
||||
"minio": "^7.0.32",
|
||||
"moment": "^2.29.4",
|
||||
|
@ -47,7 +47,7 @@ export default class API {
|
||||
|
||||
jwtStrategy = global.jwtStrategy = {
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
secretOrKey: this.server.oskid,
|
||||
secretOrKey: this.server.server_token,
|
||||
algorithms: ["sha1", "RS256", "HS256"],
|
||||
expiresIn: process.env.signLifetime ?? "1h",
|
||||
enforceRegenerationTokenExpiration: false,
|
||||
|
@ -2,9 +2,11 @@ import { User, Badge } from "@models"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/user",
|
||||
route: "/user/:user_id",
|
||||
fn: async (req, res) => {
|
||||
const user = await User.findOne({ _id: req.query.user_id ?? req.user._id })
|
||||
const user = await User.findOne({
|
||||
_id: req.params.user_id,
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: "User not found" })
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { Schematized } from "@lib"
|
||||
import newComment from "../methods/newComment"
|
||||
import newComment from "../services/newComment"
|
||||
|
||||
export default {
|
||||
method: "POST",
|
||||
route: "/post/:post_id",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
required: ["message"],
|
||||
select: ["message"],
|
||||
|
@ -1,4 +1,4 @@
|
||||
import deleteComment from "../methods/deleteComment"
|
||||
import deleteComment from "../services/deleteComment"
|
||||
|
||||
export default {
|
||||
method: "DELETE",
|
||||
|
@ -1,4 +1,4 @@
|
||||
import getComments from "../methods/getComments"
|
||||
import getComments from "../services/getComments"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
|
@ -26,8 +26,8 @@ export default async (payload) => {
|
||||
|
||||
await comment.delete()
|
||||
|
||||
global.wsInterface.io.emit(`comment.delete.${comment_id}`)
|
||||
global.wsInterface.io.emit(`post.delete.comment.${comment.parent_id.toString()}`, comment_id)
|
||||
global.websocket_instance.io.emit(`comment.delete.${comment_id}`)
|
||||
global.websocket_instance.io.emit(`post.delete.comment.${comment.parent_id.toString()}`, comment_id)
|
||||
|
||||
return comment
|
||||
}
|
@ -25,12 +25,12 @@ export default async (payload) => {
|
||||
|
||||
const userData = await User.findById(user_id)
|
||||
|
||||
global.wsInterface.io.emit(`comment.new.${parent_id}`, {
|
||||
global.websocket_instance.io.emit(`comment.new.${parent_id}`, {
|
||||
...comment.toObject(),
|
||||
user: userData.toObject(),
|
||||
})
|
||||
|
||||
global.wsInterface.io.emit(`post.new.comment.${parent_id}`, {
|
||||
global.websocket_instance.io.emit(`post.new.comment.${parent_id}`, {
|
||||
...comment.toObject(),
|
||||
user: userData.toObject(),
|
||||
})
|
@ -2,7 +2,7 @@ import { UserFollow } from "@models"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/user/:user_id/is_followed",
|
||||
route: "/user/:user_id",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const isFollowed = await UserFollow.findOne({
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { User, UserFollow } from "@lib"
|
||||
import { User, UserFollow } from "@models"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
|
@ -30,10 +30,10 @@ export default async (payload) => {
|
||||
|
||||
await newFollow.save()
|
||||
|
||||
global.wsInterface.io.emit(`user.follow`, {
|
||||
global.websocket_instance.io.emit(`user.follow`, {
|
||||
...user.toObject(),
|
||||
})
|
||||
global.wsInterface.io.emit(`user.follow.${payload.user_id}`, {
|
||||
global.websocket_instance.io.emit(`user.follow.${payload.user_id}`, {
|
||||
...user.toObject(),
|
||||
})
|
||||
|
||||
|
@ -25,10 +25,10 @@ export default async (payload) => {
|
||||
|
||||
await follow.remove()
|
||||
|
||||
global.wsInterface.io.emit(`user.unfollow`, {
|
||||
global.websocket_instance.io.emit(`user.unfollow`, {
|
||||
...user.toObject(),
|
||||
})
|
||||
global.wsInterface.io.emit(`user.unfollow.${payload.user_id}`, {
|
||||
global.websocket_instance.io.emit(`user.unfollow.${payload.user_id}`, {
|
||||
...user.toObject(),
|
||||
})
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Schematized } from "@lib"
|
||||
import { CreatePost } from "../methods"
|
||||
import { CreatePost } from "../services"
|
||||
|
||||
export default {
|
||||
method: "POST",
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { DeletePost } from "../methods"
|
||||
import { DeletePost } from "../services"
|
||||
|
||||
export default {
|
||||
method: "DELETE",
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Schematized } from "@lib"
|
||||
import { GetPostData } from "../methods"
|
||||
import { GetPostData } from "../services"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { GetPostData } from "../methods"
|
||||
import { GetPostData } from "../services"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/:post_id",
|
||||
route: "/post/:post_id",
|
||||
middlewares: ["withOptionalAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
let post = await GetPostData({
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { GetPostData } from "../methods"
|
||||
import { GetPostData } from "../services"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Schematized } from "@lib"
|
||||
import { GetPostData } from "../methods"
|
||||
import { GetPostData } from "../services"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { Schematized } from "@lib"
|
||||
import { ToogleLike } from "../methods"
|
||||
import { ToogleLike } from "../services"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
method: "POST",
|
||||
route: "/:post_id/toogle_like",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ToogleSavePost } from "../methods"
|
||||
import { ToogleSavePost } from "../services"
|
||||
|
||||
export default {
|
||||
method: "POST",
|
||||
|
@ -36,8 +36,8 @@ export default async (payload) => {
|
||||
|
||||
const resultPost = await getPostData({ post_id: post._id.toString() })
|
||||
|
||||
global.wsInterface.io.emit(`post.new`, resultPost)
|
||||
global.wsInterface.io.emit(`post.new.${post.user_id}`, resultPost)
|
||||
global.websocket_instance.io.emit(`post.new`, resultPost)
|
||||
global.websocket_instance.io.emit(`post.new.${post.user_id}`, resultPost)
|
||||
|
||||
// push to background job to check if is NSFW
|
||||
flagNsfwByAttachments(post._id.toString())
|
@ -27,7 +27,7 @@ export default async (payload) => {
|
||||
}
|
||||
|
||||
await post.remove()
|
||||
global.wsInterface.io.emit(`post.delete`, post_id)
|
||||
global.websocket_instance.io.emit(`post.delete`, post_id)
|
||||
|
||||
return post.toObject()
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { Post, User, Comment, SavedPost } from "../../../models"
|
||||
import fullfillPostsData from "../../../utils/fullfillPostsData"
|
||||
import { Post, SavedPost } from "@models"
|
||||
import fullfillPostsData from "@utils/fullfillPostsData"
|
||||
|
||||
export default async (payload) => {
|
||||
let {
|
@ -24,8 +24,8 @@ export default async (post_id, modification) => {
|
||||
}
|
||||
}
|
||||
|
||||
global.wsInterface.io.emit(`post.dataUpdate`, post)
|
||||
global.wsInterface.io.emit(`post.dataUpdate.${post_id._id}`, post)
|
||||
global.websocket_instance.io.emit(`post.dataUpdate`, post)
|
||||
global.websocket_instance.io.emit(`post.dataUpdate.${post_id._id}`, post)
|
||||
|
||||
return post
|
||||
}
|
@ -23,9 +23,9 @@ export default async (payload) => {
|
||||
|
||||
post = await modifyPostData(post._id, { likes: post.likes })
|
||||
|
||||
global.wsInterface.io.emit(`post.${to ? "like" : "unlike"}`, post)
|
||||
global.wsInterface.io.emit(`post.${to ? "like" : "unlike"}.${post.user_id}`, post)
|
||||
global.wsInterface.io.emit(`post.${to ? "like" : "unlike"}.${post_id}`, post)
|
||||
global.websocket_instance.io.emit(`post.${to ? "like" : "unlike"}`, post)
|
||||
global.websocket_instance.io.emit(`post.${to ? "like" : "unlike"}.${post.user_id}`, post)
|
||||
global.websocket_instance.io.emit(`post.${to ? "like" : "unlike"}.${post_id}`, post)
|
||||
|
||||
return post
|
||||
}
|
@ -15,19 +15,19 @@ export default class RolesController extends Controller {
|
||||
|
||||
return res.json(roles)
|
||||
}),
|
||||
"/user_roles": {
|
||||
"/roles/self": {
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
select: ["username"],
|
||||
}, async (req, res) => {
|
||||
const user = await User.findOne(req.selection)
|
||||
fn: async (req, res) => {
|
||||
const user = await User.findOne({
|
||||
_id: req.user._id.toString(),
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: "No user founded" })
|
||||
}
|
||||
|
||||
return res.json(user.roles)
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -2,14 +2,14 @@ import { Session } from "@models"
|
||||
|
||||
export default {
|
||||
method: "DELETE",
|
||||
route: "/",
|
||||
route: "/current",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const { token } = req.body
|
||||
const token = req.jwtToken
|
||||
const user_id = req.user._id.toString()
|
||||
|
||||
if (typeof token === "undefined") {
|
||||
return res.status(400).json("No token provided")
|
||||
return res.status(400).json("Cannot access to token")
|
||||
}
|
||||
|
||||
const session = await Session.findOneAndDelete({ user_id, token })
|
@ -0,0 +1,16 @@
|
||||
import { Endpoint } from "linebridge/dist/server"
|
||||
import getConnectedUsersFollowing from "../services/getConnectedUsersFollowing"
|
||||
|
||||
export default class GetConnectedFollowedUsers extends Endpoint {
|
||||
static method = "GET"
|
||||
static route = "/connected/following"
|
||||
static middlewares = ["withAuthentication"]
|
||||
|
||||
async fn(req, res) {
|
||||
const users = await getConnectedUsersFollowing({
|
||||
from_user_id: req.user._id.toString(),
|
||||
})
|
||||
|
||||
return res.json(users)
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
import { Controller } from "linebridge/dist/server"
|
||||
import generateEndpointsFromDir from "linebridge/dist/server/lib/generateEndpointsFromDir"
|
||||
|
||||
export default class StatusController extends Controller {
|
||||
static refName = "StatusController"
|
||||
static useRoute = "/status"
|
||||
|
||||
httpEndpoints = generateEndpointsFromDir(__dirname + "/endpoints")
|
||||
}
|
@ -12,7 +12,7 @@ export default async (payload = {}) => {
|
||||
const connectedUsers = []
|
||||
|
||||
following.forEach((follow) => {
|
||||
const connectedClient = global.wsInterface.clients.find((client) => {
|
||||
const connectedClient = global.websocket_instance.clients.find((client) => {
|
||||
return client.user_id === follow.to
|
||||
})
|
||||
|
@ -1,11 +1,11 @@
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/stream/addresses",
|
||||
route: "/streaming/addresses",
|
||||
middlewares: ["withOptionalAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const addresses = {
|
||||
api: process.env.STREAMING_INGEST_SERVER,
|
||||
ingest: process.env.STREAMING_API_SERVER,
|
||||
api: process.env.STREAMING_API_SERVER,
|
||||
ingest: process.env.STREAMING_INGEST_SERVER,
|
||||
}
|
||||
|
||||
if (req.user) {
|
||||
|
@ -14,11 +14,11 @@ export default {
|
||||
}
|
||||
|
||||
if (!user_id) {
|
||||
user_id = await User.findOne({
|
||||
const user = await User.findOne({
|
||||
username: req.query.username,
|
||||
})
|
||||
|
||||
user_id = user_id["_id"].toString()
|
||||
user_id = user._id.toString()
|
||||
}
|
||||
|
||||
let info = await StreamingInfo.findOne({
|
||||
@ -33,19 +33,6 @@ export default {
|
||||
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",
|
||||
}
|
||||
})
|
||||
return res.json(info.toObject())
|
||||
}
|
||||
}
|
@ -6,6 +6,12 @@ export default {
|
||||
fn: async (req, res) => {
|
||||
const categories = await StreamingCategory.find()
|
||||
|
||||
if (req.query.key) {
|
||||
const category = categories.find((category) => category.key === req.query.key)
|
||||
|
||||
return res.json(category)
|
||||
}
|
||||
|
||||
return res.json(categories)
|
||||
}
|
||||
}
|
@ -6,6 +6,18 @@ export default {
|
||||
fn: async (req, res) => {
|
||||
const remoteStreams = await fetchStreamsFromAPI()
|
||||
|
||||
if (req.query.username) {
|
||||
const stream = remoteStreams.find((stream) => stream.username === req.query.username)
|
||||
|
||||
if (!stream) {
|
||||
return res.status(404).json({
|
||||
error: "Stream not found"
|
||||
})
|
||||
}
|
||||
|
||||
return res.json(stream)
|
||||
}
|
||||
|
||||
return res.json(remoteStreams)
|
||||
}
|
||||
}
|
@ -19,9 +19,9 @@ export default {
|
||||
})
|
||||
|
||||
if (streaming) {
|
||||
global.wsInterface.io.emit(`streaming.new`, streaming)
|
||||
global.websocket_instance.io.emit(`streaming.new`, streaming)
|
||||
|
||||
global.wsInterface.io.emit(`streaming.new.${streaming.username}`, streaming)
|
||||
global.websocket_instance.io.emit(`streaming.new.${streaming.username}`, streaming)
|
||||
|
||||
return res.json({
|
||||
code: 0,
|
||||
|
@ -13,9 +13,9 @@ export default {
|
||||
})
|
||||
|
||||
if (streaming) {
|
||||
global.wsInterface.io.emit(`streaming.end`, streaming)
|
||||
global.websocket_instance.io.emit(`streaming.end`, streaming)
|
||||
|
||||
global.wsInterface.io.emit(`streaming.end.${streaming.username}`, streaming)
|
||||
global.websocket_instance.io.emit(`streaming.end.${streaming.username}`, streaming)
|
||||
|
||||
return res.json({
|
||||
code: 0,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import axios from "axios"
|
||||
import lodash from "lodash"
|
||||
|
||||
import { StreamingCategory } from "@models"
|
||||
import { StreamingCategory, StreamingInfo } from "@models"
|
||||
import generateStreamDataFromStreamingKey from "./generateStreamDataFromStreamingKey"
|
||||
|
||||
const streamingServerAPIAddress = process.env.STREAMING_API_SERVER ?? ""
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { StreamingKey } from "@models"
|
||||
|
||||
const streamingServerAPIUri = process.env.STREAMING_API_SERVER ? `${process.env.STREAMING_API_SERVER.startsWith("https") ? "https" : "http"}://${process.env.STREAMING_API_SERVER.split("://")[1]}` : "Not available"
|
||||
|
||||
export default async (key) => {
|
||||
// generate a stream from a streamkey
|
||||
const streamingKey = await StreamingKey.findOne({
|
||||
@ -12,9 +14,9 @@ export default async (key) => {
|
||||
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`,
|
||||
rtmp: `${process.env.STREAMING_INGEST_SERVER}/live/${streamingKey.username}`,
|
||||
hls: `${streamingServerAPIUri}/live/${streamingKey.username}/src.m3u8`,
|
||||
flv: `${streamingServerAPIUri}/live/${streamingKey.username}/src.flv`,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ export default async (payload) => {
|
||||
|
||||
await info.save()
|
||||
|
||||
global.wsInterface.io.emit(`streaming.info_update.${payload.user_id}`, info)
|
||||
global.websocket_instance.io.emit(`streaming.info_update.${payload.user_id}`, info)
|
||||
|
||||
return info
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
import getConnectedUsersFollowing from "../methods/getConnectedUsersFollowing"
|
||||
import getConnectedUsersFollowing from "../services/getConnectedUsersFollowing"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/connected_following_users",
|
||||
route: "/connected/followers",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const users = await getConnectedUsersFollowing({
|
||||
|
@ -1,39 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
@ -1,14 +1,35 @@
|
||||
import lodash from "lodash"
|
||||
import { User } from "@models"
|
||||
|
||||
const publicGetters = [
|
||||
"_id",
|
||||
"username",
|
||||
"fullName",
|
||||
"avatar",
|
||||
"roles",
|
||||
"badges",
|
||||
"cover",
|
||||
"verified",
|
||||
"description",
|
||||
]
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/:user_id/data",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
let user = await User.findOne(req.params.user_id)
|
||||
let user = await User.findOne({
|
||||
_id: req.params.user_id,
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: "User not exists" })
|
||||
}
|
||||
|
||||
if (req.user._id.toString() !== user._id.toString()) {
|
||||
user = lodash.pick(user, publicGetters)
|
||||
}
|
||||
|
||||
return res.json(user)
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
import { User } from "@models"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/user_id/:username",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
const user = await User.findOne({ username: req.params.username })
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: "User not exists" })
|
||||
}
|
||||
|
||||
return res.json({
|
||||
username: user.username,
|
||||
user_id: user._id,
|
||||
})
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import UpdateUserData from "../methods/updateUserData"
|
||||
import UpdateUserData from "../services/updateUserData"
|
||||
|
||||
export default {
|
||||
method: "DELETE",
|
||||
@ -7,7 +7,7 @@ export default {
|
||||
fn: async (req, res) => {
|
||||
const user_id = req.user._id.toString()
|
||||
|
||||
UpdateUserData.update({
|
||||
UpdateUserData({
|
||||
user_id: user_id,
|
||||
update: {
|
||||
fullName: undefined
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Schematized } from "@lib"
|
||||
|
||||
import UpdateUserData from "../methods/updateUserData"
|
||||
import { User } from "@models"
|
||||
import UpdateUserData from "../services/updateUserData"
|
||||
|
||||
const AllowedPublicUpdateFields = [
|
||||
"fullName",
|
||||
@ -19,7 +19,7 @@ const MaxStringsLengths = {
|
||||
export default {
|
||||
method: "POST",
|
||||
route: "/self/update_data",
|
||||
middlewares: ["withAuthentication", "roles"],
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: Schematized({
|
||||
required: ["update"],
|
||||
select: ["update"],
|
||||
@ -43,7 +43,20 @@ export default {
|
||||
}
|
||||
})
|
||||
|
||||
UpdateUserData.update({
|
||||
// check if email is already in use
|
||||
if (typeof update.email !== "undefined") {
|
||||
const user = await User.findOne({
|
||||
email: update.email,
|
||||
})
|
||||
|
||||
if (user) {
|
||||
return res.status(400).json({
|
||||
error: "Email is already in use",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
UpdateUserData({
|
||||
user_id: user_id,
|
||||
update: update,
|
||||
}).then((user) => {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Schematized } from "@lib"
|
||||
import { User } from "@models"
|
||||
|
||||
import updateUserPassword from "../methods/updateUserPassword"
|
||||
import updateUserPassword from "../services/updateUserPassword"
|
||||
import bcrypt from "bcrypt"
|
||||
|
||||
export default {
|
||||
|
@ -0,0 +1,25 @@
|
||||
import { UserFollow } from "@models"
|
||||
|
||||
export default async (payload = {}) => {
|
||||
const { from_user_id } = payload
|
||||
|
||||
// get all the users that are following
|
||||
const following = await UserFollow.find({
|
||||
user_id: from_user_id,
|
||||
})
|
||||
|
||||
// check if following users are connected
|
||||
const connectedUsers = []
|
||||
|
||||
following.forEach((follow) => {
|
||||
const connectedClient = global.websocket_instance.clients.find((client) => {
|
||||
return client.user_id === follow.to
|
||||
})
|
||||
|
||||
if (connectedClient) {
|
||||
connectedUsers.push(connectedClient.user_id)
|
||||
}
|
||||
})
|
||||
|
||||
return connectedUsers
|
||||
}
|
@ -22,10 +22,10 @@ export default async (payload) => {
|
||||
|
||||
await user.save()
|
||||
|
||||
global.wsInterface.io.emit(`user.update`, {
|
||||
global.websocket_instance.io.emit(`user.update`, {
|
||||
...user.toObject(),
|
||||
})
|
||||
global.wsInterface.io.emit(`user.update.${payload.user_id}`, {
|
||||
global.websocket_instance.io.emit(`user.update.${payload.user_id}`, {
|
||||
...user.toObject(),
|
||||
})
|
||||
|
@ -4,6 +4,7 @@ export { default as UserController } from "./UserController"
|
||||
export { default as AuthController } from "./AuthController"
|
||||
export { default as SessionController } from "./SessionController"
|
||||
|
||||
export { default as StatusController } from "./StatusController"
|
||||
export { default as FollowController } from "./FollowController"
|
||||
|
||||
export { default as PostsController } from "./PostsController"
|
||||
|
@ -8,7 +8,7 @@ export default async (user_id) => {
|
||||
|
||||
// send event to ws clients (if are connected)
|
||||
followers.forEach((follow) => {
|
||||
const connectedClient = global.wsInterface.clients.find((client) => {
|
||||
const connectedClient = global.websocket_instance.clients.find((client) => {
|
||||
return client.user_id === follow.user_id
|
||||
})
|
||||
|
||||
|
@ -8,7 +8,7 @@ export default async (user_id) => {
|
||||
|
||||
// send event to ws clients (if are connected)
|
||||
followers.forEach((follow) => {
|
||||
const connectedClient = global.wsInterface.clients.find((client) => {
|
||||
const connectedClient = global.websocket_instance.clients.find((client) => {
|
||||
return client.user_id === follow.user_id
|
||||
})
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
require("dotenv").config()
|
||||
const crypto = require("node:crypto")
|
||||
import { webcrypto as crypto } from "crypto"
|
||||
|
||||
// patches
|
||||
const { Buffer } = require("buffer")
|
||||
|
@ -1,11 +0,0 @@
|
||||
import { genV1 } from "../../essc"
|
||||
|
||||
export default (obj) => {
|
||||
obj.essc = genV1({
|
||||
type: obj.vaultItemTypeSelector ?? obj.type,
|
||||
serial: obj.vaultItemSerial ?? obj.serial,
|
||||
manufacturer: obj.vaultItemManufacturer ?? obj.manufacturer,
|
||||
})
|
||||
|
||||
return obj
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
export default (obj) => {
|
||||
return {
|
||||
...obj,
|
||||
...obj.properties,
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
export default (obj) => {
|
||||
const fixedKeys = {
|
||||
vaultItemManufacturer: "manufacturer",
|
||||
vaultItemSerial: "serial",
|
||||
vaultItemTypeSelector: "type",
|
||||
vaultItemManufacturedYear: "manufacturedYear",
|
||||
}
|
||||
|
||||
Object.keys(obj).forEach(key => {
|
||||
if (fixedKeys[key]) {
|
||||
obj[fixedKeys[key]] = obj[key]
|
||||
delete obj[key]
|
||||
}
|
||||
})
|
||||
|
||||
return obj
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
export default async (additions, obj) => {
|
||||
let query = []
|
||||
|
||||
if (Array.isArray(additions)) {
|
||||
query = additions
|
||||
}else {
|
||||
query.push(additions)
|
||||
}
|
||||
|
||||
for await(let addition of query) {
|
||||
try {
|
||||
let script = await import(`./handlers/${addition}.js`)
|
||||
script = script.default || script
|
||||
|
||||
obj = await script(obj)
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
// random 5 digits number
|
||||
const random5 = () => Math.floor(Math.random() * 90000) + 10000
|
||||
|
||||
// secure random 5 digits number
|
||||
const random5Secure = () => {
|
||||
const random = random5()
|
||||
return random.toString().padStart(5, '0')
|
||||
}
|
||||
|
||||
// aa-bbbbb-cccc
|
||||
//* a: type (2 digits)
|
||||
//* b: serial (5 digits)
|
||||
//* c: manufacturer (4 digits)
|
||||
|
||||
const typesNumber = {
|
||||
"computers-desktop": [1],
|
||||
"computers-laptop": [2],
|
||||
"computers-tablet": [3],
|
||||
"computers-smartphone": [4],
|
||||
"networking": [5],
|
||||
"peripherals-printer": [6],
|
||||
"peripherals-monitor": [7],
|
||||
}
|
||||
|
||||
export function genV1(params) {
|
||||
let { type, serial, manufacturer } = params // please in that order
|
||||
type = type.toLowerCase()
|
||||
|
||||
let str = []
|
||||
|
||||
// Type parsing
|
||||
let typeBuf = []
|
||||
|
||||
if (typeof typesNumber[type] === "undefined") {
|
||||
typeBuf[0] = 0
|
||||
typeBuf[1] = "X"
|
||||
} else {
|
||||
typeBuf[0] = typesNumber[type][0]
|
||||
typeBuf[1] = typesNumber[type][1] ?? "X"
|
||||
}
|
||||
|
||||
str.push(typeBuf.join(""))
|
||||
|
||||
// Serial parsing
|
||||
// if serial is not defined, generate a random 4 digits number
|
||||
if (typeof serial === "undefined") {
|
||||
str.push(random5().toString())
|
||||
} else {
|
||||
// push last 5 digits of serial, if serial is not 5 digits, pad with 0
|
||||
let serialBuf = []
|
||||
|
||||
serialBuf[0] = serial.slice(-5, -4) ?? "0"
|
||||
serialBuf[1] = serial.slice(-4, -3) ?? "0"
|
||||
serialBuf[2] = serial.slice(-3, -2) ?? "0"
|
||||
serialBuf[3] = serial.slice(-2, -1) ?? "0"
|
||||
serialBuf[4] = serial.slice(-1) ?? "0"
|
||||
|
||||
str.push(serialBuf.join(""))
|
||||
}
|
||||
|
||||
// Manufacturer parsing
|
||||
// abreviate manufacturer name to 4 letters
|
||||
if (typeof manufacturer === "undefined") {
|
||||
str.push("GENR")
|
||||
} else {
|
||||
str.push(manufacturer.slice(0, 4).toUpperCase())
|
||||
}
|
||||
|
||||
return str.join("-")
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
export { default as Schematized } from "./schematized"
|
||||
export { default as additionsHandler } from "./additionsHandler"
|
||||
|
||||
export * as Token from "./token"
|
133
packages/server/src/lib/secureEntry/index.js
Normal file
133
packages/server/src/lib/secureEntry/index.js
Normal file
@ -0,0 +1,133 @@
|
||||
import crypto from "crypto"
|
||||
|
||||
export default class SecureEntry {
|
||||
constructor(model, params = {}) {
|
||||
this.params = params
|
||||
|
||||
if (!model) {
|
||||
throw new Error("Missing model")
|
||||
}
|
||||
|
||||
this.model = model
|
||||
}
|
||||
|
||||
static get encrytionAlgorithm() {
|
||||
return "aes-256-cbc"
|
||||
}
|
||||
|
||||
async set(key, value, {
|
||||
keyName = "key",
|
||||
valueName = "value",
|
||||
}) {
|
||||
if (!keyName) {
|
||||
throw new Error("Missing keyName")
|
||||
}
|
||||
|
||||
if (!valueName) {
|
||||
throw new Error("Missing valueName")
|
||||
}
|
||||
|
||||
if (!key) {
|
||||
throw new Error("Missing key")
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
throw new Error("Missing value")
|
||||
}
|
||||
|
||||
let entry = await this.model.findOne({
|
||||
[keyName]: key,
|
||||
[valueName]: value,
|
||||
}).catch(() => null)
|
||||
|
||||
const encryptionKey = Buffer.from(process.env.SYNC_ENCRIPT_SECRET, "hex")
|
||||
const iv = crypto.randomBytes(16)
|
||||
|
||||
const cipher = crypto.createCipheriv(SecureEntry.encrytionAlgorithm, encryptionKey, iv)
|
||||
|
||||
let encryptedData
|
||||
|
||||
try {
|
||||
encryptedData = cipher.update(value)
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
|
||||
encryptedData = Buffer.concat([encryptedData, cipher.final()])
|
||||
|
||||
value = iv.toString("hex") + ":" + encryptedData.toString("hex")
|
||||
|
||||
if (entry) {
|
||||
entry[valueName] = value
|
||||
|
||||
await entry.save()
|
||||
|
||||
return entry
|
||||
}
|
||||
|
||||
entry = new this.model({
|
||||
[keyName]: key,
|
||||
[valueName]: value,
|
||||
})
|
||||
|
||||
await entry.save()
|
||||
|
||||
return entry
|
||||
}
|
||||
|
||||
async get(key, value, {
|
||||
keyName = "key",
|
||||
valueName = "value",
|
||||
}) {
|
||||
if (!keyName) {
|
||||
throw new Error("Missing keyName")
|
||||
}
|
||||
if (!key) {
|
||||
throw new Error("Missing key")
|
||||
}
|
||||
|
||||
const searchQuery = {
|
||||
[keyName]: key,
|
||||
}
|
||||
|
||||
if (value) {
|
||||
searchQuery[valueName] = value
|
||||
}
|
||||
|
||||
const entry = await this.model.findOne(searchQuery).catch(() => null)
|
||||
|
||||
if (!entry || !entry[valueName]) {
|
||||
return null
|
||||
}
|
||||
|
||||
const encryptionKey = Buffer.from(process.env.SYNC_ENCRIPT_SECRET, "hex")
|
||||
|
||||
const iv = Buffer.from(entry[valueName].split(":")[0], "hex")
|
||||
const encryptedText = Buffer.from(entry[valueName].split(":")[1], "hex")
|
||||
|
||||
const decipher = crypto.createDecipheriv(SecureEntry.encrytionAlgorithm, encryptionKey, iv)
|
||||
|
||||
let decrypted = decipher.update(encryptedText)
|
||||
|
||||
decrypted = Buffer.concat([decrypted, decipher.final()])
|
||||
|
||||
return decrypted.toString()
|
||||
}
|
||||
|
||||
async deleteByID(_id) {
|
||||
if (!_id) {
|
||||
throw new Error("Missing _id")
|
||||
}
|
||||
|
||||
const entry = await this.model.findById(_id).catch(() => null)
|
||||
|
||||
if (!entry) {
|
||||
return null
|
||||
}
|
||||
|
||||
await entry.delete()
|
||||
|
||||
return entry
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
export const errorHandler = (error, req, res, next) => {
|
||||
res.json({ error: error.message })
|
||||
}
|
||||
|
||||
export default errorHandler
|
@ -1,30 +0,0 @@
|
||||
export const hasPermissions = (req, res, next) => {
|
||||
if (typeof (req.userData) == "undefined") {
|
||||
return res.status(403).json(`User data is not available, please ensure if you are authenticated`)
|
||||
}
|
||||
|
||||
const { _id, username, roles } = req.userData
|
||||
const { permissions } = req.body
|
||||
|
||||
req.userPermissions = roles
|
||||
|
||||
let check = []
|
||||
|
||||
if (Array.isArray(permissions)) {
|
||||
check = permissions
|
||||
} else {
|
||||
check.push(permissions)
|
||||
}
|
||||
|
||||
if (check.length > 0) {
|
||||
check.forEach((role) => {
|
||||
if (!roles.includes(role)) {
|
||||
return res.status(403).json(`${username} not have permissions ${permissions}`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
next()
|
||||
}
|
||||
|
||||
export default hasPermissions
|
@ -1,12 +1,6 @@
|
||||
// const fileUpload = require("@nanoexpress/middleware-file-upload/cjs")()
|
||||
|
||||
export { default as withAuthentication } from "./withAuthentication"
|
||||
export { default as withOptionalAuthentication } from "./withOptionalAuthentication"
|
||||
|
||||
export { default as errorHandler } from "./errorHandler"
|
||||
export { default as hasPermissions } from "./hasPermissions"
|
||||
export { default as roles } from "./roles"
|
||||
export { default as onlyAdmin } from "./onlyAdmin"
|
||||
export { default as permissions } from "./permissions"
|
||||
|
||||
// export { fileUpload as fileUpload }
|
||||
export { default as permissions } from "./permissions"
|
@ -1,83 +1,119 @@
|
||||
import { Session, User } from "../../models"
|
||||
import { Token } from "../../lib"
|
||||
import { Session, User, authorizedServerTokens } from "@models"
|
||||
import { Token } from "@lib"
|
||||
|
||||
import SecureEntry from "@lib/secureEntry"
|
||||
|
||||
import jwt from "jsonwebtoken"
|
||||
|
||||
export default (req, res, next) => {
|
||||
export default async (req, res, next) => {
|
||||
function reject(description) {
|
||||
return res.status(403).json({ error: `${description ?? "Invalid session"}` })
|
||||
}
|
||||
|
||||
const authHeader = req.headers?.authorization?.split(" ")
|
||||
try {
|
||||
const tokenAuthHeader = req.headers?.authorization?.split(" ")
|
||||
const serverTokenHeader = req.headers?.server_token
|
||||
|
||||
if (authHeader && authHeader[0] === "Bearer") {
|
||||
const token = authHeader[1]
|
||||
let decoded = null
|
||||
|
||||
try {
|
||||
decoded = jwt.decode(token)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
if (!serverTokenHeader && !tokenAuthHeader) {
|
||||
return reject("Missing token header")
|
||||
}
|
||||
|
||||
if (!decoded) {
|
||||
return reject("Cannot decode token")
|
||||
if (serverTokenHeader) {
|
||||
const [client_id, token] = serverTokenHeader.split(":")
|
||||
|
||||
if (client_id === "undefined" || token === "undefined") {
|
||||
return reject("Invalid server token")
|
||||
}
|
||||
|
||||
const secureEntries = new SecureEntry(authorizedServerTokens)
|
||||
|
||||
const serverTokenEntry = await secureEntries.get(client_id, undefined, {
|
||||
keyName: "client_id",
|
||||
valueName: "token",
|
||||
})
|
||||
|
||||
if (!serverTokenEntry) {
|
||||
return reject("Invalid server token")
|
||||
}
|
||||
|
||||
if (serverTokenEntry !== token) {
|
||||
return reject("Missmatching server token")
|
||||
}
|
||||
|
||||
return next()
|
||||
}
|
||||
|
||||
jwt.verify(token, global.jwtStrategy.secretOrKey, async (err) => {
|
||||
const sessions = await Session.find({ user_id: decoded.user_id })
|
||||
const currentSession = sessions.find((session) => session.token === token)
|
||||
if (!serverTokenHeader && tokenAuthHeader && tokenAuthHeader[0] === "Bearer") {
|
||||
const token = tokenAuthHeader[1]
|
||||
let decoded = null
|
||||
|
||||
if (!currentSession) {
|
||||
return reject("Cannot find session")
|
||||
try {
|
||||
decoded = jwt.decode(token)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
|
||||
const userData = await User.findOne({ _id: currentSession.user_id }).select("+refreshToken")
|
||||
|
||||
if (!userData) {
|
||||
return res.status(404).json({ error: "No user data found" })
|
||||
if (!decoded) {
|
||||
return reject("Cannot decode token")
|
||||
}
|
||||
|
||||
// if cannot verify token, start regeneration process
|
||||
if (err) {
|
||||
// first check if token is only expired, if is corrupted, reject
|
||||
if (err.name !== "TokenExpiredError") {
|
||||
return reject("Invalid token, cannot regenerate")
|
||||
jwt.verify(token, global.jwtStrategy.secretOrKey, async (err) => {
|
||||
const sessions = await Session.find({ user_id: decoded.user_id })
|
||||
const currentSession = sessions.find((session) => session.token === token)
|
||||
|
||||
if (!currentSession) {
|
||||
return reject("Cannot find session")
|
||||
}
|
||||
|
||||
let regenerationToken = null
|
||||
const userData = await User.findOne({ _id: currentSession.user_id }).select("+refreshToken")
|
||||
|
||||
// check if this expired token has a regeneration token associated
|
||||
const associatedRegenerationToken = await Token.getRegenerationToken(token)
|
||||
if (!userData) {
|
||||
return res.status(404).json({ error: "No user data found" })
|
||||
}
|
||||
|
||||
if (associatedRegenerationToken) {
|
||||
regenerationToken = associatedRegenerationToken.refreshToken
|
||||
} else {
|
||||
// create a new regeneration token with the expired token
|
||||
regenerationToken = await Token.createNewRegenerationToken(token).catch((error) => {
|
||||
// in case of error, reject
|
||||
reject(error.message)
|
||||
// if cannot verify token, start regeneration process
|
||||
if (err) {
|
||||
// first check if token is only expired, if is corrupted, reject
|
||||
if (err.name !== "TokenExpiredError") {
|
||||
return reject("Invalid token, cannot regenerate")
|
||||
}
|
||||
|
||||
return null
|
||||
let regenerationToken = null
|
||||
|
||||
// check if this expired token has a regeneration token associated
|
||||
const associatedRegenerationToken = await Token.getRegenerationToken(token)
|
||||
|
||||
if (associatedRegenerationToken) {
|
||||
regenerationToken = associatedRegenerationToken.refreshToken
|
||||
} else {
|
||||
// create a new regeneration token with the expired token
|
||||
regenerationToken = await Token.createNewRegenerationToken(token).catch((error) => {
|
||||
// in case of error, reject
|
||||
reject(error.message)
|
||||
|
||||
return null
|
||||
})
|
||||
}
|
||||
|
||||
if (!regenerationToken) return
|
||||
|
||||
// now send the regeneration token to the client (start Expired Exception Event [EEE])
|
||||
return res.status(401).json({
|
||||
error: "Token expired",
|
||||
refreshToken: regenerationToken.refreshToken,
|
||||
})
|
||||
}
|
||||
|
||||
if (!regenerationToken) return
|
||||
req.user = userData
|
||||
req.jwtToken = token
|
||||
req.decodedToken = decoded
|
||||
req.currentSession = currentSession
|
||||
|
||||
// now send the regeneration token to the client (start Expired Exception Event [EEE])
|
||||
return res.status(401).json({
|
||||
error: "Token expired",
|
||||
refreshToken: regenerationToken.refreshToken,
|
||||
})
|
||||
}
|
||||
|
||||
req.user = userData
|
||||
req.jwtToken = token
|
||||
req.decodedToken = decoded
|
||||
req.currentSession = currentSession
|
||||
|
||||
return next()
|
||||
})
|
||||
} else {
|
||||
return reject("Missing token header")
|
||||
return next()
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return res.status(500).json({ error: "This endpoint needs authentication, but an error occurred." })
|
||||
}
|
||||
}
|
||||
|
28
packages/server/src/models/authorizedServerTokens/index.js
Normal file
28
packages/server/src/models/authorizedServerTokens/index.js
Normal file
@ -0,0 +1,28 @@
|
||||
export default {
|
||||
name: "authorizedServerTokens",
|
||||
collection: "authorizedServerTokens",
|
||||
schema: {
|
||||
client_id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
token: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
access: {
|
||||
type: Array,
|
||||
default: [],
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
},
|
||||
createdAt: {
|
||||
type: Date,
|
||||
default: Date.now,
|
||||
},
|
||||
}
|
||||
}
|
29
packages/server/src/setup/authorizeSelfServerToken/index.js
Normal file
29
packages/server/src/setup/authorizeSelfServerToken/index.js
Normal file
@ -0,0 +1,29 @@
|
||||
import SecureEntry from "@lib/secureEntry"
|
||||
import { authorizedServerTokens } from "@models"
|
||||
|
||||
const rootClientID = "00000000-0000-0000-000000000000"
|
||||
|
||||
export default async () => {
|
||||
// check if process.env.SERVER_TOKEN is included in authorizedServerKeys
|
||||
if (process.env.SERVER_TOKEN) {
|
||||
console.log("Checking if server token is authorized on server tokens list...")
|
||||
|
||||
const secureEntries = new SecureEntry(authorizedServerTokens)
|
||||
|
||||
const currentServerToken = await secureEntries.get(rootClientID, undefined, {
|
||||
keyName: "client_id",
|
||||
})
|
||||
|
||||
// check if match or not exist, if not, update
|
||||
if (currentServerToken !== process.env.SERVER_TOKEN) {
|
||||
console.log("Server token is not authorized on server tokens list, updating...")
|
||||
|
||||
await secureEntries.set(rootClientID, process.env.SERVER_TOKEN, {
|
||||
keyName: "client_id",
|
||||
valueName: "token",
|
||||
})
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { User } from "../../models"
|
||||
import createUser from "../../controllers/UserController/methods/createUser"
|
||||
import { User } from "@models"
|
||||
import createUser from "@controllers/UserController/services/createUser"
|
||||
|
||||
export default async () => {
|
||||
// check if any user with includes admin role exists
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { default as dbAdmin } from "./dbAdmin"
|
||||
import { default as authorizeSelfServerToken } from "./authorizeSelfServerToken"
|
||||
|
||||
// set here the setup functions
|
||||
export default [
|
||||
dbAdmin,
|
||||
authorizeSelfServerToken,
|
||||
]
|
@ -1,4 +1,4 @@
|
||||
import { User, Comment, SavedPost } from "../../models"
|
||||
import { User, Comment, SavedPost } from "@models"
|
||||
|
||||
export default async (payload) => {
|
||||
let {
|
||||
|
Loading…
x
Reference in New Issue
Block a user