From a722ea8ccd98c66784442d71a1e1cd14b7835d48 Mon Sep 17 00:00:00 2001 From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com> Date: Thu, 3 Oct 2024 17:05:14 +0900 Subject: fix(backend): 連合限定先が間違って連合しない先に代入されているのを修正 (#14662) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(backend): 連合限定先が間違って連合しない先に代入されているのを修正 * build: fix property typo --- packages/backend/src/server/api/endpoints/admin/update-meta.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index daef236397..9ffae840b6 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -652,7 +652,7 @@ export default class extends Endpoint { // eslint- } if (Array.isArray(ps.federationHosts)) { - set.blockedHosts = ps.federationHosts.filter(Boolean).map(x => x.toLowerCase()); + set.federationHosts = ps.federationHosts.filter(Boolean).map(x => x.toLowerCase()); } const before = await this.metaService.fetch(true); -- cgit v1.2.3-freya From 2c1a7470d35cb840950e63008fb4014e5e341dd6 Mon Sep 17 00:00:00 2001 From: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Thu, 3 Oct 2024 18:18:00 +0900 Subject: feat: サーバー初期設定時に初期パスワードを要求できるように (#14626) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: サーバー初期設定時専用の初期パスワードを設定できるように * 無いのに入力された場合もエラーにする * :art: * :art: * cypress-devcontainerにもpassを設定(テストが失敗するため) * [ci skip] :art: * :v: * test: please revert this commit before merge * Revert "test: please revert this commit before merge" This reverts commit 66b2b48f66830d2450d8cda03955c143feba76c7. * Update locales/ja-JP.yml Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> * build assets * Update Changelog * fix condition * fix condition * add comment * change error code * 他のエラーコードと合わせる * Update CHANGELOG.md --------- Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> --- .config/cypress-devcontainer.yml | 13 ++++++++ .config/example.yml | 13 ++++++++ CHANGELOG.md | 5 +++ cypress/e2e/basic.cy.ts | 1 + locales/index.d.ts | 14 ++++++++ locales/ja-JP.yml | 3 ++ packages/backend/src/config.ts | 4 +++ .../server/api/endpoints/admin/accounts/create.ts | 38 +++++++++++++++++++++- packages/frontend/src/pages/welcome.setup.vue | 26 ++++++++++++--- packages/misskey-js/src/autogen/types.ts | 1 + 10 files changed, 113 insertions(+), 5 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/.config/cypress-devcontainer.yml b/.config/cypress-devcontainer.yml index 91dce35155..64988aff66 100644 --- a/.config/cypress-devcontainer.yml +++ b/.config/cypress-devcontainer.yml @@ -2,6 +2,19 @@ # Misskey configuration #━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# ┌────────────────────────┐ +#───┘ Initial Setup Password └───────────────────────────────────────────────────── + +# Password to initiate setting up admin account. +# It will not be used after the initial setup is complete. +# +# Be sure to change this when you set up Misskey via the Internet. +# +# The provider of the service who sets up Misskey on behalf of the customer should +# set this value to something unique when generating the Misskey config file, +# and provide it to the customer. +initialPassword: example_password_please_change_this_or_you_will_get_hacked + # ┌─────┐ #───┘ URL └───────────────────────────────────────────────────── diff --git a/.config/example.yml b/.config/example.yml index 7080159117..fbc4cdff4b 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -59,6 +59,19 @@ # # publishTarballInsteadOfProvideRepositoryUrl: true +# ┌────────────────────────┐ +#───┘ Initial Setup Password └───────────────────────────────────────────────────── + +# Password to initiate setting up admin account. +# It will not be used after the initial setup is complete. +# +# Be sure to change this when you set up Misskey via the Internet. +# +# The provider of the service who sets up Misskey on behalf of the customer should +# set this value to something unique when generating the Misskey config file, +# and provide it to the customer. +initialPassword: example_password_please_change_this_or_you_will_get_hacked + # ┌─────┐ #───┘ URL └───────────────────────────────────────────────────── diff --git a/CHANGELOG.md b/CHANGELOG.md index 188e3b7d82..2e48931267 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ ## 2024.10.0 +### Note +- サーバー初期設定時に使用する初期パスワードを設定できるようになりました。今後Misskeyサーバーを新たに設置する際には、初回の起動前にコンフィグファイルの`initialPassword`を必ず変更してください。(すでに初期設定を完了しているサーバーについては、この変更に伴い対応する必要はありません) + ホスティングサービスを運営している場合は、コンフィグファイルを構築する際に`initialPassword`をランダムな値に設定し、ユーザーに通知するようにしてください。 + ### General +- Feat: サーバー初期設定時に初期パスワードを設定できるように - Enhance: セキュリティ向上のため、サインイン時もCAPTCHAを求めるようになりました - Enhance: 依存関係の更新 - Enhance: l10nの更新 diff --git a/cypress/e2e/basic.cy.ts b/cypress/e2e/basic.cy.ts index d2525e0a7d..e4baeacbf3 100644 --- a/cypress/e2e/basic.cy.ts +++ b/cypress/e2e/basic.cy.ts @@ -23,6 +23,7 @@ describe('Before setup instance', () => { cy.intercept('POST', '/api/admin/accounts/create').as('signup'); + cy.get('[data-cy-admin-initial-password] input').type('example_password_please_change_this_or_you_will_get_hacked'); cy.get('[data-cy-admin-username] input').type('admin'); cy.get('[data-cy-admin-password] input').type('admin1234'); cy.get('[data-cy-admin-ok]').click(); diff --git a/locales/index.d.ts b/locales/index.d.ts index 0a9123f03d..86a6df3100 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -48,6 +48,20 @@ export interface Locale extends ILocale { * パスワード */ "password": string; + /** + * 初期設定開始用パスワード + */ + "initialPasswordForSetup": string; + /** + * 初期設定開始用のパスワードが違います。 + */ + "initialPasswordIsIncorrect": string; + /** + * Misskeyを自分でインストールした場合は、設定ファイルに入力したパスワードを使用してください。 + * Misskeyのホスティングサービスなどを使用している場合は、提供されたパスワードを使用してください。 + * パスワードを設定していない場合は、空欄にしたまま続行してください。 + */ + "initialPasswordForSetupDescription": string; /** * パスワードを忘れた */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index cfbe0dcc75..62317cd5e6 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -8,6 +8,9 @@ search: "検索" notifications: "通知" username: "ユーザー名" password: "パスワード" +initialPasswordForSetup: "初期設定開始用パスワード" +initialPasswordIsIncorrect: "初期設定開始用のパスワードが違います。" +initialPasswordForSetupDescription: "Misskeyを自分でインストールした場合は、設定ファイルに入力したパスワードを使用してください。\nMisskeyのホスティングサービスなどを使用している場合は、提供されたパスワードを使用してください。\nパスワードを設定していない場合は、空欄にしたまま続行してください。" forgotPassword: "パスワードを忘れた" fetchingAsApObject: "連合に照会中" ok: "OK" diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts index 97ba79c574..b320ce5403 100644 --- a/packages/backend/src/config.ts +++ b/packages/backend/src/config.ts @@ -63,6 +63,8 @@ type Source = { publishTarballInsteadOfProvideRepositoryUrl?: boolean; + initialPassword?: string; + proxy?: string; proxySmtp?: string; proxyBypassHosts?: string[]; @@ -152,6 +154,7 @@ export type Config = { version: string; publishTarballInsteadOfProvideRepositoryUrl: boolean; + initialPassword: string | undefined; host: string; hostname: string; scheme: string; @@ -232,6 +235,7 @@ export function loadConfig(): Config { return { version, publishTarballInsteadOfProvideRepositoryUrl: !!config.publishTarballInsteadOfProvideRepositoryUrl, + initialPassword: config.initialPassword, url: url.origin, port: config.port ?? parseInt(process.env.PORT ?? '', 10), socket: config.socket, diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts index a7e8a3b018..bddf7f45d3 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts @@ -12,11 +12,27 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { InstanceActorService } from '@/core/InstanceActorService.js'; import { localUsernameSchema, passwordSchema } from '@/models/User.js'; import { DI } from '@/di-symbols.js'; +import type { Config } from '@/config.js'; +import { ApiError } from '@/server/api/error.js'; import { Packed } from '@/misc/json-schema.js'; export const meta = { tags: ['admin'], + errors: { + accessDenied: { + message: 'Access denied.', + code: 'ACCESS_DENIED', + id: '1fb7cb09-d46a-4fff-b8df-057708cce513', + }, + + wrongInitialPassword: { + message: 'Initial password is incorrect.', + code: 'INCORRECT_INITIAL_PASSWORD', + id: '97147c55-1ae1-4f6f-91d6-e1c3e0e76d62', + }, + }, + res: { type: 'object', optional: false, nullable: false, @@ -35,6 +51,7 @@ export const paramDef = { properties: { username: localUsernameSchema, password: passwordSchema, + initialPassword: { type: 'string', nullable: true }, }, required: ['username', 'password'], } as const; @@ -42,6 +59,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { // eslint-disable-line import/no-default-export constructor( + @Inject(DI.config) + private config: Config, + @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -52,7 +72,23 @@ export default class extends Endpoint { // eslint- super(meta, paramDef, async (ps, _me, token) => { const me = _me ? await this.usersRepository.findOneByOrFail({ id: _me.id }) : null; const realUsers = await this.instanceActorService.realLocalUsersPresent(); - if ((realUsers && !me?.isRoot) || token !== null) throw new Error('access denied'); + + if (!realUsers && me == null && token == null) { + // 初回セットアップの場合 + if (this.config.initialPassword != null) { + // 初期パスワードが設定されている場合 + if (ps.initialPassword !== this.config.initialPassword) { + // 初期パスワードが違う場合 + throw new ApiError(meta.errors.wrongInitialPassword); + } + } else if (ps.initialPassword != null && ps.initialPassword.trim() !== '') { + // 初期パスワードが設定されていないのに初期パスワードが入力された場合 + throw new ApiError(meta.errors.wrongInitialPassword); + } + } else if ((realUsers && !me?.isRoot) || token !== null) { + // 初回セットアップではなく、管理者でない場合 or 外部トークンを使用している場合 + throw new ApiError(meta.errors.accessDenied); + } const { account, secret } = await this.signupService.signup({ username: ps.username, diff --git a/packages/frontend/src/pages/welcome.setup.vue b/packages/frontend/src/pages/welcome.setup.vue index a227c7c4bc..cb20cfc5fc 100644 --- a/packages/frontend/src/pages/welcome.setup.vue +++ b/packages/frontend/src/pages/welcome.setup.vue @@ -14,6 +14,10 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.intro }}
+ + + + @@ -47,6 +51,7 @@ import MkAnimBg from '@/components/MkAnimBg.vue'; const username = ref(''); const password = ref(''); +const initialPassword = ref(''); const submitting = ref(false); function submit() { @@ -56,14 +61,27 @@ function submit() { misskeyApi('admin/accounts/create', { username: username.value, password: password.value, + initialPassword: initialPassword.value === '' ? null : initialPassword.value, }).then(res => { return login(res.token); - }).catch(() => { + }).catch((err) => { submitting.value = false; + let title = i18n.ts.somethingHappened; + let text = err.message + '\n' + err.id; + + if (err.code === 'ACCESS_DENIED') { + title = i18n.ts.permissionDeniedError; + text = i18n.ts.operationForbidden; + } else if (err.code === 'INCORRECT_INITIAL_PASSWORD') { + title = i18n.ts.permissionDeniedError; + text = i18n.ts.incorrectPassword; + } + os.alert({ type: 'error', - text: i18n.ts.somethingHappened, + title, + text, }); }); } @@ -74,8 +92,8 @@ function submit() { min-height: 100svh; padding: 32px 32px 64px 32px; box-sizing: border-box; -display: grid; -place-content: center; + display: grid; + place-content: center; } .form { diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 46fc2496da..ee5cd477f1 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -5611,6 +5611,7 @@ export type operations = { 'application/json': { username: string; password: string; + initialPassword?: string | null; }; }; }; -- cgit v1.2.3-freya From d2175a9b9f6e38ca3ec0ca28b29d99f4b46f9dcd Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 3 Oct 2024 20:40:39 +0900 Subject: initialPassword -> setupPassword --- .config/cypress-devcontainer.yml | 2 +- .config/example.yml | 2 +- packages/backend/src/config.ts | 6 +++--- .../backend/src/server/api/endpoints/admin/accounts/create.ts | 8 ++++---- packages/frontend/src/pages/welcome.setup.vue | 8 ++++---- packages/misskey-js/src/autogen/types.ts | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/.config/cypress-devcontainer.yml b/.config/cypress-devcontainer.yml index 64988aff66..3907615f73 100644 --- a/.config/cypress-devcontainer.yml +++ b/.config/cypress-devcontainer.yml @@ -13,7 +13,7 @@ # The provider of the service who sets up Misskey on behalf of the customer should # set this value to something unique when generating the Misskey config file, # and provide it to the customer. -initialPassword: example_password_please_change_this_or_you_will_get_hacked +setupPassword: example_password_please_change_this_or_you_will_get_hacked # ┌─────┐ #───┘ URL └───────────────────────────────────────────────────── diff --git a/.config/example.yml b/.config/example.yml index fbc4cdff4b..600c1c632e 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -70,7 +70,7 @@ # The provider of the service who sets up Misskey on behalf of the customer should # set this value to something unique when generating the Misskey config file, # and provide it to the customer. -initialPassword: example_password_please_change_this_or_you_will_get_hacked +setupPassword: example_password_please_change_this_or_you_will_get_hacked # ┌─────┐ #───┘ URL └───────────────────────────────────────────────────── diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts index b320ce5403..42f1033b9d 100644 --- a/packages/backend/src/config.ts +++ b/packages/backend/src/config.ts @@ -63,7 +63,7 @@ type Source = { publishTarballInsteadOfProvideRepositoryUrl?: boolean; - initialPassword?: string; + setupPassword?: string; proxy?: string; proxySmtp?: string; @@ -154,7 +154,7 @@ export type Config = { version: string; publishTarballInsteadOfProvideRepositoryUrl: boolean; - initialPassword: string | undefined; + setupPassword: string | undefined; host: string; hostname: string; scheme: string; @@ -235,7 +235,7 @@ export function loadConfig(): Config { return { version, publishTarballInsteadOfProvideRepositoryUrl: !!config.publishTarballInsteadOfProvideRepositoryUrl, - initialPassword: config.initialPassword, + setupPassword: config.setupPassword, url: url.origin, port: config.port ?? parseInt(process.env.PORT ?? '', 10), socket: config.socket, diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts index bddf7f45d3..d30131a62f 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts @@ -51,7 +51,7 @@ export const paramDef = { properties: { username: localUsernameSchema, password: passwordSchema, - initialPassword: { type: 'string', nullable: true }, + setupPassword: { type: 'string', nullable: true }, }, required: ['username', 'password'], } as const; @@ -75,13 +75,13 @@ export default class extends Endpoint { // eslint- if (!realUsers && me == null && token == null) { // 初回セットアップの場合 - if (this.config.initialPassword != null) { + if (this.config.setupPassword != null) { // 初期パスワードが設定されている場合 - if (ps.initialPassword !== this.config.initialPassword) { + if (ps.setupPassword !== this.config.setupPassword) { // 初期パスワードが違う場合 throw new ApiError(meta.errors.wrongInitialPassword); } - } else if (ps.initialPassword != null && ps.initialPassword.trim() !== '') { + } else if (ps.setupPassword != null && ps.setupPassword.trim() !== '') { // 初期パスワードが設定されていないのに初期パスワードが入力された場合 throw new ApiError(meta.errors.wrongInitialPassword); } diff --git a/packages/frontend/src/pages/welcome.setup.vue b/packages/frontend/src/pages/welcome.setup.vue index cb20cfc5fc..dd258aad98 100644 --- a/packages/frontend/src/pages/welcome.setup.vue +++ b/packages/frontend/src/pages/welcome.setup.vue @@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.intro }}
- + @@ -40,9 +40,9 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/frontend/src/pages/avatar-decorations.vue b/packages/frontend/src/pages/avatar-decorations.vue index b97e7c0eea..a5cafb1678 100644 --- a/packages/frontend/src/pages/avatar-decorations.vue +++ b/packages/frontend/src/pages/avatar-decorations.vue @@ -5,92 +5,38 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue index f72a0b9383..3c9914b4e2 100644 --- a/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue +++ b/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue @@ -10,12 +10,12 @@ SPDX-License-Identifier: AGPL-3.0-only >
{{ decoration.name }}
- +
diff --git a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue index 853e536ea3..aa899ac649 100644 --- a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue +++ b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue @@ -38,7 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.update }} {{ i18n.ts.detach }} - {{ i18n.ts.attach }} + {{ i18n.ts.attach }}
@@ -61,6 +61,7 @@ const props = defineProps<{ id: string; url: string; name: string; + roleIdsThatCanBeUsedThisDecoration: string[]; }; }>(); @@ -83,6 +84,7 @@ const emit = defineEmits<{ const dialog = shallowRef>(); const exceeded = computed(() => ($i.policies.avatarDecorationLimit - $i.avatarDecorations.length) <= 0); +const locked = computed(() => props.decoration.roleIdsThatCanBeUsedThisDecoration.length > 0 && !$i.roles.some(r => props.decoration.roleIdsThatCanBeUsedThisDecoration.includes(r.id))); const angle = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].angle : null) ?? 0); const flipH = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].flipH : null) ?? false); const offsetX = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].offsetX : null) ?? 0); diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 61de8b8c7e..061b533b72 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -121,6 +121,9 @@ type AdminAnnouncementsUpdateRequest = operations['admin___announcements___updat // @public (undocumented) type AdminAvatarDecorationsCreateRequest = operations['admin___avatar-decorations___create']['requestBody']['content']['application/json']; +// @public (undocumented) +type AdminAvatarDecorationsCreateResponse = operations['admin___avatar-decorations___create']['responses']['200']['content']['application/json']; + // @public (undocumented) type AdminAvatarDecorationsDeleteRequest = operations['admin___avatar-decorations___delete']['requestBody']['content']['application/json']; @@ -1253,6 +1256,7 @@ declare namespace entities { AdminAnnouncementsListResponse, AdminAnnouncementsUpdateRequest, AdminAvatarDecorationsCreateRequest, + AdminAvatarDecorationsCreateResponse, AdminAvatarDecorationsDeleteRequest, AdminAvatarDecorationsListRequest, AdminAvatarDecorationsListResponse, diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts index d0367d8496..5e6bc0a99c 100644 --- a/packages/misskey-js/src/autogen/endpoint.ts +++ b/packages/misskey-js/src/autogen/endpoint.ts @@ -31,6 +31,7 @@ import type { AdminAnnouncementsListResponse, AdminAnnouncementsUpdateRequest, AdminAvatarDecorationsCreateRequest, + AdminAvatarDecorationsCreateResponse, AdminAvatarDecorationsDeleteRequest, AdminAvatarDecorationsListRequest, AdminAvatarDecorationsListResponse, @@ -597,7 +598,7 @@ export type Endpoints = { 'admin/announcements/delete': { req: AdminAnnouncementsDeleteRequest; res: EmptyResponse }; 'admin/announcements/list': { req: AdminAnnouncementsListRequest; res: AdminAnnouncementsListResponse }; 'admin/announcements/update': { req: AdminAnnouncementsUpdateRequest; res: EmptyResponse }; - 'admin/avatar-decorations/create': { req: AdminAvatarDecorationsCreateRequest; res: EmptyResponse }; + 'admin/avatar-decorations/create': { req: AdminAvatarDecorationsCreateRequest; res: AdminAvatarDecorationsCreateResponse }; 'admin/avatar-decorations/delete': { req: AdminAvatarDecorationsDeleteRequest; res: EmptyResponse }; 'admin/avatar-decorations/list': { req: AdminAvatarDecorationsListRequest; res: AdminAvatarDecorationsListResponse }; 'admin/avatar-decorations/update': { req: AdminAvatarDecorationsUpdateRequest; res: EmptyResponse }; diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts index ced87c4c7e..f3ddf64481 100644 --- a/packages/misskey-js/src/autogen/entities.ts +++ b/packages/misskey-js/src/autogen/entities.ts @@ -34,6 +34,7 @@ export type AdminAnnouncementsListRequest = operations['admin___announcements___ export type AdminAnnouncementsListResponse = operations['admin___announcements___list']['responses']['200']['content']['application/json']; export type AdminAnnouncementsUpdateRequest = operations['admin___announcements___update']['requestBody']['content']['application/json']; export type AdminAvatarDecorationsCreateRequest = operations['admin___avatar-decorations___create']['requestBody']['content']['application/json']; +export type AdminAvatarDecorationsCreateResponse = operations['admin___avatar-decorations___create']['responses']['200']['content']['application/json']; export type AdminAvatarDecorationsDeleteRequest = operations['admin___avatar-decorations___delete']['requestBody']['content']['application/json']; export type AdminAvatarDecorationsListRequest = operations['admin___avatar-decorations___list']['requestBody']['content']['application/json']; export type AdminAvatarDecorationsListResponse = operations['admin___avatar-decorations___list']['responses']['200']['content']['application/json']; diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 560960f018..a5333d4f93 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -6324,9 +6324,22 @@ export type operations = { }; }; responses: { - /** @description OK (without any results) */ - 204: { - content: never; + /** @description OK (with results) */ + 200: { + content: { + 'application/json': { + /** Format: id */ + id: string; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string | null; + name: string; + description: string; + url: string; + roleIdsThatCanBeUsedThisDecoration: string[]; + }; + }; }; /** @description Client error */ 400: { -- cgit v1.2.3-freya From 8477909af208b7e0f3ce9357350be6b0a0fc783d Mon Sep 17 00:00:00 2001 From: Kio! Date: Sun, 3 Nov 2024 19:50:25 +0000 Subject: Update report-abuse.ts --- packages/backend/src/server/api/endpoints/users/report-abuse.ts | 4 ---- 1 file changed, 4 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/users/report-abuse.ts b/packages/backend/src/server/api/endpoints/users/report-abuse.ts index 5ff6de37d2..38ded8ee1e 100644 --- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts +++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts @@ -66,10 +66,6 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.cannotReportYourself); } - if (await this.roleService.isAdministrator(targetUser)) { - throw new ApiError(meta.errors.cannotReportAdmin); - } - await this.abuseReportService.report([{ targetUserId: targetUser.id, targetUserHost: targetUser.host, -- cgit v1.2.3-freya From b1c82213a320dd7c83f8b2e742406646ef18ff1c Mon Sep 17 00:00:00 2001 From: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Wed, 6 Nov 2024 22:01:21 +0900 Subject: fix(backend): FTT無効時にユーザーリストタイムラインが使用できない問題を修正 (#14878) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: return getfromdb when FanoutTimeline is not enabled * Update Changelog * fix --------- Co-authored-by: Lhc_fl --- CHANGELOG.md | 2 ++ packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/CHANGELOG.md b/CHANGELOG.md index 0309f338f1..1740d0171e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,8 @@ (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/706) - Fix: 連合への配信時に、acctの大小文字が区別されてしまい正しくメンションが処理されないことがある問題を修正 (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/711) +- Fix: FTT無効時にユーザーリストタイムラインが使用できない問題を修正 + (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/709) ### Misskey.js - Fix: Stream初期化時、別途WebSocketを指定する場合の型定義を修正 diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index 6c7185c9eb..87f9b322a6 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -112,7 +112,7 @@ export default class extends Endpoint { // eslint- this.activeUsersChart.read(me); - await this.noteEntityService.packMany(timeline, me); + return await this.noteEntityService.packMany(timeline, me); } const timeline = await this.fanoutTimelineEndpointService.timeline({ -- cgit v1.2.3-freya From bca690f256721815fb1c918c1f66a2172f4fcf40 Mon Sep 17 00:00:00 2001 From: 4ster1sk <146138447+4ster1sk@users.noreply.github.com> Date: Thu, 7 Nov 2024 15:10:10 +0900 Subject: fix(backend): フォロワーへのメッセージの絵文字をemojisに含めるように (#14904) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/server/api/endpoints/i/update.ts | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 2183beac7c..d91e2fef4b 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -465,6 +465,7 @@ export default class extends Endpoint { // eslint- const newName = updates.name === undefined ? user.name : updates.name; const newDescription = profileUpdates.description === undefined ? profile.description : profileUpdates.description; const newFields = profileUpdates.fields === undefined ? profile.fields : profileUpdates.fields; + const newFollowedMessage = profileUpdates.description === undefined ? profile.followedMessage : profileUpdates.followedMessage; if (newName != null) { let hasProhibitedWords = false; @@ -494,6 +495,11 @@ export default class extends Endpoint { // eslint- ]); } + if (newFollowedMessage != null) { + const tokens = mfm.parse(newFollowedMessage); + emojis = emojis.concat(extractCustomEmojisFromMfm(tokens)); + } + updates.emojis = emojis; updates.tags = tags; -- cgit v1.2.3-freya From 794cb9ffe205b1e2ca838978f80d2d6a35f17f77 Mon Sep 17 00:00:00 2001 From: 4ster1sk <146138447+4ster1sk@users.noreply.github.com> Date: Thu, 7 Nov 2024 17:16:51 +0900 Subject: fix(backend): followedMessageではなくdescriptionになっていたのを修正 (#14908) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/server/api/endpoints/i/update.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index d91e2fef4b..d3eeb75b27 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -465,7 +465,7 @@ export default class extends Endpoint { // eslint- const newName = updates.name === undefined ? user.name : updates.name; const newDescription = profileUpdates.description === undefined ? profile.description : profileUpdates.description; const newFields = profileUpdates.fields === undefined ? profile.fields : profileUpdates.fields; - const newFollowedMessage = profileUpdates.description === undefined ? profile.followedMessage : profileUpdates.followedMessage; + const newFollowedMessage = profileUpdates.followedMessage === undefined ? profile.followedMessage : profileUpdates.followedMessage; if (newName != null) { let hasProhibitedWords = false; -- cgit v1.2.3-freya From ec875d9c40d517a788a450f3b68bd589adcde5ba Mon Sep 17 00:00:00 2001 From: dakkar Date: Fri, 8 Nov 2024 16:09:02 +0000 Subject: fix merge mistakes in `admin/accounts/create.ts` --- packages/backend/src/server/api/endpoints/admin/accounts/create.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts index d5d2e909a2..53b1c4c4ec 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts @@ -3,8 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { UsersRepository } from '@/models/_.js'; import { MiAccessToken, MiUser } from '@/models/_.js'; import { SignupService } from '@/core/SignupService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; @@ -15,7 +16,6 @@ import type { Config } from '@/config.js'; import { ApiError } from '@/server/api/error.js'; import { Packed } from '@/misc/json-schema.js'; import { RoleService } from '@/core/RoleService.js'; -import { ApiError } from '@/server/api/error.js'; export const meta = { tags: ['admin'], -- cgit v1.2.3-freya From 03559156b923ce5e337c998868f4fe12acfb7f14 Mon Sep 17 00:00:00 2001 From: Caramel Date: Sat, 9 Nov 2024 00:32:03 +0100 Subject: Improve performance of notes/following API --- .../src/server/api/endpoints/notes/following.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/notes/following.ts b/packages/backend/src/server/api/endpoints/notes/following.ts index b6604b9798..f8e9e5c4a1 100644 --- a/packages/backend/src/server/api/endpoints/notes/following.ts +++ b/packages/backend/src/server/api/endpoints/notes/following.ts @@ -103,6 +103,15 @@ export default class extends Endpoint { // eslint- sub.andWhere('latest.is_quote = false'); } + // Select the appropriate collection of users + if (ps.list === 'followers') { + addFollower(sub); + } else if (ps.list === 'following') { + addFollowee(sub); + } else { + addMutual(sub); + } + return sub; }, 'latest', @@ -118,15 +127,6 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('note.channel', 'channel') ; - // Select the appropriate collection of users - if (ps.list === 'followers') { - addFollower(query); - } else if (ps.list === 'following') { - addFollowee(query); - } else { - addMutual(query); - } - // Limit to files, if requested if (ps.filesOnly) { query.andWhere('note."fileIds" != \'{}\''); -- cgit v1.2.3-freya From c0d168260482caa974e3fc9e084b121fc32e8ec4 Mon Sep 17 00:00:00 2001 From: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Fri, 15 Nov 2024 17:30:54 +0900 Subject: feat: 送信したフォローリクエストを確認できるように (#14856) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * FEAT: Allow users to view pending follow requests they sent This commit implements the `following/requests/sent` interface firstly implemented on Firefish, and provides a UI interface to view the pending follow requests users sent. * ux: should not show follow requests tab when have no pending sent follow req * fix default followreq tab * fix default followreq tab * restore missing hasPendingReceivedFollowRequest in navbar * refactor * use tabler icons * tweak design * Revert "ux: should not show follow requests tab when have no pending sent follow req" This reverts commit e580b92c37f27c2849c6d27e22ca4c47086081bb. * Update Changelog * Update Changelog * change tab titles --------- Co-authored-by: Lhc_fl Co-authored-by: Hazelnoot --- CHANGELOG.md | 2 + locales/index.d.ts | 10 ++ locales/ja-JP.yml | 4 + packages/backend/src/server/api/EndpointsModule.ts | 3 + packages/backend/src/server/api/endpoints.ts | 2 + .../api/endpoints/following/requests/sent.ts | 77 +++++++++++++++ .../frontend/src/components/MkFollowButton.vue | 10 +- packages/frontend/src/navbar.ts | 1 - packages/frontend/src/pages/follow-requests.vue | 105 ++++++++++++++------- packages/misskey-js/etc/misskey-js.api.md | 8 ++ packages/misskey-js/src/autogen/apiClientJSDoc.ts | 11 +++ packages/misskey-js/src/autogen/endpoint.ts | 3 + packages/misskey-js/src/autogen/entities.ts | 2 + packages/misskey-js/src/autogen/types.ts | 72 ++++++++++++++ 14 files changed, 272 insertions(+), 38 deletions(-) create mode 100644 packages/backend/src/server/api/endpoints/following/requests/sent.ts (limited to 'packages/backend/src/server/api/endpoints') diff --git a/CHANGELOG.md b/CHANGELOG.md index fcbfed5900..232d52d7e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,8 @@ - Enhance: カタルーニャ語 (ca-ES) に対応 - Enhance: 個別お知らせページではMetaタグを出力するように - Enhance: ノート詳細画面にロールのバッジを表示 +- Enhance: 過去に送信したフォローリクエストを確認できるように + (Based on https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/663) - Fix: 通知の範囲指定の設定項目が必要ない通知設定でも範囲指定の設定がでている問題を修正 - Fix: Turnstileが失敗・期限切れした際にも成功扱いとなってしまう問題を修正 (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/768) diff --git a/locales/index.d.ts b/locales/index.d.ts index 440f24ac84..18fbfd15f0 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -10579,6 +10579,16 @@ export interface Locale extends ILocale { */ "description3": ParameterizedString<"link">; }; + "_followRequest": { + /** + * 受け取った申請 + */ + "recieved": string; + /** + * 送った申請 + */ + "sent": string; + }; } declare const locales: { [lang: string]: Locale; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 5d8e1a5e72..439ae708c5 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2819,3 +2819,7 @@ _selfXssPrevention: description1: "ここに何かを貼り付けると、悪意のあるユーザーにアカウントを乗っ取られたり、個人情報を盗まれたりする可能性があります。" description2: "貼り付けようとしているものが何なのかを正確に理解していない場合は、%c今すぐ作業を中止してこのウィンドウを閉じてください。" description3: "詳しくはこちらをご確認ください。 {link}" + +_followRequest: + recieved: "受け取った申請" + sent: "送った申請" diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 3557fa40a5..5bb194313d 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -187,6 +187,7 @@ import * as ep___following_invalidate from './endpoints/following/invalidate.js' import * as ep___following_requests_accept from './endpoints/following/requests/accept.js'; import * as ep___following_requests_cancel from './endpoints/following/requests/cancel.js'; import * as ep___following_requests_list from './endpoints/following/requests/list.js'; +import * as ep___following_requests_sent from './endpoints/following/requests/sent.js'; import * as ep___following_requests_reject from './endpoints/following/requests/reject.js'; import * as ep___gallery_featured from './endpoints/gallery/featured.js'; import * as ep___gallery_popular from './endpoints/gallery/popular.js'; @@ -574,6 +575,7 @@ const $following_invalidate: Provider = { provide: 'ep:following/invalidate', us const $following_requests_accept: Provider = { provide: 'ep:following/requests/accept', useClass: ep___following_requests_accept.default }; const $following_requests_cancel: Provider = { provide: 'ep:following/requests/cancel', useClass: ep___following_requests_cancel.default }; const $following_requests_list: Provider = { provide: 'ep:following/requests/list', useClass: ep___following_requests_list.default }; +const $following_requests_sent: Provider = { provide: 'ep:following/requests/sent', useClass: ep___following_requests_sent.default }; const $following_requests_reject: Provider = { provide: 'ep:following/requests/reject', useClass: ep___following_requests_reject.default }; const $gallery_featured: Provider = { provide: 'ep:gallery/featured', useClass: ep___gallery_featured.default }; const $gallery_popular: Provider = { provide: 'ep:gallery/popular', useClass: ep___gallery_popular.default }; @@ -965,6 +967,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ $following_requests_accept, $following_requests_cancel, $following_requests_list, + $following_requests_sent, $following_requests_reject, $gallery_featured, $gallery_popular, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 49b07d6ced..15809b2678 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -193,6 +193,7 @@ import * as ep___following_invalidate from './endpoints/following/invalidate.js' import * as ep___following_requests_accept from './endpoints/following/requests/accept.js'; import * as ep___following_requests_cancel from './endpoints/following/requests/cancel.js'; import * as ep___following_requests_list from './endpoints/following/requests/list.js'; +import * as ep___following_requests_sent from './endpoints/following/requests/sent.js'; import * as ep___following_requests_reject from './endpoints/following/requests/reject.js'; import * as ep___gallery_featured from './endpoints/gallery/featured.js'; import * as ep___gallery_popular from './endpoints/gallery/popular.js'; @@ -578,6 +579,7 @@ const eps = [ ['following/requests/accept', ep___following_requests_accept], ['following/requests/cancel', ep___following_requests_cancel], ['following/requests/list', ep___following_requests_list], + ['following/requests/sent', ep___following_requests_sent], ['following/requests/reject', ep___following_requests_reject], ['gallery/featured', ep___gallery_featured], ['gallery/popular', ep___gallery_popular], diff --git a/packages/backend/src/server/api/endpoints/following/requests/sent.ts b/packages/backend/src/server/api/endpoints/following/requests/sent.ts new file mode 100644 index 0000000000..6325f01bb8 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/following/requests/sent.ts @@ -0,0 +1,77 @@ +/* + * 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 { QueryService } from '@/core/QueryService.js'; +import type { FollowRequestsRepository } from '@/models/_.js'; +import { FollowRequestEntityService } from '@/core/entities/FollowRequestEntityService.js'; +import { DI } from '@/di-symbols.js'; + +export const meta = { + tags: ['following', 'account'], + + requireCredential: true, + + kind: 'read:following', + + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + follower: { + type: 'object', + optional: false, nullable: false, + ref: 'UserLite', + }, + followee: { + type: 'object', + optional: false, nullable: false, + ref: 'UserLite', + }, + }, + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + }, + required: [], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.followRequestsRepository) + private followRequestsRepository: FollowRequestsRepository, + + private followRequestEntityService: FollowRequestEntityService, + private queryService: QueryService, + ) { + super(meta, paramDef, async (ps, me) => { + const query = this.queryService.makePaginationQuery(this.followRequestsRepository.createQueryBuilder('request'), ps.sinceId, ps.untilId) + .andWhere('request.followerId = :meId', { meId: me.id }); + + const requests = await query + .limit(ps.limit) + .getMany(); + + return await this.followRequestEntityService.packMany(requests, me); + }); + } +} diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue index cc07175907..c1dc67f776 100644 --- a/packages/frontend/src/components/MkFollowButton.vue +++ b/packages/frontend/src/components/MkFollowButton.vue @@ -91,7 +91,10 @@ async function onClick() { text: i18n.tsx.unfollowConfirm({ name: props.user.name || props.user.username }), }); - if (canceled) return; + if (canceled) { + wait.value = false; + return; + } await misskeyApi('following/delete', { userId: props.user.id, @@ -125,7 +128,10 @@ async function onClick() { }); hasPendingFollowRequestFromYou.value = true; - if ($i == null) return; + if ($i == null) { + wait.value = false; + return; + } claimAchievement('following1'); diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts index ac730f8021..096d404a57 100644 --- a/packages/frontend/src/navbar.ts +++ b/packages/frontend/src/navbar.ts @@ -40,7 +40,6 @@ export const navbarItemDef = reactive({ followRequests: { title: i18n.ts.followRequests, icon: 'ti ti-user-plus', - show: computed(() => $i != null && $i.isLocked), indicated: computed(() => $i != null && $i.hasPendingReceivedFollowRequest), to: '/my/follow-requests', }, diff --git a/packages/frontend/src/pages/follow-requests.vue b/packages/frontend/src/pages/follow-requests.vue index a840d0d0b3..8688863c2c 100644 --- a/packages/frontend/src/pages/follow-requests.vue +++ b/packages/frontend/src/pages/follow-requests.vue @@ -5,69 +5,104 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/frontend/src/components/MkSchedulePostListDialog.vue b/packages/frontend/src/components/MkSchedulePostListDialog.vue new file mode 100644 index 0000000000..cf793c7110 --- /dev/null +++ b/packages/frontend/src/components/MkSchedulePostListDialog.vue @@ -0,0 +1,60 @@ + + + + + diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index e73aa77a3c..deb629d534 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -733,3 +733,4 @@ export function checkExistence(fileData: ArrayBuffer): Promise { }); }); }*/ + diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index 1763db2323..5d896db98c 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -200,6 +200,25 @@ SPDX-License-Identifier: AGPL-3.0-only + + + +
+ + + + + + + + +
+
+ + {{ i18n.tsx._notification.renotedBySomeUsers({ n: notification.users.length }) }} {{ notification.header }} {{ i18n.ts._notification.edited }} + {{ i18n.ts._notification.scheduledNoteFailed }}
@@ -109,6 +112,9 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.showFile }} +
+ {{ notification.reason }} +
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 443e9e7ee9..bbde7c65f9 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -1046,8 +1046,9 @@ function openAccountMenu(ev: MouseEvent) { } function toggleScheduleNote() { - if (scheduleNote.value) scheduleNote.value = null; - else { + if (scheduleNote.value) { + scheduleNote.value = null; + } else { scheduleNote.value = { scheduledAt: null, }; diff --git a/packages/frontend/src/components/MkSchedulePostListDialog.vue b/packages/frontend/src/components/MkSchedulePostListDialog.vue index cfae94951b..d0716ead79 100644 --- a/packages/frontend/src/components/MkSchedulePostListDialog.vue +++ b/packages/frontend/src/components/MkSchedulePostListDialog.vue @@ -48,6 +48,7 @@ const cancel = () => { emit('cancel'); dialogEl.value.close(); }; + const paginationEl = ref(); const pagination: Paging = { endpoint: 'notes/schedule/list', diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index ca7a374a67..880be518fa 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -2890,7 +2890,7 @@ type Notification_2 = components['schemas']['Notification']; type NotificationsCreateRequest = operations['notifications___create']['requestBody']['content']['application/json']; // @public (undocumented) -export const notificationTypes: readonly ["note", "follow", "mention", "reply", "renote", "quote", "reaction", "pollVote", "pollEnded", "receiveFollowRequest", "followRequestAccepted", "groupInvited", "app", "roleAssigned", "achievementEarned", "edited"]; +export const notificationTypes: readonly ["note", "follow", "mention", "reply", "renote", "quote", "reaction", "pollVote", "pollEnded", "receiveFollowRequest", "followRequestAccepted", "groupInvited", "app", "roleAssigned", "achievementEarned", "edited", "scheduledNoteFailed"]; // @public (undocumented) export function nyaize(text: string): string; diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index c8d7194405..6eb1819037 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -4517,6 +4517,14 @@ export type components = { /** Format: id */ userId: string; note: components['schemas']['Note']; + } | { + /** Format: id */ + id: string; + /** Format: date-time */ + createdAt: string; + /** @enum {string} */ + type: 'scheduledNoteFailed'; + reason: string; } | { /** Format: id */ id: string; @@ -19984,8 +19992,8 @@ export type operations = { untilId?: string; /** @default true */ markAsRead?: boolean; - includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'edited' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'exportCompleted' | 'app' | 'test' | 'pollVote' | 'groupInvited')[]; - excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'edited' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'exportCompleted' | 'app' | 'test' | 'pollVote' | 'groupInvited')[]; + includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'edited' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'exportCompleted' | 'scheduledNoteFailed' | 'app' | 'test' | 'pollVote' | 'groupInvited')[]; + excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'edited' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'exportCompleted' | 'scheduledNoteFailed' | 'app' | 'test' | 'pollVote' | 'groupInvited')[]; }; }; }; @@ -20052,8 +20060,8 @@ export type operations = { untilId?: string; /** @default true */ markAsRead?: boolean; - includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'edited' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'exportCompleted' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped' | 'pollVote' | 'groupInvited')[]; - excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'edited' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'exportCompleted' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped' | 'pollVote' | 'groupInvited')[]; + includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'edited' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'exportCompleted' | 'scheduledNoteFailed' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped' | 'pollVote' | 'groupInvited')[]; + excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'edited' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'exportCompleted' | 'scheduledNoteFailed' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped' | 'pollVote' | 'groupInvited')[]; }; }; }; diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts index d090a6b46f..34fc7c1a03 100644 --- a/packages/misskey-js/src/consts.ts +++ b/packages/misskey-js/src/consts.ts @@ -16,7 +16,7 @@ import type { UserLite, } from './autogen/models.js'; -export const notificationTypes = ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app', 'roleAssigned', 'achievementEarned', 'edited'] as const; +export const notificationTypes = ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app', 'roleAssigned', 'achievementEarned', 'edited', 'scheduledNoteFailed'] as const; export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const; diff --git a/packages/sw/src/scripts/create-notification.ts b/packages/sw/src/scripts/create-notification.ts index 9c56e338c7..8442552e3b 100644 --- a/packages/sw/src/scripts/create-notification.ts +++ b/packages/sw/src/scripts/create-notification.ts @@ -258,6 +258,13 @@ async function composeNotification(data: PushNotificationDataMap[keyof PushNotif data, }]; + case 'scheduledNoteFailed': + return [i18n.ts._notification.scheduledNoteFailed, { + body: data.body.reason, + badge: iconUrl('bell'), + data, + }]; + default: return null; } diff --git a/sharkey-locales/en-US.yml b/sharkey-locales/en-US.yml index 79c362cfd8..582f4eda0a 100644 --- a/sharkey-locales/en-US.yml +++ b/sharkey-locales/en-US.yml @@ -277,6 +277,7 @@ _notification: youRenoted: "Boost from {name}" renotedBySomeUsers: "Boosted by {n} users" edited: "Note got edited" + scheduledNoteFailed: "Posting scheduled note failed" _types: renote: "Boosts" edited: "Edits" -- cgit v1.2.3-freya From 152cc074831b784bb1e12267587184cea293a186 Mon Sep 17 00:00:00 2001 From: Marie Date: Mon, 9 Dec 2024 05:58:25 +0100 Subject: Apply suggestions --- .../src/queue/processors/ScheduleNotePostProcessorService.ts | 7 +++++++ packages/backend/src/server/api/endpoints/notes/schedule/create.ts | 2 +- packages/frontend/src/components/MkPostForm.vue | 4 +--- 3 files changed, 9 insertions(+), 4 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts b/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts index f281b0ed7b..ea43448ed0 100644 --- a/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts +++ b/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts @@ -10,6 +10,7 @@ import { NoteCreateService } from '@/core/NoteCreateService.js'; import type { ChannelsRepository, DriveFilesRepository, MiDriveFile, NoteScheduleRepository, NotesRepository, UsersRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { NotificationService } from '@/core/NotificationService.js'; +import { IdentifiableError } from '@/misc/identifiable-error.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type * as Bull from 'bullmq'; import type { ScheduleNotePostJobData } from '../types.js'; @@ -119,6 +120,12 @@ export class ScheduleNotePostProcessorService { reply, renote, channel, + }).catch(async (err: IdentifiableError) => { + this.notificationService.createNotification(me.id, 'scheduledNoteFailed', { + reason: err.message, + }); + await this.noteScheduleRepository.remove(data); + throw this.logger.error(`Schedule Note Failed Reason: ${err.message}`); }); await this.noteScheduleRepository.remove(data); this.notificationService.createNotification(me.id, 'scheduledNotePosted', { diff --git a/packages/backend/src/server/api/endpoints/notes/schedule/create.ts b/packages/backend/src/server/api/endpoints/notes/schedule/create.ts index b8ae3f44a3..7d20b6b82a 100644 --- a/packages/backend/src/server/api/endpoints/notes/schedule/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/schedule/create.ts @@ -360,7 +360,7 @@ export default class extends Endpoint { // eslint- }, { delay, removeOnComplete: true, - jobId: noteId, + jobId: `schedNote:${noteId}`, }); } diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index bbde7c65f9..c7d5611847 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -821,7 +821,7 @@ async function post(ev?: MouseEvent) { const filesData = toRaw(files.value); const isMissingAltText = filesData.filter( - file => file.type.startsWith('image/') || file.type.startsWith('video/') || file.type.startsWith('audio/') + file => file.type.startsWith('image/') || file.type.startsWith('video/') || file.type.startsWith('audio/'), ).some(file => !file.comment); if (isMissingAltText) { @@ -914,8 +914,6 @@ async function post(ev?: MouseEvent) { claimAchievement('notes1'); } - poll.value = null; - const text = postData.text ?? ''; const lowerCase = text.toLowerCase(); if ((lowerCase.includes('love') || lowerCase.includes('❤')) && lowerCase.includes('sharkey')) { -- cgit v1.2.3-freya From f02d0994132c5774f3adf0d4a993f4ecca549b77 Mon Sep 17 00:00:00 2001 From: Marie Date: Mon, 9 Dec 2024 06:10:32 +0100 Subject: fix deletion of scheduled note --- packages/backend/src/server/api/endpoints/notes/schedule/delete.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/notes/schedule/delete.ts b/packages/backend/src/server/api/endpoints/notes/schedule/delete.ts index df406f99f0..628fd89926 100644 --- a/packages/backend/src/server/api/endpoints/notes/schedule/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/schedule/delete.ts @@ -61,7 +61,7 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.permissionDenied); } await this.noteScheduleRepository.delete({ id: ps.noteId }); - await this.queueService.ScheduleNotePostQueue.remove(ps.noteId); + await this.queueService.ScheduleNotePostQueue.remove(`schedNote:${ps.noteId}`); }); } } -- cgit v1.2.3-freya From 0f5c78a69bf9ccf645b981e6ec844878feafd36c Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Wed, 11 Dec 2024 09:10:56 -0500 Subject: increase chart rate limits (fixes 429s in control panel / info pages) --- packages/backend/src/server/api/endpoints/charts/active-users.ts | 7 ++++--- packages/backend/src/server/api/endpoints/charts/ap-request.ts | 7 ++++--- packages/backend/src/server/api/endpoints/charts/drive.ts | 7 ++++--- packages/backend/src/server/api/endpoints/charts/federation.ts | 7 ++++--- packages/backend/src/server/api/endpoints/charts/instance.ts | 7 ++++--- packages/backend/src/server/api/endpoints/charts/notes.ts | 7 ++++--- packages/backend/src/server/api/endpoints/charts/user/drive.ts | 7 ++++--- packages/backend/src/server/api/endpoints/charts/user/following.ts | 7 ++++--- packages/backend/src/server/api/endpoints/charts/user/notes.ts | 7 ++++--- packages/backend/src/server/api/endpoints/charts/user/pv.ts | 7 ++++--- packages/backend/src/server/api/endpoints/charts/user/reactions.ts | 7 ++++--- packages/backend/src/server/api/endpoints/charts/users.ts | 7 ++++--- 12 files changed, 48 insertions(+), 36 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/charts/active-users.ts b/packages/backend/src/server/api/endpoints/charts/active-users.ts index f6c0c045df..dcdcf46d0b 100644 --- a/packages/backend/src/server/api/endpoints/charts/active-users.ts +++ b/packages/backend/src/server/api/endpoints/charts/active-users.ts @@ -17,10 +17,11 @@ export const meta = { allowGet: true, cacheSec: 60 * 60, - // 10 calls per 5 seconds + // Burst up to 100, then 2/sec average limit: { - duration: 1000 * 5, - max: 10, + type: 'bucket', + size: 100, + dripRate: 500, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/charts/ap-request.ts b/packages/backend/src/server/api/endpoints/charts/ap-request.ts index 4c5c0d5d20..28c64229e7 100644 --- a/packages/backend/src/server/api/endpoints/charts/ap-request.ts +++ b/packages/backend/src/server/api/endpoints/charts/ap-request.ts @@ -17,10 +17,11 @@ export const meta = { allowGet: true, cacheSec: 60 * 60, - // 10 calls per 5 seconds + // Burst up to 100, then 2/sec average limit: { - duration: 1000 * 5, - max: 10, + type: 'bucket', + size: 100, + dripRate: 500, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/charts/drive.ts b/packages/backend/src/server/api/endpoints/charts/drive.ts index 8210ec8fe7..69ff3c5d7a 100644 --- a/packages/backend/src/server/api/endpoints/charts/drive.ts +++ b/packages/backend/src/server/api/endpoints/charts/drive.ts @@ -17,10 +17,11 @@ export const meta = { allowGet: true, cacheSec: 60 * 60, - // 10 calls per 5 seconds + // Burst up to 100, then 2/sec average limit: { - duration: 1000 * 5, - max: 10, + type: 'bucket', + size: 100, + dripRate: 500, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/charts/federation.ts b/packages/backend/src/server/api/endpoints/charts/federation.ts index 56a5dbea31..bd870cc3d9 100644 --- a/packages/backend/src/server/api/endpoints/charts/federation.ts +++ b/packages/backend/src/server/api/endpoints/charts/federation.ts @@ -17,10 +17,11 @@ export const meta = { allowGet: true, cacheSec: 60 * 60, - // 10 calls per 5 seconds + // Burst up to 100, then 2/sec average limit: { - duration: 1000 * 5, - max: 10, + type: 'bucket', + size: 100, + dripRate: 500, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/charts/instance.ts b/packages/backend/src/server/api/endpoints/charts/instance.ts index 7f79e1356d..765bf024ee 100644 --- a/packages/backend/src/server/api/endpoints/charts/instance.ts +++ b/packages/backend/src/server/api/endpoints/charts/instance.ts @@ -17,10 +17,11 @@ export const meta = { allowGet: true, cacheSec: 60 * 60, - // 10 calls per 5 seconds + // Burst up to 100, then 2/sec average limit: { - duration: 1000 * 5, - max: 10, + type: 'bucket', + size: 100, + dripRate: 500, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/charts/notes.ts b/packages/backend/src/server/api/endpoints/charts/notes.ts index b3660b558b..ecac436311 100644 --- a/packages/backend/src/server/api/endpoints/charts/notes.ts +++ b/packages/backend/src/server/api/endpoints/charts/notes.ts @@ -17,10 +17,11 @@ export const meta = { allowGet: true, cacheSec: 60 * 60, - // 10 calls per 5 seconds + // Burst up to 100, then 2/sec average limit: { - duration: 1000 * 5, - max: 10, + type: 'bucket', + size: 100, + dripRate: 500, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/charts/user/drive.ts b/packages/backend/src/server/api/endpoints/charts/user/drive.ts index 716c41f385..98ec40ade2 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/drive.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/drive.ts @@ -17,10 +17,11 @@ export const meta = { allowGet: true, cacheSec: 60 * 60, - // 10 calls per 5 seconds + // Burst up to 100, then 2/sec average limit: { - duration: 1000 * 5, - max: 10, + type: 'bucket', + size: 100, + dripRate: 500, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/charts/user/following.ts b/packages/backend/src/server/api/endpoints/charts/user/following.ts index b67b5ca338..cb3dd36bab 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/following.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/following.ts @@ -17,10 +17,11 @@ export const meta = { allowGet: true, cacheSec: 60 * 60, - // 10 calls per 5 seconds + // Burst up to 100, then 2/sec average limit: { - duration: 1000 * 5, - max: 10, + type: 'bucket', + size: 100, + dripRate: 500, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/charts/user/notes.ts b/packages/backend/src/server/api/endpoints/charts/user/notes.ts index e5587cab86..0742a21210 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/notes.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/notes.ts @@ -17,10 +17,11 @@ export const meta = { allowGet: true, cacheSec: 60 * 60, - // 10 calls per 5 seconds + // Burst up to 100, then 2/sec average limit: { - duration: 1000 * 5, - max: 10, + type: 'bucket', + size: 100, + dripRate: 500, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/charts/user/pv.ts b/packages/backend/src/server/api/endpoints/charts/user/pv.ts index cbae3a21c1..a220381b00 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/pv.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/pv.ts @@ -17,10 +17,11 @@ export const meta = { allowGet: true, cacheSec: 60 * 60, - // 10 calls per 5 seconds + // Burst up to 100, then 2/sec average limit: { - duration: 1000 * 5, - max: 10, + type: 'bucket', + size: 100, + dripRate: 500, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/charts/user/reactions.ts b/packages/backend/src/server/api/endpoints/charts/user/reactions.ts index d734240742..3bb33622c2 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/reactions.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/reactions.ts @@ -17,10 +17,11 @@ export const meta = { allowGet: true, cacheSec: 60 * 60, - // 10 calls per 5 seconds + // Burst up to 100, then 2/sec average limit: { - duration: 1000 * 5, - max: 10, + type: 'bucket', + size: 100, + dripRate: 500, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/charts/users.ts b/packages/backend/src/server/api/endpoints/charts/users.ts index 6e1a8ebd4f..b5452517ab 100644 --- a/packages/backend/src/server/api/endpoints/charts/users.ts +++ b/packages/backend/src/server/api/endpoints/charts/users.ts @@ -17,10 +17,11 @@ export const meta = { allowGet: true, cacheSec: 60 * 60, - // 10 calls per 5 seconds + // Burst up to 100, then 2/sec average limit: { - duration: 1000 * 5, - max: 10, + type: 'bucket', + size: 100, + dripRate: 500, }, } as const; -- cgit v1.2.3-freya From 00c4637b1186fa78a3955e8b5103c05c2e2b4431 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Thu, 12 Dec 2024 07:34:14 -0500 Subject: federate profile when `hideOnlineStatus` changes --- packages/backend/src/server/api/endpoints/i/update.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 8e61b8f784..c640caee75 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -587,7 +587,7 @@ export default class extends Endpoint { // eslint- // these two methods need to be kept in sync with // `ApRendererService.renderPerson` private userNeedsPublishing(oldUser: MiLocalUser, newUser: Partial): boolean { - for (const field of ['avatarId', 'bannerId', 'backgroundId', 'isBot', 'username', 'name', 'isLocked', 'isExplorable', 'isCat', 'noindex', 'speakAsCat', 'movedToUri', 'alsoKnownAs'] as (keyof MiUser)[]) { + for (const field of ['avatarId', 'bannerId', 'backgroundId', 'isBot', 'username', 'name', 'isLocked', 'isExplorable', 'isCat', 'noindex', 'speakAsCat', 'movedToUri', 'alsoKnownAs', 'hideOnlineStatus'] as (keyof MiUser)[]) { if ((field in newUser) && oldUser[field] !== newUser[field]) { return true; } -- cgit v1.2.3-freya From fe37aa2ce851dfba4cb7b47ee569d20272b7f75e Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Mon, 9 Dec 2024 09:11:39 -0500 Subject: Add "enable RSS" user privacy toggle --- locales/index.d.ts | 8 ++++++++ .../backend/migration/1733748798177-add_user_enableRss.js | 13 +++++++++++++ packages/backend/src/core/SignupService.ts | 1 + packages/backend/src/core/activitypub/ApRendererService.ts | 1 + packages/backend/src/core/activitypub/misc/contexts.ts | 1 + .../backend/src/core/activitypub/models/ApPersonService.ts | 2 ++ packages/backend/src/core/activitypub/type.ts | 1 + packages/backend/src/core/entities/UserEntityService.ts | 1 + packages/backend/src/models/User.ts | 11 +++++++++++ packages/backend/src/models/json-schema/user.ts | 4 ++++ packages/backend/src/server/api/endpoints/i/update.ts | 2 ++ packages/backend/src/server/web/ClientServerService.ts | 1 + packages/frontend/src/pages/settings/privacy.vue | 6 ++++++ packages/misskey-js/src/autogen/types.ts | 2 ++ sharkey-locales/en-US.yml | 2 ++ 15 files changed, 56 insertions(+) create mode 100644 packages/backend/migration/1733748798177-add_user_enableRss.js (limited to 'packages/backend/src/server/api/endpoints') diff --git a/locales/index.d.ts b/locales/index.d.ts index 7ab1b7f545..3ffa402598 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -10847,6 +10847,14 @@ export interface Locale extends ILocale { * Stop note search from indexing your public notes. */ "makeIndexableDescription": string; + /** + * Enable RSS feed + */ + "enableRss": string; + /** + * Generate an RSS feed containing your basic profile details and public notes. Users can subscribe to the feed without a follow request or approval. + */ + "enableRssDescription": string; /** * Require approval for new users */ diff --git a/packages/backend/migration/1733748798177-add_user_enableRss.js b/packages/backend/migration/1733748798177-add_user_enableRss.js new file mode 100644 index 0000000000..64662ca7b8 --- /dev/null +++ b/packages/backend/migration/1733748798177-add_user_enableRss.js @@ -0,0 +1,13 @@ +export class AddUserEnableRss1733748798177 { + name = 'AddUserEnableRss1733748798177' + + async up(queryRunner) { + // Disable by default, then specifically enable for all existing local users. + await queryRunner.query(`ALTER TABLE "user" ADD "enable_rss" boolean NOT NULL DEFAULT false`); + await queryRunner.query(`UPDATE "user" SET "enable_rss" = true WHERE host IS NULL;`) + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "enable_rss"`); + } +} diff --git a/packages/backend/src/core/SignupService.ts b/packages/backend/src/core/SignupService.ts index 1b0b1e5bbd..3f0523b610 100644 --- a/packages/backend/src/core/SignupService.ts +++ b/packages/backend/src/core/SignupService.ts @@ -135,6 +135,7 @@ export class SignupService { isRoot: isTheFirstUser, approved: isTheFirstUser || (opts.approved ?? !this.meta.approvalRequiredForSignup), signupReason: reason, + enableRss: false, })); await transactionalEntityManager.save(new MiUserKeypair({ diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index ff909778e8..57489c754f 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -531,6 +531,7 @@ export class ApRendererService { hideOnlineStatus: user.hideOnlineStatus, noindex: user.noindex, indexable: !user.noindex, + enableRss: user.enableRss, speakAsCat: user.speakAsCat, attachment: attachment.length ? attachment : undefined, }; diff --git a/packages/backend/src/core/activitypub/misc/contexts.ts b/packages/backend/src/core/activitypub/misc/contexts.ts index 1c4239502e..9c2640758f 100644 --- a/packages/backend/src/core/activitypub/misc/contexts.ts +++ b/packages/backend/src/core/activitypub/misc/contexts.ts @@ -567,6 +567,7 @@ const extension_context_definition = { hideOnlineStatus: 'sharkey:hideOnlineStatus', backgroundUrl: 'sharkey:backgroundUrl', listenbrainz: 'sharkey:listenbrainz', + enableRss: 'sharkey:enableRss', // vcard vcard: 'http://www.w3.org/2006/vcard/ns#', } satisfies Context; diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index 2cb31b1f09..b1bd379861 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -385,6 +385,7 @@ export class ApPersonService implements OnModuleInit { lastFetchedAt: new Date(), name: truncate(person.name, nameLength), noindex: (person as any).noindex ?? false, + enableRss: person.enableRss === true, isLocked: person.manuallyApprovesFollowers, movedToUri: person.movedTo, movedAt: person.movedTo ? new Date() : null, @@ -584,6 +585,7 @@ export class ApPersonService implements OnModuleInit { isCat: (person as any).isCat === true, speakAsCat: (person as any).speakAsCat != null ? (person as any).speakAsCat === true : (person as any).isCat === true, noindex: (person as any).noindex ?? false, + enableRss: person.enableRss === true, isLocked: person.manuallyApprovesFollowers, movedToUri: person.movedTo ?? null, alsoKnownAs: person.alsoKnownAs ?? null, diff --git a/packages/backend/src/core/activitypub/type.ts b/packages/backend/src/core/activitypub/type.ts index a0a5ae00dc..6da382e3ec 100644 --- a/packages/backend/src/core/activitypub/type.ts +++ b/packages/backend/src/core/activitypub/type.ts @@ -217,6 +217,7 @@ export interface IActor extends IObject { 'vcard:Address'?: string; hideOnlineStatus?: boolean; noindex?: boolean; + enableRss?: boolean; listenbrainz?: string; backgroundUrl?: string; } diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index b1832ca0f5..8a48c8c6b4 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -539,6 +539,7 @@ export class UserEntityService implements OnModuleInit { isBot: user.isBot, isCat: user.isCat, noindex: user.noindex, + enableRss: user.enableRss, isSilenced: user.isSilenced || this.roleService.getUserPolicies(user.id).then(r => !r.canPublicNote), speakAsCat: user.speakAsCat ?? false, approved: user.approved, diff --git a/packages/backend/src/models/User.ts b/packages/backend/src/models/User.ts index 73a44de558..8420b5b129 100644 --- a/packages/backend/src/models/User.ts +++ b/packages/backend/src/models/User.ts @@ -311,6 +311,17 @@ export class MiUser { }) public signupReason: string | null; + /** + * True if profile RSS feeds are enabled for this user. + * Enabled by default (opt-out) for existing users, to avoid breaking any existing feeds. + * Disabled by default (opt-in) for newly created users, for privacy. + */ + @Column('boolean', { + name: 'enable_rss', + default: true, + }) + public enableRss = true; + constructor(data: Partial) { if (data == null) return; diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index d5e847cc40..12ed1f2009 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -130,6 +130,10 @@ export const packedUserLiteSchema = { type: 'boolean', nullable: false, optional: false, }, + enableRss: { + type: 'boolean', + nullable: false, optional: false, + }, isBot: { type: 'boolean', nullable: false, optional: true, diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index c640caee75..cae2964628 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -187,6 +187,7 @@ export const paramDef = { noCrawle: { type: 'boolean' }, preventAiLearning: { type: 'boolean' }, noindex: { type: 'boolean' }, + enableRss: { type: 'boolean' }, isBot: { type: 'boolean' }, isCat: { type: 'boolean' }, speakAsCat: { type: 'boolean' }, @@ -337,6 +338,7 @@ export default class extends Endpoint { // eslint- if (typeof ps.hideOnlineStatus === 'boolean') updates.hideOnlineStatus = ps.hideOnlineStatus; if (typeof ps.publicReactions === 'boolean') profileUpdates.publicReactions = ps.publicReactions; if (typeof ps.noindex === 'boolean') updates.noindex = ps.noindex; + if (typeof ps.enableRss === 'boolean') updates.enableRss = ps.enableRss; if (typeof ps.isBot === 'boolean') updates.isBot = ps.isBot; if (typeof ps.carefulBot === 'boolean') profileUpdates.carefulBot = ps.carefulBot; if (typeof ps.autoAcceptFollowed === 'boolean') profileUpdates.autoAcceptFollowed = ps.autoAcceptFollowed; diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index aca98c4d37..42f9b104ff 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -510,6 +510,7 @@ export class ClientServerService { usernameLower: username.toLowerCase(), host: host ?? IsNull(), isSuspended: false, + enableRss: true, }); return user && await this.feedService.packFeed(user); diff --git a/packages/frontend/src/pages/settings/privacy.vue b/packages/frontend/src/pages/settings/privacy.vue index b155d6e316..dccfb584ae 100644 --- a/packages/frontend/src/pages/settings/privacy.vue +++ b/packages/frontend/src/pages/settings/privacy.vue @@ -43,6 +43,10 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.makeExplorable }} + + {{ i18n.ts.enableRss }} + +
@@ -89,6 +93,7 @@ const isLocked = ref($i.isLocked); const autoAcceptFollowed = ref($i.autoAcceptFollowed); const noCrawle = ref($i.noCrawle); const noindex = ref($i.noindex); +const enableRss = ref($i.enableRss); const isExplorable = ref($i.isExplorable); const hideOnlineStatus = ref($i.hideOnlineStatus); const publicReactions = ref($i.publicReactions); @@ -106,6 +111,7 @@ function save() { autoAcceptFollowed: !!autoAcceptFollowed.value, noCrawle: !!noCrawle.value, noindex: !!noindex.value, + enableRss: !!enableRss.value, isExplorable: !!isExplorable.value, hideOnlineStatus: !!hideOnlineStatus.value, publicReactions: !!publicReactions.value, diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 29c2e814d2..b161698f27 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -3913,6 +3913,7 @@ export type components = { /** @default false */ isSystem?: boolean; noindex: boolean; + enableRss: boolean; isBot?: boolean; isCat?: boolean; speakAsCat?: boolean; @@ -21320,6 +21321,7 @@ export type operations = { noCrawle?: boolean; preventAiLearning?: boolean; noindex?: boolean; + enableRss?: boolean; isBot?: boolean; isCat?: boolean; speakAsCat?: boolean; diff --git a/sharkey-locales/en-US.yml b/sharkey-locales/en-US.yml index f15646a156..4e462edda7 100644 --- a/sharkey-locales/en-US.yml +++ b/sharkey-locales/en-US.yml @@ -88,6 +88,8 @@ searchEngineCustomURIDescription: "The custom URI must be input in the format li searchEngineCusomURI: "Custom URI" makeIndexable: "Make public notes not indexable" makeIndexableDescription: "Stop note search from indexing your public notes." +enableRss: "Enable RSS feed" +enableRssDescription: "Generate an RSS feed containing your basic profile details and public notes. Users can subscribe to the feed without a follow request or approval." sendErrorReportsDescription: "When turned on, detailed error information will be shared with Sharkey when a problem occurs, helping to improve the quality of Sharkey.\nThis will include information such the version of your OS, what browser you're using, your activity in Sharkey, etc." noInquiryUrlWarning: "Contact URL is not set." misskeyUpdated: "Sharkey has been updated!" -- cgit v1.2.3-freya From 02b600c9dae3e4f461fef884e061468d8b647b63 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Thu, 12 Dec 2024 07:29:02 -0500 Subject: federate profile when changing `enableRss` value --- packages/backend/src/server/api/endpoints/i/update.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index cae2964628..91f871a952 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -589,7 +589,7 @@ export default class extends Endpoint { // eslint- // these two methods need to be kept in sync with // `ApRendererService.renderPerson` private userNeedsPublishing(oldUser: MiLocalUser, newUser: Partial): boolean { - for (const field of ['avatarId', 'bannerId', 'backgroundId', 'isBot', 'username', 'name', 'isLocked', 'isExplorable', 'isCat', 'noindex', 'speakAsCat', 'movedToUri', 'alsoKnownAs', 'hideOnlineStatus'] as (keyof MiUser)[]) { + for (const field of ['avatarId', 'bannerId', 'backgroundId', 'isBot', 'username', 'name', 'isLocked', 'isExplorable', 'isCat', 'noindex', 'speakAsCat', 'movedToUri', 'alsoKnownAs', 'hideOnlineStatus', 'enableRss'] as (keyof MiUser)[]) { if ((field in newUser) && oldUser[field] !== newUser[field]) { return true; } -- cgit v1.2.3-freya From 1c65f234458c5ce6f67280bc2aa7b4e04f1aa671 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Thu, 12 Dec 2024 07:31:11 -0500 Subject: safer typings for `userNeedsPublishing` and `profileNeedsPublishing` --- packages/backend/src/server/api/endpoints/i/update.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 91f871a952..c22cbe5d4b 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -589,12 +589,15 @@ export default class extends Endpoint { // eslint- // these two methods need to be kept in sync with // `ApRendererService.renderPerson` private userNeedsPublishing(oldUser: MiLocalUser, newUser: Partial): boolean { - for (const field of ['avatarId', 'bannerId', 'backgroundId', 'isBot', 'username', 'name', 'isLocked', 'isExplorable', 'isCat', 'noindex', 'speakAsCat', 'movedToUri', 'alsoKnownAs', 'hideOnlineStatus', 'enableRss'] as (keyof MiUser)[]) { + const basicFields: (keyof MiUser)[] = ['avatarId', 'bannerId', 'backgroundId', 'isBot', 'username', 'name', 'isLocked', 'isExplorable', 'isCat', 'noindex', 'speakAsCat', 'movedToUri', 'alsoKnownAs', 'hideOnlineStatus', 'enableRss']; + for (const field of basicFields) { if ((field in newUser) && oldUser[field] !== newUser[field]) { return true; } } - for (const arrayField of ['emojis', 'tags'] as (keyof MiUser)[]) { + + const arrayFields: (keyof MiUser)[] = ['emojis', 'tags']; + for (const arrayField of arrayFields) { if ((arrayField in newUser) !== (arrayField in oldUser)) { return true; } @@ -604,7 +607,7 @@ export default class extends Endpoint { // eslint- if (!Array.isArray(oldArray) || !Array.isArray(newArray)) { return true; } - if (oldArray.join("\0") !== newArray.join("\0")) { + if (oldArray.join('\0') !== newArray.join('\0')) { return true; } } @@ -612,12 +615,15 @@ export default class extends Endpoint { // eslint- } private profileNeedsPublishing(oldProfile: MiUserProfile, newProfile: Partial): boolean { - for (const field of ['description', 'followedMessage', 'birthday', 'location', 'listenbrainz'] as (keyof MiUserProfile)[]) { + const basicFields: (keyof MiUserProfile)[] = ['description', 'followedMessage', 'birthday', 'location', 'listenbrainz']; + for (const field of basicFields) { if ((field in newProfile) && oldProfile[field] !== newProfile[field]) { return true; } } - for (const arrayField of ['fields'] as (keyof MiUserProfile)[]) { + + const arrayFields: (keyof MiUserProfile)[] = ['fields']; + for (const arrayField of arrayFields) { if ((arrayField in newProfile) !== (arrayField in oldProfile)) { return true; } @@ -627,7 +633,7 @@ export default class extends Endpoint { // eslint- if (!Array.isArray(oldArray) || !Array.isArray(newArray)) { return true; } - if (oldArray.join("\0") !== newArray.join("\0")) { + if (oldArray.join('\0') !== newArray.join('\0')) { return true; } } -- cgit v1.2.3-freya From e9f68ab2b03bda4aa972f917f26e53d10f7058b7 Mon Sep 17 00:00:00 2001 From: dakkar Date: Thu, 12 Dec 2024 17:40:22 +0000 Subject: actually publish profile updates with changes to new mk fields I had forgotten to check these --- packages/backend/src/server/api/endpoints/i/update.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index fe28b50338..bb0f5aa11a 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -621,7 +621,7 @@ export default class extends Endpoint { // eslint- // these two methods need to be kept in sync with // `ApRendererService.renderPerson` private userNeedsPublishing(oldUser: MiLocalUser, newUser: Partial): boolean { - const basicFields: (keyof MiUser)[] = ['avatarId', 'bannerId', 'backgroundId', 'isBot', 'username', 'name', 'isLocked', 'isExplorable', 'isCat', 'noindex', 'speakAsCat', 'movedToUri', 'alsoKnownAs', 'hideOnlineStatus', 'enableRss']; + const basicFields: (keyof MiUser)[] = ['avatarId', 'bannerId', 'backgroundId', 'isBot', 'username', 'name', 'isLocked', 'isExplorable', 'isCat', 'noindex', 'speakAsCat', 'movedToUri', 'alsoKnownAs', 'hideOnlineStatus', 'enableRss', 'requireSigninToViewContents', 'makeNotesFollowersOnlyBefore', 'makeNotesHiddenBefore']; for (const field of basicFields) { if ((field in newUser) && oldUser[field] !== newUser[field]) { return true; -- cgit v1.2.3-freya From e50ff9db6ae91e1ad34bc50935300515841bf719 Mon Sep 17 00:00:00 2001 From: Marie Date: Sun, 15 Dec 2024 22:41:16 +0100 Subject: upd: make schedule time work cross timezones --- packages/backend/package.json | 2 +- .../server/api/endpoints/notes/schedule/create.ts | 10 ++-- packages/frontend/package.json | 1 + .../frontend/src/components/MkScheduleEditor.vue | 3 +- pnpm-lock.yaml | 54 ++++++++-------------- 5 files changed, 28 insertions(+), 42 deletions(-) (limited to 'packages/backend/src/server/api/endpoints') diff --git a/packages/backend/package.json b/packages/backend/package.json index 702b788061..dd6c9cc792 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -141,11 +141,11 @@ "juice": "11.0.0", "megalodon": "workspace:*", "meilisearch": "0.45.0", - "juice": "11.0.0", "microformats-parser": "2.0.2", "mime-types": "2.1.35", "misskey-js": "workspace:*", "misskey-reversi": "workspace:*", + "moment": "^2.30.1", "ms": "3.0.0-canary.1", "nanoid": "5.0.8", "nested-property": "4.0.0", diff --git a/packages/backend/src/server/api/endpoints/notes/schedule/create.ts b/packages/backend/src/server/api/endpoints/notes/schedule/create.ts index 7d20b6b82a..c6032fbdae 100644 --- a/packages/backend/src/server/api/endpoints/notes/schedule/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/schedule/create.ts @@ -6,6 +6,7 @@ import ms from 'ms'; import { In } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; +import moment from 'moment'; import { isPureRenote } from '@/misc/is-renote.js'; import type { MiUser } from '@/models/User.js'; import type { @@ -307,7 +308,7 @@ export default class extends Endpoint { // eslint- if (ps.poll) { let scheduleNote_scheduledAt = Date.now(); if (typeof ps.scheduleNote.scheduledAt === 'number') { - scheduleNote_scheduledAt = ps.scheduleNote.scheduledAt; + scheduleNote_scheduledAt = moment.utc(ps.scheduleNote.scheduledAt).local().valueOf(); } if (typeof ps.poll.expiresAt === 'number') { if (ps.poll.expiresAt < scheduleNote_scheduledAt) { @@ -318,7 +319,7 @@ export default class extends Endpoint { // eslint- } } if (typeof ps.scheduleNote.scheduledAt === 'number') { - if (ps.scheduleNote.scheduledAt < Date.now()) { + if (moment.utc(ps.scheduleNote.scheduledAt).local().valueOf() < Date.now()) { throw new ApiError(meta.errors.cannotCreateAlreadyExpiredSchedule); } } else { @@ -347,14 +348,15 @@ export default class extends Endpoint { // eslint- if (ps.scheduleNote.scheduledAt) { me.token = null; const noteId = this.idService.gen(new Date().getTime()); + const schedNoteLocalTime = moment.utc(ps.scheduleNote.scheduledAt).local().valueOf(); await this.noteScheduleRepository.insert({ id: noteId, note: note, userId: me.id, - scheduledAt: new Date(ps.scheduleNote.scheduledAt), + scheduledAt: new Date(schedNoteLocalTime), }); - const delay = new Date(ps.scheduleNote.scheduledAt).getTime() - Date.now(); + const delay = new Date(schedNoteLocalTime).getTime() - Date.now(); await this.queueService.ScheduleNotePostQueue.add(String(delay), { scheduleNoteId: noteId, }, { diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 752b6cb388..ce3fd90a86 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -57,6 +57,7 @@ "misskey-bubble-game": "workspace:*", "misskey-js": "workspace:*", "misskey-reversi": "workspace:*", + "moment": "^2.30.1", "photoswipe": "5.4.4", "punycode": "2.3.1", "rollup": "4.26.0", diff --git a/packages/frontend/src/components/MkScheduleEditor.vue b/packages/frontend/src/components/MkScheduleEditor.vue index f40d37c962..695a474998 100644 --- a/packages/frontend/src/components/MkScheduleEditor.vue +++ b/packages/frontend/src/components/MkScheduleEditor.vue @@ -18,6 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only