Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

お気に入り登録クリップの一覧画面から登録解除できるように #448

Merged
merged 6 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG_YOJO.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ Cherrypick 4.11.1

### General
- Enhance: 連合一覧のソートにリバーシのバージョンを追加
- Enhance: リモートのクリップをお気に入りに登録できるように

### Client
- Fix: リアクションが閲覧できる状態でも見れない問題を修正 [#429](https://github.com/yojo-art/cherrypick/pull/429)
- Enhance: チャートの連合グラフで割合を表示
- Enhance: お気に入り登録クリップの一覧画面から登録解除できるように

### Server
-
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project, yojo-art team
* SPDX-License-Identifier: AGPL-3.0-only
*/

export class clipFavoriteRemoteAuthor1726460877945 {
name = 'clipFavoriteRemoteAuthor1726460877945'

async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "clip_favorite_remote" ADD "authorId" character varying(32)`);
await queryRunner.query(`ALTER TABLE "clip_favorite_remote" ADD CONSTRAINT "FK_e306e7566fd6101e9767702980c" FOREIGN KEY ("authorId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
}

async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "clip_favorite_remote" DROP CONSTRAINT "FK_e306e7566fd6101e9767702980c"`);
await queryRunner.query(`ALTER TABLE "clip_favorite_remote" DROP COLUMN "authorId"`);
}
}
28 changes: 27 additions & 1 deletion packages/backend/src/core/ClipService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { bindThis } from '@/decorators.js';
import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js';
import { RoleService } from '@/core/RoleService.js';
import { IdService } from '@/core/IdService.js';
import type { MiLocalUser } from '@/models/User.js';
import type { MiLocalUser, MiUser } from '@/models/User.js';
import { Packed } from '@/misc/json-schema.js';

@Injectable()
Expand Down Expand Up @@ -172,6 +172,32 @@ export class ClipService {
this.notesRepository.decrement({ id: noteId }, 'clippedCount', 1);
}
@bindThis
async showRemoteOrDummy(clipId: string, author: MiUser|null) : Promise<Packed<'Clip'>> {
if (author == null) {
throw new Error();
}
try {
if (author.host == null) {
throw new Error();
}
return await this.showRemote(clipId, author.host);
} catch {
return await awaitAll({
id: clipId + '@' + (author.host ? author.host : ''),
createdAt: new Date(0).toISOString(),
lastClippedAt: new Date(0).toISOString(),
userId: author.id,
user: this.userEntityService.pack(author),
name: 'Unavailable',
description: '',
isPublic: true,
favoritedCount: 0,
isFavorited: false,
notesCount: 0,
});
}
}
@bindThis
public async showRemote(
clipId:string,
host:string,
Expand Down
129 changes: 129 additions & 0 deletions packages/backend/src/misc/remote-api-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project, yojo-art team
* SPDX-License-Identifier: AGPL-3.0-only
*/

import Redis from 'ioredis';
import got, * as Got from 'got';
import type { Config } from '@/config.js';
import { HttpRequestService } from '@/core/HttpRequestService.js';
import { MiUser } from '@/models/User.js';

export type FetchRemoteApiOpts={
/** リモートで割り当てられているid */
userId?:string,
limit?:number,
sinceId?:string,
untilId?:string,
};

export async function fetch_remote_api(
config: Config, httpRequestService: HttpRequestService, host: string, endpoint: string, opts: FetchRemoteApiOpts,
) {
const url = 'https://' + host + endpoint;
const sinceIdRemote = opts.sinceId ? opts.sinceId.split('@')[0] : undefined;
const untilIdRemote = opts.untilId ? opts.untilId.split('@')[0] : undefined;
const timeout = 30 * 1000;
const operationTimeout = 60 * 1000;
const res = got.post(url, {
headers: {
'User-Agent': config.userAgent,
'Content-Type': 'application/json; charset=utf-8',
},
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: 1,
},
enableUnixSockets: false,
body: JSON.stringify({
userId: opts.userId,
limit: opts.limit,
sinceId: sinceIdRemote,
untilId: untilIdRemote,
}),
});
return await res.text();
}
/** userがリモートで割り当てられているidを取得 */
export async function fetch_remote_user_id(
config:Config,
httpRequestService: HttpRequestService,
redisForRemoteApis: Redis.Redis,
user:MiUser,
) {
//ローカルのIDからリモートのIDを割り出す
const cache_key = 'remote-userId:' + user.id;
const id = await redisForRemoteApis.get(cache_key);
if (id !== null) {
if (id === '__NOT_MISSKEY') {
return null;
}
if (id === '__INTERVAL') {
return null;
}
//アクセス時に有効期限を更新
redisForRemoteApis.expire(cache_key, 7 * 24 * 60 * 60);
return id;
}
try {
const url = 'https://' + user.host + '/api/users/show';
const timeout = 30 * 1000;
const operationTimeout = 60 * 1000;
const res = got.post(url, {
headers: {
'User-Agent': config.userAgent,
'Content-Type': 'application/json; charset=utf-8',
},
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: 1,
},
enableUnixSockets: false,
body: JSON.stringify({
username: user.username,
}),
});
const text = await res.text();
const json = JSON.parse(text);
if (json.id != null) {
const redisPipeline = redisForRemoteApis.pipeline();
redisPipeline.set(cache_key, json.id);
//キャッシュ期限1週間
redisPipeline.expire(cache_key, 7 * 24 * 60 * 60);
await redisPipeline.exec();
return json.id as string;
}
} catch {
const redisPipeline = redisForRemoteApis.pipeline();
redisPipeline.set(cache_key, '__INTERVAL');
redisPipeline.expire(cache_key, 60 * 60);
await redisPipeline.exec();
}
return null;
}
8 changes: 8 additions & 0 deletions packages/backend/src/models/ClipFavoriteRemote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ export class MiClipFavoriteRemote {
@JoinColumn()
public user: MiUser | null;

@Column(id())
public authorId: MiUser['id'];
@ManyToOne(type => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
public author: MiUser | null;

@Column('varchar', {
length: 32,
})
Expand Down
3 changes: 2 additions & 1 deletion packages/backend/src/server/api/endpoints/clips/favorite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
const host = clipIdArray.length > 1 ? clipIdArray[1] : null;
if (host) {
const clipId = clipIdArray[0];
await clipService.showRemote(clipId, host);
const clip = await clipService.showRemote(clipId, host);

const exist = await this.clipFavoritesRemoteRepository.exists({
where: {
Expand All @@ -94,6 +94,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
clipId: clipId,
host: host,
userId: me.id,
authorId: clip.userId,
});
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
if (ps.withRemote) {
const query = this.clipFavoritesRemoteRepository.createQueryBuilder('favorite')
.andWhere('favorite.userId = :meId', { meId: me.id });
.andWhere('favorite.userId = :meId', { meId: me.id })
.leftJoinAndSelect('favorite.author', 'author');

const favorites = await query.getMany();
const remoteFavorites = await Promise.all(favorites.map(e => clipService.showRemote(e.clipId, e.host)));
let remoteFavorites = await Promise.all(favorites.map(e => clipService.showRemoteOrDummy(e.clipId, e.author)));
remoteFavorites = remoteFavorites.map(clip => {
clip.isFavorited = true;
return clip;
});
myFavorites = myFavorites.concat(remoteFavorites);
}
return myFavorites.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
Expand Down
Loading
Loading