merge from local

This commit is contained in:
SrGooglo 2024-03-09 23:43:54 +00:00
parent 6aba03e310
commit 24d6203d74
47 changed files with 414 additions and 544 deletions

View File

@ -8,7 +8,7 @@
3005 -> marketplace 3005 -> marketplace
3006 -> sync 3006 -> sync
3007 -> ems (External Messaging Service) 3007 -> ems (External Messaging Service)
3008 -> unallocated 3008 -> users
3009 -> unallocated 3009 -> unallocated
3010 -> unallocated 3010 -> unallocated
3011 -> unallocated 3011 -> unallocated

View File

@ -5,7 +5,7 @@ export default class Token {
static get strategy() { static get strategy() {
return { return {
secret: process.env.JWT_SECRET, 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", algorithm: process.env.JWT_ALGORITHM ?? "HS256",
} }
} }

View File

@ -3,8 +3,7 @@ export default {
collection: "posts", collection: "posts",
schema: { schema: {
user_id: { type: String, required: true }, user_id: { type: String, required: true },
timestamp: { type: String, required: true }, created_at: { type: String, required: true },
created_at: { type: Date, default: Date.now, required: true },
message: { type: String }, message: { type: String },
attachments: { type: Array, default: [] }, attachments: { type: Array, default: [] },
flags: { type: Array, default: [] }, flags: { type: Array, default: [] },

View File

@ -4,18 +4,16 @@ export default {
schema: { schema: {
username: { type: String, required: true }, username: { type: String, required: true },
password: { type: String, required: true, select: false }, password: { type: String, required: true, select: false },
email: { type: String, required: true }, email: { type: String, required: true, select: false },
description: { type: String, default: null }, description: { type: String, default: null },
created_at: { type: String },
public_name: { type: String, default: null }, public_name: { type: String, default: null },
fullName: { type: String, default: null },
cover: { type: String, default: null }, cover: { type: String, default: null },
avatar: { type: String, default: null }, avatar: { type: String, default: null },
roles: { type: Array, default: [] }, roles: { type: Array, default: [] },
verified: { type: Boolean, default: false }, verified: { type: Boolean, default: false },
birthday: { type: Date, default: null },
badges: { type: Array, default: [] }, badges: { type: Array, default: [] },
links: { type: Array, default: [] }, links: { type: Array, default: [] },
createdAt: { type: String }, birthday: { type: Date, default: null },
created_at: { type: String },
} }
} }

View File

@ -1,9 +1,7 @@
import withAuthentication from "../withAuthentication" import withAuthentication from "../withAuthentication"
export default (req, res, next) => { export default async (req, res, next) => {
if (req.headers?.authorization) { if (req.headers?.authorization) {
withAuthentication(req, res, next) await withAuthentication(req, res, next)
} else {
next()
} }
} }

View File

@ -6,7 +6,7 @@ import Account from "@classes/account"
export default async (payload) => { export default async (payload) => {
requiredFields(["username", "password", "email"], 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) { if (ToBoolean(acceptTos) !== true) {
throw new OperationError(400, "You must accept the terms of service in order to create an account.") 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) await Account.usernameMeetPolicy(username)
// check if username is already taken // check if username is already taken
const existentUser = await User.findOne({ username: username }) const existentUser = await User
.findOne({ username: username })
if (existentUser) { if (existentUser) {
throw new OperationError(400, "User already exists") throw new OperationError(400, "User already exists")
} }
// check if the email is already in use // 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) { if (existentEmail) {
throw new OperationError(400, "Email already in use") throw new OperationError(400, "Email already in use")
@ -37,10 +40,10 @@ export default async (payload) => {
username: username, username: username,
password: hash, password: hash,
email: email, email: email,
fullName: fullName, public_name: public_name,
avatar: avatar ?? `https://api.dicebear.com/7.x/thumbs/svg?seed=${username}`, avatar: avatar ?? `https://api.dicebear.com/7.x/thumbs/svg?seed=${username}`,
roles: roles, roles: roles,
createdAt: new Date().getTime(), created_at: new Date().getTime(),
acceptTos: acceptTos, acceptTos: acceptTos,
}) })

View File

@ -7,7 +7,7 @@ export default async ({ username, password, hash }, user) => {
let query = isEmail ? { email: username } : { username: username } 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) { if (!user) {

View File

@ -7,14 +7,17 @@ export default async (req) => {
throw new OperationError(400, "Missing username or email") throw new OperationError(400, "Missing username or email")
} }
const user = await User.findOne({ const user = await User
$or: [ .findOne({
{ username: username }, $or: [
{ email: email }, { username: username },
] { email: email },
}).catch((error) => { ]
return false })
}) .select("+email")
.catch((error) => {
return false
})
if (user) { if (user) {
return { return {

View File

@ -6,7 +6,9 @@ export default async (req) => {
const { email } = req.body const { email } = req.body
const user = await User.findOne({ email }) const user = await User
.findOne({ email })
.select("+email")
if (!user) { if (!user) {
throw new OperationError(400, "User not found") throw new OperationError(400, "User not found")

View File

@ -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)
}
},
}
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -1,7 +1,4 @@
export { default as PublicController } from "./PublicController"
export { default as UserController } from "./UserController" export { default as UserController } from "./UserController"
export { default as AuthController } from "./AuthController"
export { default as SessionController } from "./SessionController" export { default as SessionController } from "./SessionController"
export { default as StatusController } from "./StatusController" export { default as StatusController } from "./StatusController"

View File

@ -6,6 +6,8 @@ import StorageClient from "@shared-classes/StorageClient"
import Token from "@lib/token" import Token from "@lib/token"
import SharedMiddlewares from "@shared-middlewares"
export default class API extends Server { export default class API extends Server {
static refName = "main" static refName = "main"
static useEngine = "hyper-express" 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") controllers = require("@controllers")
events = require("./events") events = require("./events")

View File

@ -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" export { default as permissions } from "./permissions"

View File

@ -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()
}

View File

@ -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()
}

View File

@ -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." })
}
}

View File

@ -1,9 +0,0 @@
import withAuthentication from "../withAuthentication"
export default (req, res, next) => {
if (req.headers?.authorization) {
withAuthentication(req, res, next)
} else {
next()
}
}

View File

@ -1,7 +1,13 @@
export default class Posts { export default class Posts {
static create = require("./methods/create").default static feed = require("./methods/feed").default
static data = require("./methods/data").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 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
} }

View File

@ -1,14 +1,10 @@
import momentTimezone from "moment-timezone"
import requiredFields from "@shared-utils/requiredFields" import requiredFields from "@shared-utils/requiredFields"
import { DateTime } from "luxon"
import { Post } from "@db_models" import { Post } from "@db_models"
export default async (payload) => { export default async (payload = {}) => {
if (!payload) { await requiredFields(["user_id"], payload)
throw new Error("Payload is required")
}
await requiredFields(["user_id", "timestamp"], payload)
let { user_id, message, attachments, timestamp, reply_to } = 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 const isAttachmentsValid = Array.isArray(attachments) && attachments.length > 0
if (!isAttachmentsValid && !message) { 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() if (!timestamp) {
const created_at = momentTimezone.tz(Date.now(), current_timezone).format() timestamp = DateTime.local().toISO()
}
let post = new Post({ let post = new Post({
created_at: created_at, created_at: timestamp,
user_id: typeof user_id === "object" ? user_id.toString() : user_id, user_id: typeof user_id === "object" ? user_id.toString() : user_id,
message: message, message: message,
attachments: attachments ?? [], attachments: attachments ?? [],
timestamp: timestamp,
reply_to: reply_to, reply_to: reply_to,
flags: [], flags: [],
}) })

View File

@ -1,38 +1,17 @@
import { Post, SavedPost } from "@db_models" import { Post } from "@db_models"
import fullfillPostsData from "./fullfill" import fullfillPostsData from "./fullfill"
export default async (payload) => { export default async (payload = {}) => {
let { let {
from_user_id,
for_user_id, for_user_id,
post_id, post_id,
query = {}, query = {},
skip = 0, skip = 0,
limit = 20, limit = 20,
sort = { created_at: -1 }, sort = { created_at: -1 },
savedOnly = false,
} = payload } = payload
let posts = [] 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) { if (post_id) {
try { try {
@ -49,16 +28,6 @@ export default async (payload) => {
.limit(limit) .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 // fullfill data
posts = await fullfillPostsData({ posts = await fullfillPostsData({
posts, posts,
@ -68,6 +37,10 @@ export default async (payload) => {
// if post_id is specified, return only one post // if post_id is specified, return only one post
if (post_id) { if (post_id) {
if (posts.length === 0) {
throw new OperationError(404, "Post not found")
}
return posts[0] return posts[0]
} }

View File

@ -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
}

View File

@ -0,0 +1,3 @@
export default async () => {
throw new OperationError(501, "Not implemented")
}

View File

@ -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
}
}
})
}

View File

@ -1,6 +1,6 @@
import { User, Comment, PostLike, SavedPost } from "@db_models" import { User, Comment, PostLike, SavedPost } from "@db_models"
export default async (payload) => { export default async (payload = {}) => {
let { let {
posts, posts,
for_user_id, for_user_id,
@ -28,7 +28,9 @@ export default async (payload) => {
_id: { _id: {
$in: posts.map((post) => post.user_id) $in: posts.map((post) => post.user_id)
} }
}), })
.select("-email")
.select("-birthday"),
PostLike.find({ PostLike.find({
post_id: { post_id: {
$in: posts.map((post) => post._id) $in: posts.map((post) => post._id)

View File

@ -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
}
}
})
}

View File

@ -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
}
}
})
}

View File

@ -0,0 +1,3 @@
export default async () => {
throw new OperationError(501, "Not implemented")
}

View File

@ -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
}
}

View File

@ -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,
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
})
}
}

View File

@ -1,6 +1,13 @@
export default async (req, res) => { import Posts from "@classes/posts"
return res.json({
code: 0, export default {
message: "success", middlewares: ["withAuthentication"],
}) fn: async (req, res) => {
const result = await Posts.create({
...req.body,
user_id: req.auth.session.user_id,
})
return result
}
} }

View File

@ -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
})
}
}

View File

@ -1,10 +0,0 @@
export default async (req, res) => {
await new Promise((r) => {
setTimeout(r, 1000)
})
return {
code: 0,
message: "success",
}
}

View File

@ -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,
})
}
}

View File

@ -0,0 +1,3 @@
export default class Users {
static data = require("./method/data").default
}

View File

@ -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
}

View File

@ -1,5 +1,5 @@
{ {
"name": "feed", "name": "users",
"version": "1.0.0", "version": "1.0.0",
"main": "index.js", "main": "index.js",
"license": "MIT" "license": "MIT"

View File

@ -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,
})
}
}

View File

@ -1,13 +1,15 @@
import { Server } from "linebridge/src/server" import { Server } from "linebridge/src/server"
import DbManager from "@shared-classes/DbManager" import DbManager from "@shared-classes/DbManager"
import RedisClient from "@shared-classes/RedisClient"
import SharedMiddlewares from "@shared-middlewares" import SharedMiddlewares from "@shared-middlewares"
export default class API extends Server { export default class API extends Server {
static refName = "feed" static refName = "users"
static useEngine = "hyper-express" static useEngine = "hyper-express"
static routesPath = `${__dirname}/routes` static routesPath = `${__dirname}/routes`
static listen_port = process.env.HTTP_LISTEN_PORT ?? 3007 static listen_port = process.env.HTTP_LISTEN_PORT ?? 3008
middlewares = { middlewares = {
...SharedMiddlewares ...SharedMiddlewares
@ -15,10 +17,12 @@ export default class API extends Server {
contexts = { contexts = {
db: new DbManager(), db: new DbManager(),
redis: RedisClient()
} }
async onInitialize() { async onInitialize() {
await this.contexts.db.initialize() await this.contexts.db.initialize()
await this.contexts.redis.initialize()
} }
} }