summaryrefslogtreecommitdiff
path: root/src/server/api/endpoints/drive
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/api/endpoints/drive')
-rw-r--r--src/server/api/endpoints/drive/files.ts73
-rw-r--r--src/server/api/endpoints/drive/files/create.ts51
-rw-r--r--src/server/api/endpoints/drive/files/find.ts34
-rw-r--r--src/server/api/endpoints/drive/files/show.ts36
-rw-r--r--src/server/api/endpoints/drive/files/update.ts75
-rw-r--r--src/server/api/endpoints/drive/files/upload_from_url.ts26
-rw-r--r--src/server/api/endpoints/drive/folders.ts66
-rw-r--r--src/server/api/endpoints/drive/folders/create.ts55
-rw-r--r--src/server/api/endpoints/drive/folders/find.ts33
-rw-r--r--src/server/api/endpoints/drive/folders/show.ts34
-rw-r--r--src/server/api/endpoints/drive/folders/update.ts99
-rw-r--r--src/server/api/endpoints/drive/stream.ts67
12 files changed, 649 insertions, 0 deletions
diff --git a/src/server/api/endpoints/drive/files.ts b/src/server/api/endpoints/drive/files.ts
new file mode 100644
index 0000000000..89915331ea
--- /dev/null
+++ b/src/server/api/endpoints/drive/files.ts
@@ -0,0 +1,73 @@
+/**
+ * Module dependencies
+ */
+import $ from 'cafy';
+import DriveFile, { pack } from '../../models/drive-file';
+
+/**
+ * Get drive files
+ *
+ * @param {any} params
+ * @param {any} user
+ * @param {any} app
+ * @return {Promise<any>}
+ */
+module.exports = async (params, user, app) => {
+ // Get 'limit' parameter
+ const [limit = 10, limitErr] = $(params.limit).optional.number().range(1, 100).$;
+ if (limitErr) throw 'invalid limit param';
+
+ // Get 'since_id' parameter
+ const [sinceId, sinceIdErr] = $(params.since_id).optional.id().$;
+ if (sinceIdErr) throw 'invalid since_id param';
+
+ // Get 'until_id' parameter
+ const [untilId, untilIdErr] = $(params.until_id).optional.id().$;
+ if (untilIdErr) throw 'invalid until_id param';
+
+ // Check if both of since_id and until_id is specified
+ if (sinceId && untilId) {
+ throw 'cannot set since_id and until_id';
+ }
+
+ // Get 'folder_id' parameter
+ const [folderId = null, folderIdErr] = $(params.folder_id).optional.nullable.id().$;
+ if (folderIdErr) throw 'invalid folder_id param';
+
+ // Get 'type' parameter
+ const [type, typeErr] = $(params.type).optional.string().match(/^[a-zA-Z\/\-\*]+$/).$;
+ if (typeErr) throw 'invalid type param';
+
+ // Construct query
+ const sort = {
+ _id: -1
+ };
+ const query = {
+ 'metadata.user_id': user._id,
+ 'metadata.folder_id': folderId
+ } as any;
+ if (sinceId) {
+ sort._id = 1;
+ query._id = {
+ $gt: sinceId
+ };
+ } else if (untilId) {
+ query._id = {
+ $lt: untilId
+ };
+ }
+ if (type) {
+ query.contentType = new RegExp(`^${type.replace(/\*/g, '.+?')}$`);
+ }
+
+ // Issue query
+ const files = await DriveFile
+ .find(query, {
+ limit: limit,
+ sort: sort
+ });
+
+ // Serialize
+ const _files = await Promise.all(files.map(file => pack(file)));
+ return _files;
+};
diff --git a/src/server/api/endpoints/drive/files/create.ts b/src/server/api/endpoints/drive/files/create.ts
new file mode 100644
index 0000000000..db801b61fe
--- /dev/null
+++ b/src/server/api/endpoints/drive/files/create.ts
@@ -0,0 +1,51 @@
+/**
+ * Module dependencies
+ */
+import $ from 'cafy';
+import { validateFileName, pack } from '../../../models/drive-file';
+import create from '../../../common/drive/add-file';
+
+/**
+ * Create a file
+ *
+ * @param {any} file
+ * @param {any} params
+ * @param {any} user
+ * @return {Promise<any>}
+ */
+module.exports = async (file, params, user): Promise<any> => {
+ if (file == null) {
+ throw 'file is required';
+ }
+
+ // Get 'name' parameter
+ let name = file.originalname;
+ if (name !== undefined && name !== null) {
+ name = name.trim();
+ if (name.length === 0) {
+ name = null;
+ } else if (name === 'blob') {
+ name = null;
+ } else if (!validateFileName(name)) {
+ throw 'invalid name';
+ }
+ } else {
+ name = null;
+ }
+
+ // Get 'folder_id' parameter
+ const [folderId = null, folderIdErr] = $(params.folder_id).optional.nullable.id().$;
+ if (folderIdErr) throw 'invalid folder_id param';
+
+ try {
+ // Create file
+ const driveFile = await create(user, file.path, name, null, folderId);
+
+ // Serialize
+ return pack(driveFile);
+ } catch (e) {
+ console.error(e);
+
+ throw e;
+ }
+};
diff --git a/src/server/api/endpoints/drive/files/find.ts b/src/server/api/endpoints/drive/files/find.ts
new file mode 100644
index 0000000000..e026afe936
--- /dev/null
+++ b/src/server/api/endpoints/drive/files/find.ts
@@ -0,0 +1,34 @@
+/**
+ * Module dependencies
+ */
+import $ from 'cafy';
+import DriveFile, { pack } from '../../../models/drive-file';
+
+/**
+ * Find a file(s)
+ *
+ * @param {any} params
+ * @param {any} user
+ * @return {Promise<any>}
+ */
+module.exports = (params, user) => new Promise(async (res, rej) => {
+ // Get 'name' parameter
+ const [name, nameErr] = $(params.name).string().$;
+ if (nameErr) return rej('invalid name param');
+
+ // Get 'folder_id' parameter
+ const [folderId = null, folderIdErr] = $(params.folder_id).optional.nullable.id().$;
+ if (folderIdErr) return rej('invalid folder_id param');
+
+ // Issue query
+ const files = await DriveFile
+ .find({
+ filename: name,
+ 'metadata.user_id': user._id,
+ 'metadata.folder_id': folderId
+ });
+
+ // Serialize
+ res(await Promise.all(files.map(async file =>
+ await pack(file))));
+});
diff --git a/src/server/api/endpoints/drive/files/show.ts b/src/server/api/endpoints/drive/files/show.ts
new file mode 100644
index 0000000000..21664f7ba4
--- /dev/null
+++ b/src/server/api/endpoints/drive/files/show.ts
@@ -0,0 +1,36 @@
+/**
+ * Module dependencies
+ */
+import $ from 'cafy';
+import DriveFile, { pack } from '../../../models/drive-file';
+
+/**
+ * Show a file
+ *
+ * @param {any} params
+ * @param {any} user
+ * @return {Promise<any>}
+ */
+module.exports = async (params, user) => {
+ // Get 'file_id' parameter
+ const [fileId, fileIdErr] = $(params.file_id).id().$;
+ if (fileIdErr) throw 'invalid file_id param';
+
+ // Fetch file
+ const file = await DriveFile
+ .findOne({
+ _id: fileId,
+ 'metadata.user_id': user._id
+ });
+
+ if (file === null) {
+ throw 'file-not-found';
+ }
+
+ // Serialize
+ const _file = await pack(file, {
+ detail: true
+ });
+
+ return _file;
+};
diff --git a/src/server/api/endpoints/drive/files/update.ts b/src/server/api/endpoints/drive/files/update.ts
new file mode 100644
index 0000000000..83da462113
--- /dev/null
+++ b/src/server/api/endpoints/drive/files/update.ts
@@ -0,0 +1,75 @@
+/**
+ * Module dependencies
+ */
+import $ from 'cafy';
+import DriveFolder from '../../../models/drive-folder';
+import DriveFile, { validateFileName, pack } from '../../../models/drive-file';
+import { publishDriveStream } from '../../../event';
+
+/**
+ * Update a file
+ *
+ * @param {any} params
+ * @param {any} user
+ * @return {Promise<any>}
+ */
+module.exports = (params, user) => new Promise(async (res, rej) => {
+ // Get 'file_id' parameter
+ const [fileId, fileIdErr] = $(params.file_id).id().$;
+ if (fileIdErr) return rej('invalid file_id param');
+
+ // Fetch file
+ const file = await DriveFile
+ .findOne({
+ _id: fileId,
+ 'metadata.user_id': user._id
+ });
+
+ if (file === null) {
+ return rej('file-not-found');
+ }
+
+ // Get 'name' parameter
+ const [name, nameErr] = $(params.name).optional.string().pipe(validateFileName).$;
+ if (nameErr) return rej('invalid name param');
+ if (name) file.filename = name;
+
+ // Get 'folder_id' parameter
+ const [folderId, folderIdErr] = $(params.folder_id).optional.nullable.id().$;
+ if (folderIdErr) return rej('invalid folder_id param');
+
+ if (folderId !== undefined) {
+ if (folderId === null) {
+ file.metadata.folder_id = null;
+ } else {
+ // Fetch folder
+ const folder = await DriveFolder
+ .findOne({
+ _id: folderId,
+ user_id: user._id
+ });
+
+ if (folder === null) {
+ return rej('folder-not-found');
+ }
+
+ file.metadata.folder_id = folder._id;
+ }
+ }
+
+ await DriveFile.update(file._id, {
+ $set: {
+ filename: file.filename,
+ 'metadata.folder_id': file.metadata.folder_id
+ }
+ });
+
+ // Serialize
+ const fileObj = await pack(file);
+
+ // Response
+ res(fileObj);
+
+ // Publish file_updated event
+ publishDriveStream(user._id, 'file_updated', fileObj);
+});
diff --git a/src/server/api/endpoints/drive/files/upload_from_url.ts b/src/server/api/endpoints/drive/files/upload_from_url.ts
new file mode 100644
index 0000000000..346633c616
--- /dev/null
+++ b/src/server/api/endpoints/drive/files/upload_from_url.ts
@@ -0,0 +1,26 @@
+/**
+ * Module dependencies
+ */
+import $ from 'cafy';
+import { pack } from '../../../models/drive-file';
+import uploadFromUrl from '../../../common/drive/upload_from_url';
+
+/**
+ * Create a file from a URL
+ *
+ * @param {any} params
+ * @param {any} user
+ * @return {Promise<any>}
+ */
+module.exports = async (params, user): Promise<any> => {
+ // Get 'url' parameter
+ // TODO: Validate this url
+ const [url, urlErr] = $(params.url).string().$;
+ if (urlErr) throw 'invalid url param';
+
+ // Get 'folder_id' parameter
+ const [folderId = null, folderIdErr] = $(params.folder_id).optional.nullable.id().$;
+ if (folderIdErr) throw 'invalid folder_id param';
+
+ return pack(await uploadFromUrl(url, user, folderId));
+};
diff --git a/src/server/api/endpoints/drive/folders.ts b/src/server/api/endpoints/drive/folders.ts
new file mode 100644
index 0000000000..428bde3507
--- /dev/null
+++ b/src/server/api/endpoints/drive/folders.ts
@@ -0,0 +1,66 @@
+/**
+ * Module dependencies
+ */
+import $ from 'cafy';
+import DriveFolder, { pack } from '../../models/drive-folder';
+
+/**
+ * Get drive folders
+ *
+ * @param {any} params
+ * @param {any} user
+ * @param {any} app
+ * @return {Promise<any>}
+ */
+module.exports = (params, user, app) => new Promise(async (res, rej) => {
+ // Get 'limit' parameter
+ const [limit = 10, limitErr] = $(params.limit).optional.number().range(1, 100).$;
+ if (limitErr) return rej('invalid limit param');
+
+ // Get 'since_id' parameter
+ const [sinceId, sinceIdErr] = $(params.since_id).optional.id().$;
+ if (sinceIdErr) return rej('invalid since_id param');
+
+ // Get 'until_id' parameter
+ const [untilId, untilIdErr] = $(params.until_id).optional.id().$;
+ if (untilIdErr) return rej('invalid until_id param');
+
+ // Check if both of since_id and until_id is specified
+ if (sinceId && untilId) {
+ return rej('cannot set since_id and until_id');
+ }
+
+ // Get 'folder_id' parameter
+ const [folderId = null, folderIdErr] = $(params.folder_id).optional.nullable.id().$;
+ if (folderIdErr) return rej('invalid folder_id param');
+
+ // Construct query
+ const sort = {
+ _id: -1
+ };
+ const query = {
+ user_id: user._id,
+ parent_id: folderId
+ } as any;
+ if (sinceId) {
+ sort._id = 1;
+ query._id = {
+ $gt: sinceId
+ };
+ } else if (untilId) {
+ query._id = {
+ $lt: untilId
+ };
+ }
+
+ // Issue query
+ const folders = await DriveFolder
+ .find(query, {
+ limit: limit,
+ sort: sort
+ });
+
+ // Serialize
+ res(await Promise.all(folders.map(async folder =>
+ await pack(folder))));
+});
diff --git a/src/server/api/endpoints/drive/folders/create.ts b/src/server/api/endpoints/drive/folders/create.ts
new file mode 100644
index 0000000000..03f396ddc9
--- /dev/null
+++ b/src/server/api/endpoints/drive/folders/create.ts
@@ -0,0 +1,55 @@
+/**
+ * Module dependencies
+ */
+import $ from 'cafy';
+import DriveFolder, { isValidFolderName, pack } from '../../../models/drive-folder';
+import { publishDriveStream } from '../../../event';
+
+/**
+ * Create drive folder
+ *
+ * @param {any} params
+ * @param {any} user
+ * @return {Promise<any>}
+ */
+module.exports = (params, user) => new Promise(async (res, rej) => {
+ // Get 'name' parameter
+ const [name = '無題のフォルダー', nameErr] = $(params.name).optional.string().pipe(isValidFolderName).$;
+ if (nameErr) return rej('invalid name param');
+
+ // Get 'parent_id' parameter
+ const [parentId = null, parentIdErr] = $(params.parent_id).optional.nullable.id().$;
+ if (parentIdErr) return rej('invalid parent_id param');
+
+ // If the parent folder is specified
+ let parent = null;
+ if (parentId) {
+ // Fetch parent folder
+ parent = await DriveFolder
+ .findOne({
+ _id: parentId,
+ user_id: user._id
+ });
+
+ if (parent === null) {
+ return rej('parent-not-found');
+ }
+ }
+
+ // Create folder
+ const folder = await DriveFolder.insert({
+ created_at: new Date(),
+ name: name,
+ parent_id: parent !== null ? parent._id : null,
+ user_id: user._id
+ });
+
+ // Serialize
+ const folderObj = await pack(folder);
+
+ // Response
+ res(folderObj);
+
+ // Publish folder_created event
+ publishDriveStream(user._id, 'folder_created', folderObj);
+});
diff --git a/src/server/api/endpoints/drive/folders/find.ts b/src/server/api/endpoints/drive/folders/find.ts
new file mode 100644
index 0000000000..fc84766bc8
--- /dev/null
+++ b/src/server/api/endpoints/drive/folders/find.ts
@@ -0,0 +1,33 @@
+/**
+ * Module dependencies
+ */
+import $ from 'cafy';
+import DriveFolder, { pack } from '../../../models/drive-folder';
+
+/**
+ * Find a folder(s)
+ *
+ * @param {any} params
+ * @param {any} user
+ * @return {Promise<any>}
+ */
+module.exports = (params, user) => new Promise(async (res, rej) => {
+ // Get 'name' parameter
+ const [name, nameErr] = $(params.name).string().$;
+ if (nameErr) return rej('invalid name param');
+
+ // Get 'parent_id' parameter
+ const [parentId = null, parentIdErr] = $(params.parent_id).optional.nullable.id().$;
+ if (parentIdErr) return rej('invalid parent_id param');
+
+ // Issue query
+ const folders = await DriveFolder
+ .find({
+ name: name,
+ user_id: user._id,
+ parent_id: parentId
+ });
+
+ // Serialize
+ res(await Promise.all(folders.map(folder => pack(folder))));
+});
diff --git a/src/server/api/endpoints/drive/folders/show.ts b/src/server/api/endpoints/drive/folders/show.ts
new file mode 100644
index 0000000000..e07d14d20d
--- /dev/null
+++ b/src/server/api/endpoints/drive/folders/show.ts
@@ -0,0 +1,34 @@
+/**
+ * Module dependencies
+ */
+import $ from 'cafy';
+import DriveFolder, { pack } from '../../../models/drive-folder';
+
+/**
+ * Show a folder
+ *
+ * @param {any} params
+ * @param {any} user
+ * @return {Promise<any>}
+ */
+module.exports = (params, user) => new Promise(async (res, rej) => {
+ // Get 'folder_id' parameter
+ const [folderId, folderIdErr] = $(params.folder_id).id().$;
+ if (folderIdErr) return rej('invalid folder_id param');
+
+ // Get folder
+ const folder = await DriveFolder
+ .findOne({
+ _id: folderId,
+ user_id: user._id
+ });
+
+ if (folder === null) {
+ return rej('folder-not-found');
+ }
+
+ // Serialize
+ res(await pack(folder, {
+ detail: true
+ }));
+});
diff --git a/src/server/api/endpoints/drive/folders/update.ts b/src/server/api/endpoints/drive/folders/update.ts
new file mode 100644
index 0000000000..d3df8bdae5
--- /dev/null
+++ b/src/server/api/endpoints/drive/folders/update.ts
@@ -0,0 +1,99 @@
+/**
+ * Module dependencies
+ */
+import $ from 'cafy';
+import DriveFolder, { isValidFolderName, pack } from '../../../models/drive-folder';
+import { publishDriveStream } from '../../../event';
+
+/**
+ * Update a folder
+ *
+ * @param {any} params
+ * @param {any} user
+ * @return {Promise<any>}
+ */
+module.exports = (params, user) => new Promise(async (res, rej) => {
+ // Get 'folder_id' parameter
+ const [folderId, folderIdErr] = $(params.folder_id).id().$;
+ if (folderIdErr) return rej('invalid folder_id param');
+
+ // Fetch folder
+ const folder = await DriveFolder
+ .findOne({
+ _id: folderId,
+ user_id: user._id
+ });
+
+ if (folder === null) {
+ return rej('folder-not-found');
+ }
+
+ // Get 'name' parameter
+ const [name, nameErr] = $(params.name).optional.string().pipe(isValidFolderName).$;
+ if (nameErr) return rej('invalid name param');
+ if (name) folder.name = name;
+
+ // Get 'parent_id' parameter
+ const [parentId, parentIdErr] = $(params.parent_id).optional.nullable.id().$;
+ if (parentIdErr) return rej('invalid parent_id param');
+ if (parentId !== undefined) {
+ if (parentId === null) {
+ folder.parent_id = null;
+ } else {
+ // Get parent folder
+ const parent = await DriveFolder
+ .findOne({
+ _id: parentId,
+ user_id: user._id
+ });
+
+ if (parent === null) {
+ return rej('parent-folder-not-found');
+ }
+
+ // Check if the circular reference will occur
+ async function checkCircle(folderId) {
+ // Fetch folder
+ const folder2 = await DriveFolder.findOne({
+ _id: folderId
+ }, {
+ _id: true,
+ parent_id: true
+ });
+
+ if (folder2._id.equals(folder._id)) {
+ return true;
+ } else if (folder2.parent_id) {
+ return await checkCircle(folder2.parent_id);
+ } else {
+ return false;
+ }
+ }
+
+ if (parent.parent_id !== null) {
+ if (await checkCircle(parent.parent_id)) {
+ return rej('detected-circular-definition');
+ }
+ }
+
+ folder.parent_id = parent._id;
+ }
+ }
+
+ // Update
+ DriveFolder.update(folder._id, {
+ $set: {
+ name: folder.name,
+ parent_id: folder.parent_id
+ }
+ });
+
+ // Serialize
+ const folderObj = await pack(folder);
+
+ // Response
+ res(folderObj);
+
+ // Publish folder_updated event
+ publishDriveStream(user._id, 'folder_updated', folderObj);
+});
diff --git a/src/server/api/endpoints/drive/stream.ts b/src/server/api/endpoints/drive/stream.ts
new file mode 100644
index 0000000000..8352c7dd4c
--- /dev/null
+++ b/src/server/api/endpoints/drive/stream.ts
@@ -0,0 +1,67 @@
+/**
+ * Module dependencies
+ */
+import $ from 'cafy';
+import DriveFile, { pack } from '../../models/drive-file';
+
+/**
+ * Get drive stream
+ *
+ * @param {any} params
+ * @param {any} user
+ * @return {Promise<any>}
+ */
+module.exports = (params, user) => new Promise(async (res, rej) => {
+ // Get 'limit' parameter
+ const [limit = 10, limitErr] = $(params.limit).optional.number().range(1, 100).$;
+ if (limitErr) return rej('invalid limit param');
+
+ // Get 'since_id' parameter
+ const [sinceId, sinceIdErr] = $(params.since_id).optional.id().$;
+ if (sinceIdErr) return rej('invalid since_id param');
+
+ // Get 'until_id' parameter
+ const [untilId, untilIdErr] = $(params.until_id).optional.id().$;
+ if (untilIdErr) return rej('invalid until_id param');
+
+ // Check if both of since_id and until_id is specified
+ if (sinceId && untilId) {
+ return rej('cannot set since_id and until_id');
+ }
+
+ // Get 'type' parameter
+ const [type, typeErr] = $(params.type).optional.string().match(/^[a-zA-Z\/\-\*]+$/).$;
+ if (typeErr) return rej('invalid type param');
+
+ // Construct query
+ const sort = {
+ _id: -1
+ };
+ const query = {
+ 'metadata.user_id': user._id
+ } as any;
+ if (sinceId) {
+ sort._id = 1;
+ query._id = {
+ $gt: sinceId
+ };
+ } else if (untilId) {
+ query._id = {
+ $lt: untilId
+ };
+ }
+ if (type) {
+ query.contentType = new RegExp(`^${type.replace(/\*/g, '.+?')}$`);
+ }
+
+ // Issue query
+ const files = await DriveFile
+ .find(query, {
+ limit: limit,
+ sort: sort
+ });
+
+ // Serialize
+ res(await Promise.all(files.map(async file =>
+ await pack(file))));
+});