forked from FoundKeyGang/FoundKey
Publish pinned notes (#2731)
This commit is contained in:
parent
bec48319ec
commit
11496d887e
9 changed files with 135 additions and 2 deletions
9
src/remote/activitypub/renderer/add.ts
Normal file
9
src/remote/activitypub/renderer/add.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import config from '../../../config';
|
||||||
|
import { ILocalUser } from '../../../models/user';
|
||||||
|
|
||||||
|
export default (user: ILocalUser, target: any, object: any) => ({
|
||||||
|
type: 'Add',
|
||||||
|
actor: `${config.url}/users/${user._id}`,
|
||||||
|
target,
|
||||||
|
object
|
||||||
|
});
|
|
@ -4,8 +4,9 @@
|
||||||
* @param totalItems Total number of items
|
* @param totalItems Total number of items
|
||||||
* @param first URL of first page (optional)
|
* @param first URL of first page (optional)
|
||||||
* @param last URL of last page (optional)
|
* @param last URL of last page (optional)
|
||||||
|
* @param orderedItems attached objects (optional)
|
||||||
*/
|
*/
|
||||||
export default function(id: string, totalItems: any, first: string, last: string) {
|
export default function(id: string, totalItems: any, first?: string, last?: string, orderedItems?: object) {
|
||||||
const page: any = {
|
const page: any = {
|
||||||
id,
|
id,
|
||||||
type: 'OrderedCollection',
|
type: 'OrderedCollection',
|
||||||
|
@ -14,6 +15,7 @@ export default function(id: string, totalItems: any, first: string, last: string
|
||||||
|
|
||||||
if (first) page.first = first;
|
if (first) page.first = first;
|
||||||
if (last) page.last = last;
|
if (last) page.last = last;
|
||||||
|
if (orderedItems) page.orderedItems = orderedItems;
|
||||||
|
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ export default async (user: ILocalUser) => {
|
||||||
outbox: `${id}/outbox`,
|
outbox: `${id}/outbox`,
|
||||||
followers: `${id}/followers`,
|
followers: `${id}/followers`,
|
||||||
following: `${id}/following`,
|
following: `${id}/following`,
|
||||||
|
featured: `${id}/collections/featured`,
|
||||||
sharedInbox: `${config.url}/inbox`,
|
sharedInbox: `${config.url}/inbox`,
|
||||||
url: `${config.url}/@${user.username}`,
|
url: `${config.url}/@${user.username}`,
|
||||||
preferredUsername: user.username,
|
preferredUsername: user.username,
|
||||||
|
|
9
src/remote/activitypub/renderer/remove.ts
Normal file
9
src/remote/activitypub/renderer/remove.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import config from '../../../config';
|
||||||
|
import { ILocalUser } from '../../../models/user';
|
||||||
|
|
||||||
|
export default (user: ILocalUser, target: any, object: any) => ({
|
||||||
|
type: 'Remove',
|
||||||
|
actor: `${config.url}/users/${user._id}`,
|
||||||
|
target,
|
||||||
|
object
|
||||||
|
});
|
|
@ -53,6 +53,7 @@ export interface IPerson extends IObject {
|
||||||
publicKey: any;
|
publicKey: any;
|
||||||
followers: any;
|
followers: any;
|
||||||
following: any;
|
following: any;
|
||||||
|
featured?: any;
|
||||||
outbox: any;
|
outbox: any;
|
||||||
endpoints: string[];
|
endpoints: string[];
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import renderPerson from '../remote/activitypub/renderer/person';
|
||||||
import Outbox, { packActivity } from './activitypub/outbox';
|
import Outbox, { packActivity } from './activitypub/outbox';
|
||||||
import Followers from './activitypub/followers';
|
import Followers from './activitypub/followers';
|
||||||
import Following from './activitypub/following';
|
import Following from './activitypub/following';
|
||||||
|
import Featured from './activitypub/featured';
|
||||||
|
|
||||||
// Init router
|
// Init router
|
||||||
const router = new Router();
|
const router = new Router();
|
||||||
|
@ -102,6 +103,9 @@ router.get('/users/:user/followers', Followers);
|
||||||
// following
|
// following
|
||||||
router.get('/users/:user/following', Following);
|
router.get('/users/:user/following', Following);
|
||||||
|
|
||||||
|
// featured
|
||||||
|
router.get('/users/:user/collections/featured', Featured);
|
||||||
|
|
||||||
// publickey
|
// publickey
|
||||||
router.get('/users/:user/publickey', async ctx => {
|
router.get('/users/:user/publickey', async ctx => {
|
||||||
const userId = new mongo.ObjectID(ctx.params.user);
|
const userId = new mongo.ObjectID(ctx.params.user);
|
||||||
|
|
38
src/server/activitypub/featured.ts
Normal file
38
src/server/activitypub/featured.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import * as mongo from 'mongodb';
|
||||||
|
import * as Router from 'koa-router';
|
||||||
|
import config from '../../config';
|
||||||
|
import User from '../../models/user';
|
||||||
|
import pack from '../../remote/activitypub/renderer';
|
||||||
|
import renderOrderedCollection from '../../remote/activitypub/renderer/ordered-collection';
|
||||||
|
import { setResponseType } from '../activitypub';
|
||||||
|
import Note from '../../models/note';
|
||||||
|
import renderNote from '../../remote/activitypub/renderer/note';
|
||||||
|
|
||||||
|
export default async (ctx: Router.IRouterContext) => {
|
||||||
|
const userId = new mongo.ObjectID(ctx.params.user);
|
||||||
|
|
||||||
|
// Verify user
|
||||||
|
const user = await User.findOne({
|
||||||
|
_id: userId,
|
||||||
|
host: null
|
||||||
|
});
|
||||||
|
|
||||||
|
if (user === null) {
|
||||||
|
ctx.status = 404;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pinnedNoteIds = user.pinnedNoteIds || [];
|
||||||
|
|
||||||
|
const pinnedNotes = await Promise.all(pinnedNoteIds.map(id => Note.findOne({ _id: id })));
|
||||||
|
|
||||||
|
const renderedNotes = await Promise.all(pinnedNotes.map(note => renderNote(note)));
|
||||||
|
|
||||||
|
const rendered = renderOrderedCollection(
|
||||||
|
`${config.url}/users/${userId}/collections/featured`,
|
||||||
|
renderedNotes.length, null, null, renderedNotes
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.body = pack(rendered);
|
||||||
|
setResponseType(ctx);
|
||||||
|
};
|
|
@ -2,6 +2,7 @@ import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
|
||||||
import User, { ILocalUser } from '../../../../models/user';
|
import User, { ILocalUser } from '../../../../models/user';
|
||||||
import Note from '../../../../models/note';
|
import Note from '../../../../models/note';
|
||||||
import { pack } from '../../../../models/user';
|
import { pack } from '../../../../models/user';
|
||||||
|
import { deliverPinnedChange } from '../../../../services/i/pin';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pin note
|
* Pin note
|
||||||
|
@ -21,6 +22,9 @@ export default async (params: any, user: ILocalUser) => new Promise(async (res,
|
||||||
return rej('note not found');
|
return rej('note not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let addedId;
|
||||||
|
let removedId;
|
||||||
|
|
||||||
const pinnedNoteIds = user.pinnedNoteIds || [];
|
const pinnedNoteIds = user.pinnedNoteIds || [];
|
||||||
|
|
||||||
if (pinnedNoteIds.some(id => id.equals(note._id))) {
|
if (pinnedNoteIds.some(id => id.equals(note._id))) {
|
||||||
|
@ -28,9 +32,10 @@ export default async (params: any, user: ILocalUser) => new Promise(async (res,
|
||||||
}
|
}
|
||||||
|
|
||||||
pinnedNoteIds.unshift(note._id);
|
pinnedNoteIds.unshift(note._id);
|
||||||
|
addedId = note._id;
|
||||||
|
|
||||||
if (pinnedNoteIds.length > 5) {
|
if (pinnedNoteIds.length > 5) {
|
||||||
pinnedNoteIds.pop();
|
removedId = pinnedNoteIds.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
await User.update(user._id, {
|
await User.update(user._id, {
|
||||||
|
@ -44,6 +49,9 @@ export default async (params: any, user: ILocalUser) => new Promise(async (res,
|
||||||
detail: true
|
detail: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Send Add/Remove to followers
|
||||||
|
deliverPinnedChange(user._id, removedId, addedId);
|
||||||
|
|
||||||
// Send response
|
// Send response
|
||||||
res(iObj);
|
res(iObj);
|
||||||
});
|
});
|
||||||
|
|
61
src/services/i/pin.ts
Normal file
61
src/services/i/pin.ts
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import config from '../../config';
|
||||||
|
import * as mongo from 'mongodb';
|
||||||
|
import User, { isLocalUser, isRemoteUser, ILocalUser } from '../../models/user';
|
||||||
|
import Following from '../../models/following';
|
||||||
|
import renderAdd from '../../remote/activitypub/renderer/add';
|
||||||
|
import renderRemove from '../../remote/activitypub/renderer/remove';
|
||||||
|
import packAp from '../../remote/activitypub/renderer';
|
||||||
|
import { deliver } from '../../queue';
|
||||||
|
|
||||||
|
export async function deliverPinnedChange(userId: mongo.ObjectID, oldId: mongo.ObjectID, newId: mongo.ObjectID) {
|
||||||
|
const user = await User.findOne({
|
||||||
|
_id: userId
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!isLocalUser(user)) return;
|
||||||
|
|
||||||
|
const queue = await CreateRemoteInboxes(user);
|
||||||
|
|
||||||
|
if (queue.length < 1) return;
|
||||||
|
|
||||||
|
const target = `${config.url}/users/${user._id}/collections/featured`;
|
||||||
|
|
||||||
|
if (oldId) {
|
||||||
|
const oldItem = `${config.url}/notes/${oldId}`;
|
||||||
|
const content = packAp(renderRemove(user, target, oldItem));
|
||||||
|
queue.forEach(inbox => {
|
||||||
|
deliver(user, content, inbox);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newId) {
|
||||||
|
const newItem = `${config.url}/notes/${newId}`;
|
||||||
|
const content = packAp(renderAdd(user, target, newItem));
|
||||||
|
queue.forEach(inbox => {
|
||||||
|
deliver(user, content, inbox);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ローカルユーザーのリモートフォロワーのinboxリストを作成する
|
||||||
|
* @param user ローカルユーザー
|
||||||
|
*/
|
||||||
|
async function CreateRemoteInboxes(user: ILocalUser): Promise<string[]> {
|
||||||
|
const followers = await Following.find({
|
||||||
|
followeeId: user._id
|
||||||
|
});
|
||||||
|
|
||||||
|
const queue: string[] = [];
|
||||||
|
|
||||||
|
followers.map(following => {
|
||||||
|
const follower = following._follower;
|
||||||
|
|
||||||
|
if (isRemoteUser(follower)) {
|
||||||
|
const inbox = follower.sharedInbox || follower.inbox;
|
||||||
|
if (!queue.includes(inbox)) queue.push(inbox);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return queue;
|
||||||
|
}
|
Loading…
Reference in a new issue