Dwayne Harris
5 years ago
7 changed files with 241 additions and 63 deletions
-
7src/lib/authentication.ts
-
2src/lib/database.ts
-
12src/lib/util.ts
-
21src/plugins/api/authentication.ts
-
1src/plugins/api/index.ts
-
146src/plugins/api/posts.ts
-
115src/types/collections.ts
@ -0,0 +1,12 @@ |
|||
import { v1 } from 'uuid' |
|||
|
|||
export function trimContent(content: string, length: number = 128): string { |
|||
if (!content) return '' |
|||
if (content.length < length) return content.trim() |
|||
|
|||
return content.slice(0, length).trim() |
|||
} |
|||
|
|||
export function createPostId(): string { |
|||
return 'p' + v1().replace('/-/g', '') |
|||
} |
@ -0,0 +1,146 @@ |
|||
import fastify, { |
|||
Plugin, |
|||
DefaultQuery, |
|||
DefaultParams, |
|||
DefaultHeaders, |
|||
RouteShorthandOptions, |
|||
} from 'fastify' |
|||
|
|||
import { Server, IncomingMessage, ServerResponse } from 'http' |
|||
import { unauthorizedError, badRequestError } from '../../lib/errors' |
|||
import { containerFor, createQuerySpec } from '../../lib/database' |
|||
import { trimContent, createPostId } from '../../lib/util' |
|||
import { IPost, IPostAttachment, IUserPost, IUserFollow, IStatus, IUserTimelinePost, IPostRelationship } from '../../types/collections' |
|||
|
|||
interface PluginOptions { |
|||
|
|||
} |
|||
|
|||
function postRoute(server: fastify.FastifyInstance<Server, IncomingMessage, ServerResponse>) { |
|||
interface Body { |
|||
text?: string |
|||
cover?: string |
|||
visible: boolean |
|||
status?: IStatus |
|||
attachments: IPostAttachment[] |
|||
parent: string |
|||
} |
|||
|
|||
const options: RouteShorthandOptions = { |
|||
schema: { |
|||
body: { |
|||
type: 'object', |
|||
required: ['visible'], |
|||
properties: { |
|||
text: { type: 'string' }, |
|||
cover: { type: 'string' }, |
|||
visible: { type: 'boolean' }, |
|||
status: { |
|||
type: 'object', |
|||
required: ['date'], |
|||
properties: { |
|||
imageUrl: 'string', |
|||
text: 'string', |
|||
date: 'number', |
|||
}, |
|||
}, |
|||
attachments: { |
|||
type: 'array', |
|||
items: { |
|||
type: 'object', |
|||
required: ['imageUrl'], |
|||
properties: { |
|||
imageUrl: 'string', |
|||
caption: 'string', |
|||
cover: 'string', |
|||
}, |
|||
}, |
|||
}, |
|||
parent: { type: 'string' }, |
|||
}, |
|||
}, |
|||
response: { |
|||
200: { |
|||
type: 'object', |
|||
properties: { |
|||
id: { type: 'string' }, |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
server.post<DefaultQuery, DefaultParams, DefaultHeaders, Body>('/api/post', options, async (request, reply) => { |
|||
if (!request.user) return unauthorizedError(reply) |
|||
|
|||
let newPostRelationship: IPostRelationship = null |
|||
|
|||
const postContainer = await containerFor(server.database.client, 'Posts') |
|||
const postRelationshipContainer = await containerFor(server.database.client, 'PostRelationships') |
|||
const userContainer = await containerFor(server.database.client, 'User') |
|||
|
|||
const postId = createPostId() |
|||
|
|||
if (request.body.parent) { |
|||
const parentItem = postContainer.item(request.body.parent, request.body.parent) |
|||
const { resource: parent } = await parentItem.read<IPost>() |
|||
|
|||
if (!parent) return badRequestError(reply, 'Invalid parent', 'parent') |
|||
|
|||
const parentRelationshipItem = postRelationshipContainer.item(request.body.parent, parent.root) |
|||
const { resource: parentRelationship } = await parentRelationshipItem.read<IPostRelationship>() |
|||
|
|||
newPostRelationship = { |
|||
id: postId, |
|||
partitionKey: parent.root, |
|||
parents: [ |
|||
...parentRelationship.parents, |
|||
parent.id, |
|||
] |
|||
} |
|||
} |
|||
|
|||
const post: IPost = { |
|||
id: postId, |
|||
userId: request.user.id, |
|||
root: newPostRelationship ? newPostRelationship.partitionKey : postId, |
|||
text: trimContent(request.body.text, 1000), |
|||
cover: trimContent(request.body.cover), |
|||
visible: request.body.visible, |
|||
attachments: [], |
|||
awards: [], |
|||
created: Date.now(), |
|||
} |
|||
|
|||
const userPost: IUserPost = { |
|||
id: postId, |
|||
partitionKey: request.user.id, |
|||
type: 'post', |
|||
created: Date.now(), |
|||
} |
|||
|
|||
await postContainer.items.create<IPost>(post) |
|||
await userContainer.items.create<IUserPost>(userPost) |
|||
await postRelationshipContainer.items.create<IPostRelationship>(newPostRelationship) |
|||
|
|||
const query = createQuerySpec(`SELECT u.id FROM Users u WHERE u.partitionKey = @partitionKey AND type = 'follow'`, { partitionKey: request.user.id }) |
|||
const { resources: followers } = await userContainer.items.query<IUserFollow>(query, {}).fetchAll() |
|||
|
|||
if (followers.length < 500) { |
|||
for (let follower in followers) { |
|||
await userContainer.items.create<IUserTimelinePost>({ |
|||
id: postId, |
|||
partitionKey: followers[follower].id, |
|||
type: 'timeline', |
|||
created: Date.now(), |
|||
}) |
|||
} |
|||
} |
|||
}) |
|||
} |
|||
|
|||
const plugin: Plugin<Server, IncomingMessage, ServerResponse, PluginOptions> = async server => { |
|||
postRoute(server) |
|||
} |
|||
|
|||
export default plugin |
Write
Preview
Loading…
Cancel
Save
Reference in new issue