@ -9,7 +9,9 @@ import fastify, {
} from 'fastify'
import { Server , IncomingMessage , ServerResponse } from 'http'
import { unauthorizedError , badRequestError , notFoundError } from '../../lib/errors'
import difference from 'lodash/difference'
import { unauthorizedError , serverError , badRequestError , notFoundError } from '../../lib/errors'
import { trimContent , createPostId } from '../../lib/util'
import { getUsers } from '../../lib/collections'
@ -27,6 +29,7 @@ import {
IUserSubscription ,
IUserTimelinePost ,
IUserBlock ,
IGroupBlock ,
IPostRelationship ,
IStatus ,
} from '../../types/collections'
@ -112,9 +115,10 @@ function doPostRoute(server: fastify.FastifyInstance<Server, IncomingMessage, Se
}
server . post < DefaultQuery , DefaultParams , DefaultHeaders , Body > ( '/api/post' , options , async ( request , reply ) = > {
if ( ! server . database ) return serverError ( reply )
if ( ! request . viewer ) return unauthorizedError ( reply )
let newPostRelationship : IPostRelationship = null
let newPostRelationship : IPostRelationship | undefined
const postContainer = await containerFor ( server . database . client , 'Posts' )
const postRelationshipContainer = await containerFor ( server . database . client , 'PostRelationships' )
const userContainer = await containerFor ( server . database . client , 'Users' )
@ -122,7 +126,7 @@ function doPostRoute(server: fastify.FastifyInstance<Server, IncomingMessage, Se
const { resource : viewer } = await userContainer . item ( request . viewer . id , request . viewer . id ) . read < IUser > ( )
if ( viewer . pending ) return badRequestError ( reply , 'User requires approval' )
if ( viewer . group === '' ) return badRequestError ( reply , 'User must belong to a group' )
if ( ! viewer . group ) return badRequestError ( reply , 'User must belong to a group' )
const postId = createPostId ( )
@ -175,7 +179,7 @@ function doPostRoute(server: fastify.FastifyInstance<Server, IncomingMessage, Se
for ( const subscriber of subscribers ) {
await userContainer . items . create < IUserTimelinePost > ( {
postId ,
partitionKey : subscriber.id ,
partitionKey : subscriber.id ! ,
type : 'timeline' ,
created : Date.now ( ) ,
} )
@ -217,6 +221,8 @@ function postsByUserRoute(server: fastify.FastifyInstance<Server, IncomingMessag
}
server . get < DefaultQuery , Params , DefaultHeaders , DefaultBody > ( '/api/user/:id/posts' , options , async ( request , reply ) = > {
if ( ! server . database ) return serverError ( reply )
const id = normalize ( request . params . id )
const userContainer = await containerFor ( server . database . client , 'Users' )
const postContainer = await containerFor ( server . database . client , 'Posts' )
@ -250,6 +256,8 @@ function postsByUserRoute(server: fastify.FastifyInstance<Server, IncomingMessag
if ( request . viewer ) {
const { resource : viewer } = await userContainer . item ( request . viewer . id , request . viewer . id ) . read < IUser > ( )
if ( ! viewer . group ) return unauthorizedError ( reply )
const query = createQuerySpec ( `
SELECT u . id FROM Users u WHERE
( u . blockedId = @viewer OR u . blockedId = @viewerGroup )
@ -257,7 +265,7 @@ function postsByUserRoute(server: fastify.FastifyInstance<Server, IncomingMessag
AND u . type = 'block'
` , {
viewer : viewer.id ,
viewerGroup : viewer.group ,
viewerGroup : viewer.group.id ,
user : user.id ,
} )
@ -268,13 +276,9 @@ function postsByUserRoute(server: fastify.FastifyInstance<Server, IncomingMessag
const userPostsQuery = createQuerySpec ( ` SELECT p.id FROM Users p WHERE p.partitionKey = @user AND p.type = 'post' ` , { user : id } )
const { resources : userPosts } = await userContainer . items . query < IUserPost > ( userPostsQuery , { } ) . fetchAll ( )
const { resources : posts } = await postContainer . items . query < IPost > ( {
query : 'SELECT * FROM Posts p WHERE ARRAY_CONTAINS(@posts, p.id)' ,
parameters : [ {
name : '@posts' ,
value : userPosts.map ( p = > p . id ) ,
} ]
} , { } ) . fetchAll ( )
const { resources : posts } = await postContainer . items . query < IPost > ( createQuerySpec ( 'SELECT * FROM Posts p WHERE ARRAY_CONTAINS(@posts, p.id)' , {
posts : userPosts.map ( p = > p . id ! ) ,
} ) , { } ) . fetchAll ( )
return {
user ,
@ -320,6 +324,8 @@ function postRoute(server: fastify.FastifyInstance<Server, IncomingMessage, Serv
}
server . get < DefaultQuery , Params , DefaultHeaders , DefaultBody > ( '/api/post/:id' , options , async ( request , reply ) = > {
if ( ! server . database ) return serverError ( reply )
const postContainer = await containerFor ( server . database . client , 'Posts' )
const { resource : post } = await postContainer . item ( request . params . id , request . params . id ) . read < IPost > ( )
@ -331,29 +337,55 @@ function postRoute(server: fastify.FastifyInstance<Server, IncomingMessage, Serv
const { resources : descendantRelationships } = await postRelationshipContainer . items . query < IPostRelationship > ( query , { } ) . fetchAll ( )
const { resources : descendants } = await postContainer . items . query < IPost > ( {
query : 'SELECT * FROM Posts p WHERE ARRAY_CONTAINS(@descendants, p.id)' ,
parameters : [ {
name : '@descendants' ,
value : descendantRelationships.map ( r = > r . id ) ,
} ]
} , { } ) . fetchAll ( )
const { resources : descendants } = await postContainer . items . query < IPost > ( createQuerySpec ( 'SELECT * FROM Posts p WHERE ARRAY_CONTAINS(@descendants, p.id)' , {
descendants : descendantRelationships.map ( r = > r . id ) ,
} ) , { } ) . fetchAll ( )
const { resources : ancestors } = await postContainer . items . query < IPost > ( {
query : 'SELECT * FROM Posts p WHERE ARRAY_CONTAINS(@parents, p.id)' ,
parameters : [ {
name : '@parents' ,
value : post.parents ,
} ]
} , { } ) . fetchAll ( )
const { resources : ancestors } = await postContainer . items . query < IPost > ( createQuerySpec ( 'SELECT * FROM Posts p WHERE ARRAY_CONTAINS(@parents, p.id)' , {
parents : post.parents ,
} ) , { } ) . fetchAll ( )
const getUserId = ( post : IPost ) = > post . userId
const users = await getUsers ( server . database . client , [
const userIds = [
. . . descendants . map ( getUserId ) ,
. . . ancestors . map ( getUserId ) ,
getUserId ( post ) ,
] )
]
const users = await getUsers ( server . database . client , userIds )
if ( request . viewer ) {
const userContainer = await containerFor ( server . database . client , 'Users' )
const groupContainer = await containerFor ( server . database . client , 'Groups' )
const { resource : viewer } = await userContainer . item ( request . viewer . id , request . viewer . id ) . read < IUser > ( )
if ( ! viewer . group ) return unauthorizedError ( reply )
const blockQuery = createQuerySpec ( `
SELECT g . userId FROM Groups g WHERE
g . partitionKey = @viewerGroup AND
g . type = 'block' AND
( g . blockedId = @viewer OR g . blockedId = @viewerGroup )
ARRAY_CONTAINS ( @ids , g . userId )
` , {
viewer : viewer.id ,
viewerGroup : viewer.group.id ,
ids : userIds ,
} )
const { resources : blocks } = await groupContainer . items . query < IGroupBlock > ( blockQuery , { } ) . fetchAll ( )
const blockedUserIds = blocks . map ( b = > b . userId )
if ( blockedUserIds . includes ( post . userId ) ) return unauthorizedError ( reply )
return {
post ,
descendants : descendants.filter ( p = > ! blockedUserIds . includes ( p . userId ) ) ,
ancestors : ancestors.filter ( p = > ! blockedUserIds . includes ( p . userId ) ) ,
users : users.filter ( u = > ! blockedUserIds . includes ( u . id ) ) ,
}
}
return {
post ,