forked from woem.men/forkey
Fixes + tested with email
This commit is contained in:
parent
920a1cd8ee
commit
2d3589e4aa
19 changed files with 378 additions and 6 deletions
|
@ -43,6 +43,7 @@ alreadyFavorited: "Already added to favorites."
|
||||||
cantFavorite: "Couldn't add to favorites."
|
cantFavorite: "Couldn't add to favorites."
|
||||||
pin: "Pin to profile"
|
pin: "Pin to profile"
|
||||||
unpin: "Unpin from profile"
|
unpin: "Unpin from profile"
|
||||||
|
approvals: "Approvals"
|
||||||
copyContent: "Copy contents"
|
copyContent: "Copy contents"
|
||||||
copyLink: "Copy link"
|
copyLink: "Copy link"
|
||||||
copyLinkRenote: "Copy renote link"
|
copyLinkRenote: "Copy renote link"
|
||||||
|
@ -966,6 +967,7 @@ requireAdminForView: "You must log in with an administrator account to view this
|
||||||
isSystemAccount: "An account created and automatically operated by the system."
|
isSystemAccount: "An account created and automatically operated by the system."
|
||||||
typeToConfirm: "Please enter {x} to confirm"
|
typeToConfirm: "Please enter {x} to confirm"
|
||||||
deleteAccount: "Delete account"
|
deleteAccount: "Delete account"
|
||||||
|
pendingUserApprovals: "There are users awaiting approval."
|
||||||
approveAccount: "Approve"
|
approveAccount: "Approve"
|
||||||
denyAccount: "Deny & Delete"
|
denyAccount: "Deny & Delete"
|
||||||
approved: "Approved"
|
approved: "Approved"
|
||||||
|
|
4
locales/index.d.ts
vendored
4
locales/index.d.ts
vendored
|
@ -3891,6 +3891,10 @@ export interface Locale extends ILocale {
|
||||||
* アカウント削除
|
* アカウント削除
|
||||||
*/
|
*/
|
||||||
"deleteAccount": string;
|
"deleteAccount": string;
|
||||||
|
/**
|
||||||
|
* 承認待ちのユーザーがいます。
|
||||||
|
*/
|
||||||
|
"pendingUserApprovals": string;
|
||||||
/**
|
/**
|
||||||
* 承認する
|
* 承認する
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -968,6 +968,7 @@ requireAdminForView: "閲覧するには管理者アカウントでログイン
|
||||||
isSystemAccount: "システムにより自動で作成・管理されているアカウントです。"
|
isSystemAccount: "システムにより自動で作成・管理されているアカウントです。"
|
||||||
typeToConfirm: "この操作を行うには {x} と入力してください"
|
typeToConfirm: "この操作を行うには {x} と入力してください"
|
||||||
deleteAccount: "アカウント削除"
|
deleteAccount: "アカウント削除"
|
||||||
|
pendingUserApprovals: "承認待ちのユーザーがいます。"
|
||||||
approveAccount: "承認する"
|
approveAccount: "承認する"
|
||||||
denyAccount: "拒否と削除"
|
denyAccount: "拒否と削除"
|
||||||
approved: "承認済み"
|
approved: "承認済み"
|
||||||
|
|
|
@ -83,6 +83,7 @@ import * as ep___admin_showUser from './endpoints/admin/show-user.js';
|
||||||
import * as ep___admin_showUsers from './endpoints/admin/show-users.js';
|
import * as ep___admin_showUsers from './endpoints/admin/show-users.js';
|
||||||
import * as ep___admin_suspendUser from './endpoints/admin/suspend-user.js';
|
import * as ep___admin_suspendUser from './endpoints/admin/suspend-user.js';
|
||||||
import * as ep___admin_approveUser from './endpoints/admin/approve-user.js';
|
import * as ep___admin_approveUser from './endpoints/admin/approve-user.js';
|
||||||
|
import * as ep___admin_declineUser from "./endpoints/admin/decline-user.js";
|
||||||
import * as ep___admin_unsuspendUser from './endpoints/admin/unsuspend-user.js';
|
import * as ep___admin_unsuspendUser from './endpoints/admin/unsuspend-user.js';
|
||||||
import * as ep___admin_updateMeta from './endpoints/admin/update-meta.js';
|
import * as ep___admin_updateMeta from './endpoints/admin/update-meta.js';
|
||||||
import * as ep___admin_updateUserName from './endpoints/admin/update-user-name.js';
|
import * as ep___admin_updateUserName from './endpoints/admin/update-user-name.js';
|
||||||
|
|
|
@ -83,6 +83,7 @@ import * as ep___admin_showUser from './endpoints/admin/show-user.js';
|
||||||
import * as ep___admin_showUsers from './endpoints/admin/show-users.js';
|
import * as ep___admin_showUsers from './endpoints/admin/show-users.js';
|
||||||
import * as ep___admin_suspendUser from './endpoints/admin/suspend-user.js';
|
import * as ep___admin_suspendUser from './endpoints/admin/suspend-user.js';
|
||||||
import * as ep___admin_approveUser from './endpoints/admin/approve-user.js';
|
import * as ep___admin_approveUser from './endpoints/admin/approve-user.js';
|
||||||
|
import * as ep___admin_declineUser from './endpoints/admin/decline-user.js';
|
||||||
import * as ep___admin_unsuspendUser from './endpoints/admin/unsuspend-user.js';
|
import * as ep___admin_unsuspendUser from './endpoints/admin/unsuspend-user.js';
|
||||||
import * as ep___admin_updateMeta from './endpoints/admin/update-meta.js';
|
import * as ep___admin_updateMeta from './endpoints/admin/update-meta.js';
|
||||||
import * as ep___admin_updateUserName from './endpoints/admin/update-user-name.js';
|
import * as ep___admin_updateUserName from './endpoints/admin/update-user-name.js';
|
||||||
|
@ -477,6 +478,7 @@ const eps = [
|
||||||
['admin/show-users', ep___admin_showUsers],
|
['admin/show-users', ep___admin_showUsers],
|
||||||
['admin/suspend-user', ep___admin_suspendUser],
|
['admin/suspend-user', ep___admin_suspendUser],
|
||||||
['admin/approve-user', ep___admin_approveUser],
|
['admin/approve-user', ep___admin_approveUser],
|
||||||
|
['admin/decline-user', ep___admin_declineUser],
|
||||||
['admin/unsuspend-user', ep___admin_unsuspendUser],
|
['admin/unsuspend-user', ep___admin_unsuspendUser],
|
||||||
['admin/update-meta', ep___admin_updateMeta],
|
['admin/update-meta', ep___admin_updateMeta],
|
||||||
['admin/update-user-name', ep___admin_updateUserName],
|
['admin/update-user-name', ep___admin_updateUserName],
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import type { UsedUsernamesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
|
||||||
|
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { EmailService } from '@/core/EmailService.js';
|
||||||
|
import { DeleteAccountService } from '@/core/DeleteAccountService.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['admin'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
requireModerator: true,
|
||||||
|
kind: 'write:admin:decline-user',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
userId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['userId'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.usersRepository)
|
||||||
|
private usersRepository: UsersRepository,
|
||||||
|
|
||||||
|
@Inject(DI.userProfilesRepository)
|
||||||
|
private userProfilesRepository: UserProfilesRepository,
|
||||||
|
|
||||||
|
@Inject(DI.usedUsernamesRepository)
|
||||||
|
private usedUsernamesRepository: UsedUsernamesRepository,
|
||||||
|
|
||||||
|
private moderationLogService: ModerationLogService,
|
||||||
|
private emailService: EmailService,
|
||||||
|
private deleteAccountService: DeleteAccountService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
const user = await this.usersRepository.findOneBy({ id: ps.userId });
|
||||||
|
|
||||||
|
if (user == null || user.isDeleted) {
|
||||||
|
throw new Error('user not found or already deleted');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.approved) {
|
||||||
|
throw new Error('user is already approved');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.host) {
|
||||||
|
throw new Error('user is not local');
|
||||||
|
}
|
||||||
|
|
||||||
|
const profile = await this.userProfilesRepository.findOneBy({ userId: ps.userId });
|
||||||
|
|
||||||
|
if (profile?.email) {
|
||||||
|
this.emailService.sendEmail(profile.email, 'Account Declined',
|
||||||
|
'Your Account has been declined!',
|
||||||
|
'Your Account has been declined!');
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.usedUsernamesRepository.delete({ username: user.username });
|
||||||
|
|
||||||
|
await this.deleteAccountService.deleteAccount(user, false, me);
|
||||||
|
|
||||||
|
this.moderationLogService.log(me, 'decline', {
|
||||||
|
userId: user.id,
|
||||||
|
userUsername: user.username,
|
||||||
|
userHost: user.host,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -55,6 +55,7 @@ export const moderationLogTypes = [
|
||||||
'updateServerSettings',
|
'updateServerSettings',
|
||||||
'suspend',
|
'suspend',
|
||||||
'approve',
|
'approve',
|
||||||
|
'decline',
|
||||||
'unsuspend',
|
'unsuspend',
|
||||||
'updateUserName',
|
'updateUserName',
|
||||||
'updateUserNote',
|
'updateUserNote',
|
||||||
|
@ -117,6 +118,11 @@ export type ModerationLogPayloads = {
|
||||||
userUsername: string;
|
userUsername: string;
|
||||||
userHost: string | null;
|
userHost: string | null;
|
||||||
};
|
};
|
||||||
|
decline: {
|
||||||
|
userId: string;
|
||||||
|
userUsername: string;
|
||||||
|
userHost: string | null;
|
||||||
|
}
|
||||||
unsuspend: {
|
unsuspend: {
|
||||||
userId: string;
|
userId: string;
|
||||||
userUsername: string;
|
userUsername: string;
|
||||||
|
|
|
@ -46,8 +46,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkInput v-if="instance.approvalRequiredForSignup" v-model="reason" type="text" :spellcheck="false" required data-cy-signup-reason>
|
<MkInput v-if="instance.approvalRequiredForSignup" v-model="reason" type="text" :spellcheck="false" required data-cy-signup-reason>
|
||||||
<template #label>Reason <div v-tooltip:dialog="i18n.ts._signup.reasonInfo" class="_button _help"><i class="ph-question ph-bold ph-lg"></i></div></template>
|
<template #label>Reason <div v-tooltip:dialog="i18n.ts._signup.reasonInfo" class="_button _help"><i class="ti ti-help-circle"></i></div></template>
|
||||||
<template #prefix><i class="ti ti-comment-alt"></i></template>
|
<template #prefix><i class="ti ti-message-question"></i></template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkNewPassword ref="password" :label="i18n.ts.password"/>
|
<MkNewPassword ref="password" :label="i18n.ts.password"/>
|
||||||
<MkCaptcha v-if="instance.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" :class="$style.captcha" provider="hcaptcha" :sitekey="instance.hcaptchaSiteKey"/>
|
<MkCaptcha v-if="instance.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" :class="$style.captcha" provider="hcaptcha" :sitekey="instance.hcaptchaSiteKey"/>
|
||||||
|
|
110
packages/frontend/src/components/SkApprovalUser.vue
Normal file
110
packages/frontend/src/components/SkApprovalUser.vue
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: marie and other Sharkey contributors
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<MkFolder :expanded="false">
|
||||||
|
<template #icon><i class="ti ti-user"></i></template>
|
||||||
|
<template #label>{{ i18n.ts.user }}: {{ user.username }}</template>
|
||||||
|
|
||||||
|
<div class="_gaps_s" :class="$style.root">
|
||||||
|
<div :class="$style.items">
|
||||||
|
<div>
|
||||||
|
<div :class="$style.label">{{ i18n.ts.createdAt }}</div>
|
||||||
|
<div><MkTime :time="user.createdAt" mode="absolute"/></div>
|
||||||
|
</div>
|
||||||
|
<div v-if="email">
|
||||||
|
<div :class="$style.label">{{ i18n.ts.emailAddress }}</div>
|
||||||
|
<div>{{ email }}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div :class="$style.label">Reason</div>
|
||||||
|
<div>{{ reason }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div :class="$style.buttons">
|
||||||
|
<MkButton inline success @click="approveAccount()">{{ i18n.ts.approveAccount }}</MkButton>
|
||||||
|
<MkButton inline danger @click="deleteAccount()">{{ i18n.ts.denyAccount }}</MkButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</MkFolder>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
import { i18n } from '@/i18n.js';
|
||||||
|
import * as os from '@/os.js';
|
||||||
|
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
user: Misskey.entities.User;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
let reason = ref('');
|
||||||
|
let email = ref('');
|
||||||
|
|
||||||
|
function getReason() {
|
||||||
|
return misskeyApi('admin/show-user', {
|
||||||
|
userId: props.user.id,
|
||||||
|
}).then(info => {
|
||||||
|
reason.value = info?.signupReason;
|
||||||
|
email.value = info?.email;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getReason();
|
||||||
|
|
||||||
|
const emits = defineEmits<{
|
||||||
|
(event: 'deleted', value: string): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
async function deleteAccount() {
|
||||||
|
const confirm = await os.confirm({
|
||||||
|
type: 'warning',
|
||||||
|
text: i18n.ts.deleteAccountConfirm,
|
||||||
|
});
|
||||||
|
if (confirm.canceled) return;
|
||||||
|
|
||||||
|
await os.apiWithDialog('admin/decline-user', {
|
||||||
|
userId: props.user.id,
|
||||||
|
});
|
||||||
|
emits('deleted', props.user.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function approveAccount() {
|
||||||
|
const confirm = await os.confirm({
|
||||||
|
type: 'warning',
|
||||||
|
text: i18n.ts.approveConfirm,
|
||||||
|
});
|
||||||
|
if (confirm.canceled) return;
|
||||||
|
await misskeyApi('admin/approve-user', { userId: props.user.id });
|
||||||
|
emits('deleted', props.user.id);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.root {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.items {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||||||
|
grid-gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-size: 0.85em;
|
||||||
|
padding: 0 0 8px 0;
|
||||||
|
user-select: none;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
.buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -649,7 +649,7 @@ const headerTabs = computed(() => iAmAdmin && !approved.value ?
|
||||||
{
|
{
|
||||||
key: 'approval',
|
key: 'approval',
|
||||||
title: 'Approval',
|
title: 'Approval',
|
||||||
icon: 'ph-eye ph-bold pg-lg',
|
icon: 'ti ti-scan-eye',
|
||||||
}
|
}
|
||||||
] : [
|
] : [
|
||||||
{
|
{
|
||||||
|
|
72
packages/frontend/src/pages/admin/approvals.vue
Normal file
72
packages/frontend/src/pages/admin/approvals.vue
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: marie and other Sharkey contributors
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<MkStickyContainer>
|
||||||
|
<template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
|
<MkSpacer :contentMax="900">
|
||||||
|
<div class="_gaps_m">
|
||||||
|
<MkPagination ref="paginationComponent" :pagination="pagination" :displayLimit="50">
|
||||||
|
<template #default="{ items }">
|
||||||
|
<div class="_gaps_s">
|
||||||
|
<SkApprovalUser v-for="item in items" :key="item.id" :user="(item as any)" :onDeleted="deleted"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MkPagination>
|
||||||
|
</div>
|
||||||
|
</MkSpacer>
|
||||||
|
</MkStickyContainer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, shallowRef } from 'vue';
|
||||||
|
import XHeader from './_header_.vue';
|
||||||
|
import MkPagination from '@/components/MkPagination.vue';
|
||||||
|
import SkApprovalUser from '@/components/SkApprovalUser.vue';
|
||||||
|
import { i18n } from '@/i18n.js';
|
||||||
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
|
|
||||||
|
let paginationComponent = shallowRef<InstanceType<typeof MkPagination>>();
|
||||||
|
|
||||||
|
const pagination = {
|
||||||
|
endpoint: 'admin/show-users' as const,
|
||||||
|
limit: 10,
|
||||||
|
params: computed(() => ({
|
||||||
|
sort: '+createdAt',
|
||||||
|
state: 'approved',
|
||||||
|
origin: 'local',
|
||||||
|
})),
|
||||||
|
offsetMode: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
function deleted(id: string) {
|
||||||
|
if (paginationComponent.value) {
|
||||||
|
paginationComponent.value.items.delete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const headerActions = computed(() => []);
|
||||||
|
|
||||||
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
|
definePageMetadata(() => ({
|
||||||
|
title: i18n.ts.approvals,
|
||||||
|
icon: 'ti ti-user-eye',
|
||||||
|
}));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.inputs {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -16,6 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkInfo v-if="noMaintainerInformation" warn class="info">{{ i18n.ts.noMaintainerInformationWarning }} <MkA to="/admin/settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
|
<MkInfo v-if="noMaintainerInformation" warn class="info">{{ i18n.ts.noMaintainerInformationWarning }} <MkA to="/admin/settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
|
||||||
<MkInfo v-if="noBotProtection" warn class="info">{{ i18n.ts.noBotProtectionWarning }} <MkA to="/admin/security" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
|
<MkInfo v-if="noBotProtection" warn class="info">{{ i18n.ts.noBotProtectionWarning }} <MkA to="/admin/security" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
|
||||||
<MkInfo v-if="noEmailServer" warn class="info">{{ i18n.ts.noEmailServerWarning }} <MkA to="/admin/email-settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
|
<MkInfo v-if="noEmailServer" warn class="info">{{ i18n.ts.noEmailServerWarning }} <MkA to="/admin/email-settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
|
||||||
|
<MkInfo v-if="pendingUserApprovals" warn class="info">{{ i18n.ts.pendingUserApprovals }} <MkA to="/admin/approvals" class="_link">{{ i18n.ts.check }}</MkA></MkInfo>
|
||||||
|
|
||||||
<MkSuperMenu :def="menuDef" :grid="narrow"></MkSuperMenu>
|
<MkSuperMenu :def="menuDef" :grid="narrow"></MkSuperMenu>
|
||||||
</div>
|
</div>
|
||||||
|
@ -61,6 +62,7 @@ let noMaintainerInformation = isEmpty(instance.maintainerName) || isEmpty(instan
|
||||||
let noBotProtection = !instance.disableRegistration && !instance.enableHcaptcha && !instance.enableRecaptcha && !instance.enableTurnstile;
|
let noBotProtection = !instance.disableRegistration && !instance.enableHcaptcha && !instance.enableRecaptcha && !instance.enableTurnstile;
|
||||||
let noEmailServer = !instance.enableEmail;
|
let noEmailServer = !instance.enableEmail;
|
||||||
const thereIsUnresolvedAbuseReport = ref(false);
|
const thereIsUnresolvedAbuseReport = ref(false);
|
||||||
|
const pendingUserApprovals = ref(false);
|
||||||
const currentPage = computed(() => router.currentRef.value.child);
|
const currentPage = computed(() => router.currentRef.value.child);
|
||||||
|
|
||||||
misskeyApi('admin/abuse-user-reports', {
|
misskeyApi('admin/abuse-user-reports', {
|
||||||
|
@ -70,6 +72,14 @@ misskeyApi('admin/abuse-user-reports', {
|
||||||
if (reports.length > 0) thereIsUnresolvedAbuseReport.value = true;
|
if (reports.length > 0) thereIsUnresolvedAbuseReport.value = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
misskeyApi('admin/show-users', {
|
||||||
|
state: 'approved',
|
||||||
|
origin: 'local',
|
||||||
|
limit: 1,
|
||||||
|
}).then(approvals => {
|
||||||
|
if (approvals.length > 0) pendingUserApprovals.value = true;
|
||||||
|
});
|
||||||
|
|
||||||
const NARROW_THRESHOLD = 600;
|
const NARROW_THRESHOLD = 600;
|
||||||
const ro = new ResizeObserver((entries, observer) => {
|
const ro = new ResizeObserver((entries, observer) => {
|
||||||
if (entries.length === 0) return;
|
if (entries.length === 0) return;
|
||||||
|
@ -106,6 +116,11 @@ const menuDef = computed(() => [{
|
||||||
text: i18n.ts.invite,
|
text: i18n.ts.invite,
|
||||||
to: '/admin/invites',
|
to: '/admin/invites',
|
||||||
active: currentPage.value?.route.name === 'invites',
|
active: currentPage.value?.route.name === 'invites',
|
||||||
|
}, {
|
||||||
|
icon: 'ti ti-user-scan',
|
||||||
|
text: i18n.ts.approvals,
|
||||||
|
to: '/admin/approvals',
|
||||||
|
active: currentPage.value?.route.name === 'approvals',
|
||||||
}, {
|
}, {
|
||||||
icon: 'ti ti-badges',
|
icon: 'ti ti-badges',
|
||||||
text: i18n.ts.roles,
|
text: i18n.ts.roles,
|
||||||
|
|
|
@ -482,6 +482,10 @@ const routes: RouteDef[] = [{
|
||||||
path: '/invites',
|
path: '/invites',
|
||||||
name: 'invites',
|
name: 'invites',
|
||||||
component: page(() => import('@/pages/admin/invites.vue')),
|
component: page(() => import('@/pages/admin/invites.vue')),
|
||||||
|
}, {
|
||||||
|
path: '/approvals',
|
||||||
|
name: 'approvals',
|
||||||
|
component: page(() => import('@/pages/admin/approvals.vue')),
|
||||||
}, {
|
}, {
|
||||||
path: '/',
|
path: '/',
|
||||||
component: page(() => import('@/pages/_empty_.vue')),
|
component: page(() => import('@/pages/_empty_.vue')),
|
||||||
|
|
|
@ -137,6 +137,9 @@ type AdminAvatarDecorationsListResponse = operations['admin___avatar-decorations
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type AdminAvatarDecorationsUpdateRequest = operations['admin___avatar-decorations___update']['requestBody']['content']['application/json'];
|
type AdminAvatarDecorationsUpdateRequest = operations['admin___avatar-decorations___update']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
type AdminDeclineUserRequest = operations['admin___decline-user']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type AdminDriveDeleteAllFilesOfAUserRequest = operations['admin___drive___delete-all-files-of-a-user']['requestBody']['content']['application/json'];
|
type AdminDriveDeleteAllFilesOfAUserRequest = operations['admin___drive___delete-all-files-of-a-user']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
|
@ -1329,6 +1332,7 @@ declare namespace entities {
|
||||||
AdminShowUsersResponse,
|
AdminShowUsersResponse,
|
||||||
AdminSuspendUserRequest,
|
AdminSuspendUserRequest,
|
||||||
AdminApproveUserRequest,
|
AdminApproveUserRequest,
|
||||||
|
AdminDeclineUserRequest,
|
||||||
AdminUnsuspendUserRequest,
|
AdminUnsuspendUserRequest,
|
||||||
AdminUpdateMetaRequest,
|
AdminUpdateMetaRequest,
|
||||||
AdminUpdateUserNameRequest,
|
AdminUpdateUserNameRequest,
|
||||||
|
@ -2800,7 +2804,7 @@ type PagesUpdateRequest = operations['pages___update']['requestBody']['content']
|
||||||
function parse(acct: string): Acct;
|
function parse(acct: string): Acct;
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export const permissions: readonly ["read:account", "write:account", "read:blocks", "write:blocks", "read:drive", "write:drive", "read:favorites", "write:favorites", "read:following", "write:following", "read:messaging", "write:messaging", "read:mutes", "write:mutes", "write:notes", "read:notifications", "write:notifications", "read:reactions", "write:reactions", "write:votes", "read:pages", "write:pages", "write:page-likes", "read:page-likes", "read:user-groups", "write:user-groups", "read:channels", "write:channels", "read:gallery", "write:gallery", "read:gallery-likes", "write:gallery-likes", "read:flash", "write:flash", "read:flash-likes", "write:flash-likes", "read:admin:abuse-user-reports", "read:admin:abuse-report-resolvers", "write:admin:abuse-report-resolvers", "read:admin:index-stats", "read:admin:table-stats", "read:admin:user-ips", "read:admin:meta", "write:admin:reset-password", "write:admin:regenerate-user-token", "write:admin:resolve-abuse-user-report", "write:admin:send-email", "read:admin:server-info", "read:admin:show-moderation-log", "read:admin:show-account-move-log", "read:admin:show-user", "read:admin:show-users", "write:admin:suspend-user", "write:admin:approve-user", "write:admin:unsuspend-user", "write:admin:meta", "write:admin:user-name", "write:admin:user-note", "write:admin:user-avatar", "write:admin:user-banner", "write:admin:user-mutual-link", "write:admin:roles", "read:admin:roles", "write:admin:relays", "read:admin:relays", "write:admin:invite-codes", "read:admin:invite-codes", "write:admin:announcements", "read:admin:announcements", "write:admin:avatar-decorations", "read:admin:avatar-decorations", "write:admin:federation", "write:admin:indie-auth", "read:admin:indie-auth", "write:admin:account", "read:admin:account", "write:admin:emoji", "read:admin:emoji", "write:admin:queue", "read:admin:queue", "write:admin:promo", "write:admin:drive", "read:admin:drive", "write:admin:sso", "read:admin:sso", "write:admin:ad", "read:admin:ad", "write:invite-codes", "read:invite-codes", "write:clip-favorite", "read:clip-favorite", "read:federation", "write:report-abuse"];
|
export const permissions: readonly ["read:account", "write:account", "read:blocks", "write:blocks", "read:drive", "write:drive", "read:favorites", "write:favorites", "read:following", "write:following", "read:messaging", "write:messaging", "read:mutes", "write:mutes", "write:notes", "read:notifications", "write:notifications", "read:reactions", "write:reactions", "write:votes", "read:pages", "write:pages", "write:page-likes", "read:page-likes", "read:user-groups", "write:user-groups", "read:channels", "write:channels", "read:gallery", "write:gallery", "read:gallery-likes", "write:gallery-likes", "read:flash", "write:flash", "read:flash-likes", "write:flash-likes", "read:admin:abuse-user-reports", "read:admin:abuse-report-resolvers", "write:admin:abuse-report-resolvers", "read:admin:index-stats", "read:admin:table-stats", "read:admin:user-ips", "read:admin:meta", "write:admin:reset-password", "write:admin:regenerate-user-token", "write:admin:resolve-abuse-user-report", "write:admin:send-email", "read:admin:server-info", "read:admin:show-moderation-log", "read:admin:show-account-move-log", "read:admin:show-user", "read:admin:show-users", "write:admin:suspend-user", "write:admin:approve-user", "write:admin:decline-user", "write:admin:unsuspend-user", "write:admin:meta", "write:admin:user-name", "write:admin:user-note", "write:admin:user-avatar", "write:admin:user-banner", "write:admin:user-mutual-link", "write:admin:roles", "read:admin:roles", "write:admin:relays", "read:admin:relays", "write:admin:invite-codes", "read:admin:invite-codes", "write:admin:announcements", "read:admin:announcements", "write:admin:avatar-decorations", "read:admin:avatar-decorations", "write:admin:federation", "write:admin:indie-auth", "read:admin:indie-auth", "write:admin:account", "read:admin:account", "write:admin:emoji", "read:admin:emoji", "write:admin:queue", "read:admin:queue", "write:admin:promo", "write:admin:drive", "read:admin:drive", "write:admin:sso", "read:admin:sso", "write:admin:ad", "read:admin:ad", "write:invite-codes", "read:invite-codes", "write:clip-favorite", "read:clip-favorite", "read:federation", "write:report-abuse"];
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type PingResponse = operations['ping']['responses']['200']['content']['application/json'];
|
type PingResponse = operations['ping']['responses']['200']['content']['application/json'];
|
||||||
|
|
|
@ -851,6 +851,17 @@ declare module '../api.js' {
|
||||||
credential?: string | null,
|
credential?: string | null,
|
||||||
): Promise<SwitchCaseResponseType<E, P>>;
|
): Promise<SwitchCaseResponseType<E, P>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:admin:decline-user*
|
||||||
|
*/
|
||||||
|
request<E extends 'admin/decline-user', P extends Endpoints[E]['req']>(
|
||||||
|
endpoint: E,
|
||||||
|
params: P,
|
||||||
|
credential?: string | null,
|
||||||
|
): Promise<SwitchCaseResponseType<E, P>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* No description provided.
|
* No description provided.
|
||||||
*
|
*
|
||||||
|
|
|
@ -104,6 +104,7 @@ import type {
|
||||||
AdminShowUsersResponse,
|
AdminShowUsersResponse,
|
||||||
AdminSuspendUserRequest,
|
AdminSuspendUserRequest,
|
||||||
AdminApproveUserRequest,
|
AdminApproveUserRequest,
|
||||||
|
AdminDeclineUserRequest,
|
||||||
AdminUnsuspendUserRequest,
|
AdminUnsuspendUserRequest,
|
||||||
AdminUpdateMetaRequest,
|
AdminUpdateMetaRequest,
|
||||||
AdminUpdateUserNameRequest,
|
AdminUpdateUserNameRequest,
|
||||||
|
@ -669,6 +670,7 @@ export type Endpoints = {
|
||||||
'admin/show-users': { req: AdminShowUsersRequest; res: AdminShowUsersResponse };
|
'admin/show-users': { req: AdminShowUsersRequest; res: AdminShowUsersResponse };
|
||||||
'admin/suspend-user': { req: AdminSuspendUserRequest; res: EmptyResponse };
|
'admin/suspend-user': { req: AdminSuspendUserRequest; res: EmptyResponse };
|
||||||
'admin/approve-user': { req: AdminApproveUserRequest; res: EmptyResponse };
|
'admin/approve-user': { req: AdminApproveUserRequest; res: EmptyResponse };
|
||||||
|
'admin/decline-user': { req: AdminDeclineUserRequest; res: EmptyResponse };
|
||||||
'admin/unsuspend-user': { req: AdminUnsuspendUserRequest; res: EmptyResponse };
|
'admin/unsuspend-user': { req: AdminUnsuspendUserRequest; res: EmptyResponse };
|
||||||
'admin/update-meta': { req: AdminUpdateMetaRequest; res: EmptyResponse };
|
'admin/update-meta': { req: AdminUpdateMetaRequest; res: EmptyResponse };
|
||||||
'admin/update-user-name': { req: AdminUpdateUserNameRequest; res: EmptyResponse };
|
'admin/update-user-name': { req: AdminUpdateUserNameRequest; res: EmptyResponse };
|
||||||
|
|
|
@ -107,6 +107,7 @@ export type AdminShowUsersRequest = operations['admin___show-users']['requestBod
|
||||||
export type AdminShowUsersResponse = operations['admin___show-users']['responses']['200']['content']['application/json'];
|
export type AdminShowUsersResponse = operations['admin___show-users']['responses']['200']['content']['application/json'];
|
||||||
export type AdminSuspendUserRequest = operations['admin___suspend-user']['requestBody']['content']['application/json'];
|
export type AdminSuspendUserRequest = operations['admin___suspend-user']['requestBody']['content']['application/json'];
|
||||||
export type AdminApproveUserRequest = operations['admin___approve-user']['requestBody']['content']['application/json'];
|
export type AdminApproveUserRequest = operations['admin___approve-user']['requestBody']['content']['application/json'];
|
||||||
|
export type AdminDeclineUserRequest = operations['admin___decline-user']['requestBody']['content']['application/json'];
|
||||||
export type AdminUnsuspendUserRequest = operations['admin___unsuspend-user']['requestBody']['content']['application/json'];
|
export type AdminUnsuspendUserRequest = operations['admin___unsuspend-user']['requestBody']['content']['application/json'];
|
||||||
export type AdminUpdateMetaRequest = operations['admin___update-meta']['requestBody']['content']['application/json'];
|
export type AdminUpdateMetaRequest = operations['admin___update-meta']['requestBody']['content']['application/json'];
|
||||||
export type AdminUpdateUserNameRequest = operations['admin___update-user-name']['requestBody']['content']['application/json'];
|
export type AdminUpdateUserNameRequest = operations['admin___update-user-name']['requestBody']['content']['application/json'];
|
||||||
|
|
|
@ -706,6 +706,15 @@ export type paths = {
|
||||||
*/
|
*/
|
||||||
post: operations['admin___approve-user'];
|
post: operations['admin___approve-user'];
|
||||||
};
|
};
|
||||||
|
'/admin/decline-user': {
|
||||||
|
/**
|
||||||
|
* admin/decline-user
|
||||||
|
* @description No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:admin:decline-user*
|
||||||
|
*/
|
||||||
|
post: operations['admin___decline-user'];
|
||||||
|
};
|
||||||
'/admin/unsuspend-user': {
|
'/admin/unsuspend-user': {
|
||||||
/**
|
/**
|
||||||
* admin/unsuspend-user
|
* admin/unsuspend-user
|
||||||
|
@ -10058,6 +10067,58 @@ export type operations = {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* admin/decline-user
|
||||||
|
* @description No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:admin:decline-user*
|
||||||
|
*/
|
||||||
|
'admin___decline-user': {
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
/** Format: misskey:id */
|
||||||
|
userId: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description OK (without any results) */
|
||||||
|
204: {
|
||||||
|
content: never;
|
||||||
|
};
|
||||||
|
/** @description Client error */
|
||||||
|
400: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Authentication error */
|
||||||
|
401: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Forbidden error */
|
||||||
|
403: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description I'm Ai */
|
||||||
|
418: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Internal server error */
|
||||||
|
500: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* admin/unsuspend-user
|
* admin/unsuspend-user
|
||||||
* @description No description provided.
|
* @description No description provided.
|
||||||
|
|
|
@ -63,6 +63,7 @@ export const permissions = [
|
||||||
'read:admin:show-users',
|
'read:admin:show-users',
|
||||||
'write:admin:suspend-user',
|
'write:admin:suspend-user',
|
||||||
'write:admin:approve-user',
|
'write:admin:approve-user',
|
||||||
|
'write:admin:decline-user',
|
||||||
'write:admin:unsuspend-user',
|
'write:admin:unsuspend-user',
|
||||||
'write:admin:meta',
|
'write:admin:meta',
|
||||||
'write:admin:user-name',
|
'write:admin:user-name',
|
||||||
|
|
Loading…
Reference in a new issue