From b75184ec8e3436200bacdcd832e3324702553d20 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 18 Sep 2022 03:27:08 +0900 Subject: なんかもうめっちゃ変えた MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/server/api/EndpointsModule.ts | 1268 ++++++++++++++++++++ 1 file changed, 1268 insertions(+) create mode 100644 packages/backend/src/server/api/EndpointsModule.ts (limited to 'packages/backend/src/server/api/EndpointsModule.ts') diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts new file mode 100644 index 0000000000..d2dfd78fd4 --- /dev/null +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -0,0 +1,1268 @@ +import { Module } from '@nestjs/common'; + +import { CoreModule } from '@/core/CoreModule.js'; +import * as ep___admin_meta from './endpoints/admin/meta.js'; +import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-reports.js'; +import * as ep___admin_accounts_create from './endpoints/admin/accounts/create.js'; +import * as ep___admin_accounts_delete from './endpoints/admin/accounts/delete.js'; +import * as ep___admin_ad_create from './endpoints/admin/ad/create.js'; +import * as ep___admin_ad_delete from './endpoints/admin/ad/delete.js'; +import * as ep___admin_ad_list from './endpoints/admin/ad/list.js'; +import * as ep___admin_ad_update from './endpoints/admin/ad/update.js'; +import * as ep___admin_announcements_create from './endpoints/admin/announcements/create.js'; +import * as ep___admin_announcements_delete from './endpoints/admin/announcements/delete.js'; +import * as ep___admin_announcements_list from './endpoints/admin/announcements/list.js'; +import * as ep___admin_announcements_update from './endpoints/admin/announcements/update.js'; +import * as ep___admin_deleteAllFilesOfAUser from './endpoints/admin/delete-all-files-of-a-user.js'; +import * as ep___admin_drive_cleanRemoteFiles from './endpoints/admin/drive/clean-remote-files.js'; +import * as ep___admin_drive_cleanup from './endpoints/admin/drive/cleanup.js'; +import * as ep___admin_drive_files from './endpoints/admin/drive/files.js'; +import * as ep___admin_drive_showFile from './endpoints/admin/drive/show-file.js'; +import * as ep___admin_emoji_addAliasesBulk from './endpoints/admin/emoji/add-aliases-bulk.js'; +import * as ep___admin_emoji_add from './endpoints/admin/emoji/add.js'; +import * as ep___admin_emoji_copy from './endpoints/admin/emoji/copy.js'; +import * as ep___admin_emoji_deleteBulk from './endpoints/admin/emoji/delete-bulk.js'; +import * as ep___admin_emoji_delete from './endpoints/admin/emoji/delete.js'; +import * as ep___admin_emoji_importZip from './endpoints/admin/emoji/import-zip.js'; +import * as ep___admin_emoji_listRemote from './endpoints/admin/emoji/list-remote.js'; +import * as ep___admin_emoji_list from './endpoints/admin/emoji/list.js'; +import * as ep___admin_emoji_removeAliasesBulk from './endpoints/admin/emoji/remove-aliases-bulk.js'; +import * as ep___admin_emoji_setAliasesBulk from './endpoints/admin/emoji/set-aliases-bulk.js'; +import * as ep___admin_emoji_setCategoryBulk from './endpoints/admin/emoji/set-category-bulk.js'; +import * as ep___admin_emoji_update from './endpoints/admin/emoji/update.js'; +import * as ep___admin_federation_deleteAllFiles from './endpoints/admin/federation/delete-all-files.js'; +import * as ep___admin_federation_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js'; +import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js'; +import * as ep___admin_federation_updateInstance from './endpoints/admin/federation/update-instance.js'; +import * as ep___admin_getIndexStats from './endpoints/admin/get-index-stats.js'; +import * as ep___admin_getTableStats from './endpoints/admin/get-table-stats.js'; +import * as ep___admin_getUserIps from './endpoints/admin/get-user-ips.js'; +import * as ep___admin_invite from './endpoints/admin/invite.js'; +import * as ep___admin_moderators_add from './endpoints/admin/moderators/add.js'; +import * as ep___admin_moderators_remove from './endpoints/admin/moderators/remove.js'; +import * as ep___admin_promo_create from './endpoints/admin/promo/create.js'; +import * as ep___admin_queue_clear from './endpoints/admin/queue/clear.js'; +import * as ep___admin_queue_deliverDelayed from './endpoints/admin/queue/deliver-delayed.js'; +import * as ep___admin_queue_inboxDelayed from './endpoints/admin/queue/inbox-delayed.js'; +import * as ep___admin_queue_stats from './endpoints/admin/queue/stats.js'; +import * as ep___admin_relays_add from './endpoints/admin/relays/add.js'; +import * as ep___admin_relays_list from './endpoints/admin/relays/list.js'; +import * as ep___admin_relays_remove from './endpoints/admin/relays/remove.js'; +import * as ep___admin_resetPassword from './endpoints/admin/reset-password.js'; +import * as ep___admin_resolveAbuseUserReport from './endpoints/admin/resolve-abuse-user-report.js'; +import * as ep___admin_sendEmail from './endpoints/admin/send-email.js'; +import * as ep___admin_serverInfo from './endpoints/admin/server-info.js'; +import * as ep___admin_showModerationLogs from './endpoints/admin/show-moderation-logs.js'; +import * as ep___admin_showUser from './endpoints/admin/show-user.js'; +import * as ep___admin_showUsers from './endpoints/admin/show-users.js'; +import * as ep___admin_silenceUser from './endpoints/admin/silence-user.js'; +import * as ep___admin_suspendUser from './endpoints/admin/suspend-user.js'; +import * as ep___admin_unsilenceUser from './endpoints/admin/unsilence-user.js'; +import * as ep___admin_unsuspendUser from './endpoints/admin/unsuspend-user.js'; +import * as ep___admin_updateMeta from './endpoints/admin/update-meta.js'; +import * as ep___admin_deleteAccount from './endpoints/admin/delete-account.js'; +import * as ep___admin_updateUserNote from './endpoints/admin/update-user-note.js'; +import * as ep___announcements from './endpoints/announcements.js'; +import * as ep___antennas_create from './endpoints/antennas/create.js'; +import * as ep___antennas_delete from './endpoints/antennas/delete.js'; +import * as ep___antennas_list from './endpoints/antennas/list.js'; +import * as ep___antennas_notes from './endpoints/antennas/notes.js'; +import * as ep___antennas_show from './endpoints/antennas/show.js'; +import * as ep___antennas_update from './endpoints/antennas/update.js'; +import * as ep___ap_get from './endpoints/ap/get.js'; +import * as ep___ap_show from './endpoints/ap/show.js'; +import * as ep___app_create from './endpoints/app/create.js'; +import * as ep___app_show from './endpoints/app/show.js'; +import * as ep___auth_accept from './endpoints/auth/accept.js'; +import * as ep___auth_session_generate from './endpoints/auth/session/generate.js'; +import * as ep___auth_session_show from './endpoints/auth/session/show.js'; +import * as ep___auth_session_userkey from './endpoints/auth/session/userkey.js'; +import * as ep___blocking_create from './endpoints/blocking/create.js'; +import * as ep___blocking_delete from './endpoints/blocking/delete.js'; +import * as ep___blocking_list from './endpoints/blocking/list.js'; +import * as ep___channels_create from './endpoints/channels/create.js'; +import * as ep___channels_featured from './endpoints/channels/featured.js'; +import * as ep___channels_follow from './endpoints/channels/follow.js'; +import * as ep___channels_followed from './endpoints/channels/followed.js'; +import * as ep___channels_owned from './endpoints/channels/owned.js'; +import * as ep___channels_show from './endpoints/channels/show.js'; +import * as ep___channels_timeline from './endpoints/channels/timeline.js'; +import * as ep___channels_unfollow from './endpoints/channels/unfollow.js'; +import * as ep___channels_update from './endpoints/channels/update.js'; +import * as ep___charts_activeUsers from './endpoints/charts/active-users.js'; +import * as ep___charts_apRequest from './endpoints/charts/ap-request.js'; +import * as ep___charts_drive from './endpoints/charts/drive.js'; +import * as ep___charts_federation from './endpoints/charts/federation.js'; +import * as ep___charts_hashtag from './endpoints/charts/hashtag.js'; +import * as ep___charts_instance from './endpoints/charts/instance.js'; +import * as ep___charts_notes from './endpoints/charts/notes.js'; +import * as ep___charts_user_drive from './endpoints/charts/user/drive.js'; +import * as ep___charts_user_following from './endpoints/charts/user/following.js'; +import * as ep___charts_user_notes from './endpoints/charts/user/notes.js'; +import * as ep___charts_user_reactions from './endpoints/charts/user/reactions.js'; +import * as ep___charts_users from './endpoints/charts/users.js'; +import * as ep___clips_addNote from './endpoints/clips/add-note.js'; +import * as ep___clips_removeNote from './endpoints/clips/remove-note.js'; +import * as ep___clips_create from './endpoints/clips/create.js'; +import * as ep___clips_delete from './endpoints/clips/delete.js'; +import * as ep___clips_list from './endpoints/clips/list.js'; +import * as ep___clips_notes from './endpoints/clips/notes.js'; +import * as ep___clips_show from './endpoints/clips/show.js'; +import * as ep___clips_update from './endpoints/clips/update.js'; +import * as ep___drive from './endpoints/drive.js'; +import * as ep___drive_files from './endpoints/drive/files.js'; +import * as ep___drive_files_attachedNotes from './endpoints/drive/files/attached-notes.js'; +import * as ep___drive_files_checkExistence from './endpoints/drive/files/check-existence.js'; +import * as ep___drive_files_create from './endpoints/drive/files/create.js'; +import * as ep___drive_files_delete from './endpoints/drive/files/delete.js'; +import * as ep___drive_files_findByHash from './endpoints/drive/files/find-by-hash.js'; +import * as ep___drive_files_find from './endpoints/drive/files/find.js'; +import * as ep___drive_files_show from './endpoints/drive/files/show.js'; +import * as ep___drive_files_update from './endpoints/drive/files/update.js'; +import * as ep___drive_files_uploadFromUrl from './endpoints/drive/files/upload-from-url.js'; +import * as ep___drive_folders from './endpoints/drive/folders.js'; +import * as ep___drive_folders_create from './endpoints/drive/folders/create.js'; +import * as ep___drive_folders_delete from './endpoints/drive/folders/delete.js'; +import * as ep___drive_folders_find from './endpoints/drive/folders/find.js'; +import * as ep___drive_folders_show from './endpoints/drive/folders/show.js'; +import * as ep___drive_folders_update from './endpoints/drive/folders/update.js'; +import * as ep___drive_stream from './endpoints/drive/stream.js'; +import * as ep___emailAddress_available from './endpoints/email-address/available.js'; +import * as ep___endpoint from './endpoints/endpoint.js'; +import * as ep___endpoints from './endpoints/endpoints.js'; +import * as ep___exportCustomEmojis from './endpoints/export-custom-emojis.js'; +import * as ep___federation_followers from './endpoints/federation/followers.js'; +import * as ep___federation_following from './endpoints/federation/following.js'; +import * as ep___federation_instances from './endpoints/federation/instances.js'; +import * as ep___federation_showInstance from './endpoints/federation/show-instance.js'; +import * as ep___federation_updateRemoteUser from './endpoints/federation/update-remote-user.js'; +import * as ep___federation_users from './endpoints/federation/users.js'; +import * as ep___federation_stats from './endpoints/federation/stats.js'; +import * as ep___following_create from './endpoints/following/create.js'; +import * as ep___following_delete from './endpoints/following/delete.js'; +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_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'; +import * as ep___gallery_posts from './endpoints/gallery/posts.js'; +import * as ep___gallery_posts_create from './endpoints/gallery/posts/create.js'; +import * as ep___gallery_posts_delete from './endpoints/gallery/posts/delete.js'; +import * as ep___gallery_posts_like from './endpoints/gallery/posts/like.js'; +import * as ep___gallery_posts_show from './endpoints/gallery/posts/show.js'; +import * as ep___gallery_posts_unlike from './endpoints/gallery/posts/unlike.js'; +import * as ep___gallery_posts_update from './endpoints/gallery/posts/update.js'; +import * as ep___getOnlineUsersCount from './endpoints/get-online-users-count.js'; +import * as ep___hashtags_list from './endpoints/hashtags/list.js'; +import * as ep___hashtags_search from './endpoints/hashtags/search.js'; +import * as ep___hashtags_show from './endpoints/hashtags/show.js'; +import * as ep___hashtags_trend from './endpoints/hashtags/trend.js'; +import * as ep___hashtags_users from './endpoints/hashtags/users.js'; +import * as ep___i from './endpoints/i.js'; +import * as ep___i_2fa_done from './endpoints/i/2fa/done.js'; +import * as ep___i_2fa_keyDone from './endpoints/i/2fa/key-done.js'; +import * as ep___i_2fa_passwordLess from './endpoints/i/2fa/password-less.js'; +import * as ep___i_2fa_registerKey from './endpoints/i/2fa/register-key.js'; +import * as ep___i_2fa_register from './endpoints/i/2fa/register.js'; +import * as ep___i_2fa_removeKey from './endpoints/i/2fa/remove-key.js'; +import * as ep___i_2fa_unregister from './endpoints/i/2fa/unregister.js'; +import * as ep___i_apps from './endpoints/i/apps.js'; +import * as ep___i_authorizedApps from './endpoints/i/authorized-apps.js'; +import * as ep___i_changePassword from './endpoints/i/change-password.js'; +import * as ep___i_deleteAccount from './endpoints/i/delete-account.js'; +import * as ep___i_exportBlocking from './endpoints/i/export-blocking.js'; +import * as ep___i_exportFollowing from './endpoints/i/export-following.js'; +import * as ep___i_exportMute from './endpoints/i/export-mute.js'; +import * as ep___i_exportNotes from './endpoints/i/export-notes.js'; +import * as ep___i_exportUserLists from './endpoints/i/export-user-lists.js'; +import * as ep___i_favorites from './endpoints/i/favorites.js'; +import * as ep___i_gallery_likes from './endpoints/i/gallery/likes.js'; +import * as ep___i_gallery_posts from './endpoints/i/gallery/posts.js'; +import * as ep___i_getWordMutedNotesCount from './endpoints/i/get-word-muted-notes-count.js'; +import * as ep___i_importBlocking from './endpoints/i/import-blocking.js'; +import * as ep___i_importFollowing from './endpoints/i/import-following.js'; +import * as ep___i_importMuting from './endpoints/i/import-muting.js'; +import * as ep___i_importUserLists from './endpoints/i/import-user-lists.js'; +import * as ep___i_notifications from './endpoints/i/notifications.js'; +import * as ep___i_pageLikes from './endpoints/i/page-likes.js'; +import * as ep___i_pages from './endpoints/i/pages.js'; +import * as ep___i_pin from './endpoints/i/pin.js'; +import * as ep___i_readAllMessagingMessages from './endpoints/i/read-all-messaging-messages.js'; +import * as ep___i_readAllUnreadNotes from './endpoints/i/read-all-unread-notes.js'; +import * as ep___i_readAnnouncement from './endpoints/i/read-announcement.js'; +import * as ep___i_regenerateToken from './endpoints/i/regenerate-token.js'; +import * as ep___i_registry_getAll from './endpoints/i/registry/get-all.js'; +import * as ep___i_registry_getDetail from './endpoints/i/registry/get-detail.js'; +import * as ep___i_registry_get from './endpoints/i/registry/get.js'; +import * as ep___i_registry_keysWithType from './endpoints/i/registry/keys-with-type.js'; +import * as ep___i_registry_keys from './endpoints/i/registry/keys.js'; +import * as ep___i_registry_remove from './endpoints/i/registry/remove.js'; +import * as ep___i_registry_scopes from './endpoints/i/registry/scopes.js'; +import * as ep___i_registry_set from './endpoints/i/registry/set.js'; +import * as ep___i_revokeToken from './endpoints/i/revoke-token.js'; +import * as ep___i_signinHistory from './endpoints/i/signin-history.js'; +import * as ep___i_unpin from './endpoints/i/unpin.js'; +import * as ep___i_updateEmail from './endpoints/i/update-email.js'; +import * as ep___i_update from './endpoints/i/update.js'; +import * as ep___i_userGroupInvites from './endpoints/i/user-group-invites.js'; +import * as ep___i_webhooks_create from './endpoints/i/webhooks/create.js'; +import * as ep___i_webhooks_show from './endpoints/i/webhooks/show.js'; +import * as ep___i_webhooks_list from './endpoints/i/webhooks/list.js'; +import * as ep___i_webhooks_update from './endpoints/i/webhooks/update.js'; +import * as ep___i_webhooks_delete from './endpoints/i/webhooks/delete.js'; +import * as ep___messaging_history from './endpoints/messaging/history.js'; +import * as ep___messaging_messages from './endpoints/messaging/messages.js'; +import * as ep___messaging_messages_create from './endpoints/messaging/messages/create.js'; +import * as ep___messaging_messages_delete from './endpoints/messaging/messages/delete.js'; +import * as ep___messaging_messages_read from './endpoints/messaging/messages/read.js'; +import * as ep___meta from './endpoints/meta.js'; +import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js'; +import * as ep___mute_create from './endpoints/mute/create.js'; +import * as ep___mute_delete from './endpoints/mute/delete.js'; +import * as ep___mute_list from './endpoints/mute/list.js'; +import * as ep___my_apps from './endpoints/my/apps.js'; +import * as ep___notes from './endpoints/notes.js'; +import * as ep___notes_children from './endpoints/notes/children.js'; +import * as ep___notes_clips from './endpoints/notes/clips.js'; +import * as ep___notes_conversation from './endpoints/notes/conversation.js'; +import * as ep___notes_create from './endpoints/notes/create.js'; +import * as ep___notes_delete from './endpoints/notes/delete.js'; +import * as ep___notes_favorites_create from './endpoints/notes/favorites/create.js'; +import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete.js'; +import * as ep___notes_featured from './endpoints/notes/featured.js'; +import * as ep___notes_globalTimeline from './endpoints/notes/global-timeline.js'; +import * as ep___notes_hybridTimeline from './endpoints/notes/hybrid-timeline.js'; +import * as ep___notes_localTimeline from './endpoints/notes/local-timeline.js'; +import * as ep___notes_mentions from './endpoints/notes/mentions.js'; +import * as ep___notes_polls_recommendation from './endpoints/notes/polls/recommendation.js'; +import * as ep___notes_polls_vote from './endpoints/notes/polls/vote.js'; +import * as ep___notes_reactions from './endpoints/notes/reactions.js'; +import * as ep___notes_reactions_create from './endpoints/notes/reactions/create.js'; +import * as ep___notes_reactions_delete from './endpoints/notes/reactions/delete.js'; +import * as ep___notes_renotes from './endpoints/notes/renotes.js'; +import * as ep___notes_replies from './endpoints/notes/replies.js'; +import * as ep___notes_searchByTag from './endpoints/notes/search-by-tag.js'; +import * as ep___notes_search from './endpoints/notes/search.js'; +import * as ep___notes_show from './endpoints/notes/show.js'; +import * as ep___notes_state from './endpoints/notes/state.js'; +import * as ep___notes_threadMuting_create from './endpoints/notes/thread-muting/create.js'; +import * as ep___notes_threadMuting_delete from './endpoints/notes/thread-muting/delete.js'; +import * as ep___notes_timeline from './endpoints/notes/timeline.js'; +import * as ep___notes_translate from './endpoints/notes/translate.js'; +import * as ep___notes_unrenote from './endpoints/notes/unrenote.js'; +import * as ep___notes_userListTimeline from './endpoints/notes/user-list-timeline.js'; +import * as ep___notifications_create from './endpoints/notifications/create.js'; +import * as ep___notifications_markAllAsRead from './endpoints/notifications/mark-all-as-read.js'; +import * as ep___notifications_read from './endpoints/notifications/read.js'; +import * as ep___pagePush from './endpoints/page-push.js'; +import * as ep___pages_create from './endpoints/pages/create.js'; +import * as ep___pages_delete from './endpoints/pages/delete.js'; +import * as ep___pages_featured from './endpoints/pages/featured.js'; +import * as ep___pages_like from './endpoints/pages/like.js'; +import * as ep___pages_show from './endpoints/pages/show.js'; +import * as ep___pages_unlike from './endpoints/pages/unlike.js'; +import * as ep___pages_update from './endpoints/pages/update.js'; +import * as ep___ping from './endpoints/ping.js'; +import * as ep___pinnedUsers from './endpoints/pinned-users.js'; +import * as ep___promo_read from './endpoints/promo/read.js'; +import * as ep___requestResetPassword from './endpoints/request-reset-password.js'; +import * as ep___resetDb from './endpoints/reset-db.js'; +import * as ep___resetPassword from './endpoints/reset-password.js'; +import * as ep___serverInfo from './endpoints/server-info.js'; +import * as ep___stats from './endpoints/stats.js'; +import * as ep___sw_register from './endpoints/sw/register.js'; +import * as ep___sw_unregister from './endpoints/sw/unregister.js'; +import * as ep___test from './endpoints/test.js'; +import * as ep___username_available from './endpoints/username/available.js'; +import * as ep___users from './endpoints/users.js'; +import * as ep___users_clips from './endpoints/users/clips.js'; +import * as ep___users_followers from './endpoints/users/followers.js'; +import * as ep___users_following from './endpoints/users/following.js'; +import * as ep___users_gallery_posts from './endpoints/users/gallery/posts.js'; +import * as ep___users_getFrequentlyRepliedUsers from './endpoints/users/get-frequently-replied-users.js'; +import * as ep___users_groups_create from './endpoints/users/groups/create.js'; +import * as ep___users_groups_delete from './endpoints/users/groups/delete.js'; +import * as ep___users_groups_invitations_accept from './endpoints/users/groups/invitations/accept.js'; +import * as ep___users_groups_invitations_reject from './endpoints/users/groups/invitations/reject.js'; +import * as ep___users_groups_invite from './endpoints/users/groups/invite.js'; +import * as ep___users_groups_joined from './endpoints/users/groups/joined.js'; +import * as ep___users_groups_leave from './endpoints/users/groups/leave.js'; +import * as ep___users_groups_owned from './endpoints/users/groups/owned.js'; +import * as ep___users_groups_pull from './endpoints/users/groups/pull.js'; +import * as ep___users_groups_show from './endpoints/users/groups/show.js'; +import * as ep___users_groups_transfer from './endpoints/users/groups/transfer.js'; +import * as ep___users_groups_update from './endpoints/users/groups/update.js'; +import * as ep___users_lists_create from './endpoints/users/lists/create.js'; +import * as ep___users_lists_delete from './endpoints/users/lists/delete.js'; +import * as ep___users_lists_list from './endpoints/users/lists/list.js'; +import * as ep___users_lists_pull from './endpoints/users/lists/pull.js'; +import * as ep___users_lists_push from './endpoints/users/lists/push.js'; +import * as ep___users_lists_show from './endpoints/users/lists/show.js'; +import * as ep___users_lists_update from './endpoints/users/lists/update.js'; +import * as ep___users_notes from './endpoints/users/notes.js'; +import * as ep___users_pages from './endpoints/users/pages.js'; +import * as ep___users_reactions from './endpoints/users/reactions.js'; +import * as ep___users_recommendation from './endpoints/users/recommendation.js'; +import * as ep___users_relation from './endpoints/users/relation.js'; +import * as ep___users_reportAbuse from './endpoints/users/report-abuse.js'; +import * as ep___users_searchByUsernameAndHost from './endpoints/users/search-by-username-and-host.js'; +import * as ep___users_search from './endpoints/users/search.js'; +import * as ep___users_show from './endpoints/users/show.js'; +import * as ep___users_stats from './endpoints/users/stats.js'; +import * as ep___fetchRss from './endpoints/fetch-rss.js'; +import * as ep___admin_driveCapOverride from './endpoints/admin/drive-capacity-override.js'; +import { GetterService } from './common/GetterService.js'; +import { ApiLoggerService } from './ApiLoggerService.js'; +import type { Provider } from '@nestjs/common'; + +const $admin_meta: Provider = { provide: 'ep:admin/meta', useClass: ep___admin_meta.default }; +const $admin_abuseUserReports: Provider = { provide: 'ep:admin/abuse-user-reports', useClass: ep___admin_abuseUserReports.default }; +const $admin_accounts_create: Provider = { provide: 'ep:admin/accounts/create', useClass: ep___admin_accounts_create.default }; +const $admin_accounts_delete: Provider = { provide: 'ep:admin/accounts/delete', useClass: ep___admin_accounts_delete.default }; +const $admin_ad_create: Provider = { provide: 'ep:admin/ad/create', useClass: ep___admin_ad_create.default }; +const $admin_ad_delete: Provider = { provide: 'ep:admin/ad/delete', useClass: ep___admin_ad_delete.default }; +const $admin_ad_list: Provider = { provide: 'ep:admin/ad/list', useClass: ep___admin_ad_list.default }; +const $admin_ad_update: Provider = { provide: 'ep:admin/ad/update', useClass: ep___admin_ad_update.default }; +const $admin_announcements_create: Provider = { provide: 'ep:admin/announcements/create', useClass: ep___admin_announcements_create.default }; +const $admin_announcements_delete: Provider = { provide: 'ep:admin/announcements/delete', useClass: ep___admin_announcements_delete.default }; +const $admin_announcements_list: Provider = { provide: 'ep:admin/announcements/list', useClass: ep___admin_announcements_list.default }; +const $admin_announcements_update: Provider = { provide: 'ep:admin/announcements/update', useClass: ep___admin_announcements_update.default }; +const $admin_deleteAllFilesOfAUser: Provider = { provide: 'ep:admin/delete-all-files-of-a-user', useClass: ep___admin_deleteAllFilesOfAUser.default }; +const $admin_drive_cleanRemoteFiles: Provider = { provide: 'ep:admin/drive/clean-remote-files', useClass: ep___admin_drive_cleanRemoteFiles.default }; +const $admin_drive_cleanup: Provider = { provide: 'ep:admin/drive/cleanup', useClass: ep___admin_drive_cleanup.default }; +const $admin_drive_files: Provider = { provide: 'ep:admin/drive/files', useClass: ep___admin_drive_files.default }; +const $admin_drive_showFile: Provider = { provide: 'ep:admin/drive/show-file', useClass: ep___admin_drive_showFile.default }; +const $admin_emoji_addAliasesBulk: Provider = { provide: 'ep:admin/emoji/add-aliases-bulk', useClass: ep___admin_emoji_addAliasesBulk.default }; +const $admin_emoji_add: Provider = { provide: 'ep:admin/emoji/add', useClass: ep___admin_emoji_add.default }; +const $admin_emoji_copy: Provider = { provide: 'ep:admin/emoji/copy', useClass: ep___admin_emoji_copy.default }; +const $admin_emoji_deleteBulk: Provider = { provide: 'ep:admin/emoji/delete-bulk', useClass: ep___admin_emoji_deleteBulk.default }; +const $admin_emoji_delete: Provider = { provide: 'ep:admin/emoji/delete', useClass: ep___admin_emoji_delete.default }; +const $admin_emoji_importZip: Provider = { provide: 'ep:admin/emoji/import-zip', useClass: ep___admin_emoji_importZip.default }; +const $admin_emoji_listRemote: Provider = { provide: 'ep:admin/emoji/list-remote', useClass: ep___admin_emoji_listRemote.default }; +const $admin_emoji_list: Provider = { provide: 'ep:admin/emoji/list', useClass: ep___admin_emoji_list.default }; +const $admin_emoji_removeAliasesBulk: Provider = { provide: 'ep:admin/emoji/remove-aliases-bulk', useClass: ep___admin_emoji_removeAliasesBulk.default }; +const $admin_emoji_setAliasesBulk: Provider = { provide: 'ep:admin/emoji/set-aliases-bulk', useClass: ep___admin_emoji_setAliasesBulk.default }; +const $admin_emoji_setCategoryBulk: Provider = { provide: 'ep:admin/emoji/set-category-bulk', useClass: ep___admin_emoji_setCategoryBulk.default }; +const $admin_emoji_update: Provider = { provide: 'ep:admin/emoji/update', useClass: ep___admin_emoji_update.default }; +const $admin_federation_deleteAllFiles: Provider = { provide: 'ep:admin/federation/delete-all-files', useClass: ep___admin_federation_deleteAllFiles.default }; +const $admin_federation_refreshRemoteInstanceMetadata: Provider = { provide: 'ep:admin/federation/refresh-remote-instance-metadata', useClass: ep___admin_federation_refreshRemoteInstanceMetadata.default }; +const $admin_federation_removeAllFollowing: Provider = { provide: 'ep:admin/federation/remove-all-following', useClass: ep___admin_federation_removeAllFollowing.default }; +const $admin_federation_updateInstance: Provider = { provide: 'ep:admin/federation/update-instance', useClass: ep___admin_federation_updateInstance.default }; +const $admin_getIndexStats: Provider = { provide: 'ep:admin/get-index-stats', useClass: ep___admin_getIndexStats.default }; +const $admin_getTableStats: Provider = { provide: 'ep:admin/get-table-stats', useClass: ep___admin_getTableStats.default }; +const $admin_getUserIps: Provider = { provide: 'ep:admin/get-user-ips', useClass: ep___admin_getUserIps.default }; +const $admin_invite: Provider = { provide: 'ep:admin/invite', useClass: ep___admin_invite.default }; +const $admin_moderators_add: Provider = { provide: 'ep:admin/moderators/add', useClass: ep___admin_moderators_add.default }; +const $admin_moderators_remove: Provider = { provide: 'ep:admin/moderators/remove', useClass: ep___admin_moderators_remove.default }; +const $admin_promo_create: Provider = { provide: 'ep:admin/promo/create', useClass: ep___admin_promo_create.default }; +const $admin_queue_clear: Provider = { provide: 'ep:admin/queue/clear', useClass: ep___admin_queue_clear.default }; +const $admin_queue_deliverDelayed: Provider = { provide: 'ep:admin/queue/deliver-delayed', useClass: ep___admin_queue_deliverDelayed.default }; +const $admin_queue_inboxDelayed: Provider = { provide: 'ep:admin/queue/inbox-delayed', useClass: ep___admin_queue_inboxDelayed.default }; +const $admin_queue_stats: Provider = { provide: 'ep:admin/queue/stats', useClass: ep___admin_queue_stats.default }; +const $admin_relays_add: Provider = { provide: 'ep:admin/relays/add', useClass: ep___admin_relays_add.default }; +const $admin_relays_list: Provider = { provide: 'ep:admin/relays/list', useClass: ep___admin_relays_list.default }; +const $admin_relays_remove: Provider = { provide: 'ep:admin/relays/remove', useClass: ep___admin_relays_remove.default }; +const $admin_resetPassword: Provider = { provide: 'ep:admin/reset-password', useClass: ep___admin_resetPassword.default }; +const $admin_resolveAbuseUserReport: Provider = { provide: 'ep:admin/resolve-abuse-user-report', useClass: ep___admin_resolveAbuseUserReport.default }; +const $admin_sendEmail: Provider = { provide: 'ep:admin/send-email', useClass: ep___admin_sendEmail.default }; +const $admin_serverInfo: Provider = { provide: 'ep:admin/server-info', useClass: ep___admin_serverInfo.default }; +const $admin_showModerationLogs: Provider = { provide: 'ep:admin/show-moderation-logs', useClass: ep___admin_showModerationLogs.default }; +const $admin_showUser: Provider = { provide: 'ep:admin/show-user', useClass: ep___admin_showUser.default }; +const $admin_showUsers: Provider = { provide: 'ep:admin/show-users', useClass: ep___admin_showUsers.default }; +const $admin_silenceUser: Provider = { provide: 'ep:admin/silence-user', useClass: ep___admin_silenceUser.default }; +const $admin_suspendUser: Provider = { provide: 'ep:admin/suspend-user', useClass: ep___admin_suspendUser.default }; +const $admin_unsilenceUser: Provider = { provide: 'ep:admin/unsilence-user', useClass: ep___admin_unsilenceUser.default }; +const $admin_unsuspendUser: Provider = { provide: 'ep:admin/unsuspend-user', useClass: ep___admin_unsuspendUser.default }; +const $admin_updateMeta: Provider = { provide: 'ep:admin/update-meta', useClass: ep___admin_updateMeta.default }; +const $admin_deleteAccount: Provider = { provide: 'ep:admin/delete-account', useClass: ep___admin_deleteAccount.default }; +const $admin_updateUserNote: Provider = { provide: 'ep:admin/update-user-note', useClass: ep___admin_updateUserNote.default }; +const $announcements: Provider = { provide: 'ep:announcements', useClass: ep___announcements.default }; +const $antennas_create: Provider = { provide: 'ep:antennas/create', useClass: ep___antennas_create.default }; +const $antennas_delete: Provider = { provide: 'ep:antennas/delete', useClass: ep___antennas_delete.default }; +const $antennas_list: Provider = { provide: 'ep:antennas/list', useClass: ep___antennas_list.default }; +const $antennas_notes: Provider = { provide: 'ep:antennas/notes', useClass: ep___antennas_notes.default }; +const $antennas_show: Provider = { provide: 'ep:antennas/show', useClass: ep___antennas_show.default }; +const $antennas_update: Provider = { provide: 'ep:antennas/update', useClass: ep___antennas_update.default }; +const $ap_get: Provider = { provide: 'ep:ap/get', useClass: ep___ap_get.default }; +const $ap_show: Provider = { provide: 'ep:ap/show', useClass: ep___ap_show.default }; +const $app_create: Provider = { provide: 'ep:app/create', useClass: ep___app_create.default }; +const $app_show: Provider = { provide: 'ep:app/show', useClass: ep___app_show.default }; +const $auth_accept: Provider = { provide: 'ep:auth/accept', useClass: ep___auth_accept.default }; +const $auth_session_generate: Provider = { provide: 'ep:auth/session/generate', useClass: ep___auth_session_generate.default }; +const $auth_session_show: Provider = { provide: 'ep:auth/session/show', useClass: ep___auth_session_show.default }; +const $auth_session_userkey: Provider = { provide: 'ep:auth/session/userkey', useClass: ep___auth_session_userkey.default }; +const $blocking_create: Provider = { provide: 'ep:blocking/create', useClass: ep___blocking_create.default }; +const $blocking_delete: Provider = { provide: 'ep:blocking/delete', useClass: ep___blocking_delete.default }; +const $blocking_list: Provider = { provide: 'ep:blocking/list', useClass: ep___blocking_list.default }; +const $channels_create: Provider = { provide: 'ep:channels/create', useClass: ep___channels_create.default }; +const $channels_featured: Provider = { provide: 'ep:channels/featured', useClass: ep___channels_featured.default }; +const $channels_follow: Provider = { provide: 'ep:channels/follow', useClass: ep___channels_follow.default }; +const $channels_followed: Provider = { provide: 'ep:channels/followed', useClass: ep___channels_followed.default }; +const $channels_owned: Provider = { provide: 'ep:channels/owned', useClass: ep___channels_owned.default }; +const $channels_show: Provider = { provide: 'ep:channels/show', useClass: ep___channels_show.default }; +const $channels_timeline: Provider = { provide: 'ep:channels/timeline', useClass: ep___channels_timeline.default }; +const $channels_unfollow: Provider = { provide: 'ep:channels/unfollow', useClass: ep___channels_unfollow.default }; +const $channels_update: Provider = { provide: 'ep:channels/update', useClass: ep___channels_update.default }; +const $charts_activeUsers: Provider = { provide: 'ep:charts/active-users', useClass: ep___charts_activeUsers.default }; +const $charts_apRequest: Provider = { provide: 'ep:charts/ap-request', useClass: ep___charts_apRequest.default }; +const $charts_drive: Provider = { provide: 'ep:charts/drive', useClass: ep___charts_drive.default }; +const $charts_federation: Provider = { provide: 'ep:charts/federation', useClass: ep___charts_federation.default }; +const $charts_hashtag: Provider = { provide: 'ep:charts/hashtag', useClass: ep___charts_hashtag.default }; +const $charts_instance: Provider = { provide: 'ep:charts/instance', useClass: ep___charts_instance.default }; +const $charts_notes: Provider = { provide: 'ep:charts/notes', useClass: ep___charts_notes.default }; +const $charts_user_drive: Provider = { provide: 'ep:charts/user/drive', useClass: ep___charts_user_drive.default }; +const $charts_user_following: Provider = { provide: 'ep:charts/user/following', useClass: ep___charts_user_following.default }; +const $charts_user_notes: Provider = { provide: 'ep:charts/user/notes', useClass: ep___charts_user_notes.default }; +const $charts_user_reactions: Provider = { provide: 'ep:charts/user/reactions', useClass: ep___charts_user_reactions.default }; +const $charts_users: Provider = { provide: 'ep:charts/users', useClass: ep___charts_users.default }; +const $clips_addNote: Provider = { provide: 'ep:clips/add-note', useClass: ep___clips_addNote.default }; +const $clips_removeNote: Provider = { provide: 'ep:clips/remove-note', useClass: ep___clips_removeNote.default }; +const $clips_create: Provider = { provide: 'ep:clips/create', useClass: ep___clips_create.default }; +const $clips_delete: Provider = { provide: 'ep:clips/delete', useClass: ep___clips_delete.default }; +const $clips_list: Provider = { provide: 'ep:clips/list', useClass: ep___clips_list.default }; +const $clips_notes: Provider = { provide: 'ep:clips/notes', useClass: ep___clips_notes.default }; +const $clips_show: Provider = { provide: 'ep:clips/show', useClass: ep___clips_show.default }; +const $clips_update: Provider = { provide: 'ep:clips/update', useClass: ep___clips_update.default }; +const $drive: Provider = { provide: 'ep:drive', useClass: ep___drive.default }; +const $drive_files: Provider = { provide: 'ep:drive/files', useClass: ep___drive_files.default }; +const $drive_files_attachedNotes: Provider = { provide: 'ep:drive/files/attached-notes', useClass: ep___drive_files_attachedNotes.default }; +const $drive_files_checkExistence: Provider = { provide: 'ep:drive/files/check-existence', useClass: ep___drive_files_checkExistence.default }; +const $drive_files_create: Provider = { provide: 'ep:drive/files/create', useClass: ep___drive_files_create.default }; +const $drive_files_delete: Provider = { provide: 'ep:drive/files/delete', useClass: ep___drive_files_delete.default }; +const $drive_files_findByHash: Provider = { provide: 'ep:drive/files/find-by-hash', useClass: ep___drive_files_findByHash.default }; +const $drive_files_find: Provider = { provide: 'ep:drive/files/find', useClass: ep___drive_files_find.default }; +const $drive_files_show: Provider = { provide: 'ep:drive/files/show', useClass: ep___drive_files_show.default }; +const $drive_files_update: Provider = { provide: 'ep:drive/files/update', useClass: ep___drive_files_update.default }; +const $drive_files_uploadFromUrl: Provider = { provide: 'ep:drive/files/upload-from-url', useClass: ep___drive_files_uploadFromUrl.default }; +const $drive_folders: Provider = { provide: 'ep:drive/folders', useClass: ep___drive_folders.default }; +const $drive_folders_create: Provider = { provide: 'ep:drive/folders/create', useClass: ep___drive_folders_create.default }; +const $drive_folders_delete: Provider = { provide: 'ep:drive/folders/delete', useClass: ep___drive_folders_delete.default }; +const $drive_folders_find: Provider = { provide: 'ep:drive/folders/find', useClass: ep___drive_folders_find.default }; +const $drive_folders_show: Provider = { provide: 'ep:drive/folders/show', useClass: ep___drive_folders_show.default }; +const $drive_folders_update: Provider = { provide: 'ep:drive/folders/update', useClass: ep___drive_folders_update.default }; +const $drive_stream: Provider = { provide: 'ep:drive/stream', useClass: ep___drive_stream.default }; +const $emailAddress_available: Provider = { provide: 'ep:email-address/available', useClass: ep___emailAddress_available.default }; +const $endpoint: Provider = { provide: 'ep:endpoint', useClass: ep___endpoint.default }; +const $endpoints: Provider = { provide: 'ep:endpoints', useClass: ep___endpoints.default }; +const $exportCustomEmojis: Provider = { provide: 'ep:export-custom-emojis', useClass: ep___exportCustomEmojis.default }; +const $federation_followers: Provider = { provide: 'ep:federation/followers', useClass: ep___federation_followers.default }; +const $federation_following: Provider = { provide: 'ep:federation/following', useClass: ep___federation_following.default }; +const $federation_instances: Provider = { provide: 'ep:federation/instances', useClass: ep___federation_instances.default }; +const $federation_showInstance: Provider = { provide: 'ep:federation/show-instance', useClass: ep___federation_showInstance.default }; +const $federation_updateRemoteUser: Provider = { provide: 'ep:federation/update-remote-user', useClass: ep___federation_updateRemoteUser.default }; +const $federation_users: Provider = { provide: 'ep:federation/users', useClass: ep___federation_users.default }; +const $federation_stats: Provider = { provide: 'ep:federation/stats', useClass: ep___federation_stats.default }; +const $following_create: Provider = { provide: 'ep:following/create', useClass: ep___following_create.default }; +const $following_delete: Provider = { provide: 'ep:following/delete', useClass: ep___following_delete.default }; +const $following_invalidate: Provider = { provide: 'ep:following/invalidate', useClass: ep___following_invalidate.default }; +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_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 }; +const $gallery_posts: Provider = { provide: 'ep:gallery/posts', useClass: ep___gallery_posts.default }; +const $gallery_posts_create: Provider = { provide: 'ep:gallery/posts/create', useClass: ep___gallery_posts_create.default }; +const $gallery_posts_delete: Provider = { provide: 'ep:gallery/posts/delete', useClass: ep___gallery_posts_delete.default }; +const $gallery_posts_like: Provider = { provide: 'ep:gallery/posts/like', useClass: ep___gallery_posts_like.default }; +const $gallery_posts_show: Provider = { provide: 'ep:gallery/posts/show', useClass: ep___gallery_posts_show.default }; +const $gallery_posts_unlike: Provider = { provide: 'ep:gallery/posts/unlike', useClass: ep___gallery_posts_unlike.default }; +const $gallery_posts_update: Provider = { provide: 'ep:gallery/posts/update', useClass: ep___gallery_posts_update.default }; +const $getOnlineUsersCount: Provider = { provide: 'ep:get-online-users-count', useClass: ep___getOnlineUsersCount.default }; +const $hashtags_list: Provider = { provide: 'ep:hashtags/list', useClass: ep___hashtags_list.default }; +const $hashtags_search: Provider = { provide: 'ep:hashtags/search', useClass: ep___hashtags_search.default }; +const $hashtags_show: Provider = { provide: 'ep:hashtags/show', useClass: ep___hashtags_show.default }; +const $hashtags_trend: Provider = { provide: 'ep:hashtags/trend', useClass: ep___hashtags_trend.default }; +const $hashtags_users: Provider = { provide: 'ep:hashtags/users', useClass: ep___hashtags_users.default }; +const $i: Provider = { provide: 'ep:i', useClass: ep___i.default }; +const $i_2fa_done: Provider = { provide: 'ep:i/2fa/done', useClass: ep___i_2fa_done.default }; +const $i_2fa_keyDone: Provider = { provide: 'ep:i/2fa/key-done', useClass: ep___i_2fa_keyDone.default }; +const $i_2fa_passwordLess: Provider = { provide: 'ep:i/2fa/password-less', useClass: ep___i_2fa_passwordLess.default }; +const $i_2fa_registerKey: Provider = { provide: 'ep:i/2fa/register-key', useClass: ep___i_2fa_registerKey.default }; +const $i_2fa_register: Provider = { provide: 'ep:i/2fa/register', useClass: ep___i_2fa_register.default }; +const $i_2fa_removeKey: Provider = { provide: 'ep:i/2fa/remove-key', useClass: ep___i_2fa_removeKey.default }; +const $i_2fa_unregister: Provider = { provide: 'ep:i/2fa/unregister', useClass: ep___i_2fa_unregister.default }; +const $i_apps: Provider = { provide: 'ep:i/apps', useClass: ep___i_apps.default }; +const $i_authorizedApps: Provider = { provide: 'ep:i/authorized-apps', useClass: ep___i_authorizedApps.default }; +const $i_changePassword: Provider = { provide: 'ep:i/change-password', useClass: ep___i_changePassword.default }; +const $i_deleteAccount: Provider = { provide: 'ep:i/delete-account', useClass: ep___i_deleteAccount.default }; +const $i_exportBlocking: Provider = { provide: 'ep:i/export-blocking', useClass: ep___i_exportBlocking.default }; +const $i_exportFollowing: Provider = { provide: 'ep:i/export-following', useClass: ep___i_exportFollowing.default }; +const $i_exportMute: Provider = { provide: 'ep:i/export-mute', useClass: ep___i_exportMute.default }; +const $i_exportNotes: Provider = { provide: 'ep:i/export-notes', useClass: ep___i_exportNotes.default }; +const $i_exportUserLists: Provider = { provide: 'ep:i/export-user-lists', useClass: ep___i_exportUserLists.default }; +const $i_favorites: Provider = { provide: 'ep:i/favorites', useClass: ep___i_favorites.default }; +const $i_gallery_likes: Provider = { provide: 'ep:i/gallery/likes', useClass: ep___i_gallery_likes.default }; +const $i_gallery_posts: Provider = { provide: 'ep:i/gallery/posts', useClass: ep___i_gallery_posts.default }; +const $i_getWordMutedNotesCount: Provider = { provide: 'ep:i/get-word-muted-notes-count', useClass: ep___i_getWordMutedNotesCount.default }; +const $i_importBlocking: Provider = { provide: 'ep:i/import-blocking', useClass: ep___i_importBlocking.default }; +const $i_importFollowing: Provider = { provide: 'ep:i/import-following', useClass: ep___i_importFollowing.default }; +const $i_importMuting: Provider = { provide: 'ep:i/import-muting', useClass: ep___i_importMuting.default }; +const $i_importUserLists: Provider = { provide: 'ep:i/import-user-lists', useClass: ep___i_importUserLists.default }; +const $i_notifications: Provider = { provide: 'ep:i/notifications', useClass: ep___i_notifications.default }; +const $i_pageLikes: Provider = { provide: 'ep:i/page-likes', useClass: ep___i_pageLikes.default }; +const $i_pages: Provider = { provide: 'ep:i/pages', useClass: ep___i_pages.default }; +const $i_pin: Provider = { provide: 'ep:i/pin', useClass: ep___i_pin.default }; +const $i_readAllMessagingMessages: Provider = { provide: 'ep:i/read-all-messaging-messages', useClass: ep___i_readAllMessagingMessages.default }; +const $i_readAllUnreadNotes: Provider = { provide: 'ep:i/read-all-unread-notes', useClass: ep___i_readAllUnreadNotes.default }; +const $i_readAnnouncement: Provider = { provide: 'ep:i/read-announcement', useClass: ep___i_readAnnouncement.default }; +const $i_regenerateToken: Provider = { provide: 'ep:i/regenerate-token', useClass: ep___i_regenerateToken.default }; +const $i_registry_getAll: Provider = { provide: 'ep:i/registry/get-all', useClass: ep___i_registry_getAll.default }; +const $i_registry_getDetail: Provider = { provide: 'ep:i/registry/get-detail', useClass: ep___i_registry_getDetail.default }; +const $i_registry_get: Provider = { provide: 'ep:i/registry/get', useClass: ep___i_registry_get.default }; +const $i_registry_keysWithType: Provider = { provide: 'ep:i/registry/keys-with-type', useClass: ep___i_registry_keysWithType.default }; +const $i_registry_keys: Provider = { provide: 'ep:i/registry/keys', useClass: ep___i_registry_keys.default }; +const $i_registry_remove: Provider = { provide: 'ep:i/registry/remove', useClass: ep___i_registry_remove.default }; +const $i_registry_scopes: Provider = { provide: 'ep:i/registry/scopes', useClass: ep___i_registry_scopes.default }; +const $i_registry_set: Provider = { provide: 'ep:i/registry/set', useClass: ep___i_registry_set.default }; +const $i_revokeToken: Provider = { provide: 'ep:i/revoke-token', useClass: ep___i_revokeToken.default }; +const $i_signinHistory: Provider = { provide: 'ep:i/signin-history', useClass: ep___i_signinHistory.default }; +const $i_unpin: Provider = { provide: 'ep:i/unpin', useClass: ep___i_unpin.default }; +const $i_updateEmail: Provider = { provide: 'ep:i/update-email', useClass: ep___i_updateEmail.default }; +const $i_update: Provider = { provide: 'ep:i/update', useClass: ep___i_update.default }; +const $i_userGroupInvites: Provider = { provide: 'ep:i/user-group-invites', useClass: ep___i_userGroupInvites.default }; +const $i_webhooks_create: Provider = { provide: 'ep:i/webhooks/create', useClass: ep___i_webhooks_create.default }; +const $i_webhooks_list: Provider = { provide: 'ep:i/webhooks/list', useClass: ep___i_webhooks_list.default }; +const $i_webhooks_show: Provider = { provide: 'ep:i/webhooks/show', useClass: ep___i_webhooks_show.default }; +const $i_webhooks_update: Provider = { provide: 'ep:i/webhooks/update', useClass: ep___i_webhooks_update.default }; +const $i_webhooks_delete: Provider = { provide: 'ep:i/webhooks/delete', useClass: ep___i_webhooks_delete.default }; +const $messaging_history: Provider = { provide: 'ep:messaging/history', useClass: ep___messaging_history.default }; +const $messaging_messages: Provider = { provide: 'ep:messaging/messages', useClass: ep___messaging_messages.default }; +const $messaging_messages_create: Provider = { provide: 'ep:messaging/messages/create', useClass: ep___messaging_messages_create.default }; +const $messaging_messages_delete: Provider = { provide: 'ep:messaging/messages/delete', useClass: ep___messaging_messages_delete.default }; +const $messaging_messages_read: Provider = { provide: 'ep:messaging/messages/read', useClass: ep___messaging_messages_read.default }; +const $meta: Provider = { provide: 'ep:meta', useClass: ep___meta.default }; +const $miauth_genToken: Provider = { provide: 'ep:miauth/gen-token', useClass: ep___miauth_genToken.default }; +const $mute_create: Provider = { provide: 'ep:mute/create', useClass: ep___mute_create.default }; +const $mute_delete: Provider = { provide: 'ep:mute/delete', useClass: ep___mute_delete.default }; +const $mute_list: Provider = { provide: 'ep:mute/list', useClass: ep___mute_list.default }; +const $my_apps: Provider = { provide: 'ep:my/apps', useClass: ep___my_apps.default }; +const $notes: Provider = { provide: 'ep:notes', useClass: ep___notes.default }; +const $notes_children: Provider = { provide: 'ep:notes/children', useClass: ep___notes_children.default }; +const $notes_clips: Provider = { provide: 'ep:notes/clips', useClass: ep___notes_clips.default }; +const $notes_conversation: Provider = { provide: 'ep:notes/conversation', useClass: ep___notes_conversation.default }; +const $notes_create: Provider = { provide: 'ep:notes/create', useClass: ep___notes_create.default }; +const $notes_delete: Provider = { provide: 'ep:notes/delete', useClass: ep___notes_delete.default }; +const $notes_favorites_create: Provider = { provide: 'ep:notes/favorites/create', useClass: ep___notes_favorites_create.default }; +const $notes_favorites_delete: Provider = { provide: 'ep:notes/favorites/delete', useClass: ep___notes_favorites_delete.default }; +const $notes_featured: Provider = { provide: 'ep:notes/featured', useClass: ep___notes_featured.default }; +const $notes_globalTimeline: Provider = { provide: 'ep:notes/global-timeline', useClass: ep___notes_globalTimeline.default }; +const $notes_hybridTimeline: Provider = { provide: 'ep:notes/hybrid-timeline', useClass: ep___notes_hybridTimeline.default }; +const $notes_localTimeline: Provider = { provide: 'ep:notes/local-timeline', useClass: ep___notes_localTimeline.default }; +const $notes_mentions: Provider = { provide: 'ep:notes/mentions', useClass: ep___notes_mentions.default }; +const $notes_polls_recommendation: Provider = { provide: 'ep:notes/polls/recommendation', useClass: ep___notes_polls_recommendation.default }; +const $notes_polls_vote: Provider = { provide: 'ep:notes/polls/vote', useClass: ep___notes_polls_vote.default }; +const $notes_reactions: Provider = { provide: 'ep:notes/reactions', useClass: ep___notes_reactions.default }; +const $notes_reactions_create: Provider = { provide: 'ep:notes/reactions/create', useClass: ep___notes_reactions_create.default }; +const $notes_reactions_delete: Provider = { provide: 'ep:notes/reactions/delete', useClass: ep___notes_reactions_delete.default }; +const $notes_renotes: Provider = { provide: 'ep:notes/renotes', useClass: ep___notes_renotes.default }; +const $notes_replies: Provider = { provide: 'ep:notes/replies', useClass: ep___notes_replies.default }; +const $notes_searchByTag: Provider = { provide: 'ep:notes/search-by-tag', useClass: ep___notes_searchByTag.default }; +const $notes_search: Provider = { provide: 'ep:notes/search', useClass: ep___notes_search.default }; +const $notes_show: Provider = { provide: 'ep:notes/show', useClass: ep___notes_show.default }; +const $notes_state: Provider = { provide: 'ep:notes/state', useClass: ep___notes_state.default }; +const $notes_threadMuting_create: Provider = { provide: 'ep:notes/thread-muting/create', useClass: ep___notes_threadMuting_create.default }; +const $notes_threadMuting_delete: Provider = { provide: 'ep:notes/thread-muting/delete', useClass: ep___notes_threadMuting_delete.default }; +const $notes_timeline: Provider = { provide: 'ep:notes/timeline', useClass: ep___notes_timeline.default }; +const $notes_translate: Provider = { provide: 'ep:notes/translate', useClass: ep___notes_translate.default }; +const $notes_unrenote: Provider = { provide: 'ep:notes/unrenote', useClass: ep___notes_unrenote.default }; +const $notes_userListTimeline: Provider = { provide: 'ep:notes/user-list-timeline', useClass: ep___notes_userListTimeline.default }; +const $notifications_create: Provider = { provide: 'ep:notifications/create', useClass: ep___notifications_create.default }; +const $notifications_markAllAsRead: Provider = { provide: 'ep:notifications/mark-all-as-read', useClass: ep___notifications_markAllAsRead.default }; +const $notifications_read: Provider = { provide: 'ep:notifications/read', useClass: ep___notifications_read.default }; +const $pagePush: Provider = { provide: 'ep:page-push', useClass: ep___pagePush.default }; +const $pages_create: Provider = { provide: 'ep:pages/create', useClass: ep___pages_create.default }; +const $pages_delete: Provider = { provide: 'ep:pages/delete', useClass: ep___pages_delete.default }; +const $pages_featured: Provider = { provide: 'ep:pages/featured', useClass: ep___pages_featured.default }; +const $pages_like: Provider = { provide: 'ep:pages/like', useClass: ep___pages_like.default }; +const $pages_show: Provider = { provide: 'ep:pages/show', useClass: ep___pages_show.default }; +const $pages_unlike: Provider = { provide: 'ep:pages/unlike', useClass: ep___pages_unlike.default }; +const $pages_update: Provider = { provide: 'ep:pages/update', useClass: ep___pages_update.default }; +const $ping: Provider = { provide: 'ep:ping', useClass: ep___ping.default }; +const $pinnedUsers: Provider = { provide: 'ep:pinned-users', useClass: ep___pinnedUsers.default }; +const $promo_read: Provider = { provide: 'ep:promo/read', useClass: ep___promo_read.default }; +const $requestResetPassword: Provider = { provide: 'ep:request-reset-password', useClass: ep___requestResetPassword.default }; +const $resetDb: Provider = { provide: 'ep:reset-db', useClass: ep___resetDb.default }; +const $resetPassword: Provider = { provide: 'ep:reset-password', useClass: ep___resetPassword.default }; +const $serverInfo: Provider = { provide: 'ep:server-info', useClass: ep___serverInfo.default }; +const $stats: Provider = { provide: 'ep:stats', useClass: ep___stats.default }; +const $sw_register: Provider = { provide: 'ep:sw/register', useClass: ep___sw_register.default }; +const $sw_unregister: Provider = { provide: 'ep:sw/unregister', useClass: ep___sw_unregister.default }; +const $test: Provider = { provide: 'ep:test', useClass: ep___test.default }; +const $username_available: Provider = { provide: 'ep:username/available', useClass: ep___username_available.default }; +const $users: Provider = { provide: 'ep:users', useClass: ep___users.default }; +const $users_clips: Provider = { provide: 'ep:users/clips', useClass: ep___users_clips.default }; +const $users_followers: Provider = { provide: 'ep:users/followers', useClass: ep___users_followers.default }; +const $users_following: Provider = { provide: 'ep:users/following', useClass: ep___users_following.default }; +const $users_gallery_posts: Provider = { provide: 'ep:users/gallery/posts', useClass: ep___users_gallery_posts.default }; +const $users_getFrequentlyRepliedUsers: Provider = { provide: 'ep:users/get-frequently-replied-users', useClass: ep___users_getFrequentlyRepliedUsers.default }; +const $users_groups_create: Provider = { provide: 'ep:users/groups/create', useClass: ep___users_groups_create.default }; +const $users_groups_delete: Provider = { provide: 'ep:users/groups/delete', useClass: ep___users_groups_delete.default }; +const $users_groups_invitations_accept: Provider = { provide: 'ep:users/groups/invitations/accept', useClass: ep___users_groups_invitations_accept.default }; +const $users_groups_invitations_reject: Provider = { provide: 'ep:users/groups/invitations/reject', useClass: ep___users_groups_invitations_reject.default }; +const $users_groups_invite: Provider = { provide: 'ep:users/groups/invite', useClass: ep___users_groups_invite.default }; +const $users_groups_joined: Provider = { provide: 'ep:users/groups/joined', useClass: ep___users_groups_joined.default }; +const $users_groups_leave: Provider = { provide: 'ep:users/groups/leave', useClass: ep___users_groups_leave.default }; +const $users_groups_owned: Provider = { provide: 'ep:users/groups/owned', useClass: ep___users_groups_owned.default }; +const $users_groups_pull: Provider = { provide: 'ep:users/groups/pull', useClass: ep___users_groups_pull.default }; +const $users_groups_show: Provider = { provide: 'ep:users/groups/show', useClass: ep___users_groups_show.default }; +const $users_groups_transfer: Provider = { provide: 'ep:users/groups/transfer', useClass: ep___users_groups_transfer.default }; +const $users_groups_update: Provider = { provide: 'ep:users/groups/update', useClass: ep___users_groups_update.default }; +const $users_lists_create: Provider = { provide: 'ep:users/lists/create', useClass: ep___users_lists_create.default }; +const $users_lists_delete: Provider = { provide: 'ep:users/lists/delete', useClass: ep___users_lists_delete.default }; +const $users_lists_list: Provider = { provide: 'ep:users/lists/list', useClass: ep___users_lists_list.default }; +const $users_lists_pull: Provider = { provide: 'ep:users/lists/pull', useClass: ep___users_lists_pull.default }; +const $users_lists_push: Provider = { provide: 'ep:users/lists/push', useClass: ep___users_lists_push.default }; +const $users_lists_show: Provider = { provide: 'ep:users/lists/show', useClass: ep___users_lists_show.default }; +const $users_lists_update: Provider = { provide: 'ep:users/lists/update', useClass: ep___users_lists_update.default }; +const $users_notes: Provider = { provide: 'ep:users/notes', useClass: ep___users_notes.default }; +const $users_pages: Provider = { provide: 'ep:users/pages', useClass: ep___users_pages.default }; +const $users_reactions: Provider = { provide: 'ep:users/reactions', useClass: ep___users_reactions.default }; +const $users_recommendation: Provider = { provide: 'ep:users/recommendation', useClass: ep___users_recommendation.default }; +const $users_relation: Provider = { provide: 'ep:users/relation', useClass: ep___users_relation.default }; +const $users_reportAbuse: Provider = { provide: 'ep:users/report-abuse', useClass: ep___users_reportAbuse.default }; +const $users_searchByUsernameAndHost: Provider = { provide: 'ep:users/search-by-username-and-host', useClass: ep___users_searchByUsernameAndHost.default }; +const $users_search: Provider = { provide: 'ep:users/search', useClass: ep___users_search.default }; +const $users_show: Provider = { provide: 'ep:users/show', useClass: ep___users_show.default }; +const $users_stats: Provider = { provide: 'ep:users/stats', useClass: ep___users_stats.default }; +const $admin_driveCapOverride: Provider = { provide: 'ep:admin/drive-capacity-override', useClass: ep___admin_driveCapOverride.default }; +const $fetchRss: Provider = { provide: 'ep:fetch-rss', useClass: ep___fetchRss.default }; + +@Module({ + imports: [ + CoreModule, + ], + providers: [ + GetterService, + ApiLoggerService, + $admin_meta, + $admin_abuseUserReports, + $admin_accounts_create, + $admin_accounts_delete, + $admin_ad_create, + $admin_ad_delete, + $admin_ad_list, + $admin_ad_update, + $admin_announcements_create, + $admin_announcements_delete, + $admin_announcements_list, + $admin_announcements_update, + $admin_deleteAllFilesOfAUser, + $admin_drive_cleanRemoteFiles, + $admin_drive_cleanup, + $admin_drive_files, + $admin_drive_showFile, + $admin_emoji_addAliasesBulk, + $admin_emoji_add, + $admin_emoji_copy, + $admin_emoji_deleteBulk, + $admin_emoji_delete, + $admin_emoji_importZip, + $admin_emoji_listRemote, + $admin_emoji_list, + $admin_emoji_removeAliasesBulk, + $admin_emoji_setAliasesBulk, + $admin_emoji_setCategoryBulk, + $admin_emoji_update, + $admin_federation_deleteAllFiles, + $admin_federation_refreshRemoteInstanceMetadata, + $admin_federation_removeAllFollowing, + $admin_federation_updateInstance, + $admin_getIndexStats, + $admin_getTableStats, + $admin_getUserIps, + $admin_invite, + $admin_moderators_add, + $admin_moderators_remove, + $admin_promo_create, + $admin_queue_clear, + $admin_queue_deliverDelayed, + $admin_queue_inboxDelayed, + $admin_queue_stats, + $admin_relays_add, + $admin_relays_list, + $admin_relays_remove, + $admin_resetPassword, + $admin_resolveAbuseUserReport, + $admin_sendEmail, + $admin_serverInfo, + $admin_showModerationLogs, + $admin_showUser, + $admin_showUsers, + $admin_silenceUser, + $admin_suspendUser, + $admin_unsilenceUser, + $admin_unsuspendUser, + $admin_updateMeta, + $admin_deleteAccount, + $admin_updateUserNote, + $announcements, + $antennas_create, + $antennas_delete, + $antennas_list, + $antennas_notes, + $antennas_show, + $antennas_update, + $ap_get, + $ap_show, + $app_create, + $app_show, + $auth_accept, + $auth_session_generate, + $auth_session_show, + $auth_session_userkey, + $blocking_create, + $blocking_delete, + $blocking_list, + $channels_create, + $channels_featured, + $channels_follow, + $channels_followed, + $channels_owned, + $channels_show, + $channels_timeline, + $channels_unfollow, + $channels_update, + $charts_activeUsers, + $charts_apRequest, + $charts_drive, + $charts_federation, + $charts_hashtag, + $charts_instance, + $charts_notes, + $charts_user_drive, + $charts_user_following, + $charts_user_notes, + $charts_user_reactions, + $charts_users, + $clips_addNote, + $clips_removeNote, + $clips_create, + $clips_delete, + $clips_list, + $clips_notes, + $clips_show, + $clips_update, + $drive, + $drive_files, + $drive_files_attachedNotes, + $drive_files_checkExistence, + $drive_files_create, + $drive_files_delete, + $drive_files_findByHash, + $drive_files_find, + $drive_files_show, + $drive_files_update, + $drive_files_uploadFromUrl, + $drive_folders, + $drive_folders_create, + $drive_folders_delete, + $drive_folders_find, + $drive_folders_show, + $drive_folders_update, + $drive_stream, + $emailAddress_available, + $endpoint, + $endpoints, + $exportCustomEmojis, + $federation_followers, + $federation_following, + $federation_instances, + $federation_showInstance, + $federation_updateRemoteUser, + $federation_users, + $federation_stats, + $following_create, + $following_delete, + $following_invalidate, + $following_requests_accept, + $following_requests_cancel, + $following_requests_list, + $following_requests_reject, + $gallery_featured, + $gallery_popular, + $gallery_posts, + $gallery_posts_create, + $gallery_posts_delete, + $gallery_posts_like, + $gallery_posts_show, + $gallery_posts_unlike, + $gallery_posts_update, + $getOnlineUsersCount, + $hashtags_list, + $hashtags_search, + $hashtags_show, + $hashtags_trend, + $hashtags_users, + $i, + $i_2fa_done, + $i_2fa_keyDone, + $i_2fa_passwordLess, + $i_2fa_registerKey, + $i_2fa_register, + $i_2fa_removeKey, + $i_2fa_unregister, + $i_apps, + $i_authorizedApps, + $i_changePassword, + $i_deleteAccount, + $i_exportBlocking, + $i_exportFollowing, + $i_exportMute, + $i_exportNotes, + $i_exportUserLists, + $i_favorites, + $i_gallery_likes, + $i_gallery_posts, + $i_getWordMutedNotesCount, + $i_importBlocking, + $i_importFollowing, + $i_importMuting, + $i_importUserLists, + $i_notifications, + $i_pageLikes, + $i_pages, + $i_pin, + $i_readAllMessagingMessages, + $i_readAllUnreadNotes, + $i_readAnnouncement, + $i_regenerateToken, + $i_registry_getAll, + $i_registry_getDetail, + $i_registry_get, + $i_registry_keysWithType, + $i_registry_keys, + $i_registry_remove, + $i_registry_scopes, + $i_registry_set, + $i_revokeToken, + $i_signinHistory, + $i_unpin, + $i_updateEmail, + $i_update, + $i_userGroupInvites, + $i_webhooks_create, + $i_webhooks_list, + $i_webhooks_show, + $i_webhooks_update, + $i_webhooks_delete, + $messaging_history, + $messaging_messages, + $messaging_messages_create, + $messaging_messages_delete, + $messaging_messages_read, + $meta, + $miauth_genToken, + $mute_create, + $mute_delete, + $mute_list, + $my_apps, + $notes, + $notes_children, + $notes_clips, + $notes_conversation, + $notes_create, + $notes_delete, + $notes_favorites_create, + $notes_favorites_delete, + $notes_featured, + $notes_globalTimeline, + $notes_hybridTimeline, + $notes_localTimeline, + $notes_mentions, + $notes_polls_recommendation, + $notes_polls_vote, + $notes_reactions, + $notes_reactions_create, + $notes_reactions_delete, + $notes_renotes, + $notes_replies, + $notes_searchByTag, + $notes_search, + $notes_show, + $notes_state, + $notes_threadMuting_create, + $notes_threadMuting_delete, + $notes_timeline, + $notes_translate, + $notes_unrenote, + $notes_userListTimeline, + $notifications_create, + $notifications_markAllAsRead, + $notifications_read, + $pagePush, + $pages_create, + $pages_delete, + $pages_featured, + $pages_like, + $pages_show, + $pages_unlike, + $pages_update, + $ping, + $pinnedUsers, + $promo_read, + $requestResetPassword, + $resetDb, + $resetPassword, + $serverInfo, + $stats, + $sw_register, + $sw_unregister, + $test, + $username_available, + $users, + $users_clips, + $users_followers, + $users_following, + $users_gallery_posts, + $users_getFrequentlyRepliedUsers, + $users_groups_create, + $users_groups_delete, + $users_groups_invitations_accept, + $users_groups_invitations_reject, + $users_groups_invite, + $users_groups_joined, + $users_groups_leave, + $users_groups_owned, + $users_groups_pull, + $users_groups_show, + $users_groups_transfer, + $users_groups_update, + $users_lists_create, + $users_lists_delete, + $users_lists_list, + $users_lists_pull, + $users_lists_push, + $users_lists_show, + $users_lists_update, + $users_notes, + $users_pages, + $users_reactions, + $users_recommendation, + $users_relation, + $users_reportAbuse, + $users_searchByUsernameAndHost, + $users_search, + $users_show, + $users_stats, + $admin_driveCapOverride, + $fetchRss, + ], + exports: [ + $admin_meta, + $admin_abuseUserReports, + $admin_accounts_create, + $admin_accounts_delete, + $admin_ad_create, + $admin_ad_delete, + $admin_ad_list, + $admin_ad_update, + $admin_announcements_create, + $admin_announcements_delete, + $admin_announcements_list, + $admin_announcements_update, + $admin_deleteAllFilesOfAUser, + $admin_drive_cleanRemoteFiles, + $admin_drive_cleanup, + $admin_drive_files, + $admin_drive_showFile, + $admin_emoji_addAliasesBulk, + $admin_emoji_add, + $admin_emoji_copy, + $admin_emoji_deleteBulk, + $admin_emoji_delete, + $admin_emoji_importZip, + $admin_emoji_listRemote, + $admin_emoji_list, + $admin_emoji_removeAliasesBulk, + $admin_emoji_setAliasesBulk, + $admin_emoji_setCategoryBulk, + $admin_emoji_update, + $admin_federation_deleteAllFiles, + $admin_federation_refreshRemoteInstanceMetadata, + $admin_federation_removeAllFollowing, + $admin_federation_updateInstance, + $admin_getIndexStats, + $admin_getTableStats, + $admin_getUserIps, + $admin_invite, + $admin_moderators_add, + $admin_moderators_remove, + $admin_promo_create, + $admin_queue_clear, + $admin_queue_deliverDelayed, + $admin_queue_inboxDelayed, + $admin_queue_stats, + $admin_relays_add, + $admin_relays_list, + $admin_relays_remove, + $admin_resetPassword, + $admin_resolveAbuseUserReport, + $admin_sendEmail, + $admin_serverInfo, + $admin_showModerationLogs, + $admin_showUser, + $admin_showUsers, + $admin_silenceUser, + $admin_suspendUser, + $admin_unsilenceUser, + $admin_unsuspendUser, + $admin_updateMeta, + $admin_deleteAccount, + $admin_updateUserNote, + $announcements, + $antennas_create, + $antennas_delete, + $antennas_list, + $antennas_notes, + $antennas_show, + $antennas_update, + $ap_get, + $ap_show, + $app_create, + $app_show, + $auth_accept, + $auth_session_generate, + $auth_session_show, + $auth_session_userkey, + $blocking_create, + $blocking_delete, + $blocking_list, + $channels_create, + $channels_featured, + $channels_follow, + $channels_followed, + $channels_owned, + $channels_show, + $channels_timeline, + $channels_unfollow, + $channels_update, + $charts_activeUsers, + $charts_apRequest, + $charts_drive, + $charts_federation, + $charts_hashtag, + $charts_instance, + $charts_notes, + $charts_user_drive, + $charts_user_following, + $charts_user_notes, + $charts_user_reactions, + $charts_users, + $clips_addNote, + $clips_removeNote, + $clips_create, + $clips_delete, + $clips_list, + $clips_notes, + $clips_show, + $clips_update, + $drive, + $drive_files, + $drive_files_attachedNotes, + $drive_files_checkExistence, + $drive_files_create, + $drive_files_delete, + $drive_files_findByHash, + $drive_files_find, + $drive_files_show, + $drive_files_update, + $drive_files_uploadFromUrl, + $drive_folders, + $drive_folders_create, + $drive_folders_delete, + $drive_folders_find, + $drive_folders_show, + $drive_folders_update, + $drive_stream, + $emailAddress_available, + $endpoint, + $endpoints, + $exportCustomEmojis, + $federation_followers, + $federation_following, + $federation_instances, + $federation_showInstance, + $federation_updateRemoteUser, + $federation_users, + $federation_stats, + $following_create, + $following_delete, + $following_invalidate, + $following_requests_accept, + $following_requests_cancel, + $following_requests_list, + $following_requests_reject, + $gallery_featured, + $gallery_popular, + $gallery_posts, + $gallery_posts_create, + $gallery_posts_delete, + $gallery_posts_like, + $gallery_posts_show, + $gallery_posts_unlike, + $gallery_posts_update, + $getOnlineUsersCount, + $hashtags_list, + $hashtags_search, + $hashtags_show, + $hashtags_trend, + $hashtags_users, + $i, + $i_2fa_done, + $i_2fa_keyDone, + $i_2fa_passwordLess, + $i_2fa_registerKey, + $i_2fa_register, + $i_2fa_removeKey, + $i_2fa_unregister, + $i_apps, + $i_authorizedApps, + $i_changePassword, + $i_deleteAccount, + $i_exportBlocking, + $i_exportFollowing, + $i_exportMute, + $i_exportNotes, + $i_exportUserLists, + $i_favorites, + $i_gallery_likes, + $i_gallery_posts, + $i_getWordMutedNotesCount, + $i_importBlocking, + $i_importFollowing, + $i_importMuting, + $i_importUserLists, + $i_notifications, + $i_pageLikes, + $i_pages, + $i_pin, + $i_readAllMessagingMessages, + $i_readAllUnreadNotes, + $i_readAnnouncement, + $i_regenerateToken, + $i_registry_getAll, + $i_registry_getDetail, + $i_registry_get, + $i_registry_keysWithType, + $i_registry_keys, + $i_registry_remove, + $i_registry_scopes, + $i_registry_set, + $i_revokeToken, + $i_signinHistory, + $i_unpin, + $i_updateEmail, + $i_update, + $i_userGroupInvites, + $i_webhooks_create, + $i_webhooks_list, + $i_webhooks_show, + $i_webhooks_update, + $i_webhooks_delete, + $messaging_history, + $messaging_messages, + $messaging_messages_create, + $messaging_messages_delete, + $messaging_messages_read, + $meta, + $miauth_genToken, + $mute_create, + $mute_delete, + $mute_list, + $my_apps, + $notes, + $notes_children, + $notes_clips, + $notes_conversation, + $notes_create, + $notes_delete, + $notes_favorites_create, + $notes_favorites_delete, + $notes_featured, + $notes_globalTimeline, + $notes_hybridTimeline, + $notes_localTimeline, + $notes_mentions, + $notes_polls_recommendation, + $notes_polls_vote, + $notes_reactions, + $notes_reactions_create, + $notes_reactions_delete, + $notes_renotes, + $notes_replies, + $notes_searchByTag, + $notes_search, + $notes_show, + $notes_state, + $notes_threadMuting_create, + $notes_threadMuting_delete, + $notes_timeline, + $notes_translate, + $notes_unrenote, + $notes_userListTimeline, + $notifications_create, + $notifications_markAllAsRead, + $notifications_read, + $pagePush, + $pages_create, + $pages_delete, + $pages_featured, + $pages_like, + $pages_show, + $pages_unlike, + $pages_update, + $ping, + $pinnedUsers, + $promo_read, + $requestResetPassword, + $resetDb, + $resetPassword, + $serverInfo, + $stats, + $sw_register, + $sw_unregister, + $test, + $username_available, + $users, + $users_clips, + $users_followers, + $users_following, + $users_gallery_posts, + $users_getFrequentlyRepliedUsers, + $users_groups_create, + $users_groups_delete, + $users_groups_invitations_accept, + $users_groups_invitations_reject, + $users_groups_invite, + $users_groups_joined, + $users_groups_leave, + $users_groups_owned, + $users_groups_pull, + $users_groups_show, + $users_groups_transfer, + $users_groups_update, + $users_lists_create, + $users_lists_delete, + $users_lists_list, + $users_lists_pull, + $users_lists_push, + $users_lists_show, + $users_lists_update, + $users_notes, + $users_pages, + $users_reactions, + $users_recommendation, + $users_relation, + $users_reportAbuse, + $users_searchByUsernameAndHost, + $users_search, + $users_show, + $users_stats, + $admin_driveCapOverride, + $fetchRss, + ], +}) +export class EndpointsModule {} -- cgit v1.2.3-freya From 614b11951b6116a6485bee85126858189ecaa681 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 24 Sep 2022 07:15:16 +0900 Subject: refactor --- packages/backend/src/server/ServerModule.ts | 2 +- packages/backend/src/server/api/EndpointsModule.ts | 2 +- packages/backend/src/server/api/GetterService.ts | 74 ++++++++++++++++++++++ .../backend/src/server/api/common/GetterService.ts | 74 ---------------------- .../src/server/api/endpoints/admin/promo/create.ts | 2 +- .../src/server/api/endpoints/blocking/create.ts | 2 +- .../src/server/api/endpoints/blocking/delete.ts | 2 +- .../src/server/api/endpoints/clips/add-note.ts | 2 +- .../src/server/api/endpoints/clips/remove-note.ts | 2 +- .../api/endpoints/federation/update-remote-user.ts | 2 +- .../src/server/api/endpoints/following/create.ts | 2 +- .../src/server/api/endpoints/following/delete.ts | 2 +- .../server/api/endpoints/following/invalidate.ts | 2 +- .../api/endpoints/following/requests/accept.ts | 2 +- .../api/endpoints/following/requests/cancel.ts | 5 +- .../api/endpoints/following/requests/reject.ts | 2 +- .../src/server/api/endpoints/messaging/messages.ts | 5 +- .../api/endpoints/messaging/messages/create.ts | 2 +- .../src/server/api/endpoints/mute/create.ts | 2 +- .../src/server/api/endpoints/mute/delete.ts | 2 +- .../src/server/api/endpoints/notes/clips.ts | 2 +- .../src/server/api/endpoints/notes/conversation.ts | 2 +- .../src/server/api/endpoints/notes/delete.ts | 2 +- .../server/api/endpoints/notes/favorites/create.ts | 2 +- .../server/api/endpoints/notes/favorites/delete.ts | 2 +- .../src/server/api/endpoints/notes/polls/vote.ts | 2 +- .../server/api/endpoints/notes/reactions/create.ts | 2 +- .../server/api/endpoints/notes/reactions/delete.ts | 2 +- .../src/server/api/endpoints/notes/renotes.ts | 2 +- .../backend/src/server/api/endpoints/notes/show.ts | 2 +- .../api/endpoints/notes/thread-muting/create.ts | 2 +- .../api/endpoints/notes/thread-muting/delete.ts | 2 +- .../src/server/api/endpoints/notes/translate.ts | 2 +- .../src/server/api/endpoints/notes/unrenote.ts | 2 +- .../backend/src/server/api/endpoints/promo/read.ts | 2 +- .../users/get-frequently-replied-users.ts | 2 +- .../server/api/endpoints/users/groups/invite.ts | 2 +- .../src/server/api/endpoints/users/groups/pull.ts | 2 +- .../server/api/endpoints/users/groups/transfer.ts | 2 +- .../src/server/api/endpoints/users/lists/pull.ts | 2 +- .../src/server/api/endpoints/users/lists/push.ts | 2 +- .../src/server/api/endpoints/users/notes.ts | 2 +- .../src/server/api/endpoints/users/report-abuse.ts | 2 +- 43 files changed, 117 insertions(+), 119 deletions(-) create mode 100644 packages/backend/src/server/api/GetterService.ts delete mode 100644 packages/backend/src/server/api/common/GetterService.ts (limited to 'packages/backend/src/server/api/EndpointsModule.ts') diff --git a/packages/backend/src/server/ServerModule.ts b/packages/backend/src/server/ServerModule.ts index f05eda1cb8..474edafe41 100644 --- a/packages/backend/src/server/ServerModule.ts +++ b/packages/backend/src/server/ServerModule.ts @@ -7,7 +7,7 @@ import { MediaProxyServerService } from './MediaProxyServerService.js'; import { NodeinfoServerService } from './NodeinfoServerService.js'; import { ServerService } from './ServerService.js'; import { WellKnownServerService } from './WellKnownServerService.js'; -import { GetterService } from './api/common/GetterService.js'; +import { GetterService } from './api/GetterService.js'; import { DiscordServerService } from './api/integration/DiscordServerService.js'; import { GithubServerService } from './api/integration/GithubServerService.js'; import { TwitterServerService } from './api/integration/TwitterServerService.js'; diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index d2dfd78fd4..e41ed388b4 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -313,7 +313,7 @@ import * as ep___users_show from './endpoints/users/show.js'; import * as ep___users_stats from './endpoints/users/stats.js'; import * as ep___fetchRss from './endpoints/fetch-rss.js'; import * as ep___admin_driveCapOverride from './endpoints/admin/drive-capacity-override.js'; -import { GetterService } from './common/GetterService.js'; +import { GetterService } from './GetterService.js'; import { ApiLoggerService } from './ApiLoggerService.js'; import type { Provider } from '@nestjs/common'; diff --git a/packages/backend/src/server/api/GetterService.ts b/packages/backend/src/server/api/GetterService.ts new file mode 100644 index 0000000000..70ab46ec35 --- /dev/null +++ b/packages/backend/src/server/api/GetterService.ts @@ -0,0 +1,74 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI } from '@/di-symbols.js'; +import type { NotesRepository, UsersRepository } from '@/models/index.js'; +import { IdentifiableError } from '@/misc/identifiable-error.js'; +import type { User } from '@/models/entities/User.js'; +import type { Note } from '@/models/entities/Note.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; + +@Injectable() +export class GetterService { + constructor( + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + + private userEntityService: UserEntityService, + ) { + } + + /** + * Get note for API processing + */ + public async getNote(noteId: Note['id']) { + const note = await this.notesRepository.findOneBy({ id: noteId }); + + if (note == null) { + throw new IdentifiableError('9725d0ce-ba28-4dde-95a7-2cbb2c15de24', 'No such note.'); + } + + return note; + } + + /** + * Get user for API processing + */ + public async getUser(userId: User['id']) { + const user = await this.usersRepository.findOneBy({ id: userId }); + + if (user == null) { + throw new IdentifiableError('15348ddd-432d-49c2-8a5a-8069753becff', 'No such user.'); + } + + return user; + } + + /** + * Get remote user for API processing + */ + public async getRemoteUser(userId: User['id']) { + const user = await this.getUser(userId); + + if (!this.userEntityService.isRemoteUser(user)) { + throw new Error('user is not a remote user'); + } + + return user; + } + + /** + * Get local user for API processing + */ + public async getLocalUser(userId: User['id']) { + const user = await this.getUser(userId); + + if (!this.userEntityService.isLocalUser(user)) { + throw new Error('user is not a local user'); + } + + return user; + } +} + diff --git a/packages/backend/src/server/api/common/GetterService.ts b/packages/backend/src/server/api/common/GetterService.ts deleted file mode 100644 index 70ab46ec35..0000000000 --- a/packages/backend/src/server/api/common/GetterService.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { DI } from '@/di-symbols.js'; -import type { NotesRepository, UsersRepository } from '@/models/index.js'; -import { IdentifiableError } from '@/misc/identifiable-error.js'; -import type { User } from '@/models/entities/User.js'; -import type { Note } from '@/models/entities/Note.js'; -import { UserEntityService } from '@/core/entities/UserEntityService.js'; - -@Injectable() -export class GetterService { - constructor( - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.notesRepository) - private notesRepository: NotesRepository, - - private userEntityService: UserEntityService, - ) { - } - - /** - * Get note for API processing - */ - public async getNote(noteId: Note['id']) { - const note = await this.notesRepository.findOneBy({ id: noteId }); - - if (note == null) { - throw new IdentifiableError('9725d0ce-ba28-4dde-95a7-2cbb2c15de24', 'No such note.'); - } - - return note; - } - - /** - * Get user for API processing - */ - public async getUser(userId: User['id']) { - const user = await this.usersRepository.findOneBy({ id: userId }); - - if (user == null) { - throw new IdentifiableError('15348ddd-432d-49c2-8a5a-8069753becff', 'No such user.'); - } - - return user; - } - - /** - * Get remote user for API processing - */ - public async getRemoteUser(userId: User['id']) { - const user = await this.getUser(userId); - - if (!this.userEntityService.isRemoteUser(user)) { - throw new Error('user is not a remote user'); - } - - return user; - } - - /** - * Get local user for API processing - */ - public async getLocalUser(userId: User['id']) { - const user = await this.getUser(userId); - - if (!this.userEntityService.isLocalUser(user)) { - throw new Error('user is not a local user'); - } - - return user; - } -} - diff --git a/packages/backend/src/server/api/endpoints/admin/promo/create.ts b/packages/backend/src/server/api/endpoints/admin/promo/create.ts index 0cff6bae6a..bee1ffbaee 100644 --- a/packages/backend/src/server/api/endpoints/admin/promo/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/promo/create.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { PromoNotesRepository } from '@/models/index.js'; -import { GetterService } from '@/server/api/common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/blocking/create.ts b/packages/backend/src/server/api/endpoints/blocking/create.ts index aa6d2ecf20..c468010bce 100644 --- a/packages/backend/src/server/api/endpoints/blocking/create.ts +++ b/packages/backend/src/server/api/endpoints/blocking/create.ts @@ -6,7 +6,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; -import { GetterService } from '../../common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; export const meta = { tags: ['account'], diff --git a/packages/backend/src/server/api/endpoints/blocking/delete.ts b/packages/backend/src/server/api/endpoints/blocking/delete.ts index 46a499943c..46dd26a45a 100644 --- a/packages/backend/src/server/api/endpoints/blocking/delete.ts +++ b/packages/backend/src/server/api/endpoints/blocking/delete.ts @@ -6,7 +6,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; -import { GetterService } from '../../common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; export const meta = { tags: ['account'], diff --git a/packages/backend/src/server/api/endpoints/clips/add-note.ts b/packages/backend/src/server/api/endpoints/clips/add-note.ts index 77d02815e0..a242124e6a 100644 --- a/packages/backend/src/server/api/endpoints/clips/add-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/add-note.ts @@ -4,7 +4,7 @@ import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; import type { ClipNotesRepository, ClipsRepository } from '@/models/index.js'; import { ApiError } from '../../error.js'; -import { GetterService } from '../../common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; export const meta = { tags: ['account', 'notes', 'clips'], diff --git a/packages/backend/src/server/api/endpoints/clips/remove-note.ts b/packages/backend/src/server/api/endpoints/clips/remove-note.ts index 93805af089..55778c7ecb 100644 --- a/packages/backend/src/server/api/endpoints/clips/remove-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/remove-note.ts @@ -3,7 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { ClipNotesRepository, ClipsRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; -import { GetterService } from '../../common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; export const meta = { tags: ['account', 'notes', 'clips'], diff --git a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts index 57497bbf62..30e77aab45 100644 --- a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts +++ b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApPersonService } from '@/core/remote/activitypub/models/ApPersonService.js'; -import { GetterService } from '../../common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; export const meta = { tags: ['federation'], diff --git a/packages/backend/src/server/api/endpoints/following/create.ts b/packages/backend/src/server/api/endpoints/following/create.ts index be322e2896..f879429372 100644 --- a/packages/backend/src/server/api/endpoints/following/create.ts +++ b/packages/backend/src/server/api/endpoints/following/create.ts @@ -7,7 +7,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; -import { GetterService } from '../../common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; export const meta = { tags: ['following', 'users'], diff --git a/packages/backend/src/server/api/endpoints/following/delete.ts b/packages/backend/src/server/api/endpoints/following/delete.ts index afb59dd2c2..4f12db1273 100644 --- a/packages/backend/src/server/api/endpoints/following/delete.ts +++ b/packages/backend/src/server/api/endpoints/following/delete.ts @@ -6,7 +6,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; -import { GetterService } from '../../common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; export const meta = { tags: ['following', 'users'], diff --git a/packages/backend/src/server/api/endpoints/following/invalidate.ts b/packages/backend/src/server/api/endpoints/following/invalidate.ts index e67e136ead..22304cacda 100644 --- a/packages/backend/src/server/api/endpoints/following/invalidate.ts +++ b/packages/backend/src/server/api/endpoints/following/invalidate.ts @@ -6,7 +6,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; -import { GetterService } from '../../common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; export const meta = { tags: ['following', 'users'], diff --git a/packages/backend/src/server/api/endpoints/following/requests/accept.ts b/packages/backend/src/server/api/endpoints/following/requests/accept.ts index 5f082c087a..dcb98485da 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/accept.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/accept.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GetterService } from '@/server/api/common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; import { ApiError } from '../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts index 213b5ce32e..f39c4e3767 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts @@ -1,10 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { FollowingsRepository } from '@/models/index.js'; -import type { UsersRepository } from '@/models/index.js'; +import type { FollowingsRepository, UsersRepository } from '@/models/index.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { GetterService } from '@/server/api/common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/following/requests/reject.ts b/packages/backend/src/server/api/endpoints/following/requests/reject.ts index 663c659bf3..ab5706e8ef 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/reject.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/reject.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GetterService } from '@/server/api/common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; import { ApiError } from '../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/messaging/messages.ts b/packages/backend/src/server/api/endpoints/messaging/messages.ts index f563da3278..3673e252ae 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages.ts @@ -1,15 +1,14 @@ import { Inject, Injectable } from '@nestjs/common'; import { Brackets } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository } from '@/models/index.js'; -import type { UserGroupsRepository, MessagingMessagesRepository, UserGroupJoiningsRepository } from '@/models/index.js'; +import type { UsersRepository, UserGroupsRepository, MessagingMessagesRepository, UserGroupJoiningsRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { MessagingMessageEntityService } from '@/core/entities/MessagingMessageEntityService.js'; import { MessagingService } from '@/core/MessagingService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; -import { GetterService } from '../../common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; export const meta = { tags: ['messaging'], diff --git a/packages/backend/src/server/api/endpoints/messaging/messages/create.ts b/packages/backend/src/server/api/endpoints/messaging/messages/create.ts index f61662af75..00e65b4875 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/create.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/create.ts @@ -3,7 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { BlockingsRepository, UserGroupJoiningsRepository, DriveFilesRepository, UserGroupsRepository } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import type { UserGroup } from '@/models/entities/UserGroup.js'; -import { GetterService } from '@/server/api/common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; import { MessagingService } from '@/core/MessagingService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/mute/create.ts b/packages/backend/src/server/api/endpoints/mute/create.ts index 3b4507281f..5ead470314 100644 --- a/packages/backend/src/server/api/endpoints/mute/create.ts +++ b/packages/backend/src/server/api/endpoints/mute/create.ts @@ -6,7 +6,7 @@ import type { Muting } from '@/models/entities/Muting.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; -import { GetterService } from '../../common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; export const meta = { tags: ['account'], diff --git a/packages/backend/src/server/api/endpoints/mute/delete.ts b/packages/backend/src/server/api/endpoints/mute/delete.ts index 2fc5cee95e..612c4a4c04 100644 --- a/packages/backend/src/server/api/endpoints/mute/delete.ts +++ b/packages/backend/src/server/api/endpoints/mute/delete.ts @@ -4,7 +4,7 @@ import type { MutingsRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; -import { GetterService } from '../../common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; export const meta = { tags: ['account'], diff --git a/packages/backend/src/server/api/endpoints/notes/clips.ts b/packages/backend/src/server/api/endpoints/notes/clips.ts index 579466d4fd..d5caec6e1d 100644 --- a/packages/backend/src/server/api/endpoints/notes/clips.ts +++ b/packages/backend/src/server/api/endpoints/notes/clips.ts @@ -5,7 +5,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; -import { GetterService } from '../../common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; export const meta = { tags: ['clips', 'notes'], diff --git a/packages/backend/src/server/api/endpoints/notes/conversation.ts b/packages/backend/src/server/api/endpoints/notes/conversation.ts index ddfee31525..5ecf7cf458 100644 --- a/packages/backend/src/server/api/endpoints/notes/conversation.ts +++ b/packages/backend/src/server/api/endpoints/notes/conversation.ts @@ -5,7 +5,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; -import { GetterService } from '../../common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; export const meta = { tags: ['notes'], diff --git a/packages/backend/src/server/api/endpoints/notes/delete.ts b/packages/backend/src/server/api/endpoints/notes/delete.ts index 5765dfe66f..3c6e7bf768 100644 --- a/packages/backend/src/server/api/endpoints/notes/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/delete.ts @@ -5,7 +5,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteDeleteService } from '@/core/NoteDeleteService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; -import { GetterService } from '../../common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; export const meta = { tags: ['notes'], diff --git a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts index edbd300e08..e742c1bb35 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { NoteFavoritesRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GetterService } from '@/server/api/common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts b/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts index 8f4f2b2b9e..bb3a7c501a 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GetterService } from '@/server/api/common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; import { DI } from '@/di-symbols.js'; import type { NoteFavoritesRepository } from '@/models/index.js'; import { ApiError } from '../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts index 515f03dcce..6b3b062c15 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts @@ -4,7 +4,7 @@ import type { UsersRepository, BlockingsRepository, PollsRepository, PollVotesRe import type { IRemoteUser } from '@/models/entities/User.js'; import { IdService } from '@/core/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GetterService } from '@/server/api/common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; import { QueueService } from '@/core/QueueService.js'; import { PollService } from '@/core/PollService.js'; import { ApRendererService } from '@/core/remote/activitypub/ApRendererService.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/reactions/create.ts b/packages/backend/src/server/api/endpoints/notes/reactions/create.ts index 2af734307d..839f893db2 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions/create.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GetterService } from '@/server/api/common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; import { ReactionService } from '@/core/ReactionService.js'; import { ApiError } from '../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts b/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts index 31ed962922..cf90d7b5f6 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts @@ -1,7 +1,7 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GetterService } from '@/server/api/common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; import { ReactionService } from '@/core/ReactionService.js'; import { ApiError } from '../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts index 97ef1a17ec..026a1baa3e 100644 --- a/packages/backend/src/server/api/endpoints/notes/renotes.ts +++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts @@ -5,7 +5,7 @@ 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 { GetterService } from '../../common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; export const meta = { tags: ['notes'], diff --git a/packages/backend/src/server/api/endpoints/notes/show.ts b/packages/backend/src/server/api/endpoints/notes/show.ts index 7849cfa401..6b1b84a18e 100644 --- a/packages/backend/src/server/api/endpoints/notes/show.ts +++ b/packages/backend/src/server/api/endpoints/notes/show.ts @@ -4,7 +4,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; -import { GetterService } from '../../common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; export const meta = { tags: ['notes'], diff --git a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts index b8ddf83f3c..140614d36e 100644 --- a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { NotesRepository, NoteThreadMutingsRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GetterService } from '@/server/api/common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; import { NoteReadService } from '@/core/NoteReadService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts index 54e9b4939f..30016d48bc 100644 --- a/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { NoteThreadMutingsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GetterService } from '@/server/api/common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/translate.ts b/packages/backend/src/server/api/endpoints/notes/translate.ts index 7a3daf741e..ec16965998 100644 --- a/packages/backend/src/server/api/endpoints/notes/translate.ts +++ b/packages/backend/src/server/api/endpoints/notes/translate.ts @@ -9,7 +9,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { MetaService } from '@/core/MetaService.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; import { ApiError } from '../../error.js'; -import { GetterService } from '../../common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; export const meta = { tags: ['notes'], diff --git a/packages/backend/src/server/api/endpoints/notes/unrenote.ts b/packages/backend/src/server/api/endpoints/notes/unrenote.ts index 7378c4b600..74e459b426 100644 --- a/packages/backend/src/server/api/endpoints/notes/unrenote.ts +++ b/packages/backend/src/server/api/endpoints/notes/unrenote.ts @@ -5,7 +5,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteDeleteService } from '@/core/NoteDeleteService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; -import { GetterService } from '../../common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; export const meta = { tags: ['notes'], diff --git a/packages/backend/src/server/api/endpoints/promo/read.ts b/packages/backend/src/server/api/endpoints/promo/read.ts index a28229add3..90febdbce7 100644 --- a/packages/backend/src/server/api/endpoints/promo/read.ts +++ b/packages/backend/src/server/api/endpoints/promo/read.ts @@ -4,7 +4,7 @@ import { IdService } from '@/core/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; -import { GetterService } from '../../common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; export const meta = { tags: ['notes'], diff --git a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts index 22c49860bd..09f6acde9c 100644 --- a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts +++ b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts @@ -6,7 +6,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; -import { GetterService } from '../../common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; export const meta = { tags: ['users'], diff --git a/packages/backend/src/server/api/endpoints/users/groups/invite.ts b/packages/backend/src/server/api/endpoints/users/groups/invite.ts index 127f4ca65b..2e040c0601 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invite.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invite.ts @@ -3,7 +3,7 @@ import type { UserGroupsRepository, UserGroupJoiningsRepository, UserGroupInvita import { IdService } from '@/core/IdService.js'; import type { UserGroupInvitation } from '@/models/entities/UserGroupInvitation.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GetterService } from '@/server/api/common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; import { CreateNotificationService } from '@/core/CreateNotificationService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/users/groups/pull.ts b/packages/backend/src/server/api/endpoints/users/groups/pull.ts index 3474f22c6e..409006b0b0 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/pull.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/pull.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserGroupsRepository, UserGroupJoiningsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GetterService } from '@/server/api/common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/users/groups/transfer.ts b/packages/backend/src/server/api/endpoints/users/groups/transfer.ts index e0b2b13c7f..3130d98ed1 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/transfer.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/transfer.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserGroupsRepository, UserGroupJoiningsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserGroupEntityService } from '@/core/entities/UserGroupEntityService.js'; -import { GetterService } from '@/server/api/common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/users/lists/pull.ts b/packages/backend/src/server/api/endpoints/users/lists/pull.ts index 7e54d33376..d2dd5731ee 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/pull.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/pull.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserListsRepository, UserListJoiningsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { GetterService } from '@/server/api/common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/users/lists/push.ts b/packages/backend/src/server/api/endpoints/users/lists/push.ts index 06ea43d654..c3a1308286 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/push.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/push.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserListsRepository, UserListJoiningsRepository, BlockingsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GetterService } from '@/server/api/common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; import { UserListService } from '@/core/UserListService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index d2c9616f68..aab32cc58c 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -5,8 +5,8 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; +import { GetterService } from '@/server/api/GetterService.js'; import { ApiError } from '../../error.js'; -import { GetterService } from '../../common/GetterService.js'; export const meta = { tags: ['users', 'notes'], 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 bb37dd2715..13badab727 100644 --- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts +++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts @@ -8,7 +8,7 @@ import { MetaService } from '@/core/MetaService.js'; import { EmailService } from '@/core/EmailService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; -import { GetterService } from '../../common/GetterService.js'; +import { GetterService } from '@/server/api/GetterService.js'; export const meta = { tags: ['users'], -- cgit v1.2.3-freya From 4ecc42744c3c8b68e38f58bfe03919bf437f137a Mon Sep 17 00:00:00 2001 From: tamaina Date: Sun, 18 Dec 2022 01:59:59 +0900 Subject: enhance: Implement the toggle to (or not to) close push notifications when notifications or messages are read (#9219) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * create file * wip * fix * wip * tabun dekita * :v: * implement subscribe push notification button to tutorial * check-exists→show-registration * add column sendReadMessage * fix migration file * sw api * change PushNotificationService * wip * :v: * fix tutorial footer flex --- locales/ja-JP.yml | 11 ++ ...138716634-whetherPushNotifyToSendReadMessage.js | 11 ++ .../backend/src/core/PushNotificationService.ts | 8 + .../backend/src/models/entities/SwSubscription.ts | 5 + packages/backend/src/server/api/EndpointsModule.ts | 6 + packages/backend/src/server/api/endpoints.ts | 4 + .../src/server/api/endpoints/sw/register.ts | 20 +++ .../server/api/endpoints/sw/show-registration.ts | 66 ++++++++ .../src/server/api/endpoints/sw/unregister.ts | 4 +- .../server/api/endpoints/sw/update-registration.ts | 82 ++++++++++ .../components/MkPushNotificationAllowButton.vue | 167 +++++++++++++++++++++ .../client/src/pages/settings/notifications.vue | 30 ++++ packages/client/src/pages/timeline.tutorial.vue | 116 ++++++++------ packages/client/src/scripts/initialize-sw.ts | 55 ------- 14 files changed, 481 insertions(+), 104 deletions(-) create mode 100644 packages/backend/migration/1669138716634-whetherPushNotifyToSendReadMessage.js create mode 100644 packages/backend/src/server/api/endpoints/sw/show-registration.ts create mode 100644 packages/backend/src/server/api/endpoints/sw/update-registration.ts create mode 100644 packages/client/src/components/MkPushNotificationAllowButton.vue (limited to 'packages/backend/src/server/api/EndpointsModule.ts') diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 3fba36a754..3a53f470e3 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -13,6 +13,7 @@ fetchingAsApObject: "連合に照会中" ok: "OK" gotIt: "わかった" cancel: "キャンセル" +noThankYou: "やめておく" enterUsername: "ユーザー名を入力" renotedBy: "{user}がRenote" noNotes: "ノートはありません" @@ -898,6 +899,13 @@ navbar: "ナビゲーションバー" shuffle: "シャッフル" account: "アカウント" move: "移動" +pushNotification: "プッシュ通知" +subscribePushNotification: "プッシュ通知を有効化" +unsubscribePushNotification: "プッシュ通知を停止する" +pushNotificationAlreadySubscribed: "プッシュ通知は有効です" +pushNotificationNotSupported: "ブラウザかインスタンスがプッシュ通知に非対応" +sendPushNotificationReadMessage: "通知やメッセージが既読になったらプッシュ通知を削除する" +sendPushNotificationReadMessageCaption: "「{emptyPushNotificationMessage}」という通知が一瞬表示されるようになります。端末の電池消費量が増加する可能性があります。" _sensitiveMediaDetection: description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てることができます。サーバーの負荷が少し増えます。" @@ -1235,6 +1243,9 @@ _tutorial: step7_1: "これで、Misskeyの基本的な使い方の説明は終わりました。お疲れ様でした。" step7_2: "もっとMisskeyについて知りたいときは、{help}を見てみてください。" step7_3: "では、Misskeyをお楽しみください🚀" + step8_1: "最後に、プッシュ通知を有効化してみませんか?" + step8_2: "プッシュ通知を受け取ることで、Misskeyを開いていない時にもリアクションやフォロー、メンションなどに気づけます。" + step8_3: "通知の設定は後から変更できます。" _2fa: alreadyRegistered: "既に設定は完了しています。" diff --git a/packages/backend/migration/1669138716634-whetherPushNotifyToSendReadMessage.js b/packages/backend/migration/1669138716634-whetherPushNotifyToSendReadMessage.js new file mode 100644 index 0000000000..2265b00617 --- /dev/null +++ b/packages/backend/migration/1669138716634-whetherPushNotifyToSendReadMessage.js @@ -0,0 +1,11 @@ +export class whetherPushNotifyToSendReadMessage1669138716634 { + name = 'whetherPushNotifyToSendReadMessage1669138716634' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "sw_subscription" ADD "sendReadMessage" boolean NOT NULL DEFAULT false`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "sw_subscription" DROP COLUMN "sendReadMessage"`); + } +} diff --git a/packages/backend/src/core/PushNotificationService.ts b/packages/backend/src/core/PushNotificationService.ts index df5284de4b..842cd1a9f8 100644 --- a/packages/backend/src/core/PushNotificationService.ts +++ b/packages/backend/src/core/PushNotificationService.ts @@ -69,6 +69,14 @@ export class PushNotificationService { }); for (const subscription of subscriptions) { + // Continue if sendReadMessage is false + if ([ + 'readNotifications', + 'readAllNotifications', + 'readAllMessagingMessages', + 'readAllMessagingMessagesOfARoom', + ].includes(type) && !subscription.sendReadMessage) continue; + const pushSubscription = { endpoint: subscription.endpoint, keys: { diff --git a/packages/backend/src/models/entities/SwSubscription.ts b/packages/backend/src/models/entities/SwSubscription.ts index 51b9786e96..0658294983 100644 --- a/packages/backend/src/models/entities/SwSubscription.ts +++ b/packages/backend/src/models/entities/SwSubscription.ts @@ -34,4 +34,9 @@ export class SwSubscription { length: 128, }) public publickey: string; + + @Column('boolean', { + default: false, + }) + public sendReadMessage: boolean; } diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index e41ed388b4..647f60317a 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -272,6 +272,8 @@ import * as ep___resetDb from './endpoints/reset-db.js'; import * as ep___resetPassword from './endpoints/reset-password.js'; import * as ep___serverInfo from './endpoints/server-info.js'; import * as ep___stats from './endpoints/stats.js'; +import * as ep___sw_show_registration from './endpoints/sw/show-registration.js'; +import * as ep___sw_update_registration from './endpoints/sw/update-registration.js'; import * as ep___sw_register from './endpoints/sw/register.js'; import * as ep___sw_unregister from './endpoints/sw/unregister.js'; import * as ep___test from './endpoints/test.js'; @@ -588,6 +590,8 @@ const $resetDb: Provider = { provide: 'ep:reset-db', useClass: ep___resetDb.defa const $resetPassword: Provider = { provide: 'ep:reset-password', useClass: ep___resetPassword.default }; const $serverInfo: Provider = { provide: 'ep:server-info', useClass: ep___serverInfo.default }; const $stats: Provider = { provide: 'ep:stats', useClass: ep___stats.default }; +const $sw_show_registration: Provider = { provide: 'ep:sw/show-registration', useClass: ep___sw_show_registration.default }; +const $sw_update_registration: Provider = { provide: 'ep:sw/update-registration', useClass: ep___sw_update_registration.default }; const $sw_register: Provider = { provide: 'ep:sw/register', useClass: ep___sw_register.default }; const $sw_unregister: Provider = { provide: 'ep:sw/unregister', useClass: ep___sw_unregister.default }; const $test: Provider = { provide: 'ep:test', useClass: ep___test.default }; @@ -908,6 +912,8 @@ const $fetchRss: Provider = { provide: 'ep:fetch-rss', useClass: ep___fetchRss.d $resetPassword, $serverInfo, $stats, + $sw_show_registration, + $sw_update_registration, $sw_register, $sw_unregister, $test, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index b2ab36e070..6d10cb8f35 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -271,6 +271,8 @@ import * as ep___resetDb from './endpoints/reset-db.js'; import * as ep___resetPassword from './endpoints/reset-password.js'; import * as ep___serverInfo from './endpoints/server-info.js'; import * as ep___stats from './endpoints/stats.js'; +import * as ep___sw_show_registration from './endpoints/sw/show-registration.js'; +import * as ep___sw_update_registration from './endpoints/sw/update-registration.js'; import * as ep___sw_register from './endpoints/sw/register.js'; import * as ep___sw_unregister from './endpoints/sw/unregister.js'; import * as ep___test from './endpoints/test.js'; @@ -585,6 +587,8 @@ const eps = [ ['reset-password', ep___resetPassword], ['server-info', ep___serverInfo], ['stats', ep___stats], + ['sw/show-registration', ep___sw_show_registration], + ['sw/update-registration', ep___sw_update_registration], ['sw/register', ep___sw_register], ['sw/unregister', ep___sw_unregister], ['test', ep___test], diff --git a/packages/backend/src/server/api/endpoints/sw/register.ts b/packages/backend/src/server/api/endpoints/sw/register.ts index ddec877dd4..bfd5de7b00 100644 --- a/packages/backend/src/server/api/endpoints/sw/register.ts +++ b/packages/backend/src/server/api/endpoints/sw/register.ts @@ -25,6 +25,18 @@ export const meta = { type: 'string', optional: false, nullable: true, }, + userId: { + type: 'string', + optional: false, nullable: false, + }, + endpoint: { + type: 'string', + optional: false, nullable: false, + }, + sendReadMessage: { + type: 'boolean', + optional: false, nullable: false, + }, }, }, } as const; @@ -35,6 +47,7 @@ export const paramDef = { endpoint: { type: 'string' }, auth: { type: 'string' }, publickey: { type: 'string' }, + sendReadMessage: { type: 'boolean', default: false }, }, required: ['endpoint', 'auth', 'publickey'], } as const; @@ -64,6 +77,9 @@ export default class extends Endpoint { return { state: 'already-subscribed' as const, key: instance.swPublicKey, + userId: me.id, + endpoint: exist.endpoint, + sendReadMessage: exist.sendReadMessage, }; } @@ -74,11 +90,15 @@ export default class extends Endpoint { endpoint: ps.endpoint, auth: ps.auth, publickey: ps.publickey, + sendReadMessage: ps.sendReadMessage, }); return { state: 'subscribed' as const, key: instance.swPublicKey, + userId: me.id, + endpoint: ps.endpoint, + sendReadMessage: ps.sendReadMessage, }; }); } diff --git a/packages/backend/src/server/api/endpoints/sw/show-registration.ts b/packages/backend/src/server/api/endpoints/sw/show-registration.ts new file mode 100644 index 0000000000..bede10be5c --- /dev/null +++ b/packages/backend/src/server/api/endpoints/sw/show-registration.ts @@ -0,0 +1,66 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { SwSubscriptionsRepository } from '@/models/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; + +export const meta = { + tags: ['account'], + + requireCredential: true, + + description: 'Check push notification registration exists.', + + res: { + type: 'object', + optional: false, nullable: true, + properties: { + userId: { + type: 'string', + optional: false, nullable: false, + }, + endpoint: { + type: 'string', + optional: false, nullable: false, + }, + sendReadMessage: { + type: 'boolean', + optional: false, nullable: false, + }, + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + endpoint: { type: 'string' }, + }, + required: ['endpoint'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + @Inject(DI.swSubscriptionsRepository) + private swSubscriptionsRepository: SwSubscriptionsRepository, + ) { + super(meta, paramDef, async (ps, me) => { + // if already subscribed + const exist = await this.swSubscriptionsRepository.findOneBy({ + userId: me.id, + endpoint: ps.endpoint, + }); + + if (exist != null) { + return { + userId: exist.userId, + endpoint: exist.endpoint, + sendReadMessage: exist.sendReadMessage, + }; + } + + return null; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/sw/unregister.ts b/packages/backend/src/server/api/endpoints/sw/unregister.ts index 5772eeee26..f12b98617d 100644 --- a/packages/backend/src/server/api/endpoints/sw/unregister.ts +++ b/packages/backend/src/server/api/endpoints/sw/unregister.ts @@ -6,7 +6,7 @@ import { DI } from '@/di-symbols.js'; export const meta = { tags: ['account'], - requireCredential: true, + requireCredential: false, description: 'Unregister from receiving push notifications.', } as const; @@ -28,7 +28,7 @@ export default class extends Endpoint { ) { super(meta, paramDef, async (ps, me) => { await this.swSubscriptionsRepository.delete({ - userId: me.id, + ...(me ? { userId: me.id } : {}), endpoint: ps.endpoint, }); }); diff --git a/packages/backend/src/server/api/endpoints/sw/update-registration.ts b/packages/backend/src/server/api/endpoints/sw/update-registration.ts new file mode 100644 index 0000000000..9f08c8148d --- /dev/null +++ b/packages/backend/src/server/api/endpoints/sw/update-registration.ts @@ -0,0 +1,82 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { SwSubscriptionsRepository } from '@/models/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['account'], + + requireCredential: true, + + description: 'Update push notification registration.', + + res: { + type: 'object', + optional: false, nullable: false, + properties: { + userId: { + type: 'string', + optional: false, nullable: false, + }, + endpoint: { + type: 'string', + optional: false, nullable: false, + }, + sendReadMessage: { + type: 'boolean', + optional: false, nullable: false, + }, + }, + }, + errors: { + noSuchRegistration: { + message: 'No such registration.', + code: 'NO_SUCH_REGISTRATION', + id: ' b09d8066-8064-5613-efb6-0e963b21d012', + }, + } +} as const; + +export const paramDef = { + type: 'object', + properties: { + endpoint: { type: 'string' }, + sendReadMessage: { type: 'boolean' }, + }, + required: ['endpoint'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + @Inject(DI.swSubscriptionsRepository) + private swSubscriptionsRepository: SwSubscriptionsRepository, + ) { + super(meta, paramDef, async (ps, me) => { + const swSubscription = await this.swSubscriptionsRepository.findOneBy({ + userId: me.id, + endpoint: ps.endpoint, + }); + + if (swSubscription === null) { + throw new ApiError(meta.errors.noSuchRegistration); + } + + if (ps.sendReadMessage !== undefined) { + swSubscription.sendReadMessage = ps.sendReadMessage; + } + + await this.swSubscriptionsRepository.update(swSubscription.id, { + sendReadMessage: swSubscription.sendReadMessage, + }); + + return { + userId: swSubscription.userId, + endpoint: swSubscription.endpoint, + sendReadMessage: swSubscription.sendReadMessage, + }; + }); + } +} diff --git a/packages/client/src/components/MkPushNotificationAllowButton.vue b/packages/client/src/components/MkPushNotificationAllowButton.vue new file mode 100644 index 0000000000..a762914e64 --- /dev/null +++ b/packages/client/src/components/MkPushNotificationAllowButton.vue @@ -0,0 +1,167 @@ + + + diff --git a/packages/client/src/pages/settings/notifications.vue b/packages/client/src/pages/settings/notifications.vue index 5703e0c6b6..77ec567da4 100644 --- a/packages/client/src/pages/settings/notifications.vue +++ b/packages/client/src/pages/settings/notifications.vue @@ -6,6 +6,18 @@ {{ i18n.ts.markAsReadAllUnreadNotes }} {{ i18n.ts.markAsReadAllTalkMessages }} + + + + + + + + @@ -15,10 +27,16 @@ import { notificationTypes } from 'misskey-js'; import FormButton from '@/components/MkButton.vue'; import FormLink from '@/components/form/link.vue'; import FormSection from '@/components/form/section.vue'; +import FormSwitch from '@/components/form/switch.vue'; import * as os from '@/os'; import { $i } from '@/account'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; +import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue'; + +let allowButton = $ref>(); +let pushRegistrationInServer = $computed(() => allowButton?.pushRegistrationInServer); +let sendReadMessage = $computed(() => pushRegistrationInServer?.sendReadMessage || false); async function readAllUnreadNotes() { await os.api('i/read-all-unread-notes'); @@ -49,6 +67,18 @@ function configure() { }, 'closed'); } +function onChangeSendReadMessage(v: boolean) { + if (!pushRegistrationInServer) return; + + os.apiWithDialog('sw/update-registration', { + endpoint: pushRegistrationInServer.endpoint, + sendReadMessage: v, + }).then(res => { + if (!allowButton) return; + allowButton.pushRegistrationInServer = res; + }); +} + const headerActions = $computed(() => []); const headerTabs = $computed(() => []); diff --git a/packages/client/src/pages/timeline.tutorial.vue b/packages/client/src/pages/timeline.tutorial.vue index 7f08ccc2a1..9683cc22a5 100644 --- a/packages/client/src/pages/timeline.tutorial.vue +++ b/packages/client/src/pages/timeline.tutorial.vue @@ -1,6 +1,17 @@
{{ i18n.ts._tutorial.step5_3 }}
- {{ i18n.ts._tutorial.step5_4 }} + {{ i18n.ts._tutorial.step5_4 }}
{{ i18n.ts._tutorial.step6_1 }}
@@ -48,19 +59,20 @@
{{ i18n.ts._tutorial.step7_3 }}
+
+
{{ i18n.ts._tutorial.step8_1 }}
+
{{ i18n.ts._tutorial.step8_2 }}
+ {{ i18n.ts._tutorial.step8_3 }} +
- @@ -68,53 +80,63 @@ - diff --git a/packages/client/src/scripts/initialize-sw.ts b/packages/client/src/scripts/initialize-sw.ts index 7bacfbdf00..de52f30523 100644 --- a/packages/client/src/scripts/initialize-sw.ts +++ b/packages/client/src/scripts/initialize-sw.ts @@ -1,6 +1,3 @@ -import { instance } from '@/instance'; -import { $i } from '@/account'; -import { api } from '@/os'; import { lang } from '@/config'; export async function initializeSw() { @@ -12,57 +9,5 @@ export async function initializeSw() { msg: 'initialize', lang, }); - - if (instance.swPublickey && ('PushManager' in window) && $i && $i.token) { - // SEE: https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#Parameters - registration.pushManager.subscribe({ - userVisibleOnly: true, - applicationServerKey: urlBase64ToUint8Array(instance.swPublickey) - }) - .then(subscription => { - function encode(buffer: ArrayBuffer | null) { - return btoa(String.fromCharCode.apply(null, new Uint8Array(buffer))); - } - - // Register - api('sw/register', { - endpoint: subscription.endpoint, - auth: encode(subscription.getKey('auth')), - publickey: encode(subscription.getKey('p256dh')) - }); - }) - // When subscribe failed - .catch(async (err: Error) => { - // 通知が許可されていなかったとき - if (err.name === 'NotAllowedError') { - return; - } - - // 違うapplicationServerKey (または gcm_sender_id)のサブスクリプションが - // 既に存在していることが原因でエラーになった可能性があるので、 - // そのサブスクリプションを解除しておく - const subscription = await registration.pushManager.getSubscription(); - if (subscription) subscription.unsubscribe(); - }); - } }); } - -/** - * Convert the URL safe base64 string to a Uint8Array - * @param base64String base64 string - */ -function urlBase64ToUint8Array(base64String: string): Uint8Array { - const padding = '='.repeat((4 - base64String.length % 4) % 4); - const base64 = (base64String + padding) - .replace(/-/g, '+') - .replace(/_/g, '/'); - - const rawData = window.atob(base64); - const outputArray = new Uint8Array(rawData.length); - - for (let i = 0; i < rawData.length; ++i) { - outputArray[i] = rawData.charCodeAt(i); - } - return outputArray; -} -- cgit v1.2.3-freya From 3e81913b6a161cfc8405bda64b4a00e8e3b1fccd Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 25 Dec 2022 09:09:46 +0900 Subject: feat: introduce retention-rate aggregation --- CHANGELOG.md | 1 + .../1671924750884-RetentionAggregation.js | 13 ++++ .../1671926422832-RetentionAggregation2.js | 15 +++++ packages/backend/src/di-symbols.ts | 1 + packages/backend/src/models/RepositoryModule.ts | 10 ++- .../src/models/entities/RetentionAggregation.ts | 35 ++++++++++ packages/backend/src/models/index.ts | 3 + packages/backend/src/postgre.ts | 2 + packages/backend/src/queue/QueueProcessorModule.ts | 2 + .../backend/src/queue/QueueProcessorService.ts | 8 ++- .../src/queue/SystemQueueProcessorsService.ts | 5 +- .../AggregateRetentionProcessorService.ts | 75 ++++++++++++++++++++++ packages/backend/src/server/api/EndpointsModule.ts | 4 ++ packages/backend/src/server/api/endpoints.ts | 2 + .../backend/src/server/api/endpoints/retention.ts | 47 ++++++++++++++ 15 files changed, 220 insertions(+), 3 deletions(-) create mode 100644 packages/backend/migration/1671924750884-RetentionAggregation.js create mode 100644 packages/backend/migration/1671926422832-RetentionAggregation2.js create mode 100644 packages/backend/src/models/entities/RetentionAggregation.ts create mode 100644 packages/backend/src/queue/processors/AggregateRetentionProcessorService.ts create mode 100644 packages/backend/src/server/api/endpoints/retention.ts (limited to 'packages/backend/src/server/api/EndpointsModule.ts') diff --git a/CHANGELOG.md b/CHANGELOG.md index 4079840ae8..d58ff7691f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ You should also include the user name that made the change. - Push notification of Antenna note @tamaina - AVIF support @tamaina - Add Cloudflare Turnstile CAPTCHA support @CyberRex0 +- Introduce retention-rate aggregation @syuilo - Server: improve syslog performance @syuilo - Server: improve note scoring for featured notes @CyberRex0 - Server: delete outdated notifications regularly to improve db performance @syuilo diff --git a/packages/backend/migration/1671924750884-RetentionAggregation.js b/packages/backend/migration/1671924750884-RetentionAggregation.js new file mode 100644 index 0000000000..ed81a4b5e9 --- /dev/null +++ b/packages/backend/migration/1671924750884-RetentionAggregation.js @@ -0,0 +1,13 @@ +export class RetentionAggregation1671924750884 { + name = 'RetentionAggregation1671924750884' + + async up(queryRunner) { + await queryRunner.query(`CREATE TABLE "retention_aggregation" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userIds" character varying(32) array NOT NULL, "data" jsonb NOT NULL DEFAULT '{}', CONSTRAINT "PK_22aad3e8640b15fb3b90ee02d18" PRIMARY KEY ("id")); COMMENT ON COLUMN "retention_aggregation"."createdAt" IS 'The created date of the Note.'`); + await queryRunner.query(`CREATE INDEX "IDX_09f4e5b9e4a2f268d3e284e4b3" ON "retention_aggregation" ("createdAt") `); + } + + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "public"."IDX_09f4e5b9e4a2f268d3e284e4b3"`); + await queryRunner.query(`DROP TABLE "retention_aggregation"`); + } +} diff --git a/packages/backend/migration/1671926422832-RetentionAggregation2.js b/packages/backend/migration/1671926422832-RetentionAggregation2.js new file mode 100644 index 0000000000..725429e6ef --- /dev/null +++ b/packages/backend/migration/1671926422832-RetentionAggregation2.js @@ -0,0 +1,15 @@ +export class RetentionAggregation21671926422832 { + name = 'RetentionAggregation21671926422832' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "retention_aggregation" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`COMMENT ON COLUMN "retention_aggregation"."updatedAt" IS 'The updated date of the GalleryPost.'`); + await queryRunner.query(`ALTER TABLE "retention_aggregation" ADD "usersCount" integer NOT NULL`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "retention_aggregation" DROP COLUMN "usersCount"`); + await queryRunner.query(`COMMENT ON COLUMN "retention_aggregation"."updatedAt" IS 'The updated date of the GalleryPost.'`); + await queryRunner.query(`ALTER TABLE "retention_aggregation" DROP COLUMN "updatedAt"`); + } +} diff --git a/packages/backend/src/di-symbols.ts b/packages/backend/src/di-symbols.ts index cc775a9c8a..d2a361405f 100644 --- a/packages/backend/src/di-symbols.ts +++ b/packages/backend/src/di-symbols.ts @@ -68,5 +68,6 @@ export const DI = { webhooksRepository: Symbol('webhooksRepository'), adsRepository: Symbol('adsRepository'), passwordResetRequestsRepository: Symbol('passwordResetRequestsRepository'), + retentionAggregationsRepository: Symbol('retentionAggregationsRepository'), //#endregion }; diff --git a/packages/backend/src/models/RepositoryModule.ts b/packages/backend/src/models/RepositoryModule.ts index 9edef10e87..e22f0517ca 100644 --- a/packages/backend/src/models/RepositoryModule.ts +++ b/packages/backend/src/models/RepositoryModule.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { User, Note, Announcement, AnnouncementRead, App, NoteFavorite, NoteThreadMuting, NoteReaction, NoteUnread, Notification, Poll, PollVote, UserProfile, UserKeypair, UserPending, AttestationChallenge, UserSecurityKey, UserPublickey, UserList, UserListJoining, UserGroup, UserGroupJoining, UserGroupInvitation, UserNotePining, UserIp, UsedUsername, Following, FollowRequest, Instance, Emoji, DriveFile, DriveFolder, Meta, Muting, Blocking, SwSubscription, Hashtag, AbuseUserReport, RegistrationTicket, AuthSession, AccessToken, Signin, MessagingMessage, Page, PageLike, GalleryPost, GalleryLike, ModerationLog, Clip, ClipNote, Antenna, AntennaNote, PromoNote, PromoRead, Relay, MutedNote, Channel, ChannelFollowing, ChannelNotePining, RegistryItem, Webhook, Ad, PasswordResetRequest } from './index.js'; +import { User, Note, Announcement, AnnouncementRead, App, NoteFavorite, NoteThreadMuting, NoteReaction, NoteUnread, Notification, Poll, PollVote, UserProfile, UserKeypair, UserPending, AttestationChallenge, UserSecurityKey, UserPublickey, UserList, UserListJoining, UserGroup, UserGroupJoining, UserGroupInvitation, UserNotePining, UserIp, UsedUsername, Following, FollowRequest, Instance, Emoji, DriveFile, DriveFolder, Meta, Muting, Blocking, SwSubscription, Hashtag, AbuseUserReport, RegistrationTicket, AuthSession, AccessToken, Signin, MessagingMessage, Page, PageLike, GalleryPost, GalleryLike, ModerationLog, Clip, ClipNote, Antenna, AntennaNote, PromoNote, PromoRead, Relay, MutedNote, Channel, ChannelFollowing, ChannelNotePining, RegistryItem, Webhook, Ad, PasswordResetRequest, RetentionAggregation } from './index.js'; import type { DataSource } from 'typeorm'; import type { Provider } from '@nestjs/common'; @@ -382,6 +382,12 @@ const $passwordResetRequestsRepository: Provider = { inject: [DI.db], }; +const $retentionAggregationsRepository: Provider = { + provide: DI.retentionAggregationsRepository, + useFactory: (db: DataSource) => db.getRepository(RetentionAggregation), + inject: [DI.db], +}; + @Module({ imports: [ ], @@ -449,6 +455,7 @@ const $passwordResetRequestsRepository: Provider = { $webhooksRepository, $adsRepository, $passwordResetRequestsRepository, + $retentionAggregationsRepository, ], exports: [ $usersRepository, @@ -514,6 +521,7 @@ const $passwordResetRequestsRepository: Provider = { $webhooksRepository, $adsRepository, $passwordResetRequestsRepository, + $retentionAggregationsRepository, ], }) export class RepositoryModule {} diff --git a/packages/backend/src/models/entities/RetentionAggregation.ts b/packages/backend/src/models/entities/RetentionAggregation.ts new file mode 100644 index 0000000000..c79b762d71 --- /dev/null +++ b/packages/backend/src/models/entities/RetentionAggregation.ts @@ -0,0 +1,35 @@ +import { Entity, PrimaryColumn, Index, Column } from 'typeorm'; +import { id } from '../id.js'; +import type { User } from './User.js'; + +@Entity() +export class RetentionAggregation { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the Note.', + }) + public createdAt: Date; + + @Column('timestamp with time zone', { + comment: 'The updated date of the GalleryPost.', + }) + public updatedAt: Date; + + @Column({ + ...id(), + array: true, + }) + public userIds: User['id'][]; + + @Column('integer', { + }) + public usersCount: number; + + @Column('jsonb', { + default: {}, + }) + public data: Record; +} diff --git a/packages/backend/src/models/index.ts b/packages/backend/src/models/index.ts index 7fde3fbedb..ca7a7c9e56 100644 --- a/packages/backend/src/models/index.ts +++ b/packages/backend/src/models/index.ts @@ -61,6 +61,7 @@ import { UserPublickey } from '@/models/entities/UserPublickey.js'; import { UserSecurityKey } from '@/models/entities/UserSecurityKey.js'; import { Webhook } from '@/models/entities/Webhook.js'; import { Channel } from '@/models/entities/Channel.js'; +import { RetentionAggregation } from '@/models/entities/RetentionAggregation.js'; import type { Repository } from 'typeorm'; export { @@ -127,6 +128,7 @@ export { UserSecurityKey, Webhook, Channel, + RetentionAggregation, }; export type AbuseUserReportsRepository = Repository; @@ -192,3 +194,4 @@ export type UserPublickeysRepository = Repository; export type UserSecurityKeysRepository = Repository; export type WebhooksRepository = Repository; export type ChannelsRepository = Repository; +export type RetentionAggregationsRepository = Repository; diff --git a/packages/backend/src/postgre.ts b/packages/backend/src/postgre.ts index 829edbd7cf..4b4490a0c3 100644 --- a/packages/backend/src/postgre.ts +++ b/packages/backend/src/postgre.ts @@ -69,6 +69,7 @@ import { UserPublickey } from '@/models/entities/UserPublickey.js'; import { UserSecurityKey } from '@/models/entities/UserSecurityKey.js'; import { Webhook } from '@/models/entities/Webhook.js'; import { Channel } from '@/models/entities/Channel.js'; +import { RetentionAggregation } from '@/models/entities/RetentionAggregation.js'; import { Config } from '@/config.js'; import MisskeyLogger from '@/logger.js'; @@ -182,6 +183,7 @@ export const entities = [ UserPending, Webhook, UserIp, + RetentionAggregation, ...charts, ]; diff --git a/packages/backend/src/queue/QueueProcessorModule.ts b/packages/backend/src/queue/QueueProcessorModule.ts index f13dd3ef19..620296498c 100644 --- a/packages/backend/src/queue/QueueProcessorModule.ts +++ b/packages/backend/src/queue/QueueProcessorModule.ts @@ -29,6 +29,7 @@ import { ImportMutingProcessorService } from './processors/ImportMutingProcessor import { ImportUserListsProcessorService } from './processors/ImportUserListsProcessorService.js'; import { ResyncChartsProcessorService } from './processors/ResyncChartsProcessorService.js'; import { TickChartsProcessorService } from './processors/TickChartsProcessorService.js'; +import { AggregateRetentionProcessorService } from './processors/AggregateRetentionProcessorService.js'; @Module({ imports: [ @@ -63,6 +64,7 @@ import { TickChartsProcessorService } from './processors/TickChartsProcessorServ EndedPollNotificationProcessorService, DeliverProcessorService, InboxProcessorService, + AggregateRetentionProcessorService, QueueProcessorService, ], exports: [ diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts index 1d2feb5ef8..2123815c4c 100644 --- a/packages/backend/src/queue/QueueProcessorService.ts +++ b/packages/backend/src/queue/QueueProcessorService.ts @@ -4,6 +4,7 @@ import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; import type Logger from '@/logger.js'; import { QueueService } from '@/core/QueueService.js'; +import { bindThis } from '@/decorators.js'; import { getJobInfo } from './get-job-info.js'; import { SystemQueueProcessorsService } from './SystemQueueProcessorsService.js'; import { ObjectStorageQueueProcessorsService } from './ObjectStorageQueueProcessorsService.js'; @@ -13,7 +14,6 @@ import { EndedPollNotificationProcessorService } from './processors/EndedPollNot import { DeliverProcessorService } from './processors/DeliverProcessorService.js'; import { InboxProcessorService } from './processors/InboxProcessorService.js'; import { QueueLoggerService } from './QueueLoggerService.js'; -import { bindThis } from '@/decorators.js'; @Injectable() export class QueueProcessorService { @@ -133,6 +133,12 @@ export class QueueProcessorService { repeat: { cron: '0 0 * * *' }, removeOnComplete: true, }); + + this.queueService.systemQueue.add('aggregateRetention', { + }, { + repeat: { cron: '0 0 * * *' }, + removeOnComplete: true, + }); this.queueService.systemQueue.add('clean', { }, { diff --git a/packages/backend/src/queue/SystemQueueProcessorsService.ts b/packages/backend/src/queue/SystemQueueProcessorsService.ts index 1ce4152b2c..7fb0da4b10 100644 --- a/packages/backend/src/queue/SystemQueueProcessorsService.ts +++ b/packages/backend/src/queue/SystemQueueProcessorsService.ts @@ -1,13 +1,14 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; +import { bindThis } from '@/decorators.js'; import { TickChartsProcessorService } from './processors/TickChartsProcessorService.js'; import { ResyncChartsProcessorService } from './processors/ResyncChartsProcessorService.js'; import { CleanChartsProcessorService } from './processors/CleanChartsProcessorService.js'; import { CheckExpiredMutingsProcessorService } from './processors/CheckExpiredMutingsProcessorService.js'; import { CleanProcessorService } from './processors/CleanProcessorService.js'; +import { AggregateRetentionProcessorService } from './processors/AggregateRetentionProcessorService.js'; import type Bull from 'bull'; -import { bindThis } from '@/decorators.js'; @Injectable() export class SystemQueueProcessorsService { @@ -18,6 +19,7 @@ export class SystemQueueProcessorsService { private tickChartsProcessorService: TickChartsProcessorService, private resyncChartsProcessorService: ResyncChartsProcessorService, private cleanChartsProcessorService: CleanChartsProcessorService, + private aggregateRetentionProcessorService: AggregateRetentionProcessorService, private checkExpiredMutingsProcessorService: CheckExpiredMutingsProcessorService, private cleanProcessorService: CleanProcessorService, ) { @@ -28,6 +30,7 @@ export class SystemQueueProcessorsService { q.process('tickCharts', (job, done) => this.tickChartsProcessorService.process(job, done)); q.process('resyncCharts', (job, done) => this.resyncChartsProcessorService.process(job, done)); q.process('cleanCharts', (job, done) => this.cleanChartsProcessorService.process(job, done)); + q.process('aggregateRetention', (job, done) => this.aggregateRetentionProcessorService.process(job, done)); q.process('checkExpiredMutings', (job, done) => this.checkExpiredMutingsProcessorService.process(job, done)); q.process('clean', (job, done) => this.cleanProcessorService.process(job, done)); } diff --git a/packages/backend/src/queue/processors/AggregateRetentionProcessorService.ts b/packages/backend/src/queue/processors/AggregateRetentionProcessorService.ts new file mode 100644 index 0000000000..4650da76bb --- /dev/null +++ b/packages/backend/src/queue/processors/AggregateRetentionProcessorService.ts @@ -0,0 +1,75 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { In, IsNull, MoreThan } from 'typeorm'; +import { DI } from '@/di-symbols.js'; +import type { Config } from '@/config.js'; +import type Logger from '@/logger.js'; +import { bindThis } from '@/decorators.js'; +import type { RetentionAggregationsRepository, UsersRepository } from '@/models/index.js'; +import { deepClone } from '@/misc/clone.js'; +import { IdService } from '@/core/IdService.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; +import type Bull from 'bull'; + +@Injectable() +export class AggregateRetentionProcessorService { + private logger: Logger; + + constructor( + @Inject(DI.config) + private config: Config, + + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.retentionAggregationsRepository) + private retentionAggregationsRepository: RetentionAggregationsRepository, + + private idService: IdService, + private queueLoggerService: QueueLoggerService, + ) { + this.logger = this.queueLoggerService.logger.createSubLogger('aggregate-retention'); + } + + @bindThis + public async process(job: Bull.Job>, done: () => void): Promise { + this.logger.info('Aggregating retention...'); + + const now = new Date(); + const dateKey = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`; + + // 過去(だいたい)30日分のレコードを取得 + const pastRecords = await this.retentionAggregationsRepository.findBy({ + createdAt: MoreThan(new Date(Date.now() - (1000 * 60 * 60 * 24 * 31))), + }); + + // 今日登録したユーザーを全て取得 + const targetUsers = await this.usersRepository.findBy({ + host: IsNull(), + createdAt: MoreThan(new Date(Date.now() - (1000 * 60 * 60 * 24))), + }); + const targetUserIds = targetUsers.map(u => u.id); + + await this.retentionAggregationsRepository.insert({ + id: this.idService.genId(), + createdAt: now, + updatedAt: now, + userIds: targetUserIds, + usersCount: targetUserIds.length, + }); + + for (const record of pastRecords) { + const retention = record.userIds.filter(id => targetUserIds.includes(id)).length; + + const data = deepClone(record.data); + data[dateKey] = retention; + + this.retentionAggregationsRepository.update(record.id, { + updatedAt: now, + data, + }); + } + + this.logger.succ('Retention aggregated.'); + done(); + } +} diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 647f60317a..1f96647e7d 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -315,6 +315,7 @@ import * as ep___users_show from './endpoints/users/show.js'; import * as ep___users_stats from './endpoints/users/stats.js'; import * as ep___fetchRss from './endpoints/fetch-rss.js'; import * as ep___admin_driveCapOverride from './endpoints/admin/drive-capacity-override.js'; +import * as ep___retention from './endpoints/retention.js'; import { GetterService } from './GetterService.js'; import { ApiLoggerService } from './ApiLoggerService.js'; import type { Provider } from '@nestjs/common'; @@ -633,6 +634,7 @@ const $users_show: Provider = { provide: 'ep:users/show', useClass: ep___users_s const $users_stats: Provider = { provide: 'ep:users/stats', useClass: ep___users_stats.default }; const $admin_driveCapOverride: Provider = { provide: 'ep:admin/drive-capacity-override', useClass: ep___admin_driveCapOverride.default }; const $fetchRss: Provider = { provide: 'ep:fetch-rss', useClass: ep___fetchRss.default }; +const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention.default }; @Module({ imports: [ @@ -955,6 +957,7 @@ const $fetchRss: Provider = { provide: 'ep:fetch-rss', useClass: ep___fetchRss.d $users_stats, $admin_driveCapOverride, $fetchRss, + $retention, ], exports: [ $admin_meta, @@ -1269,6 +1272,7 @@ const $fetchRss: Provider = { provide: 'ep:fetch-rss', useClass: ep___fetchRss.d $users_stats, $admin_driveCapOverride, $fetchRss, + $retention, ], }) export class EndpointsModule {} diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 6d10cb8f35..e8dc5abfa1 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -314,6 +314,7 @@ import * as ep___users_show from './endpoints/users/show.js'; import * as ep___users_stats from './endpoints/users/stats.js'; import * as ep___fetchRss from './endpoints/fetch-rss.js'; import * as ep___admin_driveCapOverride from './endpoints/admin/drive-capacity-override.js'; +import * as ep___retention from './endpoints/retention.js'; const eps = [ ['admin/meta', ep___admin_meta], @@ -630,6 +631,7 @@ const eps = [ ['users/stats', ep___users_stats], ['admin/drive-capacity-override', ep___admin_driveCapOverride], ['fetch-rss', ep___fetchRss], + ['retention', ep___retention], ]; export interface IEndpointMeta { diff --git a/packages/backend/src/server/api/endpoints/retention.ts b/packages/backend/src/server/api/endpoints/retention.ts new file mode 100644 index 0000000000..e3c2249cdd --- /dev/null +++ b/packages/backend/src/server/api/endpoints/retention.ts @@ -0,0 +1,47 @@ +import { IsNull } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; +import type { RetentionAggregationsRepository } from '@/models/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; + +export const meta = { + tags: ['users'], + + requireCredential: false, + + res: { + }, + + allowGet: true, + cacheSec: 60 * 60, +} as const; + +export const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + @Inject(DI.retentionAggregationsRepository) + private retentionAggregationsRepository: RetentionAggregationsRepository, + ) { + super(meta, paramDef, async (ps, me) => { + const records = await this.retentionAggregationsRepository.find({ + order: { + id: 'DESC', + }, + take: 30, + }); + + return records.map(record => ({ + createdAt: record.createdAt.toISOString(), + users: record.usersCount, + data: record.data, + })); + }); + } +} -- cgit v1.2.3-freya From e414737179bff4b744f49e431d7f3620be999e46 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 1 Jan 2023 16:53:10 +0900 Subject: feat: make possible to export favorited notes #9331 --- CHANGELOG.md | 1 + locales/ja-JP.yml | 1 + packages/backend/src/core/QueueService.ts | 12 +- .../backend/src/queue/DbQueueProcessorsService.ts | 5 +- packages/backend/src/queue/QueueProcessorModule.ts | 2 + .../processors/ExportFavoritesProcessorService.ts | 162 +++++++++++++++++++++ packages/backend/src/server/api/EndpointsModule.ts | 4 + packages/backend/src/server/api/endpoints.ts | 2 + .../src/server/api/endpoints/i/export-favorites.ts | 31 ++++ .../frontend/src/pages/settings/import-export.vue | 12 ++ 10 files changed, 230 insertions(+), 2 deletions(-) create mode 100644 packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts create mode 100644 packages/backend/src/server/api/endpoints/i/export-favorites.ts (limited to 'packages/backend/src/server/api/EndpointsModule.ts') diff --git a/CHANGELOG.md b/CHANGELOG.md index 550a5fcf95..25e175fa85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ You should also include the user name that made the change. - AVIF support @tamaina - Add Cloudflare Turnstile CAPTCHA support @CyberRex0 - Introduce retention-rate aggregation @syuilo +- Make possible to export favorited notes @syuilo - Server: signToActivityPubGet is set to true by default @syuilo - Server: improve syslog performance @syuilo - Server: improve note scoring for featured notes @CyberRex0 diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 2764a85abb..a07fb5ff91 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1421,6 +1421,7 @@ _profile: _exportOrImport: allNotes: "全てのノート" + favoritedNotes: "お気に入りにしたノート" followingList: "フォロー" muteList: "ミュート" blockingList: "ブロック" diff --git a/packages/backend/src/core/QueueService.ts b/packages/backend/src/core/QueueService.ts index 7956a3a8f9..4bf41e0ac1 100644 --- a/packages/backend/src/core/QueueService.ts +++ b/packages/backend/src/core/QueueService.ts @@ -5,10 +5,10 @@ import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { Webhook, webhookEventTypes } from '@/models/entities/Webhook.js'; import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from './QueueModule.js'; import type { ThinUser } from '../queue/types.js'; import type httpSignature from '@peertube/http-signature'; -import { bindThis } from '@/decorators.js'; @Injectable() export class QueueService { @@ -97,6 +97,16 @@ export class QueueService { }); } + @bindThis + public createExportFavoritesJob(user: ThinUser) { + return this.dbQueue.add('exportFavorites', { + user: user, + }, { + removeOnComplete: true, + removeOnFail: true, + }); + } + @bindThis public createExportFollowingJob(user: ThinUser, excludeMuting = false, excludeInactive = false) { return this.dbQueue.add('exportFollowing', { diff --git a/packages/backend/src/queue/DbQueueProcessorsService.ts b/packages/backend/src/queue/DbQueueProcessorsService.ts index e5568ab9bf..df337ad810 100644 --- a/packages/backend/src/queue/DbQueueProcessorsService.ts +++ b/packages/backend/src/queue/DbQueueProcessorsService.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { DbJobData } from '@/queue/types.js'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; +import { bindThis } from '@/decorators.js'; import { DeleteDriveFilesProcessorService } from './processors/DeleteDriveFilesProcessorService.js'; import { ExportCustomEmojisProcessorService } from './processors/ExportCustomEmojisProcessorService.js'; import { ExportNotesProcessorService } from './processors/ExportNotesProcessorService.js'; @@ -15,8 +16,8 @@ import { ImportBlockingProcessorService } from './processors/ImportBlockingProce import { ImportUserListsProcessorService } from './processors/ImportUserListsProcessorService.js'; import { ImportCustomEmojisProcessorService } from './processors/ImportCustomEmojisProcessorService.js'; import { DeleteAccountProcessorService } from './processors/DeleteAccountProcessorService.js'; +import { ExportFavoritesProcessorService } from './processors/ExportFavoritesProcessorService.js'; import type Bull from 'bull'; -import { bindThis } from '@/decorators.js'; @Injectable() export class DbQueueProcessorsService { @@ -27,6 +28,7 @@ export class DbQueueProcessorsService { private deleteDriveFilesProcessorService: DeleteDriveFilesProcessorService, private exportCustomEmojisProcessorService: ExportCustomEmojisProcessorService, private exportNotesProcessorService: ExportNotesProcessorService, + private exportFavoritesProcessorService: ExportFavoritesProcessorService, private exportFollowingProcessorService: ExportFollowingProcessorService, private exportMutingProcessorService: ExportMutingProcessorService, private exportBlockingProcessorService: ExportBlockingProcessorService, @@ -45,6 +47,7 @@ export class DbQueueProcessorsService { q.process('deleteDriveFiles', (job, done) => this.deleteDriveFilesProcessorService.process(job, done)); q.process('exportCustomEmojis', (job, done) => this.exportCustomEmojisProcessorService.process(job, done)); q.process('exportNotes', (job, done) => this.exportNotesProcessorService.process(job, done)); + q.process('exportFavorites', (job, done) => this.exportFavoritesProcessorService.process(job, done)); q.process('exportFollowing', (job, done) => this.exportFollowingProcessorService.process(job, done)); q.process('exportMuting', (job, done) => this.exportMutingProcessorService.process(job, done)); q.process('exportBlocking', (job, done) => this.exportBlockingProcessorService.process(job, done)); diff --git a/packages/backend/src/queue/QueueProcessorModule.ts b/packages/backend/src/queue/QueueProcessorModule.ts index 620296498c..034e9cc5a5 100644 --- a/packages/backend/src/queue/QueueProcessorModule.ts +++ b/packages/backend/src/queue/QueueProcessorModule.ts @@ -30,6 +30,7 @@ import { ImportUserListsProcessorService } from './processors/ImportUserListsPro import { ResyncChartsProcessorService } from './processors/ResyncChartsProcessorService.js'; import { TickChartsProcessorService } from './processors/TickChartsProcessorService.js'; import { AggregateRetentionProcessorService } from './processors/AggregateRetentionProcessorService.js'; +import { ExportFavoritesProcessorService } from './processors/ExportFavoritesProcessorService.js'; @Module({ imports: [ @@ -45,6 +46,7 @@ import { AggregateRetentionProcessorService } from './processors/AggregateRetent DeleteDriveFilesProcessorService, ExportCustomEmojisProcessorService, ExportNotesProcessorService, + ExportFavoritesProcessorService, ExportFollowingProcessorService, ExportMutingProcessorService, ExportBlockingProcessorService, diff --git a/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts b/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts new file mode 100644 index 0000000000..3820705e5c --- /dev/null +++ b/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts @@ -0,0 +1,162 @@ +import * as fs from 'node:fs'; +import { Inject, Injectable } from '@nestjs/common'; +import { IsNull, MoreThan } from 'typeorm'; +import { format as dateFormat } from 'date-fns'; +import { DI } from '@/di-symbols.js'; +import type { NoteFavorite, NoteFavoritesRepository, NotesRepository, PollsRepository, User, UsersRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; +import type Logger from '@/logger.js'; +import { DriveService } from '@/core/DriveService.js'; +import { createTemp } from '@/misc/create-temp.js'; +import type { Poll } from '@/models/entities/Poll.js'; +import type { Note } from '@/models/entities/Note.js'; +import { bindThis } from '@/decorators.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; +import type Bull from 'bull'; +import type { DbUserJobData } from '../types.js'; + +@Injectable() +export class ExportFavoritesProcessorService { + private logger: Logger; + + constructor( + @Inject(DI.config) + private config: Config, + + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.pollsRepository) + private pollsRepository: PollsRepository, + + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + + @Inject(DI.noteFavoritesRepository) + private noteFavoritesRepository: NoteFavoritesRepository, + + private driveService: DriveService, + private queueLoggerService: QueueLoggerService, + ) { + this.logger = this.queueLoggerService.logger.createSubLogger('export-favorites'); + } + + @bindThis + public async process(job: Bull.Job, done: () => void): Promise { + this.logger.info(`Exporting favorites of ${job.data.user.id} ...`); + + const user = await this.usersRepository.findOneBy({ id: job.data.user.id }); + if (user == null) { + done(); + return; + } + + // Create temp file + const [path, cleanup] = await createTemp(); + + this.logger.info(`Temp file is ${path}`); + + try { + const stream = fs.createWriteStream(path, { flags: 'a' }); + + const write = (text: string): Promise => { + return new Promise((res, rej) => { + stream.write(text, err => { + if (err) { + this.logger.error(err); + rej(err); + } else { + res(); + } + }); + }); + }; + + await write('['); + + let exportedFavoritesCount = 0; + let cursor: NoteFavorite['id'] | null = null; + + while (true) { + const favorites = await this.noteFavoritesRepository.find({ + where: { + userId: user.id, + ...(cursor ? { id: MoreThan(cursor) } : {}), + }, + take: 100, + order: { + id: 1, + }, + relations: ['note', 'note.user'], + }) as (NoteFavorite & { note: Note & { user: User } })[]; + + if (favorites.length === 0) { + job.progress(100); + break; + } + + cursor = favorites[favorites.length - 1].id; + + for (const favorite of favorites) { + let poll: Poll | undefined; + if (favorite.note.hasPoll) { + poll = await this.pollsRepository.findOneByOrFail({ noteId: favorite.note.id }); + } + const content = JSON.stringify(serialize(favorite, poll)); + const isFirst = exportedFavoritesCount === 0; + await write(isFirst ? content : ',\n' + content); + exportedFavoritesCount++; + } + + const total = await this.noteFavoritesRepository.countBy({ + userId: user.id, + }); + + job.progress(exportedFavoritesCount / total); + } + + await write(']'); + + stream.end(); + this.logger.succ(`Exported to: ${path}`); + + const fileName = 'favorites-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.json'; + const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true }); + + this.logger.succ(`Exported to: ${driveFile.id}`); + } finally { + cleanup(); + } + + done(); + } +} + +function serialize(favorite: NoteFavorite & { note: Note & { user: User } }, poll: Poll | null = null): Record { + return { + id: favorite.id, + createdAt: favorite.createdAt, + note: { + id: favorite.note.id, + text: favorite.note.text, + createdAt: favorite.note.createdAt, + fileIds: favorite.note.fileIds, + replyId: favorite.note.replyId, + renoteId: favorite.note.renoteId, + poll: poll, + cw: favorite.note.cw, + visibility: favorite.note.visibility, + visibleUserIds: favorite.note.visibleUserIds, + localOnly: favorite.note.localOnly, + uri: favorite.note.uri, + url: favorite.note.url, + user: { + id: favorite.note.user.id, + name: favorite.note.user.name, + username: favorite.note.user.username, + host: favorite.note.user.host, + uri: favorite.note.user.uri, + }, + }, + }; +} diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 1f96647e7d..18ba16ac79 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -176,6 +176,7 @@ import * as ep___i_exportBlocking from './endpoints/i/export-blocking.js'; import * as ep___i_exportFollowing from './endpoints/i/export-following.js'; import * as ep___i_exportMute from './endpoints/i/export-mute.js'; import * as ep___i_exportNotes from './endpoints/i/export-notes.js'; +import * as ep___i_exportFavorites from './endpoints/i/export-favorites.js'; import * as ep___i_exportUserLists from './endpoints/i/export-user-lists.js'; import * as ep___i_favorites from './endpoints/i/favorites.js'; import * as ep___i_gallery_likes from './endpoints/i/gallery/likes.js'; @@ -495,6 +496,7 @@ const $i_exportBlocking: Provider = { provide: 'ep:i/export-blocking', useClass: const $i_exportFollowing: Provider = { provide: 'ep:i/export-following', useClass: ep___i_exportFollowing.default }; const $i_exportMute: Provider = { provide: 'ep:i/export-mute', useClass: ep___i_exportMute.default }; const $i_exportNotes: Provider = { provide: 'ep:i/export-notes', useClass: ep___i_exportNotes.default }; +const $i_exportFavorites: Provider = { provide: 'ep:i/export-favorites', useClass: ep___i_exportFavorites.default }; const $i_exportUserLists: Provider = { provide: 'ep:i/export-user-lists', useClass: ep___i_exportUserLists.default }; const $i_favorites: Provider = { provide: 'ep:i/favorites', useClass: ep___i_favorites.default }; const $i_gallery_likes: Provider = { provide: 'ep:i/gallery/likes', useClass: ep___i_gallery_likes.default }; @@ -818,6 +820,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $i_exportFollowing, $i_exportMute, $i_exportNotes, + $i_exportFavorites, $i_exportUserLists, $i_favorites, $i_gallery_likes, @@ -1135,6 +1138,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $i_exportFollowing, $i_exportMute, $i_exportNotes, + $i_exportFavorites, $i_exportUserLists, $i_favorites, $i_gallery_likes, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index e8dc5abfa1..a09ffa832c 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -175,6 +175,7 @@ import * as ep___i_exportBlocking from './endpoints/i/export-blocking.js'; import * as ep___i_exportFollowing from './endpoints/i/export-following.js'; import * as ep___i_exportMute from './endpoints/i/export-mute.js'; import * as ep___i_exportNotes from './endpoints/i/export-notes.js'; +import * as ep___i_exportFavorites from './endpoints/i/export-favorites.js'; import * as ep___i_exportUserLists from './endpoints/i/export-user-lists.js'; import * as ep___i_favorites from './endpoints/i/favorites.js'; import * as ep___i_gallery_likes from './endpoints/i/gallery/likes.js'; @@ -492,6 +493,7 @@ const eps = [ ['i/export-following', ep___i_exportFollowing], ['i/export-mute', ep___i_exportMute], ['i/export-notes', ep___i_exportNotes], + ['i/export-favorites', ep___i_exportFavorites], ['i/export-user-lists', ep___i_exportUserLists], ['i/favorites', ep___i_favorites], ['i/gallery/likes', ep___i_gallery_likes], diff --git a/packages/backend/src/server/api/endpoints/i/export-favorites.ts b/packages/backend/src/server/api/endpoints/i/export-favorites.ts new file mode 100644 index 0000000000..b32f39d3e5 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/i/export-favorites.ts @@ -0,0 +1,31 @@ +import { Inject, Injectable } from '@nestjs/common'; +import ms from 'ms'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { QueueService } from '@/core/QueueService.js'; + +export const meta = { + secure: true, + requireCredential: true, + limit: { + duration: ms('1day'), + max: 1, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + private queueService: QueueService, + ) { + super(meta, paramDef, async (ps, me) => { + this.queueService.createExportFavoritesJob(me); + }); + } +} diff --git a/packages/frontend/src/pages/settings/import-export.vue b/packages/frontend/src/pages/settings/import-export.vue index 7db267c142..ca3bea4d16 100644 --- a/packages/frontend/src/pages/settings/import-export.vue +++ b/packages/frontend/src/pages/settings/import-export.vue @@ -8,6 +8,14 @@ {{ i18n.ts.export }} + + + + + + {{ i18n.ts.export }} + + @@ -108,6 +116,10 @@ const exportNotes = () => { os.api('i/export-notes', {}).then(onExportSuccess).catch(onError); }; +const exportFavorites = () => { + os.api('i/export-favorites', {}).then(onExportSuccess).catch(onError); +}; + const exportFollowing = () => { os.api('i/export-following', { excludeMuting: excludeMutingUsers.value, -- cgit v1.2.3-freya From 969e9df889367159e64fcabadfd2150b1dfd685d Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 1 Jan 2023 17:45:49 +0900 Subject: feat: add per user pv chart --- CHANGELOG.md | 1 + .../migration/1672562400597-PerUserPvChart.js | 17 ++++++++ packages/backend/src/core/CoreModule.ts | 6 +++ .../src/core/chart/ChartManagementService.ts | 5 ++- .../src/core/chart/charts/entities/per-user-pv.ts | 12 +++++ .../backend/src/core/chart/charts/per-user-pv.ts | 51 ++++++++++++++++++++++ packages/backend/src/core/chart/entities.ts | 2 + .../processors/CleanChartsProcessorService.ts | 5 ++- .../queue/processors/TickChartsProcessorService.ts | 5 ++- packages/backend/src/server/api/EndpointsModule.ts | 4 ++ packages/backend/src/server/api/endpoints.ts | 2 + .../src/server/api/endpoints/charts/user/pv.ts | 37 ++++++++++++++++ .../backend/src/server/api/endpoints/users/show.ts | 10 ++++- 13 files changed, 153 insertions(+), 4 deletions(-) create mode 100644 packages/backend/migration/1672562400597-PerUserPvChart.js create mode 100644 packages/backend/src/core/chart/charts/entities/per-user-pv.ts create mode 100644 packages/backend/src/core/chart/charts/per-user-pv.ts create mode 100644 packages/backend/src/server/api/endpoints/charts/user/pv.ts (limited to 'packages/backend/src/server/api/EndpointsModule.ts') diff --git a/CHANGELOG.md b/CHANGELOG.md index 25e175fa85..918bb225a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ You should also include the user name that made the change. - Add Cloudflare Turnstile CAPTCHA support @CyberRex0 - Introduce retention-rate aggregation @syuilo - Make possible to export favorited notes @syuilo +- Add per user pv chart @syuilo - Server: signToActivityPubGet is set to true by default @syuilo - Server: improve syslog performance @syuilo - Server: improve note scoring for featured notes @CyberRex0 diff --git a/packages/backend/migration/1672562400597-PerUserPvChart.js b/packages/backend/migration/1672562400597-PerUserPvChart.js new file mode 100644 index 0000000000..4da6b9a8b3 --- /dev/null +++ b/packages/backend/migration/1672562400597-PerUserPvChart.js @@ -0,0 +1,17 @@ +export class PerUserPvChart1672562400597 { + name = 'PerUserPvChart1672562400597' + + async up(queryRunner) { + await queryRunner.query(`CREATE TABLE "__chart__per_user_pv" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "unique_temp___upv_user" character varying array NOT NULL DEFAULT '{}', "___upv_user" smallint NOT NULL DEFAULT '0', "___pv_user" smallint NOT NULL DEFAULT '0', "unique_temp___upv_visitor" character varying array NOT NULL DEFAULT '{}', "___upv_visitor" smallint NOT NULL DEFAULT '0', "___pv_visitor" smallint NOT NULL DEFAULT '0', CONSTRAINT "UQ_f2a56da57921ca8439f45c1d95f" UNIQUE ("date", "group"), CONSTRAINT "PK_3c938a24f0203b5bd13fab51059" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE UNIQUE INDEX "IDX_f2a56da57921ca8439f45c1d95" ON "__chart__per_user_pv" ("date", "group") `); + await queryRunner.query(`CREATE TABLE "__chart_day__per_user_pv" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "unique_temp___upv_user" character varying array NOT NULL DEFAULT '{}', "___upv_user" smallint NOT NULL DEFAULT '0', "___pv_user" smallint NOT NULL DEFAULT '0', "unique_temp___upv_visitor" character varying array NOT NULL DEFAULT '{}', "___upv_visitor" smallint NOT NULL DEFAULT '0', "___pv_visitor" smallint NOT NULL DEFAULT '0', CONSTRAINT "UQ_f221e45cfac5bea0ce0f3149fbb" UNIQUE ("date", "group"), CONSTRAINT "PK_0085d7542f6772e99b9dcfb0a9c" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE UNIQUE INDEX "IDX_f221e45cfac5bea0ce0f3149fb" ON "__chart_day__per_user_pv" ("date", "group") `); + } + + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "public"."IDX_f221e45cfac5bea0ce0f3149fb"`); + await queryRunner.query(`DROP TABLE "__chart_day__per_user_pv"`); + await queryRunner.query(`DROP INDEX "public"."IDX_f2a56da57921ca8439f45c1d95"`); + await queryRunner.query(`DROP TABLE "__chart__per_user_pv"`); + } +} diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index 085addaa05..7c6d12abf8 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -57,6 +57,7 @@ import UsersChart from './chart/charts/users.js'; import ActiveUsersChart from './chart/charts/active-users.js'; import InstanceChart from './chart/charts/instance.js'; import PerUserNotesChart from './chart/charts/per-user-notes.js'; +import PerUserPvChart from './chart/charts/per-user-pv.js'; import DriveChart from './chart/charts/drive.js'; import PerUserReactionsChart from './chart/charts/per-user-reactions.js'; import HashtagChart from './chart/charts/hashtag.js'; @@ -176,6 +177,7 @@ const $UsersChart: Provider = { provide: 'UsersChart', useExisting: UsersChart } const $ActiveUsersChart: Provider = { provide: 'ActiveUsersChart', useExisting: ActiveUsersChart }; const $InstanceChart: Provider = { provide: 'InstanceChart', useExisting: InstanceChart }; const $PerUserNotesChart: Provider = { provide: 'PerUserNotesChart', useExisting: PerUserNotesChart }; +const $PerUserPvChart: Provider = { provide: 'PerUserPvChart', useExisting: PerUserPvChart }; const $DriveChart: Provider = { provide: 'DriveChart', useExisting: DriveChart }; const $PerUserReactionsChart: Provider = { provide: 'PerUserReactionsChart', useExisting: PerUserReactionsChart }; const $HashtagChart: Provider = { provide: 'HashtagChart', useExisting: HashtagChart }; @@ -298,6 +300,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting ActiveUsersChart, InstanceChart, PerUserNotesChart, + PerUserPvChart, DriveChart, PerUserReactionsChart, HashtagChart, @@ -414,6 +417,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $ActiveUsersChart, $InstanceChart, $PerUserNotesChart, + $PerUserPvChart, $DriveChart, $PerUserReactionsChart, $HashtagChart, @@ -530,6 +534,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting ActiveUsersChart, InstanceChart, PerUserNotesChart, + PerUserPvChart, DriveChart, PerUserReactionsChart, HashtagChart, @@ -645,6 +650,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $ActiveUsersChart, $InstanceChart, $PerUserNotesChart, + $PerUserPvChart, $DriveChart, $PerUserReactionsChart, $HashtagChart, diff --git a/packages/backend/src/core/chart/ChartManagementService.ts b/packages/backend/src/core/chart/ChartManagementService.ts index 13ee06c6c5..37de30b71c 100644 --- a/packages/backend/src/core/chart/ChartManagementService.ts +++ b/packages/backend/src/core/chart/ChartManagementService.ts @@ -1,11 +1,13 @@ import { Injectable, Inject } from '@nestjs/common'; +import { bindThis } from '@/decorators.js'; import FederationChart from './charts/federation.js'; import NotesChart from './charts/notes.js'; import UsersChart from './charts/users.js'; import ActiveUsersChart from './charts/active-users.js'; import InstanceChart from './charts/instance.js'; import PerUserNotesChart from './charts/per-user-notes.js'; +import PerUserPvChart from './charts/per-user-pv.js'; import DriveChart from './charts/drive.js'; import PerUserReactionsChart from './charts/per-user-reactions.js'; import HashtagChart from './charts/hashtag.js'; @@ -13,7 +15,6 @@ import PerUserFollowingChart from './charts/per-user-following.js'; import PerUserDriveChart from './charts/per-user-drive.js'; import ApRequestChart from './charts/ap-request.js'; import type { OnApplicationShutdown } from '@nestjs/common'; -import { bindThis } from '@/decorators.js'; @Injectable() export class ChartManagementService implements OnApplicationShutdown { @@ -27,6 +28,7 @@ export class ChartManagementService implements OnApplicationShutdown { private activeUsersChart: ActiveUsersChart, private instanceChart: InstanceChart, private perUserNotesChart: PerUserNotesChart, + private perUserPvChart: PerUserPvChart, private driveChart: DriveChart, private perUserReactionsChart: PerUserReactionsChart, private hashtagChart: HashtagChart, @@ -41,6 +43,7 @@ export class ChartManagementService implements OnApplicationShutdown { this.activeUsersChart, this.instanceChart, this.perUserNotesChart, + this.perUserPvChart, this.driveChart, this.perUserReactionsChart, this.hashtagChart, diff --git a/packages/backend/src/core/chart/charts/entities/per-user-pv.ts b/packages/backend/src/core/chart/charts/entities/per-user-pv.ts new file mode 100644 index 0000000000..64c8ed1fb1 --- /dev/null +++ b/packages/backend/src/core/chart/charts/entities/per-user-pv.ts @@ -0,0 +1,12 @@ +import Chart from '../../core.js'; + +export const name = 'perUserPv'; + +export const schema = { + 'upv.user': { uniqueIncrement: true, range: 'small' }, + 'pv.user': { range: 'small' }, + 'upv.visitor': { uniqueIncrement: true, range: 'small' }, + 'pv.visitor': { range: 'small' }, +} as const; + +export const entity = Chart.schemaToEntity(name, schema, true); diff --git a/packages/backend/src/core/chart/charts/per-user-pv.ts b/packages/backend/src/core/chart/charts/per-user-pv.ts new file mode 100644 index 0000000000..53c89d8a9a --- /dev/null +++ b/packages/backend/src/core/chart/charts/per-user-pv.ts @@ -0,0 +1,51 @@ +import { Injectable, Inject } from '@nestjs/common'; +import { DataSource } from 'typeorm'; +import type { User } from '@/models/entities/User.js'; +import { AppLockService } from '@/core/AppLockService.js'; +import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; +import Chart from '../core.js'; +import { ChartLoggerService } from '../ChartLoggerService.js'; +import { name, schema } from './entities/per-user-pv.js'; +import type { KVs } from '../core.js'; + +/** + * ユーザーごとのプロフィール被閲覧数に関するチャート + */ +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class PerUserPvChart extends Chart { + constructor( + @Inject(DI.db) + private db: DataSource, + + private appLockService: AppLockService, + private chartLoggerService: ChartLoggerService, + ) { + super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true); + } + + protected async tickMajor(): Promise>> { + return {}; + } + + protected async tickMinor(): Promise>> { + return {}; + } + + @bindThis + public async commitByUser(user: { id: User['id'] }, key: string): Promise { + await this.commit({ + 'upv.user': [key], + 'pv.user': 1, + }, user.id); + } + + @bindThis + public async commitByVisitor(user: { id: User['id'] }, key: string): Promise { + await this.commit({ + 'upv.visitor': [key], + 'pv.visitor': 1, + }, user.id); + } +} diff --git a/packages/backend/src/core/chart/entities.ts b/packages/backend/src/core/chart/entities.ts index a9eeabd639..c2759e8b3c 100644 --- a/packages/backend/src/core/chart/entities.ts +++ b/packages/backend/src/core/chart/entities.ts @@ -4,6 +4,7 @@ import { entity as UsersChart } from './charts/entities/users.js'; import { entity as ActiveUsersChart } from './charts/entities/active-users.js'; import { entity as InstanceChart } from './charts/entities/instance.js'; import { entity as PerUserNotesChart } from './charts/entities/per-user-notes.js'; +import { entity as PerUserPvChart } from './charts/entities/per-user-pv.js'; import { entity as DriveChart } from './charts/entities/drive.js'; import { entity as PerUserReactionsChart } from './charts/entities/per-user-reactions.js'; import { entity as HashtagChart } from './charts/entities/hashtag.js'; @@ -23,6 +24,7 @@ export const entities = [ ActiveUsersChart.hour, ActiveUsersChart.day, InstanceChart.hour, InstanceChart.day, PerUserNotesChart.hour, PerUserNotesChart.day, + PerUserPvChart.hour, PerUserPvChart.day, DriveChart.hour, DriveChart.day, PerUserReactionsChart.hour, PerUserReactionsChart.day, HashtagChart.hour, HashtagChart.day, diff --git a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts index c57086240a..2adf7cbe6d 100644 --- a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts @@ -9,15 +9,16 @@ import UsersChart from '@/core/chart/charts/users.js'; import ActiveUsersChart from '@/core/chart/charts/active-users.js'; import InstanceChart from '@/core/chart/charts/instance.js'; import PerUserNotesChart from '@/core/chart/charts/per-user-notes.js'; +import PerUserPvChart from '@/core/chart/charts/per-user-pv.js'; import DriveChart from '@/core/chart/charts/drive.js'; import PerUserReactionsChart from '@/core/chart/charts/per-user-reactions.js'; import HashtagChart from '@/core/chart/charts/hashtag.js'; import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js'; import PerUserDriveChart from '@/core/chart/charts/per-user-drive.js'; import ApRequestChart from '@/core/chart/charts/ap-request.js'; +import { bindThis } from '@/decorators.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; -import { bindThis } from '@/decorators.js'; @Injectable() export class CleanChartsProcessorService { @@ -33,6 +34,7 @@ export class CleanChartsProcessorService { private activeUsersChart: ActiveUsersChart, private instanceChart: InstanceChart, private perUserNotesChart: PerUserNotesChart, + private perUserPvChart: PerUserPvChart, private driveChart: DriveChart, private perUserReactionsChart: PerUserReactionsChart, private hashtagChart: HashtagChart, @@ -56,6 +58,7 @@ export class CleanChartsProcessorService { this.activeUsersChart.clean(), this.instanceChart.clean(), this.perUserNotesChart.clean(), + this.perUserPvChart.clean(), this.driveChart.clean(), this.perUserReactionsChart.clean(), this.hashtagChart.clean(), diff --git a/packages/backend/src/queue/processors/TickChartsProcessorService.ts b/packages/backend/src/queue/processors/TickChartsProcessorService.ts index 323e5227cd..51eff2a155 100644 --- a/packages/backend/src/queue/processors/TickChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/TickChartsProcessorService.ts @@ -9,15 +9,16 @@ import UsersChart from '@/core/chart/charts/users.js'; import ActiveUsersChart from '@/core/chart/charts/active-users.js'; import InstanceChart from '@/core/chart/charts/instance.js'; import PerUserNotesChart from '@/core/chart/charts/per-user-notes.js'; +import PerUserPvChart from '@/core/chart/charts/per-user-pv.js'; import DriveChart from '@/core/chart/charts/drive.js'; import PerUserReactionsChart from '@/core/chart/charts/per-user-reactions.js'; import HashtagChart from '@/core/chart/charts/hashtag.js'; import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js'; import PerUserDriveChart from '@/core/chart/charts/per-user-drive.js'; import ApRequestChart from '@/core/chart/charts/ap-request.js'; +import { bindThis } from '@/decorators.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; -import { bindThis } from '@/decorators.js'; @Injectable() export class TickChartsProcessorService { @@ -33,6 +34,7 @@ export class TickChartsProcessorService { private activeUsersChart: ActiveUsersChart, private instanceChart: InstanceChart, private perUserNotesChart: PerUserNotesChart, + private perUserPvChart: PerUserPvChart, private driveChart: DriveChart, private perUserReactionsChart: PerUserReactionsChart, private hashtagChart: HashtagChart, @@ -56,6 +58,7 @@ export class TickChartsProcessorService { this.activeUsersChart.tick(false), this.instanceChart.tick(false), this.perUserNotesChart.tick(false), + this.perUserPvChart.tick(false), this.driveChart.tick(false), this.perUserReactionsChart.tick(false), this.hashtagChart.tick(false), diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 18ba16ac79..32eff7f312 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -99,6 +99,7 @@ import * as ep___charts_notes from './endpoints/charts/notes.js'; import * as ep___charts_user_drive from './endpoints/charts/user/drive.js'; import * as ep___charts_user_following from './endpoints/charts/user/following.js'; import * as ep___charts_user_notes from './endpoints/charts/user/notes.js'; +import * as ep___charts_user_pv from './endpoints/charts/user/pv.js'; import * as ep___charts_user_reactions from './endpoints/charts/user/reactions.js'; import * as ep___charts_users from './endpoints/charts/users.js'; import * as ep___clips_addNote from './endpoints/clips/add-note.js'; @@ -419,6 +420,7 @@ const $charts_notes: Provider = { provide: 'ep:charts/notes', useClass: ep___cha const $charts_user_drive: Provider = { provide: 'ep:charts/user/drive', useClass: ep___charts_user_drive.default }; const $charts_user_following: Provider = { provide: 'ep:charts/user/following', useClass: ep___charts_user_following.default }; const $charts_user_notes: Provider = { provide: 'ep:charts/user/notes', useClass: ep___charts_user_notes.default }; +const $charts_user_pv: Provider = { provide: 'ep:charts/user/pv', useClass: ep___charts_user_pv.default }; const $charts_user_reactions: Provider = { provide: 'ep:charts/user/reactions', useClass: ep___charts_user_reactions.default }; const $charts_users: Provider = { provide: 'ep:charts/users', useClass: ep___charts_users.default }; const $clips_addNote: Provider = { provide: 'ep:clips/add-note', useClass: ep___clips_addNote.default }; @@ -743,6 +745,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $charts_user_drive, $charts_user_following, $charts_user_notes, + $charts_user_pv, $charts_user_reactions, $charts_users, $clips_addNote, @@ -1061,6 +1064,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $charts_user_drive, $charts_user_following, $charts_user_notes, + $charts_user_pv, $charts_user_reactions, $charts_users, $clips_addNote, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index a09ffa832c..49dc3b224f 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -98,6 +98,7 @@ import * as ep___charts_notes from './endpoints/charts/notes.js'; import * as ep___charts_user_drive from './endpoints/charts/user/drive.js'; import * as ep___charts_user_following from './endpoints/charts/user/following.js'; import * as ep___charts_user_notes from './endpoints/charts/user/notes.js'; +import * as ep___charts_user_pv from './endpoints/charts/user/pv.js'; import * as ep___charts_user_reactions from './endpoints/charts/user/reactions.js'; import * as ep___charts_users from './endpoints/charts/users.js'; import * as ep___clips_addNote from './endpoints/clips/add-note.js'; @@ -416,6 +417,7 @@ const eps = [ ['charts/user/drive', ep___charts_user_drive], ['charts/user/following', ep___charts_user_following], ['charts/user/notes', ep___charts_user_notes], + ['charts/user/pv', ep___charts_user_pv], ['charts/user/reactions', ep___charts_user_reactions], ['charts/users', ep___charts_users], ['clips/add-note', ep___clips_addNote], diff --git a/packages/backend/src/server/api/endpoints/charts/user/pv.ts b/packages/backend/src/server/api/endpoints/charts/user/pv.ts new file mode 100644 index 0000000000..c920e0f57d --- /dev/null +++ b/packages/backend/src/server/api/endpoints/charts/user/pv.ts @@ -0,0 +1,37 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { getJsonSchema } from '@/core/chart/core.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import PerUserPvChart from '@/core/chart/charts/per-user-pv.js'; +import { schema } from '@/core/chart/charts/entities/per-user-notes.js'; + +export const meta = { + tags: ['charts', 'users'], + + res: getJsonSchema(schema), + + allowGet: true, + cacheSec: 60 * 60, +} as const; + +export const paramDef = { + type: 'object', + properties: { + span: { type: 'string', enum: ['day', 'hour'] }, + limit: { type: 'integer', minimum: 1, maximum: 500, default: 30 }, + offset: { type: 'integer', nullable: true, default: null }, + userId: { type: 'string', format: 'misskey:id' }, + }, + required: ['span', 'userId'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + private perUserPvChart: PerUserPvChart, + ) { + super(meta, paramDef, async (ps, me) => { + return await this.perUserPvChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.userId); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts index 7932d5cd12..48a6bbf9bc 100644 --- a/packages/backend/src/server/api/endpoints/users/show.ts +++ b/packages/backend/src/server/api/endpoints/users/show.ts @@ -6,6 +6,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js'; import { DI } from '@/di-symbols.js'; +import PerUserPvChart from '@/core/chart/charts/per-user-pv.js'; import { ApiError } from '../../error.js'; import { ApiLoggerService } from '../../ApiLoggerService.js'; import type { FindOptionsWhere } from 'typeorm'; @@ -90,9 +91,10 @@ export default class extends Endpoint { private userEntityService: UserEntityService, private remoteUserResolveService: RemoteUserResolveService, + private perUserPvChart: PerUserPvChart, private apiLoggerService: ApiLoggerService, ) { - super(meta, paramDef, async (ps, me) => { + super(meta, paramDef, async (ps, me, _1, _2, _3, ip) => { let user; const isAdminOrModerator = me && (me.isAdmin || me.isModerator); @@ -137,6 +139,12 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchUser); } + if (me == null && ip != null) { + this.perUserPvChart.commitByVisitor(user, ip); + } else if (me && me.id !== user.id) { + this.perUserPvChart.commitByUser(user, me.id); + } + return await this.userEntityService.pack(user, me, { detail: true, }); -- cgit v1.2.3-freya From ebe340d5105595abe2406e8f386c3ab69703b73b Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 5 Jan 2023 13:59:48 +0900 Subject: MisskeyPlay (#9467) * wip * wip * wip * wip * wip * Update ui.ts * wip * wip * wip * wip * wip * wip * wip * wip * Update CHANGELOG.md * wip * wip * wip * wip * :art: * wip * :v: --- CHANGELOG.md | 10 +- locales/ja-JP.yml | 21 + packages/backend/migration/1672822262496-Flash.js | 29 ++ packages/backend/src/core/CoreModule.ts | 12 + .../src/core/entities/FlashEntityService.ts | 55 +++ .../src/core/entities/FlashLikeEntityService.ts | 44 ++ packages/backend/src/di-symbols.ts | 2 + packages/backend/src/models/RepositoryModule.ts | 18 +- packages/backend/src/models/entities/Flash.ts | 60 +++ packages/backend/src/models/entities/FlashLike.ts | 33 ++ packages/backend/src/models/index.ts | 6 + packages/backend/src/postgre.ts | 4 + packages/backend/src/server/api/EndpointsModule.ts | 36 ++ packages/backend/src/server/api/endpoints.ts | 18 + .../src/server/api/endpoints/flash/create.ts | 66 +++ .../src/server/api/endpoints/flash/delete.ts | 56 +++ .../src/server/api/endpoints/flash/featured.ts | 48 ++ .../backend/src/server/api/endpoints/flash/like.ts | 87 ++++ .../src/server/api/endpoints/flash/my-likes.ts | 68 +++ .../backend/src/server/api/endpoints/flash/my.ts | 57 +++ .../backend/src/server/api/endpoints/flash/show.ts | 60 +++ .../src/server/api/endpoints/flash/unlike.ts | 68 +++ .../src/server/api/endpoints/flash/update.ts | 78 +++ packages/frontend/src/components/MkAsUi.vue | 107 +++++ packages/frontend/src/components/MkButton.vue | 40 +- packages/frontend/src/components/MkChartLegend.vue | 2 +- .../frontend/src/components/MkFlashPreview.vue | 112 +++++ packages/frontend/src/components/MkLaunchPad.vue | 2 +- packages/frontend/src/components/MkPagePreview.vue | 19 +- packages/frontend/src/components/form/select.vue | 2 +- packages/frontend/src/navbar.ts | 39 +- packages/frontend/src/pages/flash/flash-edit.vue | 111 +++++ packages/frontend/src/pages/flash/flash-index.vue | 99 ++++ packages/frontend/src/pages/flash/flash.vue | 291 ++++++++++++ packages/frontend/src/pages/page.vue | 18 +- packages/frontend/src/pages/scratchpad.vue | 102 ++-- packages/frontend/src/pages/settings/navbar.vue | 2 +- packages/frontend/src/router.ts | 14 + packages/frontend/src/scripts/aiscript/ui.ts | 526 +++++++++++++++++++++ .../frontend/src/ui/_common_/navbar-for-mobile.vue | 2 +- packages/frontend/src/ui/_common_/navbar.vue | 4 +- packages/frontend/src/ui/classic.header.vue | 2 +- packages/frontend/src/ui/classic.sidebar.vue | 2 +- packages/frontend/src/widgets/aiscript-app.vue | 122 +++++ packages/frontend/src/widgets/index.ts | 2 + 45 files changed, 2464 insertions(+), 92 deletions(-) create mode 100644 packages/backend/migration/1672822262496-Flash.js create mode 100644 packages/backend/src/core/entities/FlashEntityService.ts create mode 100644 packages/backend/src/core/entities/FlashLikeEntityService.ts create mode 100644 packages/backend/src/models/entities/Flash.ts create mode 100644 packages/backend/src/models/entities/FlashLike.ts create mode 100644 packages/backend/src/server/api/endpoints/flash/create.ts create mode 100644 packages/backend/src/server/api/endpoints/flash/delete.ts create mode 100644 packages/backend/src/server/api/endpoints/flash/featured.ts create mode 100644 packages/backend/src/server/api/endpoints/flash/like.ts create mode 100644 packages/backend/src/server/api/endpoints/flash/my-likes.ts create mode 100644 packages/backend/src/server/api/endpoints/flash/my.ts create mode 100644 packages/backend/src/server/api/endpoints/flash/show.ts create mode 100644 packages/backend/src/server/api/endpoints/flash/unlike.ts create mode 100644 packages/backend/src/server/api/endpoints/flash/update.ts create mode 100644 packages/frontend/src/components/MkAsUi.vue create mode 100644 packages/frontend/src/components/MkFlashPreview.vue create mode 100644 packages/frontend/src/pages/flash/flash-edit.vue create mode 100644 packages/frontend/src/pages/flash/flash-index.vue create mode 100644 packages/frontend/src/pages/flash/flash.vue create mode 100644 packages/frontend/src/scripts/aiscript/ui.ts create mode 100644 packages/frontend/src/widgets/aiscript-app.vue (limited to 'packages/backend/src/server/api/EndpointsModule.ts') diff --git a/CHANGELOG.md b/CHANGELOG.md index 83d013ce99..005b011592 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ You should also include the user name that made the change. - Migrate to Yarn Berry (v3.2.1) @ThatOneCalculator - You may have to `yarn run clean-all`, `sudo corepack enable` and `yarn set version berry` before running `yarn install` if you're still on yarn classic - 新たに動的なPagesを作ることはできなくなりました - - 代わりに今後AiScriptを用いてより柔軟に動的なコンテンツを作成できるMisskey Play機能の実装を予定しています。 + - 代わりにAiScriptを用いてより柔軟に動的なコンテンツを作成できるMisskey Play機能が実装されています。 - AiScriptが0.12.0にアップデートされました - 0.12.0の変更点についてはこちら https://github.com/syuilo/aiscript/blob/master/CHANGELOG.md#0120 - 0.12.0未満のプラグインは読み込むことはできません @@ -33,12 +33,13 @@ You should also include the user name that made the change. - API: `instance`エンティティに`latestStatus`、`lastCommunicatedAt`、`latestRequestSentAt`プロパティが含まれなくなりました ### Improvements -- Push notification of Antenna note @tamaina -- AVIF support @tamaina -- Add Cloudflare Turnstile CAPTCHA support @CyberRex0 +- Misskey Play @syuilo - Introduce retention-rate aggregation @syuilo - Make possible to export favorited notes @syuilo - Add per user pv chart @syuilo +- Push notification of Antenna note @tamaina +- AVIF support @tamaina +- Add Cloudflare Turnstile CAPTCHA support @CyberRex0 - Server: signToActivityPubGet is set to true by default @syuilo - Server: improve syslog performance @syuilo - Server: improve note scoring for featured notes @CyberRex0 @@ -47,6 +48,7 @@ You should also include the user name that made the change. - Server: delete outdated notes of antenna regularly to improve db performance @syuilo - Server: improve activitypub deliver performance @syuilo - Client: use tabler-icons instead of fontawesome to better design @syuilo +- Client: Add AiScript App widget - Client: Add new gabber kick sounds (thanks for noizenecio) - Client: Add link to user RSS feed in profile menu @ssmucny - Client: Compress non-animated PNG files @saschanaz diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index d6a5518196..32bafcd661 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -916,6 +916,10 @@ loggedInAsBot: "Botアカウントでログイン中" tools: "ツール" cannotLoad: "読み込めません" numberOfProfileView: "プロフィール表示回数" +like: "いいね!" +unlike: "いいねを解除" +numberOfLikes: "いいね数" +show: "表示" _sensitiveMediaDetection: description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てることができます。サーバーの負荷が少し増えます。" @@ -1348,6 +1352,7 @@ _widgets: jobQueue: "ジョブキュー" serverMetric: "サーバーメトリクス" aiscript: "AiScriptコンソール" + aiscriptApp: "AiScript App" aichan: "藍" userList: "ユーザーリスト" _userList: @@ -1463,6 +1468,22 @@ _timelines: social: "ソーシャル" global: "グローバル" +_play: + new: "Playの作成" + edit: "Playの編集" + created: "Playを作成しました" + updated: "Playを更新しました" + deleted: "Playを削除しました" + pageSetting: "Play設定" + editThisPage: "このPlayを編集" + viewSource: "ソースを表示" + my: "自分のPlay" + liked: "いいねしたPlay" + featured: "人気" + title: "タイトル" + script: "スクリプト" + summary: "説明" + _pages: newPage: "ページの作成" editPage: "ページの編集" diff --git a/packages/backend/migration/1672822262496-Flash.js b/packages/backend/migration/1672822262496-Flash.js new file mode 100644 index 0000000000..6c2338fab2 --- /dev/null +++ b/packages/backend/migration/1672822262496-Flash.js @@ -0,0 +1,29 @@ +export class Flash1672822262496 { + name = 'Flash1672822262496' + + async up(queryRunner) { + await queryRunner.query(`CREATE TABLE "flash" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, "title" character varying(256) NOT NULL, "summary" character varying(1024) NOT NULL, "userId" character varying(32) NOT NULL, "script" character varying(16384) NOT NULL, "permissions" character varying(256) array NOT NULL DEFAULT '{}', "likedCount" integer NOT NULL DEFAULT '0', CONSTRAINT "PK_0c01a2c1c5f2266942dd1b3fdbc" PRIMARY KEY ("id")); COMMENT ON COLUMN "flash"."createdAt" IS 'The created date of the Flash.'; COMMENT ON COLUMN "flash"."updatedAt" IS 'The updated date of the Flash.'; COMMENT ON COLUMN "flash"."userId" IS 'The ID of author.'`); + await queryRunner.query(`CREATE INDEX "IDX_149d2e44785707548c82999b01" ON "flash" ("createdAt") `); + await queryRunner.query(`CREATE INDEX "IDX_3aa8ea9a8f15214ad91638c0a7" ON "flash" ("updatedAt") `); + await queryRunner.query(`CREATE INDEX "IDX_9b88250fc2fd009b8f1b5623ed" ON "flash" ("userId") `); + await queryRunner.query(`CREATE TABLE "flash_like" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "flashId" character varying(32) NOT NULL, CONSTRAINT "PK_d110109ee310588d63d6183b233" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE INDEX "IDX_60c4af1c19a7a75f1592f93b28" ON "flash_like" ("userId") `); + await queryRunner.query(`CREATE UNIQUE INDEX "IDX_cfbfeeccb0cbedcd660b17eb07" ON "flash_like" ("userId", "flashId") `); + await queryRunner.query(`ALTER TABLE "flash" ADD CONSTRAINT "FK_9b88250fc2fd009b8f1b5623ed5" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "flash_like" ADD CONSTRAINT "FK_60c4af1c19a7a75f1592f93b287" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "flash_like" ADD CONSTRAINT "FK_6c16fe0e93b7a1951eca624b76a" FOREIGN KEY ("flashId") REFERENCES "flash"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "flash_like" DROP CONSTRAINT "FK_6c16fe0e93b7a1951eca624b76a"`); + await queryRunner.query(`ALTER TABLE "flash_like" DROP CONSTRAINT "FK_60c4af1c19a7a75f1592f93b287"`); + await queryRunner.query(`ALTER TABLE "flash" DROP CONSTRAINT "FK_9b88250fc2fd009b8f1b5623ed5"`); + await queryRunner.query(`DROP INDEX "public"."IDX_cfbfeeccb0cbedcd660b17eb07"`); + await queryRunner.query(`DROP INDEX "public"."IDX_60c4af1c19a7a75f1592f93b28"`); + await queryRunner.query(`DROP TABLE "flash_like"`); + await queryRunner.query(`DROP INDEX "public"."IDX_9b88250fc2fd009b8f1b5623ed"`); + await queryRunner.query(`DROP INDEX "public"."IDX_3aa8ea9a8f15214ad91638c0a7"`); + await queryRunner.query(`DROP INDEX "public"."IDX_149d2e44785707548c82999b01"`); + await queryRunner.query(`DROP TABLE "flash"`); + } +} diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index 7c6d12abf8..2f17fa389a 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -95,6 +95,8 @@ import { UserEntityService } from './entities/UserEntityService.js'; import { UserGroupEntityService } from './entities/UserGroupEntityService.js'; import { UserGroupInvitationEntityService } from './entities/UserGroupInvitationEntityService.js'; import { UserListEntityService } from './entities/UserListEntityService.js'; +import { FlashEntityService } from './entities/FlashEntityService.js'; +import { FlashLikeEntityService } from './entities/FlashLikeEntityService.js'; import { ApAudienceService } from './activitypub/ApAudienceService.js'; import { ApDbResolverService } from './activitypub/ApDbResolverService.js'; import { ApDeliverManagerService } from './activitypub/ApDeliverManagerService.js'; @@ -216,6 +218,8 @@ const $UserEntityService: Provider = { provide: 'UserEntityService', useExisting const $UserGroupEntityService: Provider = { provide: 'UserGroupEntityService', useExisting: UserGroupEntityService }; const $UserGroupInvitationEntityService: Provider = { provide: 'UserGroupInvitationEntityService', useExisting: UserGroupInvitationEntityService }; const $UserListEntityService: Provider = { provide: 'UserListEntityService', useExisting: UserListEntityService }; +const $FlashEntityService: Provider = { provide: 'FlashEntityService', useExisting: FlashEntityService }; +const $FlashLikeEntityService: Provider = { provide: 'FlashLikeEntityService', useExisting: FlashLikeEntityService }; const $ApAudienceService: Provider = { provide: 'ApAudienceService', useExisting: ApAudienceService }; const $ApDbResolverService: Provider = { provide: 'ApDbResolverService', useExisting: ApDbResolverService }; @@ -338,6 +342,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting UserGroupEntityService, UserGroupInvitationEntityService, UserListEntityService, + FlashEntityService, + FlashLikeEntityService, ApAudienceService, ApDbResolverService, ApDeliverManagerService, @@ -455,6 +461,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $UserGroupEntityService, $UserGroupInvitationEntityService, $UserListEntityService, + $FlashEntityService, + $FlashLikeEntityService, $ApAudienceService, $ApDbResolverService, $ApDeliverManagerService, @@ -572,6 +580,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting UserGroupEntityService, UserGroupInvitationEntityService, UserListEntityService, + FlashEntityService, + FlashLikeEntityService, ApAudienceService, ApDbResolverService, ApDeliverManagerService, @@ -688,6 +698,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $UserGroupEntityService, $UserGroupInvitationEntityService, $UserListEntityService, + $FlashEntityService, + $FlashLikeEntityService, $ApAudienceService, $ApDbResolverService, $ApDeliverManagerService, diff --git a/packages/backend/src/core/entities/FlashEntityService.ts b/packages/backend/src/core/entities/FlashEntityService.ts new file mode 100644 index 0000000000..61bd18c04f --- /dev/null +++ b/packages/backend/src/core/entities/FlashEntityService.ts @@ -0,0 +1,55 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI } from '@/di-symbols.js'; +import type { FlashsRepository, FlashLikesRepository } from '@/models/index.js'; +import { awaitAll } from '@/misc/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; +import type { Flash } from '@/models/entities/Flash.js'; +import { bindThis } from '@/decorators.js'; +import { UserEntityService } from './UserEntityService.js'; + +@Injectable() +export class FlashEntityService { + constructor( + @Inject(DI.flashsRepository) + private flashsRepository: FlashsRepository, + + @Inject(DI.flashLikesRepository) + private flashLikesRepository: FlashLikesRepository, + + private userEntityService: UserEntityService, + ) { + } + + @bindThis + public async pack( + src: Flash['id'] | Flash, + me?: { id: User['id'] } | null | undefined, + ): Promise> { + const meId = me ? me.id : null; + const flash = typeof src === 'object' ? src : await this.flashsRepository.findOneByOrFail({ id: src }); + + return await awaitAll({ + id: flash.id, + createdAt: flash.createdAt.toISOString(), + updatedAt: flash.updatedAt.toISOString(), + userId: flash.userId, + user: this.userEntityService.pack(flash.user ?? flash.userId, me), // { detail: true } すると無限ループするので注意 + title: flash.title, + summary: flash.summary, + script: flash.script, + likedCount: flash.likedCount, + isLiked: meId ? await this.flashLikesRepository.findOneBy({ flashId: flash.id, userId: meId }).then(x => x != null) : undefined, + }); + } + + @bindThis + public packMany( + flashs: Flash[], + me?: { id: User['id'] } | null | undefined, + ) { + return Promise.all(flashs.map(x => this.pack(x, me))); + } +} + diff --git a/packages/backend/src/core/entities/FlashLikeEntityService.ts b/packages/backend/src/core/entities/FlashLikeEntityService.ts new file mode 100644 index 0000000000..dcf12d53ea --- /dev/null +++ b/packages/backend/src/core/entities/FlashLikeEntityService.ts @@ -0,0 +1,44 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI } from '@/di-symbols.js'; +import type { FlashLikesRepository } from '@/models/index.js'; +import { awaitAll } from '@/misc/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; +import type { FlashLike } from '@/models/entities/FlashLike.js'; +import { bindThis } from '@/decorators.js'; +import { UserEntityService } from './UserEntityService.js'; +import { FlashEntityService } from './FlashEntityService.js'; + +@Injectable() +export class FlashLikeEntityService { + constructor( + @Inject(DI.flashLikesRepository) + private flashLikesRepository: FlashLikesRepository, + + private flashEntityService: FlashEntityService, + ) { + } + + @bindThis + public async pack( + src: FlashLike['id'] | FlashLike, + me?: { id: User['id'] } | null | undefined, + ) { + const like = typeof src === 'object' ? src : await this.flashLikesRepository.findOneByOrFail({ id: src }); + + return { + id: like.id, + flash: await this.flashEntityService.pack(like.flash ?? like.flashId, me), + }; + } + + @bindThis + public packMany( + likes: any[], + me: { id: User['id'] }, + ) { + return Promise.all(likes.map(x => this.pack(x, me))); + } +} + diff --git a/packages/backend/src/di-symbols.ts b/packages/backend/src/di-symbols.ts index d2a361405f..9719d773ca 100644 --- a/packages/backend/src/di-symbols.ts +++ b/packages/backend/src/di-symbols.ts @@ -69,5 +69,7 @@ export const DI = { adsRepository: Symbol('adsRepository'), passwordResetRequestsRepository: Symbol('passwordResetRequestsRepository'), retentionAggregationsRepository: Symbol('retentionAggregationsRepository'), + flashsRepository: Symbol('flashsRepository'), + flashLikesRepository: Symbol('flashLikesRepository'), //#endregion }; diff --git a/packages/backend/src/models/RepositoryModule.ts b/packages/backend/src/models/RepositoryModule.ts index e22f0517ca..a5d5a63931 100644 --- a/packages/backend/src/models/RepositoryModule.ts +++ b/packages/backend/src/models/RepositoryModule.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { User, Note, Announcement, AnnouncementRead, App, NoteFavorite, NoteThreadMuting, NoteReaction, NoteUnread, Notification, Poll, PollVote, UserProfile, UserKeypair, UserPending, AttestationChallenge, UserSecurityKey, UserPublickey, UserList, UserListJoining, UserGroup, UserGroupJoining, UserGroupInvitation, UserNotePining, UserIp, UsedUsername, Following, FollowRequest, Instance, Emoji, DriveFile, DriveFolder, Meta, Muting, Blocking, SwSubscription, Hashtag, AbuseUserReport, RegistrationTicket, AuthSession, AccessToken, Signin, MessagingMessage, Page, PageLike, GalleryPost, GalleryLike, ModerationLog, Clip, ClipNote, Antenna, AntennaNote, PromoNote, PromoRead, Relay, MutedNote, Channel, ChannelFollowing, ChannelNotePining, RegistryItem, Webhook, Ad, PasswordResetRequest, RetentionAggregation } from './index.js'; +import { User, Note, Announcement, AnnouncementRead, App, NoteFavorite, NoteThreadMuting, NoteReaction, NoteUnread, Notification, Poll, PollVote, UserProfile, UserKeypair, UserPending, AttestationChallenge, UserSecurityKey, UserPublickey, UserList, UserListJoining, UserGroup, UserGroupJoining, UserGroupInvitation, UserNotePining, UserIp, UsedUsername, Following, FollowRequest, Instance, Emoji, DriveFile, DriveFolder, Meta, Muting, Blocking, SwSubscription, Hashtag, AbuseUserReport, RegistrationTicket, AuthSession, AccessToken, Signin, MessagingMessage, Page, PageLike, GalleryPost, GalleryLike, ModerationLog, Clip, ClipNote, Antenna, AntennaNote, PromoNote, PromoRead, Relay, MutedNote, Channel, ChannelFollowing, ChannelNotePining, RegistryItem, Webhook, Ad, PasswordResetRequest, RetentionAggregation, FlashLike, Flash } from './index.js'; import type { DataSource } from 'typeorm'; import type { Provider } from '@nestjs/common'; @@ -388,6 +388,18 @@ const $retentionAggregationsRepository: Provider = { inject: [DI.db], }; +const $flashsRepository: Provider = { + provide: DI.flashsRepository, + useFactory: (db: DataSource) => db.getRepository(Flash), + inject: [DI.db], +}; + +const $flashLikesRepository: Provider = { + provide: DI.flashLikesRepository, + useFactory: (db: DataSource) => db.getRepository(FlashLike), + inject: [DI.db], +}; + @Module({ imports: [ ], @@ -456,6 +468,8 @@ const $retentionAggregationsRepository: Provider = { $adsRepository, $passwordResetRequestsRepository, $retentionAggregationsRepository, + $flashsRepository, + $flashLikesRepository, ], exports: [ $usersRepository, @@ -522,6 +536,8 @@ const $retentionAggregationsRepository: Provider = { $adsRepository, $passwordResetRequestsRepository, $retentionAggregationsRepository, + $flashsRepository, + $flashLikesRepository, ], }) export class RepositoryModule {} diff --git a/packages/backend/src/models/entities/Flash.ts b/packages/backend/src/models/entities/Flash.ts new file mode 100644 index 0000000000..d9a6ac987c --- /dev/null +++ b/packages/backend/src/models/entities/Flash.ts @@ -0,0 +1,60 @@ +import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; +import { id } from '../id.js'; +import { User } from './User.js'; +import { DriveFile } from './DriveFile.js'; + +@Entity() +export class Flash { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the Flash.', + }) + public createdAt: Date; + + @Index() + @Column('timestamp with time zone', { + comment: 'The updated date of the Flash.', + }) + public updatedAt: Date; + + @Column('varchar', { + length: 256, + }) + public title: string; + + @Column('varchar', { + length: 1024, + }) + public summary: string; + + @Index() + @Column({ + ...id(), + comment: 'The ID of author.', + }) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Column('varchar', { + length: 16384, + }) + public script: string; + + @Column('varchar', { + length: 256, array: true, default: '{}', + }) + public permissions: string[]; + + @Column('integer', { + default: 0, + }) + public likedCount: number; +} diff --git a/packages/backend/src/models/entities/FlashLike.ts b/packages/backend/src/models/entities/FlashLike.ts new file mode 100644 index 0000000000..81d39191ca --- /dev/null +++ b/packages/backend/src/models/entities/FlashLike.ts @@ -0,0 +1,33 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { id } from '../id.js'; +import { User } from './User.js'; +import { Flash } from './Flash.js'; + +@Entity() +@Index(['userId', 'flashId'], { unique: true }) +export class FlashLike { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone') + public createdAt: Date; + + @Index() + @Column(id()) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Column(id()) + public flashId: Flash['id']; + + @ManyToOne(type => Flash, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public flash: Flash | null; +} diff --git a/packages/backend/src/models/index.ts b/packages/backend/src/models/index.ts index ca7a7c9e56..b132475747 100644 --- a/packages/backend/src/models/index.ts +++ b/packages/backend/src/models/index.ts @@ -62,6 +62,8 @@ import { UserSecurityKey } from '@/models/entities/UserSecurityKey.js'; import { Webhook } from '@/models/entities/Webhook.js'; import { Channel } from '@/models/entities/Channel.js'; import { RetentionAggregation } from '@/models/entities/RetentionAggregation.js'; +import { Flash } from '@/models/entities/Flash.js'; +import { FlashLike } from '@/models/entities/FlashLike.js'; import type { Repository } from 'typeorm'; export { @@ -129,6 +131,8 @@ export { Webhook, Channel, RetentionAggregation, + Flash, + FlashLike, }; export type AbuseUserReportsRepository = Repository; @@ -195,3 +199,5 @@ export type UserSecurityKeysRepository = Repository; export type WebhooksRepository = Repository; export type ChannelsRepository = Repository; export type RetentionAggregationsRepository = Repository; +export type FlashsRepository = Repository; +export type FlashLikesRepository = Repository; diff --git a/packages/backend/src/postgre.ts b/packages/backend/src/postgre.ts index 4b4490a0c3..4f6b157d80 100644 --- a/packages/backend/src/postgre.ts +++ b/packages/backend/src/postgre.ts @@ -70,6 +70,8 @@ import { UserSecurityKey } from '@/models/entities/UserSecurityKey.js'; import { Webhook } from '@/models/entities/Webhook.js'; import { Channel } from '@/models/entities/Channel.js'; import { RetentionAggregation } from '@/models/entities/RetentionAggregation.js'; +import { Flash } from '@/models/entities/Flash.js'; +import { FlashLike } from '@/models/entities/FlashLike.js'; import { Config } from '@/config.js'; import MisskeyLogger from '@/logger.js'; @@ -184,6 +186,8 @@ export const entities = [ Webhook, UserIp, RetentionAggregation, + Flash, + FlashLike, ...charts, ]; diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 32eff7f312..60beca4f47 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -266,6 +266,15 @@ import * as ep___pages_like from './endpoints/pages/like.js'; import * as ep___pages_show from './endpoints/pages/show.js'; import * as ep___pages_unlike from './endpoints/pages/unlike.js'; import * as ep___pages_update from './endpoints/pages/update.js'; +import * as ep___flash_create from './endpoints/flash/create.js'; +import * as ep___flash_delete from './endpoints/flash/delete.js'; +import * as ep___flash_featured from './endpoints/flash/featured.js'; +import * as ep___flash_like from './endpoints/flash/like.js'; +import * as ep___flash_show from './endpoints/flash/show.js'; +import * as ep___flash_unlike from './endpoints/flash/unlike.js'; +import * as ep___flash_update from './endpoints/flash/update.js'; +import * as ep___flash_my from './endpoints/flash/my.js'; +import * as ep___flash_myLikes from './endpoints/flash/my-likes.js'; import * as ep___ping from './endpoints/ping.js'; import * as ep___pinnedUsers from './endpoints/pinned-users.js'; import * as ep___promo_read from './endpoints/promo/read.js'; @@ -587,6 +596,15 @@ const $pages_like: Provider = { provide: 'ep:pages/like', useClass: ep___pages_l const $pages_show: Provider = { provide: 'ep:pages/show', useClass: ep___pages_show.default }; const $pages_unlike: Provider = { provide: 'ep:pages/unlike', useClass: ep___pages_unlike.default }; const $pages_update: Provider = { provide: 'ep:pages/update', useClass: ep___pages_update.default }; +const $flash_create: Provider = { provide: 'ep:flash/create', useClass: ep___flash_create.default }; +const $flash_delete: Provider = { provide: 'ep:flash/delete', useClass: ep___flash_delete.default }; +const $flash_featured: Provider = { provide: 'ep:flash/featured', useClass: ep___flash_featured.default }; +const $flash_like: Provider = { provide: 'ep:flash/like', useClass: ep___flash_like.default }; +const $flash_show: Provider = { provide: 'ep:flash/show', useClass: ep___flash_show.default }; +const $flash_unlike: Provider = { provide: 'ep:flash/unlike', useClass: ep___flash_unlike.default }; +const $flash_update: Provider = { provide: 'ep:flash/update', useClass: ep___flash_update.default }; +const $flash_my: Provider = { provide: 'ep:flash/my', useClass: ep___flash_my.default }; +const $flash_myLikes: Provider = { provide: 'ep:flash/my-likes', useClass: ep___flash_myLikes.default }; const $ping: Provider = { provide: 'ep:ping', useClass: ep___ping.default }; const $pinnedUsers: Provider = { provide: 'ep:pinned-users', useClass: ep___pinnedUsers.default }; const $promo_read: Provider = { provide: 'ep:promo/read', useClass: ep___promo_read.default }; @@ -912,6 +930,15 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $pages_show, $pages_unlike, $pages_update, + $flash_create, + $flash_delete, + $flash_featured, + $flash_like, + $flash_show, + $flash_unlike, + $flash_update, + $flash_my, + $flash_myLikes, $ping, $pinnedUsers, $promo_read, @@ -1231,6 +1258,15 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $pages_show, $pages_unlike, $pages_update, + $flash_create, + $flash_delete, + $flash_featured, + $flash_like, + $flash_show, + $flash_unlike, + $flash_update, + $flash_my, + $flash_myLikes, $ping, $pinnedUsers, $promo_read, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 49dc3b224f..d4f8be5b85 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -265,6 +265,15 @@ import * as ep___pages_like from './endpoints/pages/like.js'; import * as ep___pages_show from './endpoints/pages/show.js'; import * as ep___pages_unlike from './endpoints/pages/unlike.js'; import * as ep___pages_update from './endpoints/pages/update.js'; +import * as ep___flash_create from './endpoints/flash/create.js'; +import * as ep___flash_delete from './endpoints/flash/delete.js'; +import * as ep___flash_featured from './endpoints/flash/featured.js'; +import * as ep___flash_like from './endpoints/flash/like.js'; +import * as ep___flash_show from './endpoints/flash/show.js'; +import * as ep___flash_unlike from './endpoints/flash/unlike.js'; +import * as ep___flash_update from './endpoints/flash/update.js'; +import * as ep___flash_my from './endpoints/flash/my.js'; +import * as ep___flash_myLikes from './endpoints/flash/my-likes.js'; import * as ep___ping from './endpoints/ping.js'; import * as ep___pinnedUsers from './endpoints/pinned-users.js'; import * as ep___promo_read from './endpoints/promo/read.js'; @@ -584,6 +593,15 @@ const eps = [ ['pages/show', ep___pages_show], ['pages/unlike', ep___pages_unlike], ['pages/update', ep___pages_update], + ['flash/create', ep___flash_create], + ['flash/delete', ep___flash_delete], + ['flash/featured', ep___flash_featured], + ['flash/like', ep___flash_like], + ['flash/show', ep___flash_show], + ['flash/unlike', ep___flash_unlike], + ['flash/update', ep___flash_update], + ['flash/my', ep___flash_my], + ['flash/my-likes', ep___flash_myLikes], ['ping', ep___ping], ['pinned-users', ep___pinnedUsers], ['promo/read', ep___promo_read], diff --git a/packages/backend/src/server/api/endpoints/flash/create.ts b/packages/backend/src/server/api/endpoints/flash/create.ts new file mode 100644 index 0000000000..a652047d98 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/flash/create.ts @@ -0,0 +1,66 @@ +import ms from 'ms'; +import { Inject, Injectable } from '@nestjs/common'; +import type { DriveFilesRepository, FlashsRepository, PagesRepository } from '@/models/index.js'; +import { IdService } from '@/core/IdService.js'; +import { Page } from '@/models/entities/Page.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { PageEntityService } from '@/core/entities/PageEntityService.js'; +import { DI } from '@/di-symbols.js'; +import { FlashEntityService } from '@/core/entities/FlashEntityService.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['flash'], + + requireCredential: true, + + kind: 'write:flash', + + limit: { + duration: ms('1hour'), + max: 10, + }, + + errors: { + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + title: { type: 'string' }, + summary: { type: 'string' }, + script: { type: 'string' }, + permissions: { type: 'array', items: { + type: 'string', + } }, + }, + required: ['title', 'summary', 'script', 'permissions'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + @Inject(DI.flashsRepository) + private flashsRepository: FlashsRepository, + + private flashEntityService: FlashEntityService, + private idService: IdService, + ) { + super(meta, paramDef, async (ps, me) => { + const flash = await this.flashsRepository.insert({ + id: this.idService.genId(), + userId: me.id, + createdAt: new Date(), + updatedAt: new Date(), + title: ps.title, + summary: ps.summary, + script: ps.script, + permissions: ps.permissions, + }).then(x => this.flashsRepository.findOneByOrFail(x.identifiers[0])); + + return await this.flashEntityService.pack(flash); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/flash/delete.ts b/packages/backend/src/server/api/endpoints/flash/delete.ts new file mode 100644 index 0000000000..e94ede9f68 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/flash/delete.ts @@ -0,0 +1,56 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { FlashsRepository } from '@/models/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['flashs'], + + requireCredential: true, + + kind: 'write:flash', + + errors: { + noSuchFlash: { + message: 'No such flash.', + code: 'NO_SUCH_FLASH', + id: 'de1623ef-bbb3-4289-a71e-14cfa83d9740', + }, + + accessDenied: { + message: 'Access denied.', + code: 'ACCESS_DENIED', + id: '1036ad7b-9f92-4fff-89c3-0e50dc941704', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + flashId: { type: 'string', format: 'misskey:id' }, + }, + required: ['flashId'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + @Inject(DI.flashsRepository) + private flashsRepository: FlashsRepository, + ) { + super(meta, paramDef, async (ps, me) => { + const flash = await this.flashsRepository.findOneBy({ id: ps.flashId }); + if (flash == null) { + throw new ApiError(meta.errors.noSuchFlash); + } + if (flash.userId !== me.id) { + throw new ApiError(meta.errors.accessDenied); + } + + await this.flashsRepository.delete(flash.id); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/flash/featured.ts b/packages/backend/src/server/api/endpoints/flash/featured.ts new file mode 100644 index 0000000000..570aef96d2 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/flash/featured.ts @@ -0,0 +1,48 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { FlashsRepository } from '@/models/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { FlashEntityService } from '@/core/entities/FlashEntityService.js'; +import { DI } from '@/di-symbols.js'; + +export const meta = { + tags: ['flash'], + + requireCredential: false, + + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + ref: 'Flash', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + @Inject(DI.flashsRepository) + private flashsRepository: FlashsRepository, + + private flashEntityService: FlashEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const query = this.flashsRepository.createQueryBuilder('flash') + .andWhere('flash.likedCount > 0') + .orderBy('flash.likedCount', 'DESC'); + + const flashs = await query.take(10).getMany(); + + return await this.flashEntityService.packMany(flashs, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/flash/like.ts b/packages/backend/src/server/api/endpoints/flash/like.ts new file mode 100644 index 0000000000..5581b8ec60 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/flash/like.ts @@ -0,0 +1,87 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { FlashsRepository, FlashLikesRepository } from '@/models/index.js'; +import { IdService } from '@/core/IdService.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['flash'], + + requireCredential: true, + + kind: 'write:flash-likes', + + errors: { + noSuchFlash: { + message: 'No such flash.', + code: 'NO_SUCH_FLASH', + id: 'c07c1491-9161-4c5c-9d75-01906f911f73', + }, + + yourFlash: { + message: 'You cannot like your flash.', + code: 'YOUR_FLASH', + id: '3fd8a0e7-5955-4ba9-85bb-bf3e0c30e13b', + }, + + alreadyLiked: { + message: 'The flash has already been liked.', + code: 'ALREADY_LIKED', + id: '010065cf-ad43-40df-8067-abff9f4686e3', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + flashId: { type: 'string', format: 'misskey:id' }, + }, + required: ['flashId'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + @Inject(DI.flashsRepository) + private flashsRepository: FlashsRepository, + + @Inject(DI.flashLikesRepository) + private flashLikesRepository: FlashLikesRepository, + + private idService: IdService, + ) { + super(meta, paramDef, async (ps, me) => { + const flash = await this.flashsRepository.findOneBy({ id: ps.flashId }); + if (flash == null) { + throw new ApiError(meta.errors.noSuchFlash); + } + + if (flash.userId === me.id) { + throw new ApiError(meta.errors.yourFlash); + } + + // if already liked + const exist = await this.flashLikesRepository.findOneBy({ + flashId: flash.id, + userId: me.id, + }); + + if (exist != null) { + throw new ApiError(meta.errors.alreadyLiked); + } + + // Create like + await this.flashLikesRepository.insert({ + id: this.idService.genId(), + createdAt: new Date(), + flashId: flash.id, + userId: me.id, + }); + + this.flashsRepository.increment({ id: flash.id }, 'likedCount', 1); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/flash/my-likes.ts b/packages/backend/src/server/api/endpoints/flash/my-likes.ts new file mode 100644 index 0000000000..f7716ea74a --- /dev/null +++ b/packages/backend/src/server/api/endpoints/flash/my-likes.ts @@ -0,0 +1,68 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { FlashLikesRepository } from '@/models/index.js'; +import { QueryService } from '@/core/QueryService.js'; +import { FlashLikeEntityService } from '@/core/entities/FlashLikeEntityService.js'; +import { DI } from '@/di-symbols.js'; + +export const meta = { + tags: ['account', 'flash'], + + requireCredential: true, + + kind: 'read:flash-likes', + + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + flash: { + type: 'object', + optional: false, nullable: false, + ref: 'Flash', + }, + }, + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + }, + required: [], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + @Inject(DI.flashLikesRepository) + private flashLikesRepository: FlashLikesRepository, + + private flashLikeEntityService: FlashLikeEntityService, + private queryService: QueryService, + ) { + super(meta, paramDef, async (ps, me) => { + const query = this.queryService.makePaginationQuery(this.flashLikesRepository.createQueryBuilder('like'), ps.sinceId, ps.untilId) + .andWhere('like.userId = :meId', { meId: me.id }) + .leftJoinAndSelect('like.flash', 'flash'); + + const likes = await query + .take(ps.limit) + .getMany(); + + return this.flashLikeEntityService.packMany(likes, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/flash/my.ts b/packages/backend/src/server/api/endpoints/flash/my.ts new file mode 100644 index 0000000000..baed7f000f --- /dev/null +++ b/packages/backend/src/server/api/endpoints/flash/my.ts @@ -0,0 +1,57 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { FlashsRepository } from '@/models/index.js'; +import { QueryService } from '@/core/QueryService.js'; +import { FlashEntityService } from '@/core/entities/FlashEntityService.js'; +import { DI } from '@/di-symbols.js'; + +export const meta = { + tags: ['account', 'flash'], + + requireCredential: true, + + kind: 'read:flash', + + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + ref: 'Flash', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + }, + required: [], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + @Inject(DI.flashsRepository) + private flashsRepository: FlashsRepository, + + private flashEntityService: FlashEntityService, + private queryService: QueryService, + ) { + super(meta, paramDef, async (ps, me) => { + const query = this.queryService.makePaginationQuery(this.flashsRepository.createQueryBuilder('flash'), ps.sinceId, ps.untilId) + .andWhere('flash.userId = :meId', { meId: me.id }); + + const flashs = await query + .take(ps.limit) + .getMany(); + + return await this.flashEntityService.packMany(flashs); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/flash/show.ts b/packages/backend/src/server/api/endpoints/flash/show.ts new file mode 100644 index 0000000000..48114c5a60 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/flash/show.ts @@ -0,0 +1,60 @@ +import { IsNull } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; +import type { UsersRepository, FlashsRepository } from '@/models/index.js'; +import type { Flash } from '@/models/entities/Flash.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { FlashEntityService } from '@/core/entities/FlashEntityService.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['flashs'], + + requireCredential: false, + + res: { + type: 'object', + optional: false, nullable: false, + ref: 'Flash', + }, + + errors: { + noSuchFlash: { + message: 'No such flash.', + code: 'NO_SUCH_FLASH', + id: 'f0d34a1a-d29a-401d-90ba-1982122b5630', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + flashId: { type: 'string', format: 'misskey:id' }, + }, + required: ['flashId'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.flashsRepository) + private flashsRepository: FlashsRepository, + + private flashEntityService: FlashEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const flash = await this.flashsRepository.findOneBy({ id: ps.flashId }); + + if (flash == null) { + throw new ApiError(meta.errors.noSuchFlash); + } + + return await this.flashEntityService.pack(flash, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/flash/unlike.ts b/packages/backend/src/server/api/endpoints/flash/unlike.ts new file mode 100644 index 0000000000..b994f5d347 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/flash/unlike.ts @@ -0,0 +1,68 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { FlashsRepository, FlashLikesRepository } from '@/models/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['flash'], + + requireCredential: true, + + kind: 'write:flash-likes', + + errors: { + noSuchFlash: { + message: 'No such flash.', + code: 'NO_SUCH_FLASH', + id: 'afe8424a-a69e-432d-a5f2-2f0740c62410', + }, + + notLiked: { + message: 'You have not liked that flash.', + code: 'NOT_LIKED', + id: '755f25a7-9871-4f65-9f34-51eaad9ae0ac', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + flashId: { type: 'string', format: 'misskey:id' }, + }, + required: ['flashId'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + @Inject(DI.flashsRepository) + private flashsRepository: FlashsRepository, + + @Inject(DI.flashLikesRepository) + private flashLikesRepository: FlashLikesRepository, + ) { + super(meta, paramDef, async (ps, me) => { + const flash = await this.flashsRepository.findOneBy({ id: ps.flashId }); + if (flash == null) { + throw new ApiError(meta.errors.noSuchFlash); + } + + const exist = await this.flashLikesRepository.findOneBy({ + flashId: flash.id, + userId: me.id, + }); + + if (exist == null) { + throw new ApiError(meta.errors.notLiked); + } + + // Delete like + await this.flashLikesRepository.delete(exist.id); + + this.flashsRepository.decrement({ id: flash.id }, 'likedCount', 1); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/flash/update.ts b/packages/backend/src/server/api/endpoints/flash/update.ts new file mode 100644 index 0000000000..9ab17a61e8 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/flash/update.ts @@ -0,0 +1,78 @@ +import ms from 'ms'; +import { Not } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; +import type { FlashsRepository, DriveFilesRepository } from '@/models/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['flash'], + + requireCredential: true, + + kind: 'write:flash', + + limit: { + duration: ms('1hour'), + max: 300, + }, + + errors: { + noSuchFlash: { + message: 'No such flash.', + code: 'NO_SUCH_FLASH', + id: '611e13d2-309e-419a-a5e4-e0422da39b02', + }, + + accessDenied: { + message: 'Access denied.', + code: 'ACCESS_DENIED', + id: '08e60c88-5948-478e-a132-02ec701d67b2', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + flashId: { type: 'string', format: 'misskey:id' }, + title: { type: 'string' }, + summary: { type: 'string' }, + script: { type: 'string' }, + permissions: { type: 'array', items: { + type: 'string', + } }, + }, + required: ['flashId', 'title', 'summary', 'script', 'permissions'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + @Inject(DI.flashsRepository) + private flashsRepository: FlashsRepository, + + @Inject(DI.driveFilesRepository) + private driveFilesRepository: DriveFilesRepository, + ) { + super(meta, paramDef, async (ps, me) => { + const flash = await this.flashsRepository.findOneBy({ id: ps.flashId }); + if (flash == null) { + throw new ApiError(meta.errors.noSuchFlash); + } + if (flash.userId !== me.id) { + throw new ApiError(meta.errors.accessDenied); + } + + await this.flashsRepository.update(flash.id, { + updatedAt: new Date(), + title: ps.title, + summary: ps.summary, + script: ps.script, + permissions: ps.permissions, + }); + }); + } +} diff --git a/packages/frontend/src/components/MkAsUi.vue b/packages/frontend/src/components/MkAsUi.vue new file mode 100644 index 0000000000..bc1e25957b --- /dev/null +++ b/packages/frontend/src/components/MkAsUi.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue index daf47e12d4..f9602de787 100644 --- a/packages/frontend/src/components/MkButton.vue +++ b/packages/frontend/src/components/MkButton.vue @@ -2,7 +2,7 @@ @@ -207,20 +207,6 @@ definePageMetadata(computed(() => page ? { padding: 16px 0 0 0; border-top: solid 0.5px var(--divider); - > .like { - > .button { - --accent: rgb(241 97 132); - --X8: rgb(241 92 128); - --buttonBg: rgb(216 71 106 / 5%); - --buttonHoverBg: rgb(216 71 106 / 10%); - color: #ff002f; - - ::v-deep(.count) { - margin-left: 0.5em; - } - } - } - > .other { margin-left: auto; diff --git a/packages/frontend/src/pages/scratchpad.vue b/packages/frontend/src/pages/scratchpad.vue index 9db17efc03..7d097fbaaa 100644 --- a/packages/frontend/src/pages/scratchpad.vue +++ b/packages/frontend/src/pages/scratchpad.vue @@ -1,25 +1,34 @@ - diff --git a/packages/frontend/src/widgets/index.ts b/packages/frontend/src/widgets/index.ts index 39826f13c8..3966649da4 100644 --- a/packages/frontend/src/widgets/index.ts +++ b/packages/frontend/src/widgets/index.ts @@ -22,6 +22,7 @@ export default function(app: App) { app.component('MkwInstanceCloud', defineAsyncComponent(() => import('./instance-cloud.vue'))); app.component('MkwButton', defineAsyncComponent(() => import('./button.vue'))); app.component('MkwAiscript', defineAsyncComponent(() => import('./aiscript.vue'))); + app.component('MkwAiscriptApp', defineAsyncComponent(() => import('./aiscript-app.vue'))); app.component('MkwAichan', defineAsyncComponent(() => import('./aichan.vue'))); app.component('MkwUserList', defineAsyncComponent(() => import('./user-list.vue'))); } @@ -48,6 +49,7 @@ export const widgets = [ 'jobQueue', 'button', 'aiscript', + 'aiscriptApp', 'aichan', 'userList', ]; -- cgit v1.2.3-freya From 462acc9eee3f89a926fd4b46ffed0b066519759b Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Jan 2023 15:50:25 +0900 Subject: カスタム絵文字一覧情報をmetaから分離 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 + .../src/core/entities/EmojiEntityService.ts | 8 +- packages/backend/src/models/schema/emoji.ts | 8 +- packages/backend/src/server/api/EndpointsModule.ts | 4 + packages/backend/src/server/api/endpoints.ts | 2 + .../backend/src/server/api/endpoints/emojis.ts | 91 ++++++++++++++++++++++ packages/backend/src/server/api/endpoints/meta.ts | 57 -------------- .../frontend/src/components/MkAutocomplete.vue | 5 +- packages/frontend/src/components/MkEmojiPicker.vue | 9 ++- packages/frontend/src/custom-emojis.ts | 48 ++++++++++++ packages/frontend/src/instance.ts | 24 +----- packages/frontend/src/local-storage.ts | 2 + packages/frontend/src/pages/about.emojis.vue | 87 ++++++++------------- .../frontend/src/pages/admin/emoji-edit-dialog.vue | 4 +- .../frontend/src/pages/admin/overview.stats.vue | 5 +- packages/frontend/src/pages/mfm-cheat-sheet.vue | 5 +- packages/frontend/src/pages/settings/index.vue | 2 + 17 files changed, 212 insertions(+), 151 deletions(-) create mode 100644 packages/backend/src/server/api/endpoints/emojis.ts create mode 100644 packages/frontend/src/custom-emojis.ts (limited to 'packages/backend/src/server/api/EndpointsModule.ts') diff --git a/CHANGELOG.md b/CHANGELOG.md index 269f8b1000..43cf0ef6e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,8 @@ You should also include the user name that made the change. - Firefox109以下はサポートされなくなりました #### For app developers +- API: metaのレスポンスに`emojis`プロパティが含まれなくなりました + - カスタム絵文字一覧情報を取得するには、`emojis`エンドポイントにリクエストします - API: カスタム絵文字エンティティに`url`プロパティが含まれなくなりました - 絵文字画像を表示するには、`/emoji/.webp`にリクエストすると画像が返ります。 - e.g. `https://p1.a9z.dev/emoji/misskey.webp` diff --git a/packages/backend/src/core/entities/EmojiEntityService.ts b/packages/backend/src/core/entities/EmojiEntityService.ts index 8a2dc70eda..2a4e09519f 100644 --- a/packages/backend/src/core/entities/EmojiEntityService.ts +++ b/packages/backend/src/core/entities/EmojiEntityService.ts @@ -22,23 +22,25 @@ export class EmojiEntityService { @bindThis public async pack( src: Emoji['id'] | Emoji, + opts: { omitHost?: boolean; omitId?: boolean; } = {}, ): Promise> { const emoji = typeof src === 'object' ? src : await this.emojisRepository.findOneByOrFail({ id: src }); return { - id: emoji.id, + id: opts.omitId ? undefined : emoji.id, aliases: emoji.aliases, name: emoji.name, category: emoji.category, - host: emoji.host, + host: opts.omitHost ? undefined : emoji.host, }; } @bindThis public packMany( emojis: any[], + opts: { omitHost?: boolean; omitId?: boolean; } = {}, ) { - return Promise.all(emojis.map(x => this.pack(x))); + return Promise.all(emojis.map(x => this.pack(x, opts))); } } diff --git a/packages/backend/src/models/schema/emoji.ts b/packages/backend/src/models/schema/emoji.ts index 9a52609b68..d897a0fc05 100644 --- a/packages/backend/src/models/schema/emoji.ts +++ b/packages/backend/src/models/schema/emoji.ts @@ -3,7 +3,7 @@ export const packedEmojiSchema = { properties: { id: { type: 'string', - optional: false, nullable: false, + optional: true, nullable: false, format: 'id', example: 'xxxxxxxxxx', }, @@ -26,12 +26,8 @@ export const packedEmojiSchema = { }, host: { type: 'string', - optional: false, nullable: true, + optional: true, nullable: true, description: 'The local host is represented with `null`.', }, - url: { - type: 'string', - optional: true, nullable: false, - }, }, } as const; diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 60beca4f47..ab9349966d 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -220,6 +220,7 @@ import * as ep___messaging_messages_create from './endpoints/messaging/messages/ import * as ep___messaging_messages_delete from './endpoints/messaging/messages/delete.js'; import * as ep___messaging_messages_read from './endpoints/messaging/messages/read.js'; import * as ep___meta from './endpoints/meta.js'; +import * as ep___emojis from './endpoints/emojis.js'; import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js'; import * as ep___mute_create from './endpoints/mute/create.js'; import * as ep___mute_delete from './endpoints/mute/delete.js'; @@ -550,6 +551,7 @@ const $messaging_messages_create: Provider = { provide: 'ep:messaging/messages/c const $messaging_messages_delete: Provider = { provide: 'ep:messaging/messages/delete', useClass: ep___messaging_messages_delete.default }; const $messaging_messages_read: Provider = { provide: 'ep:messaging/messages/read', useClass: ep___messaging_messages_read.default }; const $meta: Provider = { provide: 'ep:meta', useClass: ep___meta.default }; +const $emojis: Provider = { provide: 'ep:emojis', useClass: ep___emojis.default }; const $miauth_genToken: Provider = { provide: 'ep:miauth/gen-token', useClass: ep___miauth_genToken.default }; const $mute_create: Provider = { provide: 'ep:mute/create', useClass: ep___mute_create.default }; const $mute_delete: Provider = { provide: 'ep:mute/delete', useClass: ep___mute_delete.default }; @@ -884,6 +886,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $messaging_messages_delete, $messaging_messages_read, $meta, + $emojis, $miauth_genToken, $mute_create, $mute_delete, @@ -1212,6 +1215,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $messaging_messages_delete, $messaging_messages_read, $meta, + $emojis, $miauth_genToken, $mute_create, $mute_delete, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index d4f8be5b85..f9749ad660 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -219,6 +219,7 @@ import * as ep___messaging_messages_create from './endpoints/messaging/messages/ import * as ep___messaging_messages_delete from './endpoints/messaging/messages/delete.js'; import * as ep___messaging_messages_read from './endpoints/messaging/messages/read.js'; import * as ep___meta from './endpoints/meta.js'; +import * as ep___emojis from './endpoints/emojis.js'; import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js'; import * as ep___mute_create from './endpoints/mute/create.js'; import * as ep___mute_delete from './endpoints/mute/delete.js'; @@ -547,6 +548,7 @@ const eps = [ ['messaging/messages/delete', ep___messaging_messages_delete], ['messaging/messages/read', ep___messaging_messages_read], ['meta', ep___meta], + ['emojis', ep___emojis], ['miauth/gen-token', ep___miauth_genToken], ['mute/create', ep___mute_create], ['mute/delete', ep___mute_delete], diff --git a/packages/backend/src/server/api/endpoints/emojis.ts b/packages/backend/src/server/api/endpoints/emojis.ts new file mode 100644 index 0000000000..0a16268229 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/emojis.ts @@ -0,0 +1,91 @@ +import { IsNull, MoreThan } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; +import type { EmojisRepository } from '@/models/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; +import type { Config } from '@/config.js'; +import { DI } from '@/di-symbols.js'; + +export const meta = { + tags: ['meta'], + + requireCredential: false, + + res: { + type: 'object', + optional: false, nullable: false, + properties: { + emojis: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + aliases: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'string', + optional: false, nullable: false, + }, + }, + category: { + type: 'string', + optional: false, nullable: true, + }, + }, + }, + }, + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + }, + required: [], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + @Inject(DI.config) + private config: Config, + + @Inject(DI.emojisRepository) + private emojisRepository: EmojisRepository, + + private emojiEntityService: EmojiEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const emojis = await this.emojisRepository.find({ + where: { + host: IsNull(), + }, + order: { + category: 'ASC', + name: 'ASC', + }, + cache: { + id: 'meta_emojis', + milliseconds: 3600000, // 1 hour + }, + }); + + return { + emojis: await this.emojiEntityService.packMany(emojis, { + omitId: true, + omitHost: true, + }), + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index 05da011979..c44d63d64b 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -4,7 +4,6 @@ import type { AdsRepository, EmojisRepository, UsersRepository } from '@/models/ import { MAX_NOTE_TEXT_LENGTH, DB_MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; import { MetaService } from '@/core/MetaService.js'; import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; @@ -152,43 +151,6 @@ export const meta = { type: 'number', optional: false, nullable: false, }, - emojis: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'object', - optional: false, nullable: false, - properties: { - id: { - type: 'string', - optional: false, nullable: false, - format: 'id', - }, - aliases: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'string', - optional: false, nullable: false, - }, - }, - category: { - type: 'string', - optional: false, nullable: true, - }, - host: { - type: 'string', - optional: false, nullable: true, - description: 'The local host is represented with `null`.', - }, - url: { - type: 'string', - optional: false, nullable: false, - format: 'url', - }, - }, - }, - }, ads: { type: 'array', optional: false, nullable: false, @@ -326,30 +288,12 @@ export default class extends Endpoint { @Inject(DI.adsRepository) private adsRepository: AdsRepository, - @Inject(DI.emojisRepository) - private emojisRepository: EmojisRepository, - private userEntityService: UserEntityService, - private emojiEntityService: EmojiEntityService, private metaService: MetaService, ) { super(meta, paramDef, async (ps, me) => { const instance = await this.metaService.fetch(true); - const emojis = await this.emojisRepository.find({ - where: { - host: IsNull(), - }, - order: { - category: 'ASC', - name: 'ASC', - }, - cache: { - id: 'meta_emojis', - milliseconds: 3600000, // 1 hour - }, - }); - const ads = await this.adsRepository.find({ where: { expiresAt: MoreThan(new Date()), @@ -390,7 +334,6 @@ export default class extends Endpoint { backgroundImageUrl: instance.backgroundImageUrl, logoImageUrl: instance.logoImageUrl, maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, // 後方互換性のため - emojis: await this.emojiEntityService.packMany(emojis), defaultLightTheme: instance.defaultLightTheme, defaultDarkTheme: instance.defaultDarkTheme, ads: ads.map(ad => ({ diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue index 8ed60bc5dc..0d0c5edbae 100644 --- a/packages/frontend/src/components/MkAutocomplete.vue +++ b/packages/frontend/src/components/MkAutocomplete.vue @@ -47,6 +47,9 @@ import { emojilist } from '@/scripts/emojilist'; import { instance } from '@/instance'; import { i18n } from '@/i18n'; import { miLocalStorage } from '@/local-storage'; +import { getCustomEmojis } from '@/custom-emojis'; + +const customEmojis = await getCustomEmojis(); type EmojiDef = { emoji: string; @@ -86,7 +89,6 @@ for (const x of lib) { emjdb.sort((a, b) => a.name.length - b.name.length); //#region Construct Emoji DB -const customEmojis = instance.emojis; const emojiDefinitions: EmojiDef[] = []; for (const x of customEmojis) { @@ -117,7 +119,6 @@ export default { emojiDb, emojiDefinitions, emojilist, - customEmojis, }; diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index f3b3ac0b50..8df01f6c25 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -6,7 +6,7 @@
@@ -28,8 +28,8 @@ - diff --git a/packages/frontend/src/components/MkUserCardMini.vue b/packages/frontend/src/components/MkUserCardMini.vue index 1a4c494987..be8a4c408e 100644 --- a/packages/frontend/src/components/MkUserCardMini.vue +++ b/packages/frontend/src/components/MkUserCardMini.vue @@ -1,5 +1,5 @@ + - - - - - - - - - + + +
@@ -180,12 +181,16 @@ import { definePageMetadata } from '@/scripts/page-metadata'; import { i18n } from '@/i18n'; import { iAmAdmin, iAmModerator } from '@/account'; import { instance } from '@/instance'; +import MkRolePreview from '@/components/MkRolePreview.vue'; -const props = defineProps<{ +const props = withDefaults(defineProps<{ userId: string; -}>(); + initialTab?: string; +}>(), { + initialTab: 'overview', +}); -let tab = $ref('overview'); +let tab = $ref(props.initialTab); let chartSrc = $ref('per-user-notes'); let user = $ref(); let init = $ref>(); @@ -195,7 +200,6 @@ let ap = $ref(null); let moderator = $ref(false); let silenced = $ref(false); let suspended = $ref(false); -let driveCapacityOverrideMb: number | null = $ref(0); let moderationNote = $ref(''); const filesPagination = { endpoint: 'admin/drive/files' as const, @@ -220,7 +224,6 @@ function createFetcher() { moderator = info.isModerator; silenced = info.isSilenced; suspended = info.isSuspended; - driveCapacityOverrideMb = user.driveCapacityOverrideMb; moderationNote = info.moderationNote; watch($$(moderationNote), async () => { @@ -257,19 +260,6 @@ async function resetPassword() { }); } -async function toggleSilence(v) { - const confirm = await os.confirm({ - type: 'warning', - text: v ? i18n.ts.silenceConfirm : i18n.ts.unsilenceConfirm, - }); - if (confirm.canceled) { - silenced = !v; - } else { - await os.api(v ? 'admin/silence-user' : 'admin/unsilence-user', { userId: user.id }); - await refreshUser(); - } -} - async function toggleSuspend(v) { const confirm = await os.confirm({ type: 'warning', @@ -283,11 +273,6 @@ async function toggleSuspend(v) { } } -async function toggleModerator(v) { - await os.api(v ? 'admin/moderators/add' : 'admin/moderators/remove', { userId: user.id }); - await refreshUser(); -} - async function deleteAllFiles() { const confirm = await os.confirm({ type: 'warning', @@ -307,22 +292,6 @@ async function deleteAllFiles() { await refreshUser(); } -async function applyDriveCapacityOverride() { - let driveCapOrMb = driveCapacityOverrideMb; - if (driveCapacityOverrideMb && driveCapacityOverrideMb < 0) { - driveCapOrMb = null; - } - try { - await os.apiWithDialog('admin/drive-capacity-override', { userId: user.id, overrideMb: driveCapOrMb }); - await refreshUser(); - } catch (err) { - os.alert({ - type: 'error', - text: err.toString(), - }); - } -} - async function deleteAccount() { const confirm = await os.confirm({ type: 'warning', @@ -347,6 +316,31 @@ async function deleteAccount() { } } +async function assignRole() { + const roles = await os.api('admin/roles/list'); + + const { canceled, result: roleId } = await os.select({ + title: i18n.ts._role.chooseRoleToAssign, + items: roles.map(r => ({ text: r.name, value: r.id })), + }); + if (canceled) return; + + await os.apiWithDialog('admin/roles/assign', { roleId, userId: user.id }); + refreshUser(); +} + +async function unassignRole(role, ev) { + os.popupMenu([{ + text: i18n.ts.unassign, + icon: 'ti ti-x', + danger: true, + action: async () => { + await os.apiWithDialog('admin/roles/unassign', { roleId: role.id, userId: user.id }); + refreshUser(); + }, + }], ev.currentTarget ?? ev.target); +} + watch(() => props.userId, () => { init = createFetcher(); }, { @@ -484,4 +478,19 @@ definePageMetadata(computed(() => ({ margin-left: auto; } } + +.roleItem { + display: flex; +} + +.role { + flex: 1; +} + +.roleUnassign { + width: 32px; + height: 32px; + margin-left: 8px; + align-self: center; +} diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index 07d34a794d..eea4d20094 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -18,7 +18,6 @@
-
@@ -35,7 +34,6 @@
-
@@ -189,7 +187,7 @@ onMounted(() => { const bd = parseInt(props.user.birthday.split('-')[2]); if (m === bm && d === bd) { confetti({ - duration: 1000 * 4 + duration: 1000 * 4, }); } } diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts index 4b9f49f8fd..05dcd7806e 100644 --- a/packages/frontend/src/router.ts +++ b/packages/frontend/src/router.ts @@ -37,6 +37,7 @@ export const routes = [{ }, { path: '/user-info/:userId', component: page(() => import('./pages/user-info.vue')), + hash: 'initialTab', }, { path: '/instance-info/:host', component: page(() => import('./pages/instance-info.vue')), @@ -351,6 +352,22 @@ export const routes = [{ path: '/ads', name: 'ads', component: page(() => import('./pages/admin/ads.vue')), + }, { + path: '/roles/:id/edit', + name: 'roles', + component: page(() => import('./pages/admin/roles.edit.vue')), + }, { + path: '/roles/new', + name: 'roles', + component: page(() => import('./pages/admin/roles.edit.vue')), + }, { + path: '/roles/:id', + name: 'roles', + component: page(() => import('./pages/admin/roles.role.vue')), + }, { + path: '/roles', + name: 'roles', + component: page(() => import('./pages/admin/roles.vue')), }, { path: '/database', name: 'database', diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts index 7ede64c327..74bd61fd78 100644 --- a/packages/frontend/src/scripts/get-user-menu.ts +++ b/packages/frontend/src/scripts/get-user-menu.ts @@ -108,26 +108,6 @@ export function getUserMenu(user, router: Router = mainRouter) { }); } - async function toggleSilence() { - if (!await getConfirmed(i18n.t(user.isSilenced ? 'unsilenceConfirm' : 'silenceConfirm'))) return; - - os.apiWithDialog(user.isSilenced ? 'admin/unsilence-user' : 'admin/silence-user', { - userId: user.id, - }).then(() => { - user.isSilenced = !user.isSilenced; - }); - } - - async function toggleSuspend() { - if (!await getConfirmed(i18n.t(user.isSuspended ? 'unsuspendConfirm' : 'suspendConfirm'))) return; - - os.apiWithDialog(user.isSuspended ? 'admin/unsuspend-user' : 'admin/suspend-user', { - userId: user.id, - }).then(() => { - user.isSuspended = !user.isSuspended; - }); - } - function reportAbuse() { os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), { user: user, @@ -218,13 +198,11 @@ export function getUserMenu(user, router: Router = mainRouter) { if (iAmModerator) { menu = menu.concat([null, { - icon: 'ti ti-microphone-2-off', - text: user.isSilenced ? i18n.ts.unsilence : i18n.ts.silence, - action: toggleSilence, - }, { - icon: 'ti ti-snowflake', - text: user.isSuspended ? i18n.ts.unsuspend : i18n.ts.suspend, - action: toggleSuspend, + icon: 'ti ti-user-exclamation', + text: i18n.ts.moderation, + action: () => { + router.push('/user-info/' + user.id + '#moderation'); + }, }]); } } diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue index f75e526939..b8a0a504a3 100644 --- a/packages/frontend/src/ui/deck/tl-column.vue +++ b/packages/frontend/src/ui/deck/tl-column.vue @@ -45,9 +45,7 @@ onMounted(() => { if (props.column.tl == null) { setType(); } else if ($i) { - disabled = !$i.isModerator && !$i.isAdmin && ( - instance.disableLocalTimeline && ['local', 'social'].includes(props.column.tl) || - instance.disableGlobalTimeline && ['global'].includes(props.column.tl)); + disabled = false; // TODO } }); -- cgit v1.2.3-freya From 7b7faf1e84c4f2c53a9adbfd918d91590c890a0a Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 13 Jan 2023 14:22:53 +0900 Subject: 招待コード発行を権限を持つユーザーが行えるように MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve #9541 --- locales/ja-JP.yml | 1 + packages/backend/src/core/RoleService.ts | 3 + packages/backend/src/server/api/EndpointsModule.ts | 8 +-- packages/backend/src/server/api/endpoints.ts | 4 +- .../src/server/api/endpoints/admin/invite.ts | 61 -------------------- .../backend/src/server/api/endpoints/invite.ts | 67 ++++++++++++++++++++++ packages/frontend/src/pages/admin/index.vue | 4 +- packages/frontend/src/pages/admin/roles.editor.vue | 16 ++++++ packages/frontend/src/pages/admin/roles.vue | 10 ++++ packages/frontend/src/ui/_common_/common.ts | 19 +++++- 10 files changed, 123 insertions(+), 70 deletions(-) delete mode 100644 packages/backend/src/server/api/endpoints/admin/invite.ts create mode 100644 packages/backend/src/server/api/endpoints/invite.ts (limited to 'packages/backend/src/server/api/EndpointsModule.ts') diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index ae535735b6..4a1a2d68ef 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -957,6 +957,7 @@ _role: gtlAvailable: "グローバルタイムラインの閲覧" ltlAvailable: "ローカルタイムラインの閲覧" canPublicNote: "パブリック投稿の許可" + canInvite: "インスタンス招待コードの発行" driveCapacity: "ドライブ容量" antennaMax: "アンテナの作成可能数" _condition: diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index 3183adb369..78524c833d 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -16,6 +16,7 @@ export type RoleOptions = { gtlAvailable: boolean; ltlAvailable: boolean; canPublicNote: boolean; + canInvite: boolean; driveCapacityMb: number; antennaLimit: number; }; @@ -24,6 +25,7 @@ export const DEFAULT_ROLE: RoleOptions = { gtlAvailable: true, ltlAvailable: true, canPublicNote: true, + canInvite: false, driveCapacityMb: 100, antennaLimit: 5, }; @@ -179,6 +181,7 @@ export class RoleService implements OnApplicationShutdown { gtlAvailable: getOptionValues('gtlAvailable').some(x => x === true), ltlAvailable: getOptionValues('ltlAvailable').some(x => x === true), canPublicNote: getOptionValues('canPublicNote').some(x => x === true), + canInvite: getOptionValues('canInvite').some(x => x === true), driveCapacityMb: Math.max(...getOptionValues('driveCapacityMb')), antennaLimit: Math.max(...getOptionValues('antennaLimit')), }; diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index c226c4e93c..aa88a9dd13 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -37,7 +37,7 @@ import * as ep___admin_federation_updateInstance from './endpoints/admin/federat import * as ep___admin_getIndexStats from './endpoints/admin/get-index-stats.js'; import * as ep___admin_getTableStats from './endpoints/admin/get-table-stats.js'; import * as ep___admin_getUserIps from './endpoints/admin/get-user-ips.js'; -import * as ep___admin_invite from './endpoints/admin/invite.js'; +import * as ep___invite from './endpoints/invite.js'; import * as ep___admin_promo_create from './endpoints/admin/promo/create.js'; import * as ep___admin_queue_clear from './endpoints/admin/queue/clear.js'; import * as ep___admin_queue_deliverDelayed from './endpoints/admin/queue/deliver-delayed.js'; @@ -371,7 +371,7 @@ const $admin_federation_updateInstance: Provider = { provide: 'ep:admin/federati const $admin_getIndexStats: Provider = { provide: 'ep:admin/get-index-stats', useClass: ep___admin_getIndexStats.default }; const $admin_getTableStats: Provider = { provide: 'ep:admin/get-table-stats', useClass: ep___admin_getTableStats.default }; const $admin_getUserIps: Provider = { provide: 'ep:admin/get-user-ips', useClass: ep___admin_getUserIps.default }; -const $admin_invite: Provider = { provide: 'ep:admin/invite', useClass: ep___admin_invite.default }; +const $invite: Provider = { provide: 'ep:invite', useClass: ep___invite.default }; const $admin_promo_create: Provider = { provide: 'ep:admin/promo/create', useClass: ep___admin_promo_create.default }; const $admin_queue_clear: Provider = { provide: 'ep:admin/queue/clear', useClass: ep___admin_queue_clear.default }; const $admin_queue_deliverDelayed: Provider = { provide: 'ep:admin/queue/deliver-delayed', useClass: ep___admin_queue_deliverDelayed.default }; @@ -709,7 +709,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_getIndexStats, $admin_getTableStats, $admin_getUserIps, - $admin_invite, + $invite, $admin_promo_create, $admin_queue_clear, $admin_queue_deliverDelayed, @@ -1041,7 +1041,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_getIndexStats, $admin_getTableStats, $admin_getUserIps, - $admin_invite, + $invite, $admin_promo_create, $admin_queue_clear, $admin_queue_deliverDelayed, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 1df3240e41..0a26094c44 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -36,7 +36,7 @@ import * as ep___admin_federation_updateInstance from './endpoints/admin/federat import * as ep___admin_getIndexStats from './endpoints/admin/get-index-stats.js'; import * as ep___admin_getTableStats from './endpoints/admin/get-table-stats.js'; import * as ep___admin_getUserIps from './endpoints/admin/get-user-ips.js'; -import * as ep___admin_invite from './endpoints/admin/invite.js'; +import * as ep___invite from './endpoints/invite.js'; import * as ep___admin_promo_create from './endpoints/admin/promo/create.js'; import * as ep___admin_queue_clear from './endpoints/admin/queue/clear.js'; import * as ep___admin_queue_deliverDelayed from './endpoints/admin/queue/deliver-delayed.js'; @@ -368,7 +368,7 @@ const eps = [ ['admin/get-index-stats', ep___admin_getIndexStats], ['admin/get-table-stats', ep___admin_getTableStats], ['admin/get-user-ips', ep___admin_getUserIps], - ['admin/invite', ep___admin_invite], + ['invite', ep___invite], ['admin/promo/create', ep___admin_promo_create], ['admin/queue/clear', ep___admin_queue_clear], ['admin/queue/deliver-delayed', ep___admin_queue_deliverDelayed], diff --git a/packages/backend/src/server/api/endpoints/admin/invite.ts b/packages/backend/src/server/api/endpoints/admin/invite.ts deleted file mode 100644 index bc42bf792a..0000000000 --- a/packages/backend/src/server/api/endpoints/admin/invite.ts +++ /dev/null @@ -1,61 +0,0 @@ -import rndstr from 'rndstr'; -import { Inject, Injectable } from '@nestjs/common'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RegistrationTicketsRepository } from '@/models/index.js'; -import { IdService } from '@/core/IdService.js'; -import { DI } from '@/di-symbols.js'; - -export const meta = { - tags: ['admin'], - - requireCredential: true, - requireModerator: true, - - res: { - type: 'object', - optional: false, nullable: false, - properties: { - code: { - type: 'string', - optional: false, nullable: false, - example: '2ERUA5VR', - maxLength: 8, - minLength: 8, - }, - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: {}, - required: [], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint { - constructor( - @Inject(DI.registrationTicketsRepository) - private registrationTicketsRepository: RegistrationTicketsRepository, - - private idService: IdService, - ) { - super(meta, paramDef, async () => { - const code = rndstr({ - length: 8, - chars: '2-9A-HJ-NP-Z', // [0-9A-Z] w/o [01IO] (32 patterns) - }); - - await this.registrationTicketsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), - code, - }); - - return { - code, - }; - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/invite.ts b/packages/backend/src/server/api/endpoints/invite.ts new file mode 100644 index 0000000000..d22946e04a --- /dev/null +++ b/packages/backend/src/server/api/endpoints/invite.ts @@ -0,0 +1,67 @@ +import rndstr from 'rndstr'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { RegistrationTicketsRepository } from '@/models/index.js'; +import { IdService } from '@/core/IdService.js'; +import { DI } from '@/di-symbols.js'; +import { RoleService } from '@/core/RoleService.js'; + +export const meta = { + tags: ['meta'], + + requireCredential: true, + + res: { + type: 'object', + optional: false, nullable: false, + properties: { + code: { + type: 'string', + optional: false, nullable: false, + example: '2ERUA5VR', + maxLength: 8, + minLength: 8, + }, + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + @Inject(DI.registrationTicketsRepository) + private registrationTicketsRepository: RegistrationTicketsRepository, + + private roleService: RoleService, + private idService: IdService, + ) { + super(meta, paramDef, async (ps, me) => { + const role = await this.roleService.getUserRoleOptions(me.id); + if (!me.isRoot && !role.canInvite) { + throw new Error('access denied'); + } + + const code = rndstr({ + length: 8, + chars: '2-9A-HJ-NP-Z', // [0-9A-Z] w/o [01IO] (32 patterns) + }); + + await this.registrationTicketsRepository.insert({ + id: this.idService.genId(), + createdAt: new Date(), + code, + }); + + return { + code, + }; + }); + } +} diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue index 83f3c10d1d..0166960ff6 100644 --- a/packages/frontend/src/pages/admin/index.vue +++ b/packages/frontend/src/pages/admin/index.vue @@ -80,7 +80,7 @@ const menuDef = $computed(() => [{ action: lookup, }, ...(instance.disableRegistration ? [{ type: 'button', - icon: 'ti ti-user', + icon: 'ti ti-user-plus', text: i18n.ts.invite, action: invite, }] : [])], @@ -223,7 +223,7 @@ provideMetadataReceiver((info) => { }); const invite = () => { - os.api('admin/invite').then(x => { + os.api('invite').then(x => { os.alert({ type: 'info', text: x.code, diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index e1efb8dcec..1d83ae3244 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -77,6 +77,19 @@
+ + + +
+ + + + + + +
+
+ @@ -160,6 +173,8 @@ let options_ltlAvailable_useDefault = $ref(role?.options?.ltlAvailable?.useDefau let options_ltlAvailable_value = $ref(role?.options?.ltlAvailable?.value ?? false); let options_canPublicNote_useDefault = $ref(role?.options?.canPublicNote?.useDefault ?? true); let options_canPublicNote_value = $ref(role?.options?.canPublicNote?.value ?? false); +let options_canInvite_useDefault = $ref(role?.options?.canInvite?.useDefault ?? true); +let options_canInvite_value = $ref(role?.options?.canInvite?.value ?? false); let options_driveCapacityMb_useDefault = $ref(role?.options?.driveCapacityMb?.useDefault ?? true); let options_driveCapacityMb_value = $ref(role?.options?.driveCapacityMb?.value ?? 0); let options_antennaLimit_useDefault = $ref(role?.options?.antennaLimit?.useDefault ?? true); @@ -176,6 +191,7 @@ function getOptions() { gtlAvailable: { useDefault: options_gtlAvailable_useDefault, value: options_gtlAvailable_value }, ltlAvailable: { useDefault: options_ltlAvailable_useDefault, value: options_ltlAvailable_value }, canPublicNote: { useDefault: options_canPublicNote_useDefault, value: options_canPublicNote_value }, + canInvite: { useDefault: options_canInvite_useDefault, value: options_canInvite_value }, driveCapacityMb: { useDefault: options_driveCapacityMb_useDefault, value: options_driveCapacityMb_value }, antennaLimit: { useDefault: options_antennaLimit_useDefault, value: options_antennaLimit_value }, }; diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue index f74a3dcf5a..6495074bb7 100644 --- a/packages/frontend/src/pages/admin/roles.vue +++ b/packages/frontend/src/pages/admin/roles.vue @@ -32,6 +32,14 @@ + + + + + + + + @@ -81,6 +89,7 @@ const roles = await os.api('admin/roles/list'); let options_gtlAvailable = $ref(instance.baseRole.gtlAvailable); let options_ltlAvailable = $ref(instance.baseRole.ltlAvailable); let options_canPublicNote = $ref(instance.baseRole.canPublicNote); +let options_canInvite = $ref(instance.baseRole.canInvite); let options_driveCapacityMb = $ref(instance.baseRole.driveCapacityMb); let options_antennaLimit = $ref(instance.baseRole.antennaLimit); @@ -90,6 +99,7 @@ async function updateBaseRole() { gtlAvailable: options_gtlAvailable, ltlAvailable: options_ltlAvailable, canPublicNote: options_canPublicNote, + canInvite: options_canInvite, driveCapacityMb: options_driveCapacityMb, antennaLimit: options_antennaLimit, }, diff --git a/packages/frontend/src/ui/_common_/common.ts b/packages/frontend/src/ui/_common_/common.ts index c63e962a8d..079b31489e 100644 --- a/packages/frontend/src/ui/_common_/common.ts +++ b/packages/frontend/src/ui/_common_/common.ts @@ -2,6 +2,7 @@ import * as os from '@/os'; import { instance } from '@/instance'; import { host } from '@/config'; import { i18n } from '@/i18n'; +import { $i } from '@/account'; export function openInstanceMenu(ev: MouseEvent) { os.popupMenu([{ @@ -46,7 +47,23 @@ export function openInstanceMenu(ev: MouseEvent) { to: '/clicker', text: '🍪👈', icon: 'ti ti-cookie', - }], + }, ($i && ($i.isRoot || $i.role.canInvite) && instance.disableRegistration) ? { + text: i18n.ts.invite, + icon: 'ti ti-user-plus', + action: () => { + os.api('invite').then(x => { + os.alert({ + type: 'info', + text: x.code, + }); + }).catch(err => { + os.alert({ + type: 'error', + text: err, + }); + }); + }, + } : undefined], }, null, { text: i18n.ts.help, icon: 'ti ti-question-circle', -- cgit v1.2.3-freya From 81f11d8f860803cf01fbb2cfd106bd3344db98f2 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 15 Jan 2023 20:52:53 +0900 Subject: refactor: rename role.options -> role.policies --- locales/ja-JP.yml | 1 + .../backend/migration/1673783015567-Policies.js | 13 ++ packages/backend/src/core/DriveService.ts | 4 +- packages/backend/src/core/NoteCreateService.ts | 2 +- packages/backend/src/core/NotePiningService.ts | 2 +- packages/backend/src/core/RoleService.ts | 26 ++-- packages/backend/src/core/UserListService.ts | 2 +- .../backend/src/core/entities/RoleEntityService.ts | 10 +- .../backend/src/core/entities/UserEntityService.ts | 4 +- packages/backend/src/models/entities/Meta.ts | 2 +- packages/backend/src/models/entities/Role.ts | 2 +- .../backend/src/server/NodeinfoServerService.ts | 8 +- packages/backend/src/server/api/ApiCallService.ts | 8 +- packages/backend/src/server/api/EndpointsModule.ts | 8 +- packages/backend/src/server/api/endpoints.ts | 6 +- .../api/endpoints/admin/emoji/add-aliases-bulk.ts | 2 +- .../src/server/api/endpoints/admin/emoji/add.ts | 2 +- .../src/server/api/endpoints/admin/emoji/copy.ts | 2 +- .../api/endpoints/admin/emoji/delete-bulk.ts | 2 +- .../src/server/api/endpoints/admin/emoji/delete.ts | 2 +- .../server/api/endpoints/admin/emoji/import-zip.ts | 2 +- .../api/endpoints/admin/emoji/list-remote.ts | 2 +- .../src/server/api/endpoints/admin/emoji/list.ts | 2 +- .../endpoints/admin/emoji/remove-aliases-bulk.ts | 2 +- .../api/endpoints/admin/emoji/set-aliases-bulk.ts | 2 +- .../api/endpoints/admin/emoji/set-category-bulk.ts | 2 +- .../src/server/api/endpoints/admin/emoji/update.ts | 2 +- .../backend/src/server/api/endpoints/admin/meta.ts | 4 +- .../src/server/api/endpoints/admin/roles/create.ts | 6 +- .../admin/roles/update-default-policies.ts | 42 ++++++ .../admin/roles/update-default-role-override.ts | 42 ------ .../src/server/api/endpoints/admin/roles/update.ts | 6 +- .../src/server/api/endpoints/admin/show-user.ts | 3 +- .../src/server/api/endpoints/antennas/create.ts | 2 +- .../src/server/api/endpoints/clips/add-note.ts | 2 +- .../src/server/api/endpoints/clips/create.ts | 2 +- packages/backend/src/server/api/endpoints/drive.ts | 4 +- .../backend/src/server/api/endpoints/i/update.ts | 2 +- .../src/server/api/endpoints/i/webhooks/create.ts | 2 +- .../backend/src/server/api/endpoints/invite.ts | 2 +- packages/backend/src/server/api/endpoints/meta.ts | 4 +- .../server/api/endpoints/notes/global-timeline.ts | 4 +- .../server/api/endpoints/notes/hybrid-timeline.ts | 4 +- .../server/api/endpoints/notes/local-timeline.ts | 4 +- .../src/server/api/endpoints/users/lists/create.ts | 2 +- .../server/api/stream/channels/global-timeline.ts | 4 +- .../server/api/stream/channels/hybrid-timeline.ts | 4 +- .../server/api/stream/channels/local-timeline.ts | 4 +- packages/backend/src/server/api/stream/types.ts | 2 +- packages/frontend/src/pages/admin/roles.editor.vue | 142 ++++++++++----------- packages/frontend/src/pages/admin/roles.vue | 119 ++++++++--------- packages/frontend/src/pages/timeline.vue | 4 +- packages/frontend/src/pages/user-info.vue | 20 ++- packages/frontend/src/ui/_common_/common.ts | 4 +- 54 files changed, 292 insertions(+), 270 deletions(-) create mode 100644 packages/backend/migration/1673783015567-Policies.js create mode 100644 packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts delete mode 100644 packages/backend/src/server/api/endpoints/admin/roles/update-default-role-override.ts (limited to 'packages/backend/src/server/api/EndpointsModule.ts') diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 79856447a0..786d41ca41 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -952,6 +952,7 @@ _role: isPublic: "ロールを公開" descriptionOfIsPublic: "ロールにアサインされたユーザーを誰でも見ることができます。また、ユーザーのプロフィールでこのロールが表示されます。" options: "オプション" + policies: "ポリシー" baseRole: "ベースロール" useBaseValue: "ベースロールの値を使用" chooseRoleToAssign: "アサインするロールを選択" diff --git a/packages/backend/migration/1673783015567-Policies.js b/packages/backend/migration/1673783015567-Policies.js new file mode 100644 index 0000000000..8b36921d41 --- /dev/null +++ b/packages/backend/migration/1673783015567-Policies.js @@ -0,0 +1,13 @@ +export class Policies1673783015567 { + name = 'Policies1673783015567' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "role" RENAME COLUMN "options" TO "policies"`); + await queryRunner.query(`ALTER TABLE "meta" RENAME COLUMN "defaultRoleOverride" TO "policies"`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" RENAME COLUMN "policies" TO "defaultRoleOverride"`); + await queryRunner.query(`ALTER TABLE "role" RENAME COLUMN "policies" TO "options"`); + } +} diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index 9002c96a65..598a457e83 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -479,8 +479,8 @@ export class DriveService { if (user && !isLink) { const usage = await this.driveFileEntityService.calcDriveUsageOf(user); - const role = await this.roleService.getUserRoleOptions(user.id); - const driveCapacity = 1024 * 1024 * role.driveCapacityMb; + const policies = await this.roleService.getUserPolicies(user.id); + const driveCapacity = 1024 * 1024 * policies.driveCapacityMb; this.registerLogger.debug('drive capacity override applied'); this.registerLogger.debug(`overrideCap: ${driveCapacity}bytes, usage: ${usage}bytes, u+s: ${usage + info.size}bytes`); diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 112b84fdf9..3dc44a25fe 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -226,7 +226,7 @@ export class NoteCreateService { if (data.channel != null) data.localOnly = true; if (data.visibility === 'public' && data.channel == null) { - if ((await this.roleService.getUserRoleOptions(user.id)).canPublicNote === false) { + if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) { data.visibility = 'home'; } } diff --git a/packages/backend/src/core/NotePiningService.ts b/packages/backend/src/core/NotePiningService.ts index bc038e17a7..bb6def1edb 100644 --- a/packages/backend/src/core/NotePiningService.ts +++ b/packages/backend/src/core/NotePiningService.ts @@ -57,7 +57,7 @@ export class NotePiningService { const pinings = await this.userNotePiningsRepository.findBy({ userId: user.id }); - if (pinings.length >= (await this.roleService.getUserRoleOptions(user.id)).pinLimit) { + if (pinings.length >= (await this.roleService.getUserPolicies(user.id)).pinLimit) { throw new IdentifiableError('15a018eb-58e5-4da1-93be-330fcc5e4e1a', 'You can not pin notes any more.'); } diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index 42b477d9ed..abad058303 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -13,7 +13,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { StreamMessages } from '@/server/api/stream/types.js'; import type { OnApplicationShutdown } from '@nestjs/common'; -export type RoleOptions = { +export type RolePolicies = { gtlAvailable: boolean; ltlAvailable: boolean; canPublicNote: boolean; @@ -31,7 +31,7 @@ export type RoleOptions = { rateLimitFactor: number; }; -export const DEFAULT_ROLE: RoleOptions = { +export const DEFAULT_POLICIES: RolePolicies = { gtlAvailable: true, ltlAvailable: true, canPublicNote: true, @@ -195,26 +195,26 @@ export class RoleService implements OnApplicationShutdown { } @bindThis - public async getUserRoleOptions(userId: User['id'] | null): Promise { + public async getUserPolicies(userId: User['id'] | null): Promise { const meta = await this.metaService.fetch(); - const baseRoleOptions = { ...DEFAULT_ROLE, ...meta.defaultRoleOverride }; + const basePolicies = { ...DEFAULT_POLICIES, ...meta.policies }; - if (userId == null) return baseRoleOptions; + if (userId == null) return basePolicies; const roles = await this.getUserRoles(userId); - function calc(name: T, aggregate: (values: RoleOptions[T][]) => RoleOptions[T]) { - if (roles.length === 0) return baseRoleOptions[name]; + function calc(name: T, aggregate: (values: RolePolicies[T][]) => RolePolicies[T]) { + if (roles.length === 0) return basePolicies[name]; - const options = roles.map(role => role.options[name] ?? { priority: 0, useDefault: true }); + const policies = roles.map(role => role.policies[name] ?? { priority: 0, useDefault: true }); - const p2 = options.filter(option => option.priority === 2); - if (p2.length > 0) return aggregate(p2.map(option => option.useDefault ? baseRoleOptions[name] : option.value)); + const p2 = policies.filter(policy => policy.priority === 2); + if (p2.length > 0) return aggregate(p2.map(policy => policy.useDefault ? basePolicies[name] : policy.value)); - const p1 = options.filter(option => option.priority === 1); - if (p1.length > 0) return aggregate(p2.map(option => option.useDefault ? baseRoleOptions[name] : option.value)); + const p1 = policies.filter(policy => policy.priority === 1); + if (p1.length > 0) return aggregate(p2.map(policy => policy.useDefault ? basePolicies[name] : policy.value)); - return aggregate(options.map(option => option.useDefault ? baseRoleOptions[name] : option.value)); + return aggregate(policies.map(policy => policy.useDefault ? basePolicies[name] : policy.value)); } return { diff --git a/packages/backend/src/core/UserListService.ts b/packages/backend/src/core/UserListService.ts index 18c9787fa8..fc48738307 100644 --- a/packages/backend/src/core/UserListService.ts +++ b/packages/backend/src/core/UserListService.ts @@ -35,7 +35,7 @@ export class UserListService { const currentCount = await this.userListJoiningsRepository.countBy({ userListId: list.id, }); - if (currentCount > (await this.roleService.getUserRoleOptions(me.id)).userEachUserListsLimit) { + if (currentCount > (await this.roleService.getUserPolicies(me.id)).userEachUserListsLimit) { throw new Error('Too many users'); } diff --git a/packages/backend/src/core/entities/RoleEntityService.ts b/packages/backend/src/core/entities/RoleEntityService.ts index 6a14775653..52f3374468 100644 --- a/packages/backend/src/core/entities/RoleEntityService.ts +++ b/packages/backend/src/core/entities/RoleEntityService.ts @@ -6,7 +6,7 @@ import type { Packed } from '@/misc/schema.js'; import type { User } from '@/models/entities/User.js'; import type { Role } from '@/models/entities/Role.js'; import { bindThis } from '@/decorators.js'; -import { DEFAULT_ROLE } from '@/core/RoleService.js'; +import { DEFAULT_POLICIES } from '@/core/RoleService.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() @@ -40,9 +40,9 @@ export class RoleEntityService { roleId: role.id, }); - const roleOptions = { ...role.options }; - for (const [k, v] of Object.entries(DEFAULT_ROLE)) { - if (roleOptions[k] == null) roleOptions[k] = { + const policies = { ...role.policies }; + for (const [k, v] of Object.entries(DEFAULT_POLICIES)) { + if (policies[k] == null) policies[k] = { useDefault: true, priority: 0, value: v, @@ -62,7 +62,7 @@ export class RoleEntityService { isAdministrator: role.isAdministrator, isModerator: role.isModerator, canEditMembersByModerator: role.canEditMembersByModerator, - options: roleOptions, + policies: policies, usersCount: assigns.length, ...(opts.detail ? { users: this.userEntityService.packMany(assigns.map(x => x.userId), me), diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 6b754150cf..bf6f6f4553 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -423,7 +423,7 @@ export class UserEntityService implements OnModuleInit { bannerUrl: user.banner ? this.driveFileEntityService.getPublicUrl(user.banner, false) : null, bannerBlurhash: user.banner?.blurhash ?? null, isLocked: user.isLocked, - isSilenced: this.roleService.getUserRoleOptions(user.id).then(r => !r.canPublicNote), + isSilenced: this.roleService.getUserPolicies(user.id).then(r => !r.canPublicNote), isSuspended: user.isSuspended ?? falsy, description: profile!.description, location: profile!.location, @@ -496,7 +496,7 @@ export class UserEntityService implements OnModuleInit { } : {}), ...(opts.includeSecrets ? { - role: this.roleService.getUserRoleOptions(user.id), + policies: this.roleService.getUserPolicies(user.id), email: profile!.email, emailVerified: profile!.emailVerified, securityKeysList: profile!.twoFactorEnabled diff --git a/packages/backend/src/models/entities/Meta.ts b/packages/backend/src/models/entities/Meta.ts index e724ba9a49..5d222a6da1 100644 --- a/packages/backend/src/models/entities/Meta.ts +++ b/packages/backend/src/models/entities/Meta.ts @@ -458,5 +458,5 @@ export class Meta { @Column('jsonb', { default: { }, }) - public defaultRoleOverride: Record; + public policies: Record; } diff --git a/packages/backend/src/models/entities/Role.ts b/packages/backend/src/models/entities/Role.ts index d8d203493b..abd5f864a2 100644 --- a/packages/backend/src/models/entities/Role.ts +++ b/packages/backend/src/models/entities/Role.ts @@ -136,7 +136,7 @@ export class Role { @Column('jsonb', { default: { }, }) - public options: Record null) : null; - const baseRoleOptions = { ...DEFAULT_ROLE, ...meta.defaultRoleOverride }; + const basePolicies = { ...DEFAULT_POLICIES, ...meta.policies }; return { software: { @@ -105,8 +105,8 @@ export class NodeinfoServerService { repositoryUrl: meta.repositoryUrl, feedbackUrl: meta.feedbackUrl, disableRegistration: meta.disableRegistration, - disableLocalTimeline: !baseRoleOptions.ltlAvailable, - disableGlobalTimeline: !baseRoleOptions.gtlAvailable, + disableLocalTimeline: !basePolicies.ltlAvailable, + disableGlobalTimeline: !basePolicies.gtlAvailable, emailRequiredForSignup: meta.emailRequiredForSignup, enableHcaptcha: meta.enableHcaptcha, enableRecaptcha: meta.enableRecaptcha, diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts index dcc9342a82..395a1c468a 100644 --- a/packages/backend/src/server/api/ApiCallService.ts +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -225,7 +225,7 @@ export class ApiCallService implements OnApplicationShutdown { } // TODO: 毎リクエスト計算するのもあれだしキャッシュしたい - const factor = user ? (await this.roleService.getUserRoleOptions(user.id)).rateLimitFactor : 1; + const factor = user ? (await this.roleService.getUserPolicies(user.id)).rateLimitFactor : 1; // Rate limit await this.rateLimiterService.limit(limit as IEndpointMeta['limit'] & { key: NonNullable }, limitActor, factor).catch(err => { @@ -274,9 +274,9 @@ export class ApiCallService implements OnApplicationShutdown { } } - if (ep.meta.requireRoleOption != null && !user!.isRoot) { - const myRole = await this.roleService.getUserRoleOptions(user!.id); - if (!myRole[ep.meta.requireRoleOption]) { + if (ep.meta.requireRolePolicy != null && !user!.isRoot) { + const policies = await this.roleService.getUserPolicies(user!.id); + if (!policies[ep.meta.requireRolePolicy]) { throw new ApiError({ message: 'You are not assigned to a required role.', code: 'ROLE_PERMISSION_DENIED', diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index aa88a9dd13..14927da7d6 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -65,7 +65,7 @@ import * as ep___admin_roles_show from './endpoints/admin/roles/show.js'; import * as ep___admin_roles_update from './endpoints/admin/roles/update.js'; import * as ep___admin_roles_assign from './endpoints/admin/roles/assign.js'; import * as ep___admin_roles_unassign from './endpoints/admin/roles/unassign.js'; -import * as ep___admin_roles_updateDefaultRoleOverride from './endpoints/admin/roles/update-default-role-override.js'; +import * as ep___admin_roles_updateDefaultPolicies from './endpoints/admin/roles/update-default-policies.js'; import * as ep___announcements from './endpoints/announcements.js'; import * as ep___antennas_create from './endpoints/antennas/create.js'; import * as ep___antennas_delete from './endpoints/antennas/delete.js'; @@ -399,7 +399,7 @@ const $admin_roles_show: Provider = { provide: 'ep:admin/roles/show', useClass: const $admin_roles_update: Provider = { provide: 'ep:admin/roles/update', useClass: ep___admin_roles_update.default }; const $admin_roles_assign: Provider = { provide: 'ep:admin/roles/assign', useClass: ep___admin_roles_assign.default }; const $admin_roles_unassign: Provider = { provide: 'ep:admin/roles/unassign', useClass: ep___admin_roles_unassign.default }; -const $admin_roles_updateDefaultRoleOverride: Provider = { provide: 'ep:admin/roles/update-default-role-override', useClass: ep___admin_roles_updateDefaultRoleOverride.default }; +const $admin_roles_updateDefaultPolicies: Provider = { provide: 'ep:admin/roles/update-default-policies', useClass: ep___admin_roles_updateDefaultPolicies.default }; const $announcements: Provider = { provide: 'ep:announcements', useClass: ep___announcements.default }; const $antennas_create: Provider = { provide: 'ep:antennas/create', useClass: ep___antennas_create.default }; const $antennas_delete: Provider = { provide: 'ep:antennas/delete', useClass: ep___antennas_delete.default }; @@ -737,7 +737,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_roles_update, $admin_roles_assign, $admin_roles_unassign, - $admin_roles_updateDefaultRoleOverride, + $admin_roles_updateDefaultPolicies, $announcements, $antennas_create, $antennas_delete, @@ -1069,7 +1069,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_roles_update, $admin_roles_assign, $admin_roles_unassign, - $admin_roles_updateDefaultRoleOverride, + $admin_roles_updateDefaultPolicies, $announcements, $antennas_create, $antennas_delete, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index f50a3b5dd2..54c4206ea4 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -64,7 +64,7 @@ import * as ep___admin_roles_show from './endpoints/admin/roles/show.js'; import * as ep___admin_roles_update from './endpoints/admin/roles/update.js'; import * as ep___admin_roles_assign from './endpoints/admin/roles/assign.js'; import * as ep___admin_roles_unassign from './endpoints/admin/roles/unassign.js'; -import * as ep___admin_roles_updateDefaultRoleOverride from './endpoints/admin/roles/update-default-role-override.js'; +import * as ep___admin_roles_updateDefaultPolicies from './endpoints/admin/roles/update-default-policies.js'; import * as ep___announcements from './endpoints/announcements.js'; import * as ep___antennas_create from './endpoints/antennas/create.js'; import * as ep___antennas_delete from './endpoints/antennas/delete.js'; @@ -396,7 +396,7 @@ const eps = [ ['admin/roles/update', ep___admin_roles_update], ['admin/roles/assign', ep___admin_roles_assign], ['admin/roles/unassign', ep___admin_roles_unassign], - ['admin/roles/update-default-role-override', ep___admin_roles_updateDefaultRoleOverride], + ['admin/roles/update-default-policies', ep___admin_roles_updateDefaultPolicies], ['announcements', ep___announcements], ['antennas/create', ep___antennas_create], ['antennas/delete', ep___antennas_delete], @@ -695,7 +695,7 @@ export interface IEndpointMeta { */ readonly requireAdmin?: boolean; - readonly requireRoleOption?: string; + readonly requireRolePolicy?: string; /** * エンドポイントのリミテーションに関するやつ diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts index d114fd3d55..9b6c774f0c 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts @@ -8,7 +8,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index 52ccb74447..abca1d169d 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -14,7 +14,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', errors: { noSuchFile: { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index 4d1fdd989d..b4fc7fd6f5 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -14,7 +14,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', errors: { noSuchEmoji: { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts index 27aa4fb1b1..ae45105b28 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts @@ -9,7 +9,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts index 2531246569..e237d87d34 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts @@ -10,7 +10,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', errors: { noSuchEmoji: { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts index 15f468c180..b4a07324bb 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts @@ -5,7 +5,7 @@ import { QueueService } from '@/core/QueueService.js'; export const meta = { secure: true, requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', } as const; export const paramDef = { 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 131c9ef223..d9ce97194a 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 @@ -11,7 +11,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', res: { type: 'array', 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 ef2bc936c3..1a6096f36f 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts @@ -11,7 +11,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts index a70cd8d787..5fc9e024bf 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts @@ -8,7 +8,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts index b33e5662bb..8b5ba8fbf4 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts @@ -8,7 +8,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts index 05834bc572..827b5ace7a 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts @@ -8,7 +8,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index 19645cb515..fb0ef12878 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -9,7 +9,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', errors: { noSuchEmoji: { diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index fd08a5f847..b393827054 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -4,7 +4,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { MetaService } from '@/core/MetaService.js'; import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; -import { DEFAULT_ROLE } from '@/core/RoleService.js'; +import { DEFAULT_POLICIES } from '@/core/RoleService.js'; export const meta = { tags: ['meta'], @@ -440,7 +440,7 @@ export default class extends Endpoint { deeplIsPro: instance.deeplIsPro, enableIpLogging: instance.enableIpLogging, enableActiveEmailValidation: instance.enableActiveEmailValidation, - baseRole: { ...DEFAULT_ROLE, ...instance.defaultRoleOverride }, + policies: { ...DEFAULT_POLICIES, ...instance.policies }, }; }); } diff --git a/packages/backend/src/server/api/endpoints/admin/roles/create.ts b/packages/backend/src/server/api/endpoints/admin/roles/create.ts index a9216a6386..f136c6d624 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/create.ts @@ -25,7 +25,7 @@ export const paramDef = { isModerator: { type: 'boolean' }, isAdministrator: { type: 'boolean' }, canEditMembersByModerator: { type: 'boolean' }, - options: { + policies: { type: 'object', }, }, @@ -39,7 +39,7 @@ export const paramDef = { 'isModerator', 'isAdministrator', 'canEditMembersByModerator', - 'options', + 'policies', ], } as const; @@ -70,7 +70,7 @@ export default class extends Endpoint { isAdministrator: ps.isAdministrator, isModerator: ps.isModerator, canEditMembersByModerator: ps.canEditMembersByModerator, - options: ps.options, + policies: ps.policies, }).then(x => this.rolesRepository.findOneByOrFail(x.identifiers[0])); this.globalEventService.publishInternalEvent('roleCreated', created); diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts b/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts new file mode 100644 index 0000000000..6006816bcb --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts @@ -0,0 +1,42 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { RolesRepository } from '@/models/index.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '@/server/api/error.js'; +import { MetaService } from '@/core/MetaService.js'; + +export const meta = { + tags: ['admin', 'role'], + + requireCredential: true, + requireAdmin: true, +} as const; + +export const paramDef = { + type: 'object', + properties: { + policies: { + type: 'object', + }, + }, + required: [ + 'policies', + ], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + private metaService: MetaService, + private globalEventService: GlobalEventService, + ) { + super(meta, paramDef, async (ps) => { + await this.metaService.update({ + policies: ps.policies, + }); + this.globalEventService.publishInternalEvent('policiesUpdated', ps.policies); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update-default-role-override.ts b/packages/backend/src/server/api/endpoints/admin/roles/update-default-role-override.ts deleted file mode 100644 index 35da04efd2..0000000000 --- a/packages/backend/src/server/api/endpoints/admin/roles/update-default-role-override.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RolesRepository } from '@/models/index.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; -import { DI } from '@/di-symbols.js'; -import { ApiError } from '@/server/api/error.js'; -import { MetaService } from '@/core/MetaService.js'; - -export const meta = { - tags: ['admin', 'role'], - - requireCredential: true, - requireAdmin: true, -} as const; - -export const paramDef = { - type: 'object', - properties: { - options: { - type: 'object', - }, - }, - required: [ - 'options', - ], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint { - constructor( - private metaService: MetaService, - private globalEventService: GlobalEventService, - ) { - super(meta, paramDef, async (ps) => { - await this.metaService.update({ - defaultRoleOverride: ps.options, - }); - this.globalEventService.publishInternalEvent('defaultRoleOverrideUpdated', ps.options); - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update.ts b/packages/backend/src/server/api/endpoints/admin/roles/update.ts index 4ca5124eda..fc4c3d8f11 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/update.ts @@ -33,7 +33,7 @@ export const paramDef = { isModerator: { type: 'boolean' }, isAdministrator: { type: 'boolean' }, canEditMembersByModerator: { type: 'boolean' }, - options: { + policies: { type: 'object', }, }, @@ -48,7 +48,7 @@ export const paramDef = { 'isModerator', 'isAdministrator', 'canEditMembersByModerator', - 'options', + 'policies', ], } as const; @@ -79,7 +79,7 @@ export default class extends Endpoint { isModerator: ps.isModerator, isAdministrator: ps.isAdministrator, canEditMembersByModerator: ps.canEditMembersByModerator, - options: ps.options, + policies: ps.policies, }); const updated = await this.rolesRepository.findOneByOrFail({ id: ps.roleId }); this.globalEventService.publishInternalEvent('roleUpdated', updated); diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index 3f4ec299af..94603cc91a 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -52,7 +52,7 @@ export default class extends Endpoint { } const isModerator = await this.roleService.isModerator(user); - const isSilenced = !(await this.roleService.getUserRoleOptions(user.id)).canPublicNote; + const isSilenced = !(await this.roleService.getUserPolicies(user.id)).canPublicNote; const _me = await this.usersRepository.findOneByOrFail({ id: me.id }); if (!await this.roleService.isAdministrator(_me) && await this.roleService.isAdministrator(user)) { @@ -94,6 +94,7 @@ export default class extends Endpoint { lastActiveDate: user.lastActiveDate, moderationNote: profile.moderationNote, signins, + policies: await this.roleService.getUserPolicies(user.id), roles: await this.roleEntityService.packMany(roles, me, { detail: false }), }; }); diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index 08625250c8..a1553b6a80 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -92,7 +92,7 @@ export default class extends Endpoint { const currentAntennasCount = await this.antennasRepository.countBy({ userId: me.id, }); - if (currentAntennasCount > (await this.roleService.getUserRoleOptions(me.id)).antennaLimit) { + if (currentAntennasCount > (await this.roleService.getUserPolicies(me.id)).antennaLimit) { throw new ApiError(meta.errors.tooManyAntennas); } diff --git a/packages/backend/src/server/api/endpoints/clips/add-note.ts b/packages/backend/src/server/api/endpoints/clips/add-note.ts index 3cf096c242..f3f9c3477f 100644 --- a/packages/backend/src/server/api/endpoints/clips/add-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/add-note.ts @@ -97,7 +97,7 @@ export default class extends Endpoint { const currentCount = await this.clipNotesRepository.countBy({ clipId: clip.id, }); - if (currentCount > (await this.roleService.getUserRoleOptions(me.id)).noteEachClipsLimit) { + if (currentCount > (await this.roleService.getUserPolicies(me.id)).noteEachClipsLimit) { throw new ApiError(meta.errors.tooManyClipNotes); } diff --git a/packages/backend/src/server/api/endpoints/clips/create.ts b/packages/backend/src/server/api/endpoints/clips/create.ts index abc0288c89..c095de702c 100644 --- a/packages/backend/src/server/api/endpoints/clips/create.ts +++ b/packages/backend/src/server/api/endpoints/clips/create.ts @@ -54,7 +54,7 @@ export default class extends Endpoint { const currentCount = await this.clipsRepository.countBy({ userId: me.id, }); - if (currentCount > (await this.roleService.getUserRoleOptions(me.id)).clipLimit) { + if (currentCount > (await this.roleService.getUserPolicies(me.id)).clipLimit) { throw new ApiError(meta.errors.tooManyClips); } diff --git a/packages/backend/src/server/api/endpoints/drive.ts b/packages/backend/src/server/api/endpoints/drive.ts index 2a06792dcf..e5bbfecbcf 100644 --- a/packages/backend/src/server/api/endpoints/drive.ts +++ b/packages/backend/src/server/api/endpoints/drive.ts @@ -47,10 +47,10 @@ export default class extends Endpoint { // Calculate drive usage const usage = await this.driveFileEntityService.calcDriveUsageOf(me.id); - const myRole = await this.roleService.getUserRoleOptions(me.id); + const policies = await this.roleService.getUserPolicies(me.id); return { - capacity: 1024 * 1024 * myRole.driveCapacityMb, + capacity: 1024 * 1024 * policies.driveCapacityMb, usage: usage, }; }); diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index fe09eca674..b1eaab3908 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -173,7 +173,7 @@ export default class extends Endpoint { if (ps.mutedWords !== undefined) { // TODO: ちゃんと数える const length = JSON.stringify(ps.mutedWords).length; - if (length > (await this.roleService.getUserRoleOptions(user.id)).wordMuteLimit) { + if (length > (await this.roleService.getUserPolicies(user.id)).wordMuteLimit) { throw new ApiError(meta.errors.tooManyMutedWords); } diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts index 3d89b77a7b..51fcce6cf0 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts @@ -54,7 +54,7 @@ export default class extends Endpoint { const currentWebhooksCount = await this.webhooksRepository.countBy({ userId: me.id, }); - if (currentWebhooksCount > (await this.roleService.getUserRoleOptions(me.id)).webhookLimit) { + if (currentWebhooksCount > (await this.roleService.getUserPolicies(me.id)).webhookLimit) { throw new ApiError(meta.errors.tooManyWebhooks); } diff --git a/packages/backend/src/server/api/endpoints/invite.ts b/packages/backend/src/server/api/endpoints/invite.ts index 9b03cf4bb6..5d2c479e79 100644 --- a/packages/backend/src/server/api/endpoints/invite.ts +++ b/packages/backend/src/server/api/endpoints/invite.ts @@ -9,7 +9,7 @@ export const meta = { tags: ['meta'], requireCredential: true, - requireRoleOption: 'canInvite', + requireRolePolicy: 'canInvite', res: { type: 'object', diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index f46a32dfe7..89fa503173 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -7,7 +7,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { MetaService } from '@/core/MetaService.js'; import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; -import { DEFAULT_ROLE } from '@/core/RoleService.js'; +import { DEFAULT_POLICIES } from '@/core/RoleService.js'; export const meta = { tags: ['meta'], @@ -334,7 +334,7 @@ export default class extends Endpoint { translatorAvailable: instance.deeplAuthKey != null, - baseRole: { ...DEFAULT_ROLE, ...instance.defaultRoleOverride }, + policies: { ...DEFAULT_POLICIES, ...instance.policies }, ...(ps.detail ? { pinnedPages: instance.pinnedPages, diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index 081563493d..5d0cdc3fca 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -62,8 +62,8 @@ export default class extends Endpoint { private activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { - const role = await this.roleService.getUserRoleOptions(me ? me.id : null); - if (!role.gtlAvailable) { + const policies = await this.roleService.getUserPolicies(me ? me.id : null); + if (!policies.gtlAvailable) { throw new ApiError(meta.errors.gtlDisabled); } diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index b2c504448e..2819abb125 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -71,8 +71,8 @@ export default class extends Endpoint { private activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { - const role = await this.roleService.getUserRoleOptions(me.id); - if (!role.ltlAvailable) { + const policies = await this.roleService.getUserPolicies(me.id); + if (!policies.ltlAvailable) { throw new ApiError(meta.errors.stlDisabled); } diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 6361edc310..f396f7e584 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -67,8 +67,8 @@ export default class extends Endpoint { private activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { - const role = await this.roleService.getUserRoleOptions(me ? me.id : null); - if (!role.ltlAvailable) { + const policies = await this.roleService.getUserPolicies(me ? me.id : null); + if (!policies.ltlAvailable) { throw new ApiError(meta.errors.ltlDisabled); } diff --git a/packages/backend/src/server/api/endpoints/users/lists/create.ts b/packages/backend/src/server/api/endpoints/users/lists/create.ts index 22e5e3ce78..a840c1a04e 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/create.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/create.ts @@ -55,7 +55,7 @@ export default class extends Endpoint { const currentCount = await this.userListsRepository.countBy({ userId: me.id, }); - if (currentCount > (await this.roleService.getUserRoleOptions(me.id)).userListLimit) { + if (currentCount > (await this.roleService.getUserPolicies(me.id)).userListLimit) { throw new ApiError(meta.errors.tooManyUserLists); } diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index 185c813869..43d8907fc9 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -29,8 +29,8 @@ class GlobalTimelineChannel extends Channel { @bindThis public async init(params: any) { - const role = await this.roleService.getUserRoleOptions(this.user ? this.user.id : null); - if (!role.gtlAvailable) return; + const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); + if (!policies.gtlAvailable) return; // Subscribe events this.subscriber.on('notesStream', this.onNote); diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index a0f75f202c..340f677815 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -30,8 +30,8 @@ class HybridTimelineChannel extends Channel { @bindThis public async init(params: any): Promise { - const role = await this.roleService.getUserRoleOptions(this.user ? this.user.id : null); - if (!role.ltlAvailable) return; + const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); + if (!policies.ltlAvailable) return; // Subscribe events this.subscriber.on('notesStream', this.onNote); diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index 7d76f42fe7..ea29e30d63 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -28,8 +28,8 @@ class LocalTimelineChannel extends Channel { @bindThis public async init(params: any) { - const role = await this.roleService.getUserRoleOptions(this.user ? this.user.id : null); - if (!role.ltlAvailable) return; + const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); + if (!policies.ltlAvailable) return; // Subscribe events this.subscriber.on('notesStream', this.onNote); diff --git a/packages/backend/src/server/api/stream/types.ts b/packages/backend/src/server/api/stream/types.ts index 03837baefb..a442529bb3 100644 --- a/packages/backend/src/server/api/stream/types.ts +++ b/packages/backend/src/server/api/stream/types.ts @@ -30,7 +30,7 @@ export interface InternalStreamTypes { remoteUserUpdated: Serialized<{ id: User['id']; }>; follow: Serialized<{ followerId: User['id']; followeeId: User['id']; }>; unfollow: Serialized<{ followerId: User['id']; followeeId: User['id']; }>; - defaultRoleOverrideUpdated: Serialized; + policiesUpdated: Serialized; roleCreated: Serialized; roleDeleted: Serialized; roleUpdated: Serialized; diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index 0f67cec0b2..d0be0a8c39 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -36,20 +36,20 @@ - +
- +
- + - + - +
@@ -57,15 +57,15 @@ - +
- + - + - +
@@ -73,15 +73,15 @@ - +
- + - + - +
@@ -89,15 +89,15 @@ - +
- + - + - +
@@ -105,15 +105,15 @@ - +
- + - + - +
@@ -121,15 +121,15 @@ - +
- + - + - +
@@ -137,15 +137,15 @@ - +
- + - + - +
@@ -153,14 +153,14 @@ - +
- + - + - +
@@ -168,14 +168,14 @@ - +
- + - + - +
@@ -183,15 +183,15 @@ - +
- + - + - +
@@ -199,14 +199,14 @@ - +
- + - + - +
@@ -214,14 +214,14 @@ - +
- + - + - +
@@ -229,14 +229,14 @@ - +
- + - + - +
@@ -244,14 +244,14 @@ - +
- + - + - +
@@ -259,14 +259,14 @@ - +
- + - + - +
@@ -306,7 +306,7 @@ import * as os from '@/os'; import { i18n } from '@/i18n'; import { instance } from '@/instance'; -const ROLE_OPTIONS = [ +const ROLE_POLICIES = [ 'gtlAvailable', 'ltlAvailable', 'canPublicNote', @@ -345,13 +345,13 @@ let condFormula = $ref(role?.condFormula ?? { id: uuid(), type: 'isRemote' }); let isPublic = $ref(role?.isPublic ?? false); let canEditMembersByModerator = $ref(role?.canEditMembersByModerator ?? false); -const options = reactive>({}); -for (const ROLE_OPTION of ROLE_OPTIONS) { - const _options = role?.options ?? {}; - options[ROLE_OPTION] = { - useDefault: _options[ROLE_OPTION]?.useDefault ?? true, - priority: _options[ROLE_OPTION]?.priority ?? 0, - value: _options[ROLE_OPTION]?.value ?? instance.baseRole[ROLE_OPTION], +const policies = reactive>({}); +for (const ROLE_POLICY of ROLE_POLICIES) { + const _policies = role?.policies ?? {}; + policies[ROLE_POLICY] = { + useDefault: _policies[ROLE_POLICY]?.useDefault ?? true, + priority: _policies[ROLE_POLICY]?.priority ?? 0, + value: _policies[ROLE_POLICY]?.value ?? instance.policies[ROLE_POLICY], }; } @@ -381,7 +381,7 @@ async function save() { isModerator: rolePermission === 'moderator', isPublic, canEditMembersByModerator, - options, + policies, }); emit('updated'); } else { @@ -395,7 +395,7 @@ async function save() { isModerator: rolePermission === 'moderator', isPublic, canEditMembersByModerator, - options, + policies, }); emit('created', created); } diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue index 1ceead8b6c..f074069df0 100644 --- a/packages/frontend/src/pages/admin/roles.vue +++ b/packages/frontend/src/pages/admin/roles.vue @@ -10,114 +10,114 @@
- - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + @@ -134,7 +134,7 @@