Skip to content

Commit

Permalink
Merge tag '10.102.686-m544' into custom/atsuchan
Browse files Browse the repository at this point in the history
10.102.686-m544
  • Loading branch information
atsu1125 committed Feb 4, 2024
2 parents e80a0e7 + fb2e7d2 commit f4cd398
Show file tree
Hide file tree
Showing 15 changed files with 143 additions and 14 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "misskey",
"author": "mei23 <[email protected]>",
"version": "10.102.685-m544",
"version": "10.102.686-m544",
"codename": "m544",
"repository": {
"type": "git",
Expand Down
4 changes: 4 additions & 0 deletions src/client/app/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,10 @@ export default (callback: (launch: (router: VueRouter) => [Vue, MiOS], os: MiOS)
console.log(`Cannot reapply theme. ${e}`);
}

//#region line width
document.documentElement.style.setProperty('--lineWidth', `1px`);
//#endregion

//#region fontSize
if (os.store.state.device.fontSize >= -0.5) {
document.documentElement.style.setProperty('--fontSize', `${os.store.state.device.fontSize}em`);
Expand Down
10 changes: 9 additions & 1 deletion src/misc/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,12 +189,20 @@ export class StatusError extends Error {
public statusCode: number;
public statusMessage?: string;
public isClientError: boolean;
public isPermanentError: boolean;

constructor(message: string, statusCode: number, statusMessage?: string) {
super(message);
this.name = 'StatusError';
this.statusCode = statusCode;
this.statusMessage = statusMessage;
this.isClientError = typeof this.statusCode === 'number' && this.statusCode >= 400 && this.statusCode < 500;

this.isClientError = false;
this.isPermanentError = false;

if (typeof this.statusCode === 'number' && this.statusCode >= 400 && this.statusCode < 500) {
this.isClientError = true;
this.isPermanentError = this.statusCode !== 408 && this.statusCode !== 429;
}
}
}
2 changes: 2 additions & 0 deletions src/models/note-reaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export default NoteReaction;

export interface INoteReaction {
_id: mongo.ObjectID;
/** AP id (remote only) */
uri?: string;
createdAt: Date;
noteId: mongo.ObjectID;
userId: mongo.ObjectID;
Expand Down
4 changes: 2 additions & 2 deletions src/queue/processors/deliver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ export default async (job: Bull.Job<DeliverJobData>) => {

if (res instanceof StatusError) {
// 4xx
if (res.isClientError || res.statusCode === 501) {
if (res.isPermanentError || res.statusCode === 501) {
// Mastodonから返ってくる401がどうもpermanent errorじゃなさそう
if (res.statusCode === 401 || res.statusCode === 408 || res.statusCode === 429) {
if (res.statusCode === 401) {
throw `${res.statusCode} ${res.statusMessage}`;
}

Expand Down
3 changes: 1 addition & 2 deletions src/queue/processors/inbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ export const tryProcessInbox = async (data: InboxJobData, ctx?: ApContext): Prom
try {
user = await resolvePerson(getApId(activity.actor), undefined, resolver, isDelete(activity) || isUndo(activity)) as IRemoteUser;
} catch (e) {
// 対象が4xxならスキップ
if (e instanceof StatusError && e.isClientError) {
if (e instanceof StatusError && e.isPermanentError) {
return `skip: Ignored actor ${activity.actor} - ${e.statusCode}`;
}
throw `Error in actor ${activity.actor} - ${e.statusCode || e}`;
Expand Down
2 changes: 1 addition & 1 deletion src/remote/activitypub/audience.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export async function parseAudience(actor: IRemoteUser, to?: ApObject, cc?: ApOb
};
}

if (toGroups.followers.length > 0) {
if (toGroups.followers.length > 0 || ccGroups.followers.length > 0) {
return {
visibility: 'followers',
mentionedUsers,
Expand Down
4 changes: 4 additions & 0 deletions src/remote/activitypub/kernel/create/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import createNote from './note';
import { ICreate, getApId, isPost, getApType } from '../../type';
import { apLogger } from '../../logger';
import { toArray, concat, unique } from '../../../../prelude/array';
import { StatusError } from '../../../../misc/fetch';

const logger = apLogger;

Expand Down Expand Up @@ -36,6 +37,9 @@ export default async (actor: IRemoteUser, activity: ICreate): Promise<string> =>
object = await resolver.resolve(activity.object);
} catch (e) {
logger.error(`Resolution failed: ${e}`);
if (e instanceof StatusError && e.isPermanentError) {
return `${e.statusCode} ${e.statusMessage}`;
}
throw e;
}

Expand Down
2 changes: 1 addition & 1 deletion src/remote/activitypub/kernel/create/note.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default async function(resolver: Resolver, actor: IRemoteUser, note: IObj
const n = await createNote(note, resolver, silent);
return n ? 'ok' : 'skip';
} catch (e) {
if (e instanceof StatusError && e.isClientError) {
if (e instanceof StatusError && e.isPermanentError) {
return `skip ${e.statusCode}`;
} else {
throw e;
Expand Down
2 changes: 1 addition & 1 deletion src/remote/activitypub/kernel/like.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default async (actor: IRemoteUser, activity: ILike): Promise<string> => {
await extractEmojis(activity.tag, actor.host).catch(() => null);

try {
await create(actor, note, activity._misskey_reaction || activity.content || activity.name, getApType(activity) === 'Dislike');
await create(actor, note, activity._misskey_reaction || activity.content || activity.name, getApType(activity) === 'Dislike', getApId(activity));
} catch (e: any) {
if (e instanceof ReactionError) {
return `skip: ${e.type}`;
Expand Down
2 changes: 1 addition & 1 deletion src/remote/activitypub/models/image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export async function createImage(actor: IRemoteUser, value: IObject): Promise<I
file = await uploadFromUrl({ url: image.url, user: actor, uri: image.url, sensitive: !!image.sensitive, isLink: !cache });
} catch (e) {
// 4xxの場合は添付されてなかったことにする
if (e instanceof StatusError && e.isClientError) {
if (e instanceof StatusError && e.isPermanentError) {
logger.warn(`Ignored image: ${image.url} - ${e.statusCode}`);
return null;
}
Expand Down
1 change: 1 addition & 0 deletions src/remote/activitypub/renderer/note.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ export default async function renderNote(note: INote, dive = true): Promise<any>
inReplyTo,
attachment: files.map(renderDocument),
sensitive: note.cw != null || files.some(file => file.metadata.isSensitive),
likes: `${config.url}/notes/${note._id}/likes`,
tag,
...asPoll,
};
Expand Down
14 changes: 11 additions & 3 deletions src/server/activitypub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import * as httpSignature from '@peertube/http-signature';

import { renderActivity } from '../remote/activitypub/renderer';
import Note, { INote } from '../models/note';
import User, { isLocalUser, ILocalUser, IUser } from '../models/user';
import User, { isLocalUser, ILocalUser, IUser, isRemoteUser } from '../models/user';
import Emoji from '../models/emoji';
import renderNote from '../remote/activitypub/renderer/note';
import renderKey from '../remote/activitypub/renderer/key';
import renderPerson from '../remote/activitypub/renderer/person';
import renderEmoji from '../remote/activitypub/renderer/emoji';
import Likes from './activitypub/likes';
import Outbox, { packActivity } from './activitypub/outbox';
import Followers from './activitypub/followers';
import Following from './activitypub/following';
Expand Down Expand Up @@ -239,7 +240,7 @@ export function setResponseType(ctx: Router.RouterContext) {
router.post('/inbox', inbox);
router.post('/users/:user/inbox', inbox);

const isNoteUserAvailable = async (note: INote) => {
export const isNoteUserAvailable = async (note: INote) => {
const user = await User.findOne({
_id: note.userId,
isDeleted: { $ne: true },
Expand Down Expand Up @@ -330,6 +331,9 @@ router.get('/notes/:note/activity', async ctx => {
setResponseType(ctx);
});

// likes
router.get('/notes/:note/likes', Likes);

// outbox
router.get('/users/:user/outbox', Outbox);

Expand Down Expand Up @@ -404,9 +408,13 @@ router.get('/users/:user', async (ctx, next) => {
isDeleted: { $ne: true },
isSuspended: { $ne: true },
noFederation: { $ne: true },
host: null
});

if (isRemoteUser(user)) {
ctx.redirect(user.uri);
return;
}

await userInfo(ctx, user);
});

Expand Down
102 changes: 102 additions & 0 deletions src/server/activitypub/likes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { ObjectID } from 'mongodb';
import * as Router from '@koa/router';
import config from '../../config';
import $ from 'cafy';
import ID, { transform } from '../../misc/cafy-id';
import { renderLike } from '../../remote/activitypub/renderer/like';
import { renderActivity } from '../../remote/activitypub/renderer';
import renderOrderedCollection from '../../remote/activitypub/renderer/ordered-collection';
import renderOrderedCollectionPage from '../../remote/activitypub/renderer/ordered-collection-page';
import { setResponseType, isNoteUserAvailable } from '../activitypub';

import Note from '../../models/note';
import { sum } from '../../prelude/array';
import * as url from '../../prelude/url';
import NoteReaction from '../../models/note-reaction';

export default async (ctx: Router.RouterContext) => {
if (config.disableFederation) ctx.throw(404);

if (!ObjectID.isValid(ctx.params.note)) {
ctx.status = 404;
return;
}

const note = await Note.findOne({
_id: new ObjectID(ctx.params.note),
deletedAt: { $exists: false },
'_user.host': null,
visibility: { $in: ['public', 'home'] },
localOnly: { $ne: true },
copyOnce: { $ne: true }
});

if (note == null || !await isNoteUserAvailable(note)) {
ctx.status = 404;
return;
}

// Get 'cursor' parameter
const [cursor, cursorErr] = $.optional.type(ID).get(ctx.request.query.cursor);

// Get 'page' parameter
const pageErr = !$.optional.str.or(['true', 'false']).ok(ctx.request.query.page);
const page: boolean = ctx.request.query.page === 'true';

// Validate parameters
if (cursorErr || pageErr) {
ctx.status = 400;
return;
}

const limit = 100;
const partOf = `${config.url}/notes/${note._id}/likes`;

if (page) {
const query = {
noteId: note._id
} as any;

// カーソルが指定されている場合
if (cursor) {
query._id = {
$lt: transform(cursor)
};
}

const reactions = await NoteReaction.find(query, {
limit: limit + 1,
sort: { _id: -1 },
});

// 「次のページ」があるかどうか
const inStock = reactions.length === limit + 1;
if (inStock) reactions.pop();

const renderedLikes = await Promise.all(reactions.map(reaction => reaction.uri ?? renderLike(reaction, note)));

const rendered = renderOrderedCollectionPage(
`${partOf}?${url.query({
page: 'true',
cursor
})}`,
sum(Object.values(note.reactionCounts)),
renderedLikes, partOf,
null,
inStock ? `${partOf}?${url.query({
page: 'true',
cursor: reactions[reactions.length - 1]._id.toHexString()
})}` : null
);

ctx.body = renderActivity(rendered);
ctx.set('Cache-Control', 'public, max-age=180');
setResponseType(ctx);
} else {
// index page
const rendered = renderOrderedCollection(partOf, sum(Object.values(note.reactionCounts)), `${partOf}?page=true`, null);
ctx.body = renderActivity(rendered);
ctx.set('Cache-Control', 'public, max-age=180');
setResponseType(ctx);
}
};
3 changes: 2 additions & 1 deletion src/services/note/reaction/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class ReactionError extends Error {
}
//#endregion Error

export default async (user: IUser, note: INote, reaction?: string, dislike = false): Promise<INoteReaction> => {
export default async (user: IUser, note: INote, reaction?: string, dislike = false, uri?: string): Promise<INoteReaction> => {
// detect direction
// LL => local to local, LR => local to remote, RL => remote to local, RR => remote to remote
const direction = `${ isLocalUser(user) ? 'L' : 'R' }${ note._user.host == null ? 'L' : 'R' }`;
Expand All @@ -47,6 +47,7 @@ export default async (user: IUser, note: INote, reaction?: string, dislike = fal

const inserted = {
_id: new mongo.ObjectID(),
uri,
createdAt: new Date(),
noteId: note._id,
userId: user._id,
Expand Down

0 comments on commit f4cd398

Please sign in to comment.