summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorsyuilo <syuilotan@yahoo.co.jp>2019-01-19 09:50:38 +0900
committersyuilo <syuilotan@yahoo.co.jp>2019-01-19 09:50:38 +0900
commitd2a7c56149b11948ba33c518d04060c26d3e6e94 (patch)
tree9f8bcd1ee10e862339c3d36bf40abb599cf9491c /src
parentUpdate .eslintrc (diff)
downloadmisskey-d2a7c56149b11948ba33c518d04060c26d3e6e94.tar.gz
misskey-d2a7c56149b11948ba33c518d04060c26d3e6e94.tar.bz2
misskey-d2a7c56149b11948ba33c518d04060c26d3e6e94.zip
Improve drive file operation
Resolve #3789 Resolve #3790
Diffstat (limited to 'src')
-rw-r--r--src/client/app/admin/views/drive.vue86
-rw-r--r--src/server/api/endpoints/admin/drive/show-file.ts28
-rw-r--r--src/server/api/endpoints/drive/files/show.ts55
3 files changed, 160 insertions, 9 deletions
diff --git a/src/client/app/admin/views/drive.vue b/src/client/app/admin/views/drive.vue
index a5ce2013ea..6c2e176860 100644
--- a/src/client/app/admin/views/drive.vue
+++ b/src/client/app/admin/views/drive.vue
@@ -1,6 +1,22 @@
<template>
<div class="pwnqwyet">
<ui-card>
+ <div slot="title"><fa :icon="faTerminal"/> {{ $t('operation') }}</div>
+ <section class="fit-top">
+ <ui-input v-model="target" type="text">
+ <span>{{ $t('fileid-or-url') }}</span>
+ </ui-input>
+ <ui-horizon-group>
+ <ui-button @click="findAndToggleSensitive(true)"><fa :icon="faEyeSlash"/> {{ $t('mark-as-sensitive') }}</ui-button>
+ <ui-button @click="findAndToggleSensitive(false)"><fa :icon="faEye"/> {{ $t('unmark-as-sensitive') }}</ui-button>
+ </ui-horizon-group>
+ <ui-button @click="findAndDel()"><fa :icon="faTrashAlt"/> {{ $t('delete') }}</ui-button>
+ <ui-button @click="show()"><fa :icon="faSearch"/> {{ $t('lookup') }}</ui-button>
+ <ui-textarea v-if="file" :value="file | json5" readonly tall style="margin-top:16px;"></ui-textarea>
+ </section>
+ </ui-card>
+
+ <ui-card>
<div slot="title"><fa :icon="faCloud"/> {{ $t('@.drive') }}</div>
<section class="fit-top">
<ui-horizon-group inputs>
@@ -57,7 +73,7 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../../i18n';
-import { faCloud } from '@fortawesome/free-solid-svg-icons';
+import { faCloud, faTerminal, faSearch } from '@fortawesome/free-solid-svg-icons';
import { faTrashAlt, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
export default Vue.extend({
@@ -65,13 +81,15 @@ export default Vue.extend({
data() {
return {
+ file: null,
+ target: null,
sort: '+createdAt',
origin: 'combined',
limit: 10,
offset: 0,
files: [],
existMore: false,
- faCloud, faTrashAlt, faEye, faEyeSlash
+ faCloud, faTrashAlt, faEye, faEyeSlash, faTerminal, faSearch
};
},
@@ -94,6 +112,24 @@ export default Vue.extend({
},
methods: {
+ async fetchFile() {
+ try {
+ return await this.$root.api('drive/files/show', this.target.startsWith('http') ? { url: this.target } : { fileId: this.target });
+ } catch (e) {
+ if (e == 'file-not-found') {
+ this.$root.dialog({
+ type: 'error',
+ text: this.$t('file-not-found')
+ });
+ } else {
+ this.$root.dialog({
+ type: 'error',
+ text: e.toString()
+ });
+ }
+ }
+ },
+
fetch() {
this.$root.api('admin/drive/files', {
origin: this.origin,
@@ -147,6 +183,52 @@ export default Vue.extend({
file.isSensitive = !file.isSensitive;
},
+
+ async show() {
+ const file = await this.fetchFile();
+ this.$root.api('admin/drive/show-file', { fileId: file.id }).then(info => {
+ this.file = info;
+ });
+ },
+
+ async findAndToggleSensitive(sensitive) {
+ const process = async () => {
+ const file = await this.fetchFile();
+ await this.$root.api('drive/files/update', {
+ fileId: file.id,
+ isSensitive: sensitive
+ });
+ this.$root.dialog({
+ type: 'success',
+ text: sensitive ? this.$t('marked-as-sensitive') : this.$t('unmarked-as-sensitive')
+ });
+ };
+
+ await process().catch(e => {
+ this.$root.dialog({
+ type: 'error',
+ text: e.toString()
+ });
+ });
+ },
+
+ async findAndDel() {
+ const process = async () => {
+ const file = await this.fetchFile();
+ await this.$root.api('drive/files/delete', { fileId: file.id });
+ this.$root.dialog({
+ type: 'success',
+ text: this.$t('deleted')
+ });
+ };
+
+ await process().catch(e => {
+ this.$root.dialog({
+ type: 'error',
+ text: e.toString()
+ });
+ });
+ },
}
});
</script>
diff --git a/src/server/api/endpoints/admin/drive/show-file.ts b/src/server/api/endpoints/admin/drive/show-file.ts
new file mode 100644
index 0000000000..6dfab19643
--- /dev/null
+++ b/src/server/api/endpoints/admin/drive/show-file.ts
@@ -0,0 +1,28 @@
+import $ from 'cafy';
+import ID, { transform } from '../../../../../misc/cafy-id';
+import define from '../../../define';
+import DriveFile from '../../../../../models/drive-file';
+
+export const meta = {
+ requireCredential: true,
+ requireModerator: true,
+
+ params: {
+ fileId: {
+ validator: $.type(ID),
+ transform: transform,
+ },
+ }
+};
+
+export default define(meta, (ps, me) => new Promise(async (res, rej) => {
+ const file = await DriveFile.findOne({
+ _id: ps.fileId
+ });
+
+ if (file == null) {
+ return rej('file not found');
+ }
+
+ res(file);
+}));
diff --git a/src/server/api/endpoints/drive/files/show.ts b/src/server/api/endpoints/drive/files/show.ts
index 95c3323fbb..57a22d1178 100644
--- a/src/server/api/endpoints/drive/files/show.ts
+++ b/src/server/api/endpoints/drive/files/show.ts
@@ -1,6 +1,9 @@
-import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
-import DriveFile, { pack } from '../../../../../models/drive-file';
+import $ from 'cafy';
+import * as mongo from 'mongodb';
+import ID, { transform } from '../../../../../misc/cafy-id';
+import DriveFile, { pack, IDriveFile } from '../../../../../models/drive-file';
import define from '../../../define';
+import config from '../../../../../config';
export const meta = {
stability: 'stable',
@@ -16,24 +19,62 @@ export const meta = {
params: {
fileId: {
- validator: $.type(ID),
+ validator: $.type(ID).optional,
transform: transform,
desc: {
'ja-JP': '対象のファイルID',
'en-US': 'Target file ID'
}
+ },
+
+ url: {
+ validator: $.str.optional,
+ desc: {
+ 'ja-JP': '対象のファイルのURL',
+ 'en-US': 'Target file URL'
+ }
}
}
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
- // Fetch file
- const file = await DriveFile
- .findOne({
+ let file: IDriveFile;
+
+ if (ps.fileId) {
+ file = await DriveFile.findOne({
_id: ps.fileId,
- 'metadata.userId': user._id,
'metadata.deletedAt': { $exists: false }
});
+ } else if (ps.url) {
+ const isInternalStorageUrl = ps.url.startsWith(config.drive_url);
+ if (isInternalStorageUrl) {
+ // Extract file if from url
+ // e.g.
+ // http://misskey.local/files/foo?original=bar --> foo
+ const fileId = new mongo.ObjectID(ps.url.replace(config.drive_url, '').replace(/\?(.*)$/, '').replace(/\//g, ''));
+ file = await DriveFile.findOne({
+ _id: fileId,
+ 'metadata.deletedAt': { $exists: false }
+ });
+ } else {
+ file = await DriveFile.findOne({
+ $or: [{
+ 'metadata.url': ps.url
+ }, {
+ 'metadata.webpublicUrl': ps.url
+ }, {
+ 'metadata.thumbnailUrl': ps.url
+ }],
+ 'metadata.deletedAt': { $exists: false }
+ });
+ }
+ } else {
+ return rej('fileId or url required');
+ }
+
+ if (!user.isAdmin && !user.isModerator && !file.metadata.userId.equals(user._id)) {
+ return rej('access denied');
+ }
if (file === null) {
return rej('file-not-found');