diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9efc0922c..edfe09391 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,7 +13,7 @@ You should also include the user name that made the change.
### Improvements
- Server: Add rate limit to i/notifications @tamaina
-- Client: Improve files page of control panel @syuilo
+- Client: Improve control panel @syuilo
- Client: Show warning in control panel when there is an unresolved abuse report @syuilo
- Improve player detection in URL preview @mei23
- Add Badge Image to Push Notification #8012 @tamaina
diff --git a/packages/backend/src/server/api/endpoints/admin/drive/files.ts b/packages/backend/src/server/api/endpoints/admin/drive/files.ts
index 119c4db19..ba32aac43 100644
--- a/packages/backend/src/server/api/endpoints/admin/drive/files.ts
+++ b/packages/backend/src/server/api/endpoints/admin/drive/files.ts
@@ -1,5 +1,5 @@
-import define from '../../../define.js';
import { DriveFiles } from '@/models/index.js';
+import define from '../../../define.js';
import { makePaginationQuery } from '../../../common/make-pagination-query.js';
export const meta = {
@@ -25,8 +25,9 @@ export const paramDef = {
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
sinceId: { type: 'string', format: 'misskey:id' },
untilId: { type: 'string', format: 'misskey:id' },
+ userId: { type: 'string', format: 'misskey:id', nullable: true },
type: { type: 'string', nullable: true, pattern: /^[a-zA-Z0-9\/\-*]+$/.toString().slice(1, -1) },
- origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: "local" },
+ origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: 'local' },
hostname: {
type: 'string',
nullable: true,
@@ -41,14 +42,18 @@ export const paramDef = {
export default define(meta, paramDef, async (ps, me) => {
const query = makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId);
- if (ps.origin === 'local') {
- query.andWhere('file.userHost IS NULL');
- } else if (ps.origin === 'remote') {
- query.andWhere('file.userHost IS NOT NULL');
- }
+ if (ps.userId) {
+ query.andWhere('file.userId = :userId', { userId: ps.userId });
+ } else {
+ if (ps.origin === 'local') {
+ query.andWhere('file.userHost IS NULL');
+ } else if (ps.origin === 'remote') {
+ query.andWhere('file.userHost IS NOT NULL');
+ }
- if (ps.hostname) {
- query.andWhere('file.userHost = :hostname', { hostname: ps.hostname });
+ if (ps.hostname) {
+ query.andWhere('file.userHost = :hostname', { hostname: ps.hostname });
+ }
}
if (ps.type) {
diff --git a/packages/client/src/components/file-list-for-admin.vue b/packages/client/src/components/file-list-for-admin.vue
new file mode 100644
index 000000000..992b41a4f
--- /dev/null
+++ b/packages/client/src/components/file-list-for-admin.vue
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/client/src/pages/admin/files.vue b/packages/client/src/pages/admin/files.vue
index 18bf4f9a8..1d037fc4e 100644
--- a/packages/client/src/pages/admin/files.vue
+++ b/packages/client/src/pages/admin/files.vue
@@ -16,32 +16,15 @@
{{ $ts.host }}
-
@@ -56,9 +39,7 @@ import XHeader from './_header_.vue';
import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/form/input.vue';
import MkSelect from '@/components/form/select.vue';
-import MkPagination from '@/components/ui/pagination.vue';
-import MkContainer from '@/components/ui/container.vue';
-import MkDriveFileThumbnail from '@/components/drive-file-thumbnail.vue';
+import MkFileListForAdmin from '@/components/file-list-for-admin.vue';
import bytes from '@/filters/bytes';
import * as os from '@/os';
import { i18n } from '@/i18n';
@@ -67,12 +48,14 @@ import { definePageMetadata } from '@/scripts/page-metadata';
let origin = $ref('local');
let type = $ref(null);
let searchHost = $ref('');
+let userId = $ref('');
let viewMode = $ref('grid');
const pagination = {
endpoint: 'admin/drive/files' as const,
limit: 10,
params: computed(() => ({
type: (type && type !== '') ? type : null,
+ userId: (userId && userId !== '') ? userId : null,
origin: origin,
hostname: (searchHost && searchHost !== '') ? searchHost : null,
})),
@@ -134,54 +117,5 @@ definePageMetadata(computed(() => ({
diff --git a/packages/client/src/pages/user-info.vue b/packages/client/src/pages/user-info.vue
index cc187b9df..b4c4aedfc 100644
--- a/packages/client/src/pages/user-info.vue
+++ b/packages/client/src/pages/user-info.vue
@@ -76,6 +76,9 @@
+
+
+
@@ -105,6 +108,7 @@ import FormButton from '@/components/ui/button.vue';
import MkKeyValue from '@/components/key-value.vue';
import MkSelect from '@/components/form/select.vue';
import FormSuspense from '@/components/form/suspense.vue';
+import MkFileListForAdmin from '@/components/file-list-for-admin.vue';
import * as os from '@/os';
import number from '@/filters/number';
import bytes from '@/filters/bytes';
@@ -127,6 +131,13 @@ let ap = $ref(null);
let moderator = $ref(false);
let silenced = $ref(false);
let suspended = $ref(false);
+const filesPagination = {
+ endpoint: 'admin/drive/files' as const,
+ limit: 10,
+ params: computed(() => ({
+ userId: props.userId,
+ })),
+};
function createFetcher() {
if (iAmModerator) {
@@ -244,7 +255,11 @@ const headerTabs = $computed(() => [{
key: 'chart',
title: i18n.ts.charts,
icon: 'fas fa-chart-simple',
-}, {
+}, iAmModerator ? {
+ key: 'files',
+ title: i18n.ts.files,
+ icon: 'fas fa-cloud',
+} : null, {
key: 'ap',
title: 'AP',
icon: 'fas fa-share-alt',
@@ -252,7 +267,7 @@ const headerTabs = $computed(() => [{
key: 'raw',
title: 'Raw data',
icon: 'fas fa-code',
-}]);
+}].filter(x => x != null));
definePageMetadata(computed(() => ({
title: user ? acct(user) : i18n.ts.userInfo,