Merge branch 'develop' into block-deliver-by-software
This commit is contained in:
commit
ef503f6b85
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -4,11 +4,15 @@
|
|||
-
|
||||
|
||||
### Client
|
||||
-
|
||||
- Fix: ログアウトした際に処理が終了しない問題を修正
|
||||
- Fix: 自動バックアップが設定されている環境でログアウト直前に設定をバックアップするように
|
||||
|
||||
### Server
|
||||
-
|
||||
|
||||
- Enhance: フォローしているユーザーならフォロワー限定投稿のノートでもアンテナで検知できるように
|
||||
(Cherry-picked from https://github.com/yojo-art/cherrypick/pull/568 and https://github.com/team-shahu/misskey/pull/38)
|
||||
- Fix: システムアカウントの名前がサーバー名と同期されない問題を修正
|
||||
- Fix: 大文字を含むユーザの URL で紹介された場合に 404 エラーを返す問題 #15813
|
||||
- Fix: リードレプリカ設定時にレコードの追加・更新・削除を伴うクエリを発行した際はmasterノードで実行されるように調整( #10897 )
|
||||
|
||||
## 2025.4.0
|
||||
|
||||
|
|
|
@ -3938,6 +3938,10 @@ export interface Locale extends ILocale {
|
|||
* ログアウトしますか?
|
||||
*/
|
||||
"logoutConfirm": string;
|
||||
/**
|
||||
* ログアウトするとクライアントの設定情報がブラウザから消去されます。再ログイン時に設定情報を復元できるようにするためには、設定の自動バックアップを有効にしてください。
|
||||
*/
|
||||
"logoutWillClearClientData": string;
|
||||
/**
|
||||
* 最終利用日時
|
||||
*/
|
||||
|
|
|
@ -980,6 +980,7 @@ document: "ドキュメント"
|
|||
numberOfPageCache: "ページキャッシュ数"
|
||||
numberOfPageCacheDescription: "多くすると利便性が向上しますが、負荷とメモリ使用量が増えます。"
|
||||
logoutConfirm: "ログアウトしますか?"
|
||||
logoutWillClearClientData: "ログアウトするとクライアントの設定情報がブラウザから消去されます。再ログイン時に設定情報を復元できるようにするためには、設定の自動バックアップを有効にしてください。"
|
||||
lastActiveDate: "最終利用日時"
|
||||
statusbar: "ステータスバー"
|
||||
pleaseSelect: "選択してください"
|
||||
|
|
|
@ -37,17 +37,17 @@
|
|||
},
|
||||
"optionalDependencies": {
|
||||
"@swc/core-android-arm64": "1.3.11",
|
||||
"@swc/core-darwin-arm64": "1.11.11",
|
||||
"@swc/core-darwin-x64": "1.11.11",
|
||||
"@swc/core-darwin-arm64": "1.11.18",
|
||||
"@swc/core-darwin-x64": "1.11.18",
|
||||
"@swc/core-freebsd-x64": "1.3.11",
|
||||
"@swc/core-linux-arm-gnueabihf": "1.11.11",
|
||||
"@swc/core-linux-arm64-gnu": "1.11.11",
|
||||
"@swc/core-linux-arm64-musl": "1.11.11",
|
||||
"@swc/core-linux-x64-gnu": "1.11.11",
|
||||
"@swc/core-linux-x64-musl": "1.11.11",
|
||||
"@swc/core-win32-arm64-msvc": "1.11.11",
|
||||
"@swc/core-win32-ia32-msvc": "1.11.11",
|
||||
"@swc/core-win32-x64-msvc": "1.11.11",
|
||||
"@swc/core-linux-arm-gnueabihf": "1.11.18",
|
||||
"@swc/core-linux-arm64-gnu": "1.11.18",
|
||||
"@swc/core-linux-arm64-musl": "1.11.18",
|
||||
"@swc/core-linux-x64-gnu": "1.11.18",
|
||||
"@swc/core-linux-x64-musl": "1.11.18",
|
||||
"@swc/core-win32-arm64-msvc": "1.11.18",
|
||||
"@swc/core-win32-ia32-msvc": "1.11.18",
|
||||
"@swc/core-win32-x64-msvc": "1.11.18",
|
||||
"@tensorflow/tfjs": "4.22.0",
|
||||
"@tensorflow/tfjs-node": "4.22.0",
|
||||
"bufferutil": "4.0.9",
|
||||
|
@ -67,8 +67,8 @@
|
|||
"utf-8-validate": "6.0.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "3.772.0",
|
||||
"@aws-sdk/lib-storage": "3.772.0",
|
||||
"@aws-sdk/client-s3": "3.782.0",
|
||||
"@aws-sdk/lib-storage": "3.782.0",
|
||||
"@discordapp/twemoji": "15.1.0",
|
||||
"@fastify/accepts": "5.0.2",
|
||||
"@fastify/cookie": "11.0.2",
|
||||
|
@ -78,12 +78,12 @@
|
|||
"@fastify/multipart": "9.0.3",
|
||||
"@fastify/static": "8.1.1",
|
||||
"@fastify/view": "10.0.2",
|
||||
"@misskey-dev/sharp-read-bmp": "1.2.0",
|
||||
"@misskey-dev/sharp-read-bmp": "1.3.0",
|
||||
"@misskey-dev/summaly": "5.2.0",
|
||||
"@napi-rs/canvas": "0.1.68",
|
||||
"@nestjs/common": "11.0.12",
|
||||
"@nestjs/core": "11.0.12",
|
||||
"@nestjs/testing": "11.0.12",
|
||||
"@napi-rs/canvas": "0.1.69",
|
||||
"@nestjs/common": "11.0.16",
|
||||
"@nestjs/core": "11.0.15",
|
||||
"@nestjs/testing": "11.0.15",
|
||||
"@peertube/http-signature": "1.7.0",
|
||||
"@sentry/node": "8.55.0",
|
||||
"@sentry/profiling-node": "8.55.0",
|
||||
|
@ -91,7 +91,7 @@
|
|||
"@sinonjs/fake-timers": "11.3.1",
|
||||
"@smithy/node-http-handler": "2.5.0",
|
||||
"@swc/cli": "0.6.0",
|
||||
"@swc/core": "1.11.11",
|
||||
"@swc/core": "1.11.18",
|
||||
"@twemoji/parser": "15.1.1",
|
||||
"accepts": "1.3.8",
|
||||
"ajv": "8.17.1",
|
||||
|
@ -100,7 +100,7 @@
|
|||
"bcryptjs": "2.4.3",
|
||||
"blurhash": "2.0.5",
|
||||
"body-parser": "1.20.3",
|
||||
"bullmq": "5.44.1",
|
||||
"bullmq": "5.48.1",
|
||||
"cacheable-lookup": "7.0.0",
|
||||
"cbor": "9.0.2",
|
||||
"chalk": "5.4.1",
|
||||
|
@ -111,13 +111,13 @@
|
|||
"content-disposition": "0.5.4",
|
||||
"date-fns": "2.30.0",
|
||||
"deep-email-validator": "0.1.21",
|
||||
"fastify": "5.2.1",
|
||||
"fastify": "5.2.2",
|
||||
"fastify-raw-body": "5.0.0",
|
||||
"feed": "4.2.2",
|
||||
"file-type": "19.6.0",
|
||||
"fluent-ffmpeg": "2.1.3",
|
||||
"form-data": "4.0.2",
|
||||
"got": "14.4.6",
|
||||
"got": "14.4.7",
|
||||
"happy-dom": "16.8.1",
|
||||
"hpagent": "1.2.0",
|
||||
"htmlescape": "1.1.1",
|
||||
|
@ -148,7 +148,7 @@
|
|||
"oauth2orize": "1.12.0",
|
||||
"oauth2orize-pkce": "0.1.2",
|
||||
"os-utils": "0.0.14",
|
||||
"otpauth": "9.3.6",
|
||||
"otpauth": "9.4.0",
|
||||
"parse5": "7.2.1",
|
||||
"pg": "8.14.1",
|
||||
"pkce-challenge": "4.1.0",
|
||||
|
@ -167,17 +167,16 @@
|
|||
"sanitize-html": "2.15.0",
|
||||
"secure-json-parse": "3.0.2",
|
||||
"semver": "7.7.1",
|
||||
"sharp": "0.33.5",
|
||||
"slacc": "0.0.10",
|
||||
"strict-event-emitter-types": "2.0.0",
|
||||
"stringz": "2.1.0",
|
||||
"systeminformation": "5.25.11",
|
||||
"tinycolor2": "1.6.0",
|
||||
"tmp": "0.2.3",
|
||||
"tsc-alias": "1.8.11",
|
||||
"tsc-alias": "1.8.15",
|
||||
"tsconfig-paths": "4.2.0",
|
||||
"typeorm": "0.3.21",
|
||||
"typescript": "5.8.2",
|
||||
"typeorm": "0.3.22",
|
||||
"typescript": "5.8.3",
|
||||
"ulid": "2.4.0",
|
||||
"vary": "1.1.2",
|
||||
"web-push": "3.6.7",
|
||||
|
@ -187,7 +186,7 @@
|
|||
"devDependencies": {
|
||||
"@jest/globals": "29.7.0",
|
||||
"@nestjs/platform-express": "10.4.15",
|
||||
"@sentry/vue": "9.8.0",
|
||||
"@sentry/vue": "9.12.0",
|
||||
"@simplewebauthn/types": "12.0.0",
|
||||
"@swc/jest": "0.2.37",
|
||||
"@types/accepts": "1.3.7",
|
||||
|
@ -206,7 +205,7 @@
|
|||
"@types/jsrsasign": "10.5.15",
|
||||
"@types/mime-types": "2.1.4",
|
||||
"@types/ms": "0.7.34",
|
||||
"@types/node": "22.13.10",
|
||||
"@types/node": "22.14.0",
|
||||
"@types/nodemailer": "6.4.17",
|
||||
"@types/oauth": "0.9.6",
|
||||
"@types/oauth2orize": "1.11.5",
|
||||
|
@ -217,17 +216,17 @@
|
|||
"@types/random-seed": "0.3.5",
|
||||
"@types/ratelimiter": "3.4.6",
|
||||
"@types/rename": "1.0.7",
|
||||
"@types/sanitize-html": "2.13.0",
|
||||
"@types/semver": "7.5.8",
|
||||
"@types/sanitize-html": "2.15.0",
|
||||
"@types/semver": "7.7.0",
|
||||
"@types/simple-oauth2": "5.0.7",
|
||||
"@types/sinonjs__fake-timers": "8.1.5",
|
||||
"@types/tinycolor2": "1.4.6",
|
||||
"@types/tmp": "0.2.6",
|
||||
"@types/vary": "1.1.3",
|
||||
"@types/web-push": "3.6.4",
|
||||
"@types/ws": "8.18.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.27.0",
|
||||
"@typescript-eslint/parser": "8.27.0",
|
||||
"@types/ws": "8.18.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.29.1",
|
||||
"@typescript-eslint/parser": "8.29.1",
|
||||
"aws-sdk-client-mock": "4.1.0",
|
||||
"cross-env": "7.0.3",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
|
|
|
@ -5,18 +5,19 @@
|
|||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import * as Redis from 'ioredis';
|
||||
import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
|
||||
import type { GlobalEvents } from '@/core/GlobalEventService.js';
|
||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||
import { UtilityService } from '@/core/UtilityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import * as Acct from '@/misc/acct.js';
|
||||
import type { Packed } from '@/misc/json-schema.js';
|
||||
import type { AntennasRepository, UserListMembershipsRepository } from '@/models/_.js';
|
||||
import type { MiAntenna } from '@/models/Antenna.js';
|
||||
import type { MiNote } from '@/models/Note.js';
|
||||
import type { MiUser } from '@/models/User.js';
|
||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||
import * as Acct from '@/misc/acct.js';
|
||||
import type { Packed } from '@/misc/json-schema.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { AntennasRepository, UserListMembershipsRepository } from '@/models/_.js';
|
||||
import { UtilityService } from '@/core/UtilityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import type { GlobalEvents } from '@/core/GlobalEventService.js';
|
||||
import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
|
||||
import { CacheService } from './CacheService.js';
|
||||
import type { OnApplicationShutdown } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
|
@ -37,6 +38,7 @@ export class AntennaService implements OnApplicationShutdown {
|
|||
@Inject(DI.userListMembershipsRepository)
|
||||
private userListMembershipsRepository: UserListMembershipsRepository,
|
||||
|
||||
private cacheService: CacheService,
|
||||
private utilityService: UtilityService,
|
||||
private globalEventService: GlobalEventService,
|
||||
private fanoutTimelineService: FanoutTimelineService,
|
||||
|
@ -111,9 +113,6 @@ export class AntennaService implements OnApplicationShutdown {
|
|||
|
||||
@bindThis
|
||||
public async checkHitAntenna(antenna: MiAntenna, note: (MiNote | Packed<'Note'>), noteUser: { id: MiUser['id']; username: string; host: string | null; isBot: boolean; }): Promise<boolean> {
|
||||
if (note.visibility === 'specified') return false;
|
||||
if (note.visibility === 'followers') return false;
|
||||
|
||||
if (antenna.excludeNotesInSensitiveChannel && note.channel?.isSensitive) return false;
|
||||
|
||||
if (antenna.excludeBots && noteUser.isBot) return false;
|
||||
|
@ -122,6 +121,18 @@ export class AntennaService implements OnApplicationShutdown {
|
|||
|
||||
if (!antenna.withReplies && note.replyId != null) return false;
|
||||
|
||||
if (note.visibility === 'specified') {
|
||||
if (note.userId !== antenna.userId) {
|
||||
if (note.visibleUserIds == null) return false;
|
||||
if (!note.visibleUserIds.includes(antenna.userId)) return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (note.visibility === 'followers') {
|
||||
const isFollowing = Object.hasOwn(await this.cacheService.userFollowingsCache.fetch(antenna.userId), note.userId);
|
||||
if (!isFollowing && antenna.userId !== note.userId) return false;
|
||||
}
|
||||
|
||||
if (antenna.src === 'home') {
|
||||
// TODO
|
||||
} else if (antenna.src === 'list') {
|
||||
|
|
|
@ -5,11 +5,14 @@
|
|||
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import type { OnApplicationShutdown } from '@nestjs/common';
|
||||
import { DataSource, IsNull } from 'typeorm';
|
||||
import * as Redis from 'ioredis';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import { MiLocalUser, MiUser } from '@/models/User.js';
|
||||
import { MiSystemAccount, MiUsedUsername, MiUserKeypair, MiUserProfile, type UsersRepository, type SystemAccountsRepository } from '@/models/_.js';
|
||||
import type { MiMeta, UserProfilesRepository } from '@/models/_.js';
|
||||
import type { GlobalEvents } from '@/core/GlobalEventService.js';
|
||||
import { MemoryKVCache } from '@/misc/cache.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
|
@ -20,10 +23,13 @@ import { genRsaKeyPair } from '@/misc/gen-key-pair.js';
|
|||
export const SYSTEM_ACCOUNT_TYPES = ['actor', 'relay', 'proxy'] as const;
|
||||
|
||||
@Injectable()
|
||||
export class SystemAccountService {
|
||||
export class SystemAccountService implements OnApplicationShutdown {
|
||||
private cache: MemoryKVCache<MiLocalUser>;
|
||||
|
||||
constructor(
|
||||
@Inject(DI.redisForSub)
|
||||
private redisForSub: Redis.Redis,
|
||||
|
||||
@Inject(DI.db)
|
||||
private db: DataSource,
|
||||
|
||||
|
@ -42,6 +48,31 @@ export class SystemAccountService {
|
|||
private idService: IdService,
|
||||
) {
|
||||
this.cache = new MemoryKVCache<MiLocalUser>(1000 * 60 * 10); // 10m
|
||||
|
||||
this.redisForSub.on('message', this.onMessage);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private async onMessage(_: string, data: string): Promise<void> {
|
||||
const obj = JSON.parse(data);
|
||||
|
||||
if (obj.channel === 'internal') {
|
||||
const { type, body } = obj.message as GlobalEvents['internal']['payload'];
|
||||
switch (type) {
|
||||
case 'metaUpdated': {
|
||||
if (body.before != null && body.before.name !== body.after.name) {
|
||||
for (const account of SYSTEM_ACCOUNT_TYPES) {
|
||||
await this.updateCorrespondingUserProfile(account, {
|
||||
name: body.after.name,
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
|
@ -145,7 +176,7 @@ export class SystemAccountService {
|
|||
|
||||
@bindThis
|
||||
public async updateCorrespondingUserProfile(type: typeof SYSTEM_ACCOUNT_TYPES[number], extra: {
|
||||
name?: string;
|
||||
name?: string | null;
|
||||
description?: MiUserProfile['description'];
|
||||
}): Promise<MiLocalUser> {
|
||||
const user = await this.fetch(type);
|
||||
|
@ -169,4 +200,15 @@ export class SystemAccountService {
|
|||
|
||||
return updated;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public dispose(): void {
|
||||
this.redisForSub.off('message', this.onMessage);
|
||||
this.cache.dispose();
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public onApplicationShutdown(signal?: string): void {
|
||||
this.dispose();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,29 +3,48 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { FindOneOptions, InsertQueryBuilder, ObjectLiteral, Repository, SelectQueryBuilder } from 'typeorm';
|
||||
import {
|
||||
FindOneOptions,
|
||||
InsertQueryBuilder,
|
||||
ObjectLiteral,
|
||||
QueryRunner,
|
||||
Repository,
|
||||
SelectQueryBuilder,
|
||||
} from 'typeorm';
|
||||
import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions.js';
|
||||
import { RelationCountLoader } from 'typeorm/query-builder/relation-count/RelationCountLoader.js';
|
||||
import { RelationIdLoader } from 'typeorm/query-builder/relation-id/RelationIdLoader.js';
|
||||
import { RawSqlResultsToEntityTransformer } from 'typeorm/query-builder/transformer/RawSqlResultsToEntityTransformer.js';
|
||||
import { MiAbuseUserReport } from '@/models/AbuseUserReport.js';
|
||||
import {
|
||||
RawSqlResultsToEntityTransformer,
|
||||
} from 'typeorm/query-builder/transformer/RawSqlResultsToEntityTransformer.js';
|
||||
import { MiAbuseReportNotificationRecipient } from '@/models/AbuseReportNotificationRecipient.js';
|
||||
import { MiAbuseUserReport } from '@/models/AbuseUserReport.js';
|
||||
import { MiAccessToken } from '@/models/AccessToken.js';
|
||||
import { MiAd } from '@/models/Ad.js';
|
||||
import { MiAnnouncement } from '@/models/Announcement.js';
|
||||
import { MiAnnouncementRead } from '@/models/AnnouncementRead.js';
|
||||
import { MiAntenna } from '@/models/Antenna.js';
|
||||
import { MiApp } from '@/models/App.js';
|
||||
import { MiAvatarDecoration } from '@/models/AvatarDecoration.js';
|
||||
import { MiAuthSession } from '@/models/AuthSession.js';
|
||||
import { MiAvatarDecoration } from '@/models/AvatarDecoration.js';
|
||||
import { MiBlocking } from '@/models/Blocking.js';
|
||||
import { MiChannelFollowing } from '@/models/ChannelFollowing.js';
|
||||
import { MiBubbleGameRecord } from '@/models/BubbleGameRecord.js';
|
||||
import { MiChannel } from '@/models/Channel.js';
|
||||
import { MiChannelFavorite } from '@/models/ChannelFavorite.js';
|
||||
import { MiChannelFollowing } from '@/models/ChannelFollowing.js';
|
||||
import { MiChatApproval } from '@/models/ChatApproval.js';
|
||||
import { MiChatMessage } from '@/models/ChatMessage.js';
|
||||
import { MiChatRoom } from '@/models/ChatRoom.js';
|
||||
import { MiChatRoomInvitation } from '@/models/ChatRoomInvitation.js';
|
||||
import { MiChatRoomMembership } from '@/models/ChatRoomMembership.js';
|
||||
import { MiClip } from '@/models/Clip.js';
|
||||
import { MiClipNote } from '@/models/ClipNote.js';
|
||||
import { MiClipFavorite } from '@/models/ClipFavorite.js';
|
||||
import { MiClipNote } from '@/models/ClipNote.js';
|
||||
import { MiDriveFile } from '@/models/DriveFile.js';
|
||||
import { MiDriveFolder } from '@/models/DriveFolder.js';
|
||||
import { MiEmoji } from '@/models/Emoji.js';
|
||||
import { MiFlash } from '@/models/Flash.js';
|
||||
import { MiFlashLike } from '@/models/FlashLike.js';
|
||||
import { MiFollowing } from '@/models/Following.js';
|
||||
import { MiFollowRequest } from '@/models/FollowRequest.js';
|
||||
import { MiGalleryLike } from '@/models/GalleryLike.js';
|
||||
|
@ -35,7 +54,6 @@ import { MiInstance } from '@/models/Instance.js';
|
|||
import { MiMeta } from '@/models/Meta.js';
|
||||
import { MiModerationLog } from '@/models/ModerationLog.js';
|
||||
import { MiMuting } from '@/models/Muting.js';
|
||||
import { MiRenoteMuting } from '@/models/RenoteMuting.js';
|
||||
import { MiNote } from '@/models/Note.js';
|
||||
import { MiNoteFavorite } from '@/models/NoteFavorite.js';
|
||||
import { MiNoteReaction } from '@/models/NoteReaction.js';
|
||||
|
@ -50,42 +68,38 @@ import { MiPromoRead } from '@/models/PromoRead.js';
|
|||
import { MiRegistrationTicket } from '@/models/RegistrationTicket.js';
|
||||
import { MiRegistryItem } from '@/models/RegistryItem.js';
|
||||
import { MiRelay } from '@/models/Relay.js';
|
||||
import { MiRenoteMuting } from '@/models/RenoteMuting.js';
|
||||
import { MiRetentionAggregation } from '@/models/RetentionAggregation.js';
|
||||
import { MiReversiGame } from '@/models/ReversiGame.js';
|
||||
import { MiRole } from '@/models/Role.js';
|
||||
import { MiRoleAssignment } from '@/models/RoleAssignment.js';
|
||||
import { MiSignin } from '@/models/Signin.js';
|
||||
import { MiSwSubscription } from '@/models/SwSubscription.js';
|
||||
import { MiSystemAccount } from '@/models/SystemAccount.js';
|
||||
import { MiSystemWebhook } from '@/models/SystemWebhook.js';
|
||||
import { MiUsedUsername } from '@/models/UsedUsername.js';
|
||||
import { MiUser } from '@/models/User.js';
|
||||
import { MiUserIp } from '@/models/UserIp.js';
|
||||
import { MiUserKeypair } from '@/models/UserKeypair.js';
|
||||
import { MiUserList } from '@/models/UserList.js';
|
||||
import { MiUserListFavorite } from '@/models/UserListFavorite.js';
|
||||
import { MiUserListMembership } from '@/models/UserListMembership.js';
|
||||
import { MiUserMemo } from '@/models/UserMemo.js';
|
||||
import { MiUserNotePining } from '@/models/UserNotePining.js';
|
||||
import { MiUserPending } from '@/models/UserPending.js';
|
||||
import { MiUserProfile } from '@/models/UserProfile.js';
|
||||
import { MiUserPublickey } from '@/models/UserPublickey.js';
|
||||
import { MiUserSecurityKey } from '@/models/UserSecurityKey.js';
|
||||
import { MiUserMemo } from '@/models/UserMemo.js';
|
||||
import { MiWebhook } from '@/models/Webhook.js';
|
||||
import { MiSystemWebhook } from '@/models/SystemWebhook.js';
|
||||
import { MiChannel } from '@/models/Channel.js';
|
||||
import { MiRetentionAggregation } from '@/models/RetentionAggregation.js';
|
||||
import { MiRole } from '@/models/Role.js';
|
||||
import { MiRoleAssignment } from '@/models/RoleAssignment.js';
|
||||
import { MiFlash } from '@/models/Flash.js';
|
||||
import { MiFlashLike } from '@/models/FlashLike.js';
|
||||
import { MiUserListFavorite } from '@/models/UserListFavorite.js';
|
||||
import { MiChatMessage } from '@/models/ChatMessage.js';
|
||||
import { MiChatRoom } from '@/models/ChatRoom.js';
|
||||
import { MiChatRoomMembership } from '@/models/ChatRoomMembership.js';
|
||||
import { MiChatRoomInvitation } from '@/models/ChatRoomInvitation.js';
|
||||
import { MiChatApproval } from '@/models/ChatApproval.js';
|
||||
import { MiBubbleGameRecord } from '@/models/BubbleGameRecord.js';
|
||||
import { MiReversiGame } from '@/models/ReversiGame.js';
|
||||
import type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity.js';
|
||||
|
||||
export interface MiRepository<T extends ObjectLiteral> {
|
||||
createTableColumnNames(this: Repository<T> & MiRepository<T>): string[];
|
||||
|
||||
insertOne(this: Repository<T> & MiRepository<T>, entity: QueryDeepPartialEntity<T>, findOptions?: Pick<FindOneOptions<T>, 'relations'>): Promise<T>;
|
||||
|
||||
insertOneImpl(this: Repository<T> & MiRepository<T>, entity: QueryDeepPartialEntity<T>, findOptions?: Pick<FindOneOptions<T>, 'relations'>, queryRunner?: QueryRunner): Promise<T>;
|
||||
|
||||
selectAliasColumnNames(this: Repository<T> & MiRepository<T>, queryBuilder: InsertQueryBuilder<T>, builder: SelectQueryBuilder<T>): void;
|
||||
}
|
||||
|
||||
|
@ -94,6 +108,21 @@ export const miRepository = {
|
|||
return this.metadata.columns.filter(column => column.isSelect && !column.isVirtual).map(column => column.databaseName);
|
||||
},
|
||||
async insertOne(entity, findOptions?) {
|
||||
const opt = this.manager.connection.options as PostgresConnectionOptions;
|
||||
if (opt.replication) {
|
||||
const queryRunner = this.manager.connection.createQueryRunner('master');
|
||||
try {
|
||||
return this.insertOneImpl(entity, findOptions, queryRunner);
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
} else {
|
||||
return this.insertOneImpl(entity, findOptions);
|
||||
}
|
||||
},
|
||||
async insertOneImpl(entity, findOptions?, queryRunner?) {
|
||||
// ---- insert + returningの結果を共通テーブル式(CTE)に保持するクエリを生成 ----
|
||||
|
||||
const queryBuilder = this.createQueryBuilder().insert().values(entity);
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const mainAlias = queryBuilder.expressionMap.mainAlias!;
|
||||
|
@ -101,7 +130,9 @@ export const miRepository = {
|
|||
mainAlias.name = 't';
|
||||
const columnNames = this.createTableColumnNames();
|
||||
queryBuilder.returning(columnNames.reduce((a, c) => `${a}, ${queryBuilder.escape(c)}`, '').slice(2));
|
||||
const builder = this.createQueryBuilder().addCommonTableExpression(queryBuilder, 'cte', { columnNames });
|
||||
|
||||
// ---- 共通テーブル式(CTE)から結果を取得 ----
|
||||
const builder = this.createQueryBuilder(undefined, queryRunner).addCommonTableExpression(queryBuilder, 'cte', { columnNames });
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
builder.expressionMap.mainAlias!.tablePath = 'cte';
|
||||
this.selectAliasColumnNames(queryBuilder, builder);
|
||||
|
@ -204,7 +235,9 @@ export {
|
|||
};
|
||||
|
||||
export type AbuseUserReportsRepository = Repository<MiAbuseUserReport> & MiRepository<MiAbuseUserReport>;
|
||||
export type AbuseReportNotificationRecipientRepository = Repository<MiAbuseReportNotificationRecipient> & MiRepository<MiAbuseReportNotificationRecipient>;
|
||||
export type AbuseReportNotificationRecipientRepository =
|
||||
Repository<MiAbuseReportNotificationRecipient>
|
||||
& MiRepository<MiAbuseReportNotificationRecipient>;
|
||||
export type AccessTokensRepository = Repository<MiAccessToken> & MiRepository<MiAccessToken>;
|
||||
export type AdsRepository = Repository<MiAd> & MiRepository<MiAd>;
|
||||
export type AnnouncementsRepository = Repository<MiAnnouncement> & MiRepository<MiAnnouncement>;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
// https://github.com/typeorm/typeorm/issues/2400
|
||||
import pg from 'pg';
|
||||
import { DataSource, Logger } from 'typeorm';
|
||||
import { DataSource, Logger, type QueryRunner } from 'typeorm';
|
||||
import * as highlight from 'cli-highlight';
|
||||
import { entities as charts } from '@/core/chart/entities.js';
|
||||
import { Config } from '@/config.js';
|
||||
|
@ -96,6 +96,7 @@ const sqlLogger = dbLogger.createSubLogger('sql', 'gray');
|
|||
export type LoggerProps = {
|
||||
disableQueryTruncation?: boolean;
|
||||
enableQueryParamLogging?: boolean;
|
||||
printReplicationMode?: boolean,
|
||||
};
|
||||
|
||||
function highlightSql(sql: string) {
|
||||
|
@ -121,8 +122,10 @@ class MyCustomLogger implements Logger {
|
|||
}
|
||||
|
||||
@bindThis
|
||||
private transformQueryLog(sql: string) {
|
||||
let modded = sql;
|
||||
private transformQueryLog(sql: string, opts?: {
|
||||
prefix?: string;
|
||||
}) {
|
||||
let modded = opts?.prefix ? opts.prefix + sql : sql;
|
||||
if (!this.props.disableQueryTruncation) {
|
||||
modded = truncateSql(modded);
|
||||
}
|
||||
|
@ -140,18 +143,27 @@ class MyCustomLogger implements Logger {
|
|||
}
|
||||
|
||||
@bindThis
|
||||
public logQuery(query: string, parameters?: any[]) {
|
||||
sqlLogger.info(this.transformQueryLog(query), this.transformParameters(parameters));
|
||||
public logQuery(query: string, parameters?: any[], queryRunner?: QueryRunner) {
|
||||
const prefix = (this.props.printReplicationMode && queryRunner)
|
||||
? `[${queryRunner.getReplicationMode()}] `
|
||||
: undefined;
|
||||
sqlLogger.info(this.transformQueryLog(query, { prefix }), this.transformParameters(parameters));
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public logQueryError(error: string, query: string, parameters?: any[]) {
|
||||
sqlLogger.error(this.transformQueryLog(query), this.transformParameters(parameters));
|
||||
public logQueryError(error: string, query: string, parameters?: any[], queryRunner?: QueryRunner) {
|
||||
const prefix = (this.props.printReplicationMode && queryRunner)
|
||||
? `[${queryRunner.getReplicationMode()}] `
|
||||
: undefined;
|
||||
sqlLogger.error(this.transformQueryLog(query, { prefix }), this.transformParameters(parameters));
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public logQuerySlow(time: number, query: string, parameters?: any[]) {
|
||||
sqlLogger.warn(this.transformQueryLog(query), this.transformParameters(parameters));
|
||||
public logQuerySlow(time: number, query: string, parameters?: any[], queryRunner?: QueryRunner) {
|
||||
const prefix = (this.props.printReplicationMode && queryRunner)
|
||||
? `[${queryRunner.getReplicationMode()}] `
|
||||
: undefined;
|
||||
sqlLogger.warn(this.transformQueryLog(query, { prefix }), this.transformParameters(parameters));
|
||||
}
|
||||
|
||||
@bindThis
|
||||
|
@ -298,6 +310,7 @@ export function createPostgresDataSource(config: Config) {
|
|||
? new MyCustomLogger({
|
||||
disableQueryTruncation: config.logging?.sql?.disableQueryTruncation,
|
||||
enableQueryParamLogging: config.logging?.sql?.enableQueryParamLogging,
|
||||
printReplicationMode: !!config.dbReplications,
|
||||
})
|
||||
: undefined,
|
||||
maxQueryExecutionTime: 300,
|
||||
|
|
|
@ -735,7 +735,7 @@ export class ActivityPubServerService {
|
|||
const acct = Acct.parse(request.params.acct);
|
||||
|
||||
const user = await this.usersRepository.findOneBy({
|
||||
usernameLower: acct.username,
|
||||
usernameLower: acct.username.toLowerCase(),
|
||||
host: acct.host ?? IsNull(),
|
||||
isSuspended: false,
|
||||
});
|
||||
|
|
|
@ -138,7 +138,7 @@ fastify.get('/.well-known/change-password', async (request, reply) => {
|
|||
|
||||
const fromAcct = (acct: Acct.Acct): FindOptionsWhere<MiUser> | number =>
|
||||
!acct.host || acct.host === this.config.host.toLowerCase() ? {
|
||||
usernameLower: acct.username,
|
||||
usernameLower: acct.username.toLowerCase(),
|
||||
host: IsNull(),
|
||||
isSuspended: false,
|
||||
} : 422;
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
process.env.NODE_ENV = 'test';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
||||
import {
|
||||
api,
|
||||
failedApiCall,
|
||||
|
@ -19,6 +18,7 @@ import {
|
|||
userList,
|
||||
} from '../utils.js';
|
||||
import type * as misskey from 'misskey-js';
|
||||
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
||||
|
||||
const compareBy = <T extends { id: string }>(selector: (s: T) => string = (s: T): string => s.id) => (a: T, b: T): number => {
|
||||
return selector(a).localeCompare(selector(b));
|
||||
|
@ -235,12 +235,12 @@ describe('アンテナ', () => {
|
|||
await failedApiCall({
|
||||
endpoint: 'antennas/create',
|
||||
parameters: { ...defaultParam, keywords: [[]], excludeKeywords: [[]] },
|
||||
user: alice
|
||||
user: alice,
|
||||
}, {
|
||||
status: 400,
|
||||
code: 'EMPTY_KEYWORD',
|
||||
id: '53ee222e-1ddd-4f9a-92e5-9fb82ddb463a'
|
||||
})
|
||||
id: '53ee222e-1ddd-4f9a-92e5-9fb82ddb463a',
|
||||
});
|
||||
});
|
||||
//#endregion
|
||||
//#region 更新(antennas/update)
|
||||
|
@ -274,12 +274,12 @@ describe('アンテナ', () => {
|
|||
await failedApiCall({
|
||||
endpoint: 'antennas/update',
|
||||
parameters: { ...defaultParam, antennaId: antenna.id, keywords: [[]], excludeKeywords: [[]] },
|
||||
user: alice
|
||||
user: alice,
|
||||
}, {
|
||||
status: 400,
|
||||
code: 'EMPTY_KEYWORD',
|
||||
id: '721aaff6-4e1b-4d88-8de6-877fae9f68c4'
|
||||
})
|
||||
id: '721aaff6-4e1b-4d88-8de6-877fae9f68c4',
|
||||
});
|
||||
});
|
||||
|
||||
//#endregion
|
||||
|
@ -375,14 +375,23 @@ describe('アンテナ', () => {
|
|||
],
|
||||
},
|
||||
{
|
||||
// https://github.com/misskey-dev/misskey/issues/9025
|
||||
label: 'ただし、フォロワー限定投稿とDM投稿を含まない。フォロワーであっても。',
|
||||
label: 'フォロワー限定投稿とDM投稿を含む',
|
||||
parameters: () => ({}),
|
||||
posts: [
|
||||
{ note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}`, visibility: 'public' }), included: true },
|
||||
{ note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}`, visibility: 'home' }), included: true },
|
||||
{ note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}`, visibility: 'followers' }) },
|
||||
{ note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}`, visibility: 'specified', visibleUserIds: [alice.id] }) },
|
||||
{ note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}`, visibility: 'followers' }), included: true },
|
||||
{ note: (): Promise<Note> => post(bob, { text: `${keyword}`, visibility: 'specified', visibleUserIds: [alice.id] }), included: true },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'フォロワー限定投稿とDM投稿を含まない',
|
||||
parameters: () => ({}),
|
||||
posts: [
|
||||
{ note: (): Promise<Note> => post(bob, { text: `${keyword}`, visibility: 'public' }), included: true },
|
||||
{ note: (): Promise<Note> => post(bob, { text: `${keyword}`, visibility: 'home' }), included: true },
|
||||
{ note: (): Promise<Note> => post(bob, { text: `${keyword}`, visibility: 'followers' }) },
|
||||
{ note: (): Promise<Note> => post(bob, { text: `${keyword}`, visibility: 'specified', visibleUserIds: [carol.id] }) },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
@ -44,7 +44,7 @@ describe('AnnouncementService', () => {
|
|||
return usersRepository.insert({
|
||||
id: genAidx(Date.now()),
|
||||
username: un,
|
||||
usernameLower: un,
|
||||
usernameLower: un.toLowerCase(),
|
||||
...data,
|
||||
})
|
||||
.then(x => usersRepository.findOneByOrFail(x.identifiers[0]));
|
||||
|
|
|
@ -89,8 +89,8 @@ describe('SigninWithPasskeyApiService', () => {
|
|||
app = await Test.createTestingModule({
|
||||
imports: [GlobalModule, CoreModule],
|
||||
providers: [
|
||||
SigninWithPasskeyApiService,
|
||||
{ provide: RateLimiterService, useClass: FakeLimiter },
|
||||
SigninWithPasskeyApiService,
|
||||
{ provide: RateLimiterService, useClass: FakeLimiter },
|
||||
{ provide: SigninService, useClass: FakeSigninService },
|
||||
],
|
||||
}).useMocker((token) => {
|
||||
|
@ -115,7 +115,7 @@ describe('SigninWithPasskeyApiService', () => {
|
|||
jest.spyOn(webAuthnService, 'verifySignInWithPasskeyAuthentication').mockImplementation(FakeWebauthnVerify);
|
||||
|
||||
const dummyUser = {
|
||||
id: uid, username: uid, usernameLower: uid.toLocaleLowerCase(), uri: null, host: null,
|
||||
id: uid, username: uid, usernameLower: uid.toLowerCase(), uri: null, host: null,
|
||||
};
|
||||
const dummyProfile = {
|
||||
userId: uid,
|
||||
|
|
|
@ -74,7 +74,7 @@ describe('UserEntityService', () => {
|
|||
...userData,
|
||||
id: genAidx(Date.now()),
|
||||
username: un,
|
||||
usernameLower: un,
|
||||
usernameLower: un.toLowerCase(),
|
||||
})
|
||||
.then(x => usersRepository.findOneByOrFail(x.identifiers[0]));
|
||||
|
||||
|
|
|
@ -25,13 +25,13 @@
|
|||
"misskey-js": "workspace:*",
|
||||
"frontend-shared": "workspace:*",
|
||||
"punycode.js": "2.3.1",
|
||||
"rollup": "4.36.0",
|
||||
"sass": "1.86.0",
|
||||
"shiki": "3.2.1",
|
||||
"rollup": "4.39.0",
|
||||
"sass": "1.86.3",
|
||||
"shiki": "3.2.2",
|
||||
"tinycolor2": "1.6.0",
|
||||
"tsc-alias": "1.8.11",
|
||||
"tsc-alias": "1.8.15",
|
||||
"tsconfig-paths": "4.2.0",
|
||||
"typescript": "5.8.2",
|
||||
"typescript": "5.8.3",
|
||||
"uuid": "11.1.0",
|
||||
"json5": "2.2.3",
|
||||
"vite": "6.2.4",
|
||||
|
@ -40,15 +40,15 @@
|
|||
"devDependencies": {
|
||||
"@misskey-dev/summaly": "5.2.0",
|
||||
"@testing-library/vue": "8.1.0",
|
||||
"@types/estree": "1.0.6",
|
||||
"@types/estree": "1.0.7",
|
||||
"@types/micromatch": "4.0.9",
|
||||
"@types/node": "22.13.11",
|
||||
"@types/node": "22.14.0",
|
||||
"@types/punycode.js": "npm:@types/punycode@2.1.4",
|
||||
"@types/tinycolor2": "1.4.6",
|
||||
"@types/ws": "8.18.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.27.0",
|
||||
"@typescript-eslint/parser": "8.27.0",
|
||||
"@vitest/coverage-v8": "3.0.9",
|
||||
"@types/ws": "8.18.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.29.1",
|
||||
"@typescript-eslint/parser": "8.29.1",
|
||||
"@vitest/coverage-v8": "3.1.1",
|
||||
"@vue/runtime-core": "3.5.13",
|
||||
"acorn": "8.14.1",
|
||||
"cross-env": "7.0.3",
|
||||
|
@ -64,7 +64,7 @@
|
|||
"start-server-and-test": "2.0.11",
|
||||
"vite-plugin-turbosnap": "1.0.3",
|
||||
"vue-component-type-helpers": "2.2.8",
|
||||
"vue-eslint-parser": "10.1.1",
|
||||
"vue-eslint-parser": "10.1.3",
|
||||
"vue-tsc": "2.2.8"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,14 +21,14 @@
|
|||
"lint": "pnpm typecheck && pnpm eslint"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "22.13.11",
|
||||
"@typescript-eslint/eslint-plugin": "8.27.0",
|
||||
"@typescript-eslint/parser": "8.27.0",
|
||||
"esbuild": "0.25.1",
|
||||
"@types/node": "22.14.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.29.1",
|
||||
"@typescript-eslint/parser": "8.29.1",
|
||||
"esbuild": "0.25.2",
|
||||
"eslint-plugin-vue": "10.0.0",
|
||||
"nodemon": "3.1.9",
|
||||
"typescript": "5.8.2",
|
||||
"vue-eslint-parser": "10.1.1"
|
||||
"typescript": "5.8.3",
|
||||
"vue-eslint-parser": "10.1.3"
|
||||
},
|
||||
"files": [
|
||||
"js-built"
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
"@rollup/plugin-json": "6.1.0",
|
||||
"@rollup/plugin-replace": "6.0.2",
|
||||
"@rollup/pluginutils": "5.1.4",
|
||||
"@sentry/vue": "9.8.0",
|
||||
"@sentry/vue": "9.12.0",
|
||||
"@syuilo/aiscript": "0.19.0",
|
||||
"@tabler/icons-webfont": "3.31.0",
|
||||
"@twemoji/parser": "15.1.1",
|
||||
|
@ -33,7 +33,7 @@
|
|||
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.15",
|
||||
"analytics": "0.8.16",
|
||||
"astring": "1.9.0",
|
||||
"broadcast-channel": "7.0.0",
|
||||
"broadcast-channel": "7.1.0",
|
||||
"buraha": "0.0.1",
|
||||
"canvas-confetti": "1.9.3",
|
||||
"chart.js": "4.4.8",
|
||||
|
@ -41,7 +41,7 @@
|
|||
"chartjs-chart-matrix": "2.1.1",
|
||||
"chartjs-plugin-gradient": "0.6.1",
|
||||
"chartjs-plugin-zoom": "2.2.0",
|
||||
"chromatic": "11.27.0",
|
||||
"chromatic": "11.28.0",
|
||||
"compare-versions": "6.1.1",
|
||||
"cropperjs": "2.0.0",
|
||||
"date-fns": "4.1.0",
|
||||
|
@ -60,18 +60,18 @@
|
|||
"misskey-reversi": "workspace:*",
|
||||
"photoswipe": "5.4.4",
|
||||
"punycode.js": "2.3.1",
|
||||
"rollup": "4.36.0",
|
||||
"rollup": "4.39.0",
|
||||
"sanitize-html": "2.15.0",
|
||||
"sass": "1.86.0",
|
||||
"shiki": "3.2.1",
|
||||
"sass": "1.86.3",
|
||||
"shiki": "3.2.2",
|
||||
"strict-event-emitter-types": "2.0.0",
|
||||
"textarea-caret": "3.1.0",
|
||||
"three": "0.174.0",
|
||||
"three": "0.175.0",
|
||||
"throttle-debounce": "5.0.2",
|
||||
"tinycolor2": "1.6.0",
|
||||
"tsc-alias": "1.8.11",
|
||||
"tsc-alias": "1.8.15",
|
||||
"tsconfig-paths": "4.2.0",
|
||||
"typescript": "5.8.2",
|
||||
"typescript": "5.8.3",
|
||||
"uuid": "11.1.0",
|
||||
"v-code-diff": "1.13.1",
|
||||
"vite": "6.2.4",
|
||||
|
@ -81,44 +81,44 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@misskey-dev/summaly": "5.2.0",
|
||||
"@storybook/addon-actions": "8.6.7",
|
||||
"@storybook/addon-essentials": "8.6.7",
|
||||
"@storybook/addon-interactions": "8.6.7",
|
||||
"@storybook/addon-links": "8.6.7",
|
||||
"@storybook/addon-mdx-gfm": "8.6.7",
|
||||
"@storybook/addon-storysource": "8.6.7",
|
||||
"@storybook/blocks": "8.6.7",
|
||||
"@storybook/components": "8.6.7",
|
||||
"@storybook/core-events": "8.6.7",
|
||||
"@storybook/manager-api": "8.6.7",
|
||||
"@storybook/preview-api": "8.6.7",
|
||||
"@storybook/react": "8.6.7",
|
||||
"@storybook/react-vite": "8.6.7",
|
||||
"@storybook/test": "8.6.7",
|
||||
"@storybook/theming": "8.6.7",
|
||||
"@storybook/types": "8.6.7",
|
||||
"@storybook/vue3": "8.6.7",
|
||||
"@storybook/vue3-vite": "8.6.7",
|
||||
"@storybook/addon-actions": "8.6.12",
|
||||
"@storybook/addon-essentials": "8.6.12",
|
||||
"@storybook/addon-interactions": "8.6.12",
|
||||
"@storybook/addon-links": "8.6.12",
|
||||
"@storybook/addon-mdx-gfm": "8.6.12",
|
||||
"@storybook/addon-storysource": "8.6.12",
|
||||
"@storybook/blocks": "8.6.12",
|
||||
"@storybook/components": "8.6.12",
|
||||
"@storybook/core-events": "8.6.12",
|
||||
"@storybook/manager-api": "8.6.12",
|
||||
"@storybook/preview-api": "8.6.12",
|
||||
"@storybook/react": "8.6.12",
|
||||
"@storybook/react-vite": "8.6.12",
|
||||
"@storybook/test": "8.6.12",
|
||||
"@storybook/theming": "8.6.12",
|
||||
"@storybook/types": "8.6.12",
|
||||
"@storybook/vue3": "8.6.12",
|
||||
"@storybook/vue3-vite": "8.6.12",
|
||||
"@testing-library/vue": "8.1.0",
|
||||
"@types/canvas-confetti": "1.9.0",
|
||||
"@types/estree": "1.0.6",
|
||||
"@types/estree": "1.0.7",
|
||||
"@types/matter-js": "0.19.8",
|
||||
"@types/micromatch": "4.0.9",
|
||||
"@types/node": "22.13.11",
|
||||
"@types/node": "22.14.0",
|
||||
"@types/punycode.js": "npm:@types/punycode@2.1.4",
|
||||
"@types/sanitize-html": "2.13.0",
|
||||
"@types/sanitize-html": "2.15.0",
|
||||
"@types/seedrandom": "3.0.8",
|
||||
"@types/throttle-debounce": "5.0.2",
|
||||
"@types/tinycolor2": "1.4.6",
|
||||
"@types/ws": "8.18.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.27.0",
|
||||
"@typescript-eslint/parser": "8.27.0",
|
||||
"@vitest/coverage-v8": "3.0.9",
|
||||
"@types/ws": "8.18.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.29.1",
|
||||
"@typescript-eslint/parser": "8.29.1",
|
||||
"@vitest/coverage-v8": "3.1.1",
|
||||
"@vue/compiler-core": "3.5.13",
|
||||
"@vue/runtime-core": "3.5.13",
|
||||
"acorn": "8.14.1",
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "14.2.0",
|
||||
"cypress": "14.3.0",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-vue": "10.0.0",
|
||||
"fast-glob": "3.3.3",
|
||||
|
@ -130,18 +130,17 @@
|
|||
"msw-storybook-addon": "2.0.4",
|
||||
"nodemon": "3.1.9",
|
||||
"prettier": "3.5.3",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"seedrandom": "3.0.5",
|
||||
"start-server-and-test": "2.0.11",
|
||||
"storybook": "8.6.7",
|
||||
"storybook": "8.6.12",
|
||||
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
|
||||
"vite-node": "3.0.9",
|
||||
"vite-plugin-turbosnap": "1.0.3",
|
||||
"vitest": "3.0.9",
|
||||
"vitest": "3.1.1",
|
||||
"vitest-fetch-mock": "0.4.5",
|
||||
"vue-component-type-helpers": "2.2.8",
|
||||
"vue-eslint-parser": "10.1.1",
|
||||
"vue-eslint-parser": "10.1.3",
|
||||
"vue-tsc": "2.2.8"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -177,7 +177,8 @@ const menuDef = computed<SuperMenuDef[]>(() => [{
|
|||
action: async () => {
|
||||
const { canceled } = await os.confirm({
|
||||
type: 'warning',
|
||||
text: i18n.ts.logoutConfirm,
|
||||
title: i18n.ts.logoutConfirm,
|
||||
text: i18n.ts.logoutWillClearClientData,
|
||||
});
|
||||
if (canceled) return;
|
||||
signout();
|
||||
|
|
|
@ -4,28 +4,48 @@
|
|||
*/
|
||||
|
||||
import { apiUrl } from '@@/js/config.js';
|
||||
import { defaultMemoryStorage } from '@/memory-storage';
|
||||
import { cloudBackup } from '@/preferences/utility.js';
|
||||
import { store } from '@/store.js';
|
||||
import { waiting } from '@/os.js';
|
||||
import { unisonReload, reloadChannel } from '@/utility/unison-reload.js';
|
||||
import { unisonReload } from '@/utility/unison-reload.js';
|
||||
import { clear } from '@/utility/idb-proxy.js';
|
||||
import { $i } from '@/i.js';
|
||||
|
||||
export async function signout() {
|
||||
if (!$i) return;
|
||||
|
||||
// TODO: preferの自動バックアップがオンの場合、いろいろ消す前に強制バックアップ
|
||||
|
||||
waiting();
|
||||
|
||||
localStorage.clear();
|
||||
defaultMemoryStorage.clear();
|
||||
if (store.s.enablePreferencesAutoCloudBackup) {
|
||||
await cloudBackup();
|
||||
}
|
||||
|
||||
const idbPromises = ['MisskeyClient', 'keyval-store'].map((name, i, arr) => new Promise<void>((res, rej) => {
|
||||
localStorage.clear();
|
||||
|
||||
const idbAbortController = new AbortController();
|
||||
const timeout = window.setTimeout(() => idbAbortController.abort(), 5000);
|
||||
|
||||
const idbPromises = ['MisskeyClient'].map((name, i, arr) => new Promise<void>((res, rej) => {
|
||||
const delidb = indexedDB.deleteDatabase(name);
|
||||
delidb.onsuccess = () => res();
|
||||
delidb.onerror = e => rej(e);
|
||||
delidb.onblocked = () => idbAbortController.signal.aborted && rej(new Error('Operation aborted'));
|
||||
}));
|
||||
|
||||
await Promise.all(idbPromises);
|
||||
try {
|
||||
await Promise.race([
|
||||
Promise.all([
|
||||
...idbPromises,
|
||||
// idb keyval-storeはidb-keyvalライブラリによる別管理
|
||||
clear(),
|
||||
]),
|
||||
new Promise((_, rej) => idbAbortController.signal.addEventListener('abort', () => rej(new Error('Operation timed out')))),
|
||||
]);
|
||||
} catch {
|
||||
// nothing
|
||||
} finally {
|
||||
window.clearTimeout(timeout);
|
||||
}
|
||||
|
||||
//#region Remove service worker registration
|
||||
try {
|
||||
|
@ -50,7 +70,9 @@ export async function signout() {
|
|||
.then(registrations => {
|
||||
return Promise.all(registrations.map(registration => registration.unregister()));
|
||||
});
|
||||
} catch (err) {}
|
||||
} catch {
|
||||
// nothing
|
||||
}
|
||||
//#endregion
|
||||
|
||||
unisonReload('/');
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
get as iget,
|
||||
set as iset,
|
||||
del as idel,
|
||||
clear as iclear,
|
||||
} from 'idb-keyval';
|
||||
import { miLocalStorage } from '@/local-storage.js';
|
||||
|
||||
|
@ -51,3 +52,7 @@ export async function del(key: string) {
|
|||
if (idbAvailable) return idel(key);
|
||||
return miLocalStorage.removeItem(`${PREFIX}${key}`);
|
||||
}
|
||||
|
||||
export async function clear() {
|
||||
if (idbAvailable) return iclear();
|
||||
}
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({});
|
|
@ -24,13 +24,13 @@
|
|||
"devDependencies": {
|
||||
"@types/matter-js": "0.19.8",
|
||||
"@types/seedrandom": "3.0.8",
|
||||
"@types/node": "22.13.11",
|
||||
"@typescript-eslint/eslint-plugin": "8.27.0",
|
||||
"@typescript-eslint/parser": "8.27.0",
|
||||
"@types/node": "22.14.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.29.1",
|
||||
"@typescript-eslint/parser": "8.29.1",
|
||||
"nodemon": "3.1.9",
|
||||
"execa": "9.5.2",
|
||||
"typescript": "5.8.2",
|
||||
"esbuild": "0.25.1",
|
||||
"typescript": "5.8.3",
|
||||
"esbuild": "0.25.2",
|
||||
"glob": "11.0.1"
|
||||
},
|
||||
"files": [
|
||||
|
|
|
@ -22,13 +22,13 @@
|
|||
"lint": "pnpm typecheck && pnpm eslint"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "22.13.11",
|
||||
"@typescript-eslint/eslint-plugin": "8.27.0",
|
||||
"@typescript-eslint/parser": "8.27.0",
|
||||
"@types/node": "22.14.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.29.1",
|
||||
"@typescript-eslint/parser": "8.29.1",
|
||||
"execa": "9.5.2",
|
||||
"nodemon": "3.1.9",
|
||||
"typescript": "5.8.2",
|
||||
"esbuild": "0.25.1",
|
||||
"typescript": "5.8.3",
|
||||
"esbuild": "0.25.2",
|
||||
"glob": "11.0.1"
|
||||
},
|
||||
"files": [
|
||||
|
|
|
@ -9,16 +9,16 @@
|
|||
"lint": "pnpm typecheck && pnpm eslint"
|
||||
},
|
||||
"dependencies": {
|
||||
"esbuild": "0.25.1",
|
||||
"esbuild": "0.25.2",
|
||||
"idb-keyval": "6.2.1",
|
||||
"misskey-js": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/parser": "8.27.0",
|
||||
"@typescript-eslint/parser": "8.29.1",
|
||||
"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.74",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"nodemon": "3.1.9",
|
||||
"typescript": "5.8.2"
|
||||
"typescript": "5.8.3"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
|
|
3857
pnpm-lock.yaml
3857
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue