From 24d6203d74d5fcb41b88533dbbbc9381996d8d8b Mon Sep 17 00:00:00 2001 From: SrGooglo Date: Sat, 9 Mar 2024 23:43:54 +0000 Subject: [PATCH] merge from local --- api-ports.md | 2 +- packages/server/classes/AuthToken/index.js | 2 +- packages/server/db_models/post/index.js | 3 +- packages/server/db_models/user/index.js | 8 +- .../withOptionalAuthentication/index.js | 6 +- .../auth/classes/account/methods/create.js | 13 +- .../classes/account/methods/loginStrategy.js | 2 +- .../services/auth/routes/availability/get.js | 19 ++- .../services/auth/routes/forgot/post.js | 4 +- .../services/feed/FeedController/index.js | 140 ------------------ .../services/getGlobalReleases.js | 25 ---- .../services/getPlaylistsFromFollowing.js | 42 ------ .../feed/FeedController/services/getPosts.js | 39 ----- .../services/getReleasesFromFollowing.js | 40 ----- .../server/services/main/controllers/index.js | 3 - packages/server/services/main/main.service.js | 8 +- .../server/services/main/middlewares/index.js | 5 - .../main/middlewares/onlyAdmin/index.js | 7 - .../services/main/middlewares/roles/index.js | 19 --- .../middlewares/withAuthentication/index.js | 118 --------------- .../withOptionalAuthentication/index.js | 9 -- .../services/posts/classes/posts/index.js | 12 +- .../posts/classes/posts/methods/create.js | 20 +-- .../posts/classes/posts/methods/data.js | 39 +---- .../posts/classes/posts/methods/feed.js | 43 ++++++ .../posts/classes/posts/methods/flag.js | 3 + .../posts/classes/posts/methods/fromUserId.js | 25 ++++ .../posts/classes/posts/methods/fullfill.js | 6 +- .../posts/classes/posts/methods/getLiked.js | 24 +++ .../posts/classes/posts/methods/getSaved.js | 24 +++ .../posts/classes/posts/methods/report.js | 3 + .../posts/classes/posts/methods/toggleLike.js | 52 +++++++ .../posts/classes/posts/methods/toggleSave.js | 41 +++++ .../server/services/posts/routes/feed/get.js | 19 +++ .../routes/posts/[post_id]/{ => data}/get.js | 0 .../posts/[post_id]/toggle_like/post.js | 13 ++ .../posts/[post_id]/toggle_save/post.js | 13 ++ .../services/posts/routes/posts/liked/get.js | 10 ++ .../services/posts/routes/posts/new/post.js | 17 ++- .../services/posts/routes/posts/saved/get.js | 10 ++ .../services/posts/routes/posts/test/get.js | 10 -- .../posts/routes/posts/user/[user_id]/get.js | 13 ++ .../services/users/classes/users/index.js | 3 + .../users/classes/users/method/data.js | 21 +++ .../services/{feed => users}/package.json | 2 +- .../users/routes/users/[user_id]/data/get.js | 13 ++ .../users.service.js} | 8 +- 47 files changed, 414 insertions(+), 544 deletions(-) delete mode 100755 packages/server/services/feed/FeedController/index.js delete mode 100755 packages/server/services/feed/FeedController/services/getGlobalReleases.js delete mode 100755 packages/server/services/feed/FeedController/services/getPlaylistsFromFollowing.js delete mode 100755 packages/server/services/feed/FeedController/services/getPosts.js delete mode 100755 packages/server/services/feed/FeedController/services/getReleasesFromFollowing.js delete mode 100755 packages/server/services/main/middlewares/onlyAdmin/index.js delete mode 100755 packages/server/services/main/middlewares/roles/index.js delete mode 100755 packages/server/services/main/middlewares/withAuthentication/index.js delete mode 100755 packages/server/services/main/middlewares/withOptionalAuthentication/index.js create mode 100644 packages/server/services/posts/classes/posts/methods/feed.js create mode 100644 packages/server/services/posts/classes/posts/methods/flag.js create mode 100644 packages/server/services/posts/classes/posts/methods/fromUserId.js create mode 100644 packages/server/services/posts/classes/posts/methods/getLiked.js create mode 100644 packages/server/services/posts/classes/posts/methods/getSaved.js create mode 100644 packages/server/services/posts/classes/posts/methods/report.js create mode 100644 packages/server/services/posts/classes/posts/methods/toggleLike.js create mode 100644 packages/server/services/posts/classes/posts/methods/toggleSave.js create mode 100644 packages/server/services/posts/routes/feed/get.js rename packages/server/services/posts/routes/posts/[post_id]/{ => data}/get.js (100%) create mode 100644 packages/server/services/posts/routes/posts/[post_id]/toggle_like/post.js create mode 100644 packages/server/services/posts/routes/posts/[post_id]/toggle_save/post.js create mode 100644 packages/server/services/posts/routes/posts/liked/get.js create mode 100644 packages/server/services/posts/routes/posts/saved/get.js delete mode 100644 packages/server/services/posts/routes/posts/test/get.js create mode 100644 packages/server/services/posts/routes/posts/user/[user_id]/get.js create mode 100644 packages/server/services/users/classes/users/index.js create mode 100644 packages/server/services/users/classes/users/method/data.js rename packages/server/services/{feed => users}/package.json (77%) create mode 100644 packages/server/services/users/routes/users/[user_id]/data/get.js rename packages/server/services/{feed/feed.service.js => users/users.service.js} (68%) diff --git a/api-ports.md b/api-ports.md index ec6a7441..b8bfce21 100644 --- a/api-ports.md +++ b/api-ports.md @@ -8,7 +8,7 @@ 3005 -> marketplace 3006 -> sync 3007 -> ems (External Messaging Service) -3008 -> unallocated +3008 -> users 3009 -> unallocated 3010 -> unallocated 3011 -> unallocated diff --git a/packages/server/classes/AuthToken/index.js b/packages/server/classes/AuthToken/index.js index 52baa40a..801093b0 100644 --- a/packages/server/classes/AuthToken/index.js +++ b/packages/server/classes/AuthToken/index.js @@ -5,7 +5,7 @@ export default class Token { static get strategy() { return { secret: process.env.JWT_SECRET, - expiresIn: process.env.JWT_EXPIRES_IN ?? "1h", + expiresIn: process.env.JWT_EXPIRES_IN ?? "24h", algorithm: process.env.JWT_ALGORITHM ?? "HS256", } } diff --git a/packages/server/db_models/post/index.js b/packages/server/db_models/post/index.js index 3db664b6..8914aad7 100755 --- a/packages/server/db_models/post/index.js +++ b/packages/server/db_models/post/index.js @@ -3,8 +3,7 @@ export default { collection: "posts", schema: { user_id: { type: String, required: true }, - timestamp: { type: String, required: true }, - created_at: { type: Date, default: Date.now, required: true }, + created_at: { type: String, required: true }, message: { type: String }, attachments: { type: Array, default: [] }, flags: { type: Array, default: [] }, diff --git a/packages/server/db_models/user/index.js b/packages/server/db_models/user/index.js index f9efdfba..b0d2dbaf 100755 --- a/packages/server/db_models/user/index.js +++ b/packages/server/db_models/user/index.js @@ -4,18 +4,16 @@ export default { schema: { username: { type: String, required: true }, password: { type: String, required: true, select: false }, - email: { type: String, required: true }, + email: { type: String, required: true, select: false }, description: { type: String, default: null }, + created_at: { type: String }, public_name: { type: String, default: null }, - fullName: { type: String, default: null }, cover: { type: String, default: null }, avatar: { type: String, default: null }, roles: { type: Array, default: [] }, verified: { type: Boolean, default: false }, - birthday: { type: Date, default: null }, badges: { type: Array, default: [] }, links: { type: Array, default: [] }, - createdAt: { type: String }, - created_at: { type: String }, + birthday: { type: Date, default: null }, } } \ No newline at end of file diff --git a/packages/server/middlewares/withOptionalAuthentication/index.js b/packages/server/middlewares/withOptionalAuthentication/index.js index 4a03cd05..ba0c3d7a 100755 --- a/packages/server/middlewares/withOptionalAuthentication/index.js +++ b/packages/server/middlewares/withOptionalAuthentication/index.js @@ -1,9 +1,7 @@ import withAuthentication from "../withAuthentication" -export default (req, res, next) => { +export default async (req, res, next) => { if (req.headers?.authorization) { - withAuthentication(req, res, next) - } else { - next() + await withAuthentication(req, res, next) } } \ No newline at end of file diff --git a/packages/server/services/auth/classes/account/methods/create.js b/packages/server/services/auth/classes/account/methods/create.js index c9d6f0f0..fc45e06d 100644 --- a/packages/server/services/auth/classes/account/methods/create.js +++ b/packages/server/services/auth/classes/account/methods/create.js @@ -6,7 +6,7 @@ import Account from "@classes/account" export default async (payload) => { requiredFields(["username", "password", "email"], payload) - let { username, password, email, fullName, roles, avatar, acceptTos } = payload + let { username, password, email, public_name, roles, avatar, acceptTos } = payload if (ToBoolean(acceptTos) !== true) { throw new OperationError(400, "You must accept the terms of service in order to create an account.") @@ -15,14 +15,17 @@ export default async (payload) => { await Account.usernameMeetPolicy(username) // check if username is already taken - const existentUser = await User.findOne({ username: username }) + const existentUser = await User + .findOne({ username: username }) if (existentUser) { throw new OperationError(400, "User already exists") } // check if the email is already in use - const existentEmail = await User.findOne({ email: email }) + const existentEmail = await User + .findOne({ email: email }) + .select("+email") if (existentEmail) { throw new OperationError(400, "Email already in use") @@ -37,10 +40,10 @@ export default async (payload) => { username: username, password: hash, email: email, - fullName: fullName, + public_name: public_name, avatar: avatar ?? `https://api.dicebear.com/7.x/thumbs/svg?seed=${username}`, roles: roles, - createdAt: new Date().getTime(), + created_at: new Date().getTime(), acceptTos: acceptTos, }) diff --git a/packages/server/services/auth/classes/account/methods/loginStrategy.js b/packages/server/services/auth/classes/account/methods/loginStrategy.js index 6dc87e06..2d8afc8d 100644 --- a/packages/server/services/auth/classes/account/methods/loginStrategy.js +++ b/packages/server/services/auth/classes/account/methods/loginStrategy.js @@ -7,7 +7,7 @@ export default async ({ username, password, hash }, user) => { let query = isEmail ? { email: username } : { username: username } - user = await User.findOne(query).select("+password") + user = await User.findOne(query).select("+email").select("+password") } if (!user) { diff --git a/packages/server/services/auth/routes/availability/get.js b/packages/server/services/auth/routes/availability/get.js index 89f58b0f..6973e271 100644 --- a/packages/server/services/auth/routes/availability/get.js +++ b/packages/server/services/auth/routes/availability/get.js @@ -7,14 +7,17 @@ export default async (req) => { throw new OperationError(400, "Missing username or email") } - const user = await User.findOne({ - $or: [ - { username: username }, - { email: email }, - ] - }).catch((error) => { - return false - }) + const user = await User + .findOne({ + $or: [ + { username: username }, + { email: email }, + ] + }) + .select("+email") + .catch((error) => { + return false + }) if (user) { return { diff --git a/packages/server/services/auth/routes/forgot/post.js b/packages/server/services/auth/routes/forgot/post.js index 654fe71a..3228cfdd 100644 --- a/packages/server/services/auth/routes/forgot/post.js +++ b/packages/server/services/auth/routes/forgot/post.js @@ -6,7 +6,9 @@ export default async (req) => { const { email } = req.body - const user = await User.findOne({ email }) + const user = await User + .findOne({ email }) + .select("+email") if (!user) { throw new OperationError(400, "User not found") diff --git a/packages/server/services/feed/FeedController/index.js b/packages/server/services/feed/FeedController/index.js deleted file mode 100755 index 6260e6e4..00000000 --- a/packages/server/services/feed/FeedController/index.js +++ /dev/null @@ -1,140 +0,0 @@ -import { Controller } from "linebridge/dist/server" - -import pmap from "p-map" - -import getPosts from "./services/getPosts" - -import getGlobalReleases from "./services/getGlobalReleases" -import getReleasesFromFollowing from "./services/getReleasesFromFollowing" -import getPlaylistsFromFollowing from "./services/getPlaylistsFromFollowing" - -export default class FeedController extends Controller { - static refName = "FeedController" - static useRoute = "/feed" - - httpEndpoints = { - get: { - "/timeline": { - 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 posts - let posts = await getPosts({ - for_user_id, - limit: req.query?.limit, - skip: req.query?.trim, - }) - - // add type to posts and playlists - posts = posts.map((data) => { - data.type = "post" - - return data - }) - - let feed = [ - ...posts, - ] - - // sort feed - feed.sort((a, b) => { - return new Date(b.created_at) - new Date(a.created_at) - }) - - return res.json(feed) - } - }, - "/music/global": { - 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 from global - const result = await getGlobalReleases({ - for_user_id, - limit: req.query?.limit, - skip: req.query?.trim, - }) - - return res.json(result) - } - }, - "/music": { - 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" - }) - } - - const searchers = [ - getGlobalReleases, - //getReleasesFromFollowing, - //getPlaylistsFromFollowing, - ] - - let result = await pmap( - searchers, - async (fn, index) => { - const data = await fn({ - for_user_id, - limit: req.query?.limit, - skip: req.query?.trim, - }) - - return data - }, { - concurrency: 3, - },) - - result = result.reduce((acc, cur) => { - return [...acc, ...cur] - }, []) - - return res.json(result) - } - }, - "/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" - }) - } - - 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) - } - }, - } - } -} \ No newline at end of file diff --git a/packages/server/services/feed/FeedController/services/getGlobalReleases.js b/packages/server/services/feed/FeedController/services/getGlobalReleases.js deleted file mode 100755 index d0c88316..00000000 --- a/packages/server/services/feed/FeedController/services/getGlobalReleases.js +++ /dev/null @@ -1,25 +0,0 @@ -import { Release } from "@db_models" - -export default async (payload) => { - const { - limit = 20, - skip = 0, - } = payload - - let releases = await Release.find({ - $or: [ - { public: true }, - ] - }) - .sort({ created_at: -1 }) - .limit(limit) - .skip(skip) - - releases = Promise.all(releases.map(async (release) => { - release = release.toObject() - - return release - })) - - return releases -} \ No newline at end of file diff --git a/packages/server/services/feed/FeedController/services/getPlaylistsFromFollowing.js b/packages/server/services/feed/FeedController/services/getPlaylistsFromFollowing.js deleted file mode 100755 index 9faac13d..00000000 --- a/packages/server/services/feed/FeedController/services/getPlaylistsFromFollowing.js +++ /dev/null @@ -1,42 +0,0 @@ -import { Playlist, User, UserFollow } from "@db_models" - -export default async (payload) => { - const { - for_user_id, - limit = 20, - skip = 0, - } = payload - - // get post from users that the user follows - const followingUsers = await UserFollow.find({ - user_id: for_user_id - }) - - const followingUserIds = followingUsers.map((followingUser) => followingUser.to) - - const fetchFromUserIds = [ - for_user_id, - ...followingUserIds, - ] - - // firter out the playlists that are not public - let playlists = await Playlist.find({ - user_id: { $in: fetchFromUserIds }, - $or: [ - { public: true }, - ] - }) - .sort({ created_at: -1 }) - .limit(limit) - .skip(skip) - - playlists = Promise.all(playlists.map(async (playlist) => { - playlist = playlist.toObject() - - playlist.type = "playlist" - - return playlist - })) - - return playlists -} \ No newline at end of file diff --git a/packages/server/services/feed/FeedController/services/getPosts.js b/packages/server/services/feed/FeedController/services/getPosts.js deleted file mode 100755 index 4d68c706..00000000 --- a/packages/server/services/feed/FeedController/services/getPosts.js +++ /dev/null @@ -1,39 +0,0 @@ -import { Post, UserFollow } from "@db_models" - -import fullfillPostsData from "@utils/fullfillPostsData" - -export default async (payload) => { - const { - for_user_id, - limit = 20, - skip = 0, - } = payload - - // get post from users that the user follows - const followingUsers = await UserFollow.find({ - user_id: for_user_id - }) - - const followingUserIds = followingUsers.map((followingUser) => followingUser.to) - - const fetchPostsFromIds = [ - for_user_id, - ...followingUserIds, - ] - - let posts = await Post.find({ - user_id: { $in: fetchPostsFromIds } - }) - .sort({ created_at: -1 }) - .limit(limit) - .skip(skip) - - // fullfill data - posts = await fullfillPostsData({ - posts, - for_user_id, - skip, - }) - - return posts -} \ No newline at end of file diff --git a/packages/server/services/feed/FeedController/services/getReleasesFromFollowing.js b/packages/server/services/feed/FeedController/services/getReleasesFromFollowing.js deleted file mode 100755 index bd35e20f..00000000 --- a/packages/server/services/feed/FeedController/services/getReleasesFromFollowing.js +++ /dev/null @@ -1,40 +0,0 @@ -import { Release, UserFollow } from "@db_models" - -export default async (payload) => { - const { - for_user_id, - limit = 20, - skip = 0, - } = payload - - // get post from users that the user follows - const followingUsers = await UserFollow.find({ - user_id: for_user_id - }) - - const followingUserIds = followingUsers.map((followingUser) => followingUser.to) - - const fetchFromUserIds = [ - for_user_id, - ...followingUserIds, - ] - - // firter out the releases that are not public - let releases = await Release.find({ - user_id: { $in: fetchFromUserIds }, - $or: [ - { public: true }, - ] - }) - .sort({ created_at: -1 }) - .limit(limit) - .skip(skip) - - releases = Promise.all(releases.map(async (release) => { - release = release.toObject() - - return release - })) - - return releases -} \ No newline at end of file diff --git a/packages/server/services/main/controllers/index.js b/packages/server/services/main/controllers/index.js index dae3a62c..48b289cb 100755 --- a/packages/server/services/main/controllers/index.js +++ b/packages/server/services/main/controllers/index.js @@ -1,7 +1,4 @@ -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 StatusController } from "./StatusController" diff --git a/packages/server/services/main/main.service.js b/packages/server/services/main/main.service.js index 0b2c4f98..7d22e91d 100755 --- a/packages/server/services/main/main.service.js +++ b/packages/server/services/main/main.service.js @@ -6,6 +6,8 @@ import StorageClient from "@shared-classes/StorageClient" import Token from "@lib/token" +import SharedMiddlewares from "@shared-middlewares" + export default class API extends Server { static refName = "main" static useEngine = "hyper-express" @@ -22,7 +24,11 @@ export default class API extends Server { } } - middlewares = require("@middlewares") + middlewares = { + ...require("@middlewares").default, + ...SharedMiddlewares + } + controllers = require("@controllers") events = require("./events") diff --git a/packages/server/services/main/middlewares/index.js b/packages/server/services/main/middlewares/index.js index 28f1685b..9c53482e 100755 --- a/packages/server/services/main/middlewares/index.js +++ b/packages/server/services/main/middlewares/index.js @@ -1,6 +1 @@ -export { default as withAuthentication } from "./withAuthentication" -export { default as withOptionalAuthentication } from "./withOptionalAuthentication" - -export { default as roles } from "./roles" -export { default as onlyAdmin } from "./onlyAdmin" export { default as permissions } from "./permissions" \ No newline at end of file diff --git a/packages/server/services/main/middlewares/onlyAdmin/index.js b/packages/server/services/main/middlewares/onlyAdmin/index.js deleted file mode 100755 index 730faba8..00000000 --- a/packages/server/services/main/middlewares/onlyAdmin/index.js +++ /dev/null @@ -1,7 +0,0 @@ -export default (req, res, next) => { - if (!req.user.roles.includes("admin")) { - return res.status(403).json({ error: "To make this request it is necessary to have administrator permissions" }) - } - - next() -} \ No newline at end of file diff --git a/packages/server/services/main/middlewares/roles/index.js b/packages/server/services/main/middlewares/roles/index.js deleted file mode 100755 index 16c9e3c3..00000000 --- a/packages/server/services/main/middlewares/roles/index.js +++ /dev/null @@ -1,19 +0,0 @@ -export default (req, res, next) => { - req.isAdmin = () => { - if (req.user.roles.includes("admin")) { - return true - } - - return false - } - - req.hasRole = (role) => { - if (req.user.roles.includes(role)) { - return true - } - - return false - } - - next() -} \ No newline at end of file diff --git a/packages/server/services/main/middlewares/withAuthentication/index.js b/packages/server/services/main/middlewares/withAuthentication/index.js deleted file mode 100755 index 73b852b8..00000000 --- a/packages/server/services/main/middlewares/withAuthentication/index.js +++ /dev/null @@ -1,118 +0,0 @@ -import { Session, User, authorizedServerTokens } from "@db_models" - -import createTokenRegeneration from "@utils/createTokenRegeneration" - -import SecureEntry from "@lib/secureEntry" - -import jwt from "jsonwebtoken" - -export default async (req, res, next) => { - function reject(description) { - return res.status(401).json({ error: `${description ?? "Invalid session"}` }) - } - - try { - const tokenAuthHeader = req.headers?.authorization?.split(" ") - - if (!tokenAuthHeader) { - return reject("Missing token header") - } - - if (!tokenAuthHeader[1]) { - return reject("Recived header, missing token") - } - - switch (tokenAuthHeader[0]) { - case "Bearer": { - const token = tokenAuthHeader[1] - let decoded = null - - try { - decoded = jwt.decode(token) - } catch (error) { - console.error(error) - } - - if (!decoded) { - return reject("Cannot decode token") - } - - 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") - } - - const userData = await User.findOne({ _id: currentSession.user_id }).select("+refreshToken") - - if (!userData) { - return reject("Cannot find user") - } - - // 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") - } - - const refreshToken = await createTokenRegeneration(token) - - // now send the regeneration token to the client (start Expired Exception Event [EEE]) - return res.status(401).json({ - error: "Token expired", - refreshToken: refreshToken, - }) - } - - req.user = userData - req.jwtToken = token - req.decodedToken = decoded - req.currentSession = currentSession - - return next() - }) - - break - } - case "Server": { - const [client_id, token] = tokenAuthHeader[1].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") - } - - req.user = { - __server: true, - _id: client_id, - roles: ["server"], - } - - return next() - } - default: { - return reject("Invalid token type") - } - } - } catch (error) { - console.error(error) - return res.status(500).json({ error: "This endpoint needs authentication, but an error occurred." }) - } -} diff --git a/packages/server/services/main/middlewares/withOptionalAuthentication/index.js b/packages/server/services/main/middlewares/withOptionalAuthentication/index.js deleted file mode 100755 index 4a03cd05..00000000 --- a/packages/server/services/main/middlewares/withOptionalAuthentication/index.js +++ /dev/null @@ -1,9 +0,0 @@ -import withAuthentication from "../withAuthentication" - -export default (req, res, next) => { - if (req.headers?.authorization) { - withAuthentication(req, res, next) - } else { - next() - } -} \ No newline at end of file diff --git a/packages/server/services/posts/classes/posts/index.js b/packages/server/services/posts/classes/posts/index.js index 844afa3a..9cd34dd0 100644 --- a/packages/server/services/posts/classes/posts/index.js +++ b/packages/server/services/posts/classes/posts/index.js @@ -1,7 +1,13 @@ export default class Posts { - static create = require("./methods/create").default - + static feed = require("./methods/feed").default static data = require("./methods/data").default - + static getLiked = require("./methods/getLiked").default + static getSaved = require("./methods/getSaved").default + static fromUserId = require("./methods/fromUserId").default + static create = require("./methods/create").default static fullfillPost = require("./methods/fullfill").default + static toggleSave = require("./methods/toggleSave").default + static toggleLike = require("./methods/toggleLike").default + static report = require("./methods/report").default + static flag = require("./methods/flag").default } \ No newline at end of file diff --git a/packages/server/services/posts/classes/posts/methods/create.js b/packages/server/services/posts/classes/posts/methods/create.js index 7237ad00..87bd047a 100644 --- a/packages/server/services/posts/classes/posts/methods/create.js +++ b/packages/server/services/posts/classes/posts/methods/create.js @@ -1,14 +1,10 @@ -import momentTimezone from "moment-timezone" import requiredFields from "@shared-utils/requiredFields" +import { DateTime } from "luxon" import { Post } from "@db_models" -export default async (payload) => { - if (!payload) { - throw new Error("Payload is required") - } - - await requiredFields(["user_id", "timestamp"], payload) +export default async (payload = {}) => { + await requiredFields(["user_id"], payload) let { user_id, message, attachments, timestamp, reply_to } = payload @@ -16,18 +12,18 @@ export default async (payload) => { const isAttachmentsValid = Array.isArray(attachments) && attachments.length > 0 if (!isAttachmentsValid && !message) { - throw new Error("Cannot create a post without message or attachments") + throw new OperationError(400, "Cannot create a post without message or attachments") } - const current_timezone = momentTimezone.tz.guess() - const created_at = momentTimezone.tz(Date.now(), current_timezone).format() + if (!timestamp) { + timestamp = DateTime.local().toISO() + } let post = new Post({ - created_at: created_at, + created_at: timestamp, user_id: typeof user_id === "object" ? user_id.toString() : user_id, message: message, attachments: attachments ?? [], - timestamp: timestamp, reply_to: reply_to, flags: [], }) diff --git a/packages/server/services/posts/classes/posts/methods/data.js b/packages/server/services/posts/classes/posts/methods/data.js index e2a18ead..a0b079c2 100644 --- a/packages/server/services/posts/classes/posts/methods/data.js +++ b/packages/server/services/posts/classes/posts/methods/data.js @@ -1,38 +1,17 @@ -import { Post, SavedPost } from "@db_models" +import { Post } from "@db_models" import fullfillPostsData from "./fullfill" -export default async (payload) => { +export default async (payload = {}) => { let { - from_user_id, for_user_id, post_id, query = {}, skip = 0, limit = 20, sort = { created_at: -1 }, - savedOnly = false, } = payload let posts = [] - let savedPostsIds = [] - - // if for_user_id is provided, get saved posts - if (for_user_id) { - const savedPosts = await SavedPost.find({ user_id: for_user_id }) - .sort({ saved_at: -1 }) - - savedPostsIds = savedPosts.map((savedPost) => savedPost.post_id) - } - - // if from_user_id is provided, get posts from that user - if (from_user_id) { - query.user_id = from_user_id - } - - // if savedOnly is true,set to query to get only saved posts - if (savedOnly) { - query._id = { $in: savedPostsIds } - } if (post_id) { try { @@ -49,16 +28,6 @@ export default async (payload) => { .limit(limit) } - // short posts if is savedOnly argument - if (savedOnly) { - posts.sort((a, b) => { - return ( - savedPostsIds.indexOf(a._id.toString()) - - savedPostsIds.indexOf(b._id.toString()) - ) - }) - } - // fullfill data posts = await fullfillPostsData({ posts, @@ -68,6 +37,10 @@ export default async (payload) => { // if post_id is specified, return only one post if (post_id) { + if (posts.length === 0) { + throw new OperationError(404, "Post not found") + } + return posts[0] } diff --git a/packages/server/services/posts/classes/posts/methods/feed.js b/packages/server/services/posts/classes/posts/methods/feed.js new file mode 100644 index 00000000..df292bfd --- /dev/null +++ b/packages/server/services/posts/classes/posts/methods/feed.js @@ -0,0 +1,43 @@ +import { UserFollow } from "@db_models" + +import GetPostData from "./data" + +export default async (payload = {}) => { + let { + user_id, + skip, + limit, + } = payload + + let query = {} + + //TODO: include posts from groups + //TODO: include promotional posts + if (user_id) { + const from_users = [] + + from_users.push(user_id) + + // get post from users that the user follows + const followingUsers = await UserFollow.find({ + user_id: user_id + }) + + const followingUserIds = followingUsers.map((followingUser) => followingUser.to) + + from_users.push(...followingUserIds) + + query.user_id = { + $in: from_users + } + } + + const posts = await GetPostData({ + for_user_id: user_id, + skip, + limit, + query: query, + }) + + return posts +} \ No newline at end of file diff --git a/packages/server/services/posts/classes/posts/methods/flag.js b/packages/server/services/posts/classes/posts/methods/flag.js new file mode 100644 index 00000000..38502f3b --- /dev/null +++ b/packages/server/services/posts/classes/posts/methods/flag.js @@ -0,0 +1,3 @@ +export default async () => { + throw new OperationError(501, "Not implemented") +} \ No newline at end of file diff --git a/packages/server/services/posts/classes/posts/methods/fromUserId.js b/packages/server/services/posts/classes/posts/methods/fromUserId.js new file mode 100644 index 00000000..0ac5e5e7 --- /dev/null +++ b/packages/server/services/posts/classes/posts/methods/fromUserId.js @@ -0,0 +1,25 @@ +import GetData from "./data" + +export default async (payload = {}) => { + const { + for_user_id, + user_id, + skip, + limit, + } = payload + + if (!user_id) { + throw new OperationError(400, "Missing user_id") + } + + return await GetData({ + for_user_id: for_user_id, + skip, + limit, + query: { + user_id: { + $in: user_id + } + } + }) +} \ No newline at end of file diff --git a/packages/server/services/posts/classes/posts/methods/fullfill.js b/packages/server/services/posts/classes/posts/methods/fullfill.js index d9a10abc..6551b608 100644 --- a/packages/server/services/posts/classes/posts/methods/fullfill.js +++ b/packages/server/services/posts/classes/posts/methods/fullfill.js @@ -1,6 +1,6 @@ import { User, Comment, PostLike, SavedPost } from "@db_models" -export default async (payload) => { +export default async (payload = {}) => { let { posts, for_user_id, @@ -28,7 +28,9 @@ export default async (payload) => { _id: { $in: posts.map((post) => post.user_id) } - }), + }) + .select("-email") + .select("-birthday"), PostLike.find({ post_id: { $in: posts.map((post) => post._id) diff --git a/packages/server/services/posts/classes/posts/methods/getLiked.js b/packages/server/services/posts/classes/posts/methods/getLiked.js new file mode 100644 index 00000000..bd823c92 --- /dev/null +++ b/packages/server/services/posts/classes/posts/methods/getLiked.js @@ -0,0 +1,24 @@ +import { PostLike } from "@db_models" +import GetData from "./data" + +export default async (payload = {}) => { + let { user_id } = payload + + if (!user_id) { + throw new OperationError(400, "Missing user_id") + } + + let ids = await PostLike.find({ user_id }) + + ids = ids.map((item) => item.post_id) + + return await GetData({ + for_user_id: user_id, + query: { + _id: { + $in: ids + } + } + }) +} + diff --git a/packages/server/services/posts/classes/posts/methods/getSaved.js b/packages/server/services/posts/classes/posts/methods/getSaved.js new file mode 100644 index 00000000..c9ce760a --- /dev/null +++ b/packages/server/services/posts/classes/posts/methods/getSaved.js @@ -0,0 +1,24 @@ +import { SavedPost } from "@db_models" +import GetData from "./data" + +export default async (payload = {}) => { + let { user_id } = payload + + if (!user_id) { + throw new OperationError(400, "Missing user_id") + } + + let ids = await SavedPost.find({ user_id }) + + ids = ids.map((item) => item.post_id) + + return await GetData({ + for_user_id: user_id, + query: { + _id: { + $in: ids + } + } + }) +} + diff --git a/packages/server/services/posts/classes/posts/methods/report.js b/packages/server/services/posts/classes/posts/methods/report.js new file mode 100644 index 00000000..38502f3b --- /dev/null +++ b/packages/server/services/posts/classes/posts/methods/report.js @@ -0,0 +1,3 @@ +export default async () => { + throw new OperationError(501, "Not implemented") +} \ No newline at end of file diff --git a/packages/server/services/posts/classes/posts/methods/toggleLike.js b/packages/server/services/posts/classes/posts/methods/toggleLike.js new file mode 100644 index 00000000..f31cee20 --- /dev/null +++ b/packages/server/services/posts/classes/posts/methods/toggleLike.js @@ -0,0 +1,52 @@ +import { Post, PostLike } from "@db_models" + +export default async (payload = {}) => { + let { post_id, user_id, to } = payload + + if (!post_id || !user_id) { + throw new OperationError(400, "Missing post_id or user_id") + } + + // check if post exist + let existPost = await Post.findOne({ + post_id, + }) + + if (!existPost) { + throw new OperationError(404, "Post not found") + } + + let likeObj = await PostLike.findOne({ + post_id, + user_id, + }) + + if (typeof to === "undefined") { + if (likeObj) { + to = false + } else { + to = true + } + } + + if (to) { + likeObj = new PostLike({ + post_id, + user_id, + }) + + await likeObj.save() + } else { + await PostLike.findByIdAndDelete(likeObj._id) + } + + // global.engine.ws.io.of("/").emit(`post.${post_id}.likes.update`, { + // to, + // post_id, + // user_id, + // }) + + return { + liked: to + } +} \ No newline at end of file diff --git a/packages/server/services/posts/classes/posts/methods/toggleSave.js b/packages/server/services/posts/classes/posts/methods/toggleSave.js new file mode 100644 index 00000000..3ae9c83a --- /dev/null +++ b/packages/server/services/posts/classes/posts/methods/toggleSave.js @@ -0,0 +1,41 @@ +import { Post, SavedPost } from "@db_models" + +export default async (payload = {}) => { + let { post_id, user_id } = payload + + if (!post_id || !user_id) { + throw new OperationError(400, "Missing post_id or user_id") + } + + // check if post exist + let existPost = await Post.findOne({ + post_id, + }) + + if (!existPost) { + throw new OperationError(404, "Post not found") + } + + let post = await SavedPost.findOne({ post_id, user_id }) + + if (post) { + await SavedPost.findByIdAndDelete(post._id).catch((err) => { + throw new OperationError(500, `An error has occurred: ${err.message}`) + }) + + post = null + } else { + post = new SavedPost({ + post_id, + user_id, + }) + + await post.save().catch((err) => { + throw new OperationError(500, `An error has occurred: ${err.message}`) + }) + } + + return { + saved: !!post, + } +} \ No newline at end of file diff --git a/packages/server/services/posts/routes/feed/get.js b/packages/server/services/posts/routes/feed/get.js new file mode 100644 index 00000000..3bad7282 --- /dev/null +++ b/packages/server/services/posts/routes/feed/get.js @@ -0,0 +1,19 @@ +import Posts from "@classes/posts" + +export default { + middlewares: ["withOptionalAuthentication"], + fn: async (req, res) => { + const payload = { + limit: req.query?.limit, + skip: req.query?.skip, + } + + if (req.auth) { + payload.user_id = req.auth.session.user_id + } + + const result = await Posts.feed(payload) + + return result + } +} \ No newline at end of file diff --git a/packages/server/services/posts/routes/posts/[post_id]/get.js b/packages/server/services/posts/routes/posts/[post_id]/data/get.js similarity index 100% rename from packages/server/services/posts/routes/posts/[post_id]/get.js rename to packages/server/services/posts/routes/posts/[post_id]/data/get.js diff --git a/packages/server/services/posts/routes/posts/[post_id]/toggle_like/post.js b/packages/server/services/posts/routes/posts/[post_id]/toggle_like/post.js new file mode 100644 index 00000000..4232b97b --- /dev/null +++ b/packages/server/services/posts/routes/posts/[post_id]/toggle_like/post.js @@ -0,0 +1,13 @@ +import Posts from "@classes/posts" + +export default { + middlewares: ["withAuthentication"], + fn: async (req, res) => { + const result = await Posts.toggleLike({ + post_id: req.params.post_id, + user_id: req.auth.session.user_id + }) + + return result + } +} \ No newline at end of file diff --git a/packages/server/services/posts/routes/posts/[post_id]/toggle_save/post.js b/packages/server/services/posts/routes/posts/[post_id]/toggle_save/post.js new file mode 100644 index 00000000..4d2bbfd5 --- /dev/null +++ b/packages/server/services/posts/routes/posts/[post_id]/toggle_save/post.js @@ -0,0 +1,13 @@ +import Posts from "@classes/posts" + +export default { + middlewares: ["withAuthentication"], + fn: async (req, res) => { + const result = await Posts.toggleSave({ + post_id: req.params.post_id, + user_id: req.auth.session.user_id + }) + + return result + } +} \ No newline at end of file diff --git a/packages/server/services/posts/routes/posts/liked/get.js b/packages/server/services/posts/routes/posts/liked/get.js new file mode 100644 index 00000000..10ebfe66 --- /dev/null +++ b/packages/server/services/posts/routes/posts/liked/get.js @@ -0,0 +1,10 @@ +import Posts from "@classes/posts" + +export default { + middlewares: ["withAuthentication"], + fn: async (req) => { + return await Posts.getLiked({ + user_id: req.auth.session.user_id + }) + } +} \ No newline at end of file diff --git a/packages/server/services/posts/routes/posts/new/post.js b/packages/server/services/posts/routes/posts/new/post.js index d1d290fd..e6f857c2 100644 --- a/packages/server/services/posts/routes/posts/new/post.js +++ b/packages/server/services/posts/routes/posts/new/post.js @@ -1,6 +1,13 @@ -export default async (req, res) => { - return res.json({ - code: 0, - message: "success", - }) +import Posts from "@classes/posts" + +export default { + middlewares: ["withAuthentication"], + fn: async (req, res) => { + const result = await Posts.create({ + ...req.body, + user_id: req.auth.session.user_id, + }) + + return result + } } \ No newline at end of file diff --git a/packages/server/services/posts/routes/posts/saved/get.js b/packages/server/services/posts/routes/posts/saved/get.js new file mode 100644 index 00000000..b4b97c77 --- /dev/null +++ b/packages/server/services/posts/routes/posts/saved/get.js @@ -0,0 +1,10 @@ +import Posts from "@classes/posts" + +export default { + middlewares: ["withAuthentication"], + fn: async (req) => { + return await Posts.getSaved({ + user_id: req.auth.session.user_id + }) + } +} \ No newline at end of file diff --git a/packages/server/services/posts/routes/posts/test/get.js b/packages/server/services/posts/routes/posts/test/get.js deleted file mode 100644 index 88e61886..00000000 --- a/packages/server/services/posts/routes/posts/test/get.js +++ /dev/null @@ -1,10 +0,0 @@ -export default async (req, res) => { - await new Promise((r) => { - setTimeout(r, 1000) - }) - - return { - code: 0, - message: "success", - } -} \ No newline at end of file diff --git a/packages/server/services/posts/routes/posts/user/[user_id]/get.js b/packages/server/services/posts/routes/posts/user/[user_id]/get.js new file mode 100644 index 00000000..481b2105 --- /dev/null +++ b/packages/server/services/posts/routes/posts/user/[user_id]/get.js @@ -0,0 +1,13 @@ +import Posts from "@classes/posts" + +export default { + middlewares: ["withOptionalAuthentication"], + fn: async (req, res) => { + return await Posts.fromUserId({ + skip: req.query.skip, + limit: req.query.limit, + user_id: req.params.user_id, + for_user_id: req.auth?.session?.user_id, + }) + } +} \ No newline at end of file diff --git a/packages/server/services/users/classes/users/index.js b/packages/server/services/users/classes/users/index.js new file mode 100644 index 00000000..20345c19 --- /dev/null +++ b/packages/server/services/users/classes/users/index.js @@ -0,0 +1,3 @@ +export default class Users { + static data = require("./method/data").default +} \ No newline at end of file diff --git a/packages/server/services/users/classes/users/method/data.js b/packages/server/services/users/classes/users/method/data.js new file mode 100644 index 00000000..56c43ac7 --- /dev/null +++ b/packages/server/services/users/classes/users/method/data.js @@ -0,0 +1,21 @@ +import { User } from "@db_models" + +export default async (payload = {}) => { + const { user_id, from_user_id } = payload + + if (!user_id) { + throw new OperationError(400, "Missing user_id") + } + + const user = await User.findOne({ + _id: user_id, + }).catch((err) => { + return false + }) + + if (!user) { + throw new OperationError(404, "User not found") + } + + return user +} \ No newline at end of file diff --git a/packages/server/services/feed/package.json b/packages/server/services/users/package.json similarity index 77% rename from packages/server/services/feed/package.json rename to packages/server/services/users/package.json index 941c7b1e..41a56905 100644 --- a/packages/server/services/feed/package.json +++ b/packages/server/services/users/package.json @@ -1,5 +1,5 @@ { - "name": "feed", + "name": "users", "version": "1.0.0", "main": "index.js", "license": "MIT" diff --git a/packages/server/services/users/routes/users/[user_id]/data/get.js b/packages/server/services/users/routes/users/[user_id]/data/get.js new file mode 100644 index 00000000..7c205939 --- /dev/null +++ b/packages/server/services/users/routes/users/[user_id]/data/get.js @@ -0,0 +1,13 @@ +import Users from "@classes/users" + +export default { + middlewares: ["withOptionalAuthentication"], + fn: async (req) => { + const { user_id } = req.params + + return await Users.data({ + user_id: user_id, + from_user_id: req.auth?.session.user_id, + }) + } +} \ No newline at end of file diff --git a/packages/server/services/feed/feed.service.js b/packages/server/services/users/users.service.js similarity index 68% rename from packages/server/services/feed/feed.service.js rename to packages/server/services/users/users.service.js index 6e41e198..8f878048 100644 --- a/packages/server/services/feed/feed.service.js +++ b/packages/server/services/users/users.service.js @@ -1,13 +1,15 @@ import { Server } from "linebridge/src/server" + import DbManager from "@shared-classes/DbManager" +import RedisClient from "@shared-classes/RedisClient" import SharedMiddlewares from "@shared-middlewares" export default class API extends Server { - static refName = "feed" + static refName = "users" static useEngine = "hyper-express" static routesPath = `${__dirname}/routes` - static listen_port = process.env.HTTP_LISTEN_PORT ?? 3007 + static listen_port = process.env.HTTP_LISTEN_PORT ?? 3008 middlewares = { ...SharedMiddlewares @@ -15,10 +17,12 @@ export default class API extends Server { contexts = { db: new DbManager(), + redis: RedisClient() } async onInitialize() { await this.contexts.db.initialize() + await this.contexts.redis.initialize() } }