Skip to content

Commit

Permalink
Merge branch 'develop' into basedCherrypickVersion
Browse files Browse the repository at this point in the history
  • Loading branch information
kozakura913 authored Dec 20, 2024
2 parents f1d7a33 + 32aa87a commit ed0af37
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 63 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG_YOJO.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
- Enhance: 表示中のタグTLをお気に入り登録するボタンを追加 [#561](https://github.com/yojo-art/cherrypick/pull/561)

### Server
- Fix: `api/ap/fetch-outbox`が正しく動作しないのを修正[#560](https://github.com/yojo-art/cherrypick/pull/560)
- Fix: PersonのserchableByが正しく連合できていないのを修正[#556](https://github.com/yojo-art/cherrypick/pull/556)
- Fix: SerchableByが未設定の時にプライバシーを更新できないことがある[#567](https://github.com/yojo-art/cherrypick/pull/567)
- Enhance: `/users/${id}``Accept: application/ld+json`ではないリクエストが来たとき`/@${username}`にリダイレクトするように [#554](https://github.com/yojo-art/cherrypick/pull/554)

## 1.1.0
Expand Down
15 changes: 14 additions & 1 deletion packages/backend/src/core/activitypub/ApResolverService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { UtilityService } from '@/core/UtilityService.js';
import { bindThis } from '@/decorators.js';
import { LoggerService } from '@/core/LoggerService.js';
import type Logger from '@/logger.js';
import { isCollectionOrOrderedCollection, isIOrderedCollectionPage } from './type.js';
import { isCollectionOrOrderedCollection, isIOrderedCollectionPage, isOrderedCollection } from './type.js';
import { ApDbResolverService } from './ApDbResolverService.js';
import { ApRendererService } from './ApRendererService.js';
import { ApRequestService } from './ApRequestService.js';
Expand Down Expand Up @@ -65,6 +65,19 @@ export class Resolver {
}
}

@bindThis
public async resolveOrderedCollection(value: string | IObject): Promise<IOrderedCollection> {
const collection = typeof value === 'string'
? await this.resolve(value)
: value;

if (isOrderedCollection(collection)) {
return collection;
} else {
throw new Error(`unrecognized collection type: ${collection.type}`);
}
}

@bindThis
public async resolveOrderedCollectionPage(value: string | IObject): Promise<IOrderedCollectionPage> {
const collection = typeof value === 'string'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { NoteCreateService } from '@/core/NoteCreateService.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import { ApDbResolverService } from '@/core/activitypub/ApDbResolverService.js';
import { isIOrderedCollectionPage, isCreate, IOrderedCollectionPage, isNote } from '../type.js';
import { isCreate, IOrderedCollectionPage, isNote } from '../type.js';
import { ApAudienceService } from '../ApAudienceService.js';
import type { OnModuleInit } from '@nestjs/common';
import type { ApNoteService } from './ApNoteService.js';
Expand Down Expand Up @@ -83,55 +83,42 @@ export class ApOutboxFetchService implements OnModuleInit {
this.logger.info(`Fetcing the Outbox: ${outboxUrl}`);
const Resolver = resolver ?? this.apResolverService.createResolver();
const cache = await this.redisClient.get(`${outboxUrl}--next`);
// Resolve to (Ordered)Collection Object
const outbox = cache ? await Resolver.resolveOrderedCollectionPage(cache) : await Resolver.resolveCollection(outboxUrl);
let next: string | IOrderedCollectionPage;

if (!cache && outbox.type !== 'OrderedCollection') throw new IdentifiableError('0be2f5a1-2345-46d8-b8c3-430b111c68d3', 'outbox type is not OrderedCollection');
if (!cache && !outbox.first) throw new IdentifiableError('a723c2df-0250-4091-b5fc-e3a7b36c7b61', 'outbox first page not exist');
if (!cache) {
// Resolve to (Ordered)Collection Object
const outbox = await Resolver.resolveOrderedCollection(outboxUrl);
if (!outbox.first) throw new IdentifiableError('a723c2df-0250-4091-b5fc-e3a7b36c7b61', 'outbox first page not exist');
next = outbox.first;
} else next = cache;

let nextUrl = cache ? (outbox as IOrderedCollectionPage).next : outbox.first;
let page = 0;
let created = 0;
if (typeof(nextUrl) !== 'string') {
const first = (nextUrl as any);
if (first.partOf !== user.outbox) throw new IdentifiableError('6603433f-99db-4134-980c-48705ae57ab8', 'outbox part is invalid');

const activityes = first.orderedItems ?? first.items;
await this.fetchObjects(user, activityes, includeAnnounce, created);
for (let page = 0; page < pagelimit; page++) {
const collection = (typeof(next) === 'string' ? await Resolver.resolveOrderedCollectionPage(next) : next);
if (collection.partOf !== user.outbox) throw new IdentifiableError('6603433f-99db-4134-980c-48705ae57ab8', 'outbox part is invalid');

page = 1;
if (!first.next) return;
}

for (; page < pagelimit; page++) {
this.logger.info(nextUrl as string);
const collectionPage = (typeof(nextUrl) === 'string' ? await Resolver.resolveOrderedCollectionPage(nextUrl) : nextUrl) as IOrderedCollectionPage;
if (!isIOrderedCollectionPage(collectionPage)) throw new IdentifiableError('2a05bb06-f38c-4854-af6f-7fd5e87c98ee', 'Object is not collectionPage');
if (collectionPage.partOf !== user.outbox) throw new IdentifiableError('6603433f-99db-4134-980c-48705ae57ab8', 'outbox part is invalid');

const activityes = (collectionPage.orderedItems ?? collectionPage.items);
nextUrl = collectionPage.next;
if (!activityes) continue;
const activityes = (collection.orderedItems ?? collection.items);
if (!activityes) throw new IdentifiableError('2a05bb06-f38c-4854-af6f-7fd5e87c98ee', 'item is unavailable');

created = await this.fetchObjects(user, activityes, includeAnnounce, created);
if (createLimit <= created) break;//次ページ見て一件だけしか取れないのは微妙
if (!nextUrl) {
break;
}
if (!collection.next) break;

await this.redisClient.set(`${outboxUrl}--next`, `${nextUrl}`, 'EX', 60 * 15);//15min
next = collection.next;
await this.redisClient.set(`${outboxUrl}--next`, `${next}`, 'EX', 60 * 15);//15min
}
this.logger.succ(`Outbox Fetced: ${outboxUrl}`);
//this.logger.info(`Outbox Fetced last: ${nextUrl}`);
}

@bindThis
private async fetchObjects(user: MiRemoteUser, activityes: any[], includeAnnounce:boolean, created: number): Promise<number> {
for (const activity of activityes) {
if (createLimit < created) return created;
try {
if (includeAnnounce && activity.type === 'Announce') {
const object = await this.apDbResolverService.getNoteFromApId(activity.id);
if (activity.actor !== user.uri) throw new IdentifiableError('bde7c204-5441-4a87-9b7e-f81e8d05788a');
if (activity.type === 'Announce' && includeAnnounce) {
const object = await this.apNoteService.fetchNote(activity.id);

if (object) continue;

Expand Down Expand Up @@ -188,18 +175,22 @@ export class ApOutboxFetchService implements OnModuleInit {
} finally {
unlock();
}
} else if (isCreate(activity) && typeof(activity.object) !== 'string' && isNote(activity.object)) {
const object = await this.apDbResolverService.getNoteFromApId(activity.object);
if (object) continue;
} else if (isCreate(activity)) {
if (typeof(activity.object) !== 'string') {
if (!isNote(activity)) continue;
}
const fetch = await this.apNoteService.fetchNote(activity.object);
if (fetch) continue;
await this.apNoteService.createNote(activity.object, undefined, true);
}
} catch (err) {
if (err instanceof AbortError) {
this.logger.warn(`Aborted note: ${activity.id}`);
//リモートのリモートが落ちてるなどで止まるとほかが見れなくなってしまうので再スローしない
if (err instanceof IdentifiableError) {
if (err.id === 'bde7c204-5441-4a87-9b7e-f81e8d05788a') this.logger.error(`fetchErrorInvalidActor:${activity.id}`);
} else {
this.logger.warn(JSON.stringify(err));
this.logger.warn(JSON.stringify(activity));
throw err;
this.logger.error(`fetchError:${activity.id}`);
this.logger.error(`${err}`);
continue;
}
}
created ++;
Expand Down
3 changes: 1 addition & 2 deletions packages/backend/src/core/activitypub/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,15 +112,14 @@ export interface IOrderedCollection extends IObject {
type: 'OrderedCollection';
totalItems?: number;
orderedItems?: ApObject;
first?: IObject | string;
first?: IOrderedCollectionPage | string;
last?: IObject | string;
}

export interface IOrderedCollectionPage extends IObject {
type: 'OrderedCollectionPage';
partOf: string;
totalItems?: number;
first?: IObject | string;
orderedItems?: IObject[];
items?: IObject[];
prev: string;
Expand Down
16 changes: 12 additions & 4 deletions packages/backend/src/server/api/endpoints/ap/fetch-outbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@ export const meta = {
code: 'OUTBOX_FIRST_PAGE_UNDEFINED_THIS_USER',
id: 'e1f29e66-86a9-4fdc-9be6-63d4587dc350',
},
invalidPart: {
message: 'outbox part is invalid',
code: 'OUTBOX_PART_IS_INVALID',
id: 'c3e584df-068a-4b1d-967e-54f2f30f7cba',
},
itemIsUnavailable: {
message: 'outbox item is unavailable',
code: 'OUTBOX_ITEM_IS_UNAVAILABLE',
id: 'a07b05af-5f66-4203-918c-ebff9e9384bf',
},
},
} as const;

Expand Down Expand Up @@ -83,11 +93,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (err.id === '3fc5a089-cab4-48db-b9f3-f220574b3c0a') throw new ApiError(meta.errors.noSuchUser);
if (err.id === '67070303-177c-4600-af93-b26a7ab889c6') throw new ApiError(meta.errors.isLocalUser);
if (err.id === 'e7a2e510-a8ce-40e9-b1e6-c007bacdc89f') throw new ApiError(meta.errors.outboxUndefined);
//if (err.id === 'b27090c8-8a68-4189-a445-14591c32a89c')
//if (err.id === '0be2f5a1-2345-46d8-b8c3-430b111c68d3')
if (err.id === 'a723c2df-0250-4091-b5fc-e3a7b36c7b61') throw new ApiError(meta.errors.outboxFirstPageUndefined);
//if (err.id === '6603433f-99db-4134-980c-48705ae57ab8')
//if (err.id === '2a05bb06-f38c-4854-af6f-7fd5e87c98ee')
if (err.id === '6603433f-99db-4134-980c-48705ae57ab8') throw new ApiError(meta.errors.invalidPart);
if (err.id === '2a05bb06-f38c-4854-af6f-7fd5e87c98ee') throw new ApiError(meta.errors.itemIsUnavailable);
}
throw (err);
}
Expand Down
6 changes: 5 additions & 1 deletion packages/backend/src/server/api/endpoints/i/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,11 @@ export const paramDef = {
isLocked: { type: 'boolean' },
isExplorable: { type: 'boolean' },
isIndexable: { type: 'boolean' },
searchableBy: { type: 'string', enum: ['public', 'followersAndReacted', 'reactedOnly', 'private'] },
searchableBy: {
type: 'string',
enum: ['public', 'followersAndReacted', 'reactedOnly', 'private', null],
nullable: true,
description: '検索許可SearchableByの値を指定しますデフォルトはnull(isIndexableを参照)' },
hideOnlineStatus: { type: 'boolean' },
publicReactions: { type: 'boolean' },
carefulBot: { type: 'boolean' },
Expand Down
7 changes: 5 additions & 2 deletions packages/cherrypick-js/src/autogen/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20283,8 +20283,11 @@ export type operations = {
isLocked?: boolean;
isExplorable?: boolean;
isIndexable?: boolean;
/** @enum {string} */
searchableBy?: 'public' | 'followersAndReacted' | 'reactedOnly' | 'private';
/**
* @description 検索許可SearchableByの値を指定しますデフォルトはnull(isIndexableを参照)
* @enum {string|null}
*/
searchableBy?: 'public' | 'followersAndReacted' | 'reactedOnly' | 'private' | null;
hideOnlineStatus?: boolean;
publicReactions?: boolean;
carefulBot?: boolean;
Expand Down
9 changes: 4 additions & 5 deletions packages/frontend/src/components/MkNoteDetailed.vue
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template v-if="defaultStore.state.showReplyButtonInNoteFooter">
<button v-if="!note.isHidden" v-vibrate="defaultStore.state.vibrateSystem ? 5 : []" v-tooltip="i18n.ts.reply" class="_button" :class="$style.noteFooterButton" @click="reply()">
<i class="ti ti-arrow-back-up"></i>
<p v-if="appearNote.repliesCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.repliesCount) }}</p>
<p v-if="appearNote.repliesCount > 0" :class="$style.noteFooterButtonCount">{{ Number(appearNote.repliesCount) }}</p>
</button>
<button v-else-if="note.isHidden" class="_button" :class="$style.noteFooterButton" disabled>
<i class="ti ti-ban"></i>
Expand All @@ -192,7 +192,7 @@ SPDX-License-Identifier: AGPL-3.0-only
@click.stop="defaultStore.state.renoteQuoteButtonSeparation && ((!defaultStore.state.renoteVisibilitySelection && !appearNote.channel) || (appearNote.channel && !appearNote.channel.allowRenoteToExternal) || appearNote.visibility === 'followers') ? renoteOnly() : renote()"
>
<i class="ti ti-repeat"></i>
<p v-if="appearNote.renoteCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.renoteCount) }}</p>
<p v-if="appearNote.renoteCount > 0" :class="$style.noteFooterButtonCount">{{ Number(appearNote.renoteCount) }}</p>
</button>
<button v-else-if="!canRenote" class="_button" :class="$style.noteFooterButton" disabled>
<i class="ti ti-ban"></i>
Expand All @@ -206,7 +206,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<i v-else-if="appearNote.myReaction != null" class="ti ti-mood-edit" style="color: var(--MI_THEME-accent);"></i>
<i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
<i v-else class="ti ti-mood-plus"></i>
<p v-if="(appearNote.reactionAcceptance === 'likeOnly' || defaultStore.state.showReactionsCount) && appearNote.reactionCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.reactionCount) }}</p>
<p v-if="(appearNote.reactionAcceptance === 'likeOnly' || defaultStore.state.showReactionsCount) && appearNote.reactionCount > 0" :class="$style.noteFooterButtonCount">{{ Number(appearNote.reactionCount) }}</p>
</button>
<button v-if="canRenote && defaultStore.state.renoteQuoteButtonSeparation && defaultStore.state.showQuoteButtonInNoteFooter" ref="quoteButton" v-vibrate="defaultStore.state.vibrateSystem ? 5 : []" v-tooltip="i18n.ts.quote" class="_button" :class="$style.noteFooterButton" @click="quote()">
<i class="ti ti-quote"></i>
Expand Down Expand Up @@ -306,13 +306,13 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>

<script lang="ts" setup>
import { error } from 'console';
import { computed, inject, onMounted, provide, ref, shallowRef } from 'vue';
import * as mfm from 'mfc-js';
import * as Misskey from 'cherrypick-js';
import { CodeDiff } from 'v-code-diff';
import { isLink } from '@@/js/is-link.js';
import { host } from '@@/js/config.js';
import { shouldAnimatedMfm } from '@@/js/collapsed.js';
import MkSwitch from '@/components/MkSwitch.vue';
import MkNoteSub from '@/components/MkNoteSub.vue';
import MkNoteSimple from '@/components/MkNoteSimple.vue';
Expand All @@ -336,7 +336,6 @@ import { defaultStore, noteViewInterruptors } from '@/store.js';
import { reactionPicker } from '@/scripts/reaction-picker.js';
import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
import { $i } from '@/account.js';
import { shouldAnimatedMfm } from '@@/js/collapsed.js';
import { i18n } from '@/i18n.js';
import { getAbuseNoteMenu, getNoteClipMenu, getNoteMenu, getRenoteMenu, getRenoteOnly } from '@/scripts/get-note-menu.js';
import { useNoteCapture } from '@/scripts/use-note-capture.js';
Expand Down
8 changes: 0 additions & 8 deletions packages/frontend/src/scripts/get-user-menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,14 +316,6 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter
window.open(user.url, '_blank', 'noopener');
},
});
menuItems.push({
icon: 'ti ti-server',
text: i18n.ts.instanceInfo,
action: () => {
if (user.host == null) return;
router.push(`/instance-info/${user.host}`);
},
});
} else {
menuItems.push({
icon: 'ti ti-code',
Expand Down

0 comments on commit ed0af37

Please sign in to comment.