summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlqvp <183242690+lqvp@users.noreply.github.com>2025-02-11 10:15:33 +0900
committerGitHub <noreply@github.com>2025-02-11 01:15:33 +0000
commite3392936736c16000b837f1549943d2f241e01b2 (patch)
tree6b2c743e5982e990ce6ec832706f95642eccb6dd
parentfix(frontend): コンディショナルロールを手動で割り当てで... (diff)
downloadsharkey-e3392936736c16000b837f1549943d2f241e01b2.tar.gz
sharkey-e3392936736c16000b837f1549943d2f241e01b2.tar.bz2
sharkey-e3392936736c16000b837f1549943d2f241e01b2.zip
feat: アクセストークン発行時に通知するように (#15422)
* feat: アクセストークン発行時に通知するように (misskey-dev/misskey#13353) * fix: 不要な翻訳を削除/インデントを揃えるように * chore(backend): 不要なawaitを削除 * chore: changelogへ追加
-rw-r--r--CHANGELOG.md2
-rw-r--r--locales/index.d.ts8
-rw-r--r--locales/ja-JP.yml2
-rw-r--r--packages/backend/src/models/Notification.ts4
-rw-r--r--packages/backend/src/models/json-schema/notification.ts10
-rw-r--r--packages/backend/src/server/api/endpoints/miauth/gen-token.ts5
-rw-r--r--packages/backend/src/types.ts2
-rw-r--r--packages/frontend-shared/js/const.ts1
-rw-r--r--packages/frontend/src/components/MkNotification.vue14
-rw-r--r--packages/frontend/src/pages/settings/notifications.vue2
-rw-r--r--packages/misskey-js/src/autogen/types.ts15
11 files changed, 58 insertions, 7 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 39f948597d..bdef319c2d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,7 @@
## Unreleased
### General
--
+- Feat: アクセストークン発行時に通知するように
### Client
- Enhance: 投稿フォームの「迷惑になる可能性があります」のダイアログを表示する条件においてCWを考慮するように
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 4e4e95b6ed..4fe605490e 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -9472,6 +9472,14 @@ export interface Locale extends ILocale {
* ログインがありました
*/
"login": string;
+ /**
+ * アクセストークンが作成されました
+ */
+ "createToken": string;
+ /**
+ * 心当たりがない場合は「{text}」を通じてアクセストークンを削除してください。
+ */
+ "createTokenDescription": ParameterizedString<"text">;
"_types": {
/**
* すべて
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index ea18cc063c..57b11e9e04 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2500,6 +2500,8 @@ _notification:
flushNotification: "通知の履歴をリセットする"
exportOfXCompleted: "{x}のエクスポートが完了しました"
login: "ログインがありました"
+ createToken: "アクセストークンが作成されました"
+ createTokenDescription: "心当たりがない場合は「{text}」を通じてアクセストークンを削除してください。"
_types:
all: "すべて"
diff --git a/packages/backend/src/models/Notification.ts b/packages/backend/src/models/Notification.ts
index b7f8e94d69..5772ace338 100644
--- a/packages/backend/src/models/Notification.ts
+++ b/packages/backend/src/models/Notification.ts
@@ -91,6 +91,10 @@ export type MiNotification = {
id: string;
createdAt: string;
} | {
+ type: 'createToken';
+ id: string;
+ createdAt: string;
+} | {
type: 'app';
id: string;
createdAt: string;
diff --git a/packages/backend/src/models/json-schema/notification.ts b/packages/backend/src/models/json-schema/notification.ts
index cddaf4bc83..1638b2b3c7 100644
--- a/packages/backend/src/models/json-schema/notification.ts
+++ b/packages/backend/src/models/json-schema/notification.ts
@@ -339,6 +339,16 @@ export const packedNotificationSchema = {
type: {
type: 'string',
optional: false, nullable: false,
+ enum: ['createToken'],
+ },
+ },
+ }, {
+ type: 'object',
+ properties: {
+ ...baseSchema.properties,
+ type: {
+ type: 'string',
+ optional: false, nullable: false,
enum: ['app'],
},
body: {
diff --git a/packages/backend/src/server/api/endpoints/miauth/gen-token.ts b/packages/backend/src/server/api/endpoints/miauth/gen-token.ts
index fc9a8f3ebe..f1e3726641 100644
--- a/packages/backend/src/server/api/endpoints/miauth/gen-token.ts
+++ b/packages/backend/src/server/api/endpoints/miauth/gen-token.ts
@@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import type { AccessTokensRepository } from '@/models/_.js';
import { IdService } from '@/core/IdService.js';
+import { NotificationService } from '@/core/NotificationService.js';
import { secureRndstr } from '@/misc/secure-rndstr.js';
import { DI } from '@/di-symbols.js';
@@ -50,6 +51,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private accessTokensRepository: AccessTokensRepository,
private idService: IdService,
+ private notificationService: NotificationService,
) {
super(meta, paramDef, async (ps, me) => {
// Generate access token
@@ -71,6 +73,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
permission: ps.permission,
});
+ // アクセストークンが生成されたことを通知
+ this.notificationService.createNotification(me.id, 'createToken', {});
+
return {
token: accessToken,
};
diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts
index df3cfee171..bf409031c8 100644
--- a/packages/backend/src/types.ts
+++ b/packages/backend/src/types.ts
@@ -18,6 +18,7 @@
* achievementEarned - 実績を獲得
* exportCompleted - エクスポートが完了
* login - ログイン
+ * createToken - トークン作成
* app - アプリ通知
* test - テスト通知(サーバー側)
*/
@@ -36,6 +37,7 @@ export const notificationTypes = [
'achievementEarned',
'exportCompleted',
'login',
+ 'createToken',
'app',
'test',
] as const;
diff --git a/packages/frontend-shared/js/const.ts b/packages/frontend-shared/js/const.ts
index 4fe5cbb205..9e20479e26 100644
--- a/packages/frontend-shared/js/const.ts
+++ b/packages/frontend-shared/js/const.ts
@@ -69,6 +69,7 @@ export const notificationTypes = [
'achievementEarned',
'exportCompleted',
'login',
+ 'createToken',
'test',
'app',
] as const;
diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue
index 093bdb8b4c..80cb9a45bb 100644
--- a/packages/frontend/src/components/MkNotification.vue
+++ b/packages/frontend/src/components/MkNotification.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.root">
<div :class="$style.head">
<MkAvatar v-if="['pollEnded', 'note'].includes(notification.type) && 'note' in notification" :class="$style.icon" :user="notification.note.user" link preview/>
- <MkAvatar v-else-if="['roleAssigned', 'achievementEarned', 'exportCompleted', 'login'].includes(notification.type)" :class="$style.icon" :user="$i" link preview/>
+ <MkAvatar v-else-if="['roleAssigned', 'achievementEarned', 'exportCompleted', 'login', 'createToken'].includes(notification.type)" :class="$style.icon" :user="$i" link preview/>
<div v-else-if="notification.type === 'reaction:grouped' && notification.note.reactionAcceptance === 'likeOnly'" :class="[$style.icon, $style.icon_reactionGroupHeart]"><i class="ti ti-heart" style="line-height: 1;"></i></div>
<div v-else-if="notification.type === 'reaction:grouped'" :class="[$style.icon, $style.icon_reactionGroup]"><i class="ti ti-plus" style="line-height: 1;"></i></div>
<div v-else-if="notification.type === 'renote:grouped'" :class="[$style.icon, $style.icon_renoteGroup]"><i class="ti ti-repeat" style="line-height: 1;"></i></div>
@@ -27,6 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
[$style.t_achievementEarned]: notification.type === 'achievementEarned',
[$style.t_exportCompleted]: notification.type === 'exportCompleted',
[$style.t_login]: notification.type === 'login',
+ [$style.t_createToken]: notification.type === 'createToken',
[$style.t_roleAssigned]: notification.type === 'roleAssigned' && notification.role.iconUrl == null,
}]"
>
@@ -41,6 +42,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<i v-else-if="notification.type === 'achievementEarned'" class="ti ti-medal"></i>
<i v-else-if="notification.type === 'exportCompleted'" class="ti ti-archive"></i>
<i v-else-if="notification.type === 'login'" class="ti ti-login-2"></i>
+ <i v-else-if="notification.type === 'createToken'" class="ti ti-key"></i>
<template v-else-if="notification.type === 'roleAssigned'">
<img v-if="notification.role.iconUrl" style="height: 1.3em; vertical-align: -22%;" :src="notification.role.iconUrl" alt=""/>
<i v-else class="ti ti-badges"></i>
@@ -61,6 +63,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<span v-else-if="notification.type === 'roleAssigned'">{{ i18n.ts._notification.roleAssigned }}</span>
<span v-else-if="notification.type === 'achievementEarned'">{{ i18n.ts._notification.achievementEarned }}</span>
<span v-else-if="notification.type === 'login'">{{ i18n.ts._notification.login }}</span>
+ <span v-else-if="notification.type === 'createToken'">{{ i18n.ts._notification.createToken }}</span>
<span v-else-if="notification.type === 'test'">{{ i18n.ts._notification.testNotification }}</span>
<span v-else-if="notification.type === 'exportCompleted'">{{ i18n.tsx._notification.exportOfXCompleted({ x: exportEntityName[notification.exportedEntity] }) }}</span>
<MkA v-else-if="notification.type === 'follow' || notification.type === 'mention' || notification.type === 'reply' || notification.type === 'renote' || notification.type === 'quote' || notification.type === 'reaction' || notification.type === 'receiveFollowRequest' || notification.type === 'followRequestAccepted'" v-user-preview="notification.user.id" :class="$style.headerName" :to="userPage(notification.user)"><MkUserName :user="notification.user"/></MkA>
@@ -107,6 +110,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkA v-else-if="notification.type === 'exportCompleted'" :class="$style.text" :to="`/my/drive/file/${notification.fileId}`">
{{ i18n.ts.showFile }}
</MkA>
+ <MkA v-else-if="notification.type === 'createToken'" :class="$style.text" to="/settings/apps">
+ <Mfm :text="i18n.tsx._notification.createTokenDescription({ text: i18n.ts.manageAccessTokens })"/>
+ </MkA>
<template v-else-if="notification.type === 'follow'">
<span :class="$style.text" style="opacity: 0.6;">{{ i18n.ts.youGotNewFollower }}</span>
</template>
@@ -357,6 +363,12 @@ function getActualReactedUsersCount(notification: Misskey.entities.Notification)
pointer-events: none;
}
+.t_createToken {
+ padding: 3px;
+ background: var(--eventOther);
+ pointer-events: none;
+}
+
.tail {
flex: 1;
min-width: 0;
diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue
index 12af8d93d7..1e7436bf9c 100644
--- a/packages/frontend/src/pages/settings/notifications.vue
+++ b/packages/frontend/src/pages/settings/notifications.vue
@@ -81,7 +81,7 @@ const $i = signinRequired();
const nonConfigurableNotificationTypes = ['note', 'roleAssigned', 'followRequestAccepted', 'test', 'exportCompleted'] satisfies (typeof notificationTypes[number])[] as string[];
-const onlyOnOrOffNotificationTypes = ['app', 'achievementEarned', 'login'] satisfies (typeof notificationTypes[number])[] as string[];
+const onlyOnOrOffNotificationTypes = ['app', 'achievementEarned', 'login', 'createToken'] satisfies (typeof notificationTypes[number])[] as string[];
const allowButton = shallowRef<InstanceType<typeof MkPushNotificationAllowButton>>();
const pushRegistrationInServer = computed(() => allowButton.value?.pushRegistrationInServer);
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 313d0506fa..c7485c6c3d 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -4355,6 +4355,13 @@ export type components = {
createdAt: string;
/** @enum {string} */
type: 'login';
+ } | {
+ /** Format: id */
+ id: string;
+ /** Format: date-time */
+ createdAt: string;
+ /** @enum {string} */
+ type: 'createToken';
} | ({
/** Format: id */
id: string;
@@ -19917,8 +19924,8 @@ export type operations = {
untilId?: string;
/** @default true */
markAsRead?: boolean;
- includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'exportCompleted' | 'login' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
- excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'exportCompleted' | 'login' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
+ includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'exportCompleted' | 'login' | 'createToken' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
+ excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'exportCompleted' | 'login' | 'createToken' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
};
};
};
@@ -19985,8 +19992,8 @@ export type operations = {
untilId?: string;
/** @default true */
markAsRead?: boolean;
- includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'exportCompleted' | 'login' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped' | 'pollVote' | 'groupInvited')[];
- excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'exportCompleted' | 'login' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped' | 'pollVote' | 'groupInvited')[];
+ includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'exportCompleted' | 'login' | 'createToken' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped' | 'pollVote' | 'groupInvited')[];
+ excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'exportCompleted' | 'login' | 'createToken' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped' | 'pollVote' | 'groupInvited')[];
};
};
};