Merge tag '2024.3.1-io.4b' into host

This commit is contained in:
まっちゃとーにゅ 2024-03-30 17:31:59 +09:00
commit 1c111aeab7
No known key found for this signature in database
GPG key ID: 6AFBBF529601C1DB
19 changed files with 1154 additions and 1159 deletions

View file

@ -1,12 +1,12 @@
{
"name": "misskey",
"version": "2024.3.1-host.4a",
"version": "2024.3.1-host.4b",
"codename": "nasubi",
"repository": {
"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",

View file

@ -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",

View file

@ -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,
});
this.globalEventService.publishInternalEvent('userChangeDeletedState', { id: user.id, isDeleted: true });
await this.usersRepository.update(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

View file

@ -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');
}

View file

@ -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 { InstanceActorService } from '@/core/InstanceActorService.js';
import { bindThis } from '@/decorators.js';
import UsersChart from '@/core/chart/charts/users.js';
import { UtilityService } from '@/core/UtilityService.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 { UserEntityService } from '@/core/entities/UserEntityService.js';
import UsersChart from '@/core/chart/charts/users.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');
this.usersChart.update(account, true);
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 };
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()}`);
}
}
}

View file

@ -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<typeof meta, typeof paramDef> { // 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);
});
}
}

View file

@ -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

View file

@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<link rel="preload" href="https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/about-icon.png?raw=true" as="image" type="image/png" crossorigin="anonymous">
<link rel="preload" href="https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true" as="image" type="image/jpeg" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/@tabler/icons-webfont@2.46.0/tabler-icons.min.css">
<link rel="stylesheet" href="https://unpkg.com/@tabler/icons-webfont@3.1.0/dist/tabler-icons.min.css">
<link rel="stylesheet" href="https://unpkg.com/@fontsource/m-plus-rounded-1c/index.css">
<style>
html {

View file

@ -17,7 +17,7 @@
"lint": "pnpm typecheck && pnpm eslint"
},
"dependencies": {
"@discordapp/twemoji": "15.0.2",
"@discordapp/twemoji": "15.0.3",
"@github/webauthn-json": "2.1.1",
"@isaacs/ttlcache": "1.4.1",
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
@ -27,8 +27,8 @@
"@rollup/plugin-typescript": "11.1.6",
"@rollup/pluginutils": "5.1.0",
"@syuilo/aiscript": "0.17.0",
"@tabler/icons-webfont": "2.47.0",
"@twemoji/parser": "15.0.0",
"@tabler/icons-webfont": "3.1.0",
"@twemoji/parser": "15.1.0",
"@vitejs/plugin-vue": "5.0.4",
"@vue/compiler-sfc": "3.4.15",
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.2",
@ -41,7 +41,7 @@
"chartjs-chart-matrix": "2.0.1",
"chartjs-plugin-gradient": "0.6.1",
"chartjs-plugin-zoom": "2.0.1",
"chromatic": "11.2.0",
"chromatic": "11.3.0",
"compare-versions": "6.1.0",
"cropperjs": "2.0.0-beta.4",
"date-fns": "3.6.0",
@ -59,13 +59,13 @@
"misskey-reversi": "workspace:*",
"photoswipe": "5.4.3",
"punycode": "2.3.1",
"rollup": "4.13.0",
"rollup": "4.13.2",
"sanitize-html": "2.13.0",
"sass": "1.72.0",
"shiki": "1.2.0",
"shiki": "1.2.1",
"strict-event-emitter-types": "2.0.0",
"textarea-caret": "3.1.0",
"three": "0.162.0",
"three": "0.163.0",
"throttle-debounce": "5.0.0",
"tinycolor2": "1.6.0",
"tsc-alias": "1.8.8",
@ -73,45 +73,45 @@
"typescript": "5.4.3",
"uuid": "9.0.1",
"v-code-diff": "1.11.0",
"vite": "5.2.2",
"vite": "5.2.7",
"vue": "3.4.15",
"vuedraggable": "next"
},
"devDependencies": {
"@misskey-dev/eslint-plugin": "1.0.0",
"@misskey-dev/summaly": "5.1.0",
"@storybook/addon-actions": "8.0.4",
"@storybook/addon-essentials": "8.0.4",
"@storybook/addon-interactions": "8.0.4",
"@storybook/addon-links": "8.0.4",
"@storybook/addon-mdx-gfm": "8.0.4",
"@storybook/addon-storysource": "8.0.4",
"@storybook/blocks": "8.0.4",
"@storybook/components": "8.0.4",
"@storybook/core-events": "8.0.4",
"@storybook/manager-api": "8.0.4",
"@storybook/preview-api": "8.0.4",
"@storybook/react": "8.0.4",
"@storybook/react-vite": "8.0.4",
"@storybook/test": "8.0.4",
"@storybook/theming": "8.0.4",
"@storybook/types": "8.0.4",
"@storybook/vue3": "8.0.4",
"@storybook/vue3-vite": "8.0.4",
"@storybook/addon-actions": "8.0.5",
"@storybook/addon-essentials": "8.0.5",
"@storybook/addon-interactions": "8.0.5",
"@storybook/addon-links": "8.0.5",
"@storybook/addon-mdx-gfm": "8.0.5",
"@storybook/addon-storysource": "8.0.5",
"@storybook/blocks": "8.0.5",
"@storybook/components": "8.0.5",
"@storybook/core-events": "8.0.5",
"@storybook/manager-api": "8.0.5",
"@storybook/preview-api": "8.0.5",
"@storybook/react": "8.0.5",
"@storybook/react-vite": "8.0.5",
"@storybook/test": "8.0.5",
"@storybook/theming": "8.0.5",
"@storybook/types": "8.0.5",
"@storybook/vue3": "8.0.5",
"@storybook/vue3-vite": "8.0.5",
"@testing-library/vue": "8.0.3",
"@types/escape-regexp": "0.0.3",
"@types/estree": "1.0.5",
"@types/matter-js": "0.19.6",
"@types/micromatch": "4.0.6",
"@types/node": "20.11.30",
"@types/node": "20.12.2",
"@types/punycode": "2.1.4",
"@types/sanitize-html": "2.11.0",
"@types/throttle-debounce": "5.0.2",
"@types/tinycolor2": "1.4.6",
"@types/uuid": "9.0.8",
"@types/ws": "8.5.10",
"@typescript-eslint/eslint-plugin": "7.3.1",
"@typescript-eslint/parser": "7.3.1",
"@typescript-eslint/eslint-plugin": "7.4.0",
"@typescript-eslint/parser": "7.4.0",
"@vitest/coverage-v8": "0.34.6",
"@vue/runtime-core": "3.4.15",
"acorn": "8.11.3",
@ -119,19 +119,19 @@
"cypress": "13.7.1",
"eslint": "8.57.0",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-vue": "9.23.0",
"eslint-plugin-vue": "9.24.0",
"fast-glob": "3.3.2",
"happy-dom": "13.6.2",
"intersection-observer": "0.12.2",
"micromatch": "4.0.5",
"msw": "2.2.9",
"msw": "2.2.13",
"msw-storybook-addon": "2.0.0-beta.1",
"nodemon": "3.1.0",
"prettier": "3.2.5",
"react": "18.2.0",
"react-dom": "18.2.0",
"start-server-and-test": "2.0.3",
"storybook": "8.0.4",
"storybook": "8.0.5",
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
"vite-plugin-turbosnap": "1.0.3",
"vitest": "0.34.6",

View file

@ -6,7 +6,7 @@
// devモードで起動される際index.htmlを使うときはrouterが暴発してしまってうまく読み込めない。
// よって、devモードとして起動されるときはビルド時に組み込む形としておく。
// (pnpm start時はpugファイルの中で静的リソースとして読み込むようになっており、この問題は起こっていない)
import '@tabler/icons-webfont/tabler-icons.scss';
import '@tabler/icons-webfont/dist/tabler-icons.scss';
await main();

View file

@ -58,11 +58,16 @@ SPDX-License-Identifier: AGPL-3.0-only
<FormSection>
<div class="_gaps">
<MkSwitch v-model="suspended" @update:modelValue="toggleSuspend">{{ i18n.ts.suspend }}</MkSwitch>
<div>
<MkButton v-if="user.host == null" inline style="margin-right: 8px;" @click="resetPassword"><i class="ti ti-key"></i> {{ i18n.ts.resetPassword }}</MkButton>
</div>
<MkFolder v-if="iAmModerator" defaultOpen>
<template #icon><i class="ti ti-shield"></i></template>
<template #label>{{ i18n.ts.moderation }}</template>
<div class="_gaps">
<MkSwitch v-model="suspended" @update:modelValue="toggleSuspend">{{ i18n.ts.suspend }}</MkSwitch>
<MkButton v-if="user.host == null" @click="resetPassword"><i class="ti ti-key"></i> {{ i18n.ts.resetPassword }}</MkButton>
<MkButton inline danger @click="unsetUserAvatar"><i class="ti ti-user-circle"></i> {{ i18n.ts.unsetUserAvatar }}</MkButton>
<MkButton inline danger @click="unsetUserBanner"><i class="ti ti-photo"></i> {{ i18n.ts.unsetUserBanner }}</MkButton>
</div>
</MkFolder>
<MkFolder>
<template #icon><i class="ti ti-license"></i></template>
@ -87,11 +92,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
</MkFolder>
<div class="_buttons">
<MkButton v-if="iAmModerator" inline danger @click="unsetUserAvatar"><i class="ti ti-user-circle"></i> {{ i18n.ts.unsetUserAvatar }}</MkButton>
<MkButton v-if="iAmModerator" inline danger @click="unsetUserBanner"><i class="ti ti-photo"></i> {{ i18n.ts.unsetUserBanner }}</MkButton>
</div>
<MkButton v-if="$i.isAdmin" inline danger @click="deleteAccount">{{ i18n.ts.deleteAccount }}</MkButton>
<MkFolder v-if="$i.isAdmin">
<template #icon><i class="ti ti-user-x"></i></template>
<template #label>{{ i18n.ts.deleteAccount }}</template>
<div class="_gaps">
<MkButton inline danger @click="deleteAccount(true)"><i class="ti ti-user-x"></i> {{ i18n.ts.deleteAccount }}</MkButton>
<MkButton inline danger @click="deleteAccount(false)"><i class="ti ti-file-shredder"></i> {{ i18n.ts.deleteAccount }} ({{ i18n.ts.all }})</MkButton>
</div>
</MkFolder>
</div>
</FormSection>
</div>
@ -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({

View file

@ -20,13 +20,15 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #default="{ items: users }">
<MkDateSeparatedList v-slot="{ item }" :items="toMisskeyEntity(users)" :noGap="true">
<div v-if="item.user" :key="item.id" style="display: flex; gap: 8px; padding-right: 16px">
<MkA :to="userPage(item.user)" style="flex-grow: 1;">
<MkUserCardMini :user="item.user" :withChart="false" style="background: inherit; border-radius: unset;"/>
<div v-if="item.user" :key="item.id" style="display: grid; grid-template-columns: auto 56px; grid-column-gap: 8px;">
<MkA :to="userPage(item.user)" style="overflow: hidden;">
<MkUserCardMini :user="item.user" :withChart="false" style="text-overflow: ellipsis; background: inherit; border-radius: unset;"/>
</MkA>
<button v-tooltip.noDelay="i18n.ts.note" class="_button" :class="$style.post" @click="os.post({initialText: `@${item.user.username}${item.user.host ? `@${item.user.host}` : ''} `})">
<i class="ti-fw ti ti-confetti" :class="$style.postIcon"></i>
</button>
<div style="display: flex; margin-right: 16px;">
<button v-tooltip.noDelay="i18n.ts.note" class="_button" :class="$style.post" @click="os.post({initialText: `@${item.user.username}${item.user.host ? `@${item.user.host}` : ''} `})">
<i class="ti-fw ti ti-confetti" :class="$style.postIcon"></i>
</button>
</div>
</div>
</MkDateSeparatedList>
</template>

View file

@ -26,10 +26,10 @@
"devDependencies": {
"@misskey-dev/eslint-plugin": "1.0.0",
"@types/matter-js": "0.19.6",
"@types/node": "20.11.30",
"@types/node": "20.12.2",
"@types/seedrandom": "3.0.8",
"@typescript-eslint/eslint-plugin": "7.3.1",
"@typescript-eslint/parser": "7.3.1",
"@typescript-eslint/eslint-plugin": "7.4.0",
"@typescript-eslint/parser": "7.4.0",
"eslint": "8.57.0",
"nodemon": "3.1.0",
"typescript": "5.4.3"
@ -40,7 +40,7 @@
"dependencies": {
"esbuild": "0.20.2",
"eventemitter3": "5.0.1",
"glob": "^10.3.10",
"glob": "^10.3.12",
"matter-js": "0.19.0",
"seedrandom": "3.0.5"
}

View file

@ -9,9 +9,9 @@
"devDependencies": {
"@misskey-dev/eslint-plugin": "^1.0.0",
"@readme/openapi-parser": "2.5.0",
"@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",
"eslint": "8.57.0",
"openapi-types": "12.1.3",
"openapi-typescript": "6.7.5",

View file

@ -1,7 +1,7 @@
{
"type": "module",
"name": "misskey-js",
"version": "2024.3.1-host.4a",
"version": "2024.3.1-host.4b",
"description": "Misskey SDK for JavaScript",
"types": "./built/dts/index.d.ts",
"exports": {
@ -39,9 +39,9 @@
"@misskey-dev/eslint-plugin": "1.0.0",
"@swc/jest": "0.2.36",
"@types/jest": "29.5.12",
"@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",
"eslint": "8.57.0",
"jest": "29.7.0",
"jest-fetch-mock": "3.0.3",

View file

@ -5423,6 +5423,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;
};
};
};

View file

@ -25,9 +25,9 @@
},
"devDependencies": {
"@misskey-dev/eslint-plugin": "1.0.0",
"@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",
"eslint": "8.57.0",
"nodemon": "3.1.0",
"typescript": "5.4.3"
@ -35,7 +35,7 @@
"dependencies": {
"crc-32": "1.2.2",
"esbuild": "0.20.2",
"glob": "10.3.10"
"glob": "10.3.12"
},
"files": [
"built"

View file

@ -16,7 +16,7 @@
"devDependencies": {
"@misskey-dev/eslint-plugin": "1.0.0",
"@types/serviceworker": "0.0.84",
"@typescript-eslint/parser": "7.3.1",
"@typescript-eslint/parser": "7.4.0",
"eslint": "8.57.0",
"eslint-plugin-import": "2.29.1",
"nodemon": "3.1.0",

1937
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff