diff options
| author | syuilo <4439005+syuilo@users.noreply.github.com> | 2025-09-01 13:41:40 +0900 |
|---|---|---|
| committer | syuilo <4439005+syuilo@users.noreply.github.com> | 2025-09-01 13:41:40 +0900 |
| commit | a3d78b2f0880ecbb6437e70d0183478f0c7bba74 (patch) | |
| tree | b0a07e7e2bfba9a66d99b2e2d03e3a30c822e7ec | |
| parent | refactor (diff) | |
| download | misskey-a3d78b2f0880ecbb6437e70d0183478f0c7bba74.tar.gz misskey-a3d78b2f0880ecbb6437e70d0183478f0c7bba74.tar.bz2 misskey-a3d78b2f0880ecbb6437e70d0183478f0c7bba74.zip | |
refactor
| -rw-r--r-- | packages/frontend/src/pages/admin-file.root.vue | 182 | ||||
| -rw-r--r-- | packages/frontend/src/pages/admin-file.vue | 182 |
2 files changed, 196 insertions, 168 deletions
diff --git a/packages/frontend/src/pages/admin-file.root.vue b/packages/frontend/src/pages/admin-file.root.vue new file mode 100644 index 0000000000..211d552d1b --- /dev/null +++ b/packages/frontend/src/pages/admin-file.root.vue @@ -0,0 +1,182 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> + <div v-if="file" class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> + <div v-if="tab === 'overview'" class="cxqhhsmd _gaps_m"> + <a class="thumbnail" :href="file.url" target="_blank"> + <MkDriveFileThumbnail class="thumbnail" :file="file" fit="contain"/> + </a> + <div> + <MkKeyValue :copy="file.type" oneline style="margin: 1em 0;"> + <template #key>MIME Type</template> + <template #value><span class="_monospace">{{ file.type }}</span></template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>Size</template> + <template #value><span class="_monospace">{{ bytes(file.size) }}</span></template> + </MkKeyValue> + <MkKeyValue :copy="file.id" oneline style="margin: 1em 0;"> + <template #key>ID</template> + <template #value><span class="_monospace">{{ file.id }}</span></template> + </MkKeyValue> + <MkKeyValue :copy="file.md5" oneline style="margin: 1em 0;"> + <template #key>MD5</template> + <template #value><span class="_monospace">{{ file.md5 }}</span></template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.createdAt }}</template> + <template #value><span class="_monospace"><MkTime :time="file.createdAt" mode="detail" style="display: block;"/></span></template> + </MkKeyValue> + </div> + <MkA v-if="file.user" class="user" :to="`/admin/user/${file.user.id}`"> + <MkUserCardMini :user="file.user"/> + </MkA> + + <div> + <MkSwitch :modelValue="isSensitive" @update:modelValue="toggleSensitive">{{ i18n.ts.sensitive }}</MkSwitch> + </div> + + <div> + <MkButton danger @click="del"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> + </div> + </div> + <div v-else-if="tab === 'usage' && info" class="_gaps_m"> + <MkTabs + v-model:tab="usageTab" + :tabs="[{ + key: 'note', + title: 'Note', + }, { + key: 'chat', + title: 'Chat', + }]" + /> + <XNotes v-if="usageTab === 'note'" :fileId="props.file.id"/> + <XChat v-else-if="usageTab === 'chat'" :fileId="props.file.id"/> + </div> + <div v-else-if="tab === 'ip' && info" class="_gaps_m"> + <MkInfo v-if="!iAmAdmin" warn>{{ i18n.ts.requireAdminForView }}</MkInfo> + <MkKeyValue v-if="info.requestIp" class="_monospace" :copy="info.requestIp" oneline> + <template #key>IP</template> + <template #value>{{ info.requestIp }}</template> + </MkKeyValue> + <FormSection v-if="info.requestHeaders"> + <template #label>Headers</template> + <MkKeyValue v-for="(v, k) in info.requestHeaders" :key="k" class="_monospace"> + <template #key>{{ k }}</template> + <template #value>{{ v }}</template> + </MkKeyValue> + </FormSection> + </div> + <div v-else-if="tab === 'raw'" class="_gaps_m"> + <MkObjectView v-if="info" tall :value="info"> + </MkObjectView> + </div> + </div> +</PageWithHeader> +</template> + +<script lang="ts" setup> +import { computed, defineAsyncComponent, ref } from 'vue'; +import * as Misskey from 'misskey-js'; +import MkButton from '@/components/MkButton.vue'; +import MkSwitch from '@/components/MkSwitch.vue'; +import MkObjectView from '@/components/MkObjectView.vue'; +import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue'; +import MkKeyValue from '@/components/MkKeyValue.vue'; +import FormSection from '@/components/form/section.vue'; +import MkUserCardMini from '@/components/MkUserCardMini.vue'; +import MkInfo from '@/components/MkInfo.vue'; +import bytes from '@/filters/bytes.js'; +import * as os from '@/os.js'; +import { i18n } from '@/i18n.js'; +import { iAmAdmin, iAmModerator } from '@/i.js'; +import MkTabs from '@/components/MkTabs.vue'; + +const props = defineProps<{ + file: Misskey.entities.DriveFile, + info: Misskey.entities.AdminDriveShowFileResponse, +}>(); + +const tab = ref('overview'); +const isSensitive = ref(props.file.isSensitive); +const usageTab = ref<'note' | 'chat'>('note'); +const XNotes = defineAsyncComponent(() => import('./drive.file.notes.vue')); +const XChat = defineAsyncComponent(() => import('./admin-file.chat.vue')); + +async function del() { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.tsx.removeAreYouSure({ x: props.file.name }), + }); + if (canceled) return; + + os.apiWithDialog('drive/files/delete', { + fileId: props.file.id, + }); +} + +async function toggleSensitive() { + const { canceled } = await os.confirm({ + type: 'warning', + text: isSensitive.value ? i18n.ts.unmarkAsSensitiveConfirm : i18n.ts.markAsSensitiveConfirm, + }); + + if (canceled) return; + isSensitive.value = !isSensitive.value; + + os.apiWithDialog('drive/files/update', { + fileId: props.file.id, + isSensitive: !props.file.isSensitive, + }); +} + +const headerActions = computed(() => [{ + text: i18n.ts.openInNewTab, + icon: 'ti ti-external-link', + handler: () => { + window.open(props.file.url, '_blank', 'noopener'); + }, +}]); + +const headerTabs = computed(() => [{ + key: 'overview', + title: i18n.ts.overview, + icon: 'ti ti-info-circle', +}, iAmModerator ? { + key: 'usage', + title: i18n.ts._fileViewer.usage, + icon: 'ti ti-plus', +} : null, iAmModerator ? { + key: 'ip', + title: 'IP', + icon: 'ti ti-password', +} : null, { + key: 'raw', + title: 'Raw data', + icon: 'ti ti-code', +}].filter(x => x != null)); +</script> + +<style lang="scss" scoped> +.cxqhhsmd { + > .thumbnail { + display: block; + + > .thumbnail { + height: 300px; + max-width: 100%; + } + } + + > .user { + &:hover { + text-decoration: none; + } + } +} +</style> diff --git a/packages/frontend/src/pages/admin-file.vue b/packages/frontend/src/pages/admin-file.vue index 052829ffe2..63d3640f9c 100644 --- a/packages/frontend/src/pages/admin-file.vue +++ b/packages/frontend/src/pages/admin-file.vue @@ -4,197 +4,43 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> - <div v-if="file" class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> - <div v-if="tab === 'overview'" class="cxqhhsmd _gaps_m"> - <a class="thumbnail" :href="file.url" target="_blank"> - <MkDriveFileThumbnail class="thumbnail" :file="file" fit="contain"/> - </a> - <div> - <MkKeyValue :copy="file.type" oneline style="margin: 1em 0;"> - <template #key>MIME Type</template> - <template #value><span class="_monospace">{{ file.type }}</span></template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>Size</template> - <template #value><span class="_monospace">{{ bytes(file.size) }}</span></template> - </MkKeyValue> - <MkKeyValue :copy="file.id" oneline style="margin: 1em 0;"> - <template #key>ID</template> - <template #value><span class="_monospace">{{ file.id }}</span></template> - </MkKeyValue> - <MkKeyValue :copy="file.md5" oneline style="margin: 1em 0;"> - <template #key>MD5</template> - <template #value><span class="_monospace">{{ file.md5 }}</span></template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.createdAt }}</template> - <template #value><span class="_monospace"><MkTime :time="file.createdAt" mode="detail" style="display: block;"/></span></template> - </MkKeyValue> - </div> - <MkA v-if="file.user" class="user" :to="`/admin/user/${file.user.id}`"> - <MkUserCardMini :user="file.user"/> - </MkA> - - <div> - <MkSwitch :modelValue="isSensitive" @update:modelValue="toggleSensitive">{{ i18n.ts.sensitive }}</MkSwitch> - </div> - - <div> - <MkButton danger @click="del"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> - </div> - </div> - <div v-else-if="tab === 'usage' && info" class="_gaps_m"> - <MkTabs - v-model:tab="usageTab" - :tabs="[{ - key: 'note', - title: 'Note', - }, { - key: 'chat', - title: 'Chat', - }]" - /> - <XNotes v-if="usageTab === 'note'" :fileId="fileId"/> - <XChat v-else-if="usageTab === 'chat'" :fileId="fileId"/> - </div> - <div v-else-if="tab === 'ip' && info" class="_gaps_m"> - <MkInfo v-if="!iAmAdmin" warn>{{ i18n.ts.requireAdminForView }}</MkInfo> - <MkKeyValue v-if="info.requestIp" class="_monospace" :copy="info.requestIp" oneline> - <template #key>IP</template> - <template #value>{{ info.requestIp }}</template> - </MkKeyValue> - <FormSection v-if="info.requestHeaders"> - <template #label>Headers</template> - <MkKeyValue v-for="(v, k) in info.requestHeaders" :key="k" class="_monospace"> - <template #key>{{ k }}</template> - <template #value>{{ v }}</template> - </MkKeyValue> - </FormSection> - </div> - <div v-else-if="tab === 'raw'" class="_gaps_m"> - <MkObjectView v-if="info" tall :value="info"> - </MkObjectView> - </div> - </div> -</PageWithHeader> +<XRoot v-if="file != null && info != null" :file="file" :info="info"/> +<div v-else-if="error != null">Error: {{ error }}</div> +<MkLoading v-else/> </template> <script lang="ts" setup> -import { computed, defineAsyncComponent, ref } from 'vue'; +import { ref } from 'vue'; import * as Misskey from 'misskey-js'; -import MkButton from '@/components/MkButton.vue'; -import MkSwitch from '@/components/MkSwitch.vue'; -import MkObjectView from '@/components/MkObjectView.vue'; -import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue'; -import MkKeyValue from '@/components/MkKeyValue.vue'; -import FormSection from '@/components/form/section.vue'; -import MkUserCardMini from '@/components/MkUserCardMini.vue'; -import MkInfo from '@/components/MkInfo.vue'; -import bytes from '@/filters/bytes.js'; -import * as os from '@/os.js'; +import XRoot from './admin-file.root.vue'; import { misskeyApi } from '@/utility/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; -import { iAmAdmin, iAmModerator } from '@/i.js'; -import MkTabs from '@/components/MkTabs.vue'; -const tab = ref('overview'); const file = ref<Misskey.entities.DriveFile | null>(null); const info = ref<Misskey.entities.AdminDriveShowFileResponse | null>(null); -const isSensitive = ref<boolean>(false); -const usageTab = ref<'note' | 'chat'>('note'); -const XNotes = defineAsyncComponent(() => import('./drive.file.notes.vue')); -const XChat = defineAsyncComponent(() => import('./admin-file.chat.vue')); + +const error = ref<string | null>(null); const props = defineProps<{ fileId: string, }>(); async function _fetch_() { - file.value = await misskeyApi('drive/files/show', { fileId: props.fileId }); + try { + file.value = await misskeyApi('drive/files/show', { fileId: props.fileId }); + } catch (err: any) { + error.value = err.message + ' ' + err.id; + return; + } + info.value = await misskeyApi('admin/drive/show-file', { fileId: props.fileId }); - isSensitive.value = file.value.isSensitive; } _fetch_(); -async function del() { - const { canceled } = await os.confirm({ - type: 'warning', - text: i18n.tsx.removeAreYouSure({ x: file.value.name }), - }); - if (canceled) return; - - os.apiWithDialog('drive/files/delete', { - fileId: file.value.id, - }); -} - -async function toggleSensitive() { - if (!file.value) return; - - const { canceled } = await os.confirm({ - type: 'warning', - text: isSensitive.value ? i18n.ts.unmarkAsSensitiveConfirm : i18n.ts.markAsSensitiveConfirm, - }); - - if (canceled) return; - isSensitive.value = !isSensitive.value; - - os.apiWithDialog('drive/files/update', { - fileId: file.value.id, - isSensitive: !file.value.isSensitive, - }); -} - -const headerActions = computed(() => [{ - text: i18n.ts.openInNewTab, - icon: 'ti ti-external-link', - handler: () => { - window.open(file.value.url, '_blank', 'noopener'); - }, -}]); - -const headerTabs = computed(() => [{ - key: 'overview', - title: i18n.ts.overview, - icon: 'ti ti-info-circle', -}, iAmModerator ? { - key: 'usage', - title: i18n.ts._fileViewer.usage, - icon: 'ti ti-plus', -} : null, iAmModerator ? { - key: 'ip', - title: 'IP', - icon: 'ti ti-password', -} : null, { - key: 'raw', - title: 'Raw data', - icon: 'ti ti-code', -}].filter(x => x != null)); - definePage(() => ({ title: file.value ? `${i18n.ts.file}: ${file.value.name}` : i18n.ts.file, icon: 'ti ti-file', })); </script> - -<style lang="scss" scoped> -.cxqhhsmd { - > .thumbnail { - display: block; - - > .thumbnail { - height: 300px; - max-width: 100%; - } - } - - > .user { - &:hover { - text-decoration: none; - } - } -} -</style> |