From acc10c070953040bad7e59f806ff1daff421f73e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=BE=E3=81=A3=E3=81=A1=E3=82=83=E3=81=A8=E3=83=BC?= =?UTF-8?q?=E3=81=AB=E3=82=85?= <17376330+u1-liquid@users.noreply.github.com> Date: Sat, 30 Mar 2024 15:55:16 +0900 Subject: [PATCH 1/5] =?UTF-8?q?fix(backend):=20=E3=82=A2=E3=82=AB=E3=82=A6?= =?UTF-8?q?=E3=83=B3=E3=83=88=E3=81=AE=E4=BD=9C=E6=88=90=E3=81=A8=E5=89=8A?= =?UTF-8?q?=E9=99=A4=E3=81=AE=E9=80=94=E4=B8=AD=E3=81=A7=E3=83=AA=E3=83=88?= =?UTF-8?q?=E3=83=A9=E3=82=A4=E3=81=8C=E7=99=BA=E7=94=9F=E3=81=97=E3=81=A6?= =?UTF-8?q?=E3=82=82=E7=84=A1=E8=A6=96=E3=81=99=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=20(MisskeyIO#580)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/src/core/DeleteAccountService.ts | 44 +++++-- .../src/core/FetchInstanceMetadataService.ts | 5 +- packages/backend/src/core/SignupService.ts | 115 +++++++++++------- 3 files changed, 105 insertions(+), 59 deletions(-) diff --git a/packages/backend/src/core/DeleteAccountService.ts b/packages/backend/src/core/DeleteAccountService.ts index 385f54b42b74..d0fed760cb9f 100644 --- a/packages/backend/src/core/DeleteAccountService.ts +++ b/packages/backend/src/core/DeleteAccountService.ts @@ -4,6 +4,7 @@ */ import { Inject, Injectable } from '@nestjs/common'; +import * as Redis from 'ioredis'; import { bindThis } from '@/decorators.js'; import { DI } from '@/di-symbols.js'; import type Logger from '@/logger.js'; @@ -20,6 +21,8 @@ export class DeleteAccountService { public logger: Logger; constructor( + @Inject(DI.redis) + private redisClient: Redis.Redis, @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -29,7 +32,7 @@ export class DeleteAccountService { private globalEventService: GlobalEventService, private loggerService: LoggerService, ) { - this.logger = this.loggerService.getLogger('delete-account'); + this.logger = this.loggerService.getLogger('account:delete'); } @bindThis @@ -39,19 +42,38 @@ export class DeleteAccountService { const _user = await this.usersRepository.findOneByOrFail({ id: user.id }); if (_user.isRoot) throw new Error('cannot delete a root account'); - // 物理削除する前にDelete activityを送信する - await this.userSuspendService.doPostSuspend(user).catch(err => this.logger.error(err)); + // 5分間の間に同じアカウントに対して削除リクエストが複数回来た場合、最初のリクエストのみを処理する + const lock = await this.redisClient.set(`account:delete:lock:${user.id}`, Date.now(), 'EX', 60 * 5, 'NX'); + if (lock === null) { + this.logger.warn(`Delete account is already in progress for ${user.id}`); + return; + } - this.queueService.createDeleteAccountJob(user, { - force: me ? await this.roleService.isModerator(me) : false, - soft: soft, - }); + // noinspection ES6MissingAwait APIで呼び出される際にタイムアウトされないように + (async () => { + try { + // 物理削除する前にDelete activityを送信する + await this.userSuspendService.doPostSuspend(user).catch(err => this.logger.error(err)); - await this.usersRepository.update(user.id, { - isDeleted: true, - }); + // noinspection ES6MissingAwait + this.queueService.createDeleteAccountJob(user, { + force: me ? await this.roleService.isModerator(me) : false, + soft: soft, + }); + + await this.usersRepository.update(user.id, { + isDeleted: true, + }); - this.globalEventService.publishInternalEvent('userChangeDeletedState', { id: user.id, isDeleted: true }); + this.globalEventService.publishInternalEvent('userChangeDeletedState', { id: user.id, isDeleted: true }); + } catch (err) { + this.logger.error(`Failed to delete account ${user.id}, request by ${me ? me.id : 'remote'} (soft: ${soft})`, { error: err }); + // すでにcallstackから離れてるので、ここでエラーをthrowしても意味がない + } finally { + // 成功・失敗に関わらずロックを解除 + await this.redisClient.unlink(`account:delete:lock:${user.id}`); + } + })(); } @bindThis diff --git a/packages/backend/src/core/FetchInstanceMetadataService.ts b/packages/backend/src/core/FetchInstanceMetadataService.ts index ffc17fee19fe..ceca6a27d8c1 100644 --- a/packages/backend/src/core/FetchInstanceMetadataService.ts +++ b/packages/backend/src/core/FetchInstanceMetadataService.ts @@ -41,11 +41,12 @@ export class FetchInstanceMetadataService { private logger: Logger; constructor( + @Inject(DI.redis) + private redisClient: Redis.Redis, + private httpRequestService: HttpRequestService, private loggerService: LoggerService, private federatedInstanceService: FederatedInstanceService, - @Inject(DI.redis) - private redisClient: Redis.Redis, ) { this.logger = this.loggerService.getLogger('metadata', 'cyan'); } diff --git a/packages/backend/src/core/SignupService.ts b/packages/backend/src/core/SignupService.ts index 72d06c3dad46..76c1dc322b8a 100644 --- a/packages/backend/src/core/SignupService.ts +++ b/packages/backend/src/core/SignupService.ts @@ -6,27 +6,34 @@ import { generateKeyPair } from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; import bcrypt from 'bcryptjs'; +import * as Redis from 'ioredis'; import { DataSource, IsNull } from 'typeorm'; +import { bindThis } from '@/decorators.js'; import { DI } from '@/di-symbols.js'; +import type Logger from '@/logger.js'; +import generateUserToken from '@/misc/generate-native-user-token.js'; import type { UsedUsernamesRepository, UsersRepository } from '@/models/_.js'; import { MiUser } from '@/models/User.js'; import { MiUserProfile } from '@/models/UserProfile.js'; -import { IdService } from '@/core/IdService.js'; import { MiUserKeypair } from '@/models/UserKeypair.js'; import { MiUsedUsername } from '@/models/UsedUsername.js'; -import generateUserToken from '@/misc/generate-native-user-token.js'; -import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { IdService } from '@/core/IdService.js'; +import { MetaService } from '@/core/MetaService.js'; +import { UtilityService } from '@/core/UtilityService.js'; +import { LoggerService } from '@/core/LoggerService.js'; import { InstanceActorService } from '@/core/InstanceActorService.js'; -import { bindThis } from '@/decorators.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; import UsersChart from '@/core/chart/charts/users.js'; -import { UtilityService } from '@/core/UtilityService.js'; -import { MetaService } from '@/core/MetaService.js'; @Injectable() export class SignupService { + public logger: Logger; + constructor( @Inject(DI.db) private db: DataSource, + @Inject(DI.redis) + private redisClient: Redis.Redis, @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -34,13 +41,15 @@ export class SignupService { @Inject(DI.usedUsernamesRepository) private usedUsernamesRepository: UsedUsernamesRepository, - private utilityService: UtilityService, - private userEntityService: UserEntityService, private idService: IdService, private metaService: MetaService, + private utilityService: UtilityService, + private loggerService: LoggerService, private instanceActorService: InstanceActorService, + private userEntityService: UserEntityService, private usersChart: UsersChart, ) { + this.logger = this.loggerService.getLogger('account:create'); } @bindThis @@ -110,47 +119,61 @@ export class SignupService { err ? rej(err) : res([publicKey, privateKey]), )); - let account!: MiUser; + // 5分間のロックを取得 + const lock = await this.redisClient.set(`account:create:lock:${username.toLowerCase()}`, Date.now(), 'EX', 60 * 5, 'NX'); + if (lock === null) { + throw new Error('ALREADY_IN_PROGRESS'); + } - // Start transaction - await this.db.transaction(async transactionalEntityManager => { - const exist = await transactionalEntityManager.findOneBy(MiUser, { - usernameLower: username.toLowerCase(), - host: IsNull(), + try { + let account!: MiUser; + + // Start transaction + await this.db.transaction(async transactionalEntityManager => { + const exist = await transactionalEntityManager.findOneBy(MiUser, { + usernameLower: username.toLowerCase(), + host: IsNull(), + }); + + if (exist) throw new Error(' the username is already used'); + + account = await transactionalEntityManager.save(new MiUser({ + id: this.idService.gen(), + username: username, + usernameLower: username.toLowerCase(), + host: this.utilityService.toPunyNullable(host), + token: secret, + isRoot: isTheFirstUser, + })); + + await transactionalEntityManager.save(new MiUserKeypair({ + publicKey: keyPair[0], + privateKey: keyPair[1], + userId: account.id, + })); + + await transactionalEntityManager.save(new MiUserProfile({ + userId: account.id, + autoAcceptFollowed: true, + password: hash, + })); + + await transactionalEntityManager.save(new MiUsedUsername({ + createdAt: new Date(), + username: username.toLowerCase(), + })); }); - if (exist) throw new Error(' the username is already used'); - - account = await transactionalEntityManager.save(new MiUser({ - id: this.idService.gen(), - username: username, - usernameLower: username.toLowerCase(), - host: this.utilityService.toPunyNullable(host), - token: secret, - isRoot: isTheFirstUser, - })); - - await transactionalEntityManager.save(new MiUserKeypair({ - publicKey: keyPair[0], - privateKey: keyPair[1], - userId: account.id, - })); - - await transactionalEntityManager.save(new MiUserProfile({ - userId: account.id, - autoAcceptFollowed: true, - password: hash, - })); - - await transactionalEntityManager.save(new MiUsedUsername({ - createdAt: new Date(), - username: username.toLowerCase(), - })); - }); - - this.usersChart.update(account, true); - - return { account, secret }; + this.usersChart.update(account, true); + + return { account, secret }; + } catch (err) { + this.logger.error(`Failed to create account ${username}`, { error: err }); + throw err; + } finally { + // 成功・失敗に関わらずロックを解除 + await this.redisClient.unlink(`account:create:lock:${username.toLowerCase()}`); + } } } From b4420f895eda5c3661df273c5059a5923793432d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=BE=E3=81=A3=E3=81=A1=E3=82=83=E3=81=A8=E3=83=BC?= =?UTF-8?q?=E3=81=AB=E3=82=85?= <17376330+u1-liquid@users.noreply.github.com> Date: Sat, 30 Mar 2024 15:55:36 +0900 Subject: [PATCH 2/5] =?UTF-8?q?enhance(moderation):=20=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E8=80=85=E3=81=8C=E3=82=A2=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=81=AE=E7=89=A9=E7=90=86=E5=89=8A=E9=99=A4=E3=83=BB=E8=AB=96?= =?UTF-8?q?=E7=90=86=E5=89=8A=E9=99=A4=E3=82=92=E9=81=B8=E3=81=B9=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=20(MisskeyIO#581)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/endpoints/admin/accounts/delete.ts | 4 +-- packages/frontend/src/pages/admin-user.vue | 31 ++++++++++++------- packages/misskey-js/src/autogen/types.ts | 5 +++ 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts index ed7a1424dde8..ef66e54e2c5e 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts @@ -37,6 +37,7 @@ export const paramDef = { type: 'object', properties: { userId: { type: 'string', format: 'misskey:id' }, + soft: { type: 'boolean', default: true, description: 'Since deletion by an administrator is a moderation action, the default is to soft delete.' }, }, required: ['userId'], } as const; @@ -56,8 +57,7 @@ export default class extends Endpoint { // eslint- if (user == null) throw new ApiError(meta.errors.userNotFound); if (await this.roleService.isModerator(user)) throw new ApiError(meta.errors.cannotDeleteModerator); - // 管理者からの削除ということはモデレーション行為なので、soft delete にする - await this.deleteAccountService.deleteAccount(user, true, me); + await this.deleteAccountService.deleteAccount(user, ps.soft, me); }); } } diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue index 59dc0ba0ecf9..294807ed3db2 100644 --- a/packages/frontend/src/pages/admin-user.vue +++ b/packages/frontend/src/pages/admin-user.vue @@ -58,11 +58,16 @@ SPDX-License-Identifier: AGPL-3.0-only
- {{ i18n.ts.suspend }} - -
- {{ i18n.ts.resetPassword }} -
+ + + +
+ {{ i18n.ts.suspend }} + {{ i18n.ts.resetPassword }} + {{ i18n.ts.unsetUserAvatar }} + {{ i18n.ts.unsetUserBanner }} +
+
@@ -87,11 +92,14 @@ SPDX-License-Identifier: AGPL-3.0-only -
- {{ i18n.ts.unsetUserAvatar }} - {{ i18n.ts.unsetUserBanner }} -
- {{ i18n.ts.deleteAccount }} + + + +
+ {{ i18n.ts.deleteAccount }} + {{ i18n.ts.deleteAccount }} ({{ i18n.ts.all }}) +
+
@@ -380,7 +388,7 @@ async function deleteAllFiles() { } } -async function deleteAccount() { +async function deleteAccount(soft: boolean) { const confirm = await os.confirm({ type: 'warning', text: i18n.ts.deleteAccountConfirm, @@ -395,6 +403,7 @@ async function deleteAccount() { if (typed.result === user.value?.username) { await os.apiWithDialog('admin/accounts/delete', { userId: user.value.id, + soft, }).then(refreshUser); } else { os.alert({ diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 398da6bbe908..f4e5047116a4 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -5438,6 +5438,11 @@ export type operations = { 'application/json': { /** Format: misskey:id */ userId: string; + /** + * @description Since deletion by an administrator is a moderation action, the default is to soft delete. + * @default true + */ + soft?: boolean; }; }; }; From fa47a545b1b770e5f3e52bb2798d1104da5dd244 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=BE=E3=81=A3=E3=81=A1=E3=82=83=E3=81=A8=E3=83=BC?= =?UTF-8?q?=E3=81=AB=E3=82=85?= <17376330+u1-liquid@users.noreply.github.com> Date: Sat, 30 Mar 2024 15:55:50 +0900 Subject: [PATCH 3/5] =?UTF-8?q?fix(frontend/WidgetBirthdayFollowings):=20?= =?UTF-8?q?=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC=E3=81=AE=E5=90=8D=E5=89=8D?= =?UTF-8?q?=E3=81=8C=E9=95=B7=E3=81=84=E3=81=A8=E6=8A=95=E7=A8=BF=E3=83=9C?= =?UTF-8?q?=E3=82=BF=E3=83=B3=E3=81=8C=E3=81=AF=E3=81=BF=E5=87=BA=E3=81=A6?= =?UTF-8?q?=E3=81=97=E3=81=BE=E3=81=86=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=20(MisskeyIO#582)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/widgets/WidgetBirthdayFollowings.vue | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue index 1247a02afe90..b983301ea59a 100644 --- a/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue +++ b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue @@ -20,13 +20,15 @@ SPDX-License-Identifier: AGPL-3.0-only From 962653e5ac31ee687228cac4fe32c40146fcffb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=BE=E3=81=A3=E3=81=A1=E3=82=83=E3=81=A8=E3=83=BC?= =?UTF-8?q?=E3=81=AB=E3=82=85?= <17376330+u1-liquid@users.noreply.github.com> Date: Sat, 30 Mar 2024 16:36:20 +0900 Subject: [PATCH 4/5] update deps (MisskeyIO#583) --- package.json | 14 +- packages/backend/package.json | 40 +- .../backend/src/server/web/views/base.pug | 2 +- .../frontend/.storybook/preview-head.html | 2 +- packages/frontend/package.json | 64 +- packages/frontend/src/_dev_boot_.ts | 2 +- packages/misskey-bubble-game/package.json | 8 +- packages/misskey-js/generator/package.json | 6 +- packages/misskey-js/package.json | 6 +- packages/misskey-reversi/package.json | 8 +- packages/sw/package.json | 2 +- pnpm-lock.yaml | 1939 ++++++++--------- 12 files changed, 1013 insertions(+), 1080 deletions(-) diff --git a/package.json b/package.json index 0f87b5aab183..e4dc8090606a 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "type": "git", "url": "https://github.com/MisskeyIO/misskey.git" }, - "packageManager": "pnpm@8.15.4", + "packageManager": "pnpm@8.15.5", "workspaces": [ "packages/frontend", "packages/backend", @@ -49,20 +49,20 @@ "@tensorflow/tfjs-core": "4.17.0", "chokidar": "3.6.0", "lodash": "4.17.21", - "sharp": "0.33.2" + "sharp": "0.33.3" }, "dependencies": { - "cssnano": "6.1.1", + "cssnano": "6.1.2", "execa": "8.0.1", "js-yaml": "4.1.0", "postcss": "8.4.38", - "terser": "5.29.2", + "terser": "5.30.0", "typescript": "5.4.3" }, "devDependencies": { - "@types/node": "20.11.30", - "@typescript-eslint/eslint-plugin": "7.3.1", - "@typescript-eslint/parser": "7.3.1", + "@types/node": "20.12.2", + "@typescript-eslint/eslint-plugin": "7.4.0", + "@typescript-eslint/parser": "7.4.0", "cross-env": "7.0.3", "cypress": "13.7.1", "eslint": "8.57.0", diff --git a/packages/backend/package.json b/packages/backend/package.json index 9884d044b4f4..03c5b5afaf80 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -66,12 +66,12 @@ }, "dependencies": { "@authenio/samlify-node-xmllint": "2.0.0", - "@aws-sdk/client-s3": "3.537.0", - "@aws-sdk/lib-storage": "3.537.0", - "@bull-board/api": "5.15.1", - "@bull-board/fastify": "5.15.1", - "@bull-board/ui": "5.15.1", - "@discordapp/twemoji": "15.0.2", + "@aws-sdk/client-s3": "3.540.0", + "@aws-sdk/lib-storage": "3.540.0", + "@bull-board/api": "5.15.3", + "@bull-board/fastify": "5.15.3", + "@bull-board/ui": "5.15.3", + "@discordapp/twemoji": "15.0.3", "@fastify/accepts": "4.3.0", "@fastify/cookie": "9.3.1", "@fastify/cors": "9.0.1", @@ -79,20 +79,20 @@ "@fastify/formbody": "7.4.0", "@fastify/http-proxy": "9.5.0", "@fastify/multipart": "8.2.0", - "@fastify/static": "7.0.1", + "@fastify/static": "7.0.2", "@fastify/view": "9.0.0", "@misskey-dev/sharp-read-bmp": "1.2.0", "@misskey-dev/summaly": "5.1.0", - "@nestjs/common": "10.3.4", - "@nestjs/core": "10.3.4", - "@nestjs/testing": "10.3.4", + "@nestjs/common": "10.3.7", + "@nestjs/core": "10.3.7", + "@nestjs/testing": "10.3.7", "@peertube/http-signature": "1.7.0", "@simplewebauthn/server": "9.0.3", "@sinonjs/fake-timers": "11.2.2", "@smithy/node-http-handler": "2.5.0", "@swc/cli": "0.1.65", "@swc/core": "1.3.107", - "@twemoji/parser": "15.0.0", + "@twemoji/parser": "15.1.0", "accepts": "1.3.8", "ajv": "8.12.0", "archiver": "6.0.1", @@ -100,7 +100,7 @@ "bcryptjs": "2.4.3", "blurhash": "2.0.5", "body-parser": "1.20.2", - "bullmq": "5.4.4", + "bullmq": "5.4.6", "cacheable-lookup": "7.0.0", "cbor": "9.0.2", "chalk": "5.3.0", @@ -166,14 +166,14 @@ "ratelimiter": "3.4.1", "re2": "1.20.10", "redis-lock": "0.1.4", - "reflect-metadata": "0.2.1", + "reflect-metadata": "0.2.2", "rename": "1.0.4", "rss-parser": "3.13.0", "rxjs": "7.8.1", "samlify": "2.8.11", "sanitize-html": "2.13.0", "secure-json-parse": "2.7.0", - "sharp": "0.33.2", + "sharp": "0.33.3", "slacc": "0.0.10", "strict-event-emitter-types": "2.0.0", "stringz": "2.1.0", @@ -194,7 +194,7 @@ "devDependencies": { "@jest/globals": "29.7.0", "@misskey-dev/eslint-plugin": "1.0.0", - "@nestjs/platform-express": "10.3.4", + "@nestjs/platform-express": "10.3.7", "@simplewebauthn/types": "9.0.1", "@swc/jest": "0.2.36", "@types/accepts": "1.3.7", @@ -213,11 +213,11 @@ "@types/jsrsasign": "10.5.13", "@types/mime-types": "2.1.4", "@types/ms": "0.7.34", - "@types/node": "20.11.30", + "@types/node": "20.12.2", "@types/node-forge": "1.3.11", "@types/nodemailer": "6.4.14", "@types/oauth": "0.9.4", - "@types/oauth2orize": "1.11.4", + "@types/oauth2orize": "1.11.5", "@types/oauth2orize-pkce": "0.1.2", "@types/pg": "8.11.4", "@types/pug": "2.0.10", @@ -235,9 +235,9 @@ "@types/vary": "1.1.3", "@types/web-push": "3.6.3", "@types/ws": "8.5.10", - "@typescript-eslint/eslint-plugin": "7.3.1", - "@typescript-eslint/parser": "7.3.1", - "aws-sdk-client-mock": "3.0.1", + "@typescript-eslint/eslint-plugin": "7.4.0", + "@typescript-eslint/parser": "7.4.0", + "aws-sdk-client-mock": "4.0.0", "cross-env": "7.0.3", "eslint": "8.57.0", "eslint-plugin-import": "2.29.1", diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug index 430dffdedc8a..0389b9793174 100644 --- a/packages/backend/src/server/web/views/base.pug +++ b/packages/backend/src/server/web/views/base.pug @@ -36,7 +36,7 @@ html link(rel='prefetch' href=infoImageUrl) link(rel='prefetch' href=notFoundImageUrl) //- https://github.com/misskey-dev/misskey/issues/9842 - link(rel='stylesheet' href=`/assets/tabler-icons.${version}/tabler-icons.min.css`) + link(rel='stylesheet' href=`/assets/tabler-icons.${version}/dist/tabler-icons.min.css`) link(rel='modulepreload' href=`/vite/${clientEntry.file}`) if !config.clientManifestExists diff --git a/packages/frontend/.storybook/preview-head.html b/packages/frontend/.storybook/preview-head.html index d2e77cb7cf54..6a06aaaa0dd0 100644 --- a/packages/frontend/.storybook/preview-head.html +++ b/packages/frontend/.storybook/preview-head.html @@ -5,7 +5,7 @@ - +