diff options
Diffstat (limited to 'src/server/api/endpoints/drive')
| -rw-r--r-- | src/server/api/endpoints/drive/files.ts | 73 | ||||
| -rw-r--r-- | src/server/api/endpoints/drive/files/create.ts | 51 | ||||
| -rw-r--r-- | src/server/api/endpoints/drive/files/find.ts | 34 | ||||
| -rw-r--r-- | src/server/api/endpoints/drive/files/show.ts | 36 | ||||
| -rw-r--r-- | src/server/api/endpoints/drive/files/update.ts | 75 | ||||
| -rw-r--r-- | src/server/api/endpoints/drive/files/upload_from_url.ts | 26 | ||||
| -rw-r--r-- | src/server/api/endpoints/drive/folders.ts | 66 | ||||
| -rw-r--r-- | src/server/api/endpoints/drive/folders/create.ts | 55 | ||||
| -rw-r--r-- | src/server/api/endpoints/drive/folders/find.ts | 33 | ||||
| -rw-r--r-- | src/server/api/endpoints/drive/folders/show.ts | 34 | ||||
| -rw-r--r-- | src/server/api/endpoints/drive/folders/update.ts | 99 | ||||
| -rw-r--r-- | src/server/api/endpoints/drive/stream.ts | 67 |
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)))); +}); |