Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: アクセストークン発行時に通知するように #15422

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions locales/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9472,6 +9472,14 @@ export interface Locale extends ILocale {
* ログインがありました
*/
"login": string;
/**
* アクセストークンが作成されました
*/
"createToken": string;
/**
* 心当たりがない場合は「{text}」を通じてアクセストークンを削除してください。
*/
"createTokenDescription": ParameterizedString<"text">;
"_types": {
/**
* すべて
Expand Down
2 changes: 2 additions & 0 deletions locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2500,6 +2500,8 @@ _notification:
flushNotification: "通知の履歴をリセットする"
exportOfXCompleted: "{x}のエクスポートが完了しました"
login: "ログインがありました"
createToken: "アクセストークンが作成されました"
createTokenDescription: "心当たりがない場合は「{text}」を通じてアクセストークンを削除してください。"

_types:
all: "すべて"
Expand Down
4 changes: 4 additions & 0 deletions packages/backend/src/models/Notification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ export type MiNotification = {
type: 'login';
id: string;
createdAt: string;
} | {
type: 'createToken';
id: string;
createdAt: string;
} | {
type: 'app';
id: string;
Expand Down
10 changes: 10 additions & 0 deletions packages/backend/src/models/json-schema/notification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,16 @@ export const packedNotificationSchema = {
enum: ['login'],
},
},
}, {
type: 'object',
properties: {
...baseSchema.properties,
type: {
type: 'string',
optional: false, nullable: false,
enum: ['createToken'],
},
},
}, {
type: 'object',
properties: {
Expand Down
5 changes: 5 additions & 0 deletions packages/backend/src/server/api/endpoints/miauth/gen-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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
Expand All @@ -71,6 +73,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
permission: ps.permission,
});

// アクセストークンが生成されたことを通知
await this.notificationService.createNotification(me.id, 'createToken', {});
lqvp marked this conversation as resolved.
Show resolved Hide resolved

return {
token: accessToken,
};
Expand Down
2 changes: 2 additions & 0 deletions packages/backend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
* achievementEarned - 実績を獲得
* exportCompleted - エクスポートが完了
* login - ログイン
* createToken - トークン作成
* app - アプリ通知
* test - テスト通知(サーバー側)
*/
Expand All @@ -36,6 +37,7 @@ export const notificationTypes = [
'achievementEarned',
'exportCompleted',
'login',
'createToken',
'app',
'test',
] as const;
Expand Down
1 change: 1 addition & 0 deletions packages/frontend-shared/js/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export const notificationTypes = [
'achievementEarned',
'exportCompleted',
'login',
'createToken',
'test',
'app',
] as const;
Expand Down
14 changes: 13 additions & 1 deletion packages/frontend/src/components/MkNotification.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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,
}]"
>
Expand All @@ -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>
Expand All @@ -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>
Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/pages/settings/notifications.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
15 changes: 11 additions & 4 deletions packages/misskey-js/src/autogen/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -19916,8 +19923,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')[];
};
};
};
Expand Down Expand Up @@ -19984,8 +19991,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')[];
};
};
};
Expand Down
Loading