summaryrefslogtreecommitdiff
path: root/packages/client/src/pages/admin
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2022-01-27 00:17:13 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2022-01-27 00:17:13 +0900
commit5f5f68cdcd31653cef2ae6bd29ce8bfcf60113ff (patch)
tree51e9e6179f6d1bda3013d1412f6e43f9f8f70e86 /packages/client/src/pages/admin
parentMerge branch 'develop' (diff)
parent12.102.0 (diff)
downloadmisskey-5f5f68cdcd31653cef2ae6bd29ce8bfcf60113ff.tar.gz
misskey-5f5f68cdcd31653cef2ae6bd29ce8bfcf60113ff.tar.bz2
misskey-5f5f68cdcd31653cef2ae6bd29ce8bfcf60113ff.zip
Merge branch 'develop'
Diffstat (limited to 'packages/client/src/pages/admin')
-rw-r--r--packages/client/src/pages/admin/abuses.vue88
-rw-r--r--packages/client/src/pages/admin/ads.vue10
-rw-r--r--packages/client/src/pages/admin/announcements.vue4
-rw-r--r--packages/client/src/pages/admin/bot-protection.vue105
-rw-r--r--packages/client/src/pages/admin/database.vue35
-rw-r--r--packages/client/src/pages/admin/email-settings.vue100
-rw-r--r--packages/client/src/pages/admin/emoji-edit-dialog.vue2
-rw-r--r--packages/client/src/pages/admin/emojis.vue361
-rw-r--r--packages/client/src/pages/admin/files-settings.vue93
-rw-r--r--packages/client/src/pages/admin/files.vue28
-rw-r--r--packages/client/src/pages/admin/index.vue34
-rw-r--r--packages/client/src/pages/admin/instance-block.vue30
-rw-r--r--packages/client/src/pages/admin/instance.vue291
-rw-r--r--packages/client/src/pages/admin/integrations.discord.vue (renamed from packages/client/src/pages/admin/integrations-discord.vue)40
-rw-r--r--packages/client/src/pages/admin/integrations.github.vue (renamed from packages/client/src/pages/admin/integrations-github.vue)40
-rw-r--r--packages/client/src/pages/admin/integrations.twitter.vue (renamed from packages/client/src/pages/admin/integrations-twitter.vue)40
-rw-r--r--packages/client/src/pages/admin/integrations.vue58
-rw-r--r--packages/client/src/pages/admin/metrics.vue6
-rw-r--r--packages/client/src/pages/admin/object-storage.vue126
-rw-r--r--packages/client/src/pages/admin/other-settings.vue54
-rw-r--r--packages/client/src/pages/admin/overview.vue14
-rw-r--r--packages/client/src/pages/admin/proxy-account.vue42
-rw-r--r--packages/client/src/pages/admin/queue.vue15
-rw-r--r--packages/client/src/pages/admin/relays.vue52
-rw-r--r--packages/client/src/pages/admin/security.vue73
-rw-r--r--packages/client/src/pages/admin/service-worker.vue85
-rw-r--r--packages/client/src/pages/admin/settings.vue226
-rw-r--r--packages/client/src/pages/admin/users.vue26
28 files changed, 802 insertions, 1276 deletions
diff --git a/packages/client/src/pages/admin/abuses.vue b/packages/client/src/pages/admin/abuses.vue
index 8df20097b3..92f93797ce 100644
--- a/packages/client/src/pages/admin/abuses.vue
+++ b/packages/client/src/pages/admin/abuses.vue
@@ -34,27 +34,7 @@
-->
<MkPagination v-slot="{items}" ref="reports" :pagination="pagination" style="margin-top: var(--margin);">
- <div v-for="report in items" :key="report.id" class="bcekxzvu _card _gap">
- <div class="_content target">
- <MkAvatar class="avatar" :user="report.targetUser" :show-indicator="true"/>
- <div class="info">
- <MkUserName class="name" :user="report.targetUser"/>
- <div class="acct">@{{ acct(report.targetUser) }}</div>
- </div>
- </div>
- <div class="_content">
- <div>
- <Mfm :text="report.comment"/>
- </div>
- <hr>
- <div>Reporter: <MkAcct :user="report.reporter"/></div>
- <div><MkTime :time="report.createdAt"/></div>
- </div>
- <div class="_footer">
- <div v-if="report.assignee">Assignee: <MkAcct :user="report.assignee"/></div>
- <MkButton v-if="!report.resolved" primary @click="resolve(report)">{{ $ts.abuseMarkAsResolved }}</MkButton>
- </div>
- </div>
+ <XAbuseReport v-for="report in items" :key="report.id" :report="report" @resolved="resolved"/>
</MkPagination>
</div>
</div>
@@ -62,22 +42,21 @@
</template>
<script lang="ts">
-import { defineComponent } from 'vue';
+import { computed, defineComponent } from 'vue';
-import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/form/input.vue';
import MkSelect from '@/components/form/select.vue';
import MkPagination from '@/components/ui/pagination.vue';
-import { acct } from '@/filters/user';
+import XAbuseReport from '@/components/abuse-report.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
export default defineComponent({
components: {
- MkButton,
MkInput,
MkSelect,
MkPagination,
+ XAbuseReport,
},
emits: ['info'],
@@ -95,44 +74,20 @@ export default defineComponent({
reporterOrigin: 'combined',
targetUserOrigin: 'combined',
pagination: {
- endpoint: 'admin/abuse-user-reports',
+ endpoint: 'admin/abuse-user-reports' as const,
limit: 10,
- params: () => ({
+ params: computed(() => ({
state: this.state,
reporterOrigin: this.reporterOrigin,
targetUserOrigin: this.targetUserOrigin,
- }),
+ })),
},
}
},
- watch: {
- state() {
- this.$refs.reports.reload();
- },
-
- reporterOrigin() {
- this.$refs.reports.reload();
- },
-
- targetUserOrigin() {
- this.$refs.reports.reload();
- },
- },
-
- mounted() {
- this.$emit('info', this[symbols.PAGE_INFO]);
- },
-
methods: {
- acct,
-
- resolve(report) {
- os.apiWithDialog('admin/resolve-abuse-user-report', {
- reportId: report.id,
- }).then(() => {
- this.$refs.reports.removeItem(item => item.id === report.id);
- });
+ resolved(reportId) {
+ this.$refs.reports.removeItem(item => item.id === reportId);
},
}
});
@@ -142,29 +97,4 @@ export default defineComponent({
.lcixvhis {
margin: var(--margin);
}
-
-.bcekxzvu {
- > .target {
- display: flex;
- width: 100%;
- box-sizing: border-box;
- text-align: left;
- align-items: center;
-
- > .avatar {
- width: 42px;
- height: 42px;
- }
-
- > .info {
- margin-left: 0.3em;
- padding: 0 8px;
- flex: 1;
-
- > .name {
- font-weight: bold;
- }
- }
- }
-}
</style>
diff --git a/packages/client/src/pages/admin/ads.vue b/packages/client/src/pages/admin/ads.vue
index d12ed8563e..8f164caa99 100644
--- a/packages/client/src/pages/admin/ads.vue
+++ b/packages/client/src/pages/admin/ads.vue
@@ -23,14 +23,14 @@
<MkRadio v-model="ad.priority" value="low">{{ $ts.low }}</MkRadio>
</div>
-->
- <div class="_inputSplit">
+ <FormSplit>
<MkInput v-model="ad.ratio" type="number">
<template #label>{{ $ts.ratio }}</template>
</MkInput>
<MkInput v-model="ad.expiresAt" type="date">
<template #label>{{ $ts.expiration }}</template>
</MkInput>
- </div>
+ </FormSplit>
<MkTextarea v-model="ad.memo" class="_formBlock">
<template #label>{{ $ts.memo }}</template>
</MkTextarea>
@@ -49,6 +49,7 @@ import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/form/input.vue';
import MkTextarea from '@/components/form/textarea.vue';
import FormRadios from '@/components/form/radios.vue';
+import FormSplit from '@/components/form/split.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
@@ -58,6 +59,7 @@ export default defineComponent({
MkInput,
MkTextarea,
FormRadios,
+ FormSplit,
},
emits: ['info'],
@@ -85,10 +87,6 @@ export default defineComponent({
});
},
- mounted() {
- this.$emit('info', this[symbols.PAGE_INFO]);
- },
-
methods: {
add() {
this.ads.unshift({
diff --git a/packages/client/src/pages/admin/announcements.vue b/packages/client/src/pages/admin/announcements.vue
index 3614cb1441..a0d720bb29 100644
--- a/packages/client/src/pages/admin/announcements.vue
+++ b/packages/client/src/pages/admin/announcements.vue
@@ -61,10 +61,6 @@ export default defineComponent({
});
},
- mounted() {
- this.$emit('info', this[symbols.PAGE_INFO]);
- },
-
methods: {
add() {
this.announcements.unshift({
diff --git a/packages/client/src/pages/admin/bot-protection.vue b/packages/client/src/pages/admin/bot-protection.vue
index 5a97083841..82ab155317 100644
--- a/packages/client/src/pages/admin/bot-protection.vue
+++ b/packages/client/src/pages/admin/bot-protection.vue
@@ -1,70 +1,55 @@
<template>
-<FormBase>
+<div>
<FormSuspense :p="init">
- <FormRadios v-model="provider">
- <template #desc><i class="fas fa-shield-alt"></i> {{ $ts.botProtection }}</template>
- <option :value="null">{{ $ts.none }} ({{ $ts.notRecommended }})</option>
- <option value="hcaptcha">hCaptcha</option>
- <option value="recaptcha">reCAPTCHA</option>
- </FormRadios>
+ <div class="_formRoot">
+ <FormRadios v-model="provider" class="_formBlock">
+ <option :value="null">{{ $ts.none }} ({{ $ts.notRecommended }})</option>
+ <option value="hcaptcha">hCaptcha</option>
+ <option value="recaptcha">reCAPTCHA</option>
+ </FormRadios>
- <template v-if="provider === 'hcaptcha'">
- <div v-sticky-container class="_debobigegoItem _debobigegoNoConcat">
- <div class="_debobigegoLabel">hCaptcha</div>
- <div class="main">
- <FormInput v-model="hcaptchaSiteKey">
- <template #prefix><i class="fas fa-key"></i></template>
- <span>{{ $ts.hcaptchaSiteKey }}</span>
- </FormInput>
- <FormInput v-model="hcaptchaSecretKey">
- <template #prefix><i class="fas fa-key"></i></template>
- <span>{{ $ts.hcaptchaSecretKey }}</span>
- </FormInput>
- </div>
- </div>
- <div v-sticky-container class="_debobigegoItem _debobigegoNoConcat">
- <div class="_debobigegoLabel">{{ $ts.preview }}</div>
- <div class="_debobigegoPanel" style="padding: var(--debobigegoContentHMargin);">
+ <template v-if="provider === 'hcaptcha'">
+ <FormInput v-model="hcaptchaSiteKey" class="_formBlock">
+ <template #prefix><i class="fas fa-key"></i></template>
+ <template #label>{{ $ts.hcaptchaSiteKey }}</template>
+ </FormInput>
+ <FormInput v-model="hcaptchaSecretKey" class="_formBlock">
+ <template #prefix><i class="fas fa-key"></i></template>
+ <template #label>{{ $ts.hcaptchaSecretKey }}</template>
+ </FormInput>
+ <FormSlot class="_formBlock">
+ <template #label>{{ $ts.preview }}</template>
<MkCaptcha provider="hcaptcha" :sitekey="hcaptchaSiteKey || '10000000-ffff-ffff-ffff-000000000001'"/>
- </div>
- </div>
- </template>
- <template v-else-if="provider === 'recaptcha'">
- <div v-sticky-container class="_debobigegoItem _debobigegoNoConcat">
- <div class="_debobigegoLabel">reCAPTCHA</div>
- <div class="main">
- <FormInput v-model="recaptchaSiteKey">
- <template #prefix><i class="fas fa-key"></i></template>
- <span>{{ $ts.recaptchaSiteKey }}</span>
- </FormInput>
- <FormInput v-model="recaptchaSecretKey">
- <template #prefix><i class="fas fa-key"></i></template>
- <span>{{ $ts.recaptchaSecretKey }}</span>
- </FormInput>
- </div>
- </div>
- <div v-if="recaptchaSiteKey" v-sticky-container class="_debobigegoItem _debobigegoNoConcat">
- <div class="_debobigegoLabel">{{ $ts.preview }}</div>
- <div class="_debobigegoPanel" style="padding: var(--debobigegoContentHMargin);">
+ </FormSlot>
+ </template>
+ <template v-else-if="provider === 'recaptcha'">
+ <FormInput v-model="recaptchaSiteKey" class="_formBlock">
+ <template #prefix><i class="fas fa-key"></i></template>
+ <template #label>{{ $ts.recaptchaSiteKey }}</template>
+ </FormInput>
+ <FormInput v-model="recaptchaSecretKey" class="_formBlock">
+ <template #prefix><i class="fas fa-key"></i></template>
+ <template #label>{{ $ts.recaptchaSecretKey }}</template>
+ </FormInput>
+ <FormSlot v-if="recaptchaSiteKey" class="_formBlock">
+ <template #label>{{ $ts.preview }}</template>
<MkCaptcha provider="recaptcha" :sitekey="recaptchaSiteKey"/>
- </div>
- </div>
- </template>
+ </FormSlot>
+ </template>
- <FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+ <FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+ </div>
</FormSuspense>
-</FormBase>
+</div>
</template>
<script lang="ts">
import { defineAsyncComponent, defineComponent } from 'vue';
-import FormRadios from '@/components/debobigego/radios.vue';
-import FormInput from '@/components/debobigego/input.vue';
-import FormButton from '@/components/debobigego/button.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormGroup from '@/components/debobigego/group.vue';
-import FormInfo from '@/components/debobigego/info.vue';
-import FormSuspense from '@/components/debobigego/suspense.vue';
+import FormRadios from '@/components/form/radios.vue';
+import FormInput from '@/components/form/input.vue';
+import FormButton from '@/components/ui/button.vue';
+import FormSuspense from '@/components/form/suspense.vue';
+import FormSlot from '@/components/form/slot.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
@@ -73,11 +58,9 @@ export default defineComponent({
components: {
FormRadios,
FormInput,
- FormBase,
- FormGroup,
FormButton,
- FormInfo,
FormSuspense,
+ FormSlot,
MkCaptcha: defineAsyncComponent(() => import('@/components/captcha.vue')),
},
@@ -99,10 +82,6 @@ export default defineComponent({
}
},
- async mounted() {
- this.$emit('info', this[symbols.PAGE_INFO]);
- },
-
methods: {
async init() {
const meta = await os.api('meta', { detail: true });
diff --git a/packages/client/src/pages/admin/database.vue b/packages/client/src/pages/admin/database.vue
index b09f1ad867..3a835eeafa 100644
--- a/packages/client/src/pages/admin/database.vue
+++ b/packages/client/src/pages/admin/database.vue
@@ -1,28 +1,18 @@
<template>
-<FormBase>
+<MkSpacer :content-max="800" :margin-min="16" :margin-max="32">
<FormSuspense v-slot="{ result: database }" :p="databasePromiseFactory">
- <FormGroup v-for="table in database" :key="table[0]">
- <template #label>{{ table[0] }}</template>
- <FormKeyValueView>
- <template #key>Size</template>
- <template #value>{{ bytes(table[1].size) }}</template>
- </FormKeyValueView>
- <FormKeyValueView>
- <template #key>Records</template>
- <template #value>{{ number(table[1].count) }}</template>
- </FormKeyValueView>
- </FormGroup>
+ <MkKeyValue v-for="table in database" :key="table[0]" oneline style="margin: 1em 0;">
+ <template #key>{{ table[0] }}</template>
+ <template #value>{{ bytes(table[1].size) }} ({{ number(table[1].count) }} recs)</template>
+ </MkKeyValue>
</FormSuspense>
-</FormBase>
+</MkSpacer>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSuspense from '@/components/debobigego/suspense.vue';
-import FormKeyValueView from '@/components/debobigego/key-value-view.vue';
-import FormLink from '@/components/debobigego/link.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormGroup from '@/components/debobigego/group.vue';
+import FormSuspense from '@/components/form/suspense.vue';
+import MkKeyValue from '@/components/key-value.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
import bytes from '@/filters/bytes';
@@ -31,10 +21,7 @@ import number from '@/filters/number';
export default defineComponent({
components: {
FormSuspense,
- FormKeyValueView,
- FormBase,
- FormGroup,
- FormLink,
+ MkKeyValue,
},
emits: ['info'],
@@ -50,10 +37,6 @@ export default defineComponent({
}
},
- mounted() {
- this.$emit('info', this[symbols.PAGE_INFO]);
- },
-
methods: {
bytes, number,
}
diff --git a/packages/client/src/pages/admin/email-settings.vue b/packages/client/src/pages/admin/email-settings.vue
index 873a853918..6491a453ab 100644
--- a/packages/client/src/pages/admin/email-settings.vue
+++ b/packages/client/src/pages/admin/email-settings.vue
@@ -1,50 +1,55 @@
<template>
-<FormBase>
+<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
<FormSuspense :p="init">
- <FormSwitch v-model="enableEmail">{{ $ts.enableEmail }}<template #desc>{{ $ts.emailConfigInfo }}</template></FormSwitch>
+ <div class="_formRoot">
+ <FormSwitch v-model="enableEmail" class="_formBlock">
+ <template #label>{{ $ts.enableEmail }}</template>
+ <template #caption>{{ $ts.emailConfigInfo }}</template>
+ </FormSwitch>
- <template v-if="enableEmail">
- <FormInput v-model="email" type="email">
- <span>{{ $ts.emailAddress }}</span>
- </FormInput>
+ <template v-if="enableEmail">
+ <FormInput v-model="email" type="email" class="_formBlock">
+ <template #label>{{ $ts.emailAddress }}</template>
+ </FormInput>
- <div v-sticky-container class="_debobigegoItem _debobigegoNoConcat">
- <div class="_debobigegoLabel">{{ $ts.smtpConfig }}</div>
- <div class="main">
- <FormInput v-model="smtpHost">
- <span>{{ $ts.smtpHost }}</span>
- </FormInput>
- <FormInput v-model="smtpPort" type="number">
- <span>{{ $ts.smtpPort }}</span>
- </FormInput>
- <FormInput v-model="smtpUser">
- <span>{{ $ts.smtpUser }}</span>
- </FormInput>
- <FormInput v-model="smtpPass" type="password">
- <span>{{ $ts.smtpPass }}</span>
- </FormInput>
- <FormInfo>{{ $ts.emptyToDisableSmtpAuth }}</FormInfo>
- <FormSwitch v-model="smtpSecure">{{ $ts.smtpSecure }}<template #desc>{{ $ts.smtpSecureInfo }}</template></FormSwitch>
- </div>
- </div>
-
- <FormButton @click="testEmail">{{ $ts.testEmail }}</FormButton>
- </template>
-
- <FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+ <FormSection>
+ <template #label>{{ $ts.smtpConfig }}</template>
+ <FormSplit :min-width="280">
+ <FormInput v-model="smtpHost" class="_formBlock">
+ <template #label>{{ $ts.smtpHost }}</template>
+ </FormInput>
+ <FormInput v-model="smtpPort" type="number" class="_formBlock">
+ <template #label>{{ $ts.smtpPort }}</template>
+ </FormInput>
+ </FormSplit>
+ <FormSplit :min-width="280">
+ <FormInput v-model="smtpUser" class="_formBlock">
+ <template #label>{{ $ts.smtpUser }}</template>
+ </FormInput>
+ <FormInput v-model="smtpPass" type="password" class="_formBlock">
+ <template #label>{{ $ts.smtpPass }}</template>
+ </FormInput>
+ </FormSplit>
+ <FormInfo class="_formBlock">{{ $ts.emptyToDisableSmtpAuth }}</FormInfo>
+ <FormSwitch v-model="smtpSecure" class="_formBlock">
+ <template #label>{{ $ts.smtpSecure }}</template>
+ <template #caption>{{ $ts.smtpSecureInfo }}</template>
+ </FormSwitch>
+ </FormSection>
+ </template>
+ </div>
</FormSuspense>
-</FormBase>
+</MkSpacer>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSwitch from '@/components/debobigego/switch.vue';
-import FormInput from '@/components/debobigego/input.vue';
-import FormButton from '@/components/debobigego/button.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormGroup from '@/components/debobigego/group.vue';
-import FormInfo from '@/components/debobigego/info.vue';
-import FormSuspense from '@/components/debobigego/suspense.vue';
+import FormSwitch from '@/components/form/switch.vue';
+import FormInput from '@/components/form/input.vue';
+import FormInfo from '@/components/ui/info.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';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
@@ -53,9 +58,8 @@ export default defineComponent({
components: {
FormSwitch,
FormInput,
- FormBase,
- FormGroup,
- FormButton,
+ FormSplit,
+ FormSection,
FormInfo,
FormSuspense,
},
@@ -68,6 +72,16 @@ export default defineComponent({
title: this.$ts.emailServer,
icon: 'fas fa-envelope',
bg: 'var(--bg)',
+ actions: [{
+ asFullButton: true,
+ text: this.$ts.testEmail,
+ handler: this.testEmail,
+ }, {
+ asFullButton: true,
+ icon: 'fas fa-check',
+ text: this.$ts.save,
+ handler: this.save,
+ }],
},
enableEmail: false,
email: null,
@@ -79,10 +93,6 @@ export default defineComponent({
}
},
- async mounted() {
- this.$emit('info', this[symbols.PAGE_INFO]);
- },
-
methods: {
async init() {
const meta = await os.api('meta', { detail: true });
diff --git a/packages/client/src/pages/admin/emoji-edit-dialog.vue b/packages/client/src/pages/admin/emoji-edit-dialog.vue
index a45d92fa16..2e3903426e 100644
--- a/packages/client/src/pages/admin/emoji-edit-dialog.vue
+++ b/packages/client/src/pages/admin/emoji-edit-dialog.vue
@@ -95,7 +95,7 @@ export default defineComponent({
});
if (canceled) return;
- os.api('admin/emoji/remove', {
+ os.api('admin/emoji/delete', {
id: this.emoji.id
}).then(() => {
this.$emit('done', {
diff --git a/packages/client/src/pages/admin/emojis.vue b/packages/client/src/pages/admin/emojis.vue
index 49277325a0..5b1dfe565a 100644
--- a/packages/client/src/pages/admin/emojis.vue
+++ b/packages/client/src/pages/admin/emojis.vue
@@ -6,11 +6,22 @@
<template #prefix><i class="fas fa-search"></i></template>
<template #label>{{ $ts.search }}</template>
</MkInput>
- <MkPagination ref="emojis" :pagination="pagination">
+ <MkSwitch v-model="selectMode" style="margin: 8px 0;">
+ <template #label>Select mode</template>
+ </MkSwitch>
+ <div v-if="selectMode" style="display: flex; gap: var(--margin); flex-wrap: wrap;">
+ <MkButton inline @click="selectAll">Select all</MkButton>
+ <MkButton inline @click="setCategoryBulk">Set category</MkButton>
+ <MkButton inline @click="addTagBulk">Add tag</MkButton>
+ <MkButton inline @click="removeTagBulk">Remove tag</MkButton>
+ <MkButton inline @click="setTagBulk">Set tag</MkButton>
+ <MkButton inline danger @click="delBulk">Delete</MkButton>
+ </div>
+ <MkPagination ref="emojisPaginationComponent" :pagination="pagination">
<template #empty><span>{{ $ts.noCustomEmojis }}</span></template>
<template v-slot="{items}">
<div class="ldhfsamy">
- <button v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" @click="edit(emoji)">
+ <button v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)">
<img :src="emoji.url" class="img" :alt="emoji.name"/>
<div class="body">
<div class="name _monospace">{{ emoji.name }}</div>
@@ -23,7 +34,7 @@
</div>
<div v-else-if="tab === 'remote'" class="remote">
- <div class="_inputSplit">
+ <FormSplit>
<MkInput v-model="queryRemote" :debounce="true" type="search">
<template #prefix><i class="fas fa-search"></i></template>
<template #label>{{ $ts.search }}</template>
@@ -31,8 +42,8 @@
<MkInput v-model="host" :debounce="true">
<template #label>{{ $ts.host }}</template>
</MkInput>
- </div>
- <MkPagination ref="remoteEmojis" :pagination="remotePagination">
+ </FormSplit>
+ <MkPagination :pagination="remotePagination">
<template #empty><span>{{ $ts.noCustomEmojis }}</span></template>
<template v-slot="{items}">
<div class="ldhfsamy">
@@ -51,146 +62,233 @@
</MkSpacer>
</template>
-<script lang="ts">
-import { computed, defineComponent, toRef } from 'vue';
+<script lang="ts" setup>
+import { computed, defineComponent, ref, toRef } from 'vue';
import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/form/input.vue';
import MkPagination from '@/components/ui/pagination.vue';
import MkTab from '@/components/tab.vue';
-import { selectFiles } from '@/scripts/select-file';
+import MkSwitch from '@/components/form/switch.vue';
+import FormSplit from '@/components/form/split.vue';
+import { selectFile, selectFiles } from '@/scripts/select-file';
import * as os from '@/os';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkTab,
- MkButton,
- MkInput,
- MkPagination,
- },
+const emojisPaginationComponent = ref<InstanceType<typeof MkPagination>>();
- emits: ['info'],
+const tab = ref('local');
+const query = ref(null);
+const queryRemote = ref(null);
+const host = ref(null);
+const selectMode = ref(false);
+const selectedEmojis = ref<string[]>([]);
- data() {
- return {
- [symbols.PAGE_INFO]: computed(() => ({
- title: this.$ts.customEmojis,
- icon: 'fas fa-laugh',
- bg: 'var(--bg)',
- actions: [{
- asFullButton: true,
- icon: 'fas fa-plus',
- text: this.$ts.addEmoji,
- handler: this.add,
- }, {
- icon: 'fas fa-ellipsis-h',
- handler: this.menu,
- }],
- tabs: [{
- active: this.tab === 'local',
- title: this.$ts.local,
- onClick: () => { this.tab = 'local'; },
- }, {
- active: this.tab === 'remote',
- title: this.$ts.remote,
- onClick: () => { this.tab = 'remote'; },
- },]
- })),
- tab: 'local',
- query: null,
- queryRemote: null,
- host: '',
- pagination: {
- endpoint: 'admin/emoji/list',
- limit: 30,
- params: computed(() => ({
- query: (this.query && this.query !== '') ? this.query : null
- }))
- },
- remotePagination: {
- endpoint: 'admin/emoji/list-remote',
- limit: 30,
- params: computed(() => ({
- query: (this.queryRemote && this.queryRemote !== '') ? this.queryRemote : null,
- host: (this.host && this.host !== '') ? this.host : null
- }))
- },
- }
- },
+const pagination = {
+ endpoint: 'admin/emoji/list' as const,
+ limit: 30,
+ params: computed(() => ({
+ query: (query.value && query.value !== '') ? query.value : null,
+ })),
+};
- async mounted() {
- this.$emit('info', toRef(this, symbols.PAGE_INFO));
- },
+const remotePagination = {
+ endpoint: 'admin/emoji/list-remote' as const,
+ limit: 30,
+ params: computed(() => ({
+ query: (queryRemote.value && queryRemote.value !== '') ? queryRemote.value : null,
+ host: (host.value && host.value !== '') ? host.value : null,
+ })),
+};
- methods: {
- async add(e) {
- const files = await selectFiles(e.currentTarget || e.target, null);
+const selectAll = () => {
+ if (selectedEmojis.value.length > 0) {
+ selectedEmojis.value = [];
+ } else {
+ selectedEmojis.value = emojisPaginationComponent.value.items.map(item => item.id);
+ }
+};
- const promise = Promise.all(files.map(file => os.api('admin/emoji/add', {
- fileId: file.id,
- })));
- promise.then(() => {
- this.$refs.emojis.reload();
- });
- os.promiseDialog(promise);
- },
+const toggleSelect = (emoji) => {
+ if (selectedEmojis.value.includes(emoji.id)) {
+ selectedEmojis.value = selectedEmojis.value.filter(x => x !== emoji.id);
+ } else {
+ selectedEmojis.value.push(emoji.id);
+ }
+};
- edit(emoji) {
- os.popup(import('./emoji-edit-dialog.vue'), {
- emoji: emoji
- }, {
- done: result => {
- if (result.updated) {
- this.$refs.emojis.replaceItem(item => item.id === emoji.id, {
- ...emoji,
- ...result.updated
- });
- } else if (result.deleted) {
- this.$refs.emojis.removeItem(item => item.id === emoji.id);
- }
- },
- }, 'closed');
- },
+const add = async (ev: MouseEvent) => {
+ const files = await selectFiles(ev.currentTarget || ev.target, null);
- im(emoji) {
- os.apiWithDialog('admin/emoji/copy', {
- emojiId: emoji.id,
- });
- },
+ const promise = Promise.all(files.map(file => os.api('admin/emoji/add', {
+ fileId: file.id,
+ })));
+ promise.then(() => {
+ emojisPaginationComponent.value.reload();
+ });
+ os.promiseDialog(promise);
+};
- remoteMenu(emoji, ev) {
- os.popupMenu([{
- type: 'label',
- text: ':' + emoji.name + ':',
- }, {
- text: this.$ts.import,
- icon: 'fas fa-plus',
- action: () => { this.im(emoji) }
- }], ev.currentTarget || ev.target);
+const edit = (emoji) => {
+ os.popup(import('./emoji-edit-dialog.vue'), {
+ emoji: emoji
+ }, {
+ done: result => {
+ if (result.updated) {
+ emojisPaginationComponent.value.replaceItem(item => item.id === emoji.id, {
+ ...emoji,
+ ...result.updated
+ });
+ } else if (result.deleted) {
+ emojisPaginationComponent.value.removeItem(item => item.id === emoji.id);
+ }
},
+ }, 'closed');
+};
- menu(ev) {
- os.popupMenu([{
- icon: 'fas fa-download',
- text: this.$ts.export,
- action: async () => {
- os.api('export-custom-emojis', {
- })
- .then(() => {
- os.alert({
- type: 'info',
- text: this.$ts.exportRequested,
- });
- }).catch((e) => {
- os.alert({
- type: 'error',
- text: e.message,
- });
- });
- }
- }], ev.currentTarget || ev.target);
+const im = (emoji) => {
+ os.apiWithDialog('admin/emoji/copy', {
+ emojiId: emoji.id,
+ });
+};
+
+const remoteMenu = (emoji, ev: MouseEvent) => {
+ os.popupMenu([{
+ type: 'label',
+ text: ':' + emoji.name + ':',
+ }, {
+ text: i18n.locale.import,
+ icon: 'fas fa-plus',
+ action: () => { im(emoji) }
+ }], ev.currentTarget || ev.target);
+};
+
+const menu = (ev: MouseEvent) => {
+ os.popupMenu([{
+ icon: 'fas fa-download',
+ text: i18n.locale.export,
+ action: async () => {
+ os.api('export-custom-emojis', {
+ })
+ .then(() => {
+ os.alert({
+ type: 'info',
+ text: i18n.locale.exportRequested,
+ });
+ }).catch((e) => {
+ os.alert({
+ type: 'error',
+ text: e.message,
+ });
+ });
}
- }
+ }, {
+ icon: 'fas fa-upload',
+ text: i18n.locale.import,
+ action: async () => {
+ const file = await selectFile(ev.currentTarget || ev.target);
+ os.api('admin/emoji/import-zip', {
+ fileId: file.id,
+ })
+ .then(() => {
+ os.alert({
+ type: 'info',
+ text: i18n.locale.importRequested,
+ });
+ }).catch((e) => {
+ os.alert({
+ type: 'error',
+ text: e.message,
+ });
+ });
+ }
+ }], ev.currentTarget || ev.target);
+};
+
+const setCategoryBulk = async () => {
+ const { canceled, result } = await os.inputText({
+ title: 'Category',
+ });
+ if (canceled) return;
+ await os.apiWithDialog('admin/emoji/set-category-bulk', {
+ ids: selectedEmojis.value,
+ category: result,
+ });
+ emojisPaginationComponent.value.reload();
+};
+
+const addTagBulk = async () => {
+ const { canceled, result } = await os.inputText({
+ title: 'Tag',
+ });
+ if (canceled) return;
+ await os.apiWithDialog('admin/emoji/add-aliases-bulk', {
+ ids: selectedEmojis.value,
+ aliases: result.split(' '),
+ });
+ emojisPaginationComponent.value.reload();
+};
+
+const removeTagBulk = async () => {
+ const { canceled, result } = await os.inputText({
+ title: 'Tag',
+ });
+ if (canceled) return;
+ await os.apiWithDialog('admin/emoji/remove-aliases-bulk', {
+ ids: selectedEmojis.value,
+ aliases: result.split(' '),
+ });
+ emojisPaginationComponent.value.reload();
+};
+
+const setTagBulk = async () => {
+ const { canceled, result } = await os.inputText({
+ title: 'Tag',
+ });
+ if (canceled) return;
+ await os.apiWithDialog('admin/emoji/set-aliases-bulk', {
+ ids: selectedEmojis.value,
+ aliases: result.split(' '),
+ });
+ emojisPaginationComponent.value.reload();
+};
+
+const delBulk = async () => {
+ const { canceled } = await os.confirm({
+ type: 'warning',
+ text: i18n.locale.deleteConfirm,
+ });
+ if (canceled) return;
+ await os.apiWithDialog('admin/emoji/delete-bulk', {
+ ids: selectedEmojis.value,
+ });
+ emojisPaginationComponent.value.reload();
+};
+
+defineExpose({
+ [symbols.PAGE_INFO]: computed(() => ({
+ title: i18n.locale.customEmojis,
+ icon: 'fas fa-laugh',
+ bg: 'var(--bg)',
+ actions: [{
+ asFullButton: true,
+ icon: 'fas fa-plus',
+ text: i18n.locale.addEmoji,
+ handler: add,
+ }, {
+ icon: 'fas fa-ellipsis-h',
+ handler: menu,
+ }],
+ tabs: [{
+ active: tab.value === 'local',
+ title: i18n.locale.local,
+ onClick: () => { tab.value = 'local'; },
+ }, {
+ active: tab.value === 'remote',
+ title: i18n.locale.remote,
+ onClick: () => { tab.value = 'remote'; },
+ },]
+ })),
});
</script>
@@ -210,11 +308,16 @@ export default defineComponent({
> .emoji {
display: flex;
align-items: center;
- padding: 12px;
+ padding: 11px;
text-align: left;
+ border: solid 1px var(--panel);
&:hover {
- color: var(--accent);
+ border-color: var(--inputBorderHover);
+ }
+
+ &.selected {
+ border-color: var(--accent);
}
> .img {
diff --git a/packages/client/src/pages/admin/files-settings.vue b/packages/client/src/pages/admin/files-settings.vue
deleted file mode 100644
index df25bd0fb2..0000000000
--- a/packages/client/src/pages/admin/files-settings.vue
+++ /dev/null
@@ -1,93 +0,0 @@
-<template>
-<FormBase>
- <FormSuspense :p="init">
- <FormSwitch v-model="cacheRemoteFiles">
- {{ $ts.cacheRemoteFiles }}
- <template #desc>{{ $ts.cacheRemoteFilesDescription }}</template>
- </FormSwitch>
-
- <FormSwitch v-model="proxyRemoteFiles">
- {{ $ts.proxyRemoteFiles }}
- <template #desc>{{ $ts.proxyRemoteFilesDescription }}</template>
- </FormSwitch>
-
- <FormInput v-model="localDriveCapacityMb" type="number">
- <span>{{ $ts.driveCapacityPerLocalAccount }}</span>
- <template #suffix>MB</template>
- <template #desc>{{ $ts.inMb }}</template>
- </FormInput>
-
- <FormInput v-model="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles">
- <span>{{ $ts.driveCapacityPerRemoteAccount }}</span>
- <template #suffix>MB</template>
- <template #desc>{{ $ts.inMb }}</template>
- </FormInput>
-
- <FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
- </FormSuspense>
-</FormBase>
-</template>
-
-<script lang="ts">
-import { defineComponent } from 'vue';
-import FormSwitch from '@/components/debobigego/switch.vue';
-import FormInput from '@/components/debobigego/input.vue';
-import FormButton from '@/components/debobigego/button.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormGroup from '@/components/debobigego/group.vue';
-import FormSuspense from '@/components/debobigego/suspense.vue';
-import * as os from '@/os';
-import * as symbols from '@/symbols';
-import { fetchInstance } from '@/instance';
-
-export default defineComponent({
- components: {
- FormSwitch,
- FormInput,
- FormBase,
- FormGroup,
- FormButton,
- FormSuspense,
- },
-
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.files,
- icon: 'fas fa-cloud',
- bg: 'var(--bg)',
- },
- cacheRemoteFiles: false,
- proxyRemoteFiles: false,
- localDriveCapacityMb: 0,
- remoteDriveCapacityMb: 0,
- }
- },
-
- async mounted() {
- this.$emit('info', this[symbols.PAGE_INFO]);
- },
-
- methods: {
- async init() {
- const meta = await os.api('meta', { detail: true });
- this.cacheRemoteFiles = meta.cacheRemoteFiles;
- this.proxyRemoteFiles = meta.proxyRemoteFiles;
- this.localDriveCapacityMb = meta.driveCapacityPerLocalUserMb;
- this.remoteDriveCapacityMb = meta.driveCapacityPerRemoteUserMb;
- },
- save() {
- os.apiWithDialog('admin/update-meta', {
- cacheRemoteFiles: this.cacheRemoteFiles,
- proxyRemoteFiles: this.proxyRemoteFiles,
- localDriveCapacityMb: parseInt(this.localDriveCapacityMb, 10),
- remoteDriveCapacityMb: parseInt(this.remoteDriveCapacityMb, 10),
- }).then(() => {
- fetchInstance();
- });
- }
- }
-});
-</script>
diff --git a/packages/client/src/pages/admin/files.vue b/packages/client/src/pages/admin/files.vue
index 032e394a66..87dd12f489 100644
--- a/packages/client/src/pages/admin/files.vue
+++ b/packages/client/src/pages/admin/files.vue
@@ -19,7 +19,7 @@
<option value="local">{{ $ts.local }}</option>
<option value="remote">{{ $ts.remote }}</option>
</MkSelect>
- <MkInput v-model="searchHost" :debounce="true" type="search" style="margin: 0; flex: 1;" :disabled="pagination.params().origin === 'local'">
+ <MkInput v-model="searchHost" :debounce="true" type="search" style="margin: 0; flex: 1;" :disabled="pagination.params.origin === 'local'">
<template #label>{{ $ts.host }}</template>
</MkInput>
</div>
@@ -55,7 +55,7 @@
</template>
<script lang="ts">
-import { defineComponent } from 'vue';
+import { computed, defineComponent } from 'vue';
import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/form/input.vue';
import MkSelect from '@/components/form/select.vue';
@@ -95,33 +95,17 @@ export default defineComponent({
type: null,
searchHost: '',
pagination: {
- endpoint: 'admin/drive/files',
+ endpoint: 'admin/drive/files' as const,
limit: 10,
- params: () => ({
+ params: computed(() => ({
type: (this.type && this.type !== '') ? this.type : null,
origin: this.origin,
- hostname: (this.hostname && this.hostname !== '') ? this.hostname : null,
- }),
+ hostname: (this.searchHost && this.searchHost !== '') ? this.searchHost : null,
+ })),
},
}
},
- watch: {
- type() {
- this.$refs.files.reload();
- },
- origin() {
- this.$refs.files.reload();
- },
- searchHost() {
- this.$refs.files.reload();
- },
- },
-
- mounted() {
- this.$emit('info', this[symbols.PAGE_INFO]);
- },
-
methods: {
clear() {
os.confirm({
diff --git a/packages/client/src/pages/admin/index.vue b/packages/client/src/pages/admin/index.vue
index e363d1bd03..350e7defc6 100644
--- a/packages/client/src/pages/admin/index.vue
+++ b/packages/client/src/pages/admin/index.vue
@@ -3,14 +3,14 @@
<div v-if="!narrow || page == null" class="nav">
<MkHeader :info="header"></MkHeader>
- <MkSpacer :content-max="700">
+ <MkSpacer :content-max="700" :margin-min="16">
<div class="lxpfedzu">
<div class="banner">
<img :src="$instance.iconUrl || '/favicon.ico'" alt="" class="icon"/>
</div>
<MkInfo v-if="noMaintainerInformation" warn class="info">{{ $ts.noMaintainerInformationWarning }} <MkA to="/admin/settings" class="_link">{{ $ts.configure }}</MkA></MkInfo>
- <MkInfo v-if="noBotProtection" warn class="info">{{ $ts.noBotProtectionWarning }} <MkA to="/admin/bot-protection" class="_link">{{ $ts.configure }}</MkA></MkInfo>
+ <MkInfo v-if="noBotProtection" warn class="info">{{ $ts.noBotProtectionWarning }} <MkA to="/admin/security" class="_link">{{ $ts.configure }}</MkA></MkInfo>
<MkSuperMenu :def="menuDef" :grid="page == null"></MkSuperMenu>
</div>
@@ -19,7 +19,7 @@
<div class="main">
<MkStickyContainer>
<template #header><MkHeader v-if="childInfo && !childInfo.hideHeader" :info="childInfo"/></template>
- <component :is="component" :key="page" v-bind="pageProps" @info="onInfo"/>
+ <component :is="component" :ref="el => pageChanged(el)" :key="page" v-bind="pageProps"/>
</MkStickyContainer>
</div>
</div>
@@ -29,9 +29,6 @@
import { computed, defineAsyncComponent, defineComponent, isRef, nextTick, onMounted, reactive, ref, watch } from 'vue';
import { i18n } from '@/i18n';
import MkSuperMenu from '@/components/ui/super-menu.vue';
-import FormGroup from '@/components/debobigego/group.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormButton from '@/components/debobigego/button.vue';
import MkInfo from '@/components/ui/info.vue';
import { scroll } from '@/scripts/scroll';
import { instance } from '@/instance';
@@ -41,10 +38,7 @@ import { lookupUser } from '@/scripts/lookup-user';
export default defineComponent({
components: {
- FormBase,
MkSuperMenu,
- FormGroup,
- FormButton,
MkInfo,
},
@@ -72,7 +66,9 @@ export default defineComponent({
const narrow = ref(false);
const view = ref(null);
const el = ref(null);
- const onInfo = (viewInfo) => {
+ const pageChanged = (page) => {
+ if (page == null) return;
+ const viewInfo = page[symbols.PAGE_INFO];
if (isRef(viewInfo)) {
watch(viewInfo, () => {
childInfo.value = viewInfo.value;
@@ -163,11 +159,6 @@ export default defineComponent({
to: '/admin/settings',
active: page.value === 'settings',
}, {
- icon: 'fas fa-cloud',
- text: i18n.locale.files,
- to: '/admin/files-settings',
- active: page.value === 'files-settings',
- }, {
icon: 'fas fa-envelope',
text: i18n.locale.emailServer,
to: '/admin/email-settings',
@@ -183,11 +174,6 @@ export default defineComponent({
to: '/admin/security',
active: page.value === 'security',
}, {
- icon: 'fas fa-bolt',
- text: 'ServiceWorker',
- to: '/admin/service-worker',
- active: page.value === 'service-worker',
- }, {
icon: 'fas fa-globe',
text: i18n.locale.relays,
to: '/admin/relays',
@@ -236,17 +222,11 @@ export default defineComponent({
case 'database': return defineAsyncComponent(() => import('./database.vue'));
case 'abuses': return defineAsyncComponent(() => import('./abuses.vue'));
case 'settings': return defineAsyncComponent(() => import('./settings.vue'));
- case 'files-settings': return defineAsyncComponent(() => import('./files-settings.vue'));
case 'email-settings': return defineAsyncComponent(() => import('./email-settings.vue'));
case 'object-storage': return defineAsyncComponent(() => import('./object-storage.vue'));
case 'security': return defineAsyncComponent(() => import('./security.vue'));
- case 'bot-protection': return defineAsyncComponent(() => import('./bot-protection.vue'));
- case 'service-worker': return defineAsyncComponent(() => import('./service-worker.vue'));
case 'relays': return defineAsyncComponent(() => import('./relays.vue'));
case 'integrations': return defineAsyncComponent(() => import('./integrations.vue'));
- case 'integrations/twitter': return defineAsyncComponent(() => import('./integrations-twitter.vue'));
- case 'integrations/github': return defineAsyncComponent(() => import('./integrations-github.vue'));
- case 'integrations/discord': return defineAsyncComponent(() => import('./integrations-discord.vue'));
case 'instance-block': return defineAsyncComponent(() => import('./instance-block.vue'));
case 'proxy-account': return defineAsyncComponent(() => import('./proxy-account.vue'));
case 'other-settings': return defineAsyncComponent(() => import('./other-settings.vue'));
@@ -333,7 +313,7 @@ export default defineComponent({
narrow,
view,
el,
- onInfo,
+ pageChanged,
childInfo,
pageProps,
component,
diff --git a/packages/client/src/pages/admin/instance-block.vue b/packages/client/src/pages/admin/instance-block.vue
index 2e899de687..6cadc7df39 100644
--- a/packages/client/src/pages/admin/instance-block.vue
+++ b/packages/client/src/pages/admin/instance-block.vue
@@ -1,39 +1,29 @@
<template>
-<FormBase>
+<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
<FormSuspense :p="init">
- <FormTextarea v-model="blockedHosts">
+ <FormTextarea v-model="blockedHosts" class="_formBlock">
<span>{{ $ts.blockedInstances }}</span>
- <template #desc>{{ $ts.blockedInstancesDescription }}</template>
+ <template #caption>{{ $ts.blockedInstancesDescription }}</template>
</FormTextarea>
- <FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+ <FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
</FormSuspense>
-</FormBase>
+</MkSpacer>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSwitch from '@/components/debobigego/switch.vue';
-import FormInput from '@/components/debobigego/input.vue';
-import FormButton from '@/components/debobigego/button.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormGroup from '@/components/debobigego/group.vue';
-import FormTextarea from '@/components/debobigego/textarea.vue';
-import FormInfo from '@/components/debobigego/info.vue';
-import FormSuspense from '@/components/debobigego/suspense.vue';
+import FormButton from '@/components/ui/button.vue';
+import FormTextarea from '@/components/form/textarea.vue';
+import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
export default defineComponent({
components: {
- FormSwitch,
- FormInput,
- FormBase,
- FormGroup,
FormButton,
FormTextarea,
- FormInfo,
FormSuspense,
},
@@ -50,10 +40,6 @@ export default defineComponent({
}
},
- async mounted() {
- this.$emit('info', this[symbols.PAGE_INFO]);
- },
-
methods: {
async init() {
const meta = await os.api('meta', { detail: true });
diff --git a/packages/client/src/pages/admin/instance.vue b/packages/client/src/pages/admin/instance.vue
deleted file mode 100644
index 51fcb8675a..0000000000
--- a/packages/client/src/pages/admin/instance.vue
+++ /dev/null
@@ -1,291 +0,0 @@
-<template>
-<XModalWindow ref="dialog"
- :width="520"
- :height="500"
- @close="$refs.dialog.close()"
- @closed="$emit('closed')"
->
- <template #header>{{ instance.host }}</template>
- <div class="mk-instance-info">
- <div class="_table section">
- <div class="_row">
- <div class="_cell">
- <div class="_label">{{ $ts.software }}</div>
- <div class="_data">{{ instance.softwareName || '?' }}</div>
- </div>
- <div class="_cell">
- <div class="_label">{{ $ts.version }}</div>
- <div class="_data">{{ instance.softwareVersion || '?' }}</div>
- </div>
- </div>
- </div>
- <div class="_table data section">
- <div class="_row">
- <div class="_cell">
- <div class="_label">{{ $ts.registeredAt }}</div>
- <div class="_data">{{ new Date(instance.caughtAt).toLocaleString() }} (<MkTime :time="instance.caughtAt"/>)</div>
- </div>
- </div>
- <div class="_row">
- <div class="_cell">
- <div class="_label">{{ $ts.following }}</div>
- <button class="_data _textButton" @click="showFollowing()">{{ number(instance.followingCount) }}</button>
- </div>
- <div class="_cell">
- <div class="_label">{{ $ts.followers }}</div>
- <button class="_data _textButton" @click="showFollowers()">{{ number(instance.followersCount) }}</button>
- </div>
- </div>
- <div class="_row">
- <div class="_cell">
- <div class="_label">{{ $ts.users }}</div>
- <button class="_data _textButton" @click="showUsers()">{{ number(instance.usersCount) }}</button>
- </div>
- <div class="_cell">
- <div class="_label">{{ $ts.notes }}</div>
- <div class="_data">{{ number(instance.notesCount) }}</div>
- </div>
- </div>
- <div class="_row">
- <div class="_cell">
- <div class="_label">{{ $ts.files }}</div>
- <div class="_data">{{ number(instance.driveFiles) }}</div>
- </div>
- <div class="_cell">
- <div class="_label">{{ $ts.storageUsage }}</div>
- <div class="_data">{{ bytes(instance.driveUsage) }}</div>
- </div>
- </div>
- <div class="_row">
- <div class="_cell">
- <div class="_label">{{ $ts.latestRequestSentAt }}</div>
- <div class="_data"><MkTime v-if="instance.latestRequestSentAt" :time="instance.latestRequestSentAt"/><span v-else>N/A</span></div>
- </div>
- <div class="_cell">
- <div class="_label">{{ $ts.latestStatus }}</div>
- <div class="_data">{{ instance.latestStatus ? instance.latestStatus : 'N/A' }}</div>
- </div>
- </div>
- <div class="_row">
- <div class="_cell">
- <div class="_label">{{ $ts.latestRequestReceivedAt }}</div>
- <div class="_data"><MkTime v-if="instance.latestRequestReceivedAt" :time="instance.latestRequestReceivedAt"/><span v-else>N/A</span></div>
- </div>
- </div>
- </div>
- <div class="chart">
- <div class="header">
- <span class="label">{{ $ts.charts }}</span>
- <div class="selects">
- <MkSelect v-model="chartSrc" style="margin: 0; flex: 1;">
- <option value="instance-requests">{{ $ts._instanceCharts.requests }}</option>
- <option value="instance-users">{{ $ts._instanceCharts.users }}</option>
- <option value="instance-users-total">{{ $ts._instanceCharts.usersTotal }}</option>
- <option value="instance-notes">{{ $ts._instanceCharts.notes }}</option>
- <option value="instance-notes-total">{{ $ts._instanceCharts.notesTotal }}</option>
- <option value="instance-ff">{{ $ts._instanceCharts.ff }}</option>
- <option value="instance-ff-total">{{ $ts._instanceCharts.ffTotal }}</option>
- <option value="instance-drive-usage">{{ $ts._instanceCharts.cacheSize }}</option>
- <option value="instance-drive-usage-total">{{ $ts._instanceCharts.cacheSizeTotal }}</option>
- <option value="instance-drive-files">{{ $ts._instanceCharts.files }}</option>
- <option value="instance-drive-files-total">{{ $ts._instanceCharts.filesTotal }}</option>
- </MkSelect>
- <MkSelect v-model="chartSpan" style="margin: 0;">
- <option value="hour">{{ $ts.perHour }}</option>
- <option value="day">{{ $ts.perDay }}</option>
- </MkSelect>
- </div>
- </div>
- <div class="chart">
- <MkChart :src="chartSrc" :span="chartSpan" :limit="90" :detailed="true"></MkChart>
- </div>
- </div>
- <div class="operations section">
- <span class="label">{{ $ts.operations }}</span>
- <MkSwitch v-model="isSuspended" class="switch">{{ $ts.stopActivityDelivery }}</MkSwitch>
- <MkSwitch :model-value="isBlocked" class="switch" @update:modelValue="changeBlock">{{ $ts.blockThisInstance }}</MkSwitch>
- <details>
- <summary>{{ $ts.deleteAllFiles }}</summary>
- <MkButton style="margin: 0.5em 0 0.5em 0;" @click="deleteAllFiles()"><i class="fas fa-trash-alt"></i> {{ $ts.deleteAllFiles }}</MkButton>
- </details>
- <details>
- <summary>{{ $ts.removeAllFollowing }}</summary>
- <MkButton style="margin: 0.5em 0 0.5em 0;" @click="removeAllFollowing()"><i class="fas fa-minus-circle"></i> {{ $ts.removeAllFollowing }}</MkButton>
- <MkInfo warn>{{ $t('removeAllFollowingDescription', { host: instance.host }) }}</MkInfo>
- </details>
- </div>
- <details class="metadata section">
- <summary class="label">{{ $ts.metadata }}</summary>
- <pre><code>{{ JSON.stringify(instance, null, 2) }}</code></pre>
- </details>
- </div>
-</XModalWindow>
-</template>
-
-<script lang="ts">
-import { defineComponent, markRaw } from 'vue';
-import XModalWindow from '@/components/ui/modal-window.vue';
-import MkSelect from '@/components/form/select.vue';
-import MkButton from '@/components/ui/button.vue';
-import MkSwitch from '@/components/form/switch.vue';
-import MkInfo from '@/components/ui/info.vue';
-import MkChart from '@/components/chart.vue';
-import bytes from '@/filters/bytes';
-import number from '@/filters/number';
-import * as os from '@/os';
-
-export default defineComponent({
- components: {
- XModalWindow,
- MkSelect,
- MkButton,
- MkSwitch,
- MkInfo,
- MkChart,
- },
-
- props: {
- instance: {
- type: Object,
- required: true
- }
- },
-
- emits: ['closed'],
-
- data() {
- return {
- isSuspended: this.instance.isSuspended,
- chartSrc: 'requests',
- chartSpan: 'hour',
- };
- },
-
- computed: {
- meta() {
- return this.$instance;
- },
-
- isBlocked() {
- return this.meta && this.meta.blockedHosts && this.meta.blockedHosts.includes(this.instance.host);
- }
- },
-
- watch: {
- isSuspended() {
- os.api('admin/federation/update-instance', {
- host: this.instance.host,
- isSuspended: this.isSuspended
- });
- },
- },
-
- methods: {
- changeBlock(e) {
- os.api('admin/update-meta', {
- blockedHosts: this.isBlocked ? this.meta.blockedHosts.concat([this.instance.host]) : this.meta.blockedHosts.filter(x => x !== this.instance.host)
- });
- },
-
- removeAllFollowing() {
- os.apiWithDialog('admin/federation/remove-all-following', {
- host: this.instance.host
- });
- },
-
- deleteAllFiles() {
- os.apiWithDialog('admin/federation/delete-all-files', {
- host: this.instance.host
- });
- },
-
- showFollowing() {
- // TODO: ページ遷移
- },
-
- showFollowers() {
- // TODO: ページ遷移
- },
-
- showUsers() {
- // TODO: ページ遷移
- },
-
- bytes,
-
- number
- }
-});
-</script>
-
-<style lang="scss" scoped>
-.mk-instance-info {
- overflow: auto;
-
- > .section {
- padding: 16px 32px;
-
- @media (max-width: 500px) {
- padding: 8px 16px;
- }
-
- &:not(:first-child) {
- border-top: solid 0.5px var(--divider);
- }
- }
-
- > .chart {
- border-top: solid 0.5px var(--divider);
- padding: 16px 0 12px 0;
-
- > .header {
- padding: 0 32px;
-
- @media (max-width: 500px) {
- padding: 0 16px;
- }
-
- > .label {
- font-size: 80%;
- opacity: 0.7;
- }
-
- > .selects {
- display: flex;
- }
- }
-
- > .chart {
- padding: 0 16px;
-
- @media (max-width: 500px) {
- padding: 0;
- }
- }
- }
-
- > .operations {
- > .label {
- font-size: 80%;
- opacity: 0.7;
- }
-
- > .switch {
- margin: 16px 0;
- }
- }
-
- > .metadata {
- > .label {
- font-size: 80%;
- opacity: 0.7;
- }
-
- > pre > code {
- display: block;
- max-height: 200px;
- overflow: auto;
- }
- }
-}
-</style>
diff --git a/packages/client/src/pages/admin/integrations-discord.vue b/packages/client/src/pages/admin/integrations.discord.vue
index 383031f3d1..8fc340150a 100644
--- a/packages/client/src/pages/admin/integrations-discord.vue
+++ b/packages/client/src/pages/admin/integrations.discord.vue
@@ -1,37 +1,36 @@
<template>
-<FormBase>
- <FormSuspense :p="init">
- <FormSwitch v-model="enableDiscordIntegration">
- {{ $ts.enable }}
+<FormSuspense :p="init">
+ <div class="_formRoot">
+ <FormSwitch v-model="enableDiscordIntegration" class="_formBlock">
+ <template #label>{{ $ts.enable }}</template>
</FormSwitch>
<template v-if="enableDiscordIntegration">
- <FormInfo>Callback URL: {{ `${uri}/api/dc/cb` }}</FormInfo>
+ <FormInfo class="_formBlock">Callback URL: {{ `${uri}/api/dc/cb` }}</FormInfo>
- <FormInput v-model="discordClientId">
+ <FormInput v-model="discordClientId" class="_formBlock">
<template #prefix><i class="fas fa-key"></i></template>
- Client ID
+ <template #label>Client ID</template>
</FormInput>
- <FormInput v-model="discordClientSecret">
+ <FormInput v-model="discordClientSecret" class="_formBlock">
<template #prefix><i class="fas fa-key"></i></template>
- Client Secret
+ <template #label>Client Secret</template>
</FormInput>
</template>
- <FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
- </FormSuspense>
-</FormBase>
+ <FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+ </div>
+</FormSuspense>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSwitch from '@/components/debobigego/switch.vue';
-import FormInput from '@/components/debobigego/input.vue';
-import FormButton from '@/components/debobigego/button.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormInfo from '@/components/debobigego/info.vue';
-import FormSuspense from '@/components/debobigego/suspense.vue';
+import FormSwitch from '@/components/form/switch.vue';
+import FormInput from '@/components/form/input.vue';
+import FormButton from '@/components/ui/button.vue';
+import FormInfo from '@/components/ui/info.vue';
+import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
@@ -40,7 +39,6 @@ export default defineComponent({
components: {
FormSwitch,
FormInput,
- FormBase,
FormInfo,
FormButton,
FormSuspense,
@@ -60,10 +58,6 @@ export default defineComponent({
}
},
- async mounted() {
- this.$emit('info', this[symbols.PAGE_INFO]);
- },
-
methods: {
async init() {
const meta = await os.api('meta', { detail: true });
diff --git a/packages/client/src/pages/admin/integrations-github.vue b/packages/client/src/pages/admin/integrations.github.vue
index ecb2fd67fa..d9db9c00f1 100644
--- a/packages/client/src/pages/admin/integrations-github.vue
+++ b/packages/client/src/pages/admin/integrations.github.vue
@@ -1,37 +1,36 @@
<template>
-<FormBase>
- <FormSuspense :p="init">
- <FormSwitch v-model="enableGithubIntegration">
- {{ $ts.enable }}
+<FormSuspense :p="init">
+ <div class="_formRoot">
+ <FormSwitch v-model="enableGithubIntegration" class="_formBlock">
+ <template #label>{{ $ts.enable }}</template>
</FormSwitch>
<template v-if="enableGithubIntegration">
- <FormInfo>Callback URL: {{ `${uri}/api/gh/cb` }}</FormInfo>
+ <FormInfo class="_formBlock">Callback URL: {{ `${uri}/api/gh/cb` }}</FormInfo>
- <FormInput v-model="githubClientId">
+ <FormInput v-model="githubClientId" class="_formBlock">
<template #prefix><i class="fas fa-key"></i></template>
- Client ID
+ <template #label>Client ID</template>
</FormInput>
- <FormInput v-model="githubClientSecret">
+ <FormInput v-model="githubClientSecret" class="_formBlock">
<template #prefix><i class="fas fa-key"></i></template>
- Client Secret
+ <template #label>Client Secret</template>
</FormInput>
</template>
- <FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
- </FormSuspense>
-</FormBase>
+ <FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+ </div>
+</FormSuspense>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSwitch from '@/components/debobigego/switch.vue';
-import FormInput from '@/components/debobigego/input.vue';
-import FormButton from '@/components/debobigego/button.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormInfo from '@/components/debobigego/info.vue';
-import FormSuspense from '@/components/debobigego/suspense.vue';
+import FormSwitch from '@/components/form/switch.vue';
+import FormInput from '@/components/form/input.vue';
+import FormButton from '@/components/ui/button.vue';
+import FormInfo from '@/components/ui/info.vue';
+import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
@@ -40,7 +39,6 @@ export default defineComponent({
components: {
FormSwitch,
FormInput,
- FormBase,
FormInfo,
FormButton,
FormSuspense,
@@ -60,10 +58,6 @@ export default defineComponent({
}
},
- async mounted() {
- this.$emit('info', this[symbols.PAGE_INFO]);
- },
-
methods: {
async init() {
const meta = await os.api('meta', { detail: true });
diff --git a/packages/client/src/pages/admin/integrations-twitter.vue b/packages/client/src/pages/admin/integrations.twitter.vue
index 1404102c57..1f8074535a 100644
--- a/packages/client/src/pages/admin/integrations-twitter.vue
+++ b/packages/client/src/pages/admin/integrations.twitter.vue
@@ -1,37 +1,36 @@
<template>
-<FormBase>
- <FormSuspense :p="init">
- <FormSwitch v-model="enableTwitterIntegration">
- {{ $ts.enable }}
+<FormSuspense :p="init">
+ <div class="_formRoot">
+ <FormSwitch v-model="enableTwitterIntegration" class="_formBlock">
+ <template #label>{{ $ts.enable }}</template>
</FormSwitch>
<template v-if="enableTwitterIntegration">
- <FormInfo>Callback URL: {{ `${uri}/api/tw/cb` }}</FormInfo>
+ <FormInfo class="_formBlock">Callback URL: {{ `${uri}/api/tw/cb` }}</FormInfo>
- <FormInput v-model="twitterConsumerKey">
+ <FormInput v-model="twitterConsumerKey" class="_formBlock">
<template #prefix><i class="fas fa-key"></i></template>
- Consumer Key
+ <template #label>Consumer Key</template>
</FormInput>
- <FormInput v-model="twitterConsumerSecret">
+ <FormInput v-model="twitterConsumerSecret" class="_formBlock">
<template #prefix><i class="fas fa-key"></i></template>
- Consumer Secret
+ <template #label>Consumer Secret</template>
</FormInput>
</template>
- <FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
- </FormSuspense>
-</FormBase>
+ <FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+ </div>
+</FormSuspense>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSwitch from '@/components/debobigego/switch.vue';
-import FormInput from '@/components/debobigego/input.vue';
-import FormButton from '@/components/debobigego/button.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormInfo from '@/components/debobigego/info.vue';
-import FormSuspense from '@/components/debobigego/suspense.vue';
+import FormSwitch from '@/components/form/switch.vue';
+import FormInput from '@/components/form/input.vue';
+import FormButton from '@/components/ui/button.vue';
+import FormInfo from '@/components/ui/info.vue';
+import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
@@ -40,7 +39,6 @@ export default defineComponent({
components: {
FormSwitch,
FormInput,
- FormBase,
FormInfo,
FormButton,
FormSuspense,
@@ -60,10 +58,6 @@ export default defineComponent({
}
},
- async mounted() {
- this.$emit('info', this[symbols.PAGE_INFO]);
- },
-
methods: {
async init() {
const meta = await os.api('meta', { detail: true });
diff --git a/packages/client/src/pages/admin/integrations.vue b/packages/client/src/pages/admin/integrations.vue
index c21eebc1c6..91d03fef31 100644
--- a/packages/client/src/pages/admin/integrations.vue
+++ b/packages/client/src/pages/admin/integrations.vue
@@ -1,46 +1,48 @@
<template>
-<FormBase>
+<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
<FormSuspense :p="init">
- <FormLink to="/admin/integrations/twitter">
- <i class="fab fa-twitter"></i> Twitter
+ <FormFolder class="_formBlock">
+ <template #icon><i class="fab fa-twitter"></i></template>
+ <template #label>Twitter</template>
<template #suffix>{{ enableTwitterIntegration ? $ts.enabled : $ts.disabled }}</template>
- </FormLink>
- <FormLink to="/admin/integrations/github">
- <i class="fab fa-github"></i> GitHub
+ <XTwitter/>
+ </FormFolder>
+ <FormFolder to="/admin/integrations/github" class="_formBlock">
+ <template #icon><i class="fab fa-github"></i></template>
+ <template #label>GitHub</template>
<template #suffix>{{ enableGithubIntegration ? $ts.enabled : $ts.disabled }}</template>
- </FormLink>
- <FormLink to="/admin/integrations/discord">
- <i class="fab fa-discord"></i> Discord
+ <XGithub/>
+ </FormFolder>
+ <FormFolder to="/admin/integrations/discord" class="_formBlock">
+ <template #icon><i class="fab fa-discord"></i></template>
+ <template #label>Discord</template>
<template #suffix>{{ enableDiscordIntegration ? $ts.enabled : $ts.disabled }}</template>
- </FormLink>
+ <XDiscord/>
+ </FormFolder>
</FormSuspense>
-</FormBase>
+</MkSpacer>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
-import FormLink from '@/components/debobigego/link.vue';
-import FormInput from '@/components/debobigego/input.vue';
-import FormButton from '@/components/debobigego/button.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormGroup from '@/components/debobigego/group.vue';
-import FormTextarea from '@/components/debobigego/textarea.vue';
-import FormInfo from '@/components/debobigego/info.vue';
-import FormSuspense from '@/components/debobigego/suspense.vue';
+import FormFolder from '@/components/form/folder.vue';
+import FormSecion from '@/components/form/section.vue';
+import FormSuspense from '@/components/form/suspense.vue';
+import XTwitter from './integrations.twitter.vue';
+import XGithub from './integrations.github.vue';
+import XDiscord from './integrations.discord.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
export default defineComponent({
components: {
- FormLink,
- FormInput,
- FormBase,
- FormGroup,
- FormButton,
- FormTextarea,
- FormInfo,
+ FormFolder,
+ FormSecion,
FormSuspense,
+ XTwitter,
+ XGithub,
+ XDiscord,
},
emits: ['info'],
@@ -58,10 +60,6 @@ export default defineComponent({
}
},
- async mounted() {
- this.$emit('info', this[symbols.PAGE_INFO]);
- },
-
methods: {
async init() {
const meta = await os.api('meta', { detail: true });
diff --git a/packages/client/src/pages/admin/metrics.vue b/packages/client/src/pages/admin/metrics.vue
index 05b64b235c..1de297fd93 100644
--- a/packages/client/src/pages/admin/metrics.vue
+++ b/packages/client/src/pages/admin/metrics.vue
@@ -76,7 +76,6 @@ import MkwFederation from '../../widgets/federation.vue';
import { version, url } from '@/config';
import bytes from '@/filters/bytes';
import number from '@/filters/number';
-import MkInstanceInfo from './instance.vue';
Chart.register(
ArcElement,
@@ -101,6 +100,7 @@ const alpha = (hex, a) => {
return `rgba(${r}, ${g}, ${b}, ${a})`;
};
import * as os from '@/os';
+import { stream } from '@/stream';
export default defineComponent({
components: {
@@ -119,7 +119,7 @@ export default defineComponent({
stats: null,
serverInfo: null,
connection: null,
- queueConnection: markRaw(os.stream.useChannel('queueStats')),
+ queueConnection: markRaw(stream.useChannel('queueStats')),
memUsage: 0,
chartCpuMem: null,
chartNet: null,
@@ -150,7 +150,7 @@ export default defineComponent({
os.api('admin/server-info', {}).then(res => {
this.serverInfo = res;
- this.connection = markRaw(os.stream.useChannel('serverStats'));
+ this.connection = markRaw(stream.useChannel('serverStats'));
this.connection.on('stats', this.onStats);
this.connection.on('statsLog', this.onStatsLog);
this.connection.send('requestLog', {
diff --git a/packages/client/src/pages/admin/object-storage.vue b/packages/client/src/pages/admin/object-storage.vue
index 8984686b5e..6c5be220f8 100644
--- a/packages/client/src/pages/admin/object-storage.vue
+++ b/packages/client/src/pages/admin/object-storage.vue
@@ -1,76 +1,78 @@
<template>
-<FormBase>
+<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
<FormSuspense :p="init">
- <FormSwitch v-model="useObjectStorage">{{ $ts.useObjectStorage }}</FormSwitch>
+ <div class="_formRoot">
+ <FormSwitch v-model="useObjectStorage" class="_formBlock">{{ $ts.useObjectStorage }}</FormSwitch>
- <template v-if="useObjectStorage">
- <FormInput v-model="objectStorageBaseUrl">
- <span>{{ $ts.objectStorageBaseUrl }}</span>
- <template #desc>{{ $ts.objectStorageBaseUrlDesc }}</template>
- </FormInput>
+ <template v-if="useObjectStorage">
+ <FormInput v-model="objectStorageBaseUrl" class="_formBlock">
+ <template #label>{{ $ts.objectStorageBaseUrl }}</template>
+ <template #caption>{{ $ts.objectStorageBaseUrlDesc }}</template>
+ </FormInput>
- <FormInput v-model="objectStorageBucket">
- <span>{{ $ts.objectStorageBucket }}</span>
- <template #desc>{{ $ts.objectStorageBucketDesc }}</template>
- </FormInput>
+ <FormInput v-model="objectStorageBucket" class="_formBlock">
+ <template #label>{{ $ts.objectStorageBucket }}</template>
+ <template #caption>{{ $ts.objectStorageBucketDesc }}</template>
+ </FormInput>
- <FormInput v-model="objectStoragePrefix">
- <span>{{ $ts.objectStoragePrefix }}</span>
- <template #desc>{{ $ts.objectStoragePrefixDesc }}</template>
- </FormInput>
+ <FormInput v-model="objectStoragePrefix" class="_formBlock">
+ <template #label>{{ $ts.objectStoragePrefix }}</template>
+ <template #caption>{{ $ts.objectStoragePrefixDesc }}</template>
+ </FormInput>
- <FormInput v-model="objectStorageEndpoint">
- <span>{{ $ts.objectStorageEndpoint }}</span>
- <template #desc>{{ $ts.objectStorageEndpointDesc }}</template>
- </FormInput>
+ <FormInput v-model="objectStorageEndpoint" class="_formBlock">
+ <template #label>{{ $ts.objectStorageEndpoint }}</template>
+ <template #caption>{{ $ts.objectStorageEndpointDesc }}</template>
+ </FormInput>
- <FormInput v-model="objectStorageRegion">
- <span>{{ $ts.objectStorageRegion }}</span>
- <template #desc>{{ $ts.objectStorageRegionDesc }}</template>
- </FormInput>
+ <FormInput v-model="objectStorageRegion" class="_formBlock">
+ <template #label>{{ $ts.objectStorageRegion }}</template>
+ <template #caption>{{ $ts.objectStorageRegionDesc }}</template>
+ </FormInput>
- <FormInput v-model="objectStorageAccessKey">
- <template #prefix><i class="fas fa-key"></i></template>
- <span>Access key</span>
- </FormInput>
+ <FormSplit :min-width="280">
+ <FormInput v-model="objectStorageAccessKey" class="_formBlock">
+ <template #prefix><i class="fas fa-key"></i></template>
+ <template #label>Access key</template>
+ </FormInput>
- <FormInput v-model="objectStorageSecretKey">
- <template #prefix><i class="fas fa-key"></i></template>
- <span>Secret key</span>
- </FormInput>
+ <FormInput v-model="objectStorageSecretKey" class="_formBlock">
+ <template #prefix><i class="fas fa-key"></i></template>
+ <template #label>Secret key</template>
+ </FormInput>
+ </FormSplit>
- <FormSwitch v-model="objectStorageUseSSL">
- {{ $ts.objectStorageUseSSL }}
- <template #desc>{{ $ts.objectStorageUseSSLDesc }}</template>
- </FormSwitch>
+ <FormSwitch v-model="objectStorageUseSSL" class="_formBlock">
+ <template #label>{{ $ts.objectStorageUseSSL }}</template>
+ <template #caption>{{ $ts.objectStorageUseSSLDesc }}</template>
+ </FormSwitch>
- <FormSwitch v-model="objectStorageUseProxy">
- {{ $ts.objectStorageUseProxy }}
- <template #desc>{{ $ts.objectStorageUseProxyDesc }}</template>
- </FormSwitch>
+ <FormSwitch v-model="objectStorageUseProxy" class="_formBlock">
+ <template #label>{{ $ts.objectStorageUseProxy }}</template>
+ <template #caption>{{ $ts.objectStorageUseProxyDesc }}</template>
+ </FormSwitch>
- <FormSwitch v-model="objectStorageSetPublicRead">
- {{ $ts.objectStorageSetPublicRead }}
- </FormSwitch>
+ <FormSwitch v-model="objectStorageSetPublicRead" class="_formBlock">
+ <template #label>{{ $ts.objectStorageSetPublicRead }}</template>
+ </FormSwitch>
- <FormSwitch v-model="objectStorageS3ForcePathStyle">
- s3ForcePathStyle
- </FormSwitch>
- </template>
-
- <FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+ <FormSwitch v-model="objectStorageS3ForcePathStyle" class="_formBlock">
+ <template #label>s3ForcePathStyle</template>
+ </FormSwitch>
+ </template>
+ </div>
</FormSuspense>
-</FormBase>
+</MkSpacer>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSwitch from '@/components/debobigego/switch.vue';
-import FormInput from '@/components/debobigego/input.vue';
-import FormButton from '@/components/debobigego/button.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormGroup from '@/components/debobigego/group.vue';
-import FormSuspense from '@/components/debobigego/suspense.vue';
+import FormSwitch from '@/components/form/switch.vue';
+import FormInput from '@/components/form/input.vue';
+import FormGroup from '@/components/form/group.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';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
@@ -79,10 +81,10 @@ export default defineComponent({
components: {
FormSwitch,
FormInput,
- FormBase,
FormGroup,
- FormButton,
FormSuspense,
+ FormSplit,
+ FormSection,
},
emits: ['info'],
@@ -93,6 +95,12 @@ export default defineComponent({
title: this.$ts.objectStorage,
icon: 'fas fa-cloud',
bg: 'var(--bg)',
+ actions: [{
+ asFullButton: true,
+ icon: 'fas fa-check',
+ text: this.$ts.save,
+ handler: this.save,
+ }],
},
useObjectStorage: false,
objectStorageBaseUrl: null,
@@ -110,10 +118,6 @@ export default defineComponent({
}
},
- async mounted() {
- this.$emit('info', this[symbols.PAGE_INFO]);
- },
-
methods: {
async init() {
const meta = await os.api('meta', { detail: true });
diff --git a/packages/client/src/pages/admin/other-settings.vue b/packages/client/src/pages/admin/other-settings.vue
index eb214a21c8..6b588e88aa 100644
--- a/packages/client/src/pages/admin/other-settings.vue
+++ b/packages/client/src/pages/admin/other-settings.vue
@@ -1,34 +1,17 @@
<template>
-<FormBase>
+<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
<FormSuspense :p="init">
- <FormGroup>
- <FormInput v-model="summalyProxy">
- <template #prefix><i class="fas fa-link"></i></template>
- Summaly Proxy URL
- </FormInput>
- </FormGroup>
- <FormGroup>
- <FormInput v-model="deeplAuthKey">
- <template #prefix><i class="fas fa-key"></i></template>
- DeepL Auth Key
- </FormInput>
- <FormSwitch v-model="deeplIsPro">
- Pro account
- </FormSwitch>
- </FormGroup>
- <FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+ none
</FormSuspense>
-</FormBase>
+</MkSpacer>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSwitch from '@/components/debobigego/switch.vue';
-import FormInput from '@/components/debobigego/input.vue';
-import FormButton from '@/components/debobigego/button.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormGroup from '@/components/debobigego/group.vue';
-import FormSuspense from '@/components/debobigego/suspense.vue';
+import FormSwitch from '@/components/form/switch.vue';
+import FormInput from '@/components/form/input.vue';
+import FormSection from '@/components/form/section.vue';
+import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
@@ -37,9 +20,7 @@ export default defineComponent({
components: {
FormSwitch,
FormInput,
- FormBase,
- FormGroup,
- FormButton,
+ FormSection,
FormSuspense,
},
@@ -51,29 +32,22 @@ export default defineComponent({
title: this.$ts.other,
icon: 'fas fa-cogs',
bg: 'var(--bg)',
+ actions: [{
+ asFullButton: true,
+ icon: 'fas fa-check',
+ text: this.$ts.save,
+ handler: this.save,
+ }],
},
- summalyProxy: '',
- deeplAuthKey: '',
- deeplIsPro: false,
}
},
- async mounted() {
- this.$emit('info', this[symbols.PAGE_INFO]);
- },
-
methods: {
async init() {
const meta = await os.api('meta', { detail: true });
- this.summalyProxy = meta.summalyProxy;
- this.deeplAuthKey = meta.deeplAuthKey;
- this.deeplIsPro = meta.deeplIsPro;
},
save() {
os.apiWithDialog('admin/update-meta', {
- summalyProxy: this.summalyProxy,
- deeplAuthKey: this.deeplAuthKey,
- deeplIsPro: this.deeplIsPro,
}).then(() => {
fetchInstance();
});
diff --git a/packages/client/src/pages/admin/overview.vue b/packages/client/src/pages/admin/overview.vue
index da5fc0ba6d..b8ae8ad9e1 100644
--- a/packages/client/src/pages/admin/overview.vue
+++ b/packages/client/src/pages/admin/overview.vue
@@ -19,7 +19,7 @@
<MkContainer :foldable="true" class="charts">
<template #header><i class="fas fa-chart-bar"></i>{{ $ts.charts }}</template>
- <div style="padding-top: 12px;">
+ <div style="padding: 12px;">
<MkInstanceStats :chart-limit="500" :detailed="true"/>
</div>
</MkContainer>
@@ -67,7 +67,6 @@
<script lang="ts">
import { computed, defineComponent, markRaw, version as vueVersion } from 'vue';
-import FormKeyValueView from '@/components/debobigego/key-value-view.vue';
import MkInstanceStats from '@/components/instance-stats.vue';
import MkButton from '@/components/ui/button.vue';
import MkSelect from '@/components/form/select.vue';
@@ -78,15 +77,14 @@ import MkQueueChart from '@/components/queue-chart.vue';
import { version, url } from '@/config';
import bytes from '@/filters/bytes';
import number from '@/filters/number';
-import MkInstanceInfo from './instance.vue';
import XMetrics from './metrics.vue';
import * as os from '@/os';
+import { stream } from '@/stream';
import * as symbols from '@/symbols';
export default defineComponent({
components: {
MkNumberDiff,
- FormKeyValueView,
MkInstanceStats,
MkContainer,
MkFolder,
@@ -113,13 +111,11 @@ export default defineComponent({
notesComparedToThePrevDay: null,
fetchJobs: () => os.api('admin/queue/deliver-delayed', {}),
fetchModLogs: () => os.api('admin/show-moderation-logs', {}),
- queueStatsConnection: markRaw(os.stream.useChannel('queueStats')),
+ queueStatsConnection: markRaw(stream.useChannel('queueStats')),
}
},
async mounted() {
- this.$emit('info', this[symbols.PAGE_INFO]);
-
os.api('meta', { detail: true }).then(meta => {
this.meta = meta;
});
@@ -160,9 +156,7 @@ export default defineComponent({
host: q
});
}
- os.popup(MkInstanceInfo, {
- instance: instance
- }, {}, 'closed');
+ // TODO
},
bytes,
diff --git a/packages/client/src/pages/admin/proxy-account.vue b/packages/client/src/pages/admin/proxy-account.vue
index 14ef92a747..5c4fbffa0c 100644
--- a/packages/client/src/pages/admin/proxy-account.vue
+++ b/packages/client/src/pages/admin/proxy-account.vue
@@ -1,42 +1,32 @@
<template>
-<FormBase>
+<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
<FormSuspense :p="init">
- <FormGroup>
- <FormKeyValueView>
- <template #key>{{ $ts.proxyAccount }}</template>
- <template #value>{{ proxyAccount ? `@${proxyAccount.username}` : $ts.none }}</template>
- </FormKeyValueView>
- <template #caption>{{ $ts.proxyAccountDescription }}</template>
- </FormGroup>
+ <MkInfo class="_formBlock">{{ $ts.proxyAccountDescription }}</MkInfo>
+ <MkKeyValue class="_formBlock">
+ <template #key>{{ $ts.proxyAccount }}</template>
+ <template #value>{{ proxyAccount ? `@${proxyAccount.username}` : $ts.none }}</template>
+ </MkKeyValue>
- <FormButton primary @click="chooseProxyAccount">{{ $ts.selectAccount }}</FormButton>
+ <FormButton primary class="_formBlock" @click="chooseProxyAccount">{{ $ts.selectAccount }}</FormButton>
</FormSuspense>
-</FormBase>
+</MkSpacer>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
-import FormKeyValueView from '@/components/debobigego/key-value-view.vue';
-import FormInput from '@/components/debobigego/input.vue';
-import FormButton from '@/components/debobigego/button.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormGroup from '@/components/debobigego/group.vue';
-import FormTextarea from '@/components/debobigego/textarea.vue';
-import FormInfo from '@/components/debobigego/info.vue';
-import FormSuspense from '@/components/debobigego/suspense.vue';
+import MkKeyValue from '@/components/key-value.vue';
+import FormButton from '@/components/ui/button.vue';
+import MkInfo from '@/components/ui/info.vue';
+import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
export default defineComponent({
components: {
- FormKeyValueView,
- FormInput,
- FormBase,
- FormGroup,
+ MkKeyValue,
FormButton,
- FormTextarea,
- FormInfo,
+ MkInfo,
FormSuspense,
},
@@ -54,10 +44,6 @@ export default defineComponent({
}
},
- async mounted() {
- this.$emit('info', this[symbols.PAGE_INFO]);
- },
-
methods: {
async init() {
const meta = await os.api('meta', { detail: true });
diff --git a/packages/client/src/pages/admin/queue.vue b/packages/client/src/pages/admin/queue.vue
index 37a87089cb..522210d933 100644
--- a/packages/client/src/pages/admin/queue.vue
+++ b/packages/client/src/pages/admin/queue.vue
@@ -1,28 +1,25 @@
<template>
-<FormBase>
+<MkSpacer :content-max="800">
<XQueue :connection="connection" domain="inbox">
<template #title>In</template>
</XQueue>
<XQueue :connection="connection" domain="deliver">
<template #title>Out</template>
</XQueue>
- <FormButton danger @click="clear()"><i class="fas fa-trash-alt"></i> {{ $ts.clearQueue }}</FormButton>
-</FormBase>
+ <MkButton danger @click="clear()"><i class="fas fa-trash-alt"></i> {{ $ts.clearQueue }}</MkButton>
+</MkSpacer>
</template>
<script lang="ts">
import { defineComponent, markRaw } from 'vue';
import MkButton from '@/components/ui/button.vue';
import XQueue from './queue.chart.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormButton from '@/components/debobigego/button.vue';
import * as os from '@/os';
+import { stream } from '@/stream';
import * as symbols from '@/symbols';
export default defineComponent({
components: {
- FormBase,
- FormButton,
MkButton,
XQueue,
},
@@ -36,13 +33,11 @@ export default defineComponent({
icon: 'fas fa-clipboard-list',
bg: 'var(--bg)',
},
- connection: markRaw(os.stream.useChannel('queueStats')),
+ connection: markRaw(stream.useChannel('queueStats')),
}
},
mounted() {
- this.$emit('info', this[symbols.PAGE_INFO]);
-
this.$nextTick(() => {
this.connection.send('requestLog', {
id: Math.random().toString().substr(2, 8),
diff --git a/packages/client/src/pages/admin/relays.vue b/packages/client/src/pages/admin/relays.vue
index 3e2f1c6f26..bb840db0a2 100644
--- a/packages/client/src/pages/admin/relays.vue
+++ b/packages/client/src/pages/admin/relays.vue
@@ -1,32 +1,27 @@
<template>
-<FormBase class="relaycxt">
- <FormButton primary @click="addRelay"><i class="fas fa-plus"></i> {{ $ts.addRelay }}</FormButton>
-
- <div v-for="relay in relays" :key="relay.inbox" class="_debobigegoItem">
- <div class="_debobigegoPanel" style="padding: 16px;">
- <div>{{ relay.inbox }}</div>
- <div>{{ $t(`_relayStatus.${relay.status}`) }}</div>
- <MkButton class="button" inline danger @click="remove(relay.inbox)"><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton>
+<MkSpacer :content-max="800">
+ <div v-for="relay in relays" :key="relay.inbox" class="relaycxt _panel _block" style="padding: 16px;">
+ <div>{{ relay.inbox }}</div>
+ <div class="status">
+ <i v-if="relay.status === 'accepted'" class="fas fa-check icon accepted"></i>
+ <i v-else-if="relay.status === 'rejected'" class="fas fa-ban icon rejected"></i>
+ <i v-else class="fas fa-clock icon requesting"></i>
+ <span>{{ $t(`_relayStatus.${relay.status}`) }}</span>
</div>
+ <MkButton class="button" inline danger @click="remove(relay.inbox)"><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton>
</div>
-</FormBase>
+</MkSpacer>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import MkButton from '@/components/ui/button.vue';
-import MkInput from '@/components/form/input.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormButton from '@/components/debobigego/button.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
export default defineComponent({
components: {
- FormBase,
- FormButton,
MkButton,
- MkInput,
},
emits: ['info'],
@@ -37,6 +32,12 @@ export default defineComponent({
title: this.$ts.relays,
icon: 'fas fa-globe',
bg: 'var(--bg)',
+ actions: [{
+ asFullButton: true,
+ icon: 'fas fa-plus',
+ text: this.$ts.addRelay,
+ handler: this.addRelay,
+ }],
},
relays: [],
inbox: '',
@@ -47,10 +48,6 @@ export default defineComponent({
this.refresh();
},
- mounted() {
- this.$emit('info', this[symbols.PAGE_INFO]);
- },
-
methods: {
async addRelay() {
const { canceled, result: inbox } = await os.inputText({
@@ -94,5 +91,22 @@ export default defineComponent({
</script>
<style lang="scss" scoped>
+.relaycxt {
+ > .status {
+ margin: 8px 0;
+
+ > .icon {
+ width: 1em;
+ margin-right: 0.75em;
+ &.accepted {
+ color: var(--success);
+ }
+
+ &.rejected {
+ color: var(--error);
+ }
+ }
+ }
+}
</style>
diff --git a/packages/client/src/pages/admin/security.vue b/packages/client/src/pages/admin/security.vue
index adfb2e786c..d069891647 100644
--- a/packages/client/src/pages/admin/security.vue
+++ b/packages/client/src/pages/admin/security.vue
@@ -1,44 +1,58 @@
<template>
-<FormBase>
+<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
<FormSuspense :p="init">
- <FormLink to="/admin/bot-protection">
- <i class="fas fa-shield-alt"></i> {{ $ts.botProtection }}
- <template v-if="enableHcaptcha" #suffix>hCaptcha</template>
- <template v-else-if="enableRecaptcha" #suffix>reCAPTCHA</template>
- <template v-else #suffix>{{ $ts.none }} ({{ $ts.notRecommended }})</template>
- </FormLink>
+ <div class="_formRoot">
+ <FormFolder class="_formBlock">
+ <template #icon><i class="fas fa-shield-alt"></i></template>
+ <template #label>{{ $ts.botProtection }}</template>
+ <template v-if="enableHcaptcha" #suffix>hCaptcha</template>
+ <template v-else-if="enableRecaptcha" #suffix>reCAPTCHA</template>
+ <template v-else #suffix>{{ $ts.none }} ({{ $ts.notRecommended }})</template>
- <FormSwitch v-model="enableRegistration">{{ $ts.enableRegistration }}</FormSwitch>
+ <XBotProtection/>
+ </FormFolder>
- <FormSwitch v-model="emailRequiredForSignup">{{ $ts.emailRequiredForSignup }}</FormSwitch>
+ <FormFolder class="_formBlock">
+ <template #label>Summaly Proxy</template>
- <FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+ <div class="_formRoot">
+ <FormInput v-model="summalyProxy" class="_formBlock">
+ <template #prefix><i class="fas fa-link"></i></template>
+ <template #label>Summaly Proxy URL</template>
+ </FormInput>
+
+ <FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+ </div>
+ </FormFolder>
+ </div>
</FormSuspense>
-</FormBase>
+</MkSpacer>
</template>
<script lang="ts">
import { defineAsyncComponent, defineComponent } from 'vue';
-import FormLink from '@/components/debobigego/link.vue';
-import FormSwitch from '@/components/debobigego/switch.vue';
-import FormButton from '@/components/debobigego/button.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormGroup from '@/components/debobigego/group.vue';
-import FormInfo from '@/components/debobigego/info.vue';
-import FormSuspense from '@/components/debobigego/suspense.vue';
+import FormFolder from '@/components/form/folder.vue';
+import FormSwitch from '@/components/form/switch.vue';
+import FormInfo from '@/components/ui/info.vue';
+import FormSuspense from '@/components/form/suspense.vue';
+import FormSection from '@/components/form/section.vue';
+import FormInput from '@/components/form/input.vue';
+import FormButton from '@/components/ui/button.vue';
+import XBotProtection from './bot-protection.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
export default defineComponent({
components: {
- FormLink,
+ FormFolder,
FormSwitch,
- FormBase,
- FormGroup,
- FormButton,
FormInfo,
+ FormSection,
FormSuspense,
+ FormButton,
+ FormInput,
+ XBotProtection,
},
emits: ['info'],
@@ -50,30 +64,23 @@ export default defineComponent({
icon: 'fas fa-lock',
bg: 'var(--bg)',
},
+ summalyProxy: '',
enableHcaptcha: false,
enableRecaptcha: false,
- enableRegistration: false,
- emailRequiredForSignup: false,
}
},
- async mounted() {
- this.$emit('info', this[symbols.PAGE_INFO]);
- },
-
methods: {
async init() {
const meta = await os.api('meta', { detail: true });
+ this.summalyProxy = meta.summalyProxy;
this.enableHcaptcha = meta.enableHcaptcha;
this.enableRecaptcha = meta.enableRecaptcha;
- this.enableRegistration = !meta.disableRegistration;
- this.emailRequiredForSignup = meta.emailRequiredForSignup;
},
-
+
save() {
os.apiWithDialog('admin/update-meta', {
- disableRegistration: !this.enableRegistration,
- emailRequiredForSignup: this.emailRequiredForSignup,
+ summalyProxy: this.summalyProxy,
}).then(() => {
fetchInstance();
});
diff --git a/packages/client/src/pages/admin/service-worker.vue b/packages/client/src/pages/admin/service-worker.vue
deleted file mode 100644
index f34cb03e4e..0000000000
--- a/packages/client/src/pages/admin/service-worker.vue
+++ /dev/null
@@ -1,85 +0,0 @@
-<template>
-<FormBase>
- <FormSuspense :p="init">
- <FormSwitch v-model="enableServiceWorker">
- {{ $ts.enableServiceworker }}
- <template #desc>{{ $ts.serviceworkerInfo }}</template>
- </FormSwitch>
-
- <template v-if="enableServiceWorker">
- <FormInput v-model="swPublicKey">
- <template #prefix><i class="fas fa-key"></i></template>
- Public key
- </FormInput>
-
- <FormInput v-model="swPrivateKey">
- <template #prefix><i class="fas fa-key"></i></template>
- Private key
- </FormInput>
- </template>
-
- <FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
- </FormSuspense>
-</FormBase>
-</template>
-
-<script lang="ts">
-import { defineComponent } from 'vue';
-import FormSwitch from '@/components/debobigego/switch.vue';
-import FormInput from '@/components/debobigego/input.vue';
-import FormButton from '@/components/debobigego/button.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormGroup from '@/components/debobigego/group.vue';
-import FormSuspense from '@/components/debobigego/suspense.vue';
-import * as os from '@/os';
-import * as symbols from '@/symbols';
-import { fetchInstance } from '@/instance';
-
-export default defineComponent({
- components: {
- FormSwitch,
- FormInput,
- FormBase,
- FormGroup,
- FormButton,
- FormSuspense,
- },
-
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: 'ServiceWorker',
- icon: 'fas fa-bolt',
- bg: 'var(--bg)',
- },
- enableServiceWorker: false,
- swPublicKey: null,
- swPrivateKey: null,
- }
- },
-
- async mounted() {
- this.$emit('info', this[symbols.PAGE_INFO]);
- },
-
- methods: {
- async init() {
- const meta = await os.api('meta', { detail: true });
- this.enableServiceWorker = meta.enableServiceWorker;
- this.swPublicKey = meta.swPublickey;
- this.swPrivateKey = meta.swPrivateKey;
- },
- save() {
- os.apiWithDialog('admin/update-meta', {
- enableServiceWorker: this.enableServiceWorker,
- swPublicKey: this.swPublicKey,
- swPrivateKey: this.swPrivateKey,
- }).then(() => {
- fetchInstance();
- });
- }
- }
-});
-</script>
diff --git a/packages/client/src/pages/admin/settings.vue b/packages/client/src/pages/admin/settings.vue
index d88445abdb..a4bac93834 100644
--- a/packages/client/src/pages/admin/settings.vue
+++ b/packages/client/src/pages/admin/settings.vue
@@ -1,72 +1,146 @@
<template>
-<FormBase>
+<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
<FormSuspense :p="init">
- <FormInput v-model="name">
- <span>{{ $ts.instanceName }}</span>
- </FormInput>
+ <div class="_formRoot">
+ <FormInput v-model="name" class="_formBlock">
+ <template #label>{{ $ts.instanceName }}</template>
+ </FormInput>
- <FormTextarea v-model="description">
- <span>{{ $ts.instanceDescription }}</span>
- </FormTextarea>
+ <FormTextarea v-model="description" class="_formBlock">
+ <template #label>{{ $ts.instanceDescription }}</template>
+ </FormTextarea>
- <FormInput v-model="iconUrl">
- <template #prefix><i class="fas fa-link"></i></template>
- <span>{{ $ts.iconUrl }}</span>
- </FormInput>
+ <FormInput v-model="iconUrl" class="_formBlock">
+ <template #prefix><i class="fas fa-link"></i></template>
+ <template #label>{{ $ts.iconUrl }}</template>
+ </FormInput>
- <FormInput v-model="bannerUrl">
- <template #prefix><i class="fas fa-link"></i></template>
- <span>{{ $ts.bannerUrl }}</span>
- </FormInput>
+ <FormInput v-model="bannerUrl" class="_formBlock">
+ <template #prefix><i class="fas fa-link"></i></template>
+ <template #label>{{ $ts.bannerUrl }}</template>
+ </FormInput>
- <FormInput v-model="backgroundImageUrl">
- <template #prefix><i class="fas fa-link"></i></template>
- <span>{{ $ts.backgroundImageUrl }}</span>
- </FormInput>
+ <FormInput v-model="backgroundImageUrl" class="_formBlock">
+ <template #prefix><i class="fas fa-link"></i></template>
+ <template #label>{{ $ts.backgroundImageUrl }}</template>
+ </FormInput>
- <FormInput v-model="tosUrl">
- <template #prefix><i class="fas fa-link"></i></template>
- <span>{{ $ts.tosUrl }}</span>
- </FormInput>
+ <FormInput v-model="tosUrl" class="_formBlock">
+ <template #prefix><i class="fas fa-link"></i></template>
+ <template #label>{{ $ts.tosUrl }}</template>
+ </FormInput>
- <FormInput v-model="maintainerName">
- <span>{{ $ts.maintainerName }}</span>
- </FormInput>
+ <FormSplit :min-width="300">
+ <FormInput v-model="maintainerName" class="_formBlock">
+ <template #label>{{ $ts.maintainerName }}</template>
+ </FormInput>
- <FormInput v-model="maintainerEmail" type="email">
- <template #prefix><i class="fas fa-envelope"></i></template>
- <span>{{ $ts.maintainerEmail }}</span>
- </FormInput>
+ <FormInput v-model="maintainerEmail" type="email" class="_formBlock">
+ <template #prefix><i class="fas fa-envelope"></i></template>
+ <template #label>{{ $ts.maintainerEmail }}</template>
+ </FormInput>
+ </FormSplit>
- <FormTextarea v-model="pinnedUsers">
- <span>{{ $ts.pinnedUsers }}</span>
- <template #desc>{{ $ts.pinnedUsersDescription }}</template>
- </FormTextarea>
+ <FormTextarea v-model="pinnedUsers" class="_formBlock">
+ <template #label>{{ $ts.pinnedUsers }}</template>
+ <template #caption>{{ $ts.pinnedUsersDescription }}</template>
+ </FormTextarea>
- <FormInput v-model="maxNoteTextLength" type="number">
- <template #prefix><i class="fas fa-pencil-alt"></i></template>
- <span>{{ $ts.maxNoteTextLength }}</span>
- </FormInput>
+ <FormInput v-model="maxNoteTextLength" type="number" class="_formBlock">
+ <template #prefix><i class="fas fa-pencil-alt"></i></template>
+ <template #label>{{ $ts.maxNoteTextLength }}</template>
+ </FormInput>
- <FormSwitch v-model="enableLocalTimeline">{{ $ts.enableLocalTimeline }}</FormSwitch>
- <FormSwitch v-model="enableGlobalTimeline">{{ $ts.enableGlobalTimeline }}</FormSwitch>
- <FormInfo>{{ $ts.disablingTimelinesInfo }}</FormInfo>
+ <FormSection>
+ <FormSwitch v-model="enableRegistration" class="_formBlock">
+ <template #label>{{ $ts.enableRegistration }}</template>
+ </FormSwitch>
- <FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+ <FormSwitch v-model="emailRequiredForSignup" class="_formBlock">
+ <template #label>{{ $ts.emailRequiredForSignup }}</template>
+ </FormSwitch>
+ </FormSection>
+
+ <FormSection>
+ <FormSwitch v-model="enableLocalTimeline" class="_formBlock">{{ $ts.enableLocalTimeline }}</FormSwitch>
+ <FormSwitch v-model="enableGlobalTimeline" class="_formBlock">{{ $ts.enableGlobalTimeline }}</FormSwitch>
+ <FormInfo class="_formBlock">{{ $ts.disablingTimelinesInfo }}</FormInfo>
+ </FormSection>
+
+ <FormSection>
+ <template #label>{{ $ts.files }}</template>
+
+ <FormSwitch v-model="cacheRemoteFiles" class="_formBlock">
+ <template #label>{{ $ts.cacheRemoteFiles }}</template>
+ <template #caption>{{ $ts.cacheRemoteFilesDescription }}</template>
+ </FormSwitch>
+
+ <FormSwitch v-model="proxyRemoteFiles" class="_formBlock">
+ <template #label>{{ $ts.proxyRemoteFiles }}</template>
+ <template #caption>{{ $ts.proxyRemoteFilesDescription }}</template>
+ </FormSwitch>
+
+ <FormSplit :min-width="280">
+ <FormInput v-model="localDriveCapacityMb" type="number" class="_formBlock">
+ <template #label>{{ $ts.driveCapacityPerLocalAccount }}</template>
+ <template #suffix>MB</template>
+ <template #caption>{{ $ts.inMb }}</template>
+ </FormInput>
+
+ <FormInput v-model="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles" class="_formBlock">
+ <template #label>{{ $ts.driveCapacityPerRemoteAccount }}</template>
+ <template #suffix>MB</template>
+ <template #caption>{{ $ts.inMb }}</template>
+ </FormInput>
+ </FormSplit>
+ </FormSection>
+
+ <FormSection>
+ <template #label>ServiceWorker</template>
+
+ <FormSwitch v-model="enableServiceWorker" class="_formBlock">
+ <template #label>{{ $ts.enableServiceworker }}</template>
+ <template #caption>{{ $ts.serviceworkerInfo }}</template>
+ </FormSwitch>
+
+ <template v-if="enableServiceWorker">
+ <FormInput v-model="swPublicKey" class="_formBlock">
+ <template #prefix><i class="fas fa-key"></i></template>
+ <template #label>Public key</template>
+ </FormInput>
+
+ <FormInput v-model="swPrivateKey" class="_formBlock">
+ <template #prefix><i class="fas fa-key"></i></template>
+ <template #label>Private key</template>
+ </FormInput>
+ </template>
+ </FormSection>
+
+ <FormSection>
+ <template #label>DeepL Translation</template>
+
+ <FormInput v-model="deeplAuthKey" class="_formBlock">
+ <template #prefix><i class="fas fa-key"></i></template>
+ <template #label>DeepL Auth Key</template>
+ </FormInput>
+ <FormSwitch v-model="deeplIsPro" class="_formBlock">
+ <template #label>Pro account</template>
+ </FormSwitch>
+ </FormSection>
+ </div>
</FormSuspense>
-</FormBase>
+</MkSpacer>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
-import FormSwitch from '@/components/debobigego/switch.vue';
-import FormInput from '@/components/debobigego/input.vue';
-import FormButton from '@/components/debobigego/button.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormGroup from '@/components/debobigego/group.vue';
-import FormTextarea from '@/components/debobigego/textarea.vue';
-import FormInfo from '@/components/debobigego/info.vue';
-import FormSuspense from '@/components/debobigego/suspense.vue';
+import FormSwitch from '@/components/form/switch.vue';
+import FormInput from '@/components/form/input.vue';
+import FormTextarea from '@/components/form/textarea.vue';
+import FormInfo from '@/components/ui/info.vue';
+import FormSection from '@/components/form/section.vue';
+import FormSplit from '@/components/form/split.vue';
+import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
@@ -75,12 +149,11 @@ export default defineComponent({
components: {
FormSwitch,
FormInput,
- FormBase,
- FormGroup,
- FormButton,
+ FormSuspense,
FormTextarea,
FormInfo,
- FormSuspense,
+ FormSection,
+ FormSplit,
},
emits: ['info'],
@@ -91,6 +164,12 @@ export default defineComponent({
title: this.$ts.general,
icon: 'fas fa-cog',
bg: 'var(--bg)',
+ actions: [{
+ asFullButton: true,
+ icon: 'fas fa-check',
+ text: this.$ts.save,
+ handler: this.save,
+ }],
},
name: null,
description: null,
@@ -104,13 +183,20 @@ export default defineComponent({
enableLocalTimeline: false,
enableGlobalTimeline: false,
pinnedUsers: '',
+ cacheRemoteFiles: false,
+ proxyRemoteFiles: false,
+ localDriveCapacityMb: 0,
+ remoteDriveCapacityMb: 0,
+ enableRegistration: false,
+ emailRequiredForSignup: false,
+ enableServiceWorker: false,
+ swPublicKey: null,
+ swPrivateKey: null,
+ deeplAuthKey: '',
+ deeplIsPro: false,
}
},
- async mounted() {
- this.$emit('info', this[symbols.PAGE_INFO]);
- },
-
methods: {
async init() {
const meta = await os.api('meta', { detail: true });
@@ -126,6 +212,17 @@ export default defineComponent({
this.enableLocalTimeline = !meta.disableLocalTimeline;
this.enableGlobalTimeline = !meta.disableGlobalTimeline;
this.pinnedUsers = meta.pinnedUsers.join('\n');
+ this.cacheRemoteFiles = meta.cacheRemoteFiles;
+ this.proxyRemoteFiles = meta.proxyRemoteFiles;
+ this.localDriveCapacityMb = meta.driveCapacityPerLocalUserMb;
+ this.remoteDriveCapacityMb = meta.driveCapacityPerRemoteUserMb;
+ this.enableRegistration = !meta.disableRegistration;
+ this.emailRequiredForSignup = meta.emailRequiredForSignup;
+ this.enableServiceWorker = meta.enableServiceWorker;
+ this.swPublicKey = meta.swPublickey;
+ this.swPrivateKey = meta.swPrivateKey;
+ this.deeplAuthKey = meta.deeplAuthKey;
+ this.deeplIsPro = meta.deeplIsPro;
},
save() {
@@ -142,6 +239,17 @@ export default defineComponent({
disableLocalTimeline: !this.enableLocalTimeline,
disableGlobalTimeline: !this.enableGlobalTimeline,
pinnedUsers: this.pinnedUsers.split('\n'),
+ cacheRemoteFiles: this.cacheRemoteFiles,
+ proxyRemoteFiles: this.proxyRemoteFiles,
+ localDriveCapacityMb: parseInt(this.localDriveCapacityMb, 10),
+ remoteDriveCapacityMb: parseInt(this.remoteDriveCapacityMb, 10),
+ disableRegistration: !this.enableRegistration,
+ emailRequiredForSignup: this.emailRequiredForSignup,
+ enableServiceWorker: this.enableServiceWorker,
+ swPublicKey: this.swPublicKey,
+ swPrivateKey: this.swPrivateKey,
+ deeplAuthKey: this.deeplAuthKey,
+ deeplIsPro: this.deeplIsPro,
}).then(() => {
fetchInstance();
});
diff --git a/packages/client/src/pages/admin/users.vue b/packages/client/src/pages/admin/users.vue
index e7a3437167..03e155ddcf 100644
--- a/packages/client/src/pages/admin/users.vue
+++ b/packages/client/src/pages/admin/users.vue
@@ -30,7 +30,7 @@
<template #prefix>@</template>
<template #label>{{ $ts.username }}</template>
</MkInput>
- <MkInput v-model="searchHost" style="flex: 1;" type="text" spellcheck="false" :disabled="pagination.params().origin === 'local'" @update:modelValue="$refs.users.reload()">
+ <MkInput v-model="searchHost" style="flex: 1;" type="text" spellcheck="false" :disabled="pagination.params.origin === 'local'" @update:modelValue="$refs.users.reload()">
<template #prefix>@</template>
<template #label>{{ $ts.host }}</template>
</MkInput>
@@ -62,7 +62,7 @@
</template>
<script lang="ts">
-import { defineComponent } from 'vue';
+import { computed, defineComponent } from 'vue';
import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/form/input.vue';
import MkSelect from '@/components/form/select.vue';
@@ -110,36 +110,20 @@ export default defineComponent({
searchUsername: '',
searchHost: '',
pagination: {
- endpoint: 'admin/show-users',
+ endpoint: 'admin/show-users' as const,
limit: 10,
- params: () => ({
+ params: computed(() => ({
sort: this.sort,
state: this.state,
origin: this.origin,
username: this.searchUsername,
hostname: this.searchHost,
- }),
+ })),
offsetMode: true
},
}
},
- watch: {
- sort() {
- this.$refs.users.reload();
- },
- state() {
- this.$refs.users.reload();
- },
- origin() {
- this.$refs.users.reload();
- },
- },
-
- async mounted() {
- this.$emit('info', this[symbols.PAGE_INFO]);
- },
-
methods: {
lookupUser,