Implement post Activity Streams
This commit is contained in:
		
							parent
							
								
									fabf233478
								
							
						
					
					
						commit
						12a251c7d6
					
				
					 5 changed files with 95 additions and 4 deletions
				
			
		
							
								
								
									
										5
									
								
								src/common/remote/activitypub/context.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/common/remote/activitypub/context.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					export default [
 | 
				
			||||||
 | 
						'https://www.w3.org/ns/activitystreams',
 | 
				
			||||||
 | 
						'https://w3id.org/security/v1',
 | 
				
			||||||
 | 
						{ Hashtag: 'as:Hashtag' }
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
| 
						 | 
					@ -47,6 +47,7 @@ export type IPost = {
 | 
				
			||||||
		heading: number;
 | 
							heading: number;
 | 
				
			||||||
		speed: number;
 | 
							speed: number;
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
						tags: string[];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,13 @@
 | 
				
			||||||
import * as express from 'express';
 | 
					import * as express from 'express';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import post from './post';
 | 
				
			||||||
import user from './user';
 | 
					import user from './user';
 | 
				
			||||||
import inbox from './inbox';
 | 
					import inbox from './inbox';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const app = express();
 | 
					const app = express();
 | 
				
			||||||
app.disable('x-powered-by');
 | 
					app.disable('x-powered-by');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app.use(post);
 | 
				
			||||||
app.use(user);
 | 
					app.use(user);
 | 
				
			||||||
app.use(inbox);
 | 
					app.use(inbox);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										85
									
								
								src/server/activitypub/post.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/server/activitypub/post.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,85 @@
 | 
				
			||||||
 | 
					import * as express from 'express';
 | 
				
			||||||
 | 
					import context from '../../common/remote/activitypub/context';
 | 
				
			||||||
 | 
					import parseAcct from '../../common/user/parse-acct';
 | 
				
			||||||
 | 
					import config from '../../conf';
 | 
				
			||||||
 | 
					import DriveFile from '../../models/drive-file';
 | 
				
			||||||
 | 
					import Post from '../../models/post';
 | 
				
			||||||
 | 
					import User from '../../models/user';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const app = express();
 | 
				
			||||||
 | 
					app.disable('x-powered-by');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app.get('/@:user/:post', async (req, res, next) => {
 | 
				
			||||||
 | 
						const accepted = req.accepts(['html', 'application/activity+json', 'application/ld+json']);
 | 
				
			||||||
 | 
						if (!(['application/activity+json', 'application/ld+json'] as Array<any>).includes(accepted)) {
 | 
				
			||||||
 | 
							return next();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const { username, host } = parseAcct(req.params.user);
 | 
				
			||||||
 | 
						if (host !== null) {
 | 
				
			||||||
 | 
							return res.sendStatus(422);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const user = await User.findOne({
 | 
				
			||||||
 | 
							usernameLower: username.toLowerCase(),
 | 
				
			||||||
 | 
							host: null
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						if (user === null) {
 | 
				
			||||||
 | 
							return res.sendStatus(404);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const post = await Post.findOne({
 | 
				
			||||||
 | 
							_id: req.params.post,
 | 
				
			||||||
 | 
							userId: user._id
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						if (post === null) {
 | 
				
			||||||
 | 
							return res.sendStatus(404);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const asyncFiles = DriveFile.find({ _id: { $in: post.mediaIds } });
 | 
				
			||||||
 | 
						let inReplyTo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (post.replyId) {
 | 
				
			||||||
 | 
							const inReplyToPost = await Post.findOne({
 | 
				
			||||||
 | 
								_id: post.replyId,
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (inReplyToPost !== null) {
 | 
				
			||||||
 | 
								const inReplyToUser = await User.findOne({
 | 
				
			||||||
 | 
									_id: post.userId,
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (inReplyToUser !== null) {
 | 
				
			||||||
 | 
									inReplyTo = `${config.url}@${inReplyToUser.username}/${inReplyToPost._id}`;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							inReplyTo = null;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const attributedTo = `${config.url}/@${user.username}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res.json({
 | 
				
			||||||
 | 
							'@context': context,
 | 
				
			||||||
 | 
							id: `${attributedTo}/${post._id}`,
 | 
				
			||||||
 | 
							type: 'Note',
 | 
				
			||||||
 | 
							attributedTo,
 | 
				
			||||||
 | 
							content: post.textHtml,
 | 
				
			||||||
 | 
							published: post.createdAt.toISOString(),
 | 
				
			||||||
 | 
							to: 'https://www.w3.org/ns/activitystreams#Public',
 | 
				
			||||||
 | 
							cc: `${attributedTo}/followers`,
 | 
				
			||||||
 | 
							inReplyTo,
 | 
				
			||||||
 | 
							attachment: (await asyncFiles).map(({ _id, contentType }) => ({
 | 
				
			||||||
 | 
								type: 'Document',
 | 
				
			||||||
 | 
								mediaType: contentType,
 | 
				
			||||||
 | 
								url: `${config.drive_url}/${_id}`
 | 
				
			||||||
 | 
							})),
 | 
				
			||||||
 | 
							tag: post.tags.map(tag => ({
 | 
				
			||||||
 | 
								type: 'Hashtag',
 | 
				
			||||||
 | 
								href: `${config.url}/search?q=#${encodeURIComponent(tag)}`,
 | 
				
			||||||
 | 
								name: '#' + tag
 | 
				
			||||||
 | 
							}))
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default app;
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
import * as express from 'express';
 | 
					import * as express from 'express';
 | 
				
			||||||
import config from '../../conf';
 | 
					import config from '../../conf';
 | 
				
			||||||
import { extractPublic } from '../../crypto_key';
 | 
					import { extractPublic } from '../../crypto_key';
 | 
				
			||||||
 | 
					import context from '../../common/remote/activitypub/context';
 | 
				
			||||||
import parseAcct from '../../common/user/parse-acct';
 | 
					import parseAcct from '../../common/user/parse-acct';
 | 
				
			||||||
import User, { ILocalAccount } from '../../models/user';
 | 
					import User, { ILocalAccount } from '../../models/user';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,10 +34,7 @@ app.get('/@:user', async (req, res, next) => {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	res.json({
 | 
						res.json({
 | 
				
			||||||
		'@context': [
 | 
							'@context': context,
 | 
				
			||||||
			'https://www.w3.org/ns/activitystreams',
 | 
					 | 
				
			||||||
			'https://w3id.org/security/v1'
 | 
					 | 
				
			||||||
		],
 | 
					 | 
				
			||||||
		type: 'Person',
 | 
							type: 'Person',
 | 
				
			||||||
		id,
 | 
							id,
 | 
				
			||||||
		inbox: `${id}/inbox`,
 | 
							inbox: `${id}/inbox`,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue