From b752dc72e531f6c63f09876a1c68a87a77c03b49 Mon Sep 17 00:00:00 2001 From: taichan <40626578+tai-cha@users.noreply.github.com> Date: Wed, 25 Jun 2025 17:09:23 +0900 Subject: feat: ノートの下書き(draft of note) (#15298) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * WIp (backend) * Remove unused * 下書きbackend 続き * fix(backedn): visibilityが下書きに反映されない * Update packages/backend/src/postgres.ts Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> * Fix : import order * fix(backend) : createでcwが効かない * FIX FOREGIN KEY * wip: frontend(既存の下書きを挿入) まだ:チャンネル表示、下書きの作成、削除 * WIP: ノート選択ダイアログ 投稿時に下書きを削除 * Promiseに変更 * 連合なし、チャンネルも表示 * Hashtagの値抜け漏れ * hasthagを0文字でも作成可能に * 下書きの保存機構 * chore(misskey-js): build types * localOnly抜け漏れ * チャンネル情報の書き換え * enhance(frontend): ヘッダ部の表示改善 * fix(frontend): ファイル添付できない * fix: no file * fix(frontend): 投票が反映されない * ハッシュタグの展開(コメントアウト外し忘れ) * fix: visibleUserIdsが反映されない * enhance: APIの型を整備 * refactor: 型が整備できたのでasを削除 * Add userhost * fix * enhance: paginationを使う * fix * fix: 自分のアカウントでの投稿でしか下書きを利用できないように 完全に塞ぐことはできないが一応 * :art: * APIのエラーIDを追加 * enhance: スタイル調整 * remove unused code * :art: * fix: ロールポリシーの型 * ロールの編集画面 * ダイアログの挙動改善 * 下書き機能が利用できない場合は表示しないように * refactor * fix: ダブルクリックが効かない問題を修正 * add comments * fix * fix: 保存時のエラーの種別にかかわらずmodalを閉じないように * fix()backend: NoteDraftのreply, renoteの型が間違ってたので修正 (migtrationはあってた) * fix: 投稿フォームを空白にして通常リノートできるやつは下書きとしては弾くように * fix(backend): テキストが0文字でも下書きは保存できるように * Fix(backend): replyIdの型定義がミスっているのを修正 * chore(misskey-js): update types * Add CHANGELOG * lint * 常にサーバー下書きに保存し、上限を超えた場合のみ尋ねるように * NoteDraftServiceにcreate, updateの処理を移譲 * Fix typeerror * remove tooltip * Remove Mkbutton:short and use iconOnly * 不要なコメントの削除 * Remove Short Completely * wip * escキーまわりの挙動を改善 * 下書き選択時に下書き可能数と現在の量が分かるように * cleanUp * wip * wi * wip * Update MkPostForm.vue --------- Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> --- .../src/server/api/endpoints/notes/drafts/count.ts | 51 ++++ .../server/api/endpoints/notes/drafts/create.ts | 258 ++++++++++++++++++ .../server/api/endpoints/notes/drafts/delete.ts | 61 +++++ .../src/server/api/endpoints/notes/drafts/list.ts | 66 +++++ .../server/api/endpoints/notes/drafts/update.ts | 302 +++++++++++++++++++++ 5 files changed, 738 insertions(+) create mode 100644 packages/backend/src/server/api/endpoints/notes/drafts/count.ts create mode 100644 packages/backend/src/server/api/endpoints/notes/drafts/create.ts create mode 100644 packages/backend/src/server/api/endpoints/notes/drafts/delete.ts create mode 100644 packages/backend/src/server/api/endpoints/notes/drafts/list.ts create mode 100644 packages/backend/src/server/api/endpoints/notes/drafts/update.ts (limited to 'packages/backend/src/server/api/endpoints/notes') diff --git a/packages/backend/src/server/api/endpoints/notes/drafts/count.ts b/packages/backend/src/server/api/endpoints/notes/drafts/count.ts new file mode 100644 index 0000000000..002a545d32 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/notes/drafts/count.ts @@ -0,0 +1,51 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { NoteDraftsRepository } from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; + +export const meta = { + tags: ['notes', 'drafts'], + + requireCredential: true, + + prohibitMoved: true, + + kind: 'read:account', + + res: { + type: 'number', + optional: false, nullable: false, + description: 'The number of drafts', + }, + + errors: { + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + }, + required: [], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.noteDraftsRepository) + private noteDraftsRepository: NoteDraftsRepository, + ) { + super(meta, paramDef, async (ps, me) => { + const count = await this.noteDraftsRepository.createQueryBuilder('drafts') + .where('drafts.userId = :meId', { meId: me.id }) + .getCount(); + + return count; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/drafts/create.ts b/packages/backend/src/server/api/endpoints/notes/drafts/create.ts new file mode 100644 index 0000000000..1c28ec22d0 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/notes/drafts/create.ts @@ -0,0 +1,258 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import ms from 'ms'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { NoteDraftService } from '@/core/NoteDraftService.js'; +import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; +import { ApiError } from '@/server/api/error.js'; +import { NoteDraftEntityService } from '@/core/entities/NoteDraftEntityService.js'; +import { IdentifiableError } from '@/misc/identifiable-error.js'; + +export const meta = { + tags: ['notes', 'drafts'], + + requireCredential: true, + + prohibitMoved: true, + + kind: 'write:account', + + res: { + type: 'object', + optional: false, nullable: false, + properties: { + createdDraft: { + type: 'object', + optional: false, nullable: false, + ref: 'NoteDraft', + }, + }, + }, + + errors: { + noSuchRenoteTarget: { + message: 'No such renote target.', + code: 'NO_SUCH_RENOTE_TARGET', + id: 'b5c90186-4ab0-49c8-9bba-a1f76c282ba4', + }, + + cannotReRenote: { + message: 'You can not Renote a pure Renote.', + code: 'CANNOT_RENOTE_TO_A_PURE_RENOTE', + id: 'fd4cc33e-2a37-48dd-99cc-9b806eb2031a', + }, + + cannotRenoteDueToVisibility: { + message: 'You can not Renote due to target visibility.', + code: 'CANNOT_RENOTE_DUE_TO_VISIBILITY', + id: 'be9529e9-fe72-4de0-ae43-0b363c4938af', + }, + + noSuchReplyTarget: { + message: 'No such reply target.', + code: 'NO_SUCH_REPLY_TARGET', + id: '749ee0f6-d3da-459a-bf02-282e2da4292c', + }, + + cannotReplyToInvisibleNote: { + message: 'You cannot reply to an invisible Note.', + code: 'CANNOT_REPLY_TO_AN_INVISIBLE_NOTE', + id: 'b98980fa-3780-406c-a935-b6d0eeee10d1', + }, + + cannotReplyToPureRenote: { + message: 'You can not reply to a pure Renote.', + code: 'CANNOT_REPLY_TO_A_PURE_RENOTE', + id: '3ac74a84-8fd5-4bb0-870f-01804f82ce15', + }, + + cannotReplyToSpecifiedVisibilityNoteWithExtendedVisibility: { + message: 'You cannot reply to a specified visibility note with extended visibility.', + code: 'CANNOT_REPLY_TO_SPECIFIED_VISIBILITY_NOTE_WITH_EXTENDED_VISIBILITY', + id: 'ed940410-535c-4d5e-bfa3-af798671e93c', + }, + + cannotCreateAlreadyExpiredPoll: { + message: 'Poll is already expired.', + code: 'CANNOT_CREATE_ALREADY_EXPIRED_POLL', + id: '04da457d-b083-4055-9082-955525eda5a5', + }, + + noSuchChannel: { + message: 'No such channel.', + code: 'NO_SUCH_CHANNEL', + id: 'b1653923-5453-4edc-b786-7c4f39bb0bbb', + }, + + youHaveBeenBlocked: { + message: 'You have been blocked by this user.', + code: 'YOU_HAVE_BEEN_BLOCKED', + id: 'b390d7e1-8a5e-46ed-b625-06271cafd3d3', + }, + + noSuchFile: { + message: 'Some files are not found.', + code: 'NO_SUCH_FILE', + id: 'b6992544-63e7-67f0-fa7f-32444b1b5306', + }, + + cannotRenoteOutsideOfChannel: { + message: 'Cannot renote outside of channel.', + code: 'CANNOT_RENOTE_OUTSIDE_OF_CHANNEL', + id: '33510210-8452-094c-6227-4a6c05d99f00', + }, + + containsProhibitedWords: { + message: 'Cannot post because it contains prohibited words.', + code: 'CONTAINS_PROHIBITED_WORDS', + id: 'aa6e01d3-a85c-669d-758a-76aab43af334', + }, + + containsTooManyMentions: { + message: 'Cannot post because it exceeds the allowed number of mentions.', + code: 'CONTAINS_TOO_MANY_MENTIONS', + id: '4de0363a-3046-481b-9b0f-feff3e211025', + }, + + tooManyDrafts: { + message: 'You cannot create drafts any more.', + code: 'TOO_MANY_DRAFTS', + id: '9ee33bbe-fde3-4c71-9b51-e50492c6b9c8', + }, + + cannotRenoteToExternal: { + message: 'Cannot Renote to External.', + code: 'CANNOT_RENOTE_TO_EXTERNAL', + id: 'ed1952ac-2d26-4957-8b30-2deda76bedf7', + }, + }, + + limit: { + duration: ms('1hour'), + max: 300, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + visibility: { type: 'string', enum: ['public', 'home', 'followers', 'specified'], default: 'public' }, + visibleUserIds: { type: 'array', uniqueItems: true, items: { + type: 'string', format: 'misskey:id', + } }, + cw: { type: 'string', nullable: true, minLength: 1, maxLength: 100 }, + hashtag: { type: 'string', nullable: true, maxLength: 200 }, + localOnly: { type: 'boolean', default: false }, + reactionAcceptance: { type: 'string', nullable: true, enum: [null, 'likeOnly', 'likeOnlyForRemote', 'nonSensitiveOnly', 'nonSensitiveOnlyForLocalLikeOnlyForRemote'], default: null }, + replyId: { type: 'string', format: 'misskey:id', nullable: true }, + renoteId: { type: 'string', format: 'misskey:id', nullable: true }, + channelId: { type: 'string', format: 'misskey:id', nullable: true }, + + // anyOf内にバリデーションを書いても最初の一つしかチェックされない + text: { + type: 'string', + minLength: 0, + maxLength: MAX_NOTE_TEXT_LENGTH, + nullable: true, + }, + fileIds: { + type: 'array', + uniqueItems: true, + minItems: 1, + maxItems: 16, + items: { type: 'string', format: 'misskey:id' }, + }, + poll: { + type: 'object', + nullable: true, + properties: { + choices: { + type: 'array', + uniqueItems: true, + minItems: 0, + maxItems: 10, + items: { type: 'string', minLength: 1, maxLength: 50 }, + }, + multiple: { type: 'boolean' }, + expiresAt: { type: 'integer', nullable: true }, + expiredAfter: { type: 'integer', nullable: true, minimum: 1 }, + }, + required: ['choices'], + }, + }, + required: [], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + private noteDraftService: NoteDraftService, + private noteDraftEntityService: NoteDraftEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const draft = await this.noteDraftService.create(me, { + fileIds: ps.fileIds, + poll: ps.poll ? { + choices: ps.poll.choices, + multiple: ps.poll.multiple ?? false, + expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null, + expiredAfter: ps.poll.expiredAfter ?? null, + } : undefined, + text: ps.text ?? null, + replyId: ps.replyId ?? undefined, + renoteId: ps.renoteId ?? undefined, + cw: ps.cw ?? null, + ...(ps.hashtag ? { hashtag: ps.hashtag } : {}), + localOnly: ps.localOnly, + reactionAcceptance: ps.reactionAcceptance, + visibility: ps.visibility, + visibleUserIds: ps.visibleUserIds ?? [], + channelId: ps.channelId ?? undefined, + }).catch((err) => { + if (err instanceof IdentifiableError) { + switch (err.id) { + case '9ee33bbe-fde3-4c71-9b51-e50492c6b9c8': + throw new ApiError(meta.errors.tooManyDrafts); + case '04da457d-b083-4055-9082-955525eda5a5': + throw new ApiError(meta.errors.cannotCreateAlreadyExpiredPoll); + case 'b6992544-63e7-67f0-fa7f-32444b1b5306': + throw new ApiError(meta.errors.noSuchFile); + case '64929870-2540-4d11-af41-3b484d78c956': + throw new ApiError(meta.errors.noSuchRenoteTarget); + case '76cc5583-5a14-4ad3-8717-0298507e32db': + throw new ApiError(meta.errors.cannotReRenote); + case '075ca298-e6e7-485a-b570-51a128bb5168': + throw new ApiError(meta.errors.youHaveBeenBlocked); + case '81eb8188-aea1-4e35-9a8f-3334a3be9855': + throw new ApiError(meta.errors.cannotRenoteDueToVisibility); + case '6815399a-6f13-4069-b60d-ed5156249d12': + throw new ApiError(meta.errors.noSuchChannel); + case 'ed1952ac-2d26-4957-8b30-2deda76bedf7': + throw new ApiError(meta.errors.cannotRenoteToExternal); + case 'c4721841-22fc-4bb7-ad3d-897ef1d375b5': + throw new ApiError(meta.errors.noSuchReplyTarget); + case 'e6c10b57-2c09-4da3-bd4d-eda05d51d140': + throw new ApiError(meta.errors.cannotReplyToPureRenote); + case '593c323c-6b6a-4501-a25c-2f36bd2a93d6': + throw new ApiError(meta.errors.cannotReplyToInvisibleNote); + case '215dbc76-336c-4d2a-9605-95766ba7dab0': + throw new ApiError(meta.errors.cannotReplyToSpecifiedVisibilityNoteWithExtendedVisibility); + default: + throw err; + } + } + throw err; + }); + + const createdDraft = await this.noteDraftEntityService.pack(draft, me); + + return { + createdDraft, + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/drafts/delete.ts b/packages/backend/src/server/api/endpoints/notes/drafts/delete.ts new file mode 100644 index 0000000000..6c41145c18 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/notes/drafts/delete.ts @@ -0,0 +1,61 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { NoteDraftService } from '@/core/NoteDraftService.js'; +import { ApiError } from '../../../error.js'; + +export const meta = { + tags: ['notes', 'drafts'], + + requireCredential: true, + + prohibitMoved: true, + + kind: 'write:account', + + errors: { + noSuchNoteDraft: { + message: 'No such note draft.', + code: 'NO_SUCH_NOTE_DRAFT', + id: '49cd6b9d-848e-41ee-b0b9-adaca711a6b1', + }, + + accessDenied: { + message: 'Access denied.', + code: 'ACCESS_DENIED', + id: '56f35758-7dd5-468b-8439-5d6fb8ec9b8e', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + draftId: { type: 'string', nullable: false, format: 'misskey:id' }, + }, + required: ['draftId'], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + private noteDraftService: NoteDraftService, + ) { + super(meta, paramDef, async (ps, me) => { + const draft = await this.noteDraftService.get(me, ps.draftId); + if (draft == null) { + throw new ApiError(meta.errors.noSuchNoteDraft); + } + + if (draft.userId !== me.id) { + throw new ApiError(meta.errors.accessDenied); + } + + await this.noteDraftService.delete(me, draft.id); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/drafts/list.ts b/packages/backend/src/server/api/endpoints/notes/drafts/list.ts new file mode 100644 index 0000000000..1834585aeb --- /dev/null +++ b/packages/backend/src/server/api/endpoints/notes/drafts/list.ts @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { MiNoteDraft, NoteDraftsRepository } from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; +import { QueryService } from '@/core/QueryService.js'; +import { NoteDraftEntityService } from '@/core/entities/NoteDraftEntityService.js'; + +export const meta = { + tags: ['notes', 'drafts'], + + requireCredential: true, + + prohibitMoved: true, + + kind: 'read:account', + + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + ref: 'NoteDraft', + }, + }, + + errors: { + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 30 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + }, + required: [], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.noteDraftsRepository) + private noteDraftsRepository: NoteDraftsRepository, + + private queryService: QueryService, + private noteDraftEntityService: NoteDraftEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const query = this.queryService.makePaginationQuery(this.noteDraftsRepository.createQueryBuilder('drafts'), ps.sinceId, ps.untilId) + .andWhere('drafts.userId = :meId', { meId: me.id }); + + const drafts = await query + .limit(ps.limit) + .getMany(); + + return await this.noteDraftEntityService.packMany(drafts, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/drafts/update.ts b/packages/backend/src/server/api/endpoints/notes/drafts/update.ts new file mode 100644 index 0000000000..ee221fb765 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/notes/drafts/update.ts @@ -0,0 +1,302 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import ms from 'ms'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { NoteDraftService } from '@/core/NoteDraftService.js'; +import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; +import { NoteDraftEntityService } from '@/core/entities/NoteDraftEntityService.js'; +import { IdentifiableError } from '@/misc/identifiable-error.js'; +import { ApiError } from '../../../error.js'; + +export const meta = { + tags: ['notes', 'drafts'], + + requireCredential: true, + + prohibitMoved: true, + + kind: 'write:account', + + res: { + type: 'object', + optional: false, nullable: false, + properties: { + updatedDraft: { + type: 'object', + optional: false, nullable: false, + ref: 'NoteDraft', + }, + }, + }, + + errors: { + noSuchRenoteTarget: { + message: 'No such renote target.', + code: 'NO_SUCH_RENOTE_TARGET', + id: 'b5c90186-4ab0-49c8-9bba-a1f76c282ba4', + }, + + cannotReRenote: { + message: 'You can not Renote a pure Renote.', + code: 'CANNOT_RENOTE_TO_A_PURE_RENOTE', + id: 'fd4cc33e-2a37-48dd-99cc-9b806eb2031a', + }, + + cannotRenoteDueToVisibility: { + message: 'You can not Renote due to target visibility.', + code: 'CANNOT_RENOTE_DUE_TO_VISIBILITY', + id: 'be9529e9-fe72-4de0-ae43-0b363c4938af', + }, + + noSuchReplyTarget: { + message: 'No such reply target.', + code: 'NO_SUCH_REPLY_TARGET', + id: '749ee0f6-d3da-459a-bf02-282e2da4292c', + }, + + cannotReplyToInvisibleNote: { + message: 'You cannot reply to an invisible Note.', + code: 'CANNOT_REPLY_TO_AN_INVISIBLE_NOTE', + id: 'b98980fa-3780-406c-a935-b6d0eeee10d1', + }, + + cannotReplyToPureRenote: { + message: 'You can not reply to a pure Renote.', + code: 'CANNOT_REPLY_TO_A_PURE_RENOTE', + id: '3ac74a84-8fd5-4bb0-870f-01804f82ce15', + }, + + cannotReplyToSpecifiedNoteWithExtendedVisibility: { + message: 'You cannot reply to a specified visibility note with extended visibility.', + code: 'CANNOT_REPLY_TO_SPECIFIED_NOTE_WITH_EXTENDED_VISIBILITY', + id: 'ed940410-535c-4d5e-bfa3-af798671e93c', + }, + + cannotCreateAlreadyExpiredPoll: { + message: 'Poll is already expired.', + code: 'CANNOT_CREATE_ALREADY_EXPIRED_POLL', + id: '04da457d-b083-4055-9082-955525eda5a5', + }, + + noSuchChannel: { + message: 'No such channel.', + code: 'NO_SUCH_CHANNEL', + id: 'b1653923-5453-4edc-b786-7c4f39bb0bbb', + }, + + youHaveBeenBlocked: { + message: 'You have been blocked by this user.', + code: 'YOU_HAVE_BEEN_BLOCKED', + id: 'b390d7e1-8a5e-46ed-b625-06271cafd3d3', + }, + + noSuchFile: { + message: 'Some files are not found.', + code: 'NO_SUCH_FILE', + id: 'b6992544-63e7-67f0-fa7f-32444b1b5306', + }, + + cannotRenoteOutsideOfChannel: { + message: 'Cannot renote outside of channel.', + code: 'CANNOT_RENOTE_OUTSIDE_OF_CHANNEL', + id: '33510210-8452-094c-6227-4a6c05d99f00', + }, + + containsProhibitedWords: { + message: 'Cannot post because it contains prohibited words.', + code: 'CONTAINS_PROHIBITED_WORDS', + id: 'aa6e01d3-a85c-669d-758a-76aab43af334', + }, + + containsTooManyMentions: { + message: 'Cannot post because it exceeds the allowed number of mentions.', + code: 'CONTAINS_TOO_MANY_MENTIONS', + id: '4de0363a-3046-481b-9b0f-feff3e211025', + }, + + noSuchNoteDraft: { + message: 'No such note draft.', + code: 'NO_SUCH_NOTE_DRAFT', + id: '49cd6b9d-848e-41ee-b0b9-adaca711a6b1', + }, + + accessDenied: { + message: 'Access denied.', + code: 'ACCESS_DENIED', + id: '56f35758-7dd5-468b-8439-5d6fb8ec9b8e', + }, + + noSuchRenote: { + message: 'No such renote.', + code: 'NO_SUCH_RENOTE', + id: '64929870-2540-4d11-af41-3b484d78c956', + }, + + cannotRenote: { + message: 'Cannot renote.', + code: 'CANNOT_RENOTE', + id: '76cc5583-5a14-4ad3-8717-0298507e32db', + }, + + cannotRenoteToExternal: { + message: 'Cannot Renote to External.', + code: 'CANNOT_RENOTE_TO_EXTERNAL', + id: 'ed1952ac-2d26-4957-8b30-2deda76bedf7', + }, + + noSuchReply: { + message: 'No such reply.', + code: 'NO_SUCH_REPLY', + id: 'c4721841-22fc-4bb7-ad3d-897ef1d375b5', + }, + + cannotReplyToSpecifiedVisibilityNoteWithExtendedVisibility: { + message: 'You cannot reply to a specified visibility note with extended visibility.', + code: 'CANNOT_REPLY_TO_SPECIFIED_VISIBILITY_NOTE_WITH_EXTENDED_VISIBILITY', + id: '215dbc76-336c-4d2a-9605-95766ba7dab0', + }, + }, + + limit: { + duration: ms('1hour'), + max: 300, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + draftId: { type: 'string', nullable: false, format: 'misskey:id' }, + visibility: { type: 'string', enum: ['public', 'home', 'followers', 'specified'], default: 'public' }, + visibleUserIds: { type: 'array', uniqueItems: true, items: { + type: 'string', format: 'misskey:id', + } }, + cw: { type: 'string', nullable: true, minLength: 1, maxLength: 100 }, + hashtag: { type: 'string', nullable: true, maxLength: 200 }, + localOnly: { type: 'boolean', default: false }, + reactionAcceptance: { type: 'string', nullable: true, enum: [null, 'likeOnly', 'likeOnlyForRemote', 'nonSensitiveOnly', 'nonSensitiveOnlyForLocalLikeOnlyForRemote'], default: null }, + replyId: { type: 'string', format: 'misskey:id', nullable: true }, + renoteId: { type: 'string', format: 'misskey:id', nullable: true }, + channelId: { type: 'string', format: 'misskey:id', nullable: true }, + + // anyOf内にバリデーションを書いても最初の一つしかチェックされない + // See https://github.com/misskey-dev/misskey/pull/10082 + text: { + type: 'string', + minLength: 0, + maxLength: MAX_NOTE_TEXT_LENGTH, + nullable: true, + }, + fileIds: { + type: 'array', + uniqueItems: true, + minItems: 1, + maxItems: 16, + items: { type: 'string', format: 'misskey:id' }, + }, + poll: { + type: 'object', + nullable: true, + properties: { + choices: { + type: 'array', + uniqueItems: true, + minItems: 0, + maxItems: 10, + items: { type: 'string', minLength: 1, maxLength: 50 }, + }, + multiple: { type: 'boolean' }, + expiresAt: { type: 'integer', nullable: true }, + expiredAfter: { type: 'integer', nullable: true, minimum: 1 }, + }, + required: ['choices'], + }, + }, + required: ['draftId'], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + private noteDraftService: NoteDraftService, + private noteDraftEntityService: NoteDraftEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const draft = await this.noteDraftService.update(me, ps.draftId, { + fileIds: ps.fileIds, + poll: ps.poll ? { + choices: ps.poll.choices, + multiple: ps.poll.multiple ?? false, + expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null, + expiredAfter: ps.poll.expiredAfter ?? null, + } : undefined, + text: ps.text ?? null, + replyId: ps.replyId ?? undefined, + renoteId: ps.renoteId ?? undefined, + cw: ps.cw ?? null, + ...(ps.hashtag ? { hashtag: ps.hashtag } : {}), + localOnly: ps.localOnly, + reactionAcceptance: ps.reactionAcceptance, + visibility: ps.visibility, + visibleUserIds: ps.visibleUserIds ?? [], + channelId: ps.channelId ?? undefined, + }).catch((err) => { + if (err instanceof IdentifiableError) { + switch (err.id) { + case '49cd6b9d-848e-41ee-b0b9-adaca711a6b1': + throw new ApiError(meta.errors.noSuchNoteDraft); + case '04da457d-b083-4055-9082-955525eda5a5': + throw new ApiError(meta.errors.cannotCreateAlreadyExpiredPoll); + case 'b6992544-63e7-67f0-fa7f-32444b1b5306': + throw new ApiError(meta.errors.noSuchFile); + case '64929870-2540-4d11-af41-3b484d78c956': + throw new ApiError(meta.errors.noSuchRenote); + case '76cc5583-5a14-4ad3-8717-0298507e32db': + throw new ApiError(meta.errors.cannotRenote); + case '075ca298-e6e7-485a-b570-51a128bb5168': + throw new ApiError(meta.errors.youHaveBeenBlocked); + case '81eb8188-aea1-4e35-9a8f-3334a3be9855': + throw new ApiError(meta.errors.cannotRenoteDueToVisibility); + case '6815399a-6f13-4069-b60d-ed5156249d12': + throw new ApiError(meta.errors.noSuchChannel); + case 'ed1952ac-2d26-4957-8b30-2deda76bedf7': + throw new ApiError(meta.errors.cannotRenoteToExternal); + case 'c4721841-22fc-4bb7-ad3d-897ef1d375b5': + throw new ApiError(meta.errors.noSuchReply); + case 'e6c10b57-2c09-4da3-bd4d-eda05d51d140': + throw new ApiError(meta.errors.cannotReplyToPureRenote); + case '593c323c-6b6a-4501-a25c-2f36bd2a93d6': + throw new ApiError(meta.errors.cannotReplyToInvisibleNote); + case '215dbc76-336c-4d2a-9605-95766ba7dab0': + throw new ApiError(meta.errors.cannotReplyToSpecifiedNoteWithExtendedVisibility); + case 'b5c90186-4ab0-49c8-9bba-a1f76c282ba4': + throw new ApiError(meta.errors.noSuchRenoteTarget); + case 'fd4cc33e-2a37-48dd-99cc-9b806eb2031a': + throw new ApiError(meta.errors.cannotReRenote); + case '749ee0f6-d3da-459a-bf02-282e2da4292c': + throw new ApiError(meta.errors.noSuchReplyTarget); + case '33510210-8452-094c-6227-4a6c05d99f00': + throw new ApiError(meta.errors.cannotRenoteOutsideOfChannel); + case 'aa6e01d3-a85c-669d-758a-76aab43af334': + throw new ApiError(meta.errors.containsProhibitedWords); + case '4de0363a-3046-481b-9b0f-feff3e211025': + throw new ApiError(meta.errors.containsTooManyMentions); + default: + throw err; + } + } + throw err; + }); + + const updatedDraft = await this.noteDraftEntityService.pack(draft, me); + + return { + updatedDraft, + }; + }); + } +} -- cgit v1.2.3-freya From b8e8f3ad25fafbe0567e710eddfcced04deb03ad Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sat, 28 Jun 2025 20:21:21 +0900 Subject: enhance: ページネーション(一覧表示)の基準日時を指定できるように sinceId/untilIdが指定可能なエンドポイントにおいて、sinceDate/untilDateも指定可能に MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 +- .../api/endpoints/admin/abuse-user-reports.ts | 4 +- .../src/server/api/endpoints/admin/ad/list.ts | 4 +- .../api/endpoints/admin/announcements/list.ts | 4 +- .../api/endpoints/admin/avatar-decorations/list.ts | 2 + .../src/server/api/endpoints/admin/drive/files.ts | 4 +- .../api/endpoints/admin/emoji/list-remote.ts | 4 +- .../src/server/api/endpoints/admin/emoji/list.ts | 4 +- .../src/server/api/endpoints/admin/roles/users.ts | 4 +- .../api/endpoints/admin/show-moderation-logs.ts | 4 +- .../src/server/api/endpoints/announcements.ts | 4 +- .../src/server/api/endpoints/blocking/list.ts | 4 +- .../src/server/api/endpoints/channels/followed.ts | 6 +- .../src/server/api/endpoints/channels/owned.ts | 4 +- .../src/server/api/endpoints/channels/search.ts | 4 +- .../api/endpoints/chat/messages/room-timeline.ts | 9 +- .../api/endpoints/chat/messages/user-timeline.ts | 9 +- .../api/endpoints/chat/rooms/invitations/inbox.ts | 9 +- .../api/endpoints/chat/rooms/invitations/outbox.ts | 9 +- .../src/server/api/endpoints/chat/rooms/joining.ts | 9 +- .../src/server/api/endpoints/chat/rooms/members.ts | 9 +- .../src/server/api/endpoints/chat/rooms/owned.ts | 9 +- .../src/server/api/endpoints/clips/notes.ts | 4 +- .../src/server/api/endpoints/drive/files.ts | 4 +- .../api/endpoints/drive/files/attached-notes.ts | 6 +- .../src/server/api/endpoints/drive/folders.ts | 4 +- .../src/server/api/endpoints/drive/stream.ts | 4 +- .../server/api/endpoints/federation/followers.ts | 4 +- .../server/api/endpoints/federation/following.ts | 4 +- .../src/server/api/endpoints/federation/users.ts | 4 +- .../src/server/api/endpoints/flash/my-likes.ts | 4 +- .../backend/src/server/api/endpoints/flash/my.ts | 4 +- .../api/endpoints/following/requests/list.ts | 4 +- .../api/endpoints/following/requests/sent.ts | 4 +- .../src/server/api/endpoints/gallery/posts.ts | 4 +- .../src/server/api/endpoints/i/favorites.ts | 4 +- .../src/server/api/endpoints/i/gallery/likes.ts | 4 +- .../src/server/api/endpoints/i/gallery/posts.ts | 4 +- .../api/endpoints/i/notifications-grouped.ts | 11 +- .../src/server/api/endpoints/i/notifications.ts | 15 ++- .../src/server/api/endpoints/i/page-likes.ts | 4 +- .../backend/src/server/api/endpoints/i/pages.ts | 4 +- .../src/server/api/endpoints/i/signin-history.ts | 4 +- .../src/server/api/endpoints/invite/list.ts | 4 +- .../backend/src/server/api/endpoints/mute/list.ts | 4 +- packages/backend/src/server/api/endpoints/notes.ts | 4 +- .../src/server/api/endpoints/notes/children.ts | 4 +- .../src/server/api/endpoints/notes/drafts/list.ts | 4 +- .../src/server/api/endpoints/notes/mentions.ts | 4 +- .../src/server/api/endpoints/notes/reactions.ts | 4 +- .../src/server/api/endpoints/notes/renotes.ts | 4 +- .../src/server/api/endpoints/notes/replies.ts | 4 +- .../server/api/endpoints/notes/search-by-tag.ts | 4 +- .../src/server/api/endpoints/notes/search.ts | 11 +- .../src/server/api/endpoints/renote-mute/list.ts | 4 +- .../src/server/api/endpoints/reversi/games.ts | 4 +- .../src/server/api/endpoints/roles/users.ts | 4 +- .../src/server/api/endpoints/users/clips.ts | 4 +- .../src/server/api/endpoints/users/flashs.ts | 7 +- .../src/server/api/endpoints/users/followers.ts | 4 +- .../src/server/api/endpoints/users/following.ts | 4 +- .../server/api/endpoints/users/gallery/posts.ts | 4 +- .../api/endpoints/users/lists/get-memberships.ts | 4 +- .../src/server/api/endpoints/users/pages.ts | 4 +- .../server/api/endpoints/v2/admin/emoji/list.ts | 11 +- packages/frontend/src/components/MkButton.vue | 9 +- packages/frontend/src/components/MkPagination.vue | 43 +++++-- .../frontend/src/composables/use-pagination.ts | 13 ++- packages/misskey-js/src/autogen/types.ts | 128 +++++++++++++++++++++ 69 files changed, 429 insertions(+), 96 deletions(-) (limited to 'packages/backend/src/server/api/endpoints/notes') diff --git a/CHANGELOG.md b/CHANGELOG.md index 96de37cc36..71b92e7193 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,12 @@ ### Client - Enhance: 設定の自動バックアップをオンにした直後に自動バックアップするように - Enhance: ファイルアップロード前にキャプション設定を行えるように -- Enhance: ページネーションの並び順を逆にできるように +- Enhance: ページネーション(一覧表示)の並び順を逆にできるように +- Enhance: ページネーション(一覧表示)の基準日時を指定できるように - Fix: ファイルがドライブの既定アップロード先に指定したフォルダにアップロードされない問題を修正 ### Server +- Enhance: sinceId/untilIdが指定可能なエンドポイントにおいて、sinceDate/untilDateも指定可能に - Fix: ジョブキューのProgressの値を正しく計算する diff --git a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts index 0dbfaae054..ff7133e73a 100644 --- a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts +++ b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts @@ -98,6 +98,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, state: { type: 'string', nullable: true, default: null }, reporterOrigin: { type: 'string', enum: ['combined', 'local', 'remote'], default: 'combined' }, targetUserOrigin: { type: 'string', enum: ['combined', 'local', 'remote'], default: 'combined' }, @@ -115,7 +117,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.abuseUserReportsRepository.createQueryBuilder('report'), ps.sinceId, ps.untilId); + const query = this.queryService.makePaginationQuery(this.abuseUserReportsRepository.createQueryBuilder('report'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate); switch (ps.state) { case 'resolved': query.andWhere('report.resolved = TRUE'); break; diff --git a/packages/backend/src/server/api/endpoints/admin/ad/list.ts b/packages/backend/src/server/api/endpoints/admin/ad/list.ts index 6406709cda..4f897d98e4 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/list.ts @@ -34,6 +34,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, publishing: { type: 'boolean', default: null, nullable: true }, }, required: [], @@ -48,7 +50,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.adsRepository.createQueryBuilder('ad'), ps.sinceId, ps.untilId); + const query = this.queryService.makePaginationQuery(this.adsRepository.createQueryBuilder('ad'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate); if (ps.publishing === true) { query.andWhere('ad.expiresAt > :now', { now: new Date() }).andWhere('ad.startsAt <= :now', { now: new Date() }); } else if (ps.publishing === false) { diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts index 7596bf44e3..81a788de2b 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts @@ -68,6 +68,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, userId: { type: 'string', format: 'misskey:id', nullable: true }, status: { type: 'string', enum: ['all', 'active', 'archived'], default: 'active' }, }, @@ -87,7 +89,7 @@ export default class extends Endpoint { // eslint- private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.announcementsRepository.createQueryBuilder('announcement'), ps.sinceId, ps.untilId); + const query = this.queryService.makePaginationQuery(this.announcementsRepository.createQueryBuilder('announcement'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate); if (ps.status === 'archived') { query.andWhere('announcement.isActive = false'); diff --git a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts index d4d9a7235b..765bfd6766 100644 --- a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts @@ -71,6 +71,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, userId: { type: 'string', format: 'misskey:id', nullable: true }, }, required: [], diff --git a/packages/backend/src/server/api/endpoints/admin/drive/files.ts b/packages/backend/src/server/api/endpoints/admin/drive/files.ts index 915d777e77..59b02482a2 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/files.ts @@ -34,6 +34,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, userId: { type: 'string', format: 'misskey:id', nullable: true }, type: { type: 'string', nullable: true, pattern: /^[a-zA-Z0-9\/\-*]+$/.toString().slice(1, -1) }, origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: 'local' }, @@ -57,7 +59,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.driveFilesRepository.createQueryBuilder('file'), ps.sinceId, ps.untilId); + const query = this.queryService.makePaginationQuery(this.driveFilesRepository.createQueryBuilder('file'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate); if (ps.userId) { query.andWhere('file.userId = :userId', { userId: ps.userId }); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts index b44007962d..660aa55bf8 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts @@ -74,6 +74,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: [], } as const; @@ -89,7 +91,7 @@ export default class extends Endpoint { // eslint- private emojiEntityService: EmojiEntityService, ) { super(meta, paramDef, async (ps, me) => { - const q = this.queryService.makePaginationQuery(this.emojisRepository.createQueryBuilder('emoji'), ps.sinceId, ps.untilId); + const q = this.queryService.makePaginationQuery(this.emojisRepository.createQueryBuilder('emoji'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate); if (ps.host == null) { q.andWhere('emoji.host IS NOT NULL'); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts index 4342e178cc..34d200455e 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts @@ -68,6 +68,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: [], } as const; @@ -82,7 +84,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const q = this.queryService.makePaginationQuery(this.emojisRepository.createQueryBuilder('emoji'), ps.sinceId, ps.untilId) + const q = this.queryService.makePaginationQuery(this.emojisRepository.createQueryBuilder('emoji'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('emoji.host IS NULL'); let emojis: MiEmoji[]; diff --git a/packages/backend/src/server/api/endpoints/admin/roles/users.ts b/packages/backend/src/server/api/endpoints/admin/roles/users.ts index 198166bec2..63a8d513fd 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/users.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/users.ts @@ -49,6 +49,8 @@ export const paramDef = { roleId: { type: 'string', format: 'misskey:id' }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, }, required: ['roleId'], @@ -76,7 +78,7 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.noSuchRole); } - const query = this.queryService.makePaginationQuery(this.roleAssignmentsRepository.createQueryBuilder('assign'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.roleAssignmentsRepository.createQueryBuilder('assign'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('assign.roleId = :roleId', { roleId: role.id }) .andWhere(new Brackets(qb => { qb diff --git a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts index 58c5f1f60a..e8ffd9cd9f 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts @@ -63,6 +63,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, type: { type: 'string', nullable: true }, userId: { type: 'string', format: 'misskey:id', nullable: true }, }, @@ -79,7 +81,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.moderationLogsRepository.createQueryBuilder('report'), ps.sinceId, ps.untilId); + const query = this.queryService.makePaginationQuery(this.moderationLogsRepository.createQueryBuilder('report'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate); if (ps.type != null) { query.andWhere('report.type = :type', { type: ps.type }); diff --git a/packages/backend/src/server/api/endpoints/announcements.ts b/packages/backend/src/server/api/endpoints/announcements.ts index ff8dd73605..2ad1702f72 100644 --- a/packages/backend/src/server/api/endpoints/announcements.ts +++ b/packages/backend/src/server/api/endpoints/announcements.ts @@ -33,6 +33,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, isActive: { type: 'boolean', default: true }, }, required: [], @@ -48,7 +50,7 @@ export default class extends Endpoint { // eslint- private announcementEntityService: AnnouncementEntityService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.announcementsRepository.createQueryBuilder('announcement'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.announcementsRepository.createQueryBuilder('announcement'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('announcement.isActive = :isActive', { isActive: ps.isActive }) .andWhere(new Brackets(qb => { if (me) qb.orWhere('announcement.userId = :meId', { meId: me.id }); diff --git a/packages/backend/src/server/api/endpoints/blocking/list.ts b/packages/backend/src/server/api/endpoints/blocking/list.ts index 8431fa6b34..9885d32eda 100644 --- a/packages/backend/src/server/api/endpoints/blocking/list.ts +++ b/packages/backend/src/server/api/endpoints/blocking/list.ts @@ -34,6 +34,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 30 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: [], } as const; @@ -48,7 +50,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.blockingsRepository.createQueryBuilder('blocking'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.blockingsRepository.createQueryBuilder('blocking'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('blocking.blockerId = :meId', { meId: me.id }); const blockings = await query diff --git a/packages/backend/src/server/api/endpoints/channels/followed.ts b/packages/backend/src/server/api/endpoints/channels/followed.ts index 294b5e4bc4..faceb03631 100644 --- a/packages/backend/src/server/api/endpoints/channels/followed.ts +++ b/packages/backend/src/server/api/endpoints/channels/followed.ts @@ -33,6 +33,8 @@ export const paramDef = { properties: { sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 5 }, }, required: [], @@ -53,8 +55,8 @@ export default class extends Endpoint { // eslint- this.channelFollowingsRepository.createQueryBuilder(), ps.sinceId, ps.untilId, - null, - null, + ps.sinceDate, + ps.untilDate, 'followeeId', ) .andWhere({ followerId: me.id }); diff --git a/packages/backend/src/server/api/endpoints/channels/owned.ts b/packages/backend/src/server/api/endpoints/channels/owned.ts index daab685f1b..d22ac18b4b 100644 --- a/packages/backend/src/server/api/endpoints/channels/owned.ts +++ b/packages/backend/src/server/api/endpoints/channels/owned.ts @@ -33,6 +33,8 @@ export const paramDef = { properties: { sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 5 }, }, required: [], @@ -48,7 +50,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.channelsRepository.createQueryBuilder('channel'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.channelsRepository.createQueryBuilder('channel'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('channel.isArchived = FALSE') .andWhere({ userId: me.id }); diff --git a/packages/backend/src/server/api/endpoints/channels/search.ts b/packages/backend/src/server/api/endpoints/channels/search.ts index ae32203603..7b6c4db91c 100644 --- a/packages/backend/src/server/api/endpoints/channels/search.ts +++ b/packages/backend/src/server/api/endpoints/channels/search.ts @@ -35,6 +35,8 @@ export const paramDef = { type: { type: 'string', enum: ['nameAndDescription', 'nameOnly'], default: 'nameAndDescription' }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 5 }, }, required: ['query'], @@ -50,7 +52,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.channelsRepository.createQueryBuilder('channel'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.channelsRepository.createQueryBuilder('channel'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('channel.isArchived = FALSE'); if (ps.query !== '') { diff --git a/packages/backend/src/server/api/endpoints/chat/messages/room-timeline.ts b/packages/backend/src/server/api/endpoints/chat/messages/room-timeline.ts index c0e344b889..dd598b5628 100644 --- a/packages/backend/src/server/api/endpoints/chat/messages/room-timeline.ts +++ b/packages/backend/src/server/api/endpoints/chat/messages/room-timeline.ts @@ -9,6 +9,7 @@ import { DI } from '@/di-symbols.js'; import { ChatService } from '@/core/ChatService.js'; import { ChatEntityService } from '@/core/entities/ChatEntityService.js'; import { ApiError } from '@/server/api/error.js'; +import { IdService } from '@/core/IdService.js'; export const meta = { tags: ['chat'], @@ -42,6 +43,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, roomId: { type: 'string', format: 'misskey:id' }, }, required: ['roomId'], @@ -52,8 +55,12 @@ export default class extends Endpoint { // eslint- constructor( private chatEntityService: ChatEntityService, private chatService: ChatService, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : null); + await this.chatService.checkChatAvailability(me.id, 'read'); const room = await this.chatService.findRoomById(ps.roomId); @@ -65,7 +72,7 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.noSuchRoom); } - const messages = await this.chatService.roomTimeline(room.id, ps.limit, ps.sinceId, ps.untilId); + const messages = await this.chatService.roomTimeline(room.id, ps.limit, sinceId, untilId); this.chatService.readRoomChatMessage(me.id, room.id); diff --git a/packages/backend/src/server/api/endpoints/chat/messages/user-timeline.ts b/packages/backend/src/server/api/endpoints/chat/messages/user-timeline.ts index a057e2e088..52a13b2178 100644 --- a/packages/backend/src/server/api/endpoints/chat/messages/user-timeline.ts +++ b/packages/backend/src/server/api/endpoints/chat/messages/user-timeline.ts @@ -10,6 +10,7 @@ import { GetterService } from '@/server/api/GetterService.js'; import { ChatService } from '@/core/ChatService.js'; import { ChatEntityService } from '@/core/entities/ChatEntityService.js'; import { ApiError } from '@/server/api/error.js'; +import { IdService } from '@/core/IdService.js'; export const meta = { tags: ['chat'], @@ -43,6 +44,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, userId: { type: 'string', format: 'misskey:id' }, }, required: ['userId'], @@ -54,8 +57,12 @@ export default class extends Endpoint { // eslint- private chatEntityService: ChatEntityService, private chatService: ChatService, private getterService: GetterService, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : null); + await this.chatService.checkChatAvailability(me.id, 'read'); const other = await this.getterService.getUser(ps.userId).catch(err => { @@ -63,7 +70,7 @@ export default class extends Endpoint { // eslint- throw err; }); - const messages = await this.chatService.userTimeline(me.id, other.id, ps.limit, ps.sinceId, ps.untilId); + const messages = await this.chatService.userTimeline(me.id, other.id, ps.limit, sinceId, untilId); this.chatService.readUserChatMessage(me.id, other.id); diff --git a/packages/backend/src/server/api/endpoints/chat/rooms/invitations/inbox.ts b/packages/backend/src/server/api/endpoints/chat/rooms/invitations/inbox.ts index 8a02d1c704..f4f91a7d8f 100644 --- a/packages/backend/src/server/api/endpoints/chat/rooms/invitations/inbox.ts +++ b/packages/backend/src/server/api/endpoints/chat/rooms/invitations/inbox.ts @@ -9,6 +9,7 @@ import { DI } from '@/di-symbols.js'; import { ChatService } from '@/core/ChatService.js'; import { ChatEntityService } from '@/core/entities/ChatEntityService.js'; import { ApiError } from '@/server/api/error.js'; +import { IdService } from '@/core/IdService.js'; export const meta = { tags: ['chat'], @@ -37,6 +38,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 30 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, } as const; @@ -45,11 +48,15 @@ export default class extends Endpoint { // eslint- constructor( private chatEntityService: ChatEntityService, private chatService: ChatService, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : null); + await this.chatService.checkChatAvailability(me.id, 'read'); - const invitations = await this.chatService.getReceivedRoomInvitationsWithPagination(me.id, ps.limit, ps.sinceId, ps.untilId); + const invitations = await this.chatService.getReceivedRoomInvitationsWithPagination(me.id, ps.limit, sinceId, untilId); return this.chatEntityService.packRoomInvitations(invitations, me); }); } diff --git a/packages/backend/src/server/api/endpoints/chat/rooms/invitations/outbox.ts b/packages/backend/src/server/api/endpoints/chat/rooms/invitations/outbox.ts index 0702ba086c..827bef731c 100644 --- a/packages/backend/src/server/api/endpoints/chat/rooms/invitations/outbox.ts +++ b/packages/backend/src/server/api/endpoints/chat/rooms/invitations/outbox.ts @@ -10,6 +10,7 @@ import { DI } from '@/di-symbols.js'; import { ApiError } from '@/server/api/error.js'; import { ChatService } from '@/core/ChatService.js'; import { ChatEntityService } from '@/core/entities/ChatEntityService.js'; +import { IdService } from '@/core/IdService.js'; export const meta = { tags: ['chat'], @@ -44,6 +45,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 30 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: ['roomId'], } as const; @@ -53,8 +56,12 @@ export default class extends Endpoint { // eslint- constructor( private chatService: ChatService, private chatEntityService: ChatEntityService, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : null); + await this.chatService.checkChatAvailability(me.id, 'read'); const room = await this.chatService.findMyRoomById(me.id, ps.roomId); @@ -62,7 +69,7 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.noSuchRoom); } - const invitations = await this.chatService.getSentRoomInvitationsWithPagination(ps.roomId, ps.limit, ps.sinceId, ps.untilId); + const invitations = await this.chatService.getSentRoomInvitationsWithPagination(ps.roomId, ps.limit, sinceId, untilId); return this.chatEntityService.packRoomInvitations(invitations, me); }); } diff --git a/packages/backend/src/server/api/endpoints/chat/rooms/joining.ts b/packages/backend/src/server/api/endpoints/chat/rooms/joining.ts index ba9242c762..b061da6d9c 100644 --- a/packages/backend/src/server/api/endpoints/chat/rooms/joining.ts +++ b/packages/backend/src/server/api/endpoints/chat/rooms/joining.ts @@ -9,6 +9,7 @@ import { DI } from '@/di-symbols.js'; import { ChatService } from '@/core/ChatService.js'; import { ChatEntityService } from '@/core/entities/ChatEntityService.js'; import { ApiError } from '@/server/api/error.js'; +import { IdService } from '@/core/IdService.js'; export const meta = { tags: ['chat'], @@ -37,6 +38,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 30 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, } as const; @@ -45,11 +48,15 @@ export default class extends Endpoint { // eslint- constructor( private chatService: ChatService, private chatEntityService: ChatEntityService, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : null); + await this.chatService.checkChatAvailability(me.id, 'read'); - const memberships = await this.chatService.getMyMemberships(me.id, ps.limit, ps.sinceId, ps.untilId); + const memberships = await this.chatService.getMyMemberships(me.id, ps.limit, sinceId, untilId); return this.chatEntityService.packRoomMemberships(memberships, me, { populateUser: false, diff --git a/packages/backend/src/server/api/endpoints/chat/rooms/members.ts b/packages/backend/src/server/api/endpoints/chat/rooms/members.ts index f5ffa21d32..aa3fb5168d 100644 --- a/packages/backend/src/server/api/endpoints/chat/rooms/members.ts +++ b/packages/backend/src/server/api/endpoints/chat/rooms/members.ts @@ -9,6 +9,7 @@ import { DI } from '@/di-symbols.js'; import { ChatService } from '@/core/ChatService.js'; import { ApiError } from '@/server/api/error.js'; import { ChatEntityService } from '@/core/entities/ChatEntityService.js'; +import { IdService } from '@/core/IdService.js'; export const meta = { tags: ['chat'], @@ -43,6 +44,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 30 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: ['roomId'], } as const; @@ -52,8 +55,12 @@ export default class extends Endpoint { // eslint- constructor( private chatService: ChatService, private chatEntityService: ChatEntityService, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : null); + await this.chatService.checkChatAvailability(me.id, 'read'); const room = await this.chatService.findRoomById(ps.roomId); @@ -65,7 +72,7 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.noSuchRoom); } - const memberships = await this.chatService.getRoomMembershipsWithPagination(room.id, ps.limit, ps.sinceId, ps.untilId); + const memberships = await this.chatService.getRoomMembershipsWithPagination(room.id, ps.limit, sinceId, untilId); return this.chatEntityService.packRoomMemberships(memberships, me, { populateUser: true, diff --git a/packages/backend/src/server/api/endpoints/chat/rooms/owned.ts b/packages/backend/src/server/api/endpoints/chat/rooms/owned.ts index accf7e1bee..40c954fcca 100644 --- a/packages/backend/src/server/api/endpoints/chat/rooms/owned.ts +++ b/packages/backend/src/server/api/endpoints/chat/rooms/owned.ts @@ -9,6 +9,7 @@ import { DI } from '@/di-symbols.js'; import { ChatService } from '@/core/ChatService.js'; import { ChatEntityService } from '@/core/entities/ChatEntityService.js'; import { ApiError } from '@/server/api/error.js'; +import { IdService } from '@/core/IdService.js'; export const meta = { tags: ['chat'], @@ -37,6 +38,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 30 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, } as const; @@ -45,11 +48,15 @@ export default class extends Endpoint { // eslint- constructor( private chatEntityService: ChatEntityService, private chatService: ChatService, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : null); + await this.chatService.checkChatAvailability(me.id, 'read'); - const rooms = await this.chatService.getOwnedRoomsWithPagination(me.id, ps.limit, ps.sinceId, ps.untilId); + const rooms = await this.chatService.getOwnedRoomsWithPagination(me.id, ps.limit, sinceId, untilId); return this.chatEntityService.packRooms(rooms, me); }); } diff --git a/packages/backend/src/server/api/endpoints/clips/notes.ts b/packages/backend/src/server/api/endpoints/clips/notes.ts index 4869ffd402..19302b1f8d 100644 --- a/packages/backend/src/server/api/endpoints/clips/notes.ts +++ b/packages/backend/src/server/api/endpoints/clips/notes.ts @@ -44,6 +44,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: ['clipId'], } as const; @@ -76,7 +78,7 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.noSuchClip); } - const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .innerJoin(this.clipNotesRepository.metadata.targetName, 'clipNote', 'clipNote.noteId = note.id') .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') diff --git a/packages/backend/src/server/api/endpoints/drive/files.ts b/packages/backend/src/server/api/endpoints/drive/files.ts index 10c521332d..303a4d4925 100644 --- a/packages/backend/src/server/api/endpoints/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/drive/files.ts @@ -34,6 +34,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, folderId: { type: 'string', format: 'misskey:id', nullable: true, default: null }, type: { type: 'string', nullable: true, pattern: /^[a-zA-Z\/\-*]+$/.toString().slice(1, -1) }, sort: { type: 'string', nullable: true, enum: ['+createdAt', '-createdAt', '+name', '-name', '+size', '-size', null] }, @@ -51,7 +53,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.driveFilesRepository.createQueryBuilder('file'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.driveFilesRepository.createQueryBuilder('file'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('file.userId = :userId', { userId: me.id }); if (ps.folderId) { diff --git a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts index b86059b5e7..6bc8730a1e 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts @@ -9,8 +9,8 @@ import type { NotesRepository, DriveFilesRepository } from '@/models/_.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; -import { ApiError } from '../../../error.js'; import { RoleService } from '@/core/RoleService.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['drive', 'notes'], @@ -45,6 +45,8 @@ export const paramDef = { properties: { sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, fileId: { type: 'string', format: 'misskey:id' }, }, @@ -75,7 +77,7 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.noSuchFile); } - const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId); + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate); query.andWhere(':file <@ note.fileIds', { file: [file.id] }); const notes = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/drive/folders.ts b/packages/backend/src/server/api/endpoints/drive/folders.ts index 8c4848f8e1..23b3db60a8 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders.ts @@ -34,6 +34,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, folderId: { type: 'string', format: 'misskey:id', nullable: true, default: null }, }, required: [], @@ -49,7 +51,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.driveFoldersRepository.createQueryBuilder('folder'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.driveFoldersRepository.createQueryBuilder('folder'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('folder.userId = :userId', { userId: me.id }); if (ps.folderId) { diff --git a/packages/backend/src/server/api/endpoints/drive/stream.ts b/packages/backend/src/server/api/endpoints/drive/stream.ts index f7c1ed39b5..8bf83a9653 100644 --- a/packages/backend/src/server/api/endpoints/drive/stream.ts +++ b/packages/backend/src/server/api/endpoints/drive/stream.ts @@ -34,6 +34,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, type: { type: 'string', pattern: /^[a-zA-Z\/\-*]+$/.toString().slice(1, -1) }, }, required: [], @@ -49,7 +51,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.driveFilesRepository.createQueryBuilder('file'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.driveFilesRepository.createQueryBuilder('file'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('file.userId = :userId', { userId: me.id }); if (ps.type) { diff --git a/packages/backend/src/server/api/endpoints/federation/followers.ts b/packages/backend/src/server/api/endpoints/federation/followers.ts index ce4dd13067..296bc7c5a8 100644 --- a/packages/backend/src/server/api/endpoints/federation/followers.ts +++ b/packages/backend/src/server/api/endpoints/federation/followers.ts @@ -32,6 +32,8 @@ export const paramDef = { host: { type: 'string' }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, }, required: ['host'], @@ -47,7 +49,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.followingsRepository.createQueryBuilder('following'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.followingsRepository.createQueryBuilder('following'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('following.followeeHost = :host', { host: ps.host }); const followings = await query diff --git a/packages/backend/src/server/api/endpoints/federation/following.ts b/packages/backend/src/server/api/endpoints/federation/following.ts index 1a793889c7..091bf442af 100644 --- a/packages/backend/src/server/api/endpoints/federation/following.ts +++ b/packages/backend/src/server/api/endpoints/federation/following.ts @@ -32,6 +32,8 @@ export const paramDef = { host: { type: 'string' }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, }, required: ['host'], @@ -47,7 +49,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.followingsRepository.createQueryBuilder('following'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.followingsRepository.createQueryBuilder('following'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('following.followerHost = :host', { host: ps.host }); const followings = await query diff --git a/packages/backend/src/server/api/endpoints/federation/users.ts b/packages/backend/src/server/api/endpoints/federation/users.ts index 71b1aeb07b..c2d660f163 100644 --- a/packages/backend/src/server/api/endpoints/federation/users.ts +++ b/packages/backend/src/server/api/endpoints/federation/users.ts @@ -32,6 +32,8 @@ export const paramDef = { host: { type: 'string' }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, }, required: ['host'], @@ -47,7 +49,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.usersRepository.createQueryBuilder('user'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.usersRepository.createQueryBuilder('user'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('user.host = :host', { host: ps.host }); const users = await query diff --git a/packages/backend/src/server/api/endpoints/flash/my-likes.ts b/packages/backend/src/server/api/endpoints/flash/my-likes.ts index 755cc5acfc..c1a197214c 100644 --- a/packages/backend/src/server/api/endpoints/flash/my-likes.ts +++ b/packages/backend/src/server/api/endpoints/flash/my-likes.ts @@ -44,6 +44,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: [], } as const; @@ -58,7 +60,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.flashLikesRepository.createQueryBuilder('like'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.flashLikesRepository.createQueryBuilder('like'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('like.userId = :meId', { meId: me.id }) .leftJoinAndSelect('like.flash', 'flash'); diff --git a/packages/backend/src/server/api/endpoints/flash/my.ts b/packages/backend/src/server/api/endpoints/flash/my.ts index 5746096232..6ec623b719 100644 --- a/packages/backend/src/server/api/endpoints/flash/my.ts +++ b/packages/backend/src/server/api/endpoints/flash/my.ts @@ -34,6 +34,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: [], } as const; @@ -48,7 +50,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.flashsRepository.createQueryBuilder('flash'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.flashsRepository.createQueryBuilder('flash'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('flash.userId = :meId', { meId: me.id }); const flashs = await query diff --git a/packages/backend/src/server/api/endpoints/following/requests/list.ts b/packages/backend/src/server/api/endpoints/following/requests/list.ts index fa59e38976..cf614e433d 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/list.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/list.ts @@ -49,6 +49,8 @@ export const paramDef = { properties: { sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, }, required: [], @@ -64,7 +66,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.followRequestsRepository.createQueryBuilder('request'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.followRequestsRepository.createQueryBuilder('request'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('request.followeeId = :meId', { meId: me.id }); const requests = await query diff --git a/packages/backend/src/server/api/endpoints/following/requests/sent.ts b/packages/backend/src/server/api/endpoints/following/requests/sent.ts index 6325f01bb8..2b3cc35f03 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/sent.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/sent.ts @@ -49,6 +49,8 @@ export const paramDef = { properties: { sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, }, required: [], @@ -64,7 +66,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.followRequestsRepository.createQueryBuilder('request'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.followRequestsRepository.createQueryBuilder('request'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('request.followerId = :meId', { meId: me.id }); const requests = await query diff --git a/packages/backend/src/server/api/endpoints/gallery/posts.ts b/packages/backend/src/server/api/endpoints/gallery/posts.ts index d398418ab4..846b903bdb 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts.ts @@ -30,6 +30,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: [], } as const; @@ -44,7 +46,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.galleryPostsRepository.createQueryBuilder('post'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.galleryPostsRepository.createQueryBuilder('post'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .innerJoinAndSelect('post.user', 'user'); const posts = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/i/favorites.ts b/packages/backend/src/server/api/endpoints/i/favorites.ts index 3558035eca..00277282ba 100644 --- a/packages/backend/src/server/api/endpoints/i/favorites.ts +++ b/packages/backend/src/server/api/endpoints/i/favorites.ts @@ -34,6 +34,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: [], } as const; @@ -48,7 +50,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.noteFavoritesRepository.createQueryBuilder('favorite'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.noteFavoritesRepository.createQueryBuilder('favorite'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('favorite.userId = :meId', { meId: me.id }) .leftJoinAndSelect('favorite.note', 'note'); diff --git a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts b/packages/backend/src/server/api/endpoints/i/gallery/likes.ts index d492585ffa..9913ed7489 100644 --- a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts +++ b/packages/backend/src/server/api/endpoints/i/gallery/likes.ts @@ -45,6 +45,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: [], } as const; @@ -59,7 +61,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.galleryLikesRepository.createQueryBuilder('like'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.galleryLikesRepository.createQueryBuilder('like'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('like.userId = :meId', { meId: me.id }) .leftJoinAndSelect('like.post', 'post'); diff --git a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts b/packages/backend/src/server/api/endpoints/i/gallery/posts.ts index 73a6fcc98b..c9d17fc35c 100644 --- a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/i/gallery/posts.ts @@ -34,6 +34,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: [], } as const; @@ -48,7 +50,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.galleryPostsRepository.createQueryBuilder('post'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.galleryPostsRepository.createQueryBuilder('post'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('post.userId = :meId', { meId: me.id }); const posts = await query diff --git a/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts b/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts index b9c41b057d..f933eaab00 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts @@ -49,6 +49,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, markAsRead: { type: 'boolean', default: true }, // 後方互換のため、廃止された通知タイプも受け付ける includeTypes: { type: 'array', items: { @@ -64,15 +66,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.redis) - private redisClient: Redis.Redis, - private idService: IdService, private notificationEntityService: NotificationEntityService, private notificationService: NotificationService, ) { super(meta, paramDef, async (ps, me) => { const EXTRA_LIMIT = 100; + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : undefined); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : undefined); // includeTypes が空の場合はクエリしない if (ps.includeTypes && ps.includeTypes.length === 0) { @@ -87,8 +88,8 @@ export default class extends Endpoint { // eslint- const excludeTypes = ps.excludeTypes && ps.excludeTypes.filter(type => !(obsoleteNotificationTypes).includes(type as any)) as typeof groupedNotificationTypes[number][]; const notifications = await this.notificationService.getNotifications(me.id, { - sinceId: ps.sinceId, - untilId: ps.untilId, + sinceId: sinceId, + untilId: untilId, limit: ps.limit, includeTypes, excludeTypes, diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts index f5a48b2f69..158cc9fd73 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications.ts @@ -44,6 +44,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, markAsRead: { type: 'boolean', default: true }, // 後方互換のため、廃止された通知タイプも受け付ける includeTypes: { type: 'array', items: { @@ -59,17 +61,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.redis) - private redisClient: Redis.Redis, - - @Inject(DI.notesRepository) - private notesRepository: NotesRepository, - private idService: IdService, private notificationEntityService: NotificationEntityService, private notificationService: NotificationService, ) { super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : undefined); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : undefined); + // includeTypes が空の場合はクエリしない if (ps.includeTypes && ps.includeTypes.length === 0) { return []; @@ -83,8 +82,8 @@ export default class extends Endpoint { // eslint- const excludeTypes = ps.excludeTypes && ps.excludeTypes.filter(type => !(obsoleteNotificationTypes).includes(type as any)) as typeof notificationTypes[number][]; const notifications = await this.notificationService.getNotifications(me.id, { - sinceId: ps.sinceId, - untilId: ps.untilId, + sinceId: sinceId, + untilId: untilId, limit: ps.limit, includeTypes, excludeTypes, diff --git a/packages/backend/src/server/api/endpoints/i/page-likes.ts b/packages/backend/src/server/api/endpoints/i/page-likes.ts index d4c09426a7..d62dd819f8 100644 --- a/packages/backend/src/server/api/endpoints/i/page-likes.ts +++ b/packages/backend/src/server/api/endpoints/i/page-likes.ts @@ -44,6 +44,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: [], } as const; @@ -58,7 +60,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.pageLikesRepository.createQueryBuilder('like'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.pageLikesRepository.createQueryBuilder('like'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('like.userId = :meId', { meId: me.id }) .leftJoinAndSelect('like.page', 'page'); diff --git a/packages/backend/src/server/api/endpoints/i/pages.ts b/packages/backend/src/server/api/endpoints/i/pages.ts index 1b6359a633..385842539e 100644 --- a/packages/backend/src/server/api/endpoints/i/pages.ts +++ b/packages/backend/src/server/api/endpoints/i/pages.ts @@ -34,6 +34,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: [], } as const; @@ -48,7 +50,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.pagesRepository.createQueryBuilder('page'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.pagesRepository.createQueryBuilder('page'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('page.userId = :meId', { meId: me.id }); const pages = await query diff --git a/packages/backend/src/server/api/endpoints/i/signin-history.ts b/packages/backend/src/server/api/endpoints/i/signin-history.ts index 76ad0bbe21..c104dfdcdd 100644 --- a/packages/backend/src/server/api/endpoints/i/signin-history.ts +++ b/packages/backend/src/server/api/endpoints/i/signin-history.ts @@ -31,6 +31,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: [], } as const; @@ -45,7 +47,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.signinsRepository.createQueryBuilder('signin'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.signinsRepository.createQueryBuilder('signin'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('signin.userId = :meId', { meId: me.id }); const history = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/invite/list.ts b/packages/backend/src/server/api/endpoints/invite/list.ts index a99974a91e..71f8f579a5 100644 --- a/packages/backend/src/server/api/endpoints/invite/list.ts +++ b/packages/backend/src/server/api/endpoints/invite/list.ts @@ -34,6 +34,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 30 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: [], } as const; @@ -48,7 +50,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.registrationTicketsRepository.createQueryBuilder('ticket'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.registrationTicketsRepository.createQueryBuilder('ticket'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('ticket.createdById = :meId', { meId: me.id }) .leftJoinAndSelect('ticket.createdBy', 'createdBy') .leftJoinAndSelect('ticket.usedBy', 'usedBy'); diff --git a/packages/backend/src/server/api/endpoints/mute/list.ts b/packages/backend/src/server/api/endpoints/mute/list.ts index 23204f2829..7762556bee 100644 --- a/packages/backend/src/server/api/endpoints/mute/list.ts +++ b/packages/backend/src/server/api/endpoints/mute/list.ts @@ -34,6 +34,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 30 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: [], } as const; @@ -48,7 +50,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.mutingsRepository.createQueryBuilder('muting'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.mutingsRepository.createQueryBuilder('muting'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('muting.muterId = :meId', { meId: me.id }); const mutings = await query diff --git a/packages/backend/src/server/api/endpoints/notes.ts b/packages/backend/src/server/api/endpoints/notes.ts index 9938322a2a..0800828a87 100644 --- a/packages/backend/src/server/api/endpoints/notes.ts +++ b/packages/backend/src/server/api/endpoints/notes.ts @@ -35,6 +35,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: [], } as const; @@ -49,7 +51,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.visibility = \'public\'') .andWhere('note.localOnly = FALSE') .innerJoinAndSelect('note.user', 'user') diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts index d457ad1220..e9d56e2892 100644 --- a/packages/backend/src/server/api/endpoints/notes/children.ts +++ b/packages/backend/src/server/api/endpoints/notes/children.ts @@ -34,6 +34,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: ['noteId'], } as const; @@ -48,7 +50,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere(new Brackets(qb => { qb .where('note.replyId = :noteId', { noteId: ps.noteId }) diff --git a/packages/backend/src/server/api/endpoints/notes/drafts/list.ts b/packages/backend/src/server/api/endpoints/notes/drafts/list.ts index 1834585aeb..f24f9b8fb2 100644 --- a/packages/backend/src/server/api/endpoints/notes/drafts/list.ts +++ b/packages/backend/src/server/api/endpoints/notes/drafts/list.ts @@ -39,6 +39,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 30 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: [], } as const; @@ -53,7 +55,7 @@ export default class extends Endpoint { // eslint- private noteDraftEntityService: NoteDraftEntityService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.noteDraftsRepository.createQueryBuilder('drafts'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.noteDraftsRepository.createQueryBuilder('drafts'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('drafts.userId = :meId', { meId: me.id }); const drafts = await query diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index 7ffc349db5..05ffdc1f97 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -35,6 +35,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, visibility: { type: 'string' }, }, required: [], @@ -57,7 +59,7 @@ export default class extends Endpoint { // eslint- .select('following.followeeId') .where('following.followerId = :followerId', { followerId: me.id }); - const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere(new Brackets(qb => { qb // このmeIdAsListパラメータはqueryServiceのgenerateVisibilityQueryでセットされる .where(':meIdAsList <@ note.mentions') diff --git a/packages/backend/src/server/api/endpoints/notes/reactions.ts b/packages/backend/src/server/api/endpoints/notes/reactions.ts index 97b12ab7f7..e5e15fd4b3 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions.ts @@ -47,6 +47,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: ['noteId'], } as const; @@ -61,7 +63,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.noteReactionsRepository.createQueryBuilder('reaction'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.noteReactionsRepository.createQueryBuilder('reaction'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('reaction.noteId = :noteId', { noteId: ps.noteId }) .leftJoinAndSelect('reaction.user', 'user') .leftJoinAndSelect('reaction.note', 'note'); diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts index fa2306c1bf..b294794de6 100644 --- a/packages/backend/src/server/api/endpoints/notes/renotes.ts +++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts @@ -43,6 +43,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: ['noteId'], } as const; @@ -63,7 +65,7 @@ export default class extends Endpoint { // eslint- throw err; }); - const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.renoteId = :renoteId', { renoteId: note.id }) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') diff --git a/packages/backend/src/server/api/endpoints/notes/replies.ts b/packages/backend/src/server/api/endpoints/notes/replies.ts index 9626947480..d567d7c64a 100644 --- a/packages/backend/src/server/api/endpoints/notes/replies.ts +++ b/packages/backend/src/server/api/endpoints/notes/replies.ts @@ -32,6 +32,8 @@ export const paramDef = { noteId: { type: 'string', format: 'misskey:id' }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, }, required: ['noteId'], @@ -47,7 +49,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.replyId = :replyId', { replyId: ps.noteId }) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') diff --git a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts index 51255f0bbf..2cd6610cf6 100644 --- a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts +++ b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts @@ -72,6 +72,8 @@ export const paramDef = { poll: { type: 'boolean', nullable: true, default: null }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, }, }, @@ -88,7 +90,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts index 3fe19806e3..ab1bd934fb 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -8,6 +8,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { SearchService } from '@/core/SearchService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { RoleService } from '@/core/RoleService.js'; +import { IdService } from '@/core/IdService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -40,6 +41,8 @@ export const paramDef = { query: { type: 'string' }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, offset: { type: 'integer', default: 0 }, host: { @@ -60,8 +63,12 @@ export default class extends Endpoint { // eslint- private noteEntityService: NoteEntityService, private searchService: SearchService, private roleService: RoleService, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : undefined); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : undefined); + const policies = await this.roleService.getUserPolicies(me ? me.id : null); if (!policies.canSearchNotes) { throw new ApiError(meta.errors.unavailable); @@ -72,8 +79,8 @@ export default class extends Endpoint { // eslint- channelId: ps.channelId, host: ps.host, }, { - untilId: ps.untilId, - sinceId: ps.sinceId, + untilId: untilId, + sinceId: sinceId, limit: ps.limit, }); diff --git a/packages/backend/src/server/api/endpoints/renote-mute/list.ts b/packages/backend/src/server/api/endpoints/renote-mute/list.ts index 3be01f989a..adf5aa76bf 100644 --- a/packages/backend/src/server/api/endpoints/renote-mute/list.ts +++ b/packages/backend/src/server/api/endpoints/renote-mute/list.ts @@ -34,6 +34,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 30 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: [], } as const; @@ -48,7 +50,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.renoteMutingsRepository.createQueryBuilder('muting'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.renoteMutingsRepository.createQueryBuilder('muting'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('muting.muterId = :meId', { meId: me.id }); const mutings = await query diff --git a/packages/backend/src/server/api/endpoints/reversi/games.ts b/packages/backend/src/server/api/endpoints/reversi/games.ts index 6b06068727..22f3a0617b 100644 --- a/packages/backend/src/server/api/endpoints/reversi/games.ts +++ b/packages/backend/src/server/api/endpoints/reversi/games.ts @@ -27,6 +27,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, my: { type: 'boolean', default: false }, }, required: [], @@ -42,7 +44,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.reversiGamesRepository.createQueryBuilder('game'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.reversiGamesRepository.createQueryBuilder('game'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .innerJoinAndSelect('game.user1', 'user1') .innerJoinAndSelect('game.user2', 'user2'); diff --git a/packages/backend/src/server/api/endpoints/roles/users.ts b/packages/backend/src/server/api/endpoints/roles/users.ts index 48d350af59..c7ec7910d6 100644 --- a/packages/backend/src/server/api/endpoints/roles/users.ts +++ b/packages/backend/src/server/api/endpoints/roles/users.ts @@ -51,6 +51,8 @@ export const paramDef = { roleId: { type: 'string', format: 'misskey:id' }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, }, required: ['roleId'], @@ -79,7 +81,7 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.noSuchRole); } - const query = this.queryService.makePaginationQuery(this.roleAssignmentsRepository.createQueryBuilder('assign'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.roleAssignmentsRepository.createQueryBuilder('assign'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('assign.roleId = :roleId', { roleId: role.id }) .andWhere(new Brackets(qb => { qb diff --git a/packages/backend/src/server/api/endpoints/users/clips.ts b/packages/backend/src/server/api/endpoints/users/clips.ts index 7f7d2ea8cc..fb5a2391f2 100644 --- a/packages/backend/src/server/api/endpoints/users/clips.ts +++ b/packages/backend/src/server/api/endpoints/users/clips.ts @@ -33,6 +33,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: ['userId'], } as const; @@ -47,7 +49,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.clipsRepository.createQueryBuilder('clip'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.clipsRepository.createQueryBuilder('clip'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('clip.userId = :userId', { userId: ps.userId }) .andWhere('clip.isPublic = true'); diff --git a/packages/backend/src/server/api/endpoints/users/flashs.ts b/packages/backend/src/server/api/endpoints/users/flashs.ts index e5ea450215..2ef514bebf 100644 --- a/packages/backend/src/server/api/endpoints/users/flashs.ts +++ b/packages/backend/src/server/api/endpoints/users/flashs.ts @@ -33,11 +33,12 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: ['userId'], } as const; - -// eslint-disable-next-line import/no-default-export + @Injectable() export default class extends Endpoint { constructor( @@ -48,7 +49,7 @@ export default class extends Endpoint { private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.flashsRepository.createQueryBuilder('flash'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.flashsRepository.createQueryBuilder('flash'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('flash.userId = :userId', { userId: ps.userId }) .andWhere('flash.visibility = \'public\''); diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts index bb8d4c49e9..84c4c80d01 100644 --- a/packages/backend/src/server/api/endpoints/users/followers.ts +++ b/packages/backend/src/server/api/endpoints/users/followers.ts @@ -76,6 +76,8 @@ export const paramDef = { properties: { sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, }, }, @@ -132,7 +134,7 @@ export default class extends Endpoint { // eslint- } } - const query = this.queryService.makePaginationQuery(this.followingsRepository.createQueryBuilder('following'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.followingsRepository.createQueryBuilder('following'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('following.followeeId = :userId', { userId: user.id }) .innerJoinAndSelect('following.follower', 'follower'); diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts index 1fc87151b2..047f9a053b 100644 --- a/packages/backend/src/server/api/endpoints/users/following.ts +++ b/packages/backend/src/server/api/endpoints/users/following.ts @@ -83,6 +83,8 @@ export const paramDef = { properties: { sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, birthday: { ...birthdaySchema, nullable: true }, }, @@ -140,7 +142,7 @@ export default class extends Endpoint { // eslint- } } - const query = this.queryService.makePaginationQuery(this.followingsRepository.createQueryBuilder('following'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.followingsRepository.createQueryBuilder('following'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('following.followerId = :userId', { userId: user.id }) .innerJoinAndSelect('following.followee', 'followee'); diff --git a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts index 553886374c..55e885e3c3 100644 --- a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts @@ -33,6 +33,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: ['userId'], } as const; @@ -47,7 +49,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.galleryPostsRepository.createQueryBuilder('post'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.galleryPostsRepository.createQueryBuilder('post'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('post.userId = :userId', { userId: ps.userId }); const posts = await query diff --git a/packages/backend/src/server/api/endpoints/users/lists/get-memberships.ts b/packages/backend/src/server/api/endpoints/users/lists/get-memberships.ts index 6d6e8d34ea..a841c4b94d 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/get-memberships.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/get-memberships.ts @@ -64,6 +64,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 30 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: ['listId'], } as const; @@ -94,7 +96,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchList); } - const query = this.queryService.makePaginationQuery(this.userListMembershipsRepository.createQueryBuilder('membership'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.userListMembershipsRepository.createQueryBuilder('membership'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('membership.userListId = :userListId', { userListId: userList.id }) .innerJoinAndSelect('membership.user', 'user'); diff --git a/packages/backend/src/server/api/endpoints/users/pages.ts b/packages/backend/src/server/api/endpoints/users/pages.ts index bb7de0e0b5..1d7499d4be 100644 --- a/packages/backend/src/server/api/endpoints/users/pages.ts +++ b/packages/backend/src/server/api/endpoints/users/pages.ts @@ -33,6 +33,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, }, required: ['userId'], } as const; @@ -47,7 +49,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.pagesRepository.createQueryBuilder('page'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.pagesRepository.createQueryBuilder('page'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('page.userId = :userId', { userId: ps.userId }) .andWhere('page.visibility = \'public\''); diff --git a/packages/backend/src/server/api/endpoints/v2/admin/emoji/list.ts b/packages/backend/src/server/api/endpoints/v2/admin/emoji/list.ts index 7139715293..c07d100a99 100644 --- a/packages/backend/src/server/api/endpoints/v2/admin/emoji/list.ts +++ b/packages/backend/src/server/api/endpoints/v2/admin/emoji/list.ts @@ -7,6 +7,7 @@ import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; import { CustomEmojiService, fetchEmojisHostTypes, fetchEmojisSortKeys } from '@/core/CustomEmojiService.js'; +import { IdService } from '@/core/IdService.js'; export const meta = { tags: ['admin'], @@ -65,6 +66,8 @@ export const paramDef = { }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, page: { type: 'integer' }, sortKeys: { @@ -84,8 +87,12 @@ export default class extends Endpoint { // eslint- constructor( private customEmojiService: CustomEmojiService, private emojiEntityService: EmojiEntityService, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : undefined); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : undefined); + const q = ps.query; const result = await this.customEmojiService.fetchEmojis( { @@ -105,8 +112,8 @@ export default class extends Endpoint { // eslint- hostType: q?.hostType, roleIds: q?.roleIds, }, - sinceId: ps.sinceId, - untilId: ps.untilId, + sinceId: sinceId, + untilId: untilId, }, { limit: ps.limit, diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue index 891af7f696..a77ebd6ac5 100644 --- a/packages/frontend/src/components/MkButton.vue +++ b/packages/frontend/src/components/MkButton.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only