diff --git a/src/misc/get-drive-file-url.ts b/src/misc/get-drive-file-url.ts deleted file mode 100644 index 067db8a5d..000000000 --- a/src/misc/get-drive-file-url.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { IDriveFile } from '../models/drive-file'; -import config from '../config'; - -export default function(file: IDriveFile, thumbnail = false): string { - if (file == null) return null; - - const isImage = file.contentType && file.contentType.startsWith('image/'); - - if (file.metadata.withoutChunks) { - if (thumbnail) { - return file.metadata.thumbnailUrl || file.metadata.webpublicUrl || (isImage ? file.metadata.url : null); - } else { - return file.metadata.webpublicUrl || file.metadata.url; - } - } else { - if (thumbnail) { - return `${config.driveUrl}/${file._id}?thumbnail`; - } else { - return `${config.driveUrl}/${file._id}?web`; - } - } -} - -export function getOriginalUrl(file: IDriveFile) { - if (file.metadata && file.metadata.url) { - return file.metadata.url; - } - - const accessKey = file.metadata ? file.metadata.accessKey : null; - return `${config.driveUrl}/${file._id}${accessKey ? '?original=' + accessKey : ''}`; -} diff --git a/src/models/drive-file.ts b/src/models/drive-file.ts index f3e21f209..11037ac2a 100644 --- a/src/models/drive-file.ts +++ b/src/models/drive-file.ts @@ -4,16 +4,57 @@ import { pack as packFolder } from './drive-folder'; import { pack as packUser } from './user'; import monkDb, { nativeDbConn, dbLogger } from '../db/mongodb'; import isObjectId from '../misc/is-objectid'; -import getDriveFileUrl, { getOriginalUrl } from '../misc/get-drive-file-url'; +import config from '../config'; +import uuid = require('uuid'); const DriveFile = monkDb.get('driveFiles.files'); DriveFile.createIndex('md5'); DriveFile.createIndex('metadata.uri'); +DriveFile.createIndex('metadata.url'); +DriveFile.createIndex('metadata.webpublicUrl'); +DriveFile.createIndex('metadata.thumbnailUrl'); DriveFile.createIndex('metadata.userId'); DriveFile.createIndex('metadata.folderId'); DriveFile.createIndex('metadata._user.host'); export default DriveFile; +// 後方互換性のため +DriveFile.findOne({ + $or: [{ + 'metadata.url': { $exists: false } + }, { + 'metadata.url': null + }] +}).then(x => { + if (x != null) { + DriveFile.find({ + $or: [{ + 'metadata.url': { $exists: false } + }, { + 'metadata.url': null + }] + }, { fields: { _id: true, filename: true, contentType: true } }).then(xs => { + for (const x of xs) { + let [ext] = (x.filename.match(/\.([a-zA-Z0-9_-]+)$/) || ['']); + + if (ext === '') { + if (x.contentType === 'image/jpeg') ext = '.jpg'; + if (x.contentType === 'image/png') ext = '.png'; + if (x.contentType === 'image/webp') ext = '.webp'; + } + + DriveFile.update({ _id: x._id }, { + $set: { + 'metadata.url': `${config.driveUrl}/${uuid.v4()}${ext}`, + 'metadata.webpublicUrl': `${config.driveUrl}/${uuid.v4()}${ext}`, + 'metadata.thumbnailUrl': `${config.driveUrl}/${uuid.v4()}.jpg`, + } + }); + } + }); + } +}); + export const DriveFileChunk = monkDb.get('driveFiles.chunks'); export const getDriveFileBucket = async (): Promise => { @@ -36,26 +77,10 @@ export type IMetadata = { */ uri?: string; - /** - * URL for web(生成されている場合) or original - * * オブジェクトストレージを利用している or リモートサーバーへの直リンクである 場合のみ - */ - url?: string; - - /** - * URL for thumbnail (thumbnailがなければなし) - * * オブジェクトストレージを利用している or リモートサーバーへの直リンクである 場合のみ - */ + url: string; thumbnailUrl?: string; - - /** - * URL for original (web用が生成されてない場合はurlがoriginalを指す) - * * オブジェクトストレージを利用している or リモートサーバーへの直リンクである 場合のみ - */ webpublicUrl?: string; - accessKey?: string; - src?: string; deletedAt?: Date; @@ -188,8 +213,10 @@ export const pack = ( _target = Object.assign(_target, _file.metadata); - _target.url = getDriveFileUrl(_file); - _target.thumbnailUrl = getDriveFileUrl(_file, true); + const isImage = file.contentType && file.contentType.startsWith('image/'); + + _target.url = _file.metadata.webpublicUrl || _file.metadata.url; + _target.thumbnailUrl = _file.metadata.thumbnailUrl || _file.metadata.webpublicUrl || (isImage ? _file.metadata.url : '/files/thumbnail-not-available.png'); _target.isRemote = _file.metadata.isRemote; if (_target.properties == null) _target.properties = {}; @@ -224,7 +251,7 @@ export const pack = ( delete _target._user; if (opts.self) { - _target.url = getOriginalUrl(_file); + _target.url = _file.metadata.url; } resolve(_target); diff --git a/src/queue/processors/export-blocking.ts b/src/queue/processors/export-blocking.ts index b30d8e3bc..ad91b8846 100644 --- a/src/queue/processors/export-blocking.ts +++ b/src/queue/processors/export-blocking.ts @@ -4,7 +4,7 @@ import * as fs from 'fs'; import * as mongo from 'mongodb'; import { queueLogger } from '../logger'; -import addFile from '../../services/drive/add-file'; +import { addDriveFile } from '../../services/drive/add-file'; import User from '../../models/user'; import dateFormat = require('dateformat'); import Blocking from '../../models/blocking'; @@ -81,7 +81,7 @@ export async function exportBlocking(job: bq.Job, done: any): Promise { logger.succ(`Exported to: ${path}`); const fileName = 'blocking-' + dateFormat(new Date(), 'yyyy-mm-dd-HH-MM-ss') + '.csv'; - const driveFile = await addFile(user, path, fileName); + const driveFile = await addDriveFile(user, path, fileName); logger.succ(`Exported to: ${driveFile._id}`); cleanup(); diff --git a/src/queue/processors/export-following.ts b/src/queue/processors/export-following.ts index e6521d065..a29e4af00 100644 --- a/src/queue/processors/export-following.ts +++ b/src/queue/processors/export-following.ts @@ -4,7 +4,7 @@ import * as fs from 'fs'; import * as mongo from 'mongodb'; import { queueLogger } from '../logger'; -import addFile from '../../services/drive/add-file'; +import { addDriveFile } from '../../services/drive/add-file'; import User from '../../models/user'; import dateFormat = require('dateformat'); import Following from '../../models/following'; @@ -81,7 +81,7 @@ export async function exportFollowing(job: bq.Job, done: any): Promise { logger.succ(`Exported to: ${path}`); const fileName = 'following-' + dateFormat(new Date(), 'yyyy-mm-dd-HH-MM-ss') + '.csv'; - const driveFile = await addFile(user, path, fileName); + const driveFile = await addDriveFile(user, path, fileName); logger.succ(`Exported to: ${driveFile._id}`); cleanup(); diff --git a/src/queue/processors/export-mute.ts b/src/queue/processors/export-mute.ts index 74456c1da..8de6b41f9 100644 --- a/src/queue/processors/export-mute.ts +++ b/src/queue/processors/export-mute.ts @@ -4,7 +4,7 @@ import * as fs from 'fs'; import * as mongo from 'mongodb'; import { queueLogger } from '../logger'; -import addFile from '../../services/drive/add-file'; +import { addDriveFile } from '../../services/drive/add-file'; import User from '../../models/user'; import dateFormat = require('dateformat'); import Mute from '../../models/mute'; @@ -81,7 +81,7 @@ export async function exportMute(job: bq.Job, done: any): Promise { logger.succ(`Exported to: ${path}`); const fileName = 'mute-' + dateFormat(new Date(), 'yyyy-mm-dd-HH-MM-ss') + '.csv'; - const driveFile = await addFile(user, path, fileName); + const driveFile = await addDriveFile(user, path, fileName); logger.succ(`Exported to: ${driveFile._id}`); cleanup(); diff --git a/src/queue/processors/export-notes.ts b/src/queue/processors/export-notes.ts index 32e4cd1d6..86cff3aa5 100644 --- a/src/queue/processors/export-notes.ts +++ b/src/queue/processors/export-notes.ts @@ -5,7 +5,7 @@ import * as mongo from 'mongodb'; import { queueLogger } from '../logger'; import Note, { INote } from '../../models/note'; -import addFile from '../../services/drive/add-file'; +import { addDriveFile } from '../../services/drive/add-file'; import User from '../../models/user'; import dateFormat = require('dateformat'); @@ -101,7 +101,7 @@ export async function exportNotes(job: bq.Job, done: any): Promise { logger.succ(`Exported to: ${path}`); const fileName = 'notes-' + dateFormat(new Date(), 'yyyy-mm-dd-HH-MM-ss') + '.json'; - const driveFile = await addFile(user, path, fileName); + const driveFile = await addDriveFile(user, path, fileName); logger.succ(`Exported to: ${driveFile._id}`); cleanup(); diff --git a/src/remote/activitypub/models/person.ts b/src/remote/activitypub/models/person.ts index c2b99dfb0..7749468b9 100644 --- a/src/remote/activitypub/models/person.ts +++ b/src/remote/activitypub/models/person.ts @@ -16,7 +16,6 @@ import { URL } from 'url'; import { resolveNote, extractEmojis } from './note'; import { registerOrFetchInstanceDoc } from '../../../services/register-or-fetch-instance-doc'; import Instance from '../../../models/instance'; -import getDriveFileUrl from '../../../misc/get-drive-file-url'; import { IEmoji } from '../../../models/emoji'; import { ITag, extractHashtags } from './tag'; import Following from '../../../models/following'; @@ -227,8 +226,8 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise ({ type: 'Document', mediaType: file.contentType, - url: getDriveFileUrl(file) + url: file.metadata.webpublicUrl }); diff --git a/src/remote/activitypub/renderer/image.ts b/src/remote/activitypub/renderer/image.ts index ec637b952..e4b2eed25 100644 --- a/src/remote/activitypub/renderer/image.ts +++ b/src/remote/activitypub/renderer/image.ts @@ -1,8 +1,7 @@ import { IDriveFile } from '../../../models/drive-file'; -import getDriveFileUrl from '../../../misc/get-drive-file-url'; export default (file: IDriveFile) => ({ type: 'Image', - url: getDriveFileUrl(file), + url: file.metadata.webpublicUrl, sensitive: file.metadata.isSensitive }); diff --git a/src/server/api/endpoints/drive/files/create.ts b/src/server/api/endpoints/drive/files/create.ts index b2979c488..1f2769cbe 100644 --- a/src/server/api/endpoints/drive/files/create.ts +++ b/src/server/api/endpoints/drive/files/create.ts @@ -2,7 +2,7 @@ import * as ms from 'ms'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id'; import { validateFileName, pack } from '../../../../../models/drive-file'; -import create from '../../../../../services/drive/add-file'; +import { addDriveFile } from '../../../../../services/drive/add-file'; import define from '../../../define'; import { apiLogger } from '../../../logger'; import { ApiError } from '../../../error'; @@ -87,7 +87,7 @@ export default define(meta, async (ps, user, app, file, cleanup) => { try { // Create file - const driveFile = await create(user, file.path, name, null, ps.folderId, ps.force, false, null, null, ps.isSensitive); + const driveFile = await addDriveFile(user, file.path, name, null, ps.folderId, ps.force, false, null, null, ps.isSensitive); return pack(driveFile, { self: true }); } catch (e) { apiLogger.error(e); diff --git a/src/server/api/endpoints/drive/files/show.ts b/src/server/api/endpoints/drive/files/show.ts index 6d63a8605..c0159af72 100644 --- a/src/server/api/endpoints/drive/files/show.ts +++ b/src/server/api/endpoints/drive/files/show.ts @@ -1,9 +1,7 @@ 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'; import { ApiError } from '../../../error'; export const meta = { @@ -73,28 +71,16 @@ export default define(meta, async (ps, user) => { 'metadata.deletedAt': { $exists: false } }); } else if (ps.url) { - const isInternalStorageUrl = ps.url.startsWith(config.driveUrl); - if (isInternalStorageUrl) { - // Extract file ID from url - // e.g. - // http://misskey.local/files/foo?original=bar --> foo - const fileId = new mongo.ObjectID(ps.url.replace(config.driveUrl, '').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 } - }); - } + file = await DriveFile.findOne({ + $or: [{ + 'metadata.url': ps.url + }, { + 'metadata.webpublicUrl': ps.url + }, { + 'metadata.thumbnailUrl': ps.url + }], + 'metadata.deletedAt': { $exists: false } + }); } else { throw new ApiError(meta.errors.fileIdOrUrlRequired); } diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts index 099ef3399..1eef7dc93 100644 --- a/src/server/api/endpoints/i/update.ts +++ b/src/server/api/endpoints/i/update.ts @@ -6,7 +6,6 @@ import DriveFile from '../../../../models/drive-file'; import acceptAllFollowRequests from '../../../../services/following/requests/accept-all'; import { publishToFollowers } from '../../../../services/i/update'; import define from '../../define'; -import getDriveFileUrl from '../../../../misc/get-drive-file-url'; import { parse, parsePlain } from '../../../../mfm/parse'; import extractEmojis from '../../../../misc/extract-emojis'; import extractHashtags from '../../../../misc/extract-hashtags'; @@ -195,7 +194,7 @@ export default define(meta, async (ps, user, app) => { if (avatar.metadata.deletedAt) { updates.avatarUrl = null; } else { - updates.avatarUrl = getDriveFileUrl(avatar, true); + updates.avatarUrl = avatar.metadata.thumbnailUrl; if (avatar.metadata.properties.avgColor) { updates.avatarColor = avatar.metadata.properties.avgColor; @@ -214,7 +213,7 @@ export default define(meta, async (ps, user, app) => { if (banner.metadata.deletedAt) { updates.bannerUrl = null; } else { - updates.bannerUrl = getDriveFileUrl(banner, false); + updates.bannerUrl = banner.metadata.webpublicUrl; if (banner.metadata.properties.avgColor) { updates.bannerColor = banner.metadata.properties.avgColor; @@ -236,7 +235,7 @@ export default define(meta, async (ps, user, app) => { if (wallpaper.metadata.deletedAt) { updates.wallpaperUrl = null; } else { - updates.wallpaperUrl = getDriveFileUrl(wallpaper); + updates.wallpaperUrl = wallpaper.metadata.webpublicUrl; if (wallpaper.metadata.properties.avgColor) { updates.wallpaperColor = wallpaper.metadata.properties.avgColor; diff --git a/src/server/api/endpoints/users/show.ts b/src/server/api/endpoints/users/show.ts index 4e59945eb..9d08a48e7 100644 --- a/src/server/api/endpoints/users/show.ts +++ b/src/server/api/endpoints/users/show.ts @@ -5,6 +5,7 @@ import resolveRemoteUser from '../../../../remote/resolve-user'; import define from '../../define'; import { apiLogger } from '../../logger'; import { ApiError } from '../../error'; +import DriveFile from '../../../../models/drive-file'; const cursorOption = { fields: { data: false } }; @@ -103,6 +104,33 @@ export default define(meta, async (ps, me) => { } } + // 後方互換性のため + // TODO: そのうち削除 + if (user.avatarId) { + const avatar = await DriveFile.findOne({ _id: user.avatarId }); + User.update({ _id: user._id }, { + $set: { + avatarUrl: avatar.metadata.thumbnailUrl, + } + }); + } + if (user.bannerId) { + const banner = await DriveFile.findOne({ _id: user.bannerId }); + User.update({ _id: user._id }, { + $set: { + bannerUrl: banner.metadata.webpublicUrl, + } + }); + } + if (user.wallpaperId) { + const wallpaper = await DriveFile.findOne({ _id: user.wallpaperId }); + User.update({ _id: user._id }, { + $set: { + wallpaperUrl: wallpaper.metadata.webpublicUrl, + } + }); + } + return await pack(user, me, { detail: true }); diff --git a/src/server/file/index.ts b/src/server/file/index.ts index 973528da3..5dbb5a4b5 100644 --- a/src/server/file/index.ts +++ b/src/server/file/index.ts @@ -29,12 +29,17 @@ router.get('/default-avatar.jpg', ctx => { router.get('/app-default.jpg', ctx => { const file = fs.createReadStream(`${__dirname}/assets/dummy.png`); - ctx.set('Content-Type', 'image/jpeg'); + ctx.set('Content-Type', 'image/png'); ctx.body = file; }); -router.get('/:id', sendDriveFile); -router.get('/:id/*', sendDriveFile); +router.get('/thumbnail-not-available.png', ctx => { + const file = fs.createReadStream(`${__dirname}/assets/thumbnail-not-available.png`); + ctx.set('Content-Type', 'image/png'); + ctx.body = file; +}); + +router.get('/*', sendDriveFile); // Register router app.use(router.routes()); diff --git a/src/server/file/send-drive-file.ts b/src/server/file/send-drive-file.ts index 691d3bf84..7ccc52bae 100644 --- a/src/server/file/send-drive-file.ts +++ b/src/server/file/send-drive-file.ts @@ -1,10 +1,10 @@ import * as Koa from 'koa'; import * as send from 'koa-send'; -import * as mongodb from 'mongodb'; import DriveFile, { getDriveFileBucket } from '../../models/drive-file'; import DriveFileThumbnail, { getDriveFileThumbnailBucket } from '../../models/drive-file-thumbnail'; import DriveFileWebpublic, { getDriveFileWebpublicBucket } from '../../models/drive-file-webpublic'; import { serverLogger } from '..'; +import config from '../../config'; const assets = `${__dirname}/../../server/file/assets/`; @@ -14,16 +14,19 @@ const commonReadableHandlerGenerator = (ctx: Koa.BaseContext) => (e: Error): voi }; export default async function(ctx: Koa.BaseContext) { - // Validate id - if (!mongodb.ObjectID.isValid(ctx.params.id)) { - ctx.throw(400, 'incorrect id'); - return; - } - - const fileId = new mongodb.ObjectID(ctx.params.id); + let url = ctx.href; + if (url.startsWith('http://') && config.url.startsWith('https://')) url = url.replace('http://', 'https://'); // Fetch drive file - const file = await DriveFile.findOne({ _id: fileId }); + const file = await DriveFile.findOne({ + $or: [{ + 'metadata.url': url + }, { + 'metadata.webpublicUrl': url + }, { + 'metadata.thumbnailUrl': url + }], + }); if (file == null) { ctx.status = 404; @@ -43,21 +46,16 @@ export default async function(ctx: Koa.BaseContext) { } const sendRaw = async () => { - if (file.metadata && file.metadata.accessKey && file.metadata.accessKey != ctx.query['original']) { - ctx.status = 403; - return; - } - const bucket = await getDriveFileBucket(); - const readable = bucket.openDownloadStream(fileId); + const readable = bucket.openDownloadStream(file._id); readable.on('error', commonReadableHandlerGenerator(ctx)); ctx.set('Content-Type', file.contentType); ctx.body = readable; }; - if ('thumbnail' in ctx.query) { + if (file.metadata.thumbnailUrl === url) { const thumb = await DriveFileThumbnail.findOne({ - 'metadata.originalId': fileId + 'metadata.originalId': file._id }); if (thumb != null) { @@ -69,12 +67,12 @@ export default async function(ctx: Koa.BaseContext) { await sendRaw(); } else { ctx.status = 404; - await send(ctx as any, '/dummy.png', { root: assets }); + await send(ctx as any, '/thumbnail-not-available.png', { root: assets }); } } - } else if ('web' in ctx.query) { + } else if (file.metadata.webpublicUrl === url) { const web = await DriveFileWebpublic.findOne({ - 'metadata.originalId': fileId + 'metadata.originalId': file._id }); if (web != null) { diff --git a/src/server/web/feed.ts b/src/server/web/feed.ts index 09ac10c57..e8d06837e 100644 --- a/src/server/web/feed.ts +++ b/src/server/web/feed.ts @@ -2,7 +2,6 @@ import { Feed } from 'feed'; import config from '../../config'; import Note from '../../models/note'; import { IUser } from '../../models/user'; -import { getOriginalUrl } from '../../misc/get-drive-file-url'; export default async function(user: IUser) { const author: Author = { @@ -38,7 +37,7 @@ export default async function(user: IUser) { } as FeedOptions); for (const note of notes) { - const file = note._files && note._files.find(file => file.contentType.startsWith('image/')); + const file = note._files ? note._files.find(file => file.contentType.startsWith('image/')) : null; feed.addItem({ title: `New note by ${author.name}`, @@ -46,7 +45,7 @@ export default async function(user: IUser) { date: note.createdAt, description: note.cw, content: note.text, - image: file && getOriginalUrl(file) + image: file ? file.metadata.webpublicUrl : null }); } diff --git a/src/services/drive/add-file.ts b/src/services/drive/add-file.ts index 9f3805f94..7b28113bd 100644 --- a/src/services/drive/add-file.ts +++ b/src/services/drive/add-file.ts @@ -42,16 +42,16 @@ async function save(path: string, name: string, type: string, hash: string, size // thunbnail, webpublic を必要なら生成 const alts = await generateAlts(path, type, !metadata.uri); + let [ext] = (name.match(/\.([a-zA-Z0-9_-]+)$/) || ['']); + + if (ext === '') { + if (type === 'image/jpeg') ext = '.jpg'; + if (type === 'image/png') ext = '.png'; + if (type === 'image/webp') ext = '.webp'; + } + if (config.drive && config.drive.storage == 'minio') { //#region ObjectStorage params - let [ext] = (name.match(/\.([a-zA-Z0-9_-]+)$/) || ['']); - - if (ext === '') { - if (type === 'image/jpeg') ext = '.jpg'; - if (type === 'image/png') ext = '.png'; - if (type === 'image/webp') ext = '.webp'; - } - const baseUrl = config.drive.baseUrl || `${ config.drive.config.useSSL ? 'https' : 'http' }://${ config.drive.config.endPoint }${ config.drive.config.port ? `:${config.drive.config.port}` : '' }/${ config.drive.bucket }`; @@ -117,12 +117,15 @@ async function save(path: string, name: string, type: string, hash: string, size return file; } else { // use MongoDB GridFS + Object.assign(metadata, { + url: `${config.driveUrl}/${uuid.v4()}${ext}`, + webpublicUrl: `${config.driveUrl}/${uuid.v4()}.${alts.webpublic.ext}`, + thumbnailUrl: `${config.driveUrl}/${uuid.v4()}.${alts.thumbnail.ext}`, + } as IMetadata); + // #region store original const originalDst = await getDriveFileBucket(); - // web用(Exif削除済み)がある場合はオリジナルにアクセス制限 - if (alts.webpublic) metadata.accessKey = uuid.v4(); - const originalFile = await storeOriginal(originalDst, name, path, type, metadata); logger.info(`original stored to ${originalFile._id}`); @@ -273,7 +276,7 @@ async function deleteOldFile(user: IRemoteUser) { * @param sensitive Mark file as sensitive * @return Created drive file */ -export default async function( +export async function addDriveFile( user: IUser, path: string, name: string = null, diff --git a/src/services/drive/upload-from-url.ts b/src/services/drive/upload-from-url.ts index 1651afa27..91f6a287f 100644 --- a/src/services/drive/upload-from-url.ts +++ b/src/services/drive/upload-from-url.ts @@ -4,7 +4,7 @@ import * as tmp from 'tmp'; import * as request from 'request'; import { IDriveFile, validateFileName } from '../../models/drive-file'; -import create from './add-file'; +import { addDriveFile } from './add-file'; import config from '../../config'; import { IUser } from '../../models/user'; import * as mongodb from 'mongodb'; @@ -83,7 +83,7 @@ export default async ( let error; try { - driveFile = await create(user, path, name, null, folderId, force, link, url, uri, sensitive); + driveFile = await addDriveFile(user, path, name, null, folderId, force, link, url, uri, sensitive); logger.succ(`Got: ${driveFile._id}`); } catch (e) { error = e;