Skip to content

Commit

Permalink
リモートクリップ説明文がローカル仕様になってる問題の修正 (kokonect-link#466)
Browse files Browse the repository at this point in the history
  • Loading branch information
kozakura913 authored Sep 19, 2024
1 parent 5b803db commit b761657
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 4 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG_YOJO.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ Cherrypick 4.11.1
- Enhance: チャートの連合グラフで割合を表示
- Enhance: お気に入り登録クリップの一覧画面から登録解除できるように
- Fix: リモートから添付されてきたクリップURLにホスト情報があると二重になる不具合を修正 [#460](https://github.com/yojo-art/cherrypick/pull/460)
- Fix: リモートクリップ説明文がローカル仕様になってる問題の修正 [#466](https://github.com/yojo-art/cherrypick/pull/466)

### Server
-
- Enhance: リモートユーザーの`/api/clips/show``/api/users/clips`の応答にemojisを追加 [#466](https://github.com/yojo-art/cherrypick/pull/466)

### Misc

Expand Down
3 changes: 3 additions & 0 deletions packages/backend/src/core/ClipService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { RoleService } from '@/core/RoleService.js';
import { IdService } from '@/core/IdService.js';
import type { MiLocalUser, MiUser } from '@/models/User.js';
import { Packed } from '@/misc/json-schema.js';
import { emojis } from '@/misc/remote-api-utils.js';

@Injectable()
export class ClipService {
Expand Down Expand Up @@ -201,6 +202,7 @@ export class ClipService {
public async showRemote(
clipId:string,
host:string,
fetch_emoji = false,
) : Promise<Packed<'Clip'>> {
const cache_key = 'clip:show:' + clipId + '@' + host;
const cache_value = await this.redisForRemoteApis.get(cache_key);
Expand Down Expand Up @@ -263,6 +265,7 @@ export class ClipService {
favoritedCount: remote_clip.favoritedCount,
isFavorited: false,
notesCount: remote_clip.notesCount,
emojis: (remote_clip.description && fetch_emoji) ? emojis(this.config, this.httpRequestService, this.redisForRemoteApis, host, remote_clip.description) : {},
});
}
}
75 changes: 75 additions & 0 deletions packages/backend/src/misc/remote-api-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,81 @@ export type FetchRemoteApiOpts={
untilId?:string,
};

export async function emojis(
config: Config,
httpRequestService: HttpRequestService,
redisForRemoteApis: Redis.Redis,
host: string,
text:string,
):Promise<{[k: string]: string}> {
const emojis = new Map<string, string>();
const remote_emojis = await fetch_remote_emojis(config, httpRequestService, redisForRemoteApis, host);
for (const [key, value] of remote_emojis) {
const name = ':' + key + ':';
if (text.indexOf(name) !== -1) {
emojis.set(key, value);
}
}
return Object.fromEntries(emojis);
}

export async function fetch_remote_emojis(
config: Config,
httpRequestService: HttpRequestService,
redisForRemoteApis: Redis.Redis,
host: string,
):Promise<Map<string, string>> {
const cache_key = 'emojis:' + host;
const cache_value = await redisForRemoteApis.get(cache_key);
if (cache_value !== null) {
//ステータス格納
if (cache_value.startsWith('__')) {
if (cache_value === '__SKIP_FETCH') return new Map();
//未定義のステータス
return new Map();
}
return new Map(Object.entries(JSON.parse(cache_value)));
}
const url = 'https://' + host + '/api/emojis';
const timeout = 30 * 1000;
const operationTimeout = 60 * 1000;
const res = got.get(url, {
headers: {
'User-Agent': config.userAgent,
},
timeout: {
lookup: timeout,
connect: timeout,
secureConnect: timeout,
socket: timeout, // read timeout
response: timeout,
send: timeout,
request: operationTimeout, // whole operation timeout
},
agent: {
http: httpRequestService.httpAgent,
https: httpRequestService.httpsAgent,
},
http2: true,
retry: {
limit: 2,
},
enableUnixSockets: false,
});
const text = await res.text();
const array = JSON.parse(text)?.emojis;
const parsed = new Map<string, string>();
if (Array.isArray(array)) {
for (const entry of array) {
parsed.set(entry.name, entry.url);
}
}
const redisPipeline = redisForRemoteApis.pipeline();
redisPipeline.set(cache_key, JSON.stringify(Object.fromEntries(parsed)));
redisPipeline.expire(cache_key, 60 * 60);
await redisPipeline.exec();
return parsed;
}
export async function fetch_remote_api(
config: Config, httpRequestService: HttpRequestService, host: string, endpoint: string, opts: FetchRemoteApiOpts,
) {
Expand Down
9 changes: 9 additions & 0 deletions packages/backend/src/models/json-schema/clip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,14 @@ export const packedClipSchema = {
type: 'integer',
optional: true, nullable: false,
},
emojis: {
type: 'object',
optional: true, nullable: false,
additionalProperties: {
anyOf: [{
type: 'string',
}],
},
},
},
} as const;
3 changes: 2 additions & 1 deletion packages/backend/src/server/api/endpoints/clips/show.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
super(meta, paramDef, async (ps, me) => {
const parsed_id = ps.clipId.split('@');
if (parsed_id.length === 2 ) {//is remote
const clip = await clipService.showRemote(parsed_id[0], parsed_id[1]).catch(err => {
const clip = await clipService.showRemote(parsed_id[0], parsed_id[1], true).catch(err => {
console.error(err);
throw new ApiError(meta.errors.failedToResolveRemoteUser);
});
if (me) {
Expand Down
3 changes: 2 additions & 1 deletion packages/backend/src/server/api/endpoints/users/clips.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
import type { Config } from '@/config.js';
import { HttpRequestService } from '@/core/HttpRequestService.js';
import { awaitAll } from '@/misc/prelude/await-all.js';
import { fetch_remote_api, fetch_remote_user_id } from '@/misc/remote-api-utils.js';
import { emojis, fetch_remote_api, fetch_remote_user_id } from '@/misc/remote-api-utils.js';
import type { FindOptionsWhere } from 'typeorm';

export const meta = {
Expand Down Expand Up @@ -127,6 +127,7 @@ async function remote(
favoritedCount: remote_clip.favoritedCount,
isFavorited: false,
notesCount: remote_clip.notesCount,
emojis: remote_clip.description ? emojis(config, httpRequestService, redisForRemoteApis, user.host, remote_clip.description) : {},
});
clips.push(clip);
}
Expand Down
3 changes: 3 additions & 0 deletions packages/cherrypick-js/src/autogen/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4901,6 +4901,9 @@ export type components = {
favoritedCount: number;
isFavorited?: boolean;
notesCount?: number;
emojis?: {
[key: string]: string;
};
};
FederationInstance: {
/** Format: id */
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/pages/clip.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_panel">
<div class="_gaps_s" :class="$style.description">
<div v-if="clip.description">
<Mfm :text="clip.description" :isNote="false"/>
<Mfm :text="clip.description" :isNote="false" :author="clip.user" :emojiUrls="clip.emojis"/>
</div>
<div v-else>({{ i18n.ts.noDescription }})</div>
<div>
Expand Down

0 comments on commit b761657

Please sign in to comment.