mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-05-14 08:45:44 +02:00
コントロールパネルの検索 (#16343)
* Update settings.vue * Update settings.vue * Update settings.vue * Update settings.vue * Update settings.vue * Update performance.vue * Update performance.vue * Update performance.vue * Update external-services.vue * wip * wip * Update security.vue * Update settings.vue * Update CHANGELOG.md * wip * Update moderation.vue * wip * Update branding.vue * wip * Update email-settings.vue * Update system-webhook.vue * Update MkSuperMenu.vue * Update index.vue
This commit is contained in:
@@ -4,158 +4,161 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-shield"></i></template>
|
||||
<template #label>{{ i18n.ts.botProtection }}</template>
|
||||
<template v-if="botProtectionForm.savedState.provider === 'hcaptcha'" #suffix>hCaptcha</template>
|
||||
<template v-else-if="botProtectionForm.savedState.provider === 'mcaptcha'" #suffix>mCaptcha</template>
|
||||
<template v-else-if="botProtectionForm.savedState.provider === 'recaptcha'" #suffix>reCAPTCHA</template>
|
||||
<template v-else-if="botProtectionForm.savedState.provider === 'turnstile'" #suffix>Turnstile</template>
|
||||
<template v-else-if="botProtectionForm.savedState.provider === 'testcaptcha'" #suffix>testCaptcha</template>
|
||||
<template v-else #suffix>{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</template>
|
||||
<template #footer>
|
||||
<MkFormFooter :canSaving="canSaving" :form="botProtectionForm"/>
|
||||
</template>
|
||||
<SearchMarker markerId="botProtection" :keywords="['bot', 'protection', 'captcha', 'hcaptcha', 'mcaptcha', 'recaptcha', 'turnstile']">
|
||||
<MkFolder>
|
||||
<template #icon><SearchIcon><i class="ti ti-shield"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.botProtection }}</SearchLabel></template>
|
||||
<template v-if="botProtectionForm.savedState.provider === 'hcaptcha'" #suffix>hCaptcha</template>
|
||||
<template v-else-if="botProtectionForm.savedState.provider === 'mcaptcha'" #suffix>mCaptcha</template>
|
||||
<template v-else-if="botProtectionForm.savedState.provider === 'recaptcha'" #suffix>reCAPTCHA</template>
|
||||
<template v-else-if="botProtectionForm.savedState.provider === 'turnstile'" #suffix>Turnstile</template>
|
||||
<template v-else-if="botProtectionForm.savedState.provider === 'testcaptcha'" #suffix>testCaptcha</template>
|
||||
<template v-else #suffix>{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</template>
|
||||
<template #footer>
|
||||
<MkFormFooter :canSaving="canSaving" :form="botProtectionForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<MkRadios v-model="botProtectionForm.state.provider">
|
||||
<option value="none">{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</option>
|
||||
<option value="hcaptcha">hCaptcha</option>
|
||||
<option value="mcaptcha">mCaptcha</option>
|
||||
<option value="recaptcha">reCAPTCHA</option>
|
||||
<option value="turnstile">Turnstile</option>
|
||||
<option value="testcaptcha">testCaptcha</option>
|
||||
</MkRadios>
|
||||
<div class="_gaps_m">
|
||||
<MkRadios v-model="botProtectionForm.state.provider">
|
||||
<option value="none">{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</option>
|
||||
<option value="hcaptcha">hCaptcha</option>
|
||||
<option value="mcaptcha">mCaptcha</option>
|
||||
<option value="recaptcha">reCAPTCHA</option>
|
||||
<option value="turnstile">Turnstile</option>
|
||||
<option value="testcaptcha">testCaptcha</option>
|
||||
</MkRadios>
|
||||
|
||||
<template v-if="botProtectionForm.state.provider === 'hcaptcha'">
|
||||
<MkInput v-model="botProtectionForm.state.hcaptchaSiteKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.hcaptchaSiteKey }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="botProtectionForm.state.hcaptchaSecretKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.hcaptchaSecretKey }}</template>
|
||||
</MkInput>
|
||||
<FormSlot v-if="botProtectionForm.state.hcaptchaSiteKey">
|
||||
<template #label>{{ i18n.ts._captcha.verify }}</template>
|
||||
<MkCaptcha
|
||||
v-model="captchaResult"
|
||||
provider="hcaptcha"
|
||||
:sitekey="botProtectionForm.state.hcaptchaSiteKey"
|
||||
:secretKey="botProtectionForm.state.hcaptchaSecretKey"
|
||||
/>
|
||||
</FormSlot>
|
||||
<MkInfo>
|
||||
<div :class="$style.captchaInfoMsg">
|
||||
<div>{{ i18n.ts._captcha.testSiteKeyMessage }}</div>
|
||||
<div>
|
||||
<span>ref: </span><a href="https://docs.hcaptcha.com/#integration-testing-test-keys" target="_blank">hCaptcha Developer Guide</a>
|
||||
<template v-if="botProtectionForm.state.provider === 'hcaptcha'">
|
||||
<MkInput v-model="botProtectionForm.state.hcaptchaSiteKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.hcaptchaSiteKey }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="botProtectionForm.state.hcaptchaSecretKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.hcaptchaSecretKey }}</template>
|
||||
</MkInput>
|
||||
<FormSlot v-if="botProtectionForm.state.hcaptchaSiteKey">
|
||||
<template #label>{{ i18n.ts._captcha.verify }}</template>
|
||||
<MkCaptcha
|
||||
v-model="captchaResult"
|
||||
provider="hcaptcha"
|
||||
:sitekey="botProtectionForm.state.hcaptchaSiteKey"
|
||||
:secretKey="botProtectionForm.state.hcaptchaSecretKey"
|
||||
/>
|
||||
</FormSlot>
|
||||
<MkInfo>
|
||||
<div :class="$style.captchaInfoMsg">
|
||||
<div>{{ i18n.ts._captcha.testSiteKeyMessage }}</div>
|
||||
<div>
|
||||
<span>ref: </span><a href="https://docs.hcaptcha.com/#integration-testing-test-keys" target="_blank">hCaptcha Developer Guide</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</MkInfo>
|
||||
</template>
|
||||
</MkInfo>
|
||||
</template>
|
||||
|
||||
<template v-else-if="botProtectionForm.state.provider === 'mcaptcha'">
|
||||
<MkInput v-model="botProtectionForm.state.mcaptchaSiteKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.mcaptchaSiteKey }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="botProtectionForm.state.mcaptchaSecretKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.mcaptchaSecretKey }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="botProtectionForm.state.mcaptchaInstanceUrl" debounce>
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts.mcaptchaInstanceUrl }}</template>
|
||||
</MkInput>
|
||||
<FormSlot v-if="botProtectionForm.state.mcaptchaSiteKey && botProtectionForm.state.mcaptchaInstanceUrl">
|
||||
<template #label>{{ i18n.ts._captcha.verify }}</template>
|
||||
<MkCaptcha
|
||||
v-model="captchaResult"
|
||||
provider="mcaptcha"
|
||||
:sitekey="botProtectionForm.state.mcaptchaSiteKey"
|
||||
:secretKey="botProtectionForm.state.mcaptchaSecretKey"
|
||||
:instanceUrl="botProtectionForm.state.mcaptchaInstanceUrl"
|
||||
/>
|
||||
</FormSlot>
|
||||
</template>
|
||||
<template v-else-if="botProtectionForm.state.provider === 'mcaptcha'">
|
||||
<MkInput v-model="botProtectionForm.state.mcaptchaSiteKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.mcaptchaSiteKey }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="botProtectionForm.state.mcaptchaSecretKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.mcaptchaSecretKey }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="botProtectionForm.state.mcaptchaInstanceUrl" debounce>
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts.mcaptchaInstanceUrl }}</template>
|
||||
</MkInput>
|
||||
<FormSlot v-if="botProtectionForm.state.mcaptchaSiteKey && botProtectionForm.state.mcaptchaInstanceUrl">
|
||||
<template #label>{{ i18n.ts._captcha.verify }}</template>
|
||||
<MkCaptcha
|
||||
v-model="captchaResult"
|
||||
provider="mcaptcha"
|
||||
:sitekey="botProtectionForm.state.mcaptchaSiteKey"
|
||||
:secretKey="botProtectionForm.state.mcaptchaSecretKey"
|
||||
:instanceUrl="botProtectionForm.state.mcaptchaInstanceUrl"
|
||||
/>
|
||||
</FormSlot>
|
||||
</template>
|
||||
|
||||
<template v-else-if="botProtectionForm.state.provider === 'recaptcha'">
|
||||
<MkInput v-model="botProtectionForm.state.recaptchaSiteKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.recaptchaSiteKey }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="botProtectionForm.state.recaptchaSecretKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.recaptchaSecretKey }}</template>
|
||||
</MkInput>
|
||||
<FormSlot v-if="botProtectionForm.state.recaptchaSiteKey">
|
||||
<template #label>{{ i18n.ts._captcha.verify }}</template>
|
||||
<MkCaptcha
|
||||
v-model="captchaResult"
|
||||
provider="recaptcha"
|
||||
:sitekey="botProtectionForm.state.recaptchaSiteKey"
|
||||
:secretKey="botProtectionForm.state.recaptchaSecretKey"
|
||||
/>
|
||||
</FormSlot>
|
||||
<MkInfo>
|
||||
<div :class="$style.captchaInfoMsg">
|
||||
<div>{{ i18n.ts._captcha.testSiteKeyMessage }}</div>
|
||||
<div>
|
||||
<span>ref: </span>
|
||||
<a
|
||||
href="https://developers.google.com/recaptcha/docs/faq?hl=ja#id-like-to-run-automated-tests-with-recaptcha.-what-should-i-do"
|
||||
target="_blank"
|
||||
>reCAPTCHA FAQ</a>
|
||||
<template v-else-if="botProtectionForm.state.provider === 'recaptcha'">
|
||||
<MkInput v-model="botProtectionForm.state.recaptchaSiteKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.recaptchaSiteKey }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="botProtectionForm.state.recaptchaSecretKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.recaptchaSecretKey }}</template>
|
||||
</MkInput>
|
||||
<FormSlot v-if="botProtectionForm.state.recaptchaSiteKey">
|
||||
<template #label>{{ i18n.ts._captcha.verify }}</template>
|
||||
<MkCaptcha
|
||||
v-model="captchaResult"
|
||||
provider="recaptcha"
|
||||
:sitekey="botProtectionForm.state.recaptchaSiteKey"
|
||||
:secretKey="botProtectionForm.state.recaptchaSecretKey"
|
||||
/>
|
||||
</FormSlot>
|
||||
<MkInfo>
|
||||
<div :class="$style.captchaInfoMsg">
|
||||
<div>{{ i18n.ts._captcha.testSiteKeyMessage }}</div>
|
||||
<div>
|
||||
<span>ref: </span>
|
||||
<a
|
||||
href="https://developers.google.com/recaptcha/docs/faq?hl=ja#id-like-to-run-automated-tests-with-recaptcha.-what-should-i-do"
|
||||
target="_blank"
|
||||
>reCAPTCHA FAQ</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</MkInfo>
|
||||
</template>
|
||||
</MkInfo>
|
||||
</template>
|
||||
|
||||
<template v-else-if="botProtectionForm.state.provider === 'turnstile'">
|
||||
<MkInput v-model="botProtectionForm.state.turnstileSiteKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.turnstileSiteKey }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="botProtectionForm.state.turnstileSecretKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.turnstileSecretKey }}</template>
|
||||
</MkInput>
|
||||
<FormSlot v-if="botProtectionForm.state.turnstileSiteKey">
|
||||
<template #label>{{ i18n.ts._captcha.verify }}</template>
|
||||
<MkCaptcha
|
||||
v-model="captchaResult"
|
||||
provider="turnstile"
|
||||
:sitekey="botProtectionForm.state.turnstileSiteKey"
|
||||
:secretKey="botProtectionForm.state.turnstileSecretKey"
|
||||
/>
|
||||
</FormSlot>
|
||||
<MkInfo>
|
||||
<div :class="$style.captchaInfoMsg">
|
||||
<div>
|
||||
{{ i18n.ts._captcha.testSiteKeyMessage }}
|
||||
<template v-else-if="botProtectionForm.state.provider === 'turnstile'">
|
||||
<MkInput v-model="botProtectionForm.state.turnstileSiteKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.turnstileSiteKey }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="botProtectionForm.state.turnstileSecretKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.turnstileSecretKey }}</template>
|
||||
</MkInput>
|
||||
<FormSlot v-if="botProtectionForm.state.turnstileSiteKey">
|
||||
<template #label>{{ i18n.ts._captcha.verify }}</template>
|
||||
<MkCaptcha
|
||||
v-model="captchaResult"
|
||||
provider="turnstile"
|
||||
:sitekey="botProtectionForm.state.turnstileSiteKey"
|
||||
:secretKey="botProtectionForm.state.turnstileSecretKey"
|
||||
/>
|
||||
</FormSlot>
|
||||
<MkInfo>
|
||||
<div :class="$style.captchaInfoMsg">
|
||||
<div>
|
||||
{{ i18n.ts._captcha.testSiteKeyMessage }}
|
||||
</div>
|
||||
<div>
|
||||
<span>ref: </span><a href="https://developers.cloudflare.com/turnstile/troubleshooting/testing/" target="_blank">Cloudflare Docs</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span>ref: </span><a href="https://developers.cloudflare.com/turnstile/troubleshooting/testing/" target="_blank">Cloudflare Docs</a>
|
||||
</div>
|
||||
</div>
|
||||
</MkInfo>
|
||||
</template>
|
||||
</MkInfo>
|
||||
</template>
|
||||
|
||||
<template v-else-if="botProtectionForm.state.provider === 'testcaptcha'">
|
||||
<MkInfo warn><span v-html="i18n.ts.testCaptchaWarning"></span></MkInfo>
|
||||
<FormSlot>
|
||||
<template #label>{{ i18n.ts._captcha.verify }}</template>
|
||||
<MkCaptcha v-model="captchaResult" provider="testcaptcha" :sitekey="null"/>
|
||||
</FormSlot>
|
||||
</template>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<template v-else-if="botProtectionForm.state.provider === 'testcaptcha'">
|
||||
<MkInfo warn><span v-html="i18n.ts.testCaptchaWarning"></span></MkInfo>
|
||||
<FormSlot>
|
||||
<template #label>{{ i18n.ts._captcha.verify }}</template>
|
||||
<MkCaptcha v-model="captchaResult" provider="testcaptcha" :sitekey="null"/>
|
||||
</FormSlot>
|
||||
</template>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, defineAsyncComponent, ref, watch } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import type { ApiWithDialogCustomErrors } from '@/os.js';
|
||||
import MkRadios from '@/components/MkRadios.vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import FormSlot from '@/components/form/slot.vue';
|
||||
@@ -167,7 +170,6 @@ import { useForm } from '@/composables/use-form.js';
|
||||
import MkFormFooter from '@/components/MkFormFooter.vue';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
import MkInfo from '@/components/MkInfo.vue';
|
||||
import type { ApiWithDialogCustomErrors } from '@/os.js';
|
||||
|
||||
const MkCaptcha = defineAsyncComponent(() => import('@/components/MkCaptcha.vue'));
|
||||
|
||||
|
||||
@@ -6,89 +6,117 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template>
|
||||
<PageWithHeader :tabs="headerTabs">
|
||||
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
|
||||
<FormSuspense :p="init">
|
||||
<SearchMarker path="/admin/branding" :label="i18n.ts.branding" :keywords="['branding']" icon="ti ti-paint">
|
||||
<div class="_gaps_m">
|
||||
<MkInput v-model="iconUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts._serverSettings.iconUrl }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker :keywords="['icon', 'image']">
|
||||
<MkInput v-model="iconUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.iconUrl }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="app192IconUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts._serverSettings.iconUrl }} (App/192px)</template>
|
||||
<template #caption>
|
||||
<div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div>
|
||||
<div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div>
|
||||
<div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div>
|
||||
<div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '192x192px' }) }}</strong></div>
|
||||
</template>
|
||||
</MkInput>
|
||||
<SearchMarker :keywords="['icon', 'image']">
|
||||
<MkInput v-model="app192IconUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.iconUrl }} (App/192px)</SearchLabel></template>
|
||||
<template #caption>
|
||||
<div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div>
|
||||
<div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div>
|
||||
<div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div>
|
||||
<div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '192x192px' }) }}</strong></div>
|
||||
</template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="app512IconUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts._serverSettings.iconUrl }} (App/512px)</template>
|
||||
<template #caption>
|
||||
<div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div>
|
||||
<div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div>
|
||||
<div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div>
|
||||
<div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '512x512px' }) }}</strong></div>
|
||||
</template>
|
||||
</MkInput>
|
||||
<SearchMarker :keywords="['icon', 'image']">
|
||||
<MkInput v-model="app512IconUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.iconUrl }} (App/512px)</SearchLabel></template>
|
||||
<template #caption>
|
||||
<div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div>
|
||||
<div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div>
|
||||
<div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div>
|
||||
<div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '512x512px' }) }}</strong></div>
|
||||
</template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="bannerUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts.bannerUrl }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker :keywords="['banner', 'image']">
|
||||
<MkInput v-model="bannerUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.bannerUrl }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="backgroundImageUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts.backgroundImageUrl }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker :keywords="['background', 'image']">
|
||||
<MkInput v-model="backgroundImageUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.backgroundImageUrl }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="notFoundImageUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts.notFoundDescription }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker :keywords="['image']">
|
||||
<MkInput v-model="notFoundImageUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.notFoundDescription }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="infoImageUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts.nothing }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker :keywords="['image']">
|
||||
<MkInput v-model="infoImageUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.nothing }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="serverErrorImageUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts.somethingHappened }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker :keywords="['image']">
|
||||
<MkInput v-model="serverErrorImageUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.somethingHappened }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkColorInput v-model="themeColor">
|
||||
<template #label>{{ i18n.ts.themeColor }}</template>
|
||||
</MkColorInput>
|
||||
<SearchMarker :keywords="['theme', 'color']">
|
||||
<MkColorInput v-model="themeColor">
|
||||
<template #label><SearchLabel>{{ i18n.ts.themeColor }}</SearchLabel></template>
|
||||
</MkColorInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkTextarea v-model="defaultLightTheme">
|
||||
<template #label>{{ i18n.ts.instanceDefaultLightTheme }}</template>
|
||||
<template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template>
|
||||
</MkTextarea>
|
||||
<SearchMarker :keywords="['theme', 'default', 'light']">
|
||||
<MkTextarea v-model="defaultLightTheme">
|
||||
<template #label><SearchLabel>{{ i18n.ts.instanceDefaultLightTheme }}</SearchLabel></template>
|
||||
<template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template>
|
||||
</MkTextarea>
|
||||
</SearchMarker>
|
||||
|
||||
<MkTextarea v-model="defaultDarkTheme">
|
||||
<template #label>{{ i18n.ts.instanceDefaultDarkTheme }}</template>
|
||||
<template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template>
|
||||
</MkTextarea>
|
||||
<SearchMarker :keywords="['theme', 'default', 'dark']">
|
||||
<MkTextarea v-model="defaultDarkTheme">
|
||||
<template #label><SearchLabel>{{ i18n.ts.instanceDefaultDarkTheme }}</SearchLabel></template>
|
||||
<template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template>
|
||||
</MkTextarea>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="repositoryUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts.repositoryUrl }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="repositoryUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.repositoryUrl }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="feedbackUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts.feedbackUrl }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="feedbackUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.feedbackUrl }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkTextarea v-model="manifestJsonOverride">
|
||||
<template #label>{{ i18n.ts._serverSettings.manifestJsonOverride }}</template>
|
||||
</MkTextarea>
|
||||
<SearchMarker>
|
||||
<MkTextarea v-model="manifestJsonOverride">
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.manifestJsonOverride }}</SearchLabel></template>
|
||||
</MkTextarea>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</FormSuspense>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div :class="$style.footer">
|
||||
@@ -106,7 +134,6 @@ import JSON5 from 'json5';
|
||||
import { host } from '@@/js/config.js';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import MkTextarea from '@/components/MkTextarea.vue';
|
||||
import FormSuspense from '@/components/form/suspense.vue';
|
||||
import * as os from '@/os.js';
|
||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
import { instance, fetchInstance } from '@/instance.js';
|
||||
@@ -115,38 +142,22 @@ import { definePage } from '@/page.js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkColorInput from '@/components/MkColorInput.vue';
|
||||
|
||||
const iconUrl = ref<string | null>(null);
|
||||
const app192IconUrl = ref<string | null>(null);
|
||||
const app512IconUrl = ref<string | null>(null);
|
||||
const bannerUrl = ref<string | null>(null);
|
||||
const backgroundImageUrl = ref<string | null>(null);
|
||||
const themeColor = ref<string | null>(null);
|
||||
const defaultLightTheme = ref<string | null>(null);
|
||||
const defaultDarkTheme = ref<string | null>(null);
|
||||
const serverErrorImageUrl = ref<string | null>(null);
|
||||
const infoImageUrl = ref<string | null>(null);
|
||||
const notFoundImageUrl = ref<string | null>(null);
|
||||
const repositoryUrl = ref<string | null>(null);
|
||||
const feedbackUrl = ref<string | null>(null);
|
||||
const manifestJsonOverride = ref<string>('{}');
|
||||
const meta = await misskeyApi('admin/meta');
|
||||
|
||||
async function init() {
|
||||
const meta = await misskeyApi('admin/meta');
|
||||
iconUrl.value = meta.iconUrl;
|
||||
app192IconUrl.value = meta.app192IconUrl;
|
||||
app512IconUrl.value = meta.app512IconUrl;
|
||||
bannerUrl.value = meta.bannerUrl;
|
||||
backgroundImageUrl.value = meta.backgroundImageUrl;
|
||||
themeColor.value = meta.themeColor;
|
||||
defaultLightTheme.value = meta.defaultLightTheme;
|
||||
defaultDarkTheme.value = meta.defaultDarkTheme;
|
||||
serverErrorImageUrl.value = meta.serverErrorImageUrl;
|
||||
infoImageUrl.value = meta.infoImageUrl;
|
||||
notFoundImageUrl.value = meta.notFoundImageUrl;
|
||||
repositoryUrl.value = meta.repositoryUrl;
|
||||
feedbackUrl.value = meta.feedbackUrl;
|
||||
manifestJsonOverride.value = meta.manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON.parse(meta.manifestJsonOverride), null, '\t');
|
||||
}
|
||||
const iconUrl = ref(meta.iconUrl);
|
||||
const app192IconUrl = ref(meta.app192IconUrl);
|
||||
const app512IconUrl = ref(meta.app512IconUrl);
|
||||
const bannerUrl = ref(meta.bannerUrl);
|
||||
const backgroundImageUrl = ref(meta.backgroundImageUrl);
|
||||
const themeColor = ref(meta.themeColor);
|
||||
const defaultLightTheme = ref(meta.defaultLightTheme);
|
||||
const defaultDarkTheme = ref(meta.defaultDarkTheme);
|
||||
const serverErrorImageUrl = ref(meta.serverErrorImageUrl);
|
||||
const infoImageUrl = ref(meta.infoImageUrl);
|
||||
const notFoundImageUrl = ref(meta.notFoundImageUrl);
|
||||
const repositoryUrl = ref(meta.repositoryUrl);
|
||||
const feedbackUrl = ref(meta.feedbackUrl);
|
||||
const manifestJsonOverride = ref(meta.manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON.parse(meta.manifestJsonOverride), null, '\t'));
|
||||
|
||||
function save() {
|
||||
os.apiWithDialog('admin/update-meta', {
|
||||
|
||||
@@ -6,48 +6,67 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template>
|
||||
<PageWithHeader :tabs="headerTabs">
|
||||
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
|
||||
<FormSuspense :p="init">
|
||||
<SearchMarker path="/admin/email-settings" :label="i18n.ts.emailServer" :keywords="['email']" icon="ti ti-mail">
|
||||
<div class="_gaps_m">
|
||||
<MkSwitch v-model="enableEmail">
|
||||
<template #label>{{ i18n.ts.enableEmail }} ({{ i18n.ts.recommended }})</template>
|
||||
<template #caption>{{ i18n.ts.emailConfigInfo }}</template>
|
||||
</MkSwitch>
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="enableEmail">
|
||||
<template #label><SearchLabel>{{ i18n.ts.enableEmail }}</SearchLabel> ({{ i18n.ts.recommended }})</template>
|
||||
<template #caption><SearchText>{{ i18n.ts.emailConfigInfo }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<template v-if="enableEmail">
|
||||
<MkInput v-model="email" type="email">
|
||||
<template #label>{{ i18n.ts.emailAddress }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="email" type="email">
|
||||
<template #label><SearchLabel>{{ i18n.ts.emailAddress }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<FormSection>
|
||||
<template #label>{{ i18n.ts.smtpConfig }}</template>
|
||||
<SearchMarker>
|
||||
<FormSection>
|
||||
<template #label><SearchLabel>{{ i18n.ts.smtpConfig }}</SearchLabel></template>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<FormSplit :minWidth="280">
|
||||
<MkInput v-model="smtpHost">
|
||||
<template #label>{{ i18n.ts.smtpHost }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="smtpPort" type="number">
|
||||
<template #label>{{ i18n.ts.smtpPort }}</template>
|
||||
</MkInput>
|
||||
</FormSplit>
|
||||
<FormSplit :minWidth="280">
|
||||
<MkInput v-model="smtpUser">
|
||||
<template #label>{{ i18n.ts.smtpUser }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="smtpPass" type="password">
|
||||
<template #label>{{ i18n.ts.smtpPass }}</template>
|
||||
</MkInput>
|
||||
</FormSplit>
|
||||
<FormInfo>{{ i18n.ts.emptyToDisableSmtpAuth }}</FormInfo>
|
||||
<MkSwitch v-model="smtpSecure">
|
||||
<template #label>{{ i18n.ts.smtpSecure }}</template>
|
||||
<template #caption>{{ i18n.ts.smtpSecureInfo }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
</FormSection>
|
||||
<div class="_gaps_m">
|
||||
<FormSplit :minWidth="280">
|
||||
<SearchMarker>
|
||||
<MkInput v-model="smtpHost">
|
||||
<template #label><SearchLabel>{{ i18n.ts.smtpHost }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="smtpPort" type="number">
|
||||
<template #label><SearchLabel>{{ i18n.ts.smtpPort }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
</FormSplit>
|
||||
|
||||
<FormSplit :minWidth="280">
|
||||
<SearchMarker>
|
||||
<MkInput v-model="smtpUser">
|
||||
<template #label><SearchLabel>{{ i18n.ts.smtpUser }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="smtpPass" type="password">
|
||||
<template #label><SearchLabel>{{ i18n.ts.smtpPass }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
</FormSplit>
|
||||
|
||||
<FormInfo>{{ i18n.ts.emptyToDisableSmtpAuth }}</FormInfo>
|
||||
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="smtpSecure">
|
||||
<template #label><SearchLabel>{{ i18n.ts.smtpSecure }}</SearchLabel></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.smtpSecureInfo }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</FormSection>
|
||||
</SearchMarker>
|
||||
</template>
|
||||
</div>
|
||||
</FormSuspense>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div :class="$style.footer">
|
||||
@@ -67,7 +86,6 @@ import { ref, computed } from 'vue';
|
||||
import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import FormInfo from '@/components/MkInfo.vue';
|
||||
import FormSuspense from '@/components/form/suspense.vue';
|
||||
import FormSplit from '@/components/form/split.vue';
|
||||
import FormSection from '@/components/form/section.vue';
|
||||
import * as os from '@/os.js';
|
||||
@@ -77,24 +95,15 @@ import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
|
||||
const enableEmail = ref<boolean>(false);
|
||||
const email = ref<string | null>(null);
|
||||
const smtpSecure = ref<boolean>(false);
|
||||
const smtpHost = ref<string>('');
|
||||
const smtpPort = ref<number>(0);
|
||||
const smtpUser = ref<string>('');
|
||||
const smtpPass = ref<string>('');
|
||||
const meta = await misskeyApi('admin/meta');
|
||||
|
||||
async function init() {
|
||||
const meta = await misskeyApi('admin/meta');
|
||||
enableEmail.value = meta.enableEmail;
|
||||
email.value = meta.email;
|
||||
smtpSecure.value = meta.smtpSecure;
|
||||
smtpHost.value = meta.smtpHost;
|
||||
smtpPort.value = meta.smtpPort;
|
||||
smtpUser.value = meta.smtpUser;
|
||||
smtpPass.value = meta.smtpPass;
|
||||
}
|
||||
const enableEmail = ref(meta.enableEmail);
|
||||
const email = ref(meta.email);
|
||||
const smtpSecure = ref(meta.smtpSecure);
|
||||
const smtpHost = ref(meta.smtpHost);
|
||||
const smtpPort = ref(meta.smtpPort);
|
||||
const smtpUser = ref(meta.smtpUser);
|
||||
const smtpPass = ref(meta.smtpPass);
|
||||
|
||||
async function testEmail() {
|
||||
const { canceled, result: destination } = await os.inputText({
|
||||
|
||||
@@ -6,36 +6,49 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template>
|
||||
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
|
||||
<FormSuspense :p="init">
|
||||
<SearchMarker path="/admin/external-services" :label="i18n.ts.externalServices" :keywords="['external', 'services', 'thirdparty']" icon="ti ti-link">
|
||||
<div class="_gaps_m">
|
||||
<MkFolder>
|
||||
<template #label>Google Analytics<span class="_beta">{{ i18n.ts.beta }}</span></template>
|
||||
<SearchMarker v-slot="slotProps">
|
||||
<MkFolder :defaultOpen="slotProps.isParentOfTarget">
|
||||
<template #label><SearchLabel>Google Analytics</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<MkInput v-model="googleAnalyticsMeasurementId">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>Measurement ID</template>
|
||||
</MkInput>
|
||||
<MkButton primary @click="save_googleAnalytics">Save</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<div class="_gaps_m">
|
||||
<SearchMarker>
|
||||
<MkInput v-model="googleAnalyticsMeasurementId">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label><SearchLabel>Measurement ID</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder>
|
||||
<template #label>DeepL Translation</template>
|
||||
<MkButton primary @click="save_googleAnalytics">Save</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<MkInput v-model="deeplAuthKey">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>DeepL Auth Key</template>
|
||||
</MkInput>
|
||||
<MkSwitch v-model="deeplIsPro">
|
||||
<template #label>Pro account</template>
|
||||
</MkSwitch>
|
||||
<MkButton primary @click="save_deepl">Save</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<SearchMarker v-slot="slotProps">
|
||||
<MkFolder :defaultOpen="slotProps.isParentOfTarget">
|
||||
<template #label><SearchLabel>DeepL Translation</SearchLabel></template>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<SearchMarker>
|
||||
<MkInput v-model="deeplAuthKey">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label><SearchLabel>Auth Key</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="deeplIsPro">
|
||||
<template #label><SearchLabel>Pro account</SearchLabel></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<MkButton primary @click="save_deepl">Save</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</FormSuspense>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</PageWithHeader>
|
||||
</template>
|
||||
@@ -45,7 +58,6 @@ import { ref, computed } from 'vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import FormSuspense from '@/components/form/suspense.vue';
|
||||
import * as os from '@/os.js';
|
||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
import { fetchInstance } from '@/instance.js';
|
||||
@@ -53,17 +65,11 @@ import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
|
||||
const deeplAuthKey = ref<string>('');
|
||||
const deeplIsPro = ref<boolean>(false);
|
||||
const meta = await misskeyApi('admin/meta');
|
||||
|
||||
const googleAnalyticsMeasurementId = ref<string>('');
|
||||
|
||||
async function init() {
|
||||
const meta = await misskeyApi('admin/meta');
|
||||
deeplAuthKey.value = meta.deeplAuthKey ?? '';
|
||||
deeplIsPro.value = meta.deeplIsPro;
|
||||
googleAnalyticsMeasurementId.value = meta.googleAnalyticsMeasurementId ?? '';
|
||||
}
|
||||
const deeplAuthKey = ref(meta.deeplAuthKey ?? '');
|
||||
const deeplIsPro = ref(meta.deeplIsPro);
|
||||
const googleAnalyticsMeasurementId = ref(meta.googleAnalyticsMeasurementId ?? '');
|
||||
|
||||
function save_deepl() {
|
||||
os.apiWithDialog('admin/update-meta', {
|
||||
|
||||
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkInfo v-if="noEmailServer" warn>{{ i18n.ts.noEmailServerWarning }} <MkA to="/admin/email-settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
|
||||
</div>
|
||||
|
||||
<MkSuperMenu :def="menuDef" :grid="narrow"></MkSuperMenu>
|
||||
<MkSuperMenu :def="menuDef" :searchIndex="searchIndex" :grid="narrow"></MkSuperMenu>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -44,6 +44,9 @@ import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
import { lookupUser, lookupUserByEmail, lookupFile } from '@/utility/admin-lookup.js';
|
||||
import { definePage, provideMetadataReceiver, provideReactiveMetadata } from '@/page.js';
|
||||
import { useRouter } from '@/router.js';
|
||||
import { genSearchIndexes } from '@/utility/inapp-search.js';
|
||||
|
||||
const searchIndex = await import('search-index:admin').then(({ searchIndexes }) => genSearchIndexes(searchIndexes));
|
||||
|
||||
const isEmpty = (x: string | null) => x == null || x === '';
|
||||
|
||||
@@ -324,12 +327,6 @@ const headerActions = computed(() => []);
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
definePage(() => INFO.value);
|
||||
|
||||
defineExpose({
|
||||
header: {
|
||||
title: i18n.ts.controlPanel,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -6,140 +6,162 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template>
|
||||
<PageWithHeader :tabs="headerTabs">
|
||||
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
|
||||
<FormSuspense :p="init">
|
||||
<SearchMarker path="/admin/moderation" :label="i18n.ts.moderation" :keywords="['moderation']" icon="ti ti-shield" :inlining="['serverRules']">
|
||||
<div class="_gaps_m">
|
||||
<MkSwitch :modelValue="enableRegistration" @update:modelValue="onChange_enableRegistration">
|
||||
<template #label>{{ i18n.ts._serverSettings.openRegistration }}</template>
|
||||
<template #caption>
|
||||
<div>{{ i18n.ts._serverSettings.thisSettingWillAutomaticallyOffWhenModeratorsInactive }}</div>
|
||||
<div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._serverSettings.openRegistrationWarning }}</div>
|
||||
</template>
|
||||
</MkSwitch>
|
||||
<SearchMarker :keywords="['open', 'registration']">
|
||||
<MkSwitch :modelValue="enableRegistration" @update:modelValue="onChange_enableRegistration">
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.openRegistration }}</SearchLabel></template>
|
||||
<template #caption>
|
||||
<div><SearchText>{{ i18n.ts._serverSettings.thisSettingWillAutomaticallyOffWhenModeratorsInactive }}</SearchText></div>
|
||||
<div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> <SearchText>{{ i18n.ts._serverSettings.openRegistrationWarning }}</SearchText></div>
|
||||
</template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<MkSwitch v-model="emailRequiredForSignup" @change="onChange_emailRequiredForSignup">
|
||||
<template #label>{{ i18n.ts.emailRequiredForSignup }} ({{ i18n.ts.recommended }})</template>
|
||||
</MkSwitch>
|
||||
<SearchMarker :keywords="['email', 'required', 'signup']">
|
||||
<MkSwitch v-model="emailRequiredForSignup" @change="onChange_emailRequiredForSignup">
|
||||
<template #label><SearchLabel>{{ i18n.ts.emailRequiredForSignup }}</SearchLabel> ({{ i18n.ts.recommended }})</template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<MkSelect v-model="ugcVisibilityForVisitor" @update:modelValue="onChange_ugcVisibilityForVisitor">
|
||||
<template #label>{{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor }}</template>
|
||||
<option value="all">{{ i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.all }}</option>
|
||||
<option value="local">{{ i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.localOnly }} ({{ i18n.ts.recommended }})</option>
|
||||
<option value="none">{{ i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.none }}</option>
|
||||
<template #caption>
|
||||
<div>{{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor_description }}</div>
|
||||
<div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor_description2 }}</div>
|
||||
</template>
|
||||
</MkSelect>
|
||||
<SearchMarker :keywords="['ugc', 'content', 'visibility', 'visitor', 'guest']">
|
||||
<MkSelect v-model="ugcVisibilityForVisitor" @update:modelValue="onChange_ugcVisibilityForVisitor">
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor }}</SearchLabel></template>
|
||||
<option value="all">{{ i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.all }}</option>
|
||||
<option value="local">{{ i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.localOnly }} ({{ i18n.ts.recommended }})</option>
|
||||
<option value="none">{{ i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.none }}</option>
|
||||
<template #caption>
|
||||
<div><SearchText>{{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor_description }}</SearchText></div>
|
||||
<div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> <SearchText>{{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor_description2 }}</SearchText></div>
|
||||
</template>
|
||||
</MkSelect>
|
||||
</SearchMarker>
|
||||
|
||||
<FormLink to="/admin/server-rules">{{ i18n.ts.serverRules }}</FormLink>
|
||||
<XServerRules/>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-lock-star"></i></template>
|
||||
<template #label>{{ i18n.ts.preservedUsernames }}</template>
|
||||
<SearchMarker :keywords="['preserved', 'usernames']">
|
||||
<MkFolder>
|
||||
<template #icon><SearchIcon><i class="ti ti-lock-star"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.preservedUsernames }}</SearchLabel></template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="preservedUsernames">
|
||||
<template #caption>{{ i18n.ts.preservedUsernamesDescription }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_preservedUsernames">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="preservedUsernames">
|
||||
<template #caption>{{ i18n.ts.preservedUsernamesDescription }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_preservedUsernames">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-message-exclamation"></i></template>
|
||||
<template #label>{{ i18n.ts.sensitiveWords }}</template>
|
||||
<SearchMarker :keywords="['sensitive', 'words']">
|
||||
<MkFolder>
|
||||
<template #icon><SearchIcon><i class="ti ti-message-exclamation"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.sensitiveWords }}</SearchLabel></template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="sensitiveWords">
|
||||
<template #caption>{{ i18n.ts.sensitiveWordsDescription }}<br>{{ i18n.ts.sensitiveWordsDescription2 }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_sensitiveWords">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="sensitiveWords">
|
||||
<template #caption>{{ i18n.ts.sensitiveWordsDescription }}<br>{{ i18n.ts.sensitiveWordsDescription2 }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_sensitiveWords">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-message-x"></i></template>
|
||||
<template #label>{{ i18n.ts.prohibitedWords }}</template>
|
||||
<SearchMarker :keywords="['prohibited', 'words']">
|
||||
<MkFolder>
|
||||
<template #icon><SearchIcon><i class="ti ti-message-x"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.prohibitedWords }}</SearchLabel></template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="prohibitedWords">
|
||||
<template #caption>{{ i18n.ts.prohibitedWordsDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_prohibitedWords">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="prohibitedWords">
|
||||
<template #caption>{{ i18n.ts.prohibitedWordsDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_prohibitedWords">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-user-x"></i></template>
|
||||
<template #label>{{ i18n.ts.prohibitedWordsForNameOfUser }}</template>
|
||||
<SearchMarker :keywords="['prohibited', 'name', 'user']">
|
||||
<MkFolder>
|
||||
<template #icon><SearchIcon><i class="ti ti-user-x"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.prohibitedWordsForNameOfUser }}</SearchLabel></template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="prohibitedWordsForNameOfUser">
|
||||
<template #caption>{{ i18n.ts.prohibitedWordsForNameOfUserDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_prohibitedWordsForNameOfUser">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="prohibitedWordsForNameOfUser">
|
||||
<template #caption>{{ i18n.ts.prohibitedWordsForNameOfUserDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_prohibitedWordsForNameOfUser">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-eye-off"></i></template>
|
||||
<template #label>{{ i18n.ts.hiddenTags }}</template>
|
||||
<SearchMarker :keywords="['hidden', 'tags', 'hashtags']">
|
||||
<MkFolder>
|
||||
<template #icon><SearchIcon><i class="ti ti-eye-off"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.hiddenTags }}</SearchLabel></template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="hiddenTags">
|
||||
<template #caption>{{ i18n.ts.hiddenTagsDescription }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_hiddenTags">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="hiddenTags">
|
||||
<template #caption>{{ i18n.ts.hiddenTagsDescription }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_hiddenTags">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-eye-off"></i></template>
|
||||
<template #label>{{ i18n.ts.silencedInstances }}</template>
|
||||
<SearchMarker :keywords="['silenced', 'servers', 'hosts']">
|
||||
<MkFolder>
|
||||
<template #icon><SearchIcon><i class="ti ti-eye-off"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.silencedInstances }}</SearchLabel></template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="silencedHosts">
|
||||
<template #caption>{{ i18n.ts.silencedInstancesDescription }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_silencedHosts">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="silencedHosts">
|
||||
<template #caption>{{ i18n.ts.silencedInstancesDescription }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_silencedHosts">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-eye-off"></i></template>
|
||||
<template #label>{{ i18n.ts.mediaSilencedInstances }}</template>
|
||||
<SearchMarker :keywords="['media', 'silenced', 'servers', 'hosts']">
|
||||
<MkFolder>
|
||||
<template #icon><SearchIcon><i class="ti ti-eye-off"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.mediaSilencedInstances }}</SearchLabel></template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="mediaSilencedHosts">
|
||||
<template #caption>{{ i18n.ts.mediaSilencedInstancesDescription }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_mediaSilencedHosts">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="mediaSilencedHosts">
|
||||
<template #caption>{{ i18n.ts.mediaSilencedInstancesDescription }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_mediaSilencedHosts">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-ban"></i></template>
|
||||
<template #label>{{ i18n.ts.blockedInstances }}</template>
|
||||
<SearchMarker :keywords="['blocked', 'servers', 'hosts']">
|
||||
<MkFolder>
|
||||
<template #icon><SearchIcon><i class="ti ti-ban"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.blockedInstances }}</SearchLabel></template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="blockedHosts">
|
||||
<template #caption>{{ i18n.ts.blockedInstancesDescription }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_blockedHosts">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="blockedHosts">
|
||||
<template #caption>{{ i18n.ts.blockedInstancesDescription }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_blockedHosts">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</FormSuspense>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</PageWithHeader>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import XServerRules from './server-rules.vue';
|
||||
import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import MkTextarea from '@/components/MkTextarea.vue';
|
||||
import FormSuspense from '@/components/form/suspense.vue';
|
||||
import * as os from '@/os.js';
|
||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
import { fetchInstance } from '@/instance.js';
|
||||
@@ -150,32 +172,19 @@ import FormLink from '@/components/form/link.vue';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
import MkSelect from '@/components/MkSelect.vue';
|
||||
|
||||
const enableRegistration = ref<boolean>(false);
|
||||
const emailRequiredForSignup = ref<boolean>(false);
|
||||
const ugcVisibilityForVisitor = ref<string>('all');
|
||||
const sensitiveWords = ref<string>('');
|
||||
const prohibitedWords = ref<string>('');
|
||||
const prohibitedWordsForNameOfUser = ref<string>('');
|
||||
const hiddenTags = ref<string>('');
|
||||
const preservedUsernames = ref<string>('');
|
||||
const blockedHosts = ref<string>('');
|
||||
const silencedHosts = ref<string>('');
|
||||
const mediaSilencedHosts = ref<string>('');
|
||||
const meta = await misskeyApi('admin/meta');
|
||||
|
||||
async function init() {
|
||||
const meta = await misskeyApi('admin/meta');
|
||||
enableRegistration.value = !meta.disableRegistration;
|
||||
emailRequiredForSignup.value = meta.emailRequiredForSignup;
|
||||
ugcVisibilityForVisitor.value = meta.ugcVisibilityForVisitor;
|
||||
sensitiveWords.value = meta.sensitiveWords.join('\n');
|
||||
prohibitedWords.value = meta.prohibitedWords.join('\n');
|
||||
prohibitedWordsForNameOfUser.value = meta.prohibitedWordsForNameOfUser.join('\n');
|
||||
hiddenTags.value = meta.hiddenTags.join('\n');
|
||||
preservedUsernames.value = meta.preservedUsernames.join('\n');
|
||||
blockedHosts.value = meta.blockedHosts.join('\n');
|
||||
silencedHosts.value = meta.silencedHosts?.join('\n') ?? '';
|
||||
mediaSilencedHosts.value = meta.mediaSilencedHosts.join('\n');
|
||||
}
|
||||
const enableRegistration = ref(!meta.disableRegistration);
|
||||
const emailRequiredForSignup = ref(meta.emailRequiredForSignup);
|
||||
const ugcVisibilityForVisitor = ref(meta.ugcVisibilityForVisitor);
|
||||
const sensitiveWords = ref(meta.sensitiveWords.join('\n'));
|
||||
const prohibitedWords = ref(meta.prohibitedWords.join('\n'));
|
||||
const prohibitedWordsForNameOfUser = ref(meta.prohibitedWordsForNameOfUser.join('\n'));
|
||||
const hiddenTags = ref(meta.hiddenTags.join('\n'));
|
||||
const preservedUsernames = ref(meta.preservedUsernames.join('\n'));
|
||||
const blockedHosts = ref(meta.blockedHosts.join('\n'));
|
||||
const silencedHosts = ref(meta.silencedHosts?.join('\n') ?? '');
|
||||
const mediaSilencedHosts = ref(meta.mediaSilencedHosts.join('\n'));
|
||||
|
||||
async function onChange_enableRegistration(value: boolean) {
|
||||
if (value) {
|
||||
|
||||
@@ -6,70 +6,94 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template>
|
||||
<PageWithHeader :tabs="headerTabs">
|
||||
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
|
||||
<FormSuspense :p="init">
|
||||
<SearchMarker path="/admin/object-storage" :label="i18n.ts.objectStorage" :keywords="['objectStorage']" icon="ti ti-cloud">
|
||||
<div class="_gaps_m">
|
||||
<MkSwitch v-model="useObjectStorage">{{ i18n.ts.useObjectStorage }}</MkSwitch>
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="useObjectStorage"><SearchLabel>{{ i18n.ts.useObjectStorage }}</SearchLabel></MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<template v-if="useObjectStorage">
|
||||
<MkInput v-model="objectStorageBaseUrl" :placeholder="'https://example.com'" type="url">
|
||||
<template #label>{{ i18n.ts.objectStorageBaseUrl }}</template>
|
||||
<template #caption>{{ i18n.ts.objectStorageBaseUrlDesc }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="objectStorageBaseUrl" :placeholder="'https://example.com'" type="url">
|
||||
<template #label><SearchLabel>{{ i18n.ts.objectStorageBaseUrl }}</SearchLabel></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.objectStorageBaseUrlDesc }}</SearchText></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="objectStorageBucket">
|
||||
<template #label>{{ i18n.ts.objectStorageBucket }}</template>
|
||||
<template #caption>{{ i18n.ts.objectStorageBucketDesc }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="objectStorageBucket">
|
||||
<template #label><SearchLabel>{{ i18n.ts.objectStorageBucket }}</SearchLabel></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.objectStorageBucketDesc }}</SearchText></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="objectStoragePrefix">
|
||||
<template #label>{{ i18n.ts.objectStoragePrefix }}</template>
|
||||
<template #caption>{{ i18n.ts.objectStoragePrefixDesc }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="objectStoragePrefix">
|
||||
<template #label><SearchLabel>{{ i18n.ts.objectStoragePrefix }}</SearchLabel></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.objectStoragePrefixDesc }}</SearchText></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="objectStorageEndpoint" :placeholder="'example.com'">
|
||||
<template #label>{{ i18n.ts.objectStorageEndpoint }}</template>
|
||||
<template #prefix>https://</template>
|
||||
<template #caption>{{ i18n.ts.objectStorageEndpointDesc }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="objectStorageEndpoint" :placeholder="'example.com'">
|
||||
<template #label><SearchLabel>{{ i18n.ts.objectStorageEndpoint }}</SearchLabel></template>
|
||||
<template #prefix>https://</template>
|
||||
<template #caption><SearchText>{{ i18n.ts.objectStorageEndpointDesc }}</SearchText></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="objectStorageRegion">
|
||||
<template #label>{{ i18n.ts.objectStorageRegion }}</template>
|
||||
<template #caption>{{ i18n.ts.objectStorageRegionDesc }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="objectStorageRegion">
|
||||
<template #label><SearchLabel>{{ i18n.ts.objectStorageRegion }}</SearchLabel></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.objectStorageRegionDesc }}</SearchText></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<FormSplit :minWidth="280">
|
||||
<MkInput v-model="objectStorageAccessKey">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>Access key</template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="objectStorageAccessKey">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label><SearchLabel>Access key</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="objectStorageSecretKey" type="password">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>Secret key</template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="objectStorageSecretKey" type="password">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label><SearchLabel>Secret key</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
</FormSplit>
|
||||
|
||||
<MkSwitch v-model="objectStorageUseSSL">
|
||||
<template #label>{{ i18n.ts.objectStorageUseSSL }}</template>
|
||||
<template #caption>{{ i18n.ts.objectStorageUseSSLDesc }}</template>
|
||||
</MkSwitch>
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="objectStorageUseSSL">
|
||||
<template #label><SearchLabel>{{ i18n.ts.objectStorageUseSSL }}</SearchLabel></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.objectStorageUseSSLDesc }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<MkSwitch v-model="objectStorageUseProxy">
|
||||
<template #label>{{ i18n.ts.objectStorageUseProxy }}</template>
|
||||
<template #caption>{{ i18n.ts.objectStorageUseProxyDesc }}</template>
|
||||
</MkSwitch>
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="objectStorageUseProxy">
|
||||
<template #label><SearchLabel>{{ i18n.ts.objectStorageUseProxy }}</SearchLabel></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.objectStorageUseProxyDesc }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<MkSwitch v-model="objectStorageSetPublicRead">
|
||||
<template #label>{{ i18n.ts.objectStorageSetPublicRead }}</template>
|
||||
</MkSwitch>
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="objectStorageSetPublicRead">
|
||||
<template #label><SearchLabel>{{ i18n.ts.objectStorageSetPublicRead }}</SearchLabel></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<MkSwitch v-model="objectStorageS3ForcePathStyle">
|
||||
<template #label>s3ForcePathStyle</template>
|
||||
<template #caption>{{ i18n.ts.s3ForcePathStyleDesc }}</template>
|
||||
</MkSwitch>
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="objectStorageS3ForcePathStyle">
|
||||
<template #label><SearchLabel>s3ForcePathStyle</SearchLabel></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.s3ForcePathStyleDesc }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
</template>
|
||||
</div>
|
||||
</FormSuspense>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div :class="$style.footer">
|
||||
@@ -94,36 +118,21 @@ import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
|
||||
const useObjectStorage = ref<boolean>(false);
|
||||
const objectStorageBaseUrl = ref<string | null>(null);
|
||||
const objectStorageBucket = ref<string | null>(null);
|
||||
const objectStoragePrefix = ref<string | null>(null);
|
||||
const objectStorageEndpoint = ref<string | null>(null);
|
||||
const objectStorageRegion = ref<string | null>(null);
|
||||
const objectStoragePort = ref<number | null>(null);
|
||||
const objectStorageAccessKey = ref<string | null>(null);
|
||||
const objectStorageSecretKey = ref<string | null>(null);
|
||||
const objectStorageUseSSL = ref<boolean>(false);
|
||||
const objectStorageUseProxy = ref<boolean>(false);
|
||||
const objectStorageSetPublicRead = ref<boolean>(false);
|
||||
const objectStorageS3ForcePathStyle = ref<boolean>(true);
|
||||
const meta = await misskeyApi('admin/meta');
|
||||
|
||||
async function init() {
|
||||
const meta = await misskeyApi('admin/meta');
|
||||
useObjectStorage.value = meta.useObjectStorage;
|
||||
objectStorageBaseUrl.value = meta.objectStorageBaseUrl;
|
||||
objectStorageBucket.value = meta.objectStorageBucket;
|
||||
objectStoragePrefix.value = meta.objectStoragePrefix;
|
||||
objectStorageEndpoint.value = meta.objectStorageEndpoint;
|
||||
objectStorageRegion.value = meta.objectStorageRegion;
|
||||
objectStoragePort.value = meta.objectStoragePort;
|
||||
objectStorageAccessKey.value = meta.objectStorageAccessKey;
|
||||
objectStorageSecretKey.value = meta.objectStorageSecretKey;
|
||||
objectStorageUseSSL.value = meta.objectStorageUseSSL;
|
||||
objectStorageUseProxy.value = meta.objectStorageUseProxy;
|
||||
objectStorageSetPublicRead.value = meta.objectStorageSetPublicRead;
|
||||
objectStorageS3ForcePathStyle.value = meta.objectStorageS3ForcePathStyle;
|
||||
}
|
||||
const useObjectStorage = ref(meta.useObjectStorage);
|
||||
const objectStorageBaseUrl = ref(meta.objectStorageBaseUrl);
|
||||
const objectStorageBucket = ref(meta.objectStorageBucket);
|
||||
const objectStoragePrefix = ref(meta.objectStoragePrefix);
|
||||
const objectStorageEndpoint = ref(meta.objectStorageEndpoint);
|
||||
const objectStorageRegion = ref(meta.objectStorageRegion);
|
||||
const objectStoragePort = ref(meta.objectStoragePort);
|
||||
const objectStorageAccessKey = ref(meta.objectStorageAccessKey);
|
||||
const objectStorageSecretKey = ref(meta.objectStorageSecretKey);
|
||||
const objectStorageUseSSL = ref(meta.objectStorageUseSSL);
|
||||
const objectStorageUseProxy = ref(meta.objectStorageUseProxy);
|
||||
const objectStorageSetPublicRead = ref(meta.objectStorageSetPublicRead);
|
||||
const objectStorageS3ForcePathStyle = ref(meta.objectStorageS3ForcePathStyle);
|
||||
|
||||
function save() {
|
||||
os.apiWithDialog('admin/update-meta', {
|
||||
|
||||
@@ -6,131 +6,163 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template>
|
||||
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
|
||||
<div class="_gaps">
|
||||
<div class="_panel" style="padding: 16px;">
|
||||
<MkSwitch v-model="enableServerMachineStats" @change="onChange_enableServerMachineStats">
|
||||
<template #label>{{ i18n.ts.enableServerMachineStats }}</template>
|
||||
<template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
|
||||
<div class="_panel" style="padding: 16px;">
|
||||
<MkSwitch v-model="enableIdenticonGeneration" @change="onChange_enableIdenticonGeneration">
|
||||
<template #label>{{ i18n.ts.enableIdenticonGeneration }}</template>
|
||||
<template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
|
||||
<div class="_panel" style="padding: 16px;">
|
||||
<MkSwitch v-model="enableChartsForRemoteUser" @change="onChange_enableChartsForRemoteUser">
|
||||
<template #label>{{ i18n.ts.enableChartsForRemoteUser }}</template>
|
||||
<template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
|
||||
<div class="_panel" style="padding: 16px;">
|
||||
<MkSwitch v-model="enableStatsForFederatedInstances" @change="onChange_enableStatsForFederatedInstances">
|
||||
<template #label>{{ i18n.ts.enableStatsForFederatedInstances }}</template>
|
||||
<template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
|
||||
<div class="_panel" style="padding: 16px;">
|
||||
<MkSwitch v-model="enableChartsForFederatedInstances" @change="onChange_enableChartsForFederatedInstances">
|
||||
<template #label>{{ i18n.ts.enableChartsForFederatedInstances }}</template>
|
||||
<template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
|
||||
<MkFolder :defaultOpen="true">
|
||||
<template #icon><i class="ti ti-bolt"></i></template>
|
||||
<template #label>Misskey® Fan-out Timeline Technology™ (FTT)</template>
|
||||
<template v-if="fttForm.savedState.enableFanoutTimeline" #suffix>Enabled</template>
|
||||
<template v-else #suffix>Disabled</template>
|
||||
<template v-if="fttForm.modified.value" #footer>
|
||||
<MkFormFooter :form="fttForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkSwitch v-model="fttForm.state.enableFanoutTimeline">
|
||||
<template #label>{{ i18n.ts.enable }}<span v-if="fttForm.modifiedStates.enableFanoutTimeline" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>
|
||||
<div>{{ i18n.ts._serverSettings.fanoutTimelineDescription }}</div>
|
||||
<div><MkLink target="_blank" url="https://misskey-hub.net/docs/for-admin/features/ftt/">{{ i18n.ts.details }}</MkLink></div>
|
||||
</template>
|
||||
</MkSwitch>
|
||||
|
||||
<template v-if="fttForm.state.enableFanoutTimeline">
|
||||
<MkSwitch v-model="fttForm.state.enableFanoutTimelineDbFallback">
|
||||
<template #label>{{ i18n.ts._serverSettings.fanoutTimelineDbFallback }}<span v-if="fttForm.modifiedStates.enableFanoutTimelineDbFallback" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._serverSettings.fanoutTimelineDbFallbackDescription }}</template>
|
||||
<SearchMarker path="/admin/performance" :label="i18n.ts.performance" :keywords="['performance']" icon="ti ti-bolt">
|
||||
<div class="_gaps">
|
||||
<SearchMarker>
|
||||
<div class="_panel" style="padding: 16px;">
|
||||
<MkSwitch v-model="enableServerMachineStats" @change="onChange_enableServerMachineStats">
|
||||
<template #label><SearchLabel>{{ i18n.ts.enableServerMachineStats }}</SearchLabel></template>
|
||||
<template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="fttForm.state.perLocalUserUserTimelineCacheMax" type="number">
|
||||
<template #label>perLocalUserUserTimelineCacheMax<span v-if="fttForm.modifiedStates.perLocalUserUserTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<div class="_panel" style="padding: 16px;">
|
||||
<MkSwitch v-model="enableIdenticonGeneration" @change="onChange_enableIdenticonGeneration">
|
||||
<template #label><SearchLabel>{{ i18n.ts.enableIdenticonGeneration }}</SearchLabel></template>
|
||||
<template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="fttForm.state.perRemoteUserUserTimelineCacheMax" type="number">
|
||||
<template #label>perRemoteUserUserTimelineCacheMax<span v-if="fttForm.modifiedStates.perRemoteUserUserTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<div class="_panel" style="padding: 16px;">
|
||||
<MkSwitch v-model="enableChartsForRemoteUser" @change="onChange_enableChartsForRemoteUser">
|
||||
<template #label><SearchLabel>{{ i18n.ts.enableChartsForRemoteUser }}</SearchLabel></template>
|
||||
<template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="fttForm.state.perUserHomeTimelineCacheMax" type="number">
|
||||
<template #label>perUserHomeTimelineCacheMax<span v-if="fttForm.modifiedStates.perUserHomeTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<div class="_panel" style="padding: 16px;">
|
||||
<MkSwitch v-model="enableStatsForFederatedInstances" @change="onChange_enableStatsForFederatedInstances">
|
||||
<template #label><SearchLabel>{{ i18n.ts.enableStatsForFederatedInstances }}</SearchLabel></template>
|
||||
<template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="fttForm.state.perUserListTimelineCacheMax" type="number">
|
||||
<template #label>perUserListTimelineCacheMax<span v-if="fttForm.modifiedStates.perUserListTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkInput>
|
||||
</template>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<SearchMarker>
|
||||
<div class="_panel" style="padding: 16px;">
|
||||
<MkSwitch v-model="enableChartsForFederatedInstances" @change="onChange_enableChartsForFederatedInstances">
|
||||
<template #label><SearchLabel>{{ i18n.ts.enableChartsForFederatedInstances }}</SearchLabel></template>
|
||||
<template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder :defaultOpen="true">
|
||||
<template #icon><i class="ti ti-bolt"></i></template>
|
||||
<template #label>Misskey® Reactions Boost Technology™ (RBT)<span class="_beta">{{ i18n.ts.beta }}</span></template>
|
||||
<template v-if="rbtForm.savedState.enableReactionsBuffering" #suffix>Enabled</template>
|
||||
<template v-else #suffix>Disabled</template>
|
||||
<template v-if="rbtForm.modified.value" #footer>
|
||||
<MkFormFooter :form="rbtForm"/>
|
||||
</template>
|
||||
<SearchMarker>
|
||||
<MkFolder :defaultOpen="true">
|
||||
<template #icon><SearchIcon><i class="ti ti-bolt"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>Misskey® Fan-out Timeline Technology™ (FTT)</SearchLabel></template>
|
||||
<template v-if="fttForm.savedState.enableFanoutTimeline" #suffix>Enabled</template>
|
||||
<template v-else #suffix>Disabled</template>
|
||||
<template v-if="fttForm.modified.value" #footer>
|
||||
<MkFormFooter :form="fttForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<MkSwitch v-model="rbtForm.state.enableReactionsBuffering">
|
||||
<template #label>{{ i18n.ts.enable }}<span v-if="rbtForm.modifiedStates.enableReactionsBuffering" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._serverSettings.reactionsBufferingDescription }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<div class="_gaps">
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="fttForm.state.enableFanoutTimeline">
|
||||
<template #label><SearchLabel>{{ i18n.ts.enable }}</SearchLabel><span v-if="fttForm.modifiedStates.enableFanoutTimeline" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>
|
||||
<div><SearchText>{{ i18n.ts._serverSettings.fanoutTimelineDescription }}</SearchText></div>
|
||||
<div><MkLink target="_blank" url="https://misskey-hub.net/docs/for-admin/features/ftt/">{{ i18n.ts.details }}</MkLink></div>
|
||||
</template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder :defaultOpen="true">
|
||||
<template #icon><i class="ti ti-recycle"></i></template>
|
||||
<template #label>Remote Notes Cleaning (仮)</template>
|
||||
<template v-if="remoteNotesCleaningForm.savedState.enableRemoteNotesCleaning" #suffix>Enabled</template>
|
||||
<template v-else #suffix>Disabled</template>
|
||||
<template v-if="remoteNotesCleaningForm.modified.value" #footer>
|
||||
<MkFormFooter :form="remoteNotesCleaningForm"/>
|
||||
</template>
|
||||
<template v-if="fttForm.state.enableFanoutTimeline">
|
||||
<SearchMarker :keywords="['db', 'database', 'fallback']">
|
||||
<MkSwitch v-model="fttForm.state.enableFanoutTimelineDbFallback">
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.fanoutTimelineDbFallback }}</SearchLabel><span v-if="fttForm.modifiedStates.enableFanoutTimelineDbFallback" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts._serverSettings.fanoutTimelineDbFallbackDescription }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<MkSwitch v-model="remoteNotesCleaningForm.state.enableRemoteNotesCleaning">
|
||||
<template #label>{{ i18n.ts.enable }}<span v-if="remoteNotesCleaningForm.modifiedStates.enableRemoteNotesCleaning" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._serverSettings.remoteNotesCleaning_description }}</template>
|
||||
</MkSwitch>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="fttForm.state.perLocalUserUserTimelineCacheMax" type="number">
|
||||
<template #label><SearchLabel>perLocalUserUserTimelineCacheMax</SearchLabel><span v-if="fttForm.modifiedStates.perLocalUserUserTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<template v-if="remoteNotesCleaningForm.state.enableRemoteNotesCleaning">
|
||||
<MkInput v-model="remoteNotesCleaningForm.state.remoteNotesCleaningExpiryDaysForEachNotes" type="number">
|
||||
<template #label>{{ i18n.ts._serverSettings.remoteNotesCleaningExpiryDaysForEachNotes }} ({{ i18n.ts.inDays }})<span v-if="remoteNotesCleaningForm.modifiedStates.remoteNotesCleaningExpiryDaysForEachNotes" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #suffix>{{ i18n.ts._time.day }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="fttForm.state.perRemoteUserUserTimelineCacheMax" type="number">
|
||||
<template #label><SearchLabel>perRemoteUserUserTimelineCacheMax</SearchLabel><span v-if="fttForm.modifiedStates.perRemoteUserUserTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="remoteNotesCleaningForm.state.remoteNotesCleaningMaxProcessingDurationInMinutes" type="number">
|
||||
<template #label>{{ i18n.ts._serverSettings.remoteNotesCleaningMaxProcessingDuration }} ({{ i18n.ts.inMinutes }})<span v-if="remoteNotesCleaningForm.modifiedStates.remoteNotesCleaningMaxProcessingDurationInMinutes" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #suffix>{{ i18n.ts._time.minute }}</template>
|
||||
</MkInput>
|
||||
</template>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</div>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="fttForm.state.perUserHomeTimelineCacheMax" type="number">
|
||||
<template #label><SearchLabel>perUserHomeTimelineCacheMax</SearchLabel><span v-if="fttForm.modifiedStates.perUserHomeTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker>
|
||||
<MkInput v-model="fttForm.state.perUserListTimelineCacheMax" type="number">
|
||||
<template #label><SearchLabel>perUserListTimelineCacheMax</SearchLabel><span v-if="fttForm.modifiedStates.perUserListTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
</template>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker>
|
||||
<MkFolder :defaultOpen="true">
|
||||
<template #icon><SearchIcon><i class="ti ti-bolt"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>Misskey® Reactions Boost Technology™ (RBT)</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template>
|
||||
<template v-if="rbtForm.savedState.enableReactionsBuffering" #suffix>Enabled</template>
|
||||
<template v-else #suffix>Disabled</template>
|
||||
<template v-if="rbtForm.modified.value" #footer>
|
||||
<MkFormFooter :form="rbtForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="rbtForm.state.enableReactionsBuffering">
|
||||
<template #label><SearchLabel>{{ i18n.ts.enable }}</SearchLabel><span v-if="rbtForm.modifiedStates.enableReactionsBuffering" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts._serverSettings.reactionsBufferingDescription }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker>
|
||||
<MkFolder :defaultOpen="true">
|
||||
<template #icon><SearchIcon><i class="ti ti-recycle"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>Remote Notes Cleaning (仮)</SearchLabel></template>
|
||||
<template v-if="remoteNotesCleaningForm.savedState.enableRemoteNotesCleaning" #suffix>Enabled</template>
|
||||
<template v-else #suffix>Disabled</template>
|
||||
<template v-if="remoteNotesCleaningForm.modified.value" #footer>
|
||||
<MkFormFooter :form="remoteNotesCleaningForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<MkSwitch v-model="remoteNotesCleaningForm.state.enableRemoteNotesCleaning">
|
||||
<template #label><SearchLabel>{{ i18n.ts.enable }}</SearchLabel><span v-if="remoteNotesCleaningForm.modifiedStates.enableRemoteNotesCleaning" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts._serverSettings.remoteNotesCleaning_description }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
|
||||
<template v-if="remoteNotesCleaningForm.state.enableRemoteNotesCleaning">
|
||||
<MkInput v-model="remoteNotesCleaningForm.state.remoteNotesCleaningExpiryDaysForEachNotes" type="number">
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.remoteNotesCleaningExpiryDaysForEachNotes }}</SearchLabel> ({{ i18n.ts.inDays }})<span v-if="remoteNotesCleaningForm.modifiedStates.remoteNotesCleaningExpiryDaysForEachNotes" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #suffix>{{ i18n.ts._time.day }}</template>
|
||||
</MkInput>
|
||||
|
||||
<MkInput v-model="remoteNotesCleaningForm.state.remoteNotesCleaningMaxProcessingDurationInMinutes" type="number">
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.remoteNotesCleaningMaxProcessingDuration }}</SearchLabel> ({{ i18n.ts.inMinutes }})<span v-if="remoteNotesCleaningForm.modifiedStates.remoteNotesCleaningMaxProcessingDurationInMinutes" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #suffix>{{ i18n.ts._time.minute }}</template>
|
||||
</MkInput>
|
||||
</template>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</PageWithHeader>
|
||||
</template>
|
||||
@@ -243,7 +275,7 @@ const headerActions = computed(() => []);
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
definePage(() => ({
|
||||
title: i18n.ts.other,
|
||||
icon: 'ti ti-adjustments',
|
||||
title: i18n.ts.performance,
|
||||
icon: 'ti ti-bolt',
|
||||
}));
|
||||
</script>
|
||||
|
||||
@@ -6,18 +6,20 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template>
|
||||
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||
<div class="_spacer" style="--MI_SPACER-w: 800px;">
|
||||
<div class="_gaps">
|
||||
<div v-for="relay in relays" :key="relay.inbox" class="relaycxt _panel" style="padding: 16px;">
|
||||
<div>{{ relay.inbox }}</div>
|
||||
<div style="margin: 8px 0;">
|
||||
<i v-if="relay.status === 'accepted'" class="ti ti-check" :class="$style.icon" style="color: var(--MI_THEME-success);"></i>
|
||||
<i v-else-if="relay.status === 'rejected'" class="ti ti-ban" :class="$style.icon" style="color: var(--MI_THEME-error);"></i>
|
||||
<i v-else class="ti ti-clock" :class="$style.icon"></i>
|
||||
<span>{{ i18n.ts._relayStatus[relay.status] }}</span>
|
||||
<SearchMarker path="/admin/relays" :label="i18n.ts.relays" :keywords="['relays']" icon="ti ti-planet">
|
||||
<div class="_gaps">
|
||||
<div v-for="relay in relays" :key="relay.inbox" class="relaycxt _panel" style="padding: 16px;">
|
||||
<div>{{ relay.inbox }}</div>
|
||||
<div style="margin: 8px 0;">
|
||||
<i v-if="relay.status === 'accepted'" class="ti ti-check" :class="$style.icon" style="color: var(--MI_THEME-success);"></i>
|
||||
<i v-else-if="relay.status === 'rejected'" class="ti ti-ban" :class="$style.icon" style="color: var(--MI_THEME-error);"></i>
|
||||
<i v-else class="ti ti-clock" :class="$style.icon"></i>
|
||||
<span>{{ i18n.ts._relayStatus[relay.status] }}</span>
|
||||
</div>
|
||||
<MkButton class="button" inline danger @click="remove(relay.inbox)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton>
|
||||
</div>
|
||||
<MkButton class="button" inline danger @click="remove(relay.inbox)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</PageWithHeader>
|
||||
</template>
|
||||
|
||||
@@ -6,115 +6,153 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template>
|
||||
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
|
||||
<div class="_gaps_m">
|
||||
<XBotProtection/>
|
||||
<SearchMarker path="/admin/security" :label="i18n.ts.security" :keywords="['security']" icon="ti ti-lock" :inlining="['botProtection']">
|
||||
<div class="_gaps_m">
|
||||
<XBotProtection/>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-eye-off"></i></template>
|
||||
<template #label>{{ i18n.ts.sensitiveMediaDetection }}</template>
|
||||
<template v-if="sensitiveMediaDetectionForm.savedState.sensitiveMediaDetection === 'all'" #suffix>{{ i18n.ts.all }}</template>
|
||||
<template v-else-if="sensitiveMediaDetectionForm.savedState.sensitiveMediaDetection === 'local'" #suffix>{{ i18n.ts.localOnly }}</template>
|
||||
<template v-else-if="sensitiveMediaDetectionForm.savedState.sensitiveMediaDetection === 'remote'" #suffix>{{ i18n.ts.remoteOnly }}</template>
|
||||
<template v-else #suffix>{{ i18n.ts.none }}</template>
|
||||
<template v-if="sensitiveMediaDetectionForm.modified.value" #footer>
|
||||
<MkFormFooter :form="sensitiveMediaDetectionForm"/>
|
||||
</template>
|
||||
<SearchMarker v-slot="slotProps" :keywords="['sensitive', 'media', 'detection']">
|
||||
<MkFolder :defaultOpen="slotProps.isParentOfTarget">
|
||||
<template #icon><SearchIcon><i class="ti ti-eye-off"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.sensitiveMediaDetection }}</SearchLabel></template>
|
||||
<template v-if="sensitiveMediaDetectionForm.savedState.sensitiveMediaDetection === 'all'" #suffix>{{ i18n.ts.all }}</template>
|
||||
<template v-else-if="sensitiveMediaDetectionForm.savedState.sensitiveMediaDetection === 'local'" #suffix>{{ i18n.ts.localOnly }}</template>
|
||||
<template v-else-if="sensitiveMediaDetectionForm.savedState.sensitiveMediaDetection === 'remote'" #suffix>{{ i18n.ts.remoteOnly }}</template>
|
||||
<template v-else #suffix>{{ i18n.ts.none }}</template>
|
||||
<template v-if="sensitiveMediaDetectionForm.modified.value" #footer>
|
||||
<MkFormFooter :form="sensitiveMediaDetectionForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<span>{{ i18n.ts._sensitiveMediaDetection.description }}</span>
|
||||
<div class="_gaps_m">
|
||||
<div><SearchText>{{ i18n.ts._sensitiveMediaDetection.description }}</SearchText></div>
|
||||
|
||||
<MkRadios v-model="sensitiveMediaDetectionForm.state.sensitiveMediaDetection">
|
||||
<option value="none">{{ i18n.ts.none }}</option>
|
||||
<option value="all">{{ i18n.ts.all }}</option>
|
||||
<option value="local">{{ i18n.ts.localOnly }}</option>
|
||||
<option value="remote">{{ i18n.ts.remoteOnly }}</option>
|
||||
</MkRadios>
|
||||
<MkRadios v-model="sensitiveMediaDetectionForm.state.sensitiveMediaDetection">
|
||||
<option value="none">{{ i18n.ts.none }}</option>
|
||||
<option value="all">{{ i18n.ts.all }}</option>
|
||||
<option value="local">{{ i18n.ts.localOnly }}</option>
|
||||
<option value="remote">{{ i18n.ts.remoteOnly }}</option>
|
||||
</MkRadios>
|
||||
|
||||
<MkRange v-model="sensitiveMediaDetectionForm.state.sensitiveMediaDetectionSensitivity" :min="0" :max="4" :step="1" :textConverter="(v) => `${v + 1}`">
|
||||
<template #label>{{ i18n.ts._sensitiveMediaDetection.sensitivity }}</template>
|
||||
<template #caption>{{ i18n.ts._sensitiveMediaDetection.sensitivityDescription }}</template>
|
||||
</MkRange>
|
||||
<SearchMarker :keywords="['sensitivity']">
|
||||
<MkRange v-model="sensitiveMediaDetectionForm.state.sensitiveMediaDetectionSensitivity" :min="0" :max="4" :step="1" :textConverter="(v) => `${v + 1}`">
|
||||
<template #label><SearchLabel>{{ i18n.ts._sensitiveMediaDetection.sensitivity }}</SearchLabel></template>
|
||||
<template #caption><SearchText>{{ i18n.ts._sensitiveMediaDetection.sensitivityDescription }}</SearchText></template>
|
||||
</MkRange>
|
||||
</SearchMarker>
|
||||
|
||||
<MkSwitch v-model="sensitiveMediaDetectionForm.state.enableSensitiveMediaDetectionForVideos">
|
||||
<template #label>{{ i18n.ts._sensitiveMediaDetection.analyzeVideos }}<span class="_beta">{{ i18n.ts.beta }}</span></template>
|
||||
<template #caption>{{ i18n.ts._sensitiveMediaDetection.analyzeVideosDescription }}</template>
|
||||
</MkSwitch>
|
||||
<SearchMarker :keywords="['video', 'analyze']">
|
||||
<MkSwitch v-model="sensitiveMediaDetectionForm.state.enableSensitiveMediaDetectionForVideos">
|
||||
<template #label><SearchLabel>{{ i18n.ts._sensitiveMediaDetection.analyzeVideos }}</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts._sensitiveMediaDetection.analyzeVideosDescription }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<MkSwitch v-model="sensitiveMediaDetectionForm.state.setSensitiveFlagAutomatically">
|
||||
<template #label>{{ i18n.ts._sensitiveMediaDetection.setSensitiveFlagAutomatically }} ({{ i18n.ts.notRecommended }})</template>
|
||||
<template #caption>{{ i18n.ts._sensitiveMediaDetection.setSensitiveFlagAutomaticallyDescription }}</template>
|
||||
</MkSwitch>
|
||||
<SearchMarker :keywords="['flag', 'automatically']">
|
||||
<MkSwitch v-model="sensitiveMediaDetectionForm.state.setSensitiveFlagAutomatically">
|
||||
<template #label><SearchLabel>{{ i18n.ts._sensitiveMediaDetection.setSensitiveFlagAutomatically }}</SearchLabel> ({{ i18n.ts.notRecommended }})</template>
|
||||
<template #caption><SearchText>{{ i18n.ts._sensitiveMediaDetection.setSensitiveFlagAutomaticallyDescription }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<!-- 現状 false positive が多すぎて実用に耐えない
|
||||
<!-- 現状 false positive が多すぎて実用に耐えない
|
||||
<MkSwitch v-model="disallowUploadWhenPredictedAsPorn">
|
||||
<template #label>{{ i18n.ts._sensitiveMediaDetection.disallowUploadWhenPredictedAsPorn }}</template>
|
||||
</MkSwitch>
|
||||
-->
|
||||
</div>
|
||||
</MkFolder>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder>
|
||||
<template #label>Active Email Validation</template>
|
||||
<template v-if="emailValidationForm.savedState.enableActiveEmailValidation" #suffix>Enabled</template>
|
||||
<template v-else #suffix>Disabled</template>
|
||||
<template v-if="emailValidationForm.modified.value" #footer>
|
||||
<MkFormFooter :form="emailValidationForm"/>
|
||||
</template>
|
||||
<SearchMarker v-slot="slotProps" :keywords="['email', 'validation']">
|
||||
<MkFolder :defaultOpen="slotProps.isParentOfTarget">
|
||||
<template #label><SearchLabel>Active Email Validation</SearchLabel></template>
|
||||
<template v-if="emailValidationForm.savedState.enableActiveEmailValidation" #suffix>Enabled</template>
|
||||
<template v-else #suffix>Disabled</template>
|
||||
<template v-if="emailValidationForm.modified.value" #footer>
|
||||
<MkFormFooter :form="emailValidationForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<span>{{ i18n.ts.activeEmailValidationDescription }}</span>
|
||||
<MkSwitch v-model="emailValidationForm.state.enableActiveEmailValidation">
|
||||
<template #label>Enable</template>
|
||||
</MkSwitch>
|
||||
<MkSwitch v-model="emailValidationForm.state.enableVerifymailApi">
|
||||
<template #label>Use Verifymail.io API</template>
|
||||
</MkSwitch>
|
||||
<MkInput v-model="emailValidationForm.state.verifymailAuthKey">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>Verifymail.io API Auth Key</template>
|
||||
</MkInput>
|
||||
<MkSwitch v-model="emailValidationForm.state.enableTruemailApi">
|
||||
<template #label>Use TrueMail API</template>
|
||||
</MkSwitch>
|
||||
<MkInput v-model="emailValidationForm.state.truemailInstance">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>TrueMail API Instance</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="emailValidationForm.state.truemailAuthKey">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>TrueMail API Auth Key</template>
|
||||
</MkInput>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<div class="_gaps_m">
|
||||
<div><SearchText>{{ i18n.ts.activeEmailValidationDescription }}</SearchText></div>
|
||||
|
||||
<MkFolder>
|
||||
<template #label>Banned Email Domains</template>
|
||||
<template v-if="bannedEmailDomainsForm.modified.value" #footer>
|
||||
<MkFormFooter :form="bannedEmailDomainsForm"/>
|
||||
</template>
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="emailValidationForm.state.enableActiveEmailValidation">
|
||||
<template #label><SearchLabel>Enable</SearchLabel></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<MkTextarea v-model="bannedEmailDomainsForm.state.bannedEmailDomains">
|
||||
<template #label>Banned Email Domains List</template>
|
||||
</MkTextarea>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="emailValidationForm.state.enableVerifymailApi">
|
||||
<template #label><SearchLabel>Use Verifymail.io API</SearchLabel></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder>
|
||||
<template #label>Log IP address</template>
|
||||
<template v-if="ipLoggingForm.savedState.enableIpLogging" #suffix>Enabled</template>
|
||||
<template v-else #suffix>Disabled</template>
|
||||
<template v-if="ipLoggingForm.modified.value" #footer>
|
||||
<MkFormFooter :form="ipLoggingForm"/>
|
||||
</template>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="emailValidationForm.state.verifymailAuthKey">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label><SearchLabel>Verifymail.io API Auth Key</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<MkSwitch v-model="ipLoggingForm.state.enableIpLogging">
|
||||
<template #label>Enable</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</div>
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="emailValidationForm.state.enableTruemailApi">
|
||||
<template #label><SearchLabel>Use TrueMail API</SearchLabel></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker>
|
||||
<MkInput v-model="emailValidationForm.state.truemailInstance">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label><SearchLabel>TrueMail API Instance</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker>
|
||||
<MkInput v-model="emailValidationForm.state.truemailAuthKey">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label><SearchLabel>TrueMail API Auth Key</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker v-slot="slotProps" :keywords="['banned', 'email', 'domains', 'blacklist']">
|
||||
<MkFolder :defaultOpen="slotProps.isParentOfTarget">
|
||||
<template #label><SearchLabel>Banned Email Domains</SearchLabel></template>
|
||||
<template v-if="bannedEmailDomainsForm.modified.value" #footer>
|
||||
<MkFormFooter :form="bannedEmailDomainsForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<SearchMarker>
|
||||
<MkTextarea v-model="bannedEmailDomainsForm.state.bannedEmailDomains">
|
||||
<template #label><SearchLabel>Banned Email Domains List</SearchLabel></template>
|
||||
</MkTextarea>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker v-slot="slotProps" :keywords="['log', 'ipAddress']">
|
||||
<MkFolder :defaultOpen="slotProps.isParentOfTarget">
|
||||
<template #label><SearchLabel>Log IP address</SearchLabel></template>
|
||||
<template v-if="ipLoggingForm.savedState.enableIpLogging" #suffix>Enabled</template>
|
||||
<template v-else #suffix>Disabled</template>
|
||||
<template v-if="ipLoggingForm.modified.value" #footer>
|
||||
<MkFormFooter :form="ipLoggingForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="ipLoggingForm.state.enableIpLogging">
|
||||
<template #label><SearchLabel>Enable</SearchLabel></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</PageWithHeader>
|
||||
</template>
|
||||
|
||||
@@ -4,10 +4,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<PageWithHeader :tabs="headerTabs">
|
||||
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
|
||||
<SearchMarker markerId="serverRules" :keywords="['rules']">
|
||||
<MkFolder>
|
||||
<template #icon><SearchIcon><i class="ti ti-checkbox"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.serverRules }}</SearchLabel></template>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<div>{{ i18n.ts._serverRules.description }}</div>
|
||||
<div><SearchText>{{ i18n.ts._serverRules.description }}</SearchText></div>
|
||||
|
||||
<Sortable
|
||||
v-model="serverRules"
|
||||
class="_gaps_m"
|
||||
@@ -33,8 +37,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PageWithHeader>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@@ -42,9 +46,9 @@ import { defineAsyncComponent, ref, computed } from 'vue';
|
||||
import * as os from '@/os.js';
|
||||
import { fetchInstance, instance } from '@/instance.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
|
||||
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
|
||||
|
||||
@@ -60,13 +64,6 @@ const save = async () => {
|
||||
const remove = (index: number): void => {
|
||||
serverRules.value.splice(index, 1);
|
||||
};
|
||||
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
definePage(() => ({
|
||||
title: i18n.ts.serverRules,
|
||||
icon: 'ti ti-checkbox',
|
||||
}));
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
||||
@@ -6,292 +6,369 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template>
|
||||
<PageWithHeader :tabs="headerTabs">
|
||||
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
|
||||
<div class="_gaps_m">
|
||||
<MkFolder :defaultOpen="true">
|
||||
<template #icon><i class="ti ti-info-circle"></i></template>
|
||||
<template #label>{{ i18n.ts.info }}</template>
|
||||
<template v-if="infoForm.modified.value" #footer>
|
||||
<MkFormFooter :form="infoForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkInput v-model="infoForm.state.name">
|
||||
<template #label>{{ i18n.ts.instanceName }}<span v-if="infoForm.modifiedStates.name" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkInput>
|
||||
|
||||
<MkInput v-model="infoForm.state.shortName">
|
||||
<template #label>{{ i18n.ts._serverSettings.shortName }} ({{ i18n.ts.optional }})<span v-if="infoForm.modifiedStates.shortName" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._serverSettings.shortNameDescription }}</template>
|
||||
</MkInput>
|
||||
|
||||
<MkTextarea v-model="infoForm.state.description">
|
||||
<template #label>{{ i18n.ts.instanceDescription }}<span v-if="infoForm.modifiedStates.description" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkTextarea>
|
||||
|
||||
<FormSplit :minWidth="300">
|
||||
<MkInput v-model="infoForm.state.maintainerName">
|
||||
<template #label>{{ i18n.ts.maintainerName }}<span v-if="infoForm.modifiedStates.maintainerName" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkInput>
|
||||
|
||||
<MkInput v-model="infoForm.state.maintainerEmail" type="email">
|
||||
<template #label>{{ i18n.ts.maintainerEmail }}<span v-if="infoForm.modifiedStates.maintainerEmail" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #prefix><i class="ti ti-mail"></i></template>
|
||||
</MkInput>
|
||||
</FormSplit>
|
||||
|
||||
<MkInput v-model="infoForm.state.tosUrl" type="url">
|
||||
<template #label>{{ i18n.ts.tosUrl }}<span v-if="infoForm.modifiedStates.tosUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
</MkInput>
|
||||
|
||||
<MkInput v-model="infoForm.state.privacyPolicyUrl" type="url">
|
||||
<template #label>{{ i18n.ts.privacyPolicyUrl }}<span v-if="infoForm.modifiedStates.privacyPolicyUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
</MkInput>
|
||||
|
||||
<MkInput v-model="infoForm.state.inquiryUrl" type="url">
|
||||
<template #label>{{ i18n.ts._serverSettings.inquiryUrl }}<span v-if="infoForm.modifiedStates.inquiryUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._serverSettings.inquiryUrlDescription }}</template>
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
</MkInput>
|
||||
|
||||
<MkInput v-model="infoForm.state.repositoryUrl" type="url">
|
||||
<template #label>{{ i18n.ts.repositoryUrl }}<span v-if="infoForm.modifiedStates.repositoryUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts.repositoryUrlDescription }}</template>
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
</MkInput>
|
||||
|
||||
<MkInfo v-if="!instance.providesTarball && !infoForm.state.repositoryUrl" warn>
|
||||
{{ i18n.ts.repositoryUrlOrTarballRequired }}
|
||||
</MkInfo>
|
||||
|
||||
<MkInput v-model="infoForm.state.impressumUrl" type="url">
|
||||
<template #label>{{ i18n.ts.impressumUrl }}<span v-if="infoForm.modifiedStates.impressumUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts.impressumDescription }}</template>
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
</MkInput>
|
||||
</div>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-user-star"></i></template>
|
||||
<template #label>{{ i18n.ts.pinnedUsers }}</template>
|
||||
<template v-if="pinnedUsersForm.modified.value" #footer>
|
||||
<MkFormFooter :form="pinnedUsersForm"/>
|
||||
</template>
|
||||
|
||||
<MkTextarea v-model="pinnedUsersForm.state.pinnedUsers">
|
||||
<template #label>{{ i18n.ts.pinnedUsers }}<span v-if="pinnedUsersForm.modifiedStates.pinnedUsers" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts.pinnedUsersDescription }}</template>
|
||||
</MkTextarea>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-world-cog"></i></template>
|
||||
<template #label>ServiceWorker</template>
|
||||
<template v-if="serviceWorkerForm.modified.value" #footer>
|
||||
<MkFormFooter :form="serviceWorkerForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkSwitch v-model="serviceWorkerForm.state.enableServiceWorker">
|
||||
<template #label>{{ i18n.ts.enableServiceworker }}<span v-if="serviceWorkerForm.modifiedStates.enableServiceWorker" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts.serviceworkerInfo }}</template>
|
||||
</MkSwitch>
|
||||
|
||||
<template v-if="serviceWorkerForm.state.enableServiceWorker">
|
||||
<MkInput v-model="serviceWorkerForm.state.swPublicKey">
|
||||
<template #label>Public key<span v-if="serviceWorkerForm.modifiedStates.swPublicKey" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
</MkInput>
|
||||
|
||||
<MkInput v-model="serviceWorkerForm.state.swPrivateKey">
|
||||
<template #label>Private key<span v-if="serviceWorkerForm.modifiedStates.swPrivateKey" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
</MkInput>
|
||||
</template>
|
||||
</div>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-ad"></i></template>
|
||||
<template #label>{{ i18n.ts._ad.adsSettings }}</template>
|
||||
<template v-if="adForm.modified.value" #footer>
|
||||
<MkFormFooter :form="adForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps">
|
||||
<div class="_gaps_s">
|
||||
<MkInput v-model="adForm.state.notesPerOneAd" :min="0" type="number">
|
||||
<template #label>{{ i18n.ts._ad.notesPerOneAd }}<span v-if="adForm.modifiedStates.notesPerOneAd" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._ad.setZeroToDisable }}</template>
|
||||
</MkInput>
|
||||
<MkInfo v-if="adForm.state.notesPerOneAd > 0 && adForm.state.notesPerOneAd < 20" :warn="true">
|
||||
{{ i18n.ts._ad.adsTooClose }}
|
||||
</MkInfo>
|
||||
</div>
|
||||
</div>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-world-search"></i></template>
|
||||
<template #label>{{ i18n.ts._urlPreviewSetting.title }}</template>
|
||||
<template v-if="urlPreviewForm.modified.value" #footer>
|
||||
<MkFormFooter :form="urlPreviewForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkSwitch v-model="urlPreviewForm.state.urlPreviewEnabled">
|
||||
<template #label>{{ i18n.ts._urlPreviewSetting.enable }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewEnabled" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkSwitch>
|
||||
|
||||
<template v-if="urlPreviewForm.state.urlPreviewEnabled">
|
||||
<MkSwitch v-model="urlPreviewForm.state.urlPreviewAllowRedirect">
|
||||
<template #label>{{ i18n.ts._urlPreviewSetting.allowRedirect }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewAllowRedirect" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._urlPreviewSetting.allowRedirectDescription }}</template>
|
||||
</MkSwitch>
|
||||
|
||||
<MkSwitch v-model="urlPreviewForm.state.urlPreviewRequireContentLength">
|
||||
<template #label>{{ i18n.ts._urlPreviewSetting.requireContentLength }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewRequireContentLength" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._urlPreviewSetting.requireContentLengthDescription }}</template>
|
||||
</MkSwitch>
|
||||
|
||||
<MkInput v-model="urlPreviewForm.state.urlPreviewMaximumContentLength" type="number">
|
||||
<template #label>{{ i18n.ts._urlPreviewSetting.maximumContentLength }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewMaximumContentLength" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._urlPreviewSetting.maximumContentLengthDescription }}</template>
|
||||
</MkInput>
|
||||
|
||||
<MkInput v-model="urlPreviewForm.state.urlPreviewTimeout" type="number">
|
||||
<template #label>{{ i18n.ts._urlPreviewSetting.timeout }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewTimeout" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._urlPreviewSetting.timeoutDescription }}</template>
|
||||
</MkInput>
|
||||
|
||||
<MkInput v-model="urlPreviewForm.state.urlPreviewUserAgent" type="text">
|
||||
<template #label>{{ i18n.ts._urlPreviewSetting.userAgent }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewUserAgent" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._urlPreviewSetting.userAgentDescription }}</template>
|
||||
</MkInput>
|
||||
|
||||
<div>
|
||||
<MkInput v-model="urlPreviewForm.state.urlPreviewSummaryProxyUrl" type="text">
|
||||
<template #label>{{ i18n.ts._urlPreviewSetting.summaryProxy }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewSummaryProxyUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>[{{ i18n.ts.notUsePleaseLeaveBlank }}] {{ i18n.ts._urlPreviewSetting.summaryProxyDescription }}</template>
|
||||
</MkInput>
|
||||
|
||||
<div :class="$style.subCaption">
|
||||
{{ i18n.ts._urlPreviewSetting.summaryProxyDescription2 }}
|
||||
<ul style="padding-left: 20px; margin: 4px 0">
|
||||
<li>{{ i18n.ts._urlPreviewSetting.timeout }} / key:timeout</li>
|
||||
<li>{{ i18n.ts._urlPreviewSetting.maximumContentLength }} / key:contentLengthLimit</li>
|
||||
<li>{{ i18n.ts._urlPreviewSetting.requireContentLength }} / key:contentLengthRequired</li>
|
||||
<li>{{ i18n.ts._urlPreviewSetting.userAgent }} / key:userAgent</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-planet"></i></template>
|
||||
<template #label>{{ i18n.ts.federation }}</template>
|
||||
<template v-if="federationForm.savedState.federation === 'all'" #suffix>{{ i18n.ts.all }}</template>
|
||||
<template v-else-if="federationForm.savedState.federation === 'specified'" #suffix>{{ i18n.ts.specifyHost }}</template>
|
||||
<template v-else-if="federationForm.savedState.federation === 'none'" #suffix>{{ i18n.ts.none }}</template>
|
||||
<template v-if="federationForm.modified.value" #footer>
|
||||
<MkFormFooter :form="federationForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkRadios v-model="federationForm.state.federation">
|
||||
<template #label>{{ i18n.ts.behavior }}<span v-if="federationForm.modifiedStates.federation" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<option value="all">{{ i18n.ts.all }}</option>
|
||||
<option value="specified">{{ i18n.ts.specifyHost }}</option>
|
||||
<option value="none">{{ i18n.ts.none }}</option>
|
||||
</MkRadios>
|
||||
|
||||
<MkTextarea v-if="federationForm.state.federation === 'specified'" v-model="federationForm.state.federationHosts">
|
||||
<template #label>{{ i18n.ts.federationAllowedHosts }}<span v-if="federationForm.modifiedStates.federationHosts" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts.federationAllowedHostsDescription }}</template>
|
||||
</MkTextarea>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-list"></i></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.deliverSuspendedSoftware }}</SearchLabel></template>
|
||||
<template #footer>
|
||||
<div class="_buttons">
|
||||
<MkButton @click="federationForm.state.deliverSuspendedSoftware.push({software: '', versionRange: ''})"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
|
||||
</div>
|
||||
<SearchMarker path="/admin/settings" :label="i18n.ts.general" :keywords="['general', 'settings']" icon="ti ti-settings">
|
||||
<div class="_gaps_m">
|
||||
<SearchMarker v-slot="slotProps" :keywords="['information', 'meta']">
|
||||
<MkFolder :defaultOpen="true">
|
||||
<template #icon><SearchIcon><i class="ti ti-info-circle"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.info }}</SearchLabel></template>
|
||||
<template v-if="infoForm.modified.value" #footer>
|
||||
<MkFormFooter :form="infoForm"/>
|
||||
</template>
|
||||
|
||||
<div :class="$style.metadataRoot" class="_gaps_s">
|
||||
<MkInfo>{{ i18n.ts._serverSettings.deliverSuspendedSoftwareDescription }}</MkInfo>
|
||||
<div v-for="(element, index) in federationForm.state.deliverSuspendedSoftware" :key="index" v-panel :class="$style.fieldDragItem">
|
||||
<button class="_button" :class="$style.dragItemRemove" @click="federationForm.state.deliverSuspendedSoftware.splice(index, 1)"><i class="ti ti-x"></i></button>
|
||||
<div :class="$style.dragItemForm">
|
||||
<FormSplit :minWidth="200">
|
||||
<MkInput v-model="element.software" small :placeholder="i18n.ts.softwareName">
|
||||
</MkInput>
|
||||
<MkInput v-model="element.versionRange" small :placeholder="i18n.ts.version">
|
||||
</MkInput>
|
||||
</FormSplit>
|
||||
</div>
|
||||
<div class="_gaps">
|
||||
<SearchMarker :keywords="['name']">
|
||||
<MkInput v-model="infoForm.state.name">
|
||||
<template #label><SearchLabel>{{ i18n.ts.instanceName }}</SearchLabel><span v-if="infoForm.modifiedStates.name" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['shortName']">
|
||||
<MkInput v-model="infoForm.state.shortName">
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.shortName }}</SearchLabel> ({{ i18n.ts.optional }})<span v-if="infoForm.modifiedStates.shortName" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts._serverSettings.shortNameDescription }}</SearchText></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['description']">
|
||||
<MkTextarea v-model="infoForm.state.description">
|
||||
<template #label><SearchLabel>{{ i18n.ts.instanceDescription }}</SearchLabel><span v-if="infoForm.modifiedStates.description" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkTextarea>
|
||||
</SearchMarker>
|
||||
|
||||
<FormSplit :minWidth="300">
|
||||
<SearchMarker :keywords="['maintainer', 'name']">
|
||||
<MkInput v-model="infoForm.state.maintainerName">
|
||||
<template #label><SearchLabel>{{ i18n.ts.maintainerName }}</SearchLabel><span v-if="infoForm.modifiedStates.maintainerName" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['maintainer', 'email', 'contact']">
|
||||
<MkInput v-model="infoForm.state.maintainerEmail" type="email">
|
||||
<template #label><SearchLabel>{{ i18n.ts.maintainerEmail }}</SearchLabel><span v-if="infoForm.modifiedStates.maintainerEmail" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #prefix><i class="ti ti-mail"></i></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
</FormSplit>
|
||||
|
||||
<SearchMarker :keywords="['tos', 'termsOfService']">
|
||||
<MkInput v-model="infoForm.state.tosUrl" type="url">
|
||||
<template #label><SearchLabel>{{ i18n.ts.tosUrl }}</SearchLabel><span v-if="infoForm.modifiedStates.tosUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['privacyPolicy']">
|
||||
<MkInput v-model="infoForm.state.privacyPolicyUrl" type="url">
|
||||
<template #label><SearchLabel>{{ i18n.ts.privacyPolicyUrl }}</SearchLabel><span v-if="infoForm.modifiedStates.privacyPolicyUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['inquiry', 'contact']">
|
||||
<MkInput v-model="infoForm.state.inquiryUrl" type="url">
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.inquiryUrl }}</SearchLabel><span v-if="infoForm.modifiedStates.inquiryUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts._serverSettings.inquiryUrlDescription }}</SearchText></template>
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['repository', 'url']">
|
||||
<MkInput v-model="infoForm.state.repositoryUrl" type="url">
|
||||
<template #label><SearchLabel>{{ i18n.ts.repositoryUrl }}</SearchLabel><span v-if="infoForm.modifiedStates.repositoryUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.repositoryUrlDescription }}</SearchText></template>
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInfo v-if="!instance.providesTarball && !infoForm.state.repositoryUrl" warn>
|
||||
{{ i18n.ts.repositoryUrlOrTarballRequired }}
|
||||
</MkInfo>
|
||||
|
||||
<SearchMarker :keywords="['impressum', 'legalNotice']">
|
||||
<MkInput v-model="infoForm.state.impressumUrl" type="url">
|
||||
<template #label><SearchLabel>{{ i18n.ts.impressumUrl }}</SearchLabel><span v-if="infoForm.modifiedStates.impressumUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.impressumDescription }}</SearchText></template>
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker v-slot="slotProps" :keywords="['pinned', 'users']">
|
||||
<MkFolder :defaultOpen="slotProps.isParentOfTarget">
|
||||
<template #icon><SearchIcon><i class="ti ti-user-star"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.pinnedUsers }}</SearchLabel></template>
|
||||
<template v-if="pinnedUsersForm.modified.value" #footer>
|
||||
<MkFormFooter :form="pinnedUsersForm"/>
|
||||
</template>
|
||||
|
||||
<MkTextarea v-model="pinnedUsersForm.state.pinnedUsers">
|
||||
<template #label>{{ i18n.ts.pinnedUsers }}<span v-if="pinnedUsersForm.modifiedStates.pinnedUsers" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.pinnedUsersDescription }}</SearchText></template>
|
||||
</MkTextarea>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker v-slot="slotProps" :keywords="['serviceWorker']">
|
||||
<MkFolder :defaultOpen="slotProps.isParentOfTarget">
|
||||
<template #icon><SearchIcon><i class="ti ti-world-cog"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>ServiceWorker</SearchLabel></template>
|
||||
<template v-if="serviceWorkerForm.modified.value" #footer>
|
||||
<MkFormFooter :form="serviceWorkerForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps">
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="serviceWorkerForm.state.enableServiceWorker">
|
||||
<template #label><SearchLabel>{{ i18n.ts.enableServiceworker }}</SearchLabel><span v-if="serviceWorkerForm.modifiedStates.enableServiceWorker" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.serviceworkerInfo }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<template v-if="serviceWorkerForm.state.enableServiceWorker">
|
||||
<SearchMarker>
|
||||
<MkInput v-model="serviceWorkerForm.state.swPublicKey">
|
||||
<template #label><SearchLabel>Public key</SearchLabel><span v-if="serviceWorkerForm.modifiedStates.swPublicKey" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker>
|
||||
<MkInput v-model="serviceWorkerForm.state.swPrivateKey">
|
||||
<template #label><SearchLabel>Private key</SearchLabel><span v-if="serviceWorkerForm.modifiedStates.swPrivateKey" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
</template>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker v-slot="slotProps" :keywords="['ads']">
|
||||
<MkFolder :defaultOpen="slotProps.isParentOfTarget">
|
||||
<template #icon><SearchIcon><i class="ti ti-ad"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts._ad.adsSettings }}</SearchLabel></template>
|
||||
<template v-if="adForm.modified.value" #footer>
|
||||
<MkFormFooter :form="adForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps">
|
||||
<div class="_gaps_s">
|
||||
<SearchMarker>
|
||||
<MkInput v-model="adForm.state.notesPerOneAd" :min="0" type="number">
|
||||
<template #label><SearchLabel>{{ i18n.ts._ad.notesPerOneAd }}</SearchLabel><span v-if="adForm.modifiedStates.notesPerOneAd" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._ad.setZeroToDisable }}</template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInfo v-if="adForm.state.notesPerOneAd > 0 && adForm.state.notesPerOneAd < 20" :warn="true">
|
||||
{{ i18n.ts._ad.adsTooClose }}
|
||||
</MkInfo>
|
||||
</div>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<MkSwitch v-model="federationForm.state.signToActivityPubGet">
|
||||
<template #label>{{ i18n.ts._serverSettings.signToActivityPubGet }}<span v-if="federationForm.modifiedStates.signToActivityPubGet" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._serverSettings.signToActivityPubGet_description }}</template>
|
||||
</MkSwitch>
|
||||
|
||||
<MkSwitch v-model="federationForm.state.proxyRemoteFiles">
|
||||
<template #label>{{ i18n.ts._serverSettings.proxyRemoteFiles }}<span v-if="federationForm.modifiedStates.proxyRemoteFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._serverSettings.proxyRemoteFiles_description }}</template>
|
||||
</MkSwitch>
|
||||
|
||||
<MkSwitch v-model="federationForm.state.allowExternalApRedirect">
|
||||
<template #label>{{ i18n.ts._serverSettings.allowExternalApRedirect }}<span v-if="federationForm.modifiedStates.allowExternalApRedirect" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>
|
||||
<div>{{ i18n.ts._serverSettings.allowExternalApRedirect_description }}</div>
|
||||
<div>{{ i18n.ts.needToRestartServerToApply }}</div>
|
||||
<SearchMarker v-slot="slotProps" :keywords="['url', 'preview']">
|
||||
<MkFolder :defaultOpen="slotProps.isParentOfTarget">
|
||||
<template #icon><SearchIcon><i class="ti ti-world-search"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.title }}</SearchLabel></template>
|
||||
<template v-if="urlPreviewForm.modified.value" #footer>
|
||||
<MkFormFooter :form="urlPreviewForm"/>
|
||||
</template>
|
||||
</MkSwitch>
|
||||
|
||||
<MkSwitch v-model="federationForm.state.cacheRemoteFiles">
|
||||
<template #label>{{ i18n.ts.cacheRemoteFiles }}<span v-if="federationForm.modifiedStates.cacheRemoteFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts.cacheRemoteFilesDescription }}{{ i18n.ts.youCanCleanRemoteFilesCache }}</template>
|
||||
</MkSwitch>
|
||||
<div class="_gaps">
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="urlPreviewForm.state.urlPreviewEnabled">
|
||||
<template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.enable }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewEnabled" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<template v-if="federationForm.state.cacheRemoteFiles">
|
||||
<MkSwitch v-model="federationForm.state.cacheRemoteSensitiveFiles">
|
||||
<template #label>{{ i18n.ts.cacheRemoteSensitiveFiles }}<span v-if="federationForm.modifiedStates.cacheRemoteSensitiveFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts.cacheRemoteSensitiveFilesDescription }}</template>
|
||||
</MkSwitch>
|
||||
</template>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<template v-if="urlPreviewForm.state.urlPreviewEnabled">
|
||||
<SearchMarker :keywords="['allow', 'redirect']">
|
||||
<MkSwitch v-model="urlPreviewForm.state.urlPreviewAllowRedirect">
|
||||
<template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.allowRedirect }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewAllowRedirect" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._urlPreviewSetting.allowRedirectDescription }}</template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-ghost"></i></template>
|
||||
<template #label>{{ i18n.ts.proxyAccount }}</template>
|
||||
<template v-if="proxyAccountForm.modified.value" #footer>
|
||||
<MkFormFooter :form="proxyAccountForm"/>
|
||||
</template>
|
||||
<SearchMarker :keywords="['contentLength']">
|
||||
<MkSwitch v-model="urlPreviewForm.state.urlPreviewRequireContentLength">
|
||||
<template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.requireContentLength }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewRequireContentLength" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._urlPreviewSetting.requireContentLengthDescription }}</template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkInfo>{{ i18n.ts.proxyAccountDescription }}</MkInfo>
|
||||
<SearchMarker :keywords="['contentLength']">
|
||||
<MkInput v-model="urlPreviewForm.state.urlPreviewMaximumContentLength" type="number">
|
||||
<template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.maximumContentLength }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewMaximumContentLength" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._urlPreviewSetting.maximumContentLengthDescription }}</template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkTextarea v-model="proxyAccountForm.state.description" :max="500" tall mfmAutocomplete :mfmPreview="true">
|
||||
<template #label>{{ i18n.ts._profile.description }}</template>
|
||||
<template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template>
|
||||
</MkTextarea>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<SearchMarker :keywords="['timeout']">
|
||||
<MkInput v-model="urlPreviewForm.state.urlPreviewTimeout" type="number">
|
||||
<template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.timeout }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewTimeout" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._urlPreviewSetting.timeoutDescription }}</template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkButton primary @click="openSetupWizard">
|
||||
Open setup wizard
|
||||
</MkButton>
|
||||
</div>
|
||||
<SearchMarker :keywords="['userAgent']">
|
||||
<MkInput v-model="urlPreviewForm.state.urlPreviewUserAgent" type="text">
|
||||
<template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.userAgent }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewUserAgent" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._urlPreviewSetting.userAgentDescription }}</template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<div>
|
||||
<SearchMarker :keywords="['proxy']">
|
||||
<MkInput v-model="urlPreviewForm.state.urlPreviewSummaryProxyUrl" type="text">
|
||||
<template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.summaryProxy }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewSummaryProxyUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>[{{ i18n.ts.notUsePleaseLeaveBlank }}] {{ i18n.ts._urlPreviewSetting.summaryProxyDescription }}</template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<div :class="$style.subCaption">
|
||||
{{ i18n.ts._urlPreviewSetting.summaryProxyDescription2 }}
|
||||
<ul style="padding-left: 20px; margin: 4px 0">
|
||||
<li>{{ i18n.ts._urlPreviewSetting.timeout }} / key:timeout</li>
|
||||
<li>{{ i18n.ts._urlPreviewSetting.maximumContentLength }} / key:contentLengthLimit</li>
|
||||
<li>{{ i18n.ts._urlPreviewSetting.requireContentLength }} / key:contentLengthRequired</li>
|
||||
<li>{{ i18n.ts._urlPreviewSetting.userAgent }} / key:userAgent</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker v-slot="slotProps" :keywords="['federation']">
|
||||
<MkFolder :defaultOpen="slotProps.isParentOfTarget">
|
||||
<template #icon><SearchIcon><i class="ti ti-planet"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.federation }}</SearchLabel></template>
|
||||
<template v-if="federationForm.savedState.federation === 'all'" #suffix>{{ i18n.ts.all }}</template>
|
||||
<template v-else-if="federationForm.savedState.federation === 'specified'" #suffix>{{ i18n.ts.specifyHost }}</template>
|
||||
<template v-else-if="federationForm.savedState.federation === 'none'" #suffix>{{ i18n.ts.none }}</template>
|
||||
<template v-if="federationForm.modified.value" #footer>
|
||||
<MkFormFooter :form="federationForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps">
|
||||
<SearchMarker>
|
||||
<MkRadios v-model="federationForm.state.federation">
|
||||
<template #label><SearchLabel>{{ i18n.ts.behavior }}</SearchLabel><span v-if="federationForm.modifiedStates.federation" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<option value="all">{{ i18n.ts.all }}</option>
|
||||
<option value="specified">{{ i18n.ts.specifyHost }}</option>
|
||||
<option value="none">{{ i18n.ts.none }}</option>
|
||||
</MkRadios>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['hosts']">
|
||||
<MkTextarea v-if="federationForm.state.federation === 'specified'" v-model="federationForm.state.federationHosts">
|
||||
<template #label><SearchLabel>{{ i18n.ts.federationAllowedHosts }}</SearchLabel><span v-if="federationForm.modifiedStates.federationHosts" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts.federationAllowedHostsDescription }}</template>
|
||||
</MkTextarea>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['suspended', 'software']">
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-list"></i></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.deliverSuspendedSoftware }}</SearchLabel></template>
|
||||
<template #footer>
|
||||
<div class="_buttons">
|
||||
<MkButton @click="federationForm.state.deliverSuspendedSoftware.push({software: '', versionRange: ''})"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div :class="$style.metadataRoot" class="_gaps_s">
|
||||
<MkInfo>{{ i18n.ts._serverSettings.deliverSuspendedSoftwareDescription }}</MkInfo>
|
||||
<div v-for="(element, index) in federationForm.state.deliverSuspendedSoftware" :key="index" v-panel :class="$style.fieldDragItem">
|
||||
<button class="_button" :class="$style.dragItemRemove" @click="federationForm.state.deliverSuspendedSoftware.splice(index, 1)"><i class="ti ti-x"></i></button>
|
||||
<div :class="$style.dragItemForm">
|
||||
<FormSplit :minWidth="200">
|
||||
<MkInput v-model="element.software" small :placeholder="i18n.ts.softwareName">
|
||||
</MkInput>
|
||||
<MkInput v-model="element.versionRange" small :placeholder="i18n.ts.version">
|
||||
</MkInput>
|
||||
</FormSplit>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['sign', 'get']">
|
||||
<MkSwitch v-model="federationForm.state.signToActivityPubGet">
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.signToActivityPubGet }}</SearchLabel><span v-if="federationForm.modifiedStates.signToActivityPubGet" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts._serverSettings.signToActivityPubGet_description }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['proxy', 'remote', 'files']">
|
||||
<MkSwitch v-model="federationForm.state.proxyRemoteFiles">
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.proxyRemoteFiles }}</SearchLabel><span v-if="federationForm.modifiedStates.proxyRemoteFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts._serverSettings.proxyRemoteFiles_description }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['allow', 'external', 'redirect']">
|
||||
<MkSwitch v-model="federationForm.state.allowExternalApRedirect">
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.allowExternalApRedirect }}</SearchLabel><span v-if="federationForm.modifiedStates.allowExternalApRedirect" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>
|
||||
<div><SearchText>{{ i18n.ts._serverSettings.allowExternalApRedirect_description }}</SearchText></div>
|
||||
<div>{{ i18n.ts.needToRestartServerToApply }}</div>
|
||||
</template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['cache', 'remote', 'files']">
|
||||
<MkSwitch v-model="federationForm.state.cacheRemoteFiles">
|
||||
<template #label><SearchLabel>{{ i18n.ts.cacheRemoteFiles }}</SearchLabel><span v-if="federationForm.modifiedStates.cacheRemoteFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.cacheRemoteFilesDescription }}</SearchText>{{ i18n.ts.youCanCleanRemoteFilesCache }}</template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<template v-if="federationForm.state.cacheRemoteFiles">
|
||||
<SearchMarker :keywords="['cache', 'remote', 'sensitive', 'files']">
|
||||
<MkSwitch v-model="federationForm.state.cacheRemoteSensitiveFiles">
|
||||
<template #label><SearchLabel>{{ i18n.ts.cacheRemoteSensitiveFiles }}</SearchLabel><span v-if="federationForm.modifiedStates.cacheRemoteSensitiveFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.cacheRemoteSensitiveFilesDescription }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
</template>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker v-slot="slotProps" :keywords="['proxy', 'account']">
|
||||
<MkFolder :defaultOpen="slotProps.isParentOfTarget">
|
||||
<template #icon><SearchIcon><i class="ti ti-ghost"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.proxyAccount }}</SearchLabel></template>
|
||||
<template v-if="proxyAccountForm.modified.value" #footer>
|
||||
<MkFormFooter :form="proxyAccountForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkInfo>{{ i18n.ts.proxyAccountDescription }}</MkInfo>
|
||||
|
||||
<SearchMarker :keywords="['description']">
|
||||
<MkTextarea v-model="proxyAccountForm.state.description" :max="500" tall mfmAutocomplete :mfmPreview="true">
|
||||
<template #label><SearchLabel>{{ i18n.ts._profile.description }}</SearchLabel></template>
|
||||
<template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template>
|
||||
</MkTextarea>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<MkButton primary @click="openSetupWizard">
|
||||
Open setup wizard
|
||||
</MkButton>
|
||||
</div>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</PageWithHeader>
|
||||
</template>
|
||||
|
||||
@@ -6,17 +6,21 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template>
|
||||
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||
<div class="_spacer" style="--MI_SPACER-w: 900px;">
|
||||
<div class="_gaps_m">
|
||||
<MkButton primary @click="onCreateWebhookClicked">
|
||||
<i class="ti ti-plus"></i> {{ i18n.ts._webhookSettings.createWebhook }}
|
||||
</MkButton>
|
||||
<SearchMarker path="/admin/system-webhook" label="SystemWebhook" :keywords="['webhook']" icon="ti ti-webhook">
|
||||
<div class="_gaps_m">
|
||||
<SearchMarker>
|
||||
<MkButton primary @click="onCreateWebhookClicked">
|
||||
<i class="ti ti-plus"></i> <SearchLabel>{{ i18n.ts._webhookSettings.createWebhook }}</SearchLabel>
|
||||
</MkButton>
|
||||
</SearchMarker>
|
||||
|
||||
<FormSection>
|
||||
<div class="_gaps">
|
||||
<XItem v-for="item in webhooks" :key="item.id" :entity="item" @edit="onEditButtonClicked" @delete="onDeleteButtonClicked"/>
|
||||
</div>
|
||||
</FormSection>
|
||||
</div>
|
||||
<FormSection>
|
||||
<div class="_gaps">
|
||||
<XItem v-for="item in webhooks" :key="item.id" :entity="item" @edit="onEditButtonClicked" @delete="onDeleteButtonClicked"/>
|
||||
</div>
|
||||
</FormSection>
|
||||
</div>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</PageWithHeader>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user