Merge branch 'develop' of https://activitypub.software/TransFem-org/Sharkey into feat/instance-admin-ui
This commit is contained in:
commit
fadae347ff
37 changed files with 541 additions and 65 deletions
|
|
@ -50,6 +50,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<template #caption>{{ i18n.ts.preservedUsernamesDescription }}</template>
|
||||
</MkTextarea>
|
||||
|
||||
<MkTextarea v-model="trustedLinkUrlPatterns">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts.trustedLinkUrlPatterns }}</template>
|
||||
<template #caption>{{ i18n.ts.trustedLinkUrlPatternsDescription }}</template>
|
||||
</MkTextarea>
|
||||
|
||||
<MkTextarea v-model="sensitiveWords">
|
||||
<template #label>{{ i18n.ts.sensitiveWords }}</template>
|
||||
<template #caption>{{ i18n.ts.sensitiveWordsDescription }}<br>{{ i18n.ts.sensitiveWordsDescription2 }}</template>
|
||||
|
|
@ -105,6 +111,7 @@ const bubbleTimeline = ref<string>('');
|
|||
const tosUrl = ref<string | null>(null);
|
||||
const privacyPolicyUrl = ref<string | null>(null);
|
||||
const inquiryUrl = ref<string | null>(null);
|
||||
const trustedLinkUrlPatterns = ref<string>('');
|
||||
|
||||
async function init() {
|
||||
const meta = await misskeyApi('admin/meta');
|
||||
|
|
@ -120,6 +127,7 @@ async function init() {
|
|||
bubbleTimeline.value = meta.bubbleInstances.join('\n');
|
||||
bubbleTimelineEnabled.value = meta.policies.btlAvailable;
|
||||
inquiryUrl.value = meta.inquiryUrl;
|
||||
trustedLinkUrlPatterns.value = meta.trustedLinkUrlPatterns.join('\n');
|
||||
}
|
||||
|
||||
function save() {
|
||||
|
|
@ -135,6 +143,7 @@ function save() {
|
|||
hiddenTags: hiddenTags.value.split('\n'),
|
||||
preservedUsernames: preservedUsernames.value.split('\n'),
|
||||
bubbleInstances: bubbleTimeline.value.split('\n'),
|
||||
trustedLinkUrlPatterns: trustedLinkUrlPatterns.value.split('\n'),
|
||||
}).then(() => {
|
||||
fetchInstance(true);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -23,6 +23,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
'markSensitiveDriveFile',
|
||||
'resetPassword',
|
||||
'suspendRemoteInstance',
|
||||
'setRemoteInstanceNSFW',
|
||||
'unsetRemoteInstanceNSFW',
|
||||
'rejectRemoteInstanceReports',
|
||||
'acceptRemoteInstanceReports',
|
||||
].includes(log.type),
|
||||
[$style.logRed]: [
|
||||
'suspend',
|
||||
|
|
@ -61,6 +65,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<span v-else-if="log.type === 'unmarkSensitiveDriveFile'">: @{{ log.info.fileUserUsername }}{{ log.info.fileUserHost ? '@' + log.info.fileUserHost : '' }}</span>
|
||||
<span v-else-if="log.type === 'suspendRemoteInstance'">: {{ log.info.host }}</span>
|
||||
<span v-else-if="log.type === 'unsuspendRemoteInstance'">: {{ log.info.host }}</span>
|
||||
<span v-else-if="log.type === 'setRemoteInstanceNSFW'">: {{ log.info.host }}</span>
|
||||
<span v-else-if="log.type === 'unsetRemoteInstanceNSFW'">: {{ log.info.host }}</span>
|
||||
<span v-else-if="log.type === 'rejectRemoteInstanceReports'">: {{ log.info.host }}</span>
|
||||
<span v-else-if="log.type === 'acceptRemoteInstanceReports'">: {{ log.info.host }}</span>
|
||||
<span v-else-if="log.type === 'createGlobalAnnouncement'">: {{ log.info.announcement.title }}</span>
|
||||
<span v-else-if="log.type === 'updateGlobalAnnouncement'">: {{ log.info.before.title }}</span>
|
||||
<span v-else-if="log.type === 'deleteGlobalAnnouncement'">: {{ log.info.announcement.title }}</span>
|
||||
|
|
|
|||
|
|
@ -49,10 +49,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<MkButton v-if="suspensionState === 'none'" inline :disabled="!instance" danger @click="stopDelivery">{{ i18n.ts._delivery.stop }}</MkButton>
|
||||
<MkButton v-if="suspensionState !== 'none'" inline :disabled="!instance" @click="resumeDelivery">{{ i18n.ts._delivery.resume }}</MkButton>
|
||||
</div>
|
||||
<MkSwitch v-model="isBlocked" :disabled="!meta || !instance" @update:modelValue="toggleBlock">{{ i18n.ts.blockThisInstance }}</MkSwitch>
|
||||
<MkSwitch v-model="isSilenced" :disabled="!meta || !instance" @update:modelValue="toggleSilenced">{{ i18n.ts.silenceThisInstance }}</MkSwitch>
|
||||
<MkSwitch v-model="isNSFW" :disabled="!instance" @update:modelValue="toggleNSFW">Mark as NSFW</MkSwitch>
|
||||
<MkSwitch v-model="isMediaSilenced" :disabled="!meta || !instance" @update:modelValue="toggleMediaSilenced">{{ i18n.ts.mediaSilenceThisInstance }}</MkSwitch>
|
||||
<MkInfo v-if="isBaseBlocked" warn>{{ i18n.ts.blockedByBase }}</MkInfo>
|
||||
<MkSwitch v-model="isBlocked" :disabled="!meta || !instance || isBaseBlocked" @update:modelValue="toggleBlock">{{ i18n.ts.blockThisInstance }}</MkSwitch>
|
||||
<MkInfo v-if="isBaseSilenced" warn>{{ i18n.ts.silencedByBase }}</MkInfo>
|
||||
<MkSwitch v-model="isSilenced" :disabled="!meta || !instance || isBaseSilenced" @update:modelValue="toggleSilenced">{{ i18n.ts.silenceThisInstance }}</MkSwitch>
|
||||
<MkSwitch v-model="isNSFW" :disabled="!instance" @update:modelValue="toggleNSFW">{{ i18n.ts.markInstanceAsNSFW }}</MkSwitch>
|
||||
<MkSwitch v-model="rejectReports" :disabled="!instance" @update:modelValue="toggleRejectReports">{{ i18n.ts.rejectReports }}</MkSwitch>
|
||||
<MkInfo v-if="isBaseMediaSilenced" warn>{{ i18n.ts.mediaSilencedByBase }}</MkInfo>
|
||||
<MkSwitch v-model="isMediaSilenced" :disabled="!meta || !instance || isBaseMediaSilenced" @update:modelValue="toggleMediaSilenced">{{ i18n.ts.mediaSilenceThisInstance }}</MkSwitch>
|
||||
<MkButton @click="refreshMetadata"><i class="ti ti-refresh"></i> Refresh metadata</MkButton>
|
||||
<MkTextarea v-model="moderationNote" manualSave>
|
||||
<template #label>{{ i18n.ts.moderationNote }}</template>
|
||||
|
|
@ -160,6 +164,7 @@ import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue';
|
|||
import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js';
|
||||
import { dateString } from '@/filters/date.js';
|
||||
import MkTextarea from '@/components/MkTextarea.vue';
|
||||
import MkInfo from '@/components/MkInfo.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
host: string;
|
||||
|
|
@ -174,10 +179,26 @@ const suspensionState = ref<'none' | 'manuallySuspended' | 'goneSuspended' | 'au
|
|||
const isBlocked = ref(false);
|
||||
const isSilenced = ref(false);
|
||||
const isNSFW = ref(false);
|
||||
const rejectReports = ref(false);
|
||||
const isMediaSilenced = ref(false);
|
||||
const faviconUrl = ref<string | null>(null);
|
||||
const moderationNote = ref('');
|
||||
|
||||
const baseDomains = computed(() => {
|
||||
const domains: string[] = [];
|
||||
|
||||
const parts = props.host.toLowerCase().split('.');
|
||||
for (let s = 1; s < parts.length; s++) {
|
||||
const domain = parts.slice(s).join('.');
|
||||
domains.push(domain);
|
||||
}
|
||||
|
||||
return domains;
|
||||
});
|
||||
const isBaseBlocked = computed(() => meta.value && baseDomains.value.some(d => meta.value?.blockedHosts.includes(d)));
|
||||
const isBaseSilenced = computed(() => meta.value && baseDomains.value.some(d => meta.value?.silencedHosts.includes(d)));
|
||||
const isBaseMediaSilenced = computed(() => meta.value && baseDomains.value.some(d => meta.value?.mediaSilencedHosts.includes(d)));
|
||||
|
||||
const usersPagination = {
|
||||
endpoint: iAmModerator ? 'admin/show-users' : 'users' as const,
|
||||
limit: 10,
|
||||
|
|
@ -204,6 +225,7 @@ async function fetch(): Promise<void> {
|
|||
isBlocked.value = instance.value?.isBlocked ?? false;
|
||||
isSilenced.value = instance.value?.isSilenced ?? false;
|
||||
isNSFW.value = instance.value?.isNSFW ?? false;
|
||||
rejectReports.value = instance.value?.rejectReports ?? false;
|
||||
isMediaSilenced.value = instance.value?.isMediaSilenced ?? false;
|
||||
faviconUrl.value = getProxiedImageUrlNullable(instance.value?.faviconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.value?.iconUrl, 'preview');
|
||||
moderationNote.value = instance.value?.moderationNote ?? '';
|
||||
|
|
@ -264,6 +286,14 @@ async function toggleNSFW(): Promise<void> {
|
|||
});
|
||||
}
|
||||
|
||||
async function toggleRejectReports(): Promise<void> {
|
||||
if (!instance.value) throw new Error('No instance?');
|
||||
await misskeyApi('admin/federation/update-instance', {
|
||||
host: instance.value.host,
|
||||
rejectReports: rejectReports.value,
|
||||
});
|
||||
}
|
||||
|
||||
function refreshMetadata(): void {
|
||||
if (!instance.value) throw new Error('No instance?');
|
||||
misskeyApi('admin/federation/refresh-remote-instance-metadata', {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<span v-if="$i && $i.id != user.id && user.isFollowed" class="followed">{{ i18n.ts.followsYou }}</span>
|
||||
<div v-if="$i && $i.id != user.id" class="info-badges">
|
||||
<span v-if="user.isFollowed">{{ i18n.ts.followsYou }}</span>
|
||||
<span v-if="user.isMuted">{{ i18n.ts.muted }}</span>
|
||||
<span v-if="user.isRenoteMuted">{{ i18n.ts.renoteMuted }}</span>
|
||||
<span v-if="user.isBlocking">{{ i18n.ts.blocked }}</span>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="menu _button" @click="menu"><i class="ti ti-dots"></i></button>
|
||||
<MkFollowButton v-if="$i?.id != user.id" v-model:user="user" :inline="true" :transparent="false" :full="true" class="koudoku"/>
|
||||
|
|
@ -445,15 +450,25 @@ onUnmounted(() => {
|
|||
background: linear-gradient(transparent, rgba(#000, 0.7));
|
||||
}
|
||||
|
||||
> .followed {
|
||||
> .info-badges {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
left: 12px;
|
||||
padding: 4px 8px;
|
||||
color: #fff;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
font-size: 0.7em;
|
||||
border-radius: var(--radius-sm);
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
> * {
|
||||
padding: 4px 8px;
|
||||
color: #fff;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
font-size: 0.7em;
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
|
||||
> :not(:first-child) {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
> .actions {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue