diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2022-06-11 19:31:03 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-06-11 19:31:03 +0900 |
| commit | 182a1bf653ecfbcf76e4530b3077c6252b0d4827 (patch) | |
| tree | 45d1472747d4cac017e96616f844292f5785ccdd /packages/client/src/pages/admin | |
| parent | 12.110.1 (diff) | |
| parent | 12.111.0 (diff) | |
| download | sharkey-182a1bf653ecfbcf76e4530b3077c6252b0d4827.tar.gz sharkey-182a1bf653ecfbcf76e4530b3077c6252b0d4827.tar.bz2 sharkey-182a1bf653ecfbcf76e4530b3077c6252b0d4827.zip | |
Merge pull request #8783 from misskey-dev/develop
Release: 12.111.0
Diffstat (limited to 'packages/client/src/pages/admin')
26 files changed, 1182 insertions, 1583 deletions
diff --git a/packages/client/src/pages/admin/abuses.vue b/packages/client/src/pages/admin/abuses.vue index 92f93797ce..e1d0361c0b 100644 --- a/packages/client/src/pages/admin/abuses.vue +++ b/packages/client/src/pages/admin/abuses.vue @@ -24,10 +24,10 @@ </div> <!-- TODO <div class="inputs" style="display: flex; padding-top: 1.2em;"> - <MkInput v-model="searchUsername" style="margin: 0; flex: 1;" type="text" spellcheck="false" @update:modelValue="$refs.reports.reload()"> + <MkInput v-model="searchUsername" style="margin: 0; flex: 1;" type="text" spellcheck="false"> <span>{{ $ts.username }}</span> </MkInput> - <MkInput v-model="searchHost" style="margin: 0; flex: 1;" type="text" spellcheck="false" @update:modelValue="$refs.reports.reload()" :disabled="pagination.params().origin === 'local'"> + <MkInput v-model="searchHost" style="margin: 0; flex: 1;" type="text" spellcheck="false" :disabled="pagination.params().origin === 'local'"> <span>{{ $ts.host }}</span> </MkInput> </div> @@ -41,8 +41,8 @@ </div> </template> -<script lang="ts"> -import { computed, defineComponent } from 'vue'; +<script lang="ts" setup> +import { computed } from 'vue'; import MkInput from '@/components/form/input.vue'; import MkSelect from '@/components/form/select.vue'; @@ -50,45 +50,35 @@ import MkPagination from '@/components/ui/pagination.vue'; import XAbuseReport from '@/components/abuse-report.vue'; import * as os from '@/os'; import * as symbols from '@/symbols'; +import { i18n } from '@/i18n'; -export default defineComponent({ - components: { - MkInput, - MkSelect, - MkPagination, - XAbuseReport, - }, +let reports = $ref<InstanceType<typeof MkPagination>>(); - emits: ['info'], +let state = $ref('unresolved'); +let reporterOrigin = $ref('combined'); +let targetUserOrigin = $ref('combined'); +let searchUsername = $ref(''); +let searchHost = $ref(''); - data() { - return { - [symbols.PAGE_INFO]: { - title: this.$ts.abuseReports, - icon: 'fas fa-exclamation-circle', - bg: 'var(--bg)', - }, - searchUsername: '', - searchHost: '', - state: 'unresolved', - reporterOrigin: 'combined', - targetUserOrigin: 'combined', - pagination: { - endpoint: 'admin/abuse-user-reports' as const, - limit: 10, - params: computed(() => ({ - state: this.state, - reporterOrigin: this.reporterOrigin, - targetUserOrigin: this.targetUserOrigin, - })), - }, - } - }, +const pagination = { + endpoint: 'admin/abuse-user-reports' as const, + limit: 10, + params: computed(() => ({ + state, + reporterOrigin, + targetUserOrigin, + })), +}; - methods: { - resolved(reportId) { - this.$refs.reports.removeItem(item => item.id === reportId); - }, +function resolved(reportId) { + reports.removeItem(item => item.id === reportId); +} + +defineExpose({ + [symbols.PAGE_INFO]: { + title: i18n.ts.abuseReports, + icon: 'fas fa-exclamation-circle', + bg: 'var(--bg)', } }); </script> diff --git a/packages/client/src/pages/admin/ads.vue b/packages/client/src/pages/admin/ads.vue index 8f164caa99..b18e08db96 100644 --- a/packages/client/src/pages/admin/ads.vue +++ b/packages/client/src/pages/admin/ads.vue @@ -7,7 +7,7 @@ <template #label>URL</template> </MkInput> <MkInput v-model="ad.imageUrl" class="_formBlock"> - <template #label>{{ $ts.imageUrl }}</template> + <template #label>{{ i18n.ts.imageUrl }}</template> </MkInput> <FormRadios v-model="ad.place" class="_formBlock"> <template #label>Form</template> @@ -17,34 +17,34 @@ </FormRadios> <!-- <div style="margin: 32px 0;"> - {{ $ts.priority }} - <MkRadio v-model="ad.priority" value="high">{{ $ts.high }}</MkRadio> - <MkRadio v-model="ad.priority" value="middle">{{ $ts.middle }}</MkRadio> - <MkRadio v-model="ad.priority" value="low">{{ $ts.low }}</MkRadio> + {{ i18n.ts.priority }} + <MkRadio v-model="ad.priority" value="high">{{ i18n.ts.high }}</MkRadio> + <MkRadio v-model="ad.priority" value="middle">{{ i18n.ts.middle }}</MkRadio> + <MkRadio v-model="ad.priority" value="low">{{ i18n.ts.low }}</MkRadio> </div> --> <FormSplit> <MkInput v-model="ad.ratio" type="number"> - <template #label>{{ $ts.ratio }}</template> + <template #label>{{ i18n.ts.ratio }}</template> </MkInput> <MkInput v-model="ad.expiresAt" type="date"> - <template #label>{{ $ts.expiration }}</template> + <template #label>{{ i18n.ts.expiration }}</template> </MkInput> </FormSplit> <MkTextarea v-model="ad.memo" class="_formBlock"> - <template #label>{{ $ts.memo }}</template> + <template #label>{{ i18n.ts.memo }}</template> </MkTextarea> <div class="buttons _formBlock"> - <MkButton class="button" inline primary style="margin-right: 12px;" @click="save(ad)"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton> - <MkButton class="button" inline danger @click="remove(ad)"><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton> + <MkButton class="button" inline primary style="margin-right: 12px;" @click="save(ad)"><i class="fas fa-save"></i> {{ i18n.ts.save }}</MkButton> + <MkButton class="button" inline danger @click="remove(ad)"><i class="fas fa-trash-alt"></i> {{ i18n.ts.remove }}</MkButton> </div> </div> </div> </MkSpacer> </template> -<script lang="ts"> -import { defineComponent } from 'vue'; +<script lang="ts" setup> +import { } from 'vue'; import MkButton from '@/components/ui/button.vue'; import MkInput from '@/components/form/input.vue'; import MkTextarea from '@/components/form/textarea.vue'; @@ -52,81 +52,65 @@ import FormRadios from '@/components/form/radios.vue'; import FormSplit from '@/components/form/split.vue'; import * as os from '@/os'; import * as symbols from '@/symbols'; +import { i18n } from '@/i18n'; -export default defineComponent({ - components: { - MkButton, - MkInput, - MkTextarea, - FormRadios, - FormSplit, - }, +let ads: any[] = $ref([]); - emits: ['info'], +os.api('admin/ad/list').then(adsResponse => { + ads = adsResponse; +}); - data() { - return { - [symbols.PAGE_INFO]: { - title: this.$ts.ads, - icon: 'fas fa-audio-description', - bg: 'var(--bg)', - actions: [{ - asFullButton: true, - icon: 'fas fa-plus', - text: this.$ts.add, - handler: this.add, - }], - }, - ads: [], - } - }, +function add() { + ads.unshift({ + id: null, + memo: '', + place: 'square', + priority: 'middle', + ratio: 1, + url: '', + imageUrl: null, + expiresAt: null, + }); +} - created() { - os.api('admin/ad/list').then(ads => { - this.ads = ads; +function remove(ad) { + os.confirm({ + type: 'warning', + text: i18n.t('removeAreYouSure', { x: ad.url }), + }).then(({ canceled }) => { + if (canceled) return; + ads = ads.filter(x => x !== ad); + os.apiWithDialog('admin/ad/delete', { + id: ad.id }); - }, - - methods: { - add() { - this.ads.unshift({ - id: null, - memo: '', - place: 'square', - priority: 'middle', - ratio: 1, - url: '', - imageUrl: null, - expiresAt: null, - }); - }, + }); +} - remove(ad) { - os.confirm({ - type: 'warning', - text: this.$t('removeAreYouSure', { x: ad.url }), - }).then(({ canceled }) => { - if (canceled) return; - this.ads = this.ads.filter(x => x != ad); - os.apiWithDialog('admin/ad/delete', { - id: ad.id - }); - }); - }, +function save(ad) { + if (ad.id == null) { + os.apiWithDialog('admin/ad/create', { + ...ad, + expiresAt: new Date(ad.expiresAt).getTime() + }); + } else { + os.apiWithDialog('admin/ad/update', { + ...ad, + expiresAt: new Date(ad.expiresAt).getTime() + }); + } +} - save(ad) { - if (ad.id == null) { - os.apiWithDialog('admin/ad/create', { - ...ad, - expiresAt: new Date(ad.expiresAt).getTime() - }); - } else { - os.apiWithDialog('admin/ad/update', { - ...ad, - expiresAt: new Date(ad.expiresAt).getTime() - }); - } - } +defineExpose({ + [symbols.PAGE_INFO]: { + title: i18n.ts.ads, + icon: 'fas fa-audio-description', + bg: 'var(--bg)', + actions: [{ + asFullButton: true, + icon: 'fas fa-plus', + text: i18n.ts.add, + handler: add, + }], } }); </script> diff --git a/packages/client/src/pages/admin/announcements.vue b/packages/client/src/pages/admin/announcements.vue index a0d720bb29..97774975de 100644 --- a/packages/client/src/pages/admin/announcements.vue +++ b/packages/client/src/pages/admin/announcements.vue @@ -3,112 +3,98 @@ <section v-for="announcement in announcements" class="_card _gap announcements"> <div class="_content announcement"> <MkInput v-model="announcement.title"> - <template #label>{{ $ts.title }}</template> + <template #label>{{ i18n.ts.title }}</template> </MkInput> <MkTextarea v-model="announcement.text"> - <template #label>{{ $ts.text }}</template> + <template #label>{{ i18n.ts.text }}</template> </MkTextarea> <MkInput v-model="announcement.imageUrl"> - <template #label>{{ $ts.imageUrl }}</template> + <template #label>{{ i18n.ts.imageUrl }}</template> </MkInput> - <p v-if="announcement.reads">{{ $t('nUsersRead', { n: announcement.reads }) }}</p> + <p v-if="announcement.reads">{{ i18n.t('nUsersRead', { n: announcement.reads }) }}</p> <div class="buttons"> - <MkButton class="button" inline primary @click="save(announcement)"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton> - <MkButton class="button" inline @click="remove(announcement)"><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton> + <MkButton class="button" inline primary @click="save(announcement)"><i class="fas fa-save"></i> {{ i18n.ts.save }}</MkButton> + <MkButton class="button" inline @click="remove(announcement)"><i class="fas fa-trash-alt"></i> {{ i18n.ts.remove }}</MkButton> </div> </div> </section> </div> </template> -<script lang="ts"> -import { defineComponent } from 'vue'; +<script lang="ts" setup> +import { } from 'vue'; import MkButton from '@/components/ui/button.vue'; import MkInput from '@/components/form/input.vue'; import MkTextarea from '@/components/form/textarea.vue'; import * as os from '@/os'; import * as symbols from '@/symbols'; +import { i18n } from '@/i18n'; -export default defineComponent({ - components: { - MkButton, - MkInput, - MkTextarea, - }, +let announcements: any[] = $ref([]); - emits: ['info'], +os.api('admin/announcements/list').then(announcementResponse => { + announcements = announcementResponse; +}); - data() { - return { - [symbols.PAGE_INFO]: { - title: this.$ts.announcements, - icon: 'fas fa-broadcast-tower', - bg: 'var(--bg)', - actions: [{ - asFullButton: true, - icon: 'fas fa-plus', - text: this.$ts.add, - handler: this.add, - }], - }, - announcements: [], - } - }, +function add() { + announcements.unshift({ + id: null, + title: '', + text: '', + imageUrl: null + }); +} - created() { - os.api('admin/announcements/list').then(announcements => { - this.announcements = announcements; - }); - }, +function remove(announcement) { + os.confirm({ + type: 'warning', + text: i18n.t('removeAreYouSure', { x: announcement.title }), + }).then(({ canceled }) => { + if (canceled) return; + announcements = announcements.filter(x => x !== announcement); + os.api('admin/announcements/delete', announcement); + }); +} - methods: { - add() { - this.announcements.unshift({ - id: null, - title: '', - text: '', - imageUrl: null +function save(announcement) { + if (announcement.id == null) { + os.api('admin/announcements/create', announcement).then(() => { + os.alert({ + type: 'success', + text: i18n.ts.saved }); - }, - - remove(announcement) { - os.confirm({ - type: 'warning', - text: this.$t('removeAreYouSure', { x: announcement.title }), - }).then(({ canceled }) => { - if (canceled) return; - this.announcements = this.announcements.filter(x => x != announcement); - os.api('admin/announcements/delete', announcement); + }).catch(err => { + os.alert({ + type: 'error', + text: err }); - }, + }); + } else { + os.api('admin/announcements/update', announcement).then(() => { + os.alert({ + type: 'success', + text: i18n.ts.saved + }); + }).catch(err => { + os.alert({ + type: 'error', + text: err + }); + }); + } +} - save(announcement) { - if (announcement.id == null) { - os.api('admin/announcements/create', announcement).then(() => { - os.alert({ - type: 'success', - text: this.$ts.saved - }); - }).catch(e => { - os.alert({ - type: 'error', - text: e - }); - }); - } else { - os.api('admin/announcements/update', announcement).then(() => { - os.alert({ - type: 'success', - text: this.$ts.saved - }); - }).catch(e => { - os.alert({ - type: 'error', - text: e - }); - }); - } - } +defineExpose({ + [symbols.PAGE_INFO]: { + title: i18n.ts.announcements, + icon: 'fas fa-broadcast-tower', + bg: 'var(--bg)', + actions: [{ + asFullButton: true, + icon: 'fas fa-plus', + text: i18n.ts.add, + handler: add, + }], } }); </script> diff --git a/packages/client/src/pages/admin/bot-protection.vue b/packages/client/src/pages/admin/bot-protection.vue index 5e0cdd96a5..30fee5015a 100644 --- a/packages/client/src/pages/admin/bot-protection.vue +++ b/packages/client/src/pages/admin/bot-protection.vue @@ -43,8 +43,8 @@ </div> </template> -<script lang="ts"> -import { defineAsyncComponent, defineComponent } from 'vue'; +<script lang="ts" setup> +import { defineAsyncComponent } from 'vue'; import FormRadios from '@/components/form/radios.vue'; import FormInput from '@/components/form/input.vue'; import FormButton from '@/components/ui/button.vue'; @@ -54,64 +54,39 @@ import * as os from '@/os'; import * as symbols from '@/symbols'; import { fetchInstance } from '@/instance'; -export default defineComponent({ - components: { - FormRadios, - FormInput, - FormButton, - FormSuspense, - FormSlot, - MkCaptcha: defineAsyncComponent(() => import('@/components/captcha.vue')), - }, +const MkCaptcha = defineAsyncComponent(() => import('@/components/captcha.vue')); - emits: ['info'], +let provider = $ref(null); +let hcaptchaSiteKey: string | null = $ref(null); +let hcaptchaSecretKey: string | null = $ref(null); +let recaptchaSiteKey: string | null = $ref(null); +let recaptchaSecretKey: string | null = $ref(null); - data() { - return { - [symbols.PAGE_INFO]: { - title: this.$ts.botProtection, - icon: 'fas fa-shield-alt' - }, - provider: null, - enableHcaptcha: false, - hcaptchaSiteKey: null, - hcaptchaSecretKey: null, - enableRecaptcha: false, - recaptchaSiteKey: null, - recaptchaSecretKey: null, - } - }, +const enableHcaptcha = $computed(() => provider === 'hcaptcha'); +const enableRecaptcha = $computed(() => provider === 'recaptcha'); - methods: { - async init() { - const meta = await os.api('admin/meta'); - this.enableHcaptcha = meta.enableHcaptcha; - this.hcaptchaSiteKey = meta.hcaptchaSiteKey; - this.hcaptchaSecretKey = meta.hcaptchaSecretKey; - this.enableRecaptcha = meta.enableRecaptcha; - this.recaptchaSiteKey = meta.recaptchaSiteKey; - this.recaptchaSecretKey = meta.recaptchaSecretKey; +async function init() { + const meta = await os.api('admin/meta'); + enableHcaptcha = meta.enableHcaptcha; + hcaptchaSiteKey = meta.hcaptchaSiteKey; + hcaptchaSecretKey = meta.hcaptchaSecretKey; + enableRecaptcha = meta.enableRecaptcha; + recaptchaSiteKey = meta.recaptchaSiteKey; + recaptchaSecretKey = meta.recaptchaSecretKey; - this.provider = this.enableHcaptcha ? 'hcaptcha' : this.enableRecaptcha ? 'recaptcha' : null; + provider = enableHcaptcha ? 'hcaptcha' : enableRecaptcha ? 'recaptcha' : null; +} - this.$watch(() => this.provider, () => { - this.enableHcaptcha = this.provider === 'hcaptcha'; - this.enableRecaptcha = this.provider === 'recaptcha'; - }); - }, - - save() { - os.apiWithDialog('admin/update-meta', { - enableHcaptcha: this.enableHcaptcha, - hcaptchaSiteKey: this.hcaptchaSiteKey, - hcaptchaSecretKey: this.hcaptchaSecretKey, - enableRecaptcha: this.enableRecaptcha, - recaptchaSiteKey: this.recaptchaSiteKey, - recaptchaSecretKey: this.recaptchaSecretKey, - }).then(() => { - fetchInstance(); - }); - } - } -}); +function save() { + os.apiWithDialog('admin/update-meta', { + enableHcaptcha, + hcaptchaSiteKey, + hcaptchaSecretKey, + enableRecaptcha, + recaptchaSiteKey, + recaptchaSecretKey, + }).then(() => { + fetchInstance(); + }); +} </script> diff --git a/packages/client/src/pages/admin/database.vue b/packages/client/src/pages/admin/database.vue index 3a835eeafa..d3519922b1 100644 --- a/packages/client/src/pages/admin/database.vue +++ b/packages/client/src/pages/admin/database.vue @@ -9,36 +9,23 @@ </MkSpacer> </template> -<script lang="ts"> -import { defineComponent } from 'vue'; +<script lang="ts" setup> +import { } from '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'; import number from '@/filters/number'; +import { i18n } from '@/i18n'; -export default defineComponent({ - components: { - FormSuspense, - MkKeyValue, - }, +const databasePromiseFactory = () => os.api('admin/get-table-stats').then(res => Object.entries(res).sort((a, b) => b[1].size - a[1].size)); - emits: ['info'], - - data() { - return { - [symbols.PAGE_INFO]: { - title: this.$ts.database, - icon: 'fas fa-database', - bg: 'var(--bg)', - }, - databasePromiseFactory: () => os.api('admin/get-table-stats', {}).then(res => Object.entries(res).sort((a, b) => b[1].size - a[1].size)), - } - }, - - methods: { - bytes, number, +defineExpose({ + [symbols.PAGE_INFO]: { + title: i18n.ts.database, + icon: 'fas fa-database', + bg: 'var(--bg)', } }); </script> diff --git a/packages/client/src/pages/admin/email-settings.vue b/packages/client/src/pages/admin/email-settings.vue index 7df0b6db1c..aa13043193 100644 --- a/packages/client/src/pages/admin/email-settings.vue +++ b/packages/client/src/pages/admin/email-settings.vue @@ -3,37 +3,37 @@ <FormSuspense :p="init"> <div class="_formRoot"> <FormSwitch v-model="enableEmail" class="_formBlock"> - <template #label>{{ $ts.enableEmail }}</template> - <template #caption>{{ $ts.emailConfigInfo }}</template> + <template #label>{{ i18n.ts.enableEmail }}</template> + <template #caption>{{ i18n.ts.emailConfigInfo }}</template> </FormSwitch> <template v-if="enableEmail"> <FormInput v-model="email" type="email" class="_formBlock"> - <template #label>{{ $ts.emailAddress }}</template> + <template #label>{{ i18n.ts.emailAddress }}</template> </FormInput> <FormSection> - <template #label>{{ $ts.smtpConfig }}</template> + <template #label>{{ i18n.ts.smtpConfig }}</template> <FormSplit :min-width="280"> <FormInput v-model="smtpHost" class="_formBlock"> - <template #label>{{ $ts.smtpHost }}</template> + <template #label>{{ i18n.ts.smtpHost }}</template> </FormInput> <FormInput v-model="smtpPort" type="number" class="_formBlock"> - <template #label>{{ $ts.smtpPort }}</template> + <template #label>{{ i18n.ts.smtpPort }}</template> </FormInput> </FormSplit> <FormSplit :min-width="280"> <FormInput v-model="smtpUser" class="_formBlock"> - <template #label>{{ $ts.smtpUser }}</template> + <template #label>{{ i18n.ts.smtpUser }}</template> </FormInput> <FormInput v-model="smtpPass" type="password" class="_formBlock"> - <template #label>{{ $ts.smtpPass }}</template> + <template #label>{{ i18n.ts.smtpPass }}</template> </FormInput> </FormSplit> - <FormInfo class="_formBlock">{{ $ts.emptyToDisableSmtpAuth }}</FormInfo> + <FormInfo class="_formBlock">{{ i18n.ts.emptyToDisableSmtpAuth }}</FormInfo> <FormSwitch v-model="smtpSecure" class="_formBlock"> - <template #label>{{ $ts.smtpSecure }}</template> - <template #caption>{{ $ts.smtpSecureInfo }}</template> + <template #label>{{ i18n.ts.smtpSecure }}</template> + <template #caption>{{ i18n.ts.smtpSecureInfo }}</template> </FormSwitch> </FormSection> </template> @@ -42,8 +42,8 @@ </MkSpacer> </template> -<script lang="ts"> -import { defineComponent } from 'vue'; +<script lang="ts" setup> +import { } from 'vue'; import FormSwitch from '@/components/form/switch.vue'; import FormInput from '@/components/form/input.vue'; import FormInfo from '@/components/ui/info.vue'; @@ -52,86 +52,71 @@ 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'; +import { fetchInstance, instance } from '@/instance'; +import { i18n } from '@/i18n'; -export default defineComponent({ - components: { - FormSwitch, - FormInput, - FormSplit, - FormSection, - FormInfo, - FormSuspense, - }, +let enableEmail: boolean = $ref(false); +let email: any = $ref(null); +let smtpSecure: boolean = $ref(false); +let smtpHost: string = $ref(''); +let smtpPort: number = $ref(0); +let smtpUser: string = $ref(''); +let smtpPass: string = $ref(''); - emits: ['info'], +async function init() { + const meta = await os.api('admin/meta'); + enableEmail = meta.enableEmail; + email = meta.email; + smtpSecure = meta.smtpSecure; + smtpHost = meta.smtpHost; + smtpPort = meta.smtpPort; + smtpUser = meta.smtpUser; + smtpPass = meta.smtpPass; +} - data() { - return { - [symbols.PAGE_INFO]: { - 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, - smtpSecure: false, - smtpHost: '', - smtpPort: 0, - smtpUser: '', - smtpPass: '', - } - }, +async function testEmail() { + const { canceled, result: destination } = await os.inputText({ + title: i18n.ts.destination, + type: 'email', + placeholder: instance.maintainerEmail + }); + if (canceled) return; + os.apiWithDialog('admin/send-email', { + to: destination, + subject: 'Test email', + text: 'Yo' + }); +} - methods: { - async init() { - const meta = await os.api('admin/meta'); - this.enableEmail = meta.enableEmail; - this.email = meta.email; - this.smtpSecure = meta.smtpSecure; - this.smtpHost = meta.smtpHost; - this.smtpPort = meta.smtpPort; - this.smtpUser = meta.smtpUser; - this.smtpPass = meta.smtpPass; - }, +function save() { + os.apiWithDialog('admin/update-meta', { + enableEmail, + email, + smtpSecure, + smtpHost, + smtpPort, + smtpUser, + smtpPass, + }).then(() => { + fetchInstance(); + }); +} - async testEmail() { - const { canceled, result: destination } = await os.inputText({ - title: this.$ts.destination, - type: 'email', - placeholder: this.$instance.maintainerEmail - }); - if (canceled) return; - os.apiWithDialog('admin/send-email', { - to: destination, - subject: 'Test email', - text: 'Yo' - }); - }, - - save() { - os.apiWithDialog('admin/update-meta', { - enableEmail: this.enableEmail, - email: this.email, - smtpSecure: this.smtpSecure, - smtpHost: this.smtpHost, - smtpPort: this.smtpPort, - smtpUser: this.smtpUser, - smtpPass: this.smtpPass, - }).then(() => { - fetchInstance(); - }); - } +defineExpose({ + [symbols.PAGE_INFO]: { + title: i18n.ts.emailServer, + icon: 'fas fa-envelope', + bg: 'var(--bg)', + actions: [{ + asFullButton: true, + text: i18n.ts.testEmail, + handler: testEmail, + }, { + asFullButton: true, + icon: 'fas fa-check', + text: i18n.ts.save, + handler: save, + }], } }); </script> diff --git a/packages/client/src/pages/admin/emoji-edit-dialog.vue b/packages/client/src/pages/admin/emoji-edit-dialog.vue index 2e3903426e..d482fa49e6 100644 --- a/packages/client/src/pages/admin/emoji-edit-dialog.vue +++ b/packages/client/src/pages/admin/emoji-edit-dialog.vue @@ -27,85 +27,71 @@ </XModalWindow> </template> -<script lang="ts"> -import { defineComponent } from 'vue'; +<script lang="ts" setup> +import { } from 'vue'; import XModalWindow from '@/components/ui/modal-window.vue'; import MkButton from '@/components/ui/button.vue'; import MkInput from '@/components/form/input.vue'; import * as os from '@/os'; import { unique } from '@/scripts/array'; +import { i18n } from '@/i18n'; +import { emojiCategories } from '@/instance'; -export default defineComponent({ - components: { - XModalWindow, - MkButton, - MkInput, - }, +const props = defineProps<{ + emoji: any, +}>(); - props: { - emoji: { - required: true, - } - }, - - emits: ['done', 'closed'], +let dialog = $ref(null); +let name: string = $ref(props.emoji.name); +let category: string = $ref(props.emoji.category); +let aliases: string = $ref(props.emoji.aliases.join(' ')); +let categories: string[] = $ref(emojiCategories); - data() { - return { - name: this.emoji.name, - category: this.emoji.category, - aliases: this.emoji.aliases?.join(' '), - categories: [], - } - }, +const emit = defineEmits<{ + (ev: 'done', v: { deleted?: boolean, updated?: any }): void, + (ev: 'closed'): void +}>(); - created() { - os.api('meta', { detail: false }).then(({ emojis }) => { - this.categories = unique(emojis.map((x: any) => x.category || '').filter((x: string) => x !== '')); - }); - }, +function ok() { + update(); +} - methods: { - ok() { - this.update(); - }, +async function update() { + await os.apiWithDialog('admin/emoji/update', { + id: props.emoji.id, + name, + category, + aliases: aliases.split(' '), + }); - async update() { - await os.apiWithDialog('admin/emoji/update', { - id: this.emoji.id, - name: this.name, - category: this.category, - aliases: this.aliases.split(' '), - }); + emit('done', { + updated: { + id: props.emoji.id, + name, + category, + aliases: aliases.split(' '), + } + }); - this.$emit('done', { - updated: { - name: this.name, - category: this.category, - aliases: this.aliases.split(' '), - } - }); - this.$refs.dialog.close(); - }, + dialog.close(); +} - async del() { - const { canceled } = await os.confirm({ - type: 'warning', - text: this.$t('removeAreYouSure', { x: this.emoji.name }), - }); - if (canceled) return; +async function del() { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.t('removeAreYouSure', { x: name }), + }); + if (canceled) return; - os.api('admin/emoji/delete', { - id: this.emoji.id - }).then(() => { - this.$emit('done', { - deleted: true - }); - this.$refs.dialog.close(); - }); - }, - } -}); + os.api('admin/emoji/delete', { + id: props.emoji.id + }).then(() => { + emit('done', { + deleted: true + }); + dialog.close(); + }); +} </script> <style lang="scss" scoped> diff --git a/packages/client/src/pages/admin/emojis.vue b/packages/client/src/pages/admin/emojis.vue index a080ee9c23..8ca5b3d65c 100644 --- a/packages/client/src/pages/admin/emojis.vue +++ b/packages/client/src/pages/admin/emojis.vue @@ -63,7 +63,7 @@ </template> <script lang="ts" setup> -import { computed, defineComponent, ref, toRef } from 'vue'; +import { computed, defineAsyncComponent, 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'; @@ -130,17 +130,17 @@ const add = async (ev: MouseEvent) => { }; const edit = (emoji) => { - os.popup(import('./emoji-edit-dialog.vue'), { + os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), { emoji: emoji }, { done: result => { if (result.updated) { - emojisPaginationComponent.value.replaceItem(item => item.id === emoji.id, { - ...emoji, + emojisPaginationComponent.value.updateItem(result.updated.id, (oldEmoji: any) => ({ + ...oldEmoji, ...result.updated - }); + })); } else if (result.deleted) { - emojisPaginationComponent.value.removeItem(item => item.id === emoji.id); + emojisPaginationComponent.value.removeItem((item) => item.id === emoji.id); } }, }, 'closed'); @@ -159,7 +159,7 @@ const remoteMenu = (emoji, ev: MouseEvent) => { }, { text: i18n.ts.import, icon: 'fas fa-plus', - action: () => { im(emoji) } + action: () => { im(emoji); } }], ev.currentTarget ?? ev.target); }; @@ -175,10 +175,10 @@ const menu = (ev: MouseEvent) => { type: 'info', text: i18n.ts.exportRequested, }); - }).catch((e) => { + }).catch((err) => { os.alert({ type: 'error', - text: e.message, + text: err.message, }); }); } @@ -195,10 +195,10 @@ const menu = (ev: MouseEvent) => { type: 'info', text: i18n.ts.importRequested, }); - }).catch((e) => { + }).catch((err) => { os.alert({ type: 'error', - text: e.message, + text: err.message, }); }); } diff --git a/packages/client/src/pages/admin/file-dialog.vue b/packages/client/src/pages/admin/file-dialog.vue index 4c33f62399..0765548aab 100644 --- a/packages/client/src/pages/admin/file-dialog.vue +++ b/packages/client/src/pages/admin/file-dialog.vue @@ -34,74 +34,52 @@ </XModalWindow> </template> -<script lang="ts"> -import { computed, defineComponent } from 'vue'; +<script lang="ts" setup> +import { } from 'vue'; import MkButton from '@/components/ui/button.vue'; import MkSwitch from '@/components/form/switch.vue'; import XModalWindow from '@/components/ui/modal-window.vue'; import MkDriveFileThumbnail from '@/components/drive-file-thumbnail.vue'; import bytes from '@/filters/bytes'; import * as os from '@/os'; +import { i18n } from '@/i18n'; -export default defineComponent({ - components: { - MkButton, - MkSwitch, - XModalWindow, - MkDriveFileThumbnail, - }, +let file: any = $ref(null); +let info: any = $ref(null); +let isSensitive: boolean = $ref(false); - props: { - fileId: { - required: true, - } - }, - - emits: ['closed'], - - data() { - return { - file: null, - info: null, - isSensitive: false, - }; - }, +const props = defineProps<{ + fileId: string, +}>(); - created() { - this.fetch(); - }, - - methods: { - async fetch() { - this.file = await os.api('drive/files/show', { fileId: this.fileId }); - this.info = await os.api('admin/drive/show-file', { fileId: this.fileId }); - this.isSensitive = this.file.isSensitive; - }, +async function fetch() { + file = await os.api('drive/files/show', { fileId: props.fileId }); + info = await os.api('admin/drive/show-file', { fileId: props.fileId }); + isSensitive = file.isSensitive; +} - showUser() { - os.pageWindow(`/user-info/${this.file.userId}`); - }, +fetch(); - async del() { - const { canceled } = await os.confirm({ - type: 'warning', - text: this.$t('removeAreYouSure', { x: this.file.name }), - }); - if (canceled) return; +function showUser() { + os.pageWindow(`/user-info/${file.userId}`); +} - os.apiWithDialog('drive/files/delete', { - fileId: this.file.id - }); - }, +async function del() { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.t('removeAreYouSure', { x: file.name }), + }); + if (canceled) return; - async toggleIsSensitive(v) { - await os.api('drive/files/update', { fileId: this.fileId, isSensitive: v }); - this.isSensitive = v; - }, + os.apiWithDialog('drive/files/delete', { + fileId: file.id + }); +} - bytes - } -}); +async function toggleIsSensitive(v) { + await os.api('drive/files/update', { fileId: props.fileId, isSensitive: v }); + isSensitive = v; +} </script> <style lang="scss" scoped> diff --git a/packages/client/src/pages/admin/files.vue b/packages/client/src/pages/admin/files.vue index c62f053092..3cda688698 100644 --- a/packages/client/src/pages/admin/files.vue +++ b/packages/client/src/pages/admin/files.vue @@ -55,7 +55,7 @@ </template> <script lang="ts" setup> -import { computed } from 'vue'; +import { computed, defineAsyncComponent } from 'vue'; import MkButton from '@/components/ui/button.vue'; import MkInput from '@/components/form/input.vue'; import MkSelect from '@/components/form/select.vue'; @@ -93,7 +93,7 @@ function clear() { } function show(file) { - os.popup(import('./file-dialog.vue'), { + os.popup(defineAsyncComponent(() => import('./file-dialog.vue')), { fileId: file.id }, {}, 'closed'); } diff --git a/packages/client/src/pages/admin/index.vue b/packages/client/src/pages/admin/index.vue index 6b11650f48..9b7fa5678e 100644 --- a/packages/client/src/pages/admin/index.vue +++ b/packages/client/src/pages/admin/index.vue @@ -1,6 +1,6 @@ <template> <div ref="el" class="hiyeyicy" :class="{ wide: !narrow }"> - <div v-if="!narrow || page == null" class="nav"> + <div v-if="!narrow || initialPage == null" class="nav"> <MkHeader :info="header"></MkHeader> <MkSpacer :content-max="700" :margin-min="16"> @@ -12,21 +12,21 @@ <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/security" class="_link">{{ $ts.configure }}</MkA></MkInfo> - <MkSuperMenu :def="menuDef" :grid="page == null"></MkSuperMenu> + <MkSuperMenu :def="menuDef" :grid="initialPage == null"></MkSuperMenu> </div> </MkSpacer> </div> - <div class="main"> + <div v-if="!(narrow && initialPage == null)" class="main"> <MkStickyContainer> <template #header><MkHeader v-if="childInfo && !childInfo.hideHeader" :info="childInfo"/></template> - <component :is="component" :ref="el => pageChanged(el)" :key="page" v-bind="pageProps"/> + <component :is="component" :ref="el => pageChanged(el)" :key="initialPage" v-bind="pageProps"/> </MkStickyContainer> </div> </div> </template> -<script lang="ts"> -import { computed, defineAsyncComponent, defineComponent, isRef, nextTick, onMounted, reactive, ref, watch } from 'vue'; +<script lang="ts" setup> +import { defineAsyncComponent, nextTick, onMounted, onUnmounted, provide, watch } from 'vue'; import { i18n } from '@/i18n'; import MkSuperMenu from '@/components/ui/super-menu.vue'; import MkInfo from '@/components/ui/info.vue'; @@ -35,292 +35,277 @@ import { instance } from '@/instance'; import * as symbols from '@/symbols'; import * as os from '@/os'; import { lookupUser } from '@/scripts/lookup-user'; +import { MisskeyNavigator } from '@/scripts/navigate'; -export default defineComponent({ - components: { - MkSuperMenu, - MkInfo, - }, +const isEmpty = (x: string | null) => x == null || x === ''; - provide: { - shouldOmitHeaderTitle: false, - }, +const nav = new MisskeyNavigator(); - props: { - initialPage: { - type: String, - required: false - } - }, +const indexInfo = { + title: i18n.ts.controlPanel, + icon: 'fas fa-cog', + bg: 'var(--bg)', + hideHeader: true, +}; - setup(props, context) { - const indexInfo = { - title: i18n.ts.controlPanel, - icon: 'fas fa-cog', - bg: 'var(--bg)', - hideHeader: true, - }; - const INFO = ref(indexInfo); - const childInfo = ref(null); - const page = ref(props.initialPage); - const narrow = ref(false); - const view = ref(null); - const el = ref(null); - const pageChanged = (page) => { - if (page == null) return; - const viewInfo = page[symbols.PAGE_INFO]; - if (isRef(viewInfo)) { - watch(viewInfo, () => { - childInfo.value = viewInfo.value; - }, { immediate: true }); - } else { - childInfo.value = viewInfo; - } - }; - const pageProps = ref({}); +const props = defineProps<{ + initialPage?: string, +}>(); - const isEmpty = (x: any) => x == null || x == ''; +provide('shouldOmitHeaderTitle', false); - const noMaintainerInformation = ref(false); - const noBotProtection = ref(false); +let INFO = $ref(indexInfo); +let childInfo = $ref(null); +let page = $ref(props.initialPage); +let narrow = $ref(false); +let view = $ref(null); +let el = $ref(null); +let pageProps = $ref({}); +let noMaintainerInformation = isEmpty(instance.maintainerName) || isEmpty(instance.maintainerEmail); +let noBotProtection = !instance.enableHcaptcha && !instance.enableRecaptcha; - os.api('meta', { detail: true }).then(meta => { - // TODO: 設定が完了しても残ったままになるので、ストリーミングでmeta更新イベントを受け取ってよしなに更新する - noMaintainerInformation.value = isEmpty(meta.maintainerName) || isEmpty(meta.maintainerEmail); - noBotProtection.value = !meta.enableHcaptcha && !meta.enableRecaptcha; - }); +const NARROW_THRESHOLD = 600; +const ro = new ResizeObserver((entries, observer) => { + if (entries.length === 0) return; + narrow = entries[0].borderBoxSize[0].inlineSize < NARROW_THRESHOLD; +}); - const menuDef = computed(() => [{ - title: i18n.ts.quickAction, - items: [{ - type: 'button', - icon: 'fas fa-search', - text: i18n.ts.lookup, - action: lookup, - }, ...(instance.disableRegistration ? [{ - type: 'button', - icon: 'fas fa-user', - text: i18n.ts.invite, - action: invite, - }] : [])], - }, { - title: i18n.ts.administration, - items: [{ - icon: 'fas fa-tachometer-alt', - text: i18n.ts.dashboard, - to: '/admin/overview', - active: page.value === 'overview', - }, { - icon: 'fas fa-users', - text: i18n.ts.users, - to: '/admin/users', - active: page.value === 'users', - }, { - icon: 'fas fa-laugh', - text: i18n.ts.customEmojis, - to: '/admin/emojis', - active: page.value === 'emojis', - }, { - icon: 'fas fa-globe', - text: i18n.ts.federation, - to: '/admin/federation', - active: page.value === 'federation', - }, { - icon: 'fas fa-clipboard-list', - text: i18n.ts.jobQueue, - to: '/admin/queue', - active: page.value === 'queue', - }, { - icon: 'fas fa-cloud', - text: i18n.ts.files, - to: '/admin/files', - active: page.value === 'files', - }, { - icon: 'fas fa-broadcast-tower', - text: i18n.ts.announcements, - to: '/admin/announcements', - active: page.value === 'announcements', - }, { - icon: 'fas fa-audio-description', - text: i18n.ts.ads, - to: '/admin/ads', - active: page.value === 'ads', - }, { - icon: 'fas fa-exclamation-circle', - text: i18n.ts.abuseReports, - to: '/admin/abuses', - active: page.value === 'abuses', - }], - }, { - title: i18n.ts.settings, - items: [{ - icon: 'fas fa-cog', - text: i18n.ts.general, - to: '/admin/settings', - active: page.value === 'settings', - }, { - icon: 'fas fa-envelope', - text: i18n.ts.emailServer, - to: '/admin/email-settings', - active: page.value === 'email-settings', - }, { - icon: 'fas fa-cloud', - text: i18n.ts.objectStorage, - to: '/admin/object-storage', - active: page.value === 'object-storage', - }, { - icon: 'fas fa-lock', - text: i18n.ts.security, - to: '/admin/security', - active: page.value === 'security', - }, { - icon: 'fas fa-globe', - text: i18n.ts.relays, - to: '/admin/relays', - active: page.value === 'relays', - }, { - icon: 'fas fa-share-alt', - text: i18n.ts.integration, - to: '/admin/integrations', - active: page.value === 'integrations', - }, { - icon: 'fas fa-ban', - text: i18n.ts.instanceBlocking, - to: '/admin/instance-block', - active: page.value === 'instance-block', - }, { - icon: 'fas fa-ghost', - text: i18n.ts.proxyAccount, - to: '/admin/proxy-account', - active: page.value === 'proxy-account', - }, { - icon: 'fas fa-cogs', - text: i18n.ts.other, - to: '/admin/other-settings', - active: page.value === 'other-settings', - }], - }, { - title: i18n.ts.info, - items: [{ - icon: 'fas fa-database', - text: i18n.ts.database, - to: '/admin/database', - active: page.value === 'database', - }], - }]); - const component = computed(() => { - if (page.value == null) return null; - switch (page.value) { - case 'overview': return defineAsyncComponent(() => import('./overview.vue')); - case 'users': return defineAsyncComponent(() => import('./users.vue')); - case 'emojis': return defineAsyncComponent(() => import('./emojis.vue')); - case 'federation': return defineAsyncComponent(() => import('../federation.vue')); - case 'queue': return defineAsyncComponent(() => import('./queue.vue')); - case 'files': return defineAsyncComponent(() => import('./files.vue')); - case 'announcements': return defineAsyncComponent(() => import('./announcements.vue')); - case 'ads': return defineAsyncComponent(() => import('./ads.vue')); - case 'database': return defineAsyncComponent(() => import('./database.vue')); - case 'abuses': return defineAsyncComponent(() => import('./abuses.vue')); - case 'settings': return defineAsyncComponent(() => import('./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 'relays': return defineAsyncComponent(() => import('./relays.vue')); - case 'integrations': return defineAsyncComponent(() => import('./integrations.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')); - } - }); +const menuDef = $computed(() => [{ + title: i18n.ts.quickAction, + items: [{ + type: 'button', + icon: 'fas fa-search', + text: i18n.ts.lookup, + action: lookup, + }, ...(instance.disableRegistration ? [{ + type: 'button', + icon: 'fas fa-user', + text: i18n.ts.invite, + action: invite, + }] : [])], +}, { + title: i18n.ts.administration, + items: [{ + icon: 'fas fa-tachometer-alt', + text: i18n.ts.dashboard, + to: '/admin/overview', + active: props.initialPage === 'overview', + }, { + icon: 'fas fa-users', + text: i18n.ts.users, + to: '/admin/users', + active: props.initialPage === 'users', + }, { + icon: 'fas fa-laugh', + text: i18n.ts.customEmojis, + to: '/admin/emojis', + active: props.initialPage === 'emojis', + }, { + icon: 'fas fa-globe', + text: i18n.ts.federation, + to: '/admin/federation', + active: props.initialPage === 'federation', + }, { + icon: 'fas fa-clipboard-list', + text: i18n.ts.jobQueue, + to: '/admin/queue', + active: props.initialPage === 'queue', + }, { + icon: 'fas fa-cloud', + text: i18n.ts.files, + to: '/admin/files', + active: props.initialPage === 'files', + }, { + icon: 'fas fa-broadcast-tower', + text: i18n.ts.announcements, + to: '/admin/announcements', + active: props.initialPage === 'announcements', + }, { + icon: 'fas fa-audio-description', + text: i18n.ts.ads, + to: '/admin/ads', + active: props.initialPage === 'ads', + }, { + icon: 'fas fa-exclamation-circle', + text: i18n.ts.abuseReports, + to: '/admin/abuses', + active: props.initialPage === 'abuses', + }], +}, { + title: i18n.ts.settings, + items: [{ + icon: 'fas fa-cog', + text: i18n.ts.general, + to: '/admin/settings', + active: props.initialPage === 'settings', + }, { + icon: 'fas fa-envelope', + text: i18n.ts.emailServer, + to: '/admin/email-settings', + active: props.initialPage === 'email-settings', + }, { + icon: 'fas fa-cloud', + text: i18n.ts.objectStorage, + to: '/admin/object-storage', + active: props.initialPage === 'object-storage', + }, { + icon: 'fas fa-lock', + text: i18n.ts.security, + to: '/admin/security', + active: props.initialPage === 'security', + }, { + icon: 'fas fa-globe', + text: i18n.ts.relays, + to: '/admin/relays', + active: props.initialPage === 'relays', + }, { + icon: 'fas fa-share-alt', + text: i18n.ts.integration, + to: '/admin/integrations', + active: props.initialPage === 'integrations', + }, { + icon: 'fas fa-ban', + text: i18n.ts.instanceBlocking, + to: '/admin/instance-block', + active: props.initialPage === 'instance-block', + }, { + icon: 'fas fa-ghost', + text: i18n.ts.proxyAccount, + to: '/admin/proxy-account', + active: props.initialPage === 'proxy-account', + }, { + icon: 'fas fa-cogs', + text: i18n.ts.other, + to: '/admin/other-settings', + active: props.initialPage === 'other-settings', + }], +}, { + title: i18n.ts.info, + items: [{ + icon: 'fas fa-database', + text: i18n.ts.database, + to: '/admin/database', + active: props.initialPage === 'database', + }], +}]); - watch(component, () => { - pageProps.value = {}; +const component = $computed(() => { + if (props.initialPage == null) return null; + switch (props.initialPage) { + case 'overview': return defineAsyncComponent(() => import('./overview.vue')); + case 'users': return defineAsyncComponent(() => import('./users.vue')); + case 'emojis': return defineAsyncComponent(() => import('./emojis.vue')); + case 'federation': return defineAsyncComponent(() => import('../federation.vue')); + case 'queue': return defineAsyncComponent(() => import('./queue.vue')); + case 'files': return defineAsyncComponent(() => import('./files.vue')); + case 'announcements': return defineAsyncComponent(() => import('./announcements.vue')); + case 'ads': return defineAsyncComponent(() => import('./ads.vue')); + case 'database': return defineAsyncComponent(() => import('./database.vue')); + case 'abuses': return defineAsyncComponent(() => import('./abuses.vue')); + case 'settings': return defineAsyncComponent(() => import('./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 'relays': return defineAsyncComponent(() => import('./relays.vue')); + case 'integrations': return defineAsyncComponent(() => import('./integrations.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')); + } +}); - nextTick(() => { - scroll(el.value, { top: 0 }); - }); - }, { immediate: true }); +watch(component, () => { + pageProps = {}; - watch(() => props.initialPage, () => { - if (props.initialPage == null && !narrow.value) { - page.value = 'overview'; - } else { - page.value = props.initialPage; - if (props.initialPage == null) { - INFO.value = indexInfo; - } - } - }); + nextTick(() => { + scroll(el, { top: 0 }); + }); +}, { immediate: true }); - onMounted(() => { - narrow.value = el.value.offsetWidth < 800; - if (!narrow.value) { - page.value = 'overview'; - } - }); +watch(() => props.initialPage, () => { + if (props.initialPage == null && !narrow) { + nav.push('/admin/overview'); + } else { + if (props.initialPage == null) { + INFO = indexInfo; + } + } +}); - const invite = () => { - os.api('admin/invite').then(x => { - os.alert({ - type: 'info', - text: x.code - }); - }).catch(e => { - os.alert({ - type: 'error', - text: e - }); - }); - }; +watch(narrow, () => { + if (props.initialPage == null && !narrow) { + nav.push('/admin/overview'); + } +}); - const lookup = (ev) => { - os.popupMenu([{ - text: i18n.ts.user, - icon: 'fas fa-user', - action: () => { - lookupUser(); - } - }, { - text: i18n.ts.note, - icon: 'fas fa-pencil-alt', - action: () => { - alert('TODO'); - } - }, { - text: i18n.ts.file, - icon: 'fas fa-cloud', - action: () => { - alert('TODO'); - } - }, { - text: i18n.ts.instance, - icon: 'fas fa-globe', - action: () => { - alert('TODO'); - } - }], ev.currentTarget ?? ev.target); - }; +onMounted(() => { + ro.observe(el); + + narrow = el.offsetWidth < NARROW_THRESHOLD; + if (props.initialPage == null && !narrow) { + nav.push('/admin/overview'); + } +}); + +onUnmounted(() => { + ro.disconnect(); +}); + +const pageChanged = (page) => { + if (page == null) { + childInfo = null; + } else { + childInfo = page[symbols.PAGE_INFO]; + } +}; - return { - [symbols.PAGE_INFO]: INFO, - menuDef, - header: { - title: i18n.ts.controlPanel, - }, - noMaintainerInformation, - noBotProtection, - page, - narrow, - view, - el, - pageChanged, - childInfo, - pageProps, - component, - invite, - lookup, - }; - }, +const invite = () => { + os.api('admin/invite').then(x => { + os.alert({ + type: 'info', + text: x.code + }); + }).catch(err => { + os.alert({ + type: 'error', + text: err, + }); + }); +}; + +const lookup = (ev) => { + os.popupMenu([{ + text: i18n.ts.user, + icon: 'fas fa-user', + action: () => { + lookupUser(); + } + }, { + text: i18n.ts.note, + icon: 'fas fa-pencil-alt', + action: () => { + alert('TODO'); + } + }, { + text: i18n.ts.file, + icon: 'fas fa-cloud', + action: () => { + alert('TODO'); + } + }, { + text: i18n.ts.instance, + icon: 'fas fa-globe', + action: () => { + alert('TODO'); + } + }], ev.currentTarget ?? ev.target); +}; + +defineExpose({ + [symbols.PAGE_INFO]: INFO, + header: { + title: i18n.ts.controlPanel, + } }); </script> diff --git a/packages/client/src/pages/admin/instance-block.vue b/packages/client/src/pages/admin/instance-block.vue index 4cb8dc604e..3347846a80 100644 --- a/packages/client/src/pages/admin/instance-block.vue +++ b/packages/client/src/pages/admin/instance-block.vue @@ -2,57 +2,45 @@ <MkSpacer :content-max="700" :margin-min="16" :margin-max="32"> <FormSuspense :p="init"> <FormTextarea v-model="blockedHosts" class="_formBlock"> - <span>{{ $ts.blockedInstances }}</span> - <template #caption>{{ $ts.blockedInstancesDescription }}</template> + <span>{{ i18n.ts.blockedInstances }}</span> + <template #caption>{{ i18n.ts.blockedInstancesDescription }}</template> </FormTextarea> - <FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> + <FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ i18n.ts.save }}</FormButton> </FormSuspense> </MkSpacer> </template> -<script lang="ts"> -import { defineComponent } from 'vue'; +<script lang="ts" setup> +import { } from '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'; +import { i18n } from '@/i18n'; -export default defineComponent({ - components: { - FormButton, - FormTextarea, - FormSuspense, - }, +let blockedHosts: string = $ref(''); - emits: ['info'], +async function init() { + const meta = await os.api('admin/meta'); + blockedHosts = meta.blockedHosts.join('\n'); +} - data() { - return { - [symbols.PAGE_INFO]: { - title: this.$ts.instanceBlocking, - icon: 'fas fa-ban', - bg: 'var(--bg)', - }, - blockedHosts: '', - } - }, +function save() { + os.apiWithDialog('admin/update-meta', { + blockedHosts: blockedHosts.split('\n') || [], + }).then(() => { + fetchInstance(); + }); +} - methods: { - async init() { - const meta = await os.api('admin/meta'); - this.blockedHosts = meta.blockedHosts.join('\n'); - }, - - save() { - os.apiWithDialog('admin/update-meta', { - blockedHosts: this.blockedHosts.split('\n') || [], - }).then(() => { - fetchInstance(); - }); - } +defineExpose({ + [symbols.PAGE_INFO]: { + title: i18n.ts.instanceBlocking, + icon: 'fas fa-ban', + bg: 'var(--bg)', } }); </script> diff --git a/packages/client/src/pages/admin/integrations.discord.vue b/packages/client/src/pages/admin/integrations.discord.vue index 6b50f1b0a9..9fdc51a6ca 100644 --- a/packages/client/src/pages/admin/integrations.discord.vue +++ b/packages/client/src/pages/admin/integrations.discord.vue @@ -24,57 +24,36 @@ </FormSuspense> </template> -<script lang="ts"> -import { defineComponent } from 'vue'; +<script lang="ts" setup> +import { } from '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'; -export default defineComponent({ - components: { - FormSwitch, - FormInput, - FormInfo, - FormButton, - FormSuspense, - }, +let uri: string = $ref(''); +let enableDiscordIntegration: boolean = $ref(false); +let discordClientId: string | null = $ref(null); +let discordClientSecret: string | null = $ref(null); - emits: ['info'], +async function init() { + const meta = await os.api('admin/meta'); + uri = meta.uri; + enableDiscordIntegration = meta.enableDiscordIntegration; + discordClientId = meta.discordClientId; + discordClientSecret = meta.discordClientSecret; +} - data() { - return { - [symbols.PAGE_INFO]: { - title: 'Discord', - icon: 'fab fa-discord' - }, - enableDiscordIntegration: false, - discordClientId: null, - discordClientSecret: null, - } - }, - - methods: { - async init() { - const meta = await os.api('admin/meta'); - this.uri = meta.uri; - this.enableDiscordIntegration = meta.enableDiscordIntegration; - this.discordClientId = meta.discordClientId; - this.discordClientSecret = meta.discordClientSecret; - }, - save() { - os.apiWithDialog('admin/update-meta', { - enableDiscordIntegration: this.enableDiscordIntegration, - discordClientId: this.discordClientId, - discordClientSecret: this.discordClientSecret, - }).then(() => { - fetchInstance(); - }); - } - } -}); +function save() { + os.apiWithDialog('admin/update-meta', { + enableDiscordIntegration, + discordClientId, + discordClientSecret, + }).then(() => { + fetchInstance(); + }); +} </script> diff --git a/packages/client/src/pages/admin/integrations.github.vue b/packages/client/src/pages/admin/integrations.github.vue index 67f299e1bc..b10ccb8394 100644 --- a/packages/client/src/pages/admin/integrations.github.vue +++ b/packages/client/src/pages/admin/integrations.github.vue @@ -24,57 +24,36 @@ </FormSuspense> </template> -<script lang="ts"> -import { defineComponent } from 'vue'; +<script lang="ts" setup> +import { } from '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'; -export default defineComponent({ - components: { - FormSwitch, - FormInput, - FormInfo, - FormButton, - FormSuspense, - }, +let uri: string = $ref(''); +let enableGithubIntegration: boolean = $ref(false); +let githubClientId: string | null = $ref(null); +let githubClientSecret: string | null = $ref(null); - emits: ['info'], +async function init() { + const meta = await os.api('admin/meta'); + uri = meta.uri; + enableGithubIntegration = meta.enableGithubIntegration; + githubClientId = meta.githubClientId; + githubClientSecret = meta.githubClientSecret; +} - data() { - return { - [symbols.PAGE_INFO]: { - title: 'GitHub', - icon: 'fab fa-github' - }, - enableGithubIntegration: false, - githubClientId: null, - githubClientSecret: null, - } - }, - - methods: { - async init() { - const meta = await os.api('admin/meta'); - this.uri = meta.uri; - this.enableGithubIntegration = meta.enableGithubIntegration; - this.githubClientId = meta.githubClientId; - this.githubClientSecret = meta.githubClientSecret; - }, - save() { - os.apiWithDialog('admin/update-meta', { - enableGithubIntegration: this.enableGithubIntegration, - githubClientId: this.githubClientId, - githubClientSecret: this.githubClientSecret, - }).then(() => { - fetchInstance(); - }); - } - } -}); +function save() { + os.apiWithDialog('admin/update-meta', { + enableGithubIntegration, + githubClientId, + githubClientSecret, + }).then(() => { + fetchInstance(); + }); +} </script> diff --git a/packages/client/src/pages/admin/integrations.twitter.vue b/packages/client/src/pages/admin/integrations.twitter.vue index a389c71506..11b5fd86b2 100644 --- a/packages/client/src/pages/admin/integrations.twitter.vue +++ b/packages/client/src/pages/admin/integrations.twitter.vue @@ -24,7 +24,7 @@ </FormSuspense> </template> -<script lang="ts"> +<script lang="ts" setup> import { defineComponent } from 'vue'; import FormSwitch from '@/components/form/switch.vue'; import FormInput from '@/components/form/input.vue'; @@ -32,49 +32,28 @@ 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'; -export default defineComponent({ - components: { - FormSwitch, - FormInput, - FormInfo, - FormButton, - FormSuspense, - }, +let uri: string = $ref(''); +let enableTwitterIntegration: boolean = $ref(false); +let twitterConsumerKey: string | null = $ref(null); +let twitterConsumerSecret: string | null = $ref(null); - emits: ['info'], +async function init() { + const meta = await os.api('admin/meta'); + uri = meta.uri; + enableTwitterIntegration = meta.enableTwitterIntegration; + twitterConsumerKey = meta.twitterConsumerKey; + twitterConsumerSecret = meta.twitterConsumerSecret; +} - data() { - return { - [symbols.PAGE_INFO]: { - title: 'Twitter', - icon: 'fab fa-twitter' - }, - enableTwitterIntegration: false, - twitterConsumerKey: null, - twitterConsumerSecret: null, - } - }, - - methods: { - async init() { - const meta = await os.api('admin/meta'); - this.uri = meta.uri; - this.enableTwitterIntegration = meta.enableTwitterIntegration; - this.twitterConsumerKey = meta.twitterConsumerKey; - this.twitterConsumerSecret = meta.twitterConsumerSecret; - }, - save() { - os.apiWithDialog('admin/update-meta', { - enableTwitterIntegration: this.enableTwitterIntegration, - twitterConsumerKey: this.twitterConsumerKey, - twitterConsumerSecret: this.twitterConsumerSecret, - }).then(() => { - fetchInstance(); - }); - } - } -}); +function save() { + os.apiWithDialog('admin/update-meta', { + enableTwitterIntegration, + twitterConsumerKey, + twitterConsumerSecret, + }).then(() => { + fetchInstance(); + }); +} </script> diff --git a/packages/client/src/pages/admin/integrations.vue b/packages/client/src/pages/admin/integrations.vue index 4db8a9e0a9..d6061d0e51 100644 --- a/packages/client/src/pages/admin/integrations.vue +++ b/packages/client/src/pages/admin/integrations.vue @@ -4,69 +4,52 @@ <FormFolder class="_formBlock"> <template #icon><i class="fab fa-twitter"></i></template> <template #label>Twitter</template> - <template #suffix>{{ enableTwitterIntegration ? $ts.enabled : $ts.disabled }}</template> + <template #suffix>{{ enableTwitterIntegration ? i18n.ts.enabled : i18n.ts.disabled }}</template> <XTwitter/> </FormFolder> - <FormFolder to="/admin/integrations/github" class="_formBlock"> + <FormFolder class="_formBlock"> <template #icon><i class="fab fa-github"></i></template> <template #label>GitHub</template> - <template #suffix>{{ enableGithubIntegration ? $ts.enabled : $ts.disabled }}</template> + <template #suffix>{{ enableGithubIntegration ? i18n.ts.enabled : i18n.ts.disabled }}</template> <XGithub/> </FormFolder> - <FormFolder to="/admin/integrations/discord" class="_formBlock"> + <FormFolder class="_formBlock"> <template #icon><i class="fab fa-discord"></i></template> <template #label>Discord</template> - <template #suffix>{{ enableDiscordIntegration ? $ts.enabled : $ts.disabled }}</template> + <template #suffix>{{ enableDiscordIntegration ? i18n.ts.enabled : i18n.ts.disabled }}</template> <XDiscord/> </FormFolder> </FormSuspense> </MkSpacer> </template> -<script lang="ts"> -import { defineComponent } from 'vue'; +<script lang="ts" setup> +import { } from '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: { - FormFolder, - FormSecion, - FormSuspense, - XTwitter, - XGithub, - XDiscord, - }, +import { i18n } from '@/i18n'; - emits: ['info'], +let enableTwitterIntegration: boolean = $ref(false); +let enableGithubIntegration: boolean = $ref(false); +let enableDiscordIntegration: boolean = $ref(false); - data() { - return { - [symbols.PAGE_INFO]: { - title: this.$ts.integration, - icon: 'fas fa-share-alt', - bg: 'var(--bg)', - }, - enableTwitterIntegration: false, - enableGithubIntegration: false, - enableDiscordIntegration: false, - } - }, +async function init() { + const meta = await os.api('admin/meta'); + enableTwitterIntegration = meta.enableTwitterIntegration; + enableGithubIntegration = meta.enableGithubIntegration; + enableDiscordIntegration = meta.enableDiscordIntegration; +} - methods: { - async init() { - const meta = await os.api('admin/meta'); - this.enableTwitterIntegration = meta.enableTwitterIntegration; - this.enableGithubIntegration = meta.enableGithubIntegration; - this.enableDiscordIntegration = meta.enableDiscordIntegration; - }, +defineExpose({ + [symbols.PAGE_INFO]: { + title: i18n.ts.integration, + icon: 'fas fa-share-alt', + bg: 'var(--bg)', } }); </script> diff --git a/packages/client/src/pages/admin/metrics.vue b/packages/client/src/pages/admin/metrics.vue index 1de297fd93..7e5f5bb094 100644 --- a/packages/client/src/pages/admin/metrics.vue +++ b/packages/client/src/pages/admin/metrics.vue @@ -132,7 +132,7 @@ export default defineComponent({ overviewHeight: '1fr', queueHeight: '1fr', paused: false, - } + }; }, computed: { diff --git a/packages/client/src/pages/admin/object-storage.vue b/packages/client/src/pages/admin/object-storage.vue index a1ee0761c8..d109db9c38 100644 --- a/packages/client/src/pages/admin/object-storage.vue +++ b/packages/client/src/pages/admin/object-storage.vue @@ -2,32 +2,32 @@ <MkSpacer :content-max="700" :margin-min="16" :margin-max="32"> <FormSuspense :p="init"> <div class="_formRoot"> - <FormSwitch v-model="useObjectStorage" class="_formBlock">{{ $ts.useObjectStorage }}</FormSwitch> + <FormSwitch v-model="useObjectStorage" class="_formBlock">{{ i18n.ts.useObjectStorage }}</FormSwitch> <template v-if="useObjectStorage"> <FormInput v-model="objectStorageBaseUrl" class="_formBlock"> - <template #label>{{ $ts.objectStorageBaseUrl }}</template> - <template #caption>{{ $ts.objectStorageBaseUrlDesc }}</template> + <template #label>{{ i18n.ts.objectStorageBaseUrl }}</template> + <template #caption>{{ i18n.ts.objectStorageBaseUrlDesc }}</template> </FormInput> <FormInput v-model="objectStorageBucket" class="_formBlock"> - <template #label>{{ $ts.objectStorageBucket }}</template> - <template #caption>{{ $ts.objectStorageBucketDesc }}</template> + <template #label>{{ i18n.ts.objectStorageBucket }}</template> + <template #caption>{{ i18n.ts.objectStorageBucketDesc }}</template> </FormInput> <FormInput v-model="objectStoragePrefix" class="_formBlock"> - <template #label>{{ $ts.objectStoragePrefix }}</template> - <template #caption>{{ $ts.objectStoragePrefixDesc }}</template> + <template #label>{{ i18n.ts.objectStoragePrefix }}</template> + <template #caption>{{ i18n.ts.objectStoragePrefixDesc }}</template> </FormInput> <FormInput v-model="objectStorageEndpoint" class="_formBlock"> - <template #label>{{ $ts.objectStorageEndpoint }}</template> - <template #caption>{{ $ts.objectStorageEndpointDesc }}</template> + <template #label>{{ i18n.ts.objectStorageEndpoint }}</template> + <template #caption>{{ i18n.ts.objectStorageEndpointDesc }}</template> </FormInput> <FormInput v-model="objectStorageRegion" class="_formBlock"> - <template #label>{{ $ts.objectStorageRegion }}</template> - <template #caption>{{ $ts.objectStorageRegionDesc }}</template> + <template #label>{{ i18n.ts.objectStorageRegion }}</template> + <template #caption>{{ i18n.ts.objectStorageRegionDesc }}</template> </FormInput> <FormSplit :min-width="280"> @@ -43,17 +43,17 @@ </FormSplit> <FormSwitch v-model="objectStorageUseSSL" class="_formBlock"> - <template #label>{{ $ts.objectStorageUseSSL }}</template> - <template #caption>{{ $ts.objectStorageUseSSLDesc }}</template> + <template #label>{{ i18n.ts.objectStorageUseSSL }}</template> + <template #caption>{{ i18n.ts.objectStorageUseSSLDesc }}</template> </FormSwitch> <FormSwitch v-model="objectStorageUseProxy" class="_formBlock"> - <template #label>{{ $ts.objectStorageUseProxy }}</template> - <template #caption>{{ $ts.objectStorageUseProxyDesc }}</template> + <template #label>{{ i18n.ts.objectStorageUseProxy }}</template> + <template #caption>{{ i18n.ts.objectStorageUseProxyDesc }}</template> </FormSwitch> <FormSwitch v-model="objectStorageSetPublicRead" class="_formBlock"> - <template #label>{{ $ts.objectStorageSetPublicRead }}</template> + <template #label>{{ i18n.ts.objectStorageSetPublicRead }}</template> </FormSwitch> <FormSwitch v-model="objectStorageS3ForcePathStyle" class="_formBlock"> @@ -65,8 +65,8 @@ </MkSpacer> </template> -<script lang="ts"> -import { defineComponent } from 'vue'; +<script lang="ts" setup> +import { } from 'vue'; import FormSwitch from '@/components/form/switch.vue'; import FormInput from '@/components/form/input.vue'; import FormGroup from '@/components/form/group.vue'; @@ -76,84 +76,70 @@ import FormSection from '@/components/form/section.vue'; import * as os from '@/os'; import * as symbols from '@/symbols'; import { fetchInstance } from '@/instance'; +import { i18n } from '@/i18n'; -export default defineComponent({ - components: { - FormSwitch, - FormInput, - FormGroup, - FormSuspense, - FormSplit, - FormSection, - }, +let useObjectStorage: boolean = $ref(false); +let objectStorageBaseUrl: string | null = $ref(null); +let objectStorageBucket: string | null = $ref(null); +let objectStoragePrefix: string | null = $ref(null); +let objectStorageEndpoint: string | null = $ref(null); +let objectStorageRegion: string | null = $ref(null); +let objectStoragePort: number | null = $ref(null); +let objectStorageAccessKey: string | null = $ref(null); +let objectStorageSecretKey: string | null = $ref(null); +let objectStorageUseSSL: boolean = $ref(false); +let objectStorageUseProxy: boolean = $ref(false); +let objectStorageSetPublicRead: boolean = $ref(false); +let objectStorageS3ForcePathStyle: boolean = $ref(true); - emits: ['info'], +async function init() { + const meta = await os.api('admin/meta'); + useObjectStorage = meta.useObjectStorage; + objectStorageBaseUrl = meta.objectStorageBaseUrl; + objectStorageBucket = meta.objectStorageBucket; + objectStoragePrefix = meta.objectStoragePrefix; + objectStorageEndpoint = meta.objectStorageEndpoint; + objectStorageRegion = meta.objectStorageRegion; + objectStoragePort = meta.objectStoragePort; + objectStorageAccessKey = meta.objectStorageAccessKey; + objectStorageSecretKey = meta.objectStorageSecretKey; + objectStorageUseSSL = meta.objectStorageUseSSL; + objectStorageUseProxy = meta.objectStorageUseProxy; + objectStorageSetPublicRead = meta.objectStorageSetPublicRead; + objectStorageS3ForcePathStyle = meta.objectStorageS3ForcePathStyle; +} - data() { - return { - [symbols.PAGE_INFO]: { - 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, - objectStorageBucket: null, - objectStoragePrefix: null, - objectStorageEndpoint: null, - objectStorageRegion: null, - objectStoragePort: null, - objectStorageAccessKey: null, - objectStorageSecretKey: null, - objectStorageUseSSL: false, - objectStorageUseProxy: false, - objectStorageSetPublicRead: false, - objectStorageS3ForcePathStyle: true, - } - }, +function save() { + os.apiWithDialog('admin/update-meta', { + useObjectStorage, + objectStorageBaseUrl, + objectStorageBucket, + objectStoragePrefix, + objectStorageEndpoint, + objectStorageRegion, + objectStoragePort, + objectStorageAccessKey, + objectStorageSecretKey, + objectStorageUseSSL, + objectStorageUseProxy, + objectStorageSetPublicRead, + objectStorageS3ForcePathStyle, + }).then(() => { + fetchInstance(); + }); +} - methods: { - async init() { - const meta = await os.api('admin/meta'); - this.useObjectStorage = meta.useObjectStorage; - this.objectStorageBaseUrl = meta.objectStorageBaseUrl; - this.objectStorageBucket = meta.objectStorageBucket; - this.objectStoragePrefix = meta.objectStoragePrefix; - this.objectStorageEndpoint = meta.objectStorageEndpoint; - this.objectStorageRegion = meta.objectStorageRegion; - this.objectStoragePort = meta.objectStoragePort; - this.objectStorageAccessKey = meta.objectStorageAccessKey; - this.objectStorageSecretKey = meta.objectStorageSecretKey; - this.objectStorageUseSSL = meta.objectStorageUseSSL; - this.objectStorageUseProxy = meta.objectStorageUseProxy; - this.objectStorageSetPublicRead = meta.objectStorageSetPublicRead; - this.objectStorageS3ForcePathStyle = meta.objectStorageS3ForcePathStyle; - }, - save() { - os.apiWithDialog('admin/update-meta', { - useObjectStorage: this.useObjectStorage, - objectStorageBaseUrl: this.objectStorageBaseUrl ? this.objectStorageBaseUrl : null, - objectStorageBucket: this.objectStorageBucket ? this.objectStorageBucket : null, - objectStoragePrefix: this.objectStoragePrefix ? this.objectStoragePrefix : null, - objectStorageEndpoint: this.objectStorageEndpoint ? this.objectStorageEndpoint : null, - objectStorageRegion: this.objectStorageRegion ? this.objectStorageRegion : null, - objectStoragePort: this.objectStoragePort ? this.objectStoragePort : null, - objectStorageAccessKey: this.objectStorageAccessKey ? this.objectStorageAccessKey : null, - objectStorageSecretKey: this.objectStorageSecretKey ? this.objectStorageSecretKey : null, - objectStorageUseSSL: this.objectStorageUseSSL, - objectStorageUseProxy: this.objectStorageUseProxy, - objectStorageSetPublicRead: this.objectStorageSetPublicRead, - objectStorageS3ForcePathStyle: this.objectStorageS3ForcePathStyle, - }).then(() => { - fetchInstance(); - }); - } +defineExpose({ + [symbols.PAGE_INFO]: { + title: i18n.ts.objectStorage, + icon: 'fas fa-cloud', + bg: 'var(--bg)', + actions: [{ + asFullButton: true, + icon: 'fas fa-check', + text: i18n.ts.save, + handler: save, + }], } }); </script> diff --git a/packages/client/src/pages/admin/other-settings.vue b/packages/client/src/pages/admin/other-settings.vue index 99ea6a5f32..552b05f347 100644 --- a/packages/client/src/pages/admin/other-settings.vue +++ b/packages/client/src/pages/admin/other-settings.vue @@ -6,52 +6,35 @@ </MkSpacer> </template> -<script lang="ts"> -import { defineComponent } from 'vue'; -import FormSwitch from '@/components/form/switch.vue'; -import FormInput from '@/components/form/input.vue'; -import FormSection from '@/components/form/section.vue'; +<script lang="ts" setup> +import { } from 'vue'; import FormSuspense from '@/components/form/suspense.vue'; import * as os from '@/os'; import * as symbols from '@/symbols'; import { fetchInstance } from '@/instance'; +import { i18n } from '@/i18n'; -export default defineComponent({ - components: { - FormSwitch, - FormInput, - FormSection, - FormSuspense, - }, +async function init() { + await os.api('admin/meta'); +} - emits: ['info'], +function save() { + os.apiWithDialog('admin/update-meta').then(() => { + fetchInstance(); + }); +} - data() { - return { - [symbols.PAGE_INFO]: { - 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, - }], - }, - } - }, - - methods: { - async init() { - const meta = await os.api('admin/meta'); - }, - save() { - os.apiWithDialog('admin/update-meta', { - }).then(() => { - fetchInstance(); - }); - } +defineExpose({ + [symbols.PAGE_INFO]: { + title: i18n.ts.other, + icon: 'fas fa-cogs', + bg: 'var(--bg)', + actions: [{ + asFullButton: true, + icon: 'fas fa-check', + text: i18n.ts.save, + handler: save, + }], } }); </script> diff --git a/packages/client/src/pages/admin/overview.vue b/packages/client/src/pages/admin/overview.vue index b8ae8ad9e1..cc69424c3b 100644 --- a/packages/client/src/pages/admin/overview.vue +++ b/packages/client/src/pages/admin/overview.vue @@ -5,20 +5,20 @@ <div class="label">Users</div> <div class="value _monospace"> {{ number(stats.originalUsersCount) }} - <MkNumberDiff v-if="usersComparedToThePrevDay != null" v-tooltip="$ts.dayOverDayChanges" class="diff" :value="usersComparedToThePrevDay"><template #before>(</template><template #after>)</template></MkNumberDiff> + <MkNumberDiff v-if="usersComparedToThePrevDay != null" v-tooltip="i18n.ts.dayOverDayChanges" class="diff" :value="usersComparedToThePrevDay"><template #before>(</template><template #after>)</template></MkNumberDiff> </div> </div> <div class="number _panel"> <div class="label">Notes</div> <div class="value _monospace"> {{ number(stats.originalNotesCount) }} - <MkNumberDiff v-if="notesComparedToThePrevDay != null" v-tooltip="$ts.dayOverDayChanges" class="diff" :value="notesComparedToThePrevDay"><template #before>(</template><template #after>)</template></MkNumberDiff> + <MkNumberDiff v-if="notesComparedToThePrevDay != null" v-tooltip="i18n.ts.dayOverDayChanges" class="diff" :value="notesComparedToThePrevDay"><template #before>(</template><template #after>)</template></MkNumberDiff> </div> </div> </div> <MkContainer :foldable="true" class="charts"> - <template #header><i class="fas fa-chart-bar"></i>{{ $ts.charts }}</template> + <template #header><i class="fas fa-chart-bar"></i>{{ i18n.ts.charts }}</template> <div style="padding: 12px;"> <MkInstanceStats :chart-limit="500" :detailed="true"/> </div> @@ -38,7 +38,7 @@ <!--<XMetrics/>--> <MkFolder style="margin: var(--margin)"> - <template #header><i class="fas fa-info-circle"></i> {{ $ts.info }}</template> + <template #header><i class="fas fa-info-circle"></i> {{ i18n.ts.info }}</template> <div class="cfcdecdf"> <div class="number _panel"> <div class="label">Misskey</div> @@ -65,103 +65,61 @@ </div> </template> -<script lang="ts"> -import { computed, defineComponent, markRaw, version as vueVersion } from 'vue'; +<script lang="ts" setup> +import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick } from 'vue'; import MkInstanceStats from '@/components/instance-stats.vue'; -import MkButton from '@/components/ui/button.vue'; -import MkSelect from '@/components/form/select.vue'; import MkNumberDiff from '@/components/number-diff.vue'; import MkContainer from '@/components/ui/container.vue'; import MkFolder from '@/components/ui/folder.vue'; import MkQueueChart from '@/components/queue-chart.vue'; import { version, url } from '@/config'; -import bytes from '@/filters/bytes'; import number from '@/filters/number'; import XMetrics from './metrics.vue'; import * as os from '@/os'; import { stream } from '@/stream'; import * as symbols from '@/symbols'; +import { i18n } from '@/i18n'; -export default defineComponent({ - components: { - MkNumberDiff, - MkInstanceStats, - MkContainer, - MkFolder, - MkQueueChart, - XMetrics, - }, +let stats: any = $ref(null); +let serverInfo: any = $ref(null); +let usersComparedToThePrevDay: any = $ref(null); +let notesComparedToThePrevDay: any = $ref(null); +const queueStatsConnection = markRaw(stream.useChannel('queueStats')); - emits: ['info'], +onMounted(async () => { + os.api('stats', {}).then(statsResponse => { + stats = statsResponse; - data() { - return { - [symbols.PAGE_INFO]: { - title: this.$ts.dashboard, - icon: 'fas fa-tachometer-alt', - bg: 'var(--bg)', - }, - version, - vueVersion, - url, - stats: null, - meta: null, - serverInfo: null, - usersComparedToThePrevDay: null, - notesComparedToThePrevDay: null, - fetchJobs: () => os.api('admin/queue/deliver-delayed', {}), - fetchModLogs: () => os.api('admin/show-moderation-logs', {}), - queueStatsConnection: markRaw(stream.useChannel('queueStats')), - } - }, - - async mounted() { - os.api('meta', { detail: true }).then(meta => { - this.meta = meta; + os.api('charts/users', { limit: 2, span: 'day' }).then(chart => { + usersComparedToThePrevDay = stats.originalUsersCount - chart.local.total[1]; }); - - os.api('stats', {}).then(stats => { - this.stats = stats; - - os.api('charts/users', { limit: 2, span: 'day' }).then(chart => { - this.usersComparedToThePrevDay = this.stats.originalUsersCount - chart.local.total[1]; - }); - os.api('charts/notes', { limit: 2, span: 'day' }).then(chart => { - this.notesComparedToThePrevDay = this.stats.originalNotesCount - chart.local.total[1]; - }); + os.api('charts/notes', { limit: 2, span: 'day' }).then(chart => { + notesComparedToThePrevDay = stats.originalNotesCount - chart.local.total[1]; }); + }); - os.api('admin/server-info', {}).then(serverInfo => { - this.serverInfo = serverInfo; - }); + os.api('admin/server-info').then(serverInfoResponse => { + serverInfo = serverInfoResponse; + }); - this.$nextTick(() => { - this.queueStatsConnection.send('requestLog', { - id: Math.random().toString().substr(2, 8), - length: 200 - }); + nextTick(() => { + queueStatsConnection.send('requestLog', { + id: Math.random().toString().substr(2, 8), + length: 200 }); - }, - - beforeUnmount() { - this.queueStatsConnection.dispose(); - }, - - methods: { - async showInstanceInfo(q) { - let instance = q; - if (typeof q === 'string') { - instance = await os.api('federation/show-instance', { - host: q - }); - } - // TODO - }, + }); +}); - bytes, +onBeforeUnmount(() => { + queueStatsConnection.dispose(); +}); - number, +defineExpose({ + [symbols.PAGE_INFO]: { + title: i18n.ts.dashboard, + icon: 'fas fa-tachometer-alt', + bg: 'var(--bg)', } }); </script> diff --git a/packages/client/src/pages/admin/proxy-account.vue b/packages/client/src/pages/admin/proxy-account.vue index 00f14a176f..727e20e7e5 100644 --- a/packages/client/src/pages/admin/proxy-account.vue +++ b/packages/client/src/pages/admin/proxy-account.vue @@ -1,19 +1,19 @@ <template> <MkSpacer :content-max="700" :margin-min="16" :margin-max="32"> <FormSuspense :p="init"> - <MkInfo class="_formBlock">{{ $ts.proxyAccountDescription }}</MkInfo> + <MkInfo class="_formBlock">{{ i18n.ts.proxyAccountDescription }}</MkInfo> <MkKeyValue class="_formBlock"> - <template #key>{{ $ts.proxyAccount }}</template> - <template #value>{{ proxyAccount ? `@${proxyAccount.username}` : $ts.none }}</template> + <template #key>{{ i18n.ts.proxyAccount }}</template> + <template #value>{{ proxyAccount ? `@${proxyAccount.username}` : i18n.ts.none }}</template> </MkKeyValue> - <FormButton primary class="_formBlock" @click="chooseProxyAccount">{{ $ts.selectAccount }}</FormButton> + <FormButton primary class="_formBlock" @click="chooseProxyAccount">{{ i18n.ts.selectAccount }}</FormButton> </FormSuspense> </MkSpacer> </template> -<script lang="ts"> -import { defineComponent } from 'vue'; +<script lang="ts" setup> +import { } from 'vue'; import MkKeyValue from '@/components/key-value.vue'; import FormButton from '@/components/ui/button.vue'; import MkInfo from '@/components/ui/info.vue'; @@ -21,53 +21,40 @@ import FormSuspense from '@/components/form/suspense.vue'; import * as os from '@/os'; import * as symbols from '@/symbols'; import { fetchInstance } from '@/instance'; +import { i18n } from '@/i18n'; -export default defineComponent({ - components: { - MkKeyValue, - FormButton, - MkInfo, - FormSuspense, - }, +let proxyAccount: any = $ref(null); +let proxyAccountId: any = $ref(null); - emits: ['info'], - - data() { - return { - [symbols.PAGE_INFO]: { - title: this.$ts.proxyAccount, - icon: 'fas fa-ghost', - bg: 'var(--bg)', - }, - proxyAccount: null, - proxyAccountId: null, - } - }, +async function init() { + const meta = await os.api('admin/meta'); + proxyAccountId = meta.proxyAccountId; + if (proxyAccountId) { + proxyAccount = await os.api('users/show', { userId: proxyAccountId }); + } +} - methods: { - async init() { - const meta = await os.api('admin/meta'); - this.proxyAccountId = meta.proxyAccountId; - if (this.proxyAccountId) { - this.proxyAccount = await os.api('users/show', { userId: this.proxyAccountId }); - } - }, +function chooseProxyAccount() { + os.selectUser().then(user => { + proxyAccount = user; + proxyAccountId = user.id; + save(); + }); +} - chooseProxyAccount() { - os.selectUser().then(user => { - this.proxyAccount = user; - this.proxyAccountId = user.id; - this.save(); - }); - }, +function save() { + os.apiWithDialog('admin/update-meta', { + proxyAccountId: proxyAccountId, + }).then(() => { + fetchInstance(); + }); +} - save() { - os.apiWithDialog('admin/update-meta', { - proxyAccountId: this.proxyAccountId, - }).then(() => { - fetchInstance(); - }); - } +defineExpose({ + [symbols.PAGE_INFO]: { + title: i18n.ts.proxyAccount, + icon: 'fas fa-ghost', + bg: 'var(--bg)', } }); </script> diff --git a/packages/client/src/pages/admin/queue.chart.vue b/packages/client/src/pages/admin/queue.chart.vue index 136fb63bb6..be63830bdd 100644 --- a/packages/client/src/pages/admin/queue.chart.vue +++ b/packages/client/src/pages/admin/queue.chart.vue @@ -26,62 +26,40 @@ </div> </template> -<script lang="ts"> -import { defineComponent, markRaw, onMounted, onUnmounted, ref } from 'vue'; +<script lang="ts" setup> +import { onMounted, onUnmounted, ref } from 'vue'; import number from '@/filters/number'; import MkQueueChart from '@/components/queue-chart.vue'; import * as os from '@/os'; -export default defineComponent({ - components: { - MkQueueChart - }, +const activeSincePrevTick = ref(0); +const active = ref(0); +const waiting = ref(0); +const delayed = ref(0); +const jobs = ref([]); - props: { - domain: { - type: String, - required: true, - }, - connection: { - required: true, - }, - }, +const props = defineProps<{ + domain: string, + connection: any, +}>(); - setup(props) { - const activeSincePrevTick = ref(0); - const active = ref(0); - const waiting = ref(0); - const delayed = ref(0); - const jobs = ref([]); +onMounted(() => { + os.api(props.domain === 'inbox' ? 'admin/queue/inbox-delayed' : props.domain === 'deliver' ? 'admin/queue/deliver-delayed' : null, {}).then(result => { + jobs.value = result; + }); - onMounted(() => { - os.api(props.domain === 'inbox' ? 'admin/queue/inbox-delayed' : props.domain === 'deliver' ? 'admin/queue/deliver-delayed' : null, {}).then(result => { - jobs.value = result; - }); + const onStats = (stats) => { + activeSincePrevTick.value = stats[props.domain].activeSincePrevTick; + active.value = stats[props.domain].active; + waiting.value = stats[props.domain].waiting; + delayed.value = stats[props.domain].delayed; + }; - const onStats = (stats) => { - activeSincePrevTick.value = stats[props.domain].activeSincePrevTick; - active.value = stats[props.domain].active; - waiting.value = stats[props.domain].waiting; - delayed.value = stats[props.domain].delayed; - }; + props.connection.on('stats', onStats); - props.connection.on('stats', onStats); - - onUnmounted(() => { - props.connection.off('stats', onStats); - }); - }); - - return { - jobs, - activeSincePrevTick, - active, - waiting, - delayed, - number, - }; - }, + onUnmounted(() => { + props.connection.off('stats', onStats); + }); }); </script> diff --git a/packages/client/src/pages/admin/queue.vue b/packages/client/src/pages/admin/queue.vue index 35fd618c82..656b18199f 100644 --- a/packages/client/src/pages/admin/queue.vue +++ b/packages/client/src/pages/admin/queue.vue @@ -6,71 +6,60 @@ <XQueue :connection="connection" domain="deliver"> <template #title>Out</template> </XQueue> - <MkButton danger @click="clear()"><i class="fas fa-trash-alt"></i> {{ $ts.clearQueue }}</MkButton> + <MkButton danger @click="clear()"><i class="fas fa-trash-alt"></i> {{ i18n.ts.clearQueue }}</MkButton> </MkSpacer> </template> -<script lang="ts"> -import { defineComponent, markRaw } from 'vue'; +<script lang="ts" setup> +import { markRaw, onMounted, onBeforeUnmount, nextTick } from 'vue'; import MkButton from '@/components/ui/button.vue'; import XQueue from './queue.chart.vue'; import * as os from '@/os'; import { stream } from '@/stream'; import * as symbols from '@/symbols'; import * as config from '@/config'; +import { i18n } from '@/i18n'; -export default defineComponent({ - components: { - MkButton, - XQueue, - }, +const connection = markRaw(stream.useChannel('queueStats')); - emits: ['info'], +function clear() { + os.confirm({ + type: 'warning', + title: i18n.ts.clearQueueConfirmTitle, + text: i18n.ts.clearQueueConfirmText, + }).then(({ canceled }) => { + if (canceled) return; - data() { - return { - [symbols.PAGE_INFO]: { - title: this.$ts.jobQueue, - icon: 'fas fa-clipboard-list', - bg: 'var(--bg)', - actions: [{ - asFullButton: true, - icon: 'fas fa-up-right-from-square', - text: this.$ts.dashboard, - handler: () => { - window.open(config.url + '/queue', '_blank'); - }, - }], - }, - connection: markRaw(stream.useChannel('queueStats')), - } - }, + os.apiWithDialog('admin/queue/clear'); + }); +} - mounted() { - this.$nextTick(() => { - this.connection.send('requestLog', { - id: Math.random().toString().substr(2, 8), - length: 200 - }); +onMounted(() => { + nextTick(() => { + connection.send('requestLog', { + id: Math.random().toString().substr(2, 8), + length: 200 }); - }, - - beforeUnmount() { - this.connection.dispose(); - }, + }); +}); - methods: { - clear() { - os.confirm({ - type: 'warning', - title: this.$ts.clearQueueConfirmTitle, - text: this.$ts.clearQueueConfirmText, - }).then(({ canceled }) => { - if (canceled) return; +onBeforeUnmount(() => { + connection.dispose(); +}); - os.apiWithDialog('admin/queue/clear', {}); - }); - } +defineExpose({ + [symbols.PAGE_INFO]: { + title: i18n.ts.jobQueue, + icon: 'fas fa-clipboard-list', + bg: 'var(--bg)', + actions: [{ + asFullButton: true, + icon: 'fas fa-up-right-from-square', + text: i18n.ts.dashboard, + handler: () => { + window.open(config.url + '/queue', '_blank'); + }, + }], } }); </script> diff --git a/packages/client/src/pages/admin/relays.vue b/packages/client/src/pages/admin/relays.vue index bb840db0a2..1a36bb4753 100644 --- a/packages/client/src/pages/admin/relays.vue +++ b/packages/client/src/pages/admin/relays.vue @@ -8,84 +8,71 @@ <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> + <MkButton class="button" inline danger @click="remove(relay.inbox)"><i class="fas fa-trash-alt"></i> {{ i18n.ts.remove }}</MkButton> </div> </MkSpacer> </template> -<script lang="ts"> -import { defineComponent } from 'vue'; +<script lang="ts" setup> +import { } from 'vue'; import MkButton from '@/components/ui/button.vue'; import * as os from '@/os'; import * as symbols from '@/symbols'; +import { i18n } from '@/i18n'; -export default defineComponent({ - components: { - MkButton, - }, +let relays: any[] = $ref([]); - emits: ['info'], - - data() { - return { - [symbols.PAGE_INFO]: { - 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: '', - } - }, +async function addRelay() { + const { canceled, result: inbox } = await os.inputText({ + title: i18n.ts.addRelay, + type: 'url', + placeholder: i18n.ts.inboxUrl + }); + if (canceled) return; + os.api('admin/relays/add', { + inbox + }).then((relay: any) => { + refresh(); + }).catch((err: any) => { + os.alert({ + type: 'error', + text: err.message || err + }); + }); +} - created() { - this.refresh(); - }, +function remove(inbox: string) { + os.api('admin/relays/remove', { + inbox + }).then(() => { + refresh(); + }).catch((err: any) => { + os.alert({ + type: 'error', + text: err.message || err + }); + }); +} - methods: { - async addRelay() { - const { canceled, result: inbox } = await os.inputText({ - title: this.$ts.addRelay, - type: 'url', - placeholder: this.$ts.inboxUrl - }); - if (canceled) return; - os.api('admin/relays/add', { - inbox - }).then((relay: any) => { - this.refresh(); - }).catch((e: any) => { - os.alert({ - type: 'error', - text: e.message || e - }); - }); - }, +function refresh() { + os.api('admin/relays/list').then((relayList: any) => { + relays = relayList; + }); +} - remove(inbox: string) { - os.api('admin/relays/remove', { - inbox - }).then(() => { - this.refresh(); - }).catch((e: any) => { - os.alert({ - type: 'error', - text: e.message || e - }); - }); - }, +refresh(); - refresh() { - os.api('admin/relays/list').then((relays: any) => { - this.relays = relays; - }); - } +defineExpose({ + [symbols.PAGE_INFO]: { + title: i18n.ts.relays, + icon: 'fas fa-globe', + bg: 'var(--bg)', + actions: [{ + asFullButton: true, + icon: 'fas fa-plus', + text: i18n.ts.addRelay, + handler: addRelay, + }], } }); </script> diff --git a/packages/client/src/pages/admin/security.vue b/packages/client/src/pages/admin/security.vue index d1c979b3e0..6b8f70cca5 100644 --- a/packages/client/src/pages/admin/security.vue +++ b/packages/client/src/pages/admin/security.vue @@ -4,10 +4,10 @@ <div class="_formRoot"> <FormFolder class="_formBlock"> <template #icon><i class="fas fa-shield-alt"></i></template> - <template #label>{{ $ts.botProtection }}</template> + <template #label>{{ i18n.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> + <template v-else #suffix>{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</template> <XBotProtection/> </FormFolder> @@ -21,7 +21,7 @@ <template #label>Summaly Proxy URL</template> </FormInput> - <FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> + <FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ i18n.ts.save }}</FormButton> </div> </FormFolder> </div> @@ -29,8 +29,8 @@ </MkSpacer> </template> -<script lang="ts"> -import { defineAsyncComponent, defineComponent } from 'vue'; +<script lang="ts" setup> +import { } from 'vue'; import FormFolder from '@/components/form/folder.vue'; import FormSwitch from '@/components/form/switch.vue'; import FormInfo from '@/components/ui/info.vue'; @@ -42,49 +42,32 @@ import XBotProtection from './bot-protection.vue'; import * as os from '@/os'; import * as symbols from '@/symbols'; import { fetchInstance } from '@/instance'; +import { i18n } from '@/i18n'; -export default defineComponent({ - components: { - FormFolder, - FormSwitch, - FormInfo, - FormSection, - FormSuspense, - FormButton, - FormInput, - XBotProtection, - }, +let summalyProxy: string = $ref(''); +let enableHcaptcha: boolean = $ref(false); +let enableRecaptcha: boolean = $ref(false); - emits: ['info'], +async function init() { + const meta = await os.api('admin/meta'); + summalyProxy = meta.summalyProxy; + enableHcaptcha = meta.enableHcaptcha; + enableRecaptcha = meta.enableRecaptcha; +} - data() { - return { - [symbols.PAGE_INFO]: { - title: this.$ts.security, - icon: 'fas fa-lock', - bg: 'var(--bg)', - }, - summalyProxy: '', - enableHcaptcha: false, - enableRecaptcha: false, - } - }, +function save() { + os.apiWithDialog('admin/update-meta', { + summalyProxy, + }).then(() => { + fetchInstance(); + }); +} - methods: { - async init() { - const meta = await os.api('admin/meta'); - this.summalyProxy = meta.summalyProxy; - this.enableHcaptcha = meta.enableHcaptcha; - this.enableRecaptcha = meta.enableRecaptcha; - }, - - save() { - os.apiWithDialog('admin/update-meta', { - summalyProxy: this.summalyProxy, - }).then(() => { - fetchInstance(); - }); - } +defineExpose({ + [symbols.PAGE_INFO]: { + title: i18n.ts.security, + icon: 'fas fa-lock', + bg: 'var(--bg)', } }); </script> diff --git a/packages/client/src/pages/admin/settings.vue b/packages/client/src/pages/admin/settings.vue index f2970d0459..6dc30fe50b 100644 --- a/packages/client/src/pages/admin/settings.vue +++ b/packages/client/src/pages/admin/settings.vue @@ -3,104 +3,104 @@ <FormSuspense :p="init"> <div class="_formRoot"> <FormInput v-model="name" class="_formBlock"> - <template #label>{{ $ts.instanceName }}</template> + <template #label>{{ i18n.ts.instanceName }}</template> </FormInput> <FormTextarea v-model="description" class="_formBlock"> - <template #label>{{ $ts.instanceDescription }}</template> + <template #label>{{ i18n.ts.instanceDescription }}</template> </FormTextarea> <FormInput v-model="tosUrl" class="_formBlock"> <template #prefix><i class="fas fa-link"></i></template> - <template #label>{{ $ts.tosUrl }}</template> + <template #label>{{ i18n.ts.tosUrl }}</template> </FormInput> <FormSplit :min-width="300"> <FormInput v-model="maintainerName" class="_formBlock"> - <template #label>{{ $ts.maintainerName }}</template> + <template #label>{{ i18n.ts.maintainerName }}</template> </FormInput> <FormInput v-model="maintainerEmail" type="email" class="_formBlock"> <template #prefix><i class="fas fa-envelope"></i></template> - <template #label>{{ $ts.maintainerEmail }}</template> + <template #label>{{ i18n.ts.maintainerEmail }}</template> </FormInput> </FormSplit> <FormTextarea v-model="pinnedUsers" class="_formBlock"> - <template #label>{{ $ts.pinnedUsers }}</template> - <template #caption>{{ $ts.pinnedUsersDescription }}</template> + <template #label>{{ i18n.ts.pinnedUsers }}</template> + <template #caption>{{ i18n.ts.pinnedUsersDescription }}</template> </FormTextarea> <FormSection> <FormSwitch v-model="enableRegistration" class="_formBlock"> - <template #label>{{ $ts.enableRegistration }}</template> + <template #label>{{ i18n.ts.enableRegistration }}</template> </FormSwitch> <FormSwitch v-model="emailRequiredForSignup" class="_formBlock"> - <template #label>{{ $ts.emailRequiredForSignup }}</template> + <template #label>{{ i18n.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> + <FormSwitch v-model="enableLocalTimeline" class="_formBlock">{{ i18n.ts.enableLocalTimeline }}</FormSwitch> + <FormSwitch v-model="enableGlobalTimeline" class="_formBlock">{{ i18n.ts.enableGlobalTimeline }}</FormSwitch> + <FormInfo class="_formBlock">{{ i18n.ts.disablingTimelinesInfo }}</FormInfo> </FormSection> <FormSection> - <template #label>{{ $ts.theme }}</template> + <template #label>{{ i18n.ts.theme }}</template> <FormInput v-model="iconUrl" class="_formBlock"> <template #prefix><i class="fas fa-link"></i></template> - <template #label>{{ $ts.iconUrl }}</template> + <template #label>{{ i18n.ts.iconUrl }}</template> </FormInput> <FormInput v-model="bannerUrl" class="_formBlock"> <template #prefix><i class="fas fa-link"></i></template> - <template #label>{{ $ts.bannerUrl }}</template> + <template #label>{{ i18n.ts.bannerUrl }}</template> </FormInput> <FormInput v-model="backgroundImageUrl" class="_formBlock"> <template #prefix><i class="fas fa-link"></i></template> - <template #label>{{ $ts.backgroundImageUrl }}</template> + <template #label>{{ i18n.ts.backgroundImageUrl }}</template> </FormInput> <FormInput v-model="themeColor" class="_formBlock"> <template #prefix><i class="fas fa-palette"></i></template> - <template #label>{{ $ts.themeColor }}</template> + <template #label>{{ i18n.ts.themeColor }}</template> <template #caption>#RRGGBB</template> </FormInput> <FormTextarea v-model="defaultLightTheme" class="_formBlock"> - <template #label>{{ $ts.instanceDefaultLightTheme }}</template> - <template #caption>{{ $ts.instanceDefaultThemeDescription }}</template> + <template #label>{{ i18n.ts.instanceDefaultLightTheme }}</template> + <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template> </FormTextarea> <FormTextarea v-model="defaultDarkTheme" class="_formBlock"> - <template #label>{{ $ts.instanceDefaultDarkTheme }}</template> - <template #caption>{{ $ts.instanceDefaultThemeDescription }}</template> + <template #label>{{ i18n.ts.instanceDefaultDarkTheme }}</template> + <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template> </FormTextarea> </FormSection> <FormSection> - <template #label>{{ $ts.files }}</template> + <template #label>{{ i18n.ts.files }}</template> <FormSwitch v-model="cacheRemoteFiles" class="_formBlock"> - <template #label>{{ $ts.cacheRemoteFiles }}</template> - <template #caption>{{ $ts.cacheRemoteFilesDescription }}</template> + <template #label>{{ i18n.ts.cacheRemoteFiles }}</template> + <template #caption>{{ i18n.ts.cacheRemoteFilesDescription }}</template> </FormSwitch> <FormSplit :min-width="280"> <FormInput v-model="localDriveCapacityMb" type="number" class="_formBlock"> - <template #label>{{ $ts.driveCapacityPerLocalAccount }}</template> + <template #label>{{ i18n.ts.driveCapacityPerLocalAccount }}</template> <template #suffix>MB</template> - <template #caption>{{ $ts.inMb }}</template> + <template #caption>{{ i18n.ts.inMb }}</template> </FormInput> <FormInput v-model="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles" class="_formBlock"> - <template #label>{{ $ts.driveCapacityPerRemoteAccount }}</template> + <template #label>{{ i18n.ts.driveCapacityPerRemoteAccount }}</template> <template #suffix>MB</template> - <template #caption>{{ $ts.inMb }}</template> + <template #caption>{{ i18n.ts.inMb }}</template> </FormInput> </FormSplit> </FormSection> @@ -109,8 +109,8 @@ <template #label>ServiceWorker</template> <FormSwitch v-model="enableServiceWorker" class="_formBlock"> - <template #label>{{ $ts.enableServiceworker }}</template> - <template #caption>{{ $ts.serviceworkerInfo }}</template> + <template #label>{{ i18n.ts.enableServiceworker }}</template> + <template #caption>{{ i18n.ts.serviceworkerInfo }}</template> </FormSwitch> <template v-if="enableServiceWorker"> @@ -142,8 +142,8 @@ </MkSpacer> </template> -<script lang="ts"> -import { defineComponent } from 'vue'; +<script lang="ts" setup> +import { } from 'vue'; import FormSwitch from '@/components/form/switch.vue'; import FormInput from '@/components/form/input.vue'; import FormTextarea from '@/components/form/textarea.vue'; @@ -154,119 +154,103 @@ import FormSuspense from '@/components/form/suspense.vue'; import * as os from '@/os'; import * as symbols from '@/symbols'; import { fetchInstance } from '@/instance'; +import { i18n } from '@/i18n'; -export default defineComponent({ - components: { - FormSwitch, - FormInput, - FormSuspense, - FormTextarea, - FormInfo, - FormSection, - FormSplit, - }, +let name: string | null = $ref(null); +let description: string | null = $ref(null); +let tosUrl: string | null = $ref(null); +let maintainerName: string | null = $ref(null); +let maintainerEmail: string | null = $ref(null); +let iconUrl: string | null = $ref(null); +let bannerUrl: string | null = $ref(null); +let backgroundImageUrl: string | null = $ref(null); +let themeColor: any = $ref(null); +let defaultLightTheme: any = $ref(null); +let defaultDarkTheme: any = $ref(null); +let enableLocalTimeline: boolean = $ref(false); +let enableGlobalTimeline: boolean = $ref(false); +let pinnedUsers: string = $ref(''); +let cacheRemoteFiles: boolean = $ref(false); +let localDriveCapacityMb: any = $ref(0); +let remoteDriveCapacityMb: any = $ref(0); +let enableRegistration: boolean = $ref(false); +let emailRequiredForSignup: boolean = $ref(false); +let enableServiceWorker: boolean = $ref(false); +let swPublicKey: any = $ref(null); +let swPrivateKey: any = $ref(null); +let deeplAuthKey: string = $ref(''); +let deeplIsPro: boolean = $ref(false); - emits: ['info'], +async function init() { + const meta = await os.api('admin/meta'); + name = meta.name; + description = meta.description; + tosUrl = meta.tosUrl; + iconUrl = meta.iconUrl; + bannerUrl = meta.bannerUrl; + backgroundImageUrl = meta.backgroundImageUrl; + themeColor = meta.themeColor; + defaultLightTheme = meta.defaultLightTheme; + defaultDarkTheme = meta.defaultDarkTheme; + maintainerName = meta.maintainerName; + maintainerEmail = meta.maintainerEmail; + enableLocalTimeline = !meta.disableLocalTimeline; + enableGlobalTimeline = !meta.disableGlobalTimeline; + pinnedUsers = meta.pinnedUsers.join('\n'); + cacheRemoteFiles = meta.cacheRemoteFiles; + localDriveCapacityMb = meta.driveCapacityPerLocalUserMb; + remoteDriveCapacityMb = meta.driveCapacityPerRemoteUserMb; + enableRegistration = !meta.disableRegistration; + emailRequiredForSignup = meta.emailRequiredForSignup; + enableServiceWorker = meta.enableServiceWorker; + swPublicKey = meta.swPublickey; + swPrivateKey = meta.swPrivateKey; + deeplAuthKey = meta.deeplAuthKey; + deeplIsPro = meta.deeplIsPro; +} - data() { - return { - [symbols.PAGE_INFO]: { - 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, - tosUrl: null as string | null, - maintainerName: null, - maintainerEmail: null, - iconUrl: null, - bannerUrl: null, - backgroundImageUrl: null, - themeColor: null, - defaultLightTheme: null, - defaultDarkTheme: null, - enableLocalTimeline: false, - enableGlobalTimeline: false, - pinnedUsers: '', - cacheRemoteFiles: false, - localDriveCapacityMb: 0, - remoteDriveCapacityMb: 0, - enableRegistration: false, - emailRequiredForSignup: false, - enableServiceWorker: false, - swPublicKey: null, - swPrivateKey: null, - deeplAuthKey: '', - deeplIsPro: false, - } - }, +function save() { + os.apiWithDialog('admin/update-meta', { + name, + description, + tosUrl, + iconUrl, + bannerUrl, + backgroundImageUrl, + themeColor: themeColor === '' ? null : themeColor, + defaultLightTheme: defaultLightTheme === '' ? null : defaultLightTheme, + defaultDarkTheme: defaultDarkTheme === '' ? null : defaultDarkTheme, + maintainerName, + maintainerEmail, + disableLocalTimeline: !enableLocalTimeline, + disableGlobalTimeline: !enableGlobalTimeline, + pinnedUsers: pinnedUsers.split('\n'), + cacheRemoteFiles, + localDriveCapacityMb: parseInt(localDriveCapacityMb, 10), + remoteDriveCapacityMb: parseInt(remoteDriveCapacityMb, 10), + disableRegistration: !enableRegistration, + emailRequiredForSignup, + enableServiceWorker, + swPublicKey, + swPrivateKey, + deeplAuthKey, + deeplIsPro, + }).then(() => { + fetchInstance(); + }); +} - methods: { - async init() { - const meta = await os.api('admin/meta'); - this.name = meta.name; - this.description = meta.description; - this.tosUrl = meta.tosUrl; - this.iconUrl = meta.iconUrl; - this.bannerUrl = meta.bannerUrl; - this.backgroundImageUrl = meta.backgroundImageUrl; - this.themeColor = meta.themeColor; - this.defaultLightTheme = meta.defaultLightTheme; - this.defaultDarkTheme = meta.defaultDarkTheme; - this.maintainerName = meta.maintainerName; - this.maintainerEmail = meta.maintainerEmail; - this.enableLocalTimeline = !meta.disableLocalTimeline; - this.enableGlobalTimeline = !meta.disableGlobalTimeline; - this.pinnedUsers = meta.pinnedUsers.join('\n'); - this.cacheRemoteFiles = meta.cacheRemoteFiles; - 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() { - os.apiWithDialog('admin/update-meta', { - name: this.name, - description: this.description, - tosUrl: this.tosUrl, - iconUrl: this.iconUrl, - bannerUrl: this.bannerUrl, - backgroundImageUrl: this.backgroundImageUrl, - themeColor: this.themeColor === '' ? null : this.themeColor, - defaultLightTheme: this.defaultLightTheme === '' ? null : this.defaultLightTheme, - defaultDarkTheme: this.defaultDarkTheme === '' ? null : this.defaultDarkTheme, - maintainerName: this.maintainerName, - maintainerEmail: this.maintainerEmail, - disableLocalTimeline: !this.enableLocalTimeline, - disableGlobalTimeline: !this.enableGlobalTimeline, - pinnedUsers: this.pinnedUsers.split('\n'), - cacheRemoteFiles: this.cacheRemoteFiles, - 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(); - }); - } +defineExpose({ + [symbols.PAGE_INFO]: { + title: i18n.ts.general, + icon: 'fas fa-cog', + bg: 'var(--bg)', + actions: [{ + asFullButton: true, + icon: 'fas fa-check', + text: i18n.ts.save, + handler: save, + }], } }); </script> |