|
|
@ -37,7 +37,6 @@ import { |
|
|
|
GroupItemType, |
|
|
|
Installation, |
|
|
|
App, |
|
|
|
Group, |
|
|
|
} from '../../types/collections' |
|
|
|
|
|
|
|
import { PluginOptions } from '../../types' |
|
|
@ -45,22 +44,25 @@ import { PluginOptions } from '../../types' |
|
|
|
interface PostBody { |
|
|
|
text?: string |
|
|
|
cover?: string |
|
|
|
installation: string |
|
|
|
visible: boolean |
|
|
|
status?: Status |
|
|
|
attachments: PostAttachment[] |
|
|
|
data: object |
|
|
|
parent: string |
|
|
|
} |
|
|
|
|
|
|
|
const postBodySchema = { |
|
|
|
type: 'object', |
|
|
|
required: ['visible'], |
|
|
|
required: ['installation', 'visible'], |
|
|
|
properties: { |
|
|
|
installation: { type: 'string' }, |
|
|
|
visible: { type: 'boolean' }, |
|
|
|
text: { type: 'string' }, |
|
|
|
cover: { |
|
|
|
type: 'string', |
|
|
|
maxLength: SHORT_TEXT_LENGTH, |
|
|
|
}, |
|
|
|
visible: { type: 'boolean' }, |
|
|
|
status: { |
|
|
|
type: 'object', |
|
|
|
required: ['date'], |
|
|
@ -77,10 +79,10 @@ const postBodySchema = { |
|
|
|
type: 'array', |
|
|
|
items: { |
|
|
|
type: 'object', |
|
|
|
required: ['imageUrl'], |
|
|
|
required: ['url'], |
|
|
|
properties: { |
|
|
|
imageUrl: { type: 'string' }, |
|
|
|
caption: { |
|
|
|
url: { type: 'string' }, |
|
|
|
text: { |
|
|
|
type: 'string', |
|
|
|
maxLength: SHORT_TEXT_LENGTH, |
|
|
|
}, |
|
|
@ -91,11 +93,14 @@ const postBodySchema = { |
|
|
|
}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
data: { |
|
|
|
type: 'object', |
|
|
|
}, |
|
|
|
parent: { type: 'string' }, |
|
|
|
}, |
|
|
|
} |
|
|
|
|
|
|
|
async function createPost(client: CosmosClient, userId: string, body: PostBody, reply: FastifyReply<ServerResponse>, logger: Logger) { |
|
|
|
async function createPost(client: CosmosClient, userId: string, appId: string, body: PostBody, reply: FastifyReply<ServerResponse>, logger: Logger) { |
|
|
|
let newPostRelationship: PostRelationship | undefined |
|
|
|
|
|
|
|
const postContainer = containerFor(client, 'Posts') |
|
|
@ -111,7 +116,8 @@ async function createPost(client: CosmosClient, userId: string, body: PostBody, |
|
|
|
const postId = createPostId() |
|
|
|
|
|
|
|
if (body.parent) { |
|
|
|
const parent = await getItem<Post>({ container: postContainer, id: body.parent }) |
|
|
|
const parentItem = postContainer.item(body.parent, body.parent) |
|
|
|
const { resource: parent } = await parentItem.read<Post>() |
|
|
|
if (!parent) return badRequestFormError(reply, 'parent', 'Invalid parent') |
|
|
|
|
|
|
|
const parentRelationship = await getItem<PostRelationship>({ |
|
|
@ -130,6 +136,13 @@ async function createPost(client: CosmosClient, userId: string, body: PostBody, |
|
|
|
parent.id, |
|
|
|
] |
|
|
|
} |
|
|
|
|
|
|
|
if (body.visible) { |
|
|
|
await parentItem.replace<Post>({ |
|
|
|
...parent, |
|
|
|
replies: parent.replies + 1, |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const post: Post = { |
|
|
@ -137,13 +150,16 @@ async function createPost(client: CosmosClient, userId: string, body: PostBody, |
|
|
|
pk: postId, |
|
|
|
t: PostItemType.Post, |
|
|
|
userId, |
|
|
|
appId, |
|
|
|
root: newPostRelationship ? newPostRelationship.pk : postId, |
|
|
|
parents: newPostRelationship ? newPostRelationship.parents : [], |
|
|
|
text: trimContent(body.text, 1000), |
|
|
|
cover: trimContent(body.cover), |
|
|
|
visible: body.visible, |
|
|
|
attachments: [], |
|
|
|
attachments: body.attachments, |
|
|
|
data: body.data, |
|
|
|
awards: 0, |
|
|
|
replies: 0, |
|
|
|
latestAwards: [], |
|
|
|
created: Date.now(), |
|
|
|
} |
|
|
@ -169,8 +185,6 @@ async function createPost(client: CosmosClient, userId: string, body: PostBody, |
|
|
|
logger, |
|
|
|
}) |
|
|
|
|
|
|
|
logger.debug('subscribers', subscribers) |
|
|
|
|
|
|
|
for (const uid of [userId, ...subscribers.map(s => s.userId)]) { |
|
|
|
await userContainer.items.create<UserTimelinePost>({ |
|
|
|
id: postId, |
|
|
@ -205,13 +219,32 @@ function createPostRoute(server: FastifyInstance<Server, IncomingMessage, Server |
|
|
|
if (!server.database) return serverError(reply) |
|
|
|
if (!request.viewer) return unauthorizedError(reply) |
|
|
|
|
|
|
|
return await createPost(server.database.client, request.viewer.id, request.body, reply, request.log) |
|
|
|
const installationId = request.body.installation |
|
|
|
|
|
|
|
const container = containerFor(server.database.client, 'Apps') |
|
|
|
const installation = await getItem<Installation>({ |
|
|
|
container, |
|
|
|
id: installationId, |
|
|
|
partitionKey: INSTALLATION_PARTITION_KEY, |
|
|
|
}) |
|
|
|
|
|
|
|
if (!installation) return badRequestError(reply, 'Installation not found') |
|
|
|
if (installation.userId !== request.viewer.id) return badRequestError(reply) |
|
|
|
|
|
|
|
const app = await getItem<App>({ |
|
|
|
container, |
|
|
|
id: installation.appId, |
|
|
|
partitionKey: APP_PARTITION_KEY, |
|
|
|
}) |
|
|
|
|
|
|
|
if (!app) return serverError(reply) |
|
|
|
|
|
|
|
return await createPost(server.database.client, request.viewer.id, app.id, request.body, reply, request.log) |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
function createAppPostRoute(server: FastifyInstance<Server, IncomingMessage, ServerResponse>) { |
|
|
|
interface Headers { |
|
|
|
installation: string |
|
|
|
timestamp: string |
|
|
|
signature: string |
|
|
|
} |
|
|
@ -221,7 +254,6 @@ function createAppPostRoute(server: FastifyInstance<Server, IncomingMessage, Ser |
|
|
|
headers: { |
|
|
|
type: 'object', |
|
|
|
properties: { |
|
|
|
installation: { type: 'string' }, |
|
|
|
timestamp: { type: 'string' }, |
|
|
|
signature: { type: 'string' }, |
|
|
|
}, |
|
|
@ -242,12 +274,13 @@ function createAppPostRoute(server: FastifyInstance<Server, IncomingMessage, Ser |
|
|
|
server.post<DefaultQuery, DefaultParams, Headers, PostBody>('/api/app/post', options, async (request, reply) => { |
|
|
|
if (!server.database) return serverError(reply) |
|
|
|
|
|
|
|
const { installation: installationId, timestamp, signature } = request.headers |
|
|
|
const { timestamp, signature } = request.headers |
|
|
|
|
|
|
|
if (!installationId) return badRequestError(reply, '"installation" header required') |
|
|
|
if (!timestamp) return badRequestError(reply, '"timestamp" header required') |
|
|
|
if (!signature) return badRequestError(reply, '"signature" header required') |
|
|
|
|
|
|
|
const installationId = request.body.installation |
|
|
|
|
|
|
|
const container = containerFor(server.database.client, 'Apps') |
|
|
|
const installation = await getItem<Installation>({ |
|
|
|
container, |
|
|
@ -269,7 +302,7 @@ function createAppPostRoute(server: FastifyInstance<Server, IncomingMessage, Ser |
|
|
|
return badRequestError(reply, 'Invalid signature') |
|
|
|
} |
|
|
|
|
|
|
|
return await createPost(server.database.client, installation.userId, request.body, reply, request.log) |
|
|
|
return await createPost(server.database.client, installation.userId, app.id, request.body, reply, request.log) |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
@ -389,6 +422,7 @@ function timelineRoute(server: FastifyInstance<Server, IncomingMessage, ServerRe |
|
|
|
type: 'array', |
|
|
|
items: postSchema, |
|
|
|
}, |
|
|
|
continuation: { type: 'string' }, |
|
|
|
}, |
|
|
|
}, |
|
|
|
400: errorSchema, |
|
|
@ -455,11 +489,11 @@ function postRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespon |
|
|
|
type: 'object', |
|
|
|
properties: { |
|
|
|
post: postSchema, |
|
|
|
descendants: { |
|
|
|
parents: { |
|
|
|
type: 'array', |
|
|
|
items: postSchema, |
|
|
|
}, |
|
|
|
ancestors: { |
|
|
|
children: { |
|
|
|
type: 'array', |
|
|
|
items: postSchema, |
|
|
|
}, |
|
|
@ -527,7 +561,7 @@ function postRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespon |
|
|
|
SELECT g.userId FROM Groups g WHERE |
|
|
|
g.pk = @viewerGroup AND |
|
|
|
g.t = @type AND |
|
|
|
(g.blockedId = @viewer OR g.blockedId = @viewerGroup) |
|
|
|
(g.blockedId = @viewer OR g.blockedId = @viewerGroup) AND |
|
|
|
ARRAY_CONTAINS(@ids, g.userId) |
|
|
|
`, {
|
|
|
|
viewer: viewer.id, |
|
|
@ -539,7 +573,7 @@ function postRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespon |
|
|
|
const blocks = await queryItems<GroupBlock>({ |
|
|
|
container: containerFor(server.database.client, 'Groups'), |
|
|
|
query: blockQuery, |
|
|
|
logger: request.log |
|
|
|
logger: request.log, |
|
|
|
}) |
|
|
|
const blockedUserIds = blocks.map(b => b.userId) |
|
|
|
|
|
|
@ -547,16 +581,16 @@ function postRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespon |
|
|
|
|
|
|
|
return { |
|
|
|
post, |
|
|
|
descendants: descendants.filter(p => !blockedUserIds.includes(p.userId)), |
|
|
|
ancestors: ancestors.filter(p => !blockedUserIds.includes(p.userId)), |
|
|
|
children: descendants.filter(p => !blockedUserIds.includes(p.userId)), |
|
|
|
parents: ancestors.filter(p => !blockedUserIds.includes(p.userId)), |
|
|
|
users: users.filter(u => !blockedUserIds.includes(u.id)), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return { |
|
|
|
post, |
|
|
|
descendants, |
|
|
|
ancestors, |
|
|
|
children: descendants, |
|
|
|
parents: ancestors, |
|
|
|
users, |
|
|
|
} |
|
|
|
}) |
|
|
|