From 78487934c7e55b36b07f30b73127577ddec31f32 Mon Sep 17 00:00:00 2001 From: otofune Date: Sun, 5 Nov 2017 21:11:16 +0900 Subject: selializers - posts: unneed async-await Promise.all resolves all Promise, and selializeDriveFile returns Promise. --- src/api/serializers/post.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/api') diff --git a/src/api/serializers/post.ts b/src/api/serializers/post.ts index 7c3690ef79..b2c54e9df8 100644 --- a/src/api/serializers/post.ts +++ b/src/api/serializers/post.ts @@ -84,8 +84,8 @@ const self = ( // Populate media if (_post.media_ids) { - _post.media = await Promise.all(_post.media_ids.map(async fileId => - await serializeDriveFile(fileId) + _post.media = await Promise.all(_post.media_ids.map(fileId => + serializeDriveFile(fileId) )); } -- cgit v1.2.3-freya From 11190f56ad85a12faaa3653f8743dd75948ff11e Mon Sep 17 00:00:00 2001 From: otofune Date: Sun, 5 Nov 2017 22:13:28 +0900 Subject: serializers/post - run promises in parallel now w/ opts.detail, returns my_reaction field as 'null' w/ no reaction (before: field appears w/ some reaction) --- package.json | 1 + src/api/serializers/post.ts | 128 +++++++++++++++++++++++++------------------- 2 files changed, 73 insertions(+), 56 deletions(-) (limited to 'src/api') diff --git a/package.json b/package.json index 051eb1cb83..1e6e8d8136 100644 --- a/package.json +++ b/package.json @@ -95,6 +95,7 @@ "webpack": "3.8.1" }, "dependencies": { + "@prezzemolo/rap": "^0.1.0", "accesses": "2.5.0", "animejs": "2.2.0", "autwh": "0.0.1", diff --git a/src/api/serializers/post.ts b/src/api/serializers/post.ts index b2c54e9df8..352932acff 100644 --- a/src/api/serializers/post.ts +++ b/src/api/serializers/post.ts @@ -12,6 +12,7 @@ import serializeChannel from './channel'; import serializeUser from './user'; import serializeDriveFile from './drive-file'; import parse from '../common/text'; +import rap from '@prezzemolo/rap' /** * Serialize a post @@ -70,21 +71,21 @@ const self = ( } // Populate user - _post.user = await serializeUser(_post.user_id, meId); + _post.user = serializeUser(_post.user_id, meId); // Populate app if (_post.app_id) { - _post.app = await serializeApp(_post.app_id); + _post.app = serializeApp(_post.app_id); } // Populate channel if (_post.channel_id) { - _post.channel = await serializeChannel(_post.channel_id); + _post.channel = serializeChannel(_post.channel_id); } // Populate media if (_post.media_ids) { - _post.media = await Promise.all(_post.media_ids.map(fileId => + _post.media = Promise.all(_post.media_ids.map(fileId => serializeDriveFile(fileId) )); } @@ -92,82 +93,97 @@ const self = ( // When requested a detailed post data if (opts.detail) { // Get previous post info - const prev = await Post.findOne({ - user_id: _post.user_id, - _id: { - $lt: id - } - }, { - fields: { - _id: true - }, - sort: { - _id: -1 - } - }); - _post.prev = prev ? prev._id : null; + _post.prev = (async () => { + const prev = Post.findOne({ + user_id: _post.user_id, + _id: { + $lt: id + } + }, { + fields: { + _id: true + }, + sort: { + _id: -1 + } + }); + return prev ? prev._id : null; + })() // Get next post info - const next = await Post.findOne({ - user_id: _post.user_id, - _id: { - $gt: id - } - }, { - fields: { - _id: true - }, - sort: { - _id: 1 - } - }); - _post.next = next ? next._id : null; + _post.next = (async () => { + const next = await Post.findOne({ + user_id: _post.user_id, + _id: { + $gt: id + } + }, { + fields: { + _id: true + }, + sort: { + _id: 1 + } + }); + return next ? next._id : null; + })() if (_post.reply_id) { // Populate reply to post - _post.reply = await self(_post.reply_id, meId, { + _post.reply = self(_post.reply_id, meId, { detail: false }); } if (_post.repost_id) { // Populate repost - _post.repost = await self(_post.repost_id, meId, { + _post.repost = self(_post.repost_id, meId, { detail: _post.text == null }); } // Poll if (meId && _post.poll) { - const vote = await Vote - .findOne({ - user_id: meId, - post_id: id - }); - - if (vote != null) { - const myChoice = _post.poll.choices - .filter(c => c.id == vote.choice)[0]; - - myChoice.is_voted = true; - } + _post.poll = (async (poll) => { + const vote = await Vote + .findOne({ + user_id: meId, + post_id: id + }); + + if (vote != null) { + const myChoice = poll.choices + .filter(c => c.id == vote.choice)[0]; + + myChoice.is_voted = true; + } + + return poll + })(_post.poll) } // Fetch my reaction if (meId) { - const reaction = await Reaction - .findOne({ - user_id: meId, - post_id: id, - deleted_at: { $exists: false } - }); - - if (reaction) { - _post.my_reaction = reaction.reaction; - } + _post.my_reaction = (async () => { + const reaction = await Reaction + .findOne({ + user_id: meId, + post_id: id, + deleted_at: { $exists: false } + }); + + if (reaction) { + return reaction.reaction; + } + + return null + })(); } } + // resolve promises in _post object + _post = await rap(_post) + resolve(_post); }); -- cgit v1.2.3-freya From 5aa5e5cc7074003cec3417636ea1972b6d88150d Mon Sep 17 00:00:00 2001 From: otofune Date: Sun, 5 Nov 2017 22:22:49 +0900 Subject: serializers - user: run promises in parallel as possible --- src/api/serializers/post.ts | 2 +- src/api/serializers/user.ts | 40 +++++++++++++++++++++++----------------- 2 files changed, 24 insertions(+), 18 deletions(-) (limited to 'src/api') diff --git a/src/api/serializers/post.ts b/src/api/serializers/post.ts index 352932acff..99e9bb667c 100644 --- a/src/api/serializers/post.ts +++ b/src/api/serializers/post.ts @@ -12,7 +12,7 @@ import serializeChannel from './channel'; import serializeUser from './user'; import serializeDriveFile from './drive-file'; import parse from '../common/text'; -import rap from '@prezzemolo/rap' +import rap from '@prezzemolo/rap'; /** * Serialize a post diff --git a/src/api/serializers/user.ts b/src/api/serializers/user.ts index 3deff2d003..3527921ded 100644 --- a/src/api/serializers/user.ts +++ b/src/api/serializers/user.ts @@ -8,6 +8,7 @@ import serializePost from './post'; import Following from '../models/following'; import getFriends from '../common/get-friends'; import config from '../../conf'; +import rap from '@prezzemolo/rap'; /** * Serialize a user @@ -104,26 +105,30 @@ export default ( if (meId && !meId.equals(_user.id)) { // If the user is following - const follow = await Following.findOne({ - follower_id: meId, - followee_id: _user.id, - deleted_at: { $exists: false } - }); - _user.is_following = follow !== null; + _user.is_following = (async () => { + const follow = await Following.findOne({ + follower_id: meId, + followee_id: _user.id, + deleted_at: { $exists: false } + }); + return follow !== null; + })() // If the user is followed - const follow2 = await Following.findOne({ - follower_id: _user.id, - followee_id: meId, - deleted_at: { $exists: false } - }); - _user.is_followed = follow2 !== null; + _user.is_followed = (async () => { + const follow2 = await Following.findOne({ + follower_id: _user.id, + followee_id: meId, + deleted_at: { $exists: false } + }); + return follow2 !== null; + })() } if (opts.detail) { if (_user.pinned_post_id) { // Populate pinned post - _user.pinned_post = await serializePost(_user.pinned_post_id, meId, { + _user.pinned_post = serializePost(_user.pinned_post_id, meId, { detail: true }); } @@ -132,23 +137,24 @@ export default ( const myFollowingIds = await getFriends(meId); // Get following you know count - const followingYouKnowCount = await Following.count({ + _user.following_you_know_count = Following.count({ followee_id: { $in: myFollowingIds }, follower_id: _user.id, deleted_at: { $exists: false } }); - _user.following_you_know_count = followingYouKnowCount; // Get followers you know count - const followersYouKnowCount = await Following.count({ + _user.followers_you_know_count = Following.count({ followee_id: _user.id, follower_id: { $in: myFollowingIds }, deleted_at: { $exists: false } }); - _user.followers_you_know_count = followersYouKnowCount; } } + // resolve promises in _user object + _user = await rap(_user) + resolve(_user); }); /* -- cgit v1.2.3-freya From 7cd6b1c666605c7a256e4a8dd8db5edeb02da6db Mon Sep 17 00:00:00 2001 From: otofune Date: Sun, 5 Nov 2017 22:26:16 +0900 Subject: follow lint --- src/api/serializers/post.ts | 12 ++++++------ src/api/serializers/user.ts | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src/api') diff --git a/src/api/serializers/post.ts b/src/api/serializers/post.ts index 99e9bb667c..e1ab784359 100644 --- a/src/api/serializers/post.ts +++ b/src/api/serializers/post.ts @@ -108,7 +108,7 @@ const self = ( } }); return prev ? prev._id : null; - })() + })(); // Get next post info _post.next = (async () => { @@ -126,7 +126,7 @@ const self = ( } }); return next ? next._id : null; - })() + })(); if (_post.reply_id) { // Populate reply to post @@ -158,8 +158,8 @@ const self = ( myChoice.is_voted = true; } - return poll - })(_post.poll) + return poll; + })(_post.poll); } // Fetch my reaction @@ -176,13 +176,13 @@ const self = ( return reaction.reaction; } - return null + return null; })(); } } // resolve promises in _post object - _post = await rap(_post) + _post = await rap(_post); resolve(_post); }); diff --git a/src/api/serializers/user.ts b/src/api/serializers/user.ts index 3527921ded..d00f073897 100644 --- a/src/api/serializers/user.ts +++ b/src/api/serializers/user.ts @@ -112,7 +112,7 @@ export default ( deleted_at: { $exists: false } }); return follow !== null; - })() + })(); // If the user is followed _user.is_followed = (async () => { @@ -122,7 +122,7 @@ export default ( deleted_at: { $exists: false } }); return follow2 !== null; - })() + })(); } if (opts.detail) { @@ -153,7 +153,7 @@ export default ( } // resolve promises in _user object - _user = await rap(_user) + _user = await rap(_user); resolve(_user); }); -- cgit v1.2.3-freya From 7e81e0db6ac1289ae9504f7e3da5db6e56f41a51 Mon Sep 17 00:00:00 2001 From: otofune Date: Mon, 6 Nov 2017 14:37:00 +0900 Subject: support GridFS --- src/api/common/add-file-to-drive.ts | 37 +++++++++++++++++++++++-------------- src/api/models/drive-file.ts | 15 +++++++++++++-- src/db/mongodb.ts | 35 +++++++++++++++++++++++++++++++---- 3 files changed, 67 insertions(+), 20 deletions(-) (limited to 'src/api') diff --git a/src/api/common/add-file-to-drive.ts b/src/api/common/add-file-to-drive.ts index 714eeb520d..f48f0cbcf5 100644 --- a/src/api/common/add-file-to-drive.ts +++ b/src/api/common/add-file-to-drive.ts @@ -4,14 +4,27 @@ import * as gm from 'gm'; import * as debug from 'debug'; import fileType = require('file-type'); import prominence = require('prominence'); -import DriveFile from '../models/drive-file'; +import DriveFile, { getGridFSBucket } from '../models/drive-file'; import DriveFolder from '../models/drive-folder'; import serialize from '../serializers/drive-file'; import event from '../event'; import config from '../../conf'; +import { Duplex } from 'stream'; const log = debug('misskey:register-drive-file'); +const addToGridFS = (name, binary, metadata): Promise => new Promise(async (resolve, reject) => { + const dataStream = new Duplex() + dataStream.push(binary) + dataStream.push(null) + + const bucket = await getGridFSBucket() + const writeStream = bucket.openUploadStream(name, { metadata }) + writeStream.once('finish', (doc) => { resolve(doc) }) + writeStream.on('error', reject) + dataStream.pipe(writeStream) +}) + /** * Add file to drive * @@ -58,7 +71,7 @@ export default ( // Generate hash const hash = crypto - .createHash('sha256') + .createHash('md5') .update(data) .digest('hex') as string; @@ -67,8 +80,10 @@ export default ( if (!force) { // Check if there is a file with the same hash const much = await DriveFile.findOne({ - user_id: user._id, - hash: hash + md5: hash, + metadata: { + user_id: user._id + } }); if (much !== null) { @@ -82,13 +97,13 @@ export default ( // Calculate drive usage const usage = ((await DriveFile .aggregate([ - { $match: { user_id: user._id } }, + { $match: { metadata: { user_id: user._id } } }, { $project: { - datasize: true + length: true }}, { $group: { _id: null, - usage: { $sum: '$datasize' } + usage: { $sum: '$length' } }} ]))[0] || { usage: 0 @@ -131,21 +146,15 @@ export default ( } // Create DriveFile document - const file = await DriveFile.insert({ - created_at: new Date(), + const file = await addToGridFS(`${user._id}/${name}`, data, { user_id: user._id, folder_id: folder !== null ? folder._id : null, - data: data, - datasize: size, type: mime, name: name, comment: comment, - hash: hash, properties: properties }); - delete file.data; - log(`drive file has been created ${file._id}`); resolve(file); diff --git a/src/api/models/drive-file.ts b/src/api/models/drive-file.ts index 8d158cf563..79a87f6572 100644 --- a/src/api/models/drive-file.ts +++ b/src/api/models/drive-file.ts @@ -1,11 +1,22 @@ -import db from '../../db/mongodb'; +import * as mongodb from 'mongodb'; +import monkDb, { nativeDbConn } from '../../db/mongodb'; -const collection = db.get('drive_files'); +const collection = monkDb.get('drive_files.files'); (collection as any).createIndex('hash'); // fuck type definition export default collection as any; // fuck type definition +const getGridFSBucket = async (): Promise => { + const db = await nativeDbConn() + const bucket = new mongodb.GridFSBucket(db, { + bucketName: 'drive_files' + }) + return bucket +} + +export { getGridFSBucket } + export function validateFileName(name: string): boolean { return ( (name.trim().length > 0) && diff --git a/src/db/mongodb.ts b/src/db/mongodb.ts index 6ee7f4534f..75f1a1d3c6 100644 --- a/src/db/mongodb.ts +++ b/src/db/mongodb.ts @@ -1,11 +1,38 @@ -import * as mongo from 'monk'; - import config from '../conf'; const uri = config.mongodb.user && config.mongodb.pass - ? `mongodb://${config.mongodb.user}:${config.mongodb.pass}@${config.mongodb.host}:${config.mongodb.port}/${config.mongodb.db}` - : `mongodb://${config.mongodb.host}:${config.mongodb.port}/${config.mongodb.db}`; +? `mongodb://${config.mongodb.user}:${config.mongodb.pass}@${config.mongodb.host}:${config.mongodb.port}/${config.mongodb.db}` +: `mongodb://${config.mongodb.host}:${config.mongodb.port}/${config.mongodb.db}`; + +/** + * monk + */ +import * as mongo from 'monk'; const db = mongo(uri); export default db; + +/** + * MongoDB native module (officialy) + */ +import * as mongodb from 'mongodb' + +let mdb: mongodb.Db; + +const nativeDbConn = async (): Promise => { + if (mdb) return mdb; + + const db = await ((): Promise => new Promise((resolve, reject) => { + mongodb.MongoClient.connect(uri, (e, db) => { + if (e) return reject(e) + resolve(db) + }) + }))() + + mdb = db + + return db +} + +export { nativeDbConn } -- cgit v1.2.3-freya From 18b1ef29adc6166c2b1a327b378c3e159a18b80c Mon Sep 17 00:00:00 2001 From: otofune Date: Mon, 6 Nov 2017 15:18:45 +0900 Subject: migration to GridFS's DriveFile --- src/api/common/add-file-to-drive.ts | 1 + src/api/endpoints/drive.ts | 6 ++--- src/api/endpoints/drive/files.ts | 9 ++++--- src/api/endpoints/drive/files/find.ts | 10 ++++---- src/api/endpoints/drive/files/show.ts | 6 ++--- src/api/endpoints/drive/files/update.ts | 31 ++++++++++++++---------- src/api/endpoints/messaging/messages/create.ts | 6 ++--- src/api/endpoints/posts/create.ts | 6 ++--- src/api/endpoints/posts/timeline.ts | 24 +++++++++---------- src/api/serializers/drive-file.ts | 33 ++++++++++---------------- src/api/serializers/drive-folder.ts | 4 +++- 11 files changed, 66 insertions(+), 70 deletions(-) (limited to 'src/api') diff --git a/src/api/common/add-file-to-drive.ts b/src/api/common/add-file-to-drive.ts index f48f0cbcf5..376c470e93 100644 --- a/src/api/common/add-file-to-drive.ts +++ b/src/api/common/add-file-to-drive.ts @@ -154,6 +154,7 @@ export default ( comment: comment, properties: properties }); + console.dir(file) log(`drive file has been created ${file._id}`); diff --git a/src/api/endpoints/drive.ts b/src/api/endpoints/drive.ts index 41ad6301d7..b9c4e3e506 100644 --- a/src/api/endpoints/drive.ts +++ b/src/api/endpoints/drive.ts @@ -14,16 +14,16 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Calculate drive usage const usage = ((await DriveFile .aggregate([ - { $match: { user_id: user._id } }, + { $match: { metadata: { user_id: user._id } } }, { $project: { - datasize: true + length: true } }, { $group: { _id: null, - usage: { $sum: '$datasize' } + usage: { $sum: '$length' } } } ]))[0] || { diff --git a/src/api/endpoints/drive/files.ts b/src/api/endpoints/drive/files.ts index a68ae34817..eb0bfe6ba5 100644 --- a/src/api/endpoints/drive/files.ts +++ b/src/api/endpoints/drive/files.ts @@ -40,8 +40,10 @@ module.exports = (params, user, app) => new Promise(async (res, rej) => { _id: -1 }; const query = { - user_id: user._id, - folder_id: folderId + metadata: { + user_id: user._id, + folder_id: folderId + } } as any; if (sinceId) { sort._id = 1; @@ -57,9 +59,6 @@ module.exports = (params, user, app) => new Promise(async (res, rej) => { // Issue query const files = await DriveFile .find(query, { - fields: { - data: false - }, limit: limit, sort: sort }); diff --git a/src/api/endpoints/drive/files/find.ts b/src/api/endpoints/drive/files/find.ts index cd0b33f2ca..255faf94ec 100644 --- a/src/api/endpoints/drive/files/find.ts +++ b/src/api/endpoints/drive/files/find.ts @@ -24,12 +24,10 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Issue query const files = await DriveFile .find({ - name: name, - user_id: user._id, - folder_id: folderId - }, { - fields: { - data: false + metadata: { + name: name, + user_id: user._id, + folder_id: folderId } }); diff --git a/src/api/endpoints/drive/files/show.ts b/src/api/endpoints/drive/files/show.ts index 8dbc297e4f..9135a04c57 100644 --- a/src/api/endpoints/drive/files/show.ts +++ b/src/api/endpoints/drive/files/show.ts @@ -21,10 +21,8 @@ module.exports = (params, user) => new Promise(async (res, rej) => { const file = await DriveFile .findOne({ _id: fileId, - user_id: user._id - }, { - fields: { - data: false + metadata: { + user_id: user._id } }); diff --git a/src/api/endpoints/drive/files/update.ts b/src/api/endpoints/drive/files/update.ts index 1cfbdd8f0b..c4d2673688 100644 --- a/src/api/endpoints/drive/files/update.ts +++ b/src/api/endpoints/drive/files/update.ts @@ -20,25 +20,29 @@ module.exports = (params, user) => new Promise(async (res, rej) => { const [fileId, fileIdErr] = $(params.file_id).id().$; if (fileIdErr) return rej('invalid file_id param'); + console.dir(user) + // Fetch file const file = await DriveFile .findOne({ _id: fileId, - user_id: user._id - }, { - fields: { - data: false + metadata: { + user_id: user._id } }); + console.dir(file) + if (file === null) { return rej('file-not-found'); } + const updateQuery: any = {} + // Get 'name' parameter const [name, nameErr] = $(params.name).optional.string().pipe(validateFileName).$; if (nameErr) return rej('invalid name param'); - if (name) file.name = name; + if (name) updateQuery.name = name; // Get 'folder_id' parameter const [folderId, folderIdErr] = $(params.folder_id).optional.nullable.id().$; @@ -46,7 +50,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { if (folderId !== undefined) { if (folderId === null) { - file.folder_id = null; + updateQuery.folder_id = null; } else { // Fetch folder const folder = await DriveFolder @@ -59,19 +63,20 @@ module.exports = (params, user) => new Promise(async (res, rej) => { return rej('folder-not-found'); } - file.folder_id = folder._id; + updateQuery.folder_id = folder._id; } } - DriveFile.update(file._id, { - $set: { - name: file.name, - folder_id: file.folder_id - } + const updated = await DriveFile.update(file._id, { + $set: { metadata: updateQuery } }); + console.dir(updated) + // Serialize - const fileObj = await serialize(file); + const fileObj = await serialize(updated); + + console.dir(fileObj) // Response res(fileObj); diff --git a/src/api/endpoints/messaging/messages/create.ts b/src/api/endpoints/messaging/messages/create.ts index 8af55d850c..1d186268fb 100644 --- a/src/api/endpoints/messaging/messages/create.ts +++ b/src/api/endpoints/messaging/messages/create.ts @@ -54,9 +54,9 @@ module.exports = (params, user) => new Promise(async (res, rej) => { if (fileId !== undefined) { file = await DriveFile.findOne({ _id: fileId, - user_id: user._id - }, { - data: false + metadata: { + user_id: user._id + } }); if (file === null) { diff --git a/src/api/endpoints/posts/create.ts b/src/api/endpoints/posts/create.ts index f982b9ee93..1507639776 100644 --- a/src/api/endpoints/posts/create.ts +++ b/src/api/endpoints/posts/create.ts @@ -44,9 +44,9 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { // SELECT _id const entity = await DriveFile.findOne({ _id: mediaId, - user_id: user._id - }, { - _id: true + metadata: { + user_id: user._id + } }); if (entity === null) { diff --git a/src/api/endpoints/posts/timeline.ts b/src/api/endpoints/posts/timeline.ts index aa5aff5ba5..496de62b69 100644 --- a/src/api/endpoints/posts/timeline.ts +++ b/src/api/endpoints/posts/timeline.ts @@ -2,6 +2,7 @@ * Module dependencies */ import $ from 'cafy'; +import rap from '@prezzemolo/rap'; import Post from '../../models/post'; import ChannelWatching from '../../models/channel-watching'; import getFriends from '../../common/get-friends'; @@ -33,14 +34,15 @@ module.exports = (params, user, app) => new Promise(async (res, rej) => { return rej('cannot set since_id and max_id'); } - // ID list of the user itself and other users who the user follows - const followingIds = await getFriends(user._id); - - // Watchしているチャンネルを取得 - const watches = await ChannelWatching.find({ - user_id: user._id, - // 削除されたドキュメントは除く - deleted_at: { $exists: false } + const { followingIds, watchChannelIds } = await rap({ + // ID list of the user itself and other users who the user follows + followingIds: getFriends(user._id), + // Watchしているチャンネルを取得 + watchChannelIds: ChannelWatching.find({ + user_id: user._id, + // 削除されたドキュメントは除く + deleted_at: { $exists: false } + }).then(watches => watches.map(w => w.channel_id)) }); //#region Construct query @@ -65,7 +67,7 @@ module.exports = (params, user, app) => new Promise(async (res, rej) => { }, { // Watchしているチャンネルへの投稿 channel_id: { - $in: watches.map(w => w.channel_id) + $in: watchChannelIds } }] } as any; @@ -90,7 +92,5 @@ module.exports = (params, user, app) => new Promise(async (res, rej) => { }); // Serialize - res(await Promise.all(timeline.map(async post => - await serialize(post, user) - ))); + res(Promise.all(timeline.map(post => serialize(post, user)))); }); diff --git a/src/api/serializers/drive-file.ts b/src/api/serializers/drive-file.ts index b4e2ab064a..4c750f4c6b 100644 --- a/src/api/serializers/drive-file.ts +++ b/src/api/serializers/drive-file.ts @@ -31,44 +31,37 @@ export default ( if (mongo.ObjectID.prototype.isPrototypeOf(file)) { _file = await DriveFile.findOne({ _id: file - }, { - fields: { - data: false - } - }); + }); } else if (typeof file === 'string') { _file = await DriveFile.findOne({ _id: new mongo.ObjectID(file) - }, { - fields: { - data: false - } - }); + }); } else { _file = deepcopy(file); } - // Rename _id to id - _file.id = _file._id; - delete _file._id; + // rendered target + let _target: any = {}; + + _target.id = _file._id; - delete _file.data; + _target = Object.assign(_target, _file.metadata); - _file.url = `${config.drive_url}/${_file.id}/${encodeURIComponent(_file.name)}`; + _target.url = `${config.drive_url}/${_target.id}/${encodeURIComponent(_target.name)}`; - if (opts.detail && _file.folder_id) { + if (opts.detail && _target.folder_id) { // Populate folder - _file.folder = await serializeDriveFolder(_file.folder_id, { + _target.folder = await serializeDriveFolder(_target.folder_id, { detail: true }); } - if (opts.detail && _file.tags) { + if (opts.detail && _target.tags) { // Populate tags - _file.tags = await _file.tags.map(async (tag: any) => + _target.tags = await _target.tags.map(async (tag: any) => await serializeDriveTag(tag) ); } - resolve(_file); + resolve(_target); }); diff --git a/src/api/serializers/drive-folder.ts b/src/api/serializers/drive-folder.ts index a428464108..3b5f61aeed 100644 --- a/src/api/serializers/drive-folder.ts +++ b/src/api/serializers/drive-folder.ts @@ -44,7 +44,9 @@ const self = ( }); const childFilesCount = await DriveFile.count({ - folder_id: _folder.id + metadata: { + folder_id: _folder.id + } }); _folder.folders_count = childFoldersCount; -- cgit v1.2.3-freya From d0dab265f40a37cd715b7d4b64a364c78a7a35b9 Mon Sep 17 00:00:00 2001 From: otofune Date: Mon, 6 Nov 2017 15:27:16 +0900 Subject: serializers - drive-file: add created_at field by uploadedDate --- src/api/serializers/drive-file.ts | 1 + 1 file changed, 1 insertion(+) (limited to 'src/api') diff --git a/src/api/serializers/drive-file.ts b/src/api/serializers/drive-file.ts index 4c750f4c6b..f98cdaa599 100644 --- a/src/api/serializers/drive-file.ts +++ b/src/api/serializers/drive-file.ts @@ -44,6 +44,7 @@ export default ( let _target: any = {}; _target.id = _file._id; + _target.created_at = _file.uploadDate _target = Object.assign(_target, _file.metadata); -- cgit v1.2.3-freya From 0ee6d6592113c5b2df071f4451cb1c1697b59d61 Mon Sep 17 00:00:00 2001 From: otofune Date: Mon, 6 Nov 2017 15:45:21 +0900 Subject: fix timeline --- src/api/endpoints/posts/timeline.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/api') diff --git a/src/api/endpoints/posts/timeline.ts b/src/api/endpoints/posts/timeline.ts index 496de62b69..19578e59b1 100644 --- a/src/api/endpoints/posts/timeline.ts +++ b/src/api/endpoints/posts/timeline.ts @@ -92,5 +92,5 @@ module.exports = (params, user, app) => new Promise(async (res, rej) => { }); // Serialize - res(Promise.all(timeline.map(post => serialize(post, user)))); + res(await Promise.all(timeline.map(post => serialize(post, user)))); }); -- cgit v1.2.3-freya From 7553c6dd38c6f8574894a009238d946d50c53477 Mon Sep 17 00:00:00 2001 From: otofune Date: Mon, 6 Nov 2017 15:52:09 +0900 Subject: serializers - posts: no need Promise wrapping --- src/api/serializers/post.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/api') diff --git a/src/api/serializers/post.ts b/src/api/serializers/post.ts index e1ab784359..d1dcb66002 100644 --- a/src/api/serializers/post.ts +++ b/src/api/serializers/post.ts @@ -22,13 +22,13 @@ import rap from '@prezzemolo/rap'; * @param options? serialize options * @return response */ -const self = ( +const self = async ( post: string | mongo.ObjectID | IPost, me?: string | mongo.ObjectID | IUser, options?: { detail: boolean } -) => new Promise(async (resolve, reject) => { +) => { const opts = options || { detail: true, }; @@ -184,7 +184,7 @@ const self = ( // resolve promises in _post object _post = await rap(_post); - resolve(_post); -}); + return _post; +}; export default self; -- cgit v1.2.3-freya From 7b1fc2c5d62e229542e9411a29e078236a9d96db Mon Sep 17 00:00:00 2001 From: otofune Date: Mon, 6 Nov 2017 15:55:47 +0900 Subject: api - endpoint:timeline: unneed promise wrapping --- src/api/endpoints/posts/timeline.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'src/api') diff --git a/src/api/endpoints/posts/timeline.ts b/src/api/endpoints/posts/timeline.ts index 19578e59b1..978825a109 100644 --- a/src/api/endpoints/posts/timeline.ts +++ b/src/api/endpoints/posts/timeline.ts @@ -16,22 +16,22 @@ import serialize from '../../serializers/post'; * @param {any} app * @return {Promise} */ -module.exports = (params, user, app) => new Promise(async (res, rej) => { +module.exports = async (params, user, app) => { // Get 'limit' parameter const [limit = 10, limitErr] = $(params.limit).optional.number().range(1, 100).$; - if (limitErr) return rej('invalid limit param'); + if (limitErr) throw 'invalid limit param'; // Get 'since_id' parameter const [sinceId, sinceIdErr] = $(params.since_id).optional.id().$; - if (sinceIdErr) return rej('invalid since_id param'); + if (sinceIdErr) throw 'invalid since_id param'; // Get 'max_id' parameter const [maxId, maxIdErr] = $(params.max_id).optional.id().$; - if (maxIdErr) return rej('invalid max_id param'); + if (maxIdErr) throw 'invalid max_id param'; // Check if both of since_id and max_id is specified if (sinceId && maxId) { - return rej('cannot set since_id and max_id'); + throw 'cannot set since_id and max_id'; } const { followingIds, watchChannelIds } = await rap({ @@ -92,5 +92,6 @@ module.exports = (params, user, app) => new Promise(async (res, rej) => { }); // Serialize - res(await Promise.all(timeline.map(post => serialize(post, user)))); -}); + const _timeline = await Promise.all(timeline.map(post => serialize(post, user))) + return _timeline +}; -- cgit v1.2.3-freya From b50813649afed671b75189551342b179d8cd60f7 Mon Sep 17 00:00:00 2001 From: otofune Date: Mon, 6 Nov 2017 15:58:39 +0900 Subject: serializers - posts: fix awaiting --- src/api/serializers/post.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/api') diff --git a/src/api/serializers/post.ts b/src/api/serializers/post.ts index d1dcb66002..5788b226f4 100644 --- a/src/api/serializers/post.ts +++ b/src/api/serializers/post.ts @@ -94,7 +94,7 @@ const self = async ( if (opts.detail) { // Get previous post info _post.prev = (async () => { - const prev = Post.findOne({ + const prev = await Post.findOne({ user_id: _post.user_id, _id: { $lt: id -- cgit v1.2.3-freya From 5279d062df205514f1f3cf95e3aab4fee425a3e4 Mon Sep 17 00:00:00 2001 From: otofune Date: Mon, 6 Nov 2017 16:09:51 +0900 Subject: fix --- src/api/endpoints/drive/files.ts | 18 +++++++++--------- src/api/endpoints/drive/files/show.ts | 14 ++++++++------ src/api/endpoints/drive/folders/find.ts | 3 +-- src/api/serializers/drive-file.ts | 2 ++ 4 files changed, 20 insertions(+), 17 deletions(-) (limited to 'src/api') diff --git a/src/api/endpoints/drive/files.ts b/src/api/endpoints/drive/files.ts index eb0bfe6ba5..41687c4993 100644 --- a/src/api/endpoints/drive/files.ts +++ b/src/api/endpoints/drive/files.ts @@ -13,27 +13,27 @@ import serialize from '../../serializers/drive-file'; * @param {any} app * @return {Promise} */ -module.exports = (params, user, app) => new Promise(async (res, rej) => { +module.exports = async (params, user, app) => { // Get 'limit' parameter const [limit = 10, limitErr] = $(params.limit).optional.number().range(1, 100).$; - if (limitErr) return rej('invalid limit param'); + if (limitErr) throw 'invalid limit param'; // Get 'since_id' parameter const [sinceId, sinceIdErr] = $(params.since_id).optional.id().$; - if (sinceIdErr) return rej('invalid since_id param'); + if (sinceIdErr) throw 'invalid since_id param'; // Get 'max_id' parameter const [maxId, maxIdErr] = $(params.max_id).optional.id().$; - if (maxIdErr) return rej('invalid max_id param'); + if (maxIdErr) throw 'invalid max_id param'; // Check if both of since_id and max_id is specified if (sinceId && maxId) { - return rej('cannot set since_id and max_id'); + throw 'cannot set since_id and max_id'; } // Get 'folder_id' parameter const [folderId = null, folderIdErr] = $(params.folder_id).optional.nullable.id().$; - if (folderIdErr) return rej('invalid folder_id param'); + if (folderIdErr) throw 'invalid folder_id param'; // Construct query const sort = { @@ -64,6 +64,6 @@ module.exports = (params, user, app) => new Promise(async (res, rej) => { }); // Serialize - res(await Promise.all(files.map(async file => - await serialize(file)))); -}); + const _files = await Promise.all(files.map(file => serialize(file))); + return _files +}; diff --git a/src/api/endpoints/drive/files/show.ts b/src/api/endpoints/drive/files/show.ts index 9135a04c57..8830346008 100644 --- a/src/api/endpoints/drive/files/show.ts +++ b/src/api/endpoints/drive/files/show.ts @@ -12,10 +12,10 @@ import serialize from '../../../serializers/drive-file'; * @param {any} user * @return {Promise} */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = async (params, user) => { // Get 'file_id' parameter const [fileId, fileIdErr] = $(params.file_id).id().$; - if (fileIdErr) return rej('invalid file_id param'); + if (fileIdErr) throw 'invalid file_id param'; // Fetch file const file = await DriveFile @@ -27,11 +27,13 @@ module.exports = (params, user) => new Promise(async (res, rej) => { }); if (file === null) { - return rej('file-not-found'); + throw 'file-not-found'; } // Serialize - res(await serialize(file, { + const _file = await serialize(file, { detail: true - })); -}); + }); + + return _file +}; diff --git a/src/api/endpoints/drive/folders/find.ts b/src/api/endpoints/drive/folders/find.ts index cdf055839a..a5eb8e015d 100644 --- a/src/api/endpoints/drive/folders/find.ts +++ b/src/api/endpoints/drive/folders/find.ts @@ -30,6 +30,5 @@ module.exports = (params, user) => new Promise(async (res, rej) => { }); // Serialize - res(await Promise.all(folders.map(async folder => - await serialize(folder)))); + res(await Promise.all(folders.map(folder => serialize(folder)))); }); diff --git a/src/api/serializers/drive-file.ts b/src/api/serializers/drive-file.ts index f98cdaa599..9858c3b3c7 100644 --- a/src/api/serializers/drive-file.ts +++ b/src/api/serializers/drive-file.ts @@ -25,6 +25,8 @@ export default ( detail: false }, options); + if (!file) return reject('invalid file arg.') + let _file: any; // Populate the file if 'file' is ID -- cgit v1.2.3-freya From b266ed3e4f98ab16d95e52cff517d6519b78742a Mon Sep 17 00:00:00 2001 From: otofune Date: Mon, 6 Nov 2017 16:11:24 +0900 Subject: fix --- src/api/serializers/drive-file.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/api') diff --git a/src/api/serializers/drive-file.ts b/src/api/serializers/drive-file.ts index 9858c3b3c7..e749f80387 100644 --- a/src/api/serializers/drive-file.ts +++ b/src/api/serializers/drive-file.ts @@ -25,8 +25,6 @@ export default ( detail: false }, options); - if (!file) return reject('invalid file arg.') - let _file: any; // Populate the file if 'file' is ID @@ -42,6 +40,8 @@ export default ( _file = deepcopy(file); } + if (!_file) return reject('invalid file arg.') + // rendered target let _target: any = {}; -- cgit v1.2.3-freya From 64be0d6deddef4b8caced377dc22f94425cc4358 Mon Sep 17 00:00:00 2001 From: otofune Date: Mon, 6 Nov 2017 16:22:18 +0900 Subject: MongoDBの階層構造検索に関する思い違いの修正 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/endpoints/drive.ts | 2 +- src/api/endpoints/drive/files.ts | 6 ++---- src/api/endpoints/drive/files/find.ts | 8 +++----- src/api/endpoints/drive/files/show.ts | 4 +--- src/api/endpoints/drive/files/update.ts | 19 +++++-------------- src/api/endpoints/messaging/messages/create.ts | 4 +--- src/api/endpoints/posts/create.ts | 4 +--- src/api/serializers/drive-folder.ts | 4 +--- 8 files changed, 15 insertions(+), 36 deletions(-) (limited to 'src/api') diff --git a/src/api/endpoints/drive.ts b/src/api/endpoints/drive.ts index b9c4e3e506..d92473633a 100644 --- a/src/api/endpoints/drive.ts +++ b/src/api/endpoints/drive.ts @@ -14,7 +14,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Calculate drive usage const usage = ((await DriveFile .aggregate([ - { $match: { metadata: { user_id: user._id } } }, + { $match: { 'metadata.user_id': user._id } }, { $project: { length: true diff --git a/src/api/endpoints/drive/files.ts b/src/api/endpoints/drive/files.ts index 41687c4993..035916b309 100644 --- a/src/api/endpoints/drive/files.ts +++ b/src/api/endpoints/drive/files.ts @@ -40,10 +40,8 @@ module.exports = async (params, user, app) => { _id: -1 }; const query = { - metadata: { - user_id: user._id, - folder_id: folderId - } + 'metadata.user_id': user._id, + 'metadata.folder_id': folderId } as any; if (sinceId) { sort._id = 1; diff --git a/src/api/endpoints/drive/files/find.ts b/src/api/endpoints/drive/files/find.ts index 255faf94ec..1c818131d7 100644 --- a/src/api/endpoints/drive/files/find.ts +++ b/src/api/endpoints/drive/files/find.ts @@ -24,11 +24,9 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Issue query const files = await DriveFile .find({ - metadata: { - name: name, - user_id: user._id, - folder_id: folderId - } + 'metadata.name': name, + 'metadata.user_id': user._id, + 'metadata.folder_id': folderId }); // Serialize diff --git a/src/api/endpoints/drive/files/show.ts b/src/api/endpoints/drive/files/show.ts index 8830346008..0a19b19939 100644 --- a/src/api/endpoints/drive/files/show.ts +++ b/src/api/endpoints/drive/files/show.ts @@ -21,9 +21,7 @@ module.exports = async (params, user) => { const file = await DriveFile .findOne({ _id: fileId, - metadata: { - user_id: user._id - } + 'metadata.user_id': user._id }); if (file === null) { diff --git a/src/api/endpoints/drive/files/update.ts b/src/api/endpoints/drive/files/update.ts index c4d2673688..7a6d2562fb 100644 --- a/src/api/endpoints/drive/files/update.ts +++ b/src/api/endpoints/drive/files/update.ts @@ -20,19 +20,14 @@ module.exports = (params, user) => new Promise(async (res, rej) => { const [fileId, fileIdErr] = $(params.file_id).id().$; if (fileIdErr) return rej('invalid file_id param'); - console.dir(user) // Fetch file const file = await DriveFile .findOne({ _id: fileId, - metadata: { - user_id: user._id - } + 'metadata.user_id': user._id }); - console.dir(file) - if (file === null) { return rej('file-not-found'); } @@ -42,7 +37,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Get 'name' parameter const [name, nameErr] = $(params.name).optional.string().pipe(validateFileName).$; if (nameErr) return rej('invalid name param'); - if (name) updateQuery.name = name; + if (name) updateQuery['metadata.name'] = name; // Get 'folder_id' parameter const [folderId, folderIdErr] = $(params.folder_id).optional.nullable.id().$; @@ -50,7 +45,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { if (folderId !== undefined) { if (folderId === null) { - updateQuery.folder_id = null; + updateQuery['metadata.folder_id'] = null; } else { // Fetch folder const folder = await DriveFolder @@ -63,21 +58,17 @@ module.exports = (params, user) => new Promise(async (res, rej) => { return rej('folder-not-found'); } - updateQuery.folder_id = folder._id; + updateQuery['metadata.folder_id'] = folder._id; } } const updated = await DriveFile.update(file._id, { - $set: { metadata: updateQuery } + $set: { updateQuery } }); - console.dir(updated) - // Serialize const fileObj = await serialize(updated); - console.dir(fileObj) - // Response res(fileObj); diff --git a/src/api/endpoints/messaging/messages/create.ts b/src/api/endpoints/messaging/messages/create.ts index 1d186268fb..149852c093 100644 --- a/src/api/endpoints/messaging/messages/create.ts +++ b/src/api/endpoints/messaging/messages/create.ts @@ -54,9 +54,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { if (fileId !== undefined) { file = await DriveFile.findOne({ _id: fileId, - metadata: { - user_id: user._id - } + 'metadata.user_id': user._id }); if (file === null) { diff --git a/src/api/endpoints/posts/create.ts b/src/api/endpoints/posts/create.ts index 1507639776..4f4b7e2e83 100644 --- a/src/api/endpoints/posts/create.ts +++ b/src/api/endpoints/posts/create.ts @@ -44,9 +44,7 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { // SELECT _id const entity = await DriveFile.findOne({ _id: mediaId, - metadata: { - user_id: user._id - } + 'metadata.user_id': user._id }); if (entity === null) { diff --git a/src/api/serializers/drive-folder.ts b/src/api/serializers/drive-folder.ts index 3b5f61aeed..6ebf454a28 100644 --- a/src/api/serializers/drive-folder.ts +++ b/src/api/serializers/drive-folder.ts @@ -44,9 +44,7 @@ const self = ( }); const childFilesCount = await DriveFile.count({ - metadata: { - folder_id: _folder.id - } + 'metadata.folder_id': _folder.id }); _folder.folders_count = childFoldersCount; -- cgit v1.2.3-freya From 4c5a4d259738ba617bf29d2158d180cc5fa8401c Mon Sep 17 00:00:00 2001 From: otofune Date: Mon, 6 Nov 2017 16:26:17 +0900 Subject: core - fix metadata searching --- src/api/common/add-file-to-drive.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/api') diff --git a/src/api/common/add-file-to-drive.ts b/src/api/common/add-file-to-drive.ts index 376c470e93..1f882389ac 100644 --- a/src/api/common/add-file-to-drive.ts +++ b/src/api/common/add-file-to-drive.ts @@ -81,9 +81,7 @@ export default ( // Check if there is a file with the same hash const much = await DriveFile.findOne({ md5: hash, - metadata: { - user_id: user._id - } + 'metadata.user_id': user._id }); if (much !== null) { @@ -97,7 +95,7 @@ export default ( // Calculate drive usage const usage = ((await DriveFile .aggregate([ - { $match: { metadata: { user_id: user._id } } }, + { $match: { 'metadata.user_id': user._id } }, { $project: { length: true }}, -- cgit v1.2.3-freya From 04648db1c235b0de14d3e0a2dc83f9346d0408f8 Mon Sep 17 00:00:00 2001 From: otofune Date: Mon, 6 Nov 2017 16:29:13 +0900 Subject: remove console --- src/api/common/add-file-to-drive.ts | 1 - 1 file changed, 1 deletion(-) (limited to 'src/api') diff --git a/src/api/common/add-file-to-drive.ts b/src/api/common/add-file-to-drive.ts index 1f882389ac..dff2d52356 100644 --- a/src/api/common/add-file-to-drive.ts +++ b/src/api/common/add-file-to-drive.ts @@ -152,7 +152,6 @@ export default ( comment: comment, properties: properties }); - console.dir(file) log(`drive file has been created ${file._id}`); -- cgit v1.2.3-freya From d5cc4cc9c28eb6a981ce37859def97cd7c57abc6 Mon Sep 17 00:00:00 2001 From: otofune Date: Mon, 6 Nov 2017 16:32:01 +0900 Subject: fix lint (automattic) --- src/api/common/add-file-to-drive.ts | 20 +++++++++--------- src/api/endpoints/drive/files.ts | 2 +- src/api/endpoints/drive/files/show.ts | 2 +- src/api/endpoints/drive/files/update.ts | 3 +-- src/api/endpoints/posts/timeline.ts | 4 ++-- src/api/models/drive-file.ts | 10 ++++----- src/api/serializers/drive-file.ts | 4 ++-- src/db/mongodb.ts | 18 ++++++++--------- src/file/server.ts | 36 ++++++++++++++++----------------- 9 files changed, 49 insertions(+), 50 deletions(-) (limited to 'src/api') diff --git a/src/api/common/add-file-to-drive.ts b/src/api/common/add-file-to-drive.ts index dff2d52356..f9c22ccacd 100644 --- a/src/api/common/add-file-to-drive.ts +++ b/src/api/common/add-file-to-drive.ts @@ -14,16 +14,16 @@ import { Duplex } from 'stream'; const log = debug('misskey:register-drive-file'); const addToGridFS = (name, binary, metadata): Promise => new Promise(async (resolve, reject) => { - const dataStream = new Duplex() - dataStream.push(binary) - dataStream.push(null) - - const bucket = await getGridFSBucket() - const writeStream = bucket.openUploadStream(name, { metadata }) - writeStream.once('finish', (doc) => { resolve(doc) }) - writeStream.on('error', reject) - dataStream.pipe(writeStream) -}) + const dataStream = new Duplex(); + dataStream.push(binary); + dataStream.push(null); + + const bucket = await getGridFSBucket(); + const writeStream = bucket.openUploadStream(name, { metadata }); + writeStream.once('finish', (doc) => { resolve(doc); }); + writeStream.on('error', reject); + dataStream.pipe(writeStream); +}); /** * Add file to drive diff --git a/src/api/endpoints/drive/files.ts b/src/api/endpoints/drive/files.ts index 035916b309..53b48a8bec 100644 --- a/src/api/endpoints/drive/files.ts +++ b/src/api/endpoints/drive/files.ts @@ -63,5 +63,5 @@ module.exports = async (params, user, app) => { // Serialize const _files = await Promise.all(files.map(file => serialize(file))); - return _files + return _files; }; diff --git a/src/api/endpoints/drive/files/show.ts b/src/api/endpoints/drive/files/show.ts index 0a19b19939..3c7cf774f9 100644 --- a/src/api/endpoints/drive/files/show.ts +++ b/src/api/endpoints/drive/files/show.ts @@ -33,5 +33,5 @@ module.exports = async (params, user) => { detail: true }); - return _file + return _file; }; diff --git a/src/api/endpoints/drive/files/update.ts b/src/api/endpoints/drive/files/update.ts index 7a6d2562fb..4e56b30ace 100644 --- a/src/api/endpoints/drive/files/update.ts +++ b/src/api/endpoints/drive/files/update.ts @@ -20,7 +20,6 @@ module.exports = (params, user) => new Promise(async (res, rej) => { const [fileId, fileIdErr] = $(params.file_id).id().$; if (fileIdErr) return rej('invalid file_id param'); - // Fetch file const file = await DriveFile .findOne({ @@ -32,7 +31,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { return rej('file-not-found'); } - const updateQuery: any = {} + const updateQuery: any = {}; // Get 'name' parameter const [name, nameErr] = $(params.name).optional.string().pipe(validateFileName).$; diff --git a/src/api/endpoints/posts/timeline.ts b/src/api/endpoints/posts/timeline.ts index 978825a109..203413e23a 100644 --- a/src/api/endpoints/posts/timeline.ts +++ b/src/api/endpoints/posts/timeline.ts @@ -92,6 +92,6 @@ module.exports = async (params, user, app) => { }); // Serialize - const _timeline = await Promise.all(timeline.map(post => serialize(post, user))) - return _timeline + const _timeline = await Promise.all(timeline.map(post => serialize(post, user))); + return _timeline; }; diff --git a/src/api/models/drive-file.ts b/src/api/models/drive-file.ts index 79a87f6572..8968d065cd 100644 --- a/src/api/models/drive-file.ts +++ b/src/api/models/drive-file.ts @@ -8,14 +8,14 @@ const collection = monkDb.get('drive_files.files'); export default collection as any; // fuck type definition const getGridFSBucket = async (): Promise => { - const db = await nativeDbConn() + const db = await nativeDbConn(); const bucket = new mongodb.GridFSBucket(db, { bucketName: 'drive_files' - }) - return bucket -} + }); + return bucket; +}; -export { getGridFSBucket } +export { getGridFSBucket }; export function validateFileName(name: string): boolean { return ( diff --git a/src/api/serializers/drive-file.ts b/src/api/serializers/drive-file.ts index e749f80387..2af7db5726 100644 --- a/src/api/serializers/drive-file.ts +++ b/src/api/serializers/drive-file.ts @@ -40,13 +40,13 @@ export default ( _file = deepcopy(file); } - if (!_file) return reject('invalid file arg.') + if (!_file) return reject('invalid file arg.'); // rendered target let _target: any = {}; _target.id = _file._id; - _target.created_at = _file.uploadDate + _target.created_at = _file.uploadDate; _target = Object.assign(_target, _file.metadata); diff --git a/src/db/mongodb.ts b/src/db/mongodb.ts index 75f1a1d3c6..c978e6460f 100644 --- a/src/db/mongodb.ts +++ b/src/db/mongodb.ts @@ -16,7 +16,7 @@ export default db; /** * MongoDB native module (officialy) */ -import * as mongodb from 'mongodb' +import * as mongodb from 'mongodb'; let mdb: mongodb.Db; @@ -25,14 +25,14 @@ const nativeDbConn = async (): Promise => { const db = await ((): Promise => new Promise((resolve, reject) => { mongodb.MongoClient.connect(uri, (e, db) => { - if (e) return reject(e) - resolve(db) - }) - }))() + if (e) return reject(e); + resolve(db); + }); + }))(); - mdb = db + mdb = db; - return db -} + return db; +}; -export { nativeDbConn } +export { nativeDbConn }; diff --git a/src/file/server.ts b/src/file/server.ts index f38599b89c..375f29487d 100644 --- a/src/file/server.ts +++ b/src/file/server.ts @@ -97,7 +97,7 @@ app.get('/:id', async (req, res) => { return; } - const fileId = new mongodb.ObjectID(req.params.id) + const fileId = new mongodb.ObjectID(req.params.id); const file = await DriveFile.findOne({ _id: fileId }); if (file == null) { @@ -105,18 +105,18 @@ app.get('/:id', async (req, res) => { return; } - const bucket = await getGridFSBucket() + const bucket = await getGridFSBucket(); const buffer = await ((id): Promise => new Promise((resolve, reject) => { - const chunks = [] - const readableStream = bucket.openDownloadStream(id) - readableStream.on('data', chunk => { + const chunks = []; + const readableStream = bucket.openDownloadStream(id); + readableStream.on('data', chunk => { chunks.push(chunk); - }) + }); readableStream.on('end', () => { - resolve(Buffer.concat(chunks)) - }) - }))(fileId) + resolve(Buffer.concat(chunks)); + }); + }))(fileId); send(buffer, file.metadata.type, req, res); }); @@ -128,7 +128,7 @@ app.get('/:id/:name', async (req, res) => { return; } - const fileId = new mongodb.ObjectID(req.params.id) + const fileId = new mongodb.ObjectID(req.params.id); const file = await DriveFile.findOne({ _id: fileId }); if (file == null) { @@ -136,18 +136,18 @@ app.get('/:id/:name', async (req, res) => { return; } - const bucket = await getGridFSBucket() + const bucket = await getGridFSBucket(); const buffer = await ((id): Promise => new Promise((resolve, reject) => { - const chunks = [] - const readableStream = bucket.openDownloadStream(id) - readableStream.on('data', chunk => { + const chunks = []; + const readableStream = bucket.openDownloadStream(id); + readableStream.on('data', chunk => { chunks.push(chunk); - }) + }); readableStream.on('end', () => { - resolve(Buffer.concat(chunks)) - }) - }))(fileId) + resolve(Buffer.concat(chunks)); + }); + }))(fileId); send(buffer, file.metadata.type, req, res); }); -- cgit v1.2.3-freya From 3be69a8cb7bacca181fa400f234fd77c1d1d5bde Mon Sep 17 00:00:00 2001 From: otofune Date: Mon, 6 Nov 2017 16:49:07 +0900 Subject: /drive/files/update - return collectly value --- src/api/endpoints/drive/files/update.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'src/api') diff --git a/src/api/endpoints/drive/files/update.ts b/src/api/endpoints/drive/files/update.ts index 4e56b30ace..d7b858c2ba 100644 --- a/src/api/endpoints/drive/files/update.ts +++ b/src/api/endpoints/drive/files/update.ts @@ -31,12 +31,10 @@ module.exports = (params, user) => new Promise(async (res, rej) => { return rej('file-not-found'); } - const updateQuery: any = {}; - // Get 'name' parameter const [name, nameErr] = $(params.name).optional.string().pipe(validateFileName).$; if (nameErr) return rej('invalid name param'); - if (name) updateQuery['metadata.name'] = name; + if (name) file.metadata.name = name; // Get 'folder_id' parameter const [folderId, folderIdErr] = $(params.folder_id).optional.nullable.id().$; @@ -44,7 +42,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { if (folderId !== undefined) { if (folderId === null) { - updateQuery['metadata.folder_id'] = null; + file.metadata.folder_id = null; } else { // Fetch folder const folder = await DriveFolder @@ -57,16 +55,19 @@ module.exports = (params, user) => new Promise(async (res, rej) => { return rej('folder-not-found'); } - updateQuery['metadata.folder_id'] = folder._id; + file.metadata.folder_id = folder._id; } } - const updated = await DriveFile.update(file._id, { - $set: { updateQuery } + await DriveFile.update(file._id, { + $set: { + 'metadata.name': file.metadata.name, + 'metadata.folder_id': file.metadata.folder_id + } }); // Serialize - const fileObj = await serialize(updated); + const fileObj = await serialize(file); // Response res(fileObj); -- cgit v1.2.3-freya From c1fc3b9f6ec176999932958a7856d160317b7762 Mon Sep 17 00:00:00 2001 From: otofune Date: Mon, 6 Nov 2017 18:30:49 +0900 Subject: add safety guard to serializers & fix importing uncorrect serializer --- src/api/endpoints/drive/folders/update.ts | 2 +- src/api/serializers/post.ts | 2 ++ src/api/serializers/user.ts | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) (limited to 'src/api') diff --git a/src/api/endpoints/drive/folders/update.ts b/src/api/endpoints/drive/folders/update.ts index eec2757878..4f2e3d2a7a 100644 --- a/src/api/endpoints/drive/folders/update.ts +++ b/src/api/endpoints/drive/folders/update.ts @@ -4,7 +4,7 @@ import $ from 'cafy'; import DriveFolder from '../../../models/drive-folder'; import { isValidFolderName } from '../../../models/drive-folder'; -import serialize from '../../../serializers/drive-file'; +import serialize from '../../../serializers/drive-folder'; import event from '../../../event'; /** diff --git a/src/api/serializers/post.ts b/src/api/serializers/post.ts index 5788b226f4..5a63384f0e 100644 --- a/src/api/serializers/post.ts +++ b/src/api/serializers/post.ts @@ -57,6 +57,8 @@ const self = async ( _post = deepcopy(post); } + if (!_post) throw 'invalid post arg.'; + const id = _post._id; // Rename _id to id diff --git a/src/api/serializers/user.ts b/src/api/serializers/user.ts index d00f073897..0d24d6cc04 100644 --- a/src/api/serializers/user.ts +++ b/src/api/serializers/user.ts @@ -56,6 +56,8 @@ export default ( _user = deepcopy(user); } + if (!_user) return reject('invalid user arg.'); + // Me const meId: mongo.ObjectID = me ? mongo.ObjectID.prototype.isPrototypeOf(me) -- cgit v1.2.3-freya From d7e1ffb0055f0786a707015350a14351b8a0fbf0 Mon Sep 17 00:00:00 2001 From: otofune Date: Mon, 6 Nov 2017 18:38:59 +0900 Subject: remove whitespace --- src/api/serializers/post.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/api') diff --git a/src/api/serializers/post.ts b/src/api/serializers/post.ts index 5a63384f0e..03fd120772 100644 --- a/src/api/serializers/post.ts +++ b/src/api/serializers/post.ts @@ -57,7 +57,7 @@ const self = async ( _post = deepcopy(post); } - if (!_post) throw 'invalid post arg.'; + if (!_post) throw 'invalid post arg.'; const id = _post._id; -- cgit v1.2.3-freya From 26d4ebfe1a22615f4f69bf94cc6872659e6e4125 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 6 Nov 2017 20:03:50 +0900 Subject: Refactor --- src/api/endpoints/posts/timeline.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'src/api') diff --git a/src/api/endpoints/posts/timeline.ts b/src/api/endpoints/posts/timeline.ts index 203413e23a..7af435e82a 100644 --- a/src/api/endpoints/posts/timeline.ts +++ b/src/api/endpoints/posts/timeline.ts @@ -34,11 +34,11 @@ module.exports = async (params, user, app) => { throw 'cannot set since_id and max_id'; } - const { followingIds, watchChannelIds } = await rap({ + const { followingIds, watchingChannelIds } = await rap({ // ID list of the user itself and other users who the user follows followingIds: getFriends(user._id), // Watchしているチャンネルを取得 - watchChannelIds: ChannelWatching.find({ + watchingChannelIds: ChannelWatching.find({ user_id: user._id, // 削除されたドキュメントは除く deleted_at: { $exists: false } @@ -67,7 +67,7 @@ module.exports = async (params, user, app) => { }, { // Watchしているチャンネルへの投稿 channel_id: { - $in: watchChannelIds + $in: watchingChannelIds } }] } as any; @@ -92,6 +92,5 @@ module.exports = async (params, user, app) => { }); // Serialize - const _timeline = await Promise.all(timeline.map(post => serialize(post, user))); - return _timeline; + return await Promise.all(timeline.map(post => serialize(post, user))); }; -- cgit v1.2.3-freya From 6df3db23d6277e48e68bd4a9ee78d1ac4576332c Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 6 Nov 2017 22:10:40 +0900 Subject: Fix index creation --- src/api/models/drive-file.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/api') diff --git a/src/api/models/drive-file.ts b/src/api/models/drive-file.ts index 8968d065cd..976c97e188 100644 --- a/src/api/models/drive-file.ts +++ b/src/api/models/drive-file.ts @@ -3,7 +3,7 @@ import monkDb, { nativeDbConn } from '../../db/mongodb'; const collection = monkDb.get('drive_files.files'); -(collection as any).createIndex('hash'); // fuck type definition +(collection as any).createIndex('md5'); // fuck type definition export default collection as any; // fuck type definition -- cgit v1.2.3-freya From b06950d5e70bc9b6321f6e144042caf1f3397d0d Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 6 Nov 2017 22:22:48 +0900 Subject: ハッシュ値はMongoDB側で管理されているから手動でインデックスを明示する必要はなさそうだ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/models/drive-file.ts | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/api') diff --git a/src/api/models/drive-file.ts b/src/api/models/drive-file.ts index 976c97e188..802ee5a5fe 100644 --- a/src/api/models/drive-file.ts +++ b/src/api/models/drive-file.ts @@ -3,8 +3,6 @@ import monkDb, { nativeDbConn } from '../../db/mongodb'; const collection = monkDb.get('drive_files.files'); -(collection as any).createIndex('md5'); // fuck type definition - export default collection as any; // fuck type definition const getGridFSBucket = async (): Promise => { -- cgit v1.2.3-freya From fb422b4d603c53a70712caba55b35a48a8c2e619 Mon Sep 17 00:00:00 2001 From: otofune Date: Tue, 7 Nov 2017 09:30:51 +0900 Subject: use 'name' param as GridFS file's 'filename' --- src/api/common/add-file-to-drive.ts | 3 +-- src/api/endpoints/drive/files/find.ts | 2 +- src/api/endpoints/drive/files/update.ts | 4 ++-- src/api/serializers/drive-file.ts | 1 + src/file/server.ts | 2 +- tools/migration/use-gridfs.js | 4 +++- 6 files changed, 9 insertions(+), 7 deletions(-) (limited to 'src/api') diff --git a/src/api/common/add-file-to-drive.ts b/src/api/common/add-file-to-drive.ts index f9c22ccacd..e1baf08191 100644 --- a/src/api/common/add-file-to-drive.ts +++ b/src/api/common/add-file-to-drive.ts @@ -144,11 +144,10 @@ export default ( } // Create DriveFile document - const file = await addToGridFS(`${user._id}/${name}`, data, { + const file = await addToGridFS(name, data, { user_id: user._id, folder_id: folder !== null ? folder._id : null, type: mime, - name: name, comment: comment, properties: properties }); diff --git a/src/api/endpoints/drive/files/find.ts b/src/api/endpoints/drive/files/find.ts index 1c818131d7..a1cdf1643e 100644 --- a/src/api/endpoints/drive/files/find.ts +++ b/src/api/endpoints/drive/files/find.ts @@ -24,7 +24,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Issue query const files = await DriveFile .find({ - 'metadata.name': name, + filename: name, 'metadata.user_id': user._id, 'metadata.folder_id': folderId }); diff --git a/src/api/endpoints/drive/files/update.ts b/src/api/endpoints/drive/files/update.ts index d7b858c2ba..f265142c4d 100644 --- a/src/api/endpoints/drive/files/update.ts +++ b/src/api/endpoints/drive/files/update.ts @@ -34,7 +34,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Get 'name' parameter const [name, nameErr] = $(params.name).optional.string().pipe(validateFileName).$; if (nameErr) return rej('invalid name param'); - if (name) file.metadata.name = name; + if (name) file.filename = name; // Get 'folder_id' parameter const [folderId, folderIdErr] = $(params.folder_id).optional.nullable.id().$; @@ -61,7 +61,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { await DriveFile.update(file._id, { $set: { - 'metadata.name': file.metadata.name, + filename: file.filename, 'metadata.folder_id': file.metadata.folder_id } }); diff --git a/src/api/serializers/drive-file.ts b/src/api/serializers/drive-file.ts index 2af7db5726..57b74cd975 100644 --- a/src/api/serializers/drive-file.ts +++ b/src/api/serializers/drive-file.ts @@ -47,6 +47,7 @@ export default ( _target.id = _file._id; _target.created_at = _file.uploadDate; + _target.name = _file.filename; _target = Object.assign(_target, _file.metadata); diff --git a/src/file/server.ts b/src/file/server.ts index 1152b650be..39c2cdd2a1 100644 --- a/src/file/server.ts +++ b/src/file/server.ts @@ -97,7 +97,7 @@ async function sendFileById(req: express.Request, res: express.Response): Promis const file = await DriveFile.findOne({ _id: fileId }); // validate name - if (req.params.name !== undefined && req.params.name !== file.metadata.name) { + if (req.params.name !== undefined && req.params.name !== file.filename) { res.status(404).send('there is no file has given name'); return; } diff --git a/tools/migration/use-gridfs.js b/tools/migration/use-gridfs.js index d41514416c..148f9be261 100644 --- a/tools/migration/use-gridfs.js +++ b/tools/migration/use-gridfs.js @@ -21,15 +21,17 @@ const migrateToGridFS = async (doc) => { const id = doc._id const buffer = doc.data.buffer const created_at = doc.created_at + const name = doc.name delete doc._id delete doc.created_at delete doc.datasize delete doc.hash delete doc.data + delete doc.name const bucket = await getGridFSBucket() - const added = await writeToGridFS(bucket, buffer, id, `${id}/${doc.name}`, { metadata: doc }) + const added = await writeToGridFS(bucket, buffer, id, name, { metadata: doc }) const result = await DriveFile.update(id, { $set: { -- cgit v1.2.3-freya From a85d5ac4bbfe35652be1bef47f59a79ace7b2c73 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 7 Nov 2017 21:04:32 +0900 Subject: #882 --- src/api/common/add-file-to-drive.ts | 7 +++--- src/api/serializers/drive-file.ts | 1 + tools/migration/issue_882.js | 44 +++++++++++++++++++++++++++++++++++++ tools/migration/use-gridfs.js | 4 +++- 4 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 tools/migration/issue_882.js (limited to 'src/api') diff --git a/src/api/common/add-file-to-drive.ts b/src/api/common/add-file-to-drive.ts index e1baf08191..a96906d291 100644 --- a/src/api/common/add-file-to-drive.ts +++ b/src/api/common/add-file-to-drive.ts @@ -13,13 +13,13 @@ import { Duplex } from 'stream'; const log = debug('misskey:register-drive-file'); -const addToGridFS = (name, binary, metadata): Promise => new Promise(async (resolve, reject) => { +const addToGridFS = (name, binary, type, metadata): Promise => new Promise(async (resolve, reject) => { const dataStream = new Duplex(); dataStream.push(binary); dataStream.push(null); const bucket = await getGridFSBucket(); - const writeStream = bucket.openUploadStream(name, { metadata }); + const writeStream = bucket.openUploadStream(name, { contentType: type, metadata }); writeStream.once('finish', (doc) => { resolve(doc); }); writeStream.on('error', reject); dataStream.pipe(writeStream); @@ -144,10 +144,9 @@ export default ( } // Create DriveFile document - const file = await addToGridFS(name, data, { + const file = await addToGridFS(name, data, mime, { user_id: user._id, folder_id: folder !== null ? folder._id : null, - type: mime, comment: comment, properties: properties }); diff --git a/src/api/serializers/drive-file.ts b/src/api/serializers/drive-file.ts index 57b74cd975..3b76979a44 100644 --- a/src/api/serializers/drive-file.ts +++ b/src/api/serializers/drive-file.ts @@ -48,6 +48,7 @@ export default ( _target.id = _file._id; _target.created_at = _file.uploadDate; _target.name = _file.filename; + _target.type = _file.contentType; _target = Object.assign(_target, _file.metadata); diff --git a/tools/migration/issue_882.js b/tools/migration/issue_882.js new file mode 100644 index 0000000000..67a1551e04 --- /dev/null +++ b/tools/migration/issue_882.js @@ -0,0 +1,44 @@ +// for Node.js interpret + +const { default: DriveFile } = require('../../built/api/models/drive-file') + +const migrate = async (doc) => { + const result = await DriveFile.update(doc._id, { + $set: { + contentType: doc.metadata.type + }, + $unset: { + 'metadata.type': '' + } + }) + return result.ok === 1 +} + +async function main() { + let i = 0; + + const count = await db.get('drive_files').count({}); + + const iterate = async () => { + if (i == count) return true; + console.log(`${i} / ${count}`); + const doc = (await db.get('drive_files').find({}, { limit: 1, skip: i }))[0] + const res = await migrate(doc); + if (!res) { + return false; + } else { + i++ + return await iterate(); + } + } + + const res = await iterate(); + + if (res) { + return 'ok'; + } else { + throw 'something happened'; + } +} + +main().then(console.dir).catch(console.error) diff --git a/tools/migration/use-gridfs.js b/tools/migration/use-gridfs.js index c5883e4562..106cbd3889 100644 --- a/tools/migration/use-gridfs.js +++ b/tools/migration/use-gridfs.js @@ -22,6 +22,7 @@ const migrateToGridFS = async (doc) => { const buffer = doc.data ? doc.data.buffer : Buffer.from([0x00]) // アップロードのバグなのか知らないけどなぜか data が存在しない drive_file ドキュメントがまれにあることがわかったので const created_at = doc.created_at const name = doc.name + const type = doc.type delete doc._id delete doc.created_at @@ -29,9 +30,10 @@ const migrateToGridFS = async (doc) => { delete doc.hash delete doc.data delete doc.name + delete doc.type const bucket = await getGridFSBucket() - const added = await writeToGridFS(bucket, buffer, id, name, { metadata: doc }) + const added = await writeToGridFS(bucket, buffer, id, name, { contentType: type, metadata: doc }) const result = await DriveFile.update(id, { $set: { -- cgit v1.2.3-freya From 253747ecfb8245d94fe0110e01d8f2ca65a28f5d Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 8 Nov 2017 14:43:42 +0900 Subject: Fix bugs --- src/api/endpoints/drive/stream.ts | 5 +---- src/api/serializers/drive-file.ts | 2 ++ src/web/app/mobile/tags/drive/file-viewer.tag | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) (limited to 'src/api') diff --git a/src/api/endpoints/drive/stream.ts b/src/api/endpoints/drive/stream.ts index 32f7ac7e0a..58e7d11678 100644 --- a/src/api/endpoints/drive/stream.ts +++ b/src/api/endpoints/drive/stream.ts @@ -52,15 +52,12 @@ module.exports = (params, user) => new Promise(async (res, rej) => { }; } if (type) { - query.type = new RegExp(`^${type.replace(/\*/g, '.+?')}$`); + query.contentType = new RegExp(`^${type.replace(/\*/g, '.+?')}$`); } // Issue query const files = await DriveFile .find(query, { - fields: { - data: false - }, limit: limit, sort: sort }); diff --git a/src/api/serializers/drive-file.ts b/src/api/serializers/drive-file.ts index 3b76979a44..dcdaa01fab 100644 --- a/src/api/serializers/drive-file.ts +++ b/src/api/serializers/drive-file.ts @@ -49,6 +49,8 @@ export default ( _target.created_at = _file.uploadDate; _target.name = _file.filename; _target.type = _file.contentType; + _target.datasize = _file.length; + _target.md5 = _file.md5; _target = Object.assign(_target, _file.metadata); diff --git a/src/web/app/mobile/tags/drive/file-viewer.tag b/src/web/app/mobile/tags/drive/file-viewer.tag index e6129652b0..8dc49a0865 100644 --- a/src/web/app/mobile/tags/drive/file-viewer.tag +++ b/src/web/app/mobile/tags/drive/file-viewer.tag @@ -44,7 +44,7 @@

%i18n:mobile.tags.mk-drive-file-viewer.hash%

- { file.hash } + { file.md5 } diff --git a/src/web/app/desktop/tags/index.js b/src/web/app/desktop/tags/index.js index 7997bcc7f2..c36a06e499 100644 --- a/src/web/app/desktop/tags/index.js +++ b/src/web/app/desktop/tags/index.js @@ -57,6 +57,7 @@ require('./pages/entrance.tag'); require('./pages/entrance/signin.tag'); require('./pages/entrance/signup.tag'); require('./pages/home.tag'); +require('./pages/home-customize.tag'); require('./pages/user.tag'); require('./pages/post.tag'); require('./pages/search.tag'); diff --git a/src/web/app/desktop/tags/pages/home-customize.tag b/src/web/app/desktop/tags/pages/home-customize.tag new file mode 100644 index 0000000000..4434015615 --- /dev/null +++ b/src/web/app/desktop/tags/pages/home-customize.tag @@ -0,0 +1,14 @@ + + + + + + + diff --git a/src/web/app/desktop/tags/settings.tag b/src/web/app/desktop/tags/settings.tag index eabddfb432..4c16f9eaa8 100644 --- a/src/web/app/desktop/tags/settings.tag +++ b/src/web/app/desktop/tags/settings.tag @@ -38,6 +38,7 @@

デザイン

+ ホームをカスタマイズ
diff --git a/src/web/app/desktop/tags/ui.tag b/src/web/app/desktop/tags/ui.tag index 0a3e8d9c53..6a4982877f 100644 --- a/src/web/app/desktop/tags/ui.tag +++ b/src/web/app/desktop/tags/ui.tag @@ -37,7 +37,7 @@ - +
diff --git a/src/web/app/init.js b/src/web/app/init.js index 5a6899ed4f..7e3c2ee377 100644 --- a/src/web/app/init.js +++ b/src/web/app/init.js @@ -11,7 +11,6 @@ import checkForUpdate from './common/scripts/check-for-update'; import Connection from './common/scripts/home-stream'; import Progress from './common/scripts/loading'; import mixin from './common/mixins'; -import generateDefaultUserdata from './common/scripts/generate-default-userdata'; import CONFIG from './common/scripts/config'; require('./common/tags'); @@ -156,9 +155,7 @@ function fetchme(token, cb) { res.json().then(i => { me = i; me.token = token; - - // initialize it if user data is empty - me.data ? done() : init(); + done(); }); }, () => { // When failure // Render the error screen @@ -170,17 +167,6 @@ function fetchme(token, cb) { function done() { if (cb) cb(me); } - - // Initialize user data - function init() { - const data = generateDefaultUserdata(); - api(token, 'i/appdata/set', { - data - }).then(() => { - me.data = data; - done(); - }); - } } // BSoD diff --git a/tools/migration/node.2017-11-08..js b/tools/migration/node.2017-11-08..js new file mode 100644 index 0000000000..e25b83b3f3 --- /dev/null +++ b/tools/migration/node.2017-11-08..js @@ -0,0 +1,89 @@ +const uuid = require('uuid'); +const { default: User } = require('../../built/api/models/user') +const { default: zip } = require('@prezzemolo/zip') + +const home = { + left: [ + 'profile', + 'calendar', + 'activity', + 'rss-reader', + 'trends', + 'photo-stream', + 'version' + ], + right: [ + 'broadcast', + 'notifications', + 'user-recommendation', + 'recommended-polls', + 'server', + 'donation', + 'nav', + 'tips' + ] +}; + + +const migrate = async (doc) => { + + //#region Construct home data + const homeData = []; + + home.left.forEach(widget => { + homeData.push({ + name: widget, + id: uuid(), + place: 'left', + data: {} + }); + }); + + home.right.forEach(widget => { + homeData.push({ + name: widget, + id: uuid(), + place: 'right', + data: {} + }); + }); + //#endregion + + const result = await User.update(doc._id, { + $unset: { + data: '' + }, + $set: { + 'settings': {}, + 'client_settings.home': homeData, + 'client_settings.show_donation': false + } + }) + + return added && result.ok === 1 +} + +async function main() { + const count = await db.get('users').count(); + + console.log(`there are ${count} users.`) + + const dop = Number.parseInt(process.argv[2]) || 5 + const idop = ((count - (count % dop)) / dop) + 1 + + return zip( + 1, + async (time) => { + console.log(`${time} / ${idop}`) + const docs = await db.get('users').find({}, { limit: dop, skip: time * dop }) + return Promise.all(docs.map(migrate)) + }, + idop + ).then(a => { + const rv = [] + a.forEach(e => rv.push(...e)) + return rv + }) +} + +main().then(console.dir).catch(console.error) -- cgit v1.2.3-freya From 860a322c3637ba8933639a84fc8f335c4c6323fb Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 9 Nov 2017 02:55:03 +0900 Subject: [API] Improve drive/files --- src/api/endpoints/drive/files.ts | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/api') diff --git a/src/api/endpoints/drive/files.ts b/src/api/endpoints/drive/files.ts index 53b48a8bec..b2e094775c 100644 --- a/src/api/endpoints/drive/files.ts +++ b/src/api/endpoints/drive/files.ts @@ -35,6 +35,10 @@ module.exports = async (params, user, app) => { 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 @@ -53,6 +57,9 @@ module.exports = async (params, user, app) => { $lt: maxId }; } + if (type) { + query.contentType = new RegExp(`^${type.replace(/\*/g, '.+?')}$`); + } // Issue query const files = await DriveFile -- cgit v1.2.3-freya From 3f0f307104300e148b02f05fd51ac833f9b75b01 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 11 Nov 2017 00:27:02 +0900 Subject: [LINE] 通知の表示に対応 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/bot/core.ts | 24 ++++++++++++++++++++- src/api/models/notification.ts | 39 ++++++++++++++++++++++++++++++++++ src/common/get-notification-summary.ts | 28 ++++++++++++++++++++++++ src/common/get-reaction-emoji.ts | 14 ++++++++++++ 4 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 src/common/get-notification-summary.ts create mode 100644 src/common/get-reaction-emoji.ts (limited to 'src/api') diff --git a/src/api/bot/core.ts b/src/api/bot/core.ts index 8324390880..ff3f681804 100644 --- a/src/api/bot/core.ts +++ b/src/api/bot/core.ts @@ -5,6 +5,7 @@ import User, { IUser, init as initUser } from '../models/user'; import getPostSummary from '../../common/get-post-summary'; import getUserSummary from '../../common/get-user-summary'; +import getNotificationSummary from '../../common/get-notification-summary'; import Othello, { ai as othelloAi } from '../../common/othello'; @@ -84,6 +85,7 @@ export default class BotCore extends EventEmitter { 'logout, signout: サインアウトします\n' + 'post: 投稿します\n' + 'tl: タイムラインを見ます\n' + + 'no: 通知を見ます\n' + '@<ユーザー名>: ユーザーを表示します'; case 'me': @@ -115,6 +117,11 @@ export default class BotCore extends EventEmitter { case 'タイムライン': return await this.tlCommand(); + case 'no': + case 'notifications': + case '通知': + return await this.notificationsCommand(); + case 'guessing-game': case '数当てゲーム': this.setContext(new GuessingGameContext(this)); @@ -155,6 +162,7 @@ export default class BotCore extends EventEmitter { this.emit('updated'); } + // TODO: if (this.user == null) return 'まずサインインしてください。'; を @signinRequired みたいなデコレータでいい感じにする public async tlCommand(): Promise { if (this.user == null) return 'まずサインインしてください。'; @@ -163,7 +171,21 @@ export default class BotCore extends EventEmitter { }, this.user); const text = tl - .map(post => getPostSummary(post)) + .map(post => post.user.name + ': ' + getPostSummary(post)) + .join('\n-----\n'); + + return text; + } + + public async notificationsCommand(): Promise { + if (this.user == null) return 'まずサインインしてください。'; + + const notifications = await require('../endpoints/i/notifications')({ + limit: 5 + }, this.user); + + const text = notifications + .map(notification => getNotificationSummary(notification)) .join('\n-----\n'); return text; diff --git a/src/api/models/notification.ts b/src/api/models/notification.ts index 1065e8baaa..ecd1c25e10 100644 --- a/src/api/models/notification.ts +++ b/src/api/models/notification.ts @@ -1,8 +1,47 @@ import * as mongo from 'mongodb'; import db from '../../db/mongodb'; +import { IUser } from './user'; export default db.get('notifications') as any; // fuck type definition export interface INotification { _id: mongo.ObjectID; + created_at: Date; + + /** + * 通知の受信者 + */ + notifiee?: IUser; + + /** + * 通知の受信者 + */ + notifiee_id: mongo.ObjectID; + + /** + * イニシエータ(initiator)。通知を行う原因となったユーザー + */ + notifier?: IUser; + + /** + * イニシエータ(initiator)。通知を行う原因となったユーザー + */ + notifier_id: mongo.ObjectID; + + /** + * 通知の種類。 + * follow - フォローされた + * mention - 投稿で自分が言及された + * reply - (自分または自分がWatchしている)投稿が返信された + * repost - (自分または自分がWatchしている)投稿がRepostされた + * quote - (自分または自分がWatchしている)投稿が引用Repostされた + * reaction - (自分または自分がWatchしている)投稿にリアクションされた + * poll_vote - (自分または自分がWatchしている)投稿の投票に投票された + */ + type: 'follow' | 'mention' | 'reply' | 'repost' | 'quote' | 'reaction' | 'poll_vote'; + + /** + * 通知が読まれたかどうか + */ + is_read: Boolean; } diff --git a/src/common/get-notification-summary.ts b/src/common/get-notification-summary.ts new file mode 100644 index 0000000000..bf8610599c --- /dev/null +++ b/src/common/get-notification-summary.ts @@ -0,0 +1,28 @@ +import { INotification } from '../api/models/notification'; +import getPostSummary from './get-post-summary'; +import getReactionEmoji from './get-reaction-emoji'; + +/** + * 通知を表す文字列を取得します。 + * @param notification 通知 + */ +export default function(notification: INotification & any): string { + switch (notification.type) { + case 'follow': + return `${notification.notifier.name}にフォローされました`; + case 'mention': + return `言及されました: ${notification.notifier.name}「${getPostSummary(notification.post)}」`; + case 'reply': + return `返信されました: ${notification.notifier.name}「${getPostSummary(notification.post)}」`; + case 'repost': + return `Repostされました: ${notification.notifier.name}「${getPostSummary(notification.post)}」`; + case 'quote': + return `引用されました: ${notification.notifier.name}「${getPostSummary(notification.post)}」`; + case 'reaction': + return `リアクションされました: ${notification.notifier.name} <${getReactionEmoji(notification.reaction)}>「${getPostSummary(notification.post)}」`; + case 'poll_vote': + return `投票されました: ${notification.notifier.name}「${getPostSummary(notification.post)}」`; + default: + return `<不明な通知タイプ: ${notification.type}>`; + } +} diff --git a/src/common/get-reaction-emoji.ts b/src/common/get-reaction-emoji.ts new file mode 100644 index 0000000000..c661205379 --- /dev/null +++ b/src/common/get-reaction-emoji.ts @@ -0,0 +1,14 @@ +export default function(reaction: string): string { + switch (reaction) { + case 'like': return '👍'; + case 'love': return '❤️'; + case 'laugh': return '😆'; + case 'hmm': return '🤔'; + case 'surprise': return '😮'; + case 'congrats': return '🎉'; + case 'angry': return '💢'; + case 'confused': return '😥'; + case 'pudding': return '🍮'; + default: return ''; + } +} -- cgit v1.2.3-freya From f75f4b47190d90da2acb7de32777302d3d632793 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 11 Nov 2017 00:49:19 +0900 Subject: [Bot] タイムラインや通知を遡れるように MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/bot/core.ts | 151 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 117 insertions(+), 34 deletions(-) (limited to 'src/api') diff --git a/src/api/bot/core.ts b/src/api/bot/core.ts index ff3f681804..4b8d244471 100644 --- a/src/api/bot/core.ts +++ b/src/api/bot/core.ts @@ -63,7 +63,7 @@ export default class BotCore extends EventEmitter { return bot; } - public async q(query: string): Promise { + public async q(query: string): Promise { if (this.context != null) { return await this.context.q(query); } @@ -86,7 +86,9 @@ export default class BotCore extends EventEmitter { 'post: 投稿します\n' + 'tl: タイムラインを見ます\n' + 'no: 通知を見ます\n' + - '@<ユーザー名>: ユーザーを表示します'; + '@<ユーザー名>: ユーザーを表示します\n' + + '\n' + + 'タイムラインや通知を見た後、「次」というとさらに遡ることができます。'; case 'me': return this.user ? `${this.user.name}としてサインインしています。\n\n${getUserSummary(this.user)}` : 'サインインしていません'; @@ -115,12 +117,16 @@ export default class BotCore extends EventEmitter { case 'tl': case 'タイムライン': - return await this.tlCommand(); + if (this.user == null) return 'まずサインインしてください。'; + this.setContext(new TlContext(this)); + return await this.context.greet(); case 'no': case 'notifications': case '通知': - return await this.notificationsCommand(); + if (this.user == null) return 'まずサインインしてください。'; + this.setContext(new NotificationsContext(this)); + return await this.context.greet(); case 'guessing-game': case '数当てゲーム': @@ -162,36 +168,7 @@ export default class BotCore extends EventEmitter { this.emit('updated'); } - // TODO: if (this.user == null) return 'まずサインインしてください。'; を @signinRequired みたいなデコレータでいい感じにする - public async tlCommand(): Promise { - if (this.user == null) return 'まずサインインしてください。'; - - const tl = await require('../endpoints/posts/timeline')({ - limit: 5 - }, this.user); - - const text = tl - .map(post => post.user.name + ': ' + getPostSummary(post)) - .join('\n-----\n'); - - return text; - } - - public async notificationsCommand(): Promise { - if (this.user == null) return 'まずサインインしてください。'; - - const notifications = await require('../endpoints/i/notifications')({ - limit: 5 - }, this.user); - - const text = notifications - .map(notification => getNotificationSummary(notification)) - .join('\n-----\n'); - - return text; - } - - public async showUserCommand(q: string): Promise { + public async showUserCommand(q: string): Promise { try { const user = await require('../endpoints/users/show')({ username: q.substr(1) @@ -222,6 +199,8 @@ abstract class Context extends EventEmitter { if (data.type == 'guessing-game') return GuessingGameContext.import(bot, data.content); if (data.type == 'othello') return OthelloContext.import(bot, data.content); if (data.type == 'post') return PostContext.import(bot, data.content); + if (data.type == 'tl') return TlContext.import(bot, data.content); + if (data.type == 'notifications') return NotificationsContext.import(bot, data.content); if (data.type == 'signin') return SigninContext.import(bot, data.content); return null; } @@ -307,6 +286,110 @@ class PostContext extends Context { } } +class TlContext extends Context { + private next: string = null; + + public async greet(): Promise { + return await this.getTl(); + } + + public async q(query: string): Promise { + if (query == '次') { + return await this.getTl(); + } else { + this.bot.clearContext(); + return await this.bot.q(query); + } + } + + private async getTl() { + const tl = await require('../endpoints/posts/timeline')({ + limit: 5, + max_id: this.next ? this.next : undefined + }, this.bot.user); + + if (tl.length > 0) { + this.next = tl[tl.length - 1].id; + this.emit('updated'); + + const text = tl + .map(post => post.user.name + ': ' + getPostSummary(post)) + .join('\n-----\n'); + + return text; + } else { + return 'タイムラインに表示するものがありません...'; + } + } + + public export() { + return { + type: 'tl', + content: { + next: this.next, + } + }; + } + + public static import(bot: BotCore, data: any) { + const context = new TlContext(bot); + context.next = data.next; + return context; + } +} + +class NotificationsContext extends Context { + private next: string = null; + + public async greet(): Promise { + return await this.getNotifications(); + } + + public async q(query: string): Promise { + if (query == '次') { + return await this.getNotifications(); + } else { + this.bot.clearContext(); + return await this.bot.q(query); + } + } + + private async getNotifications() { + const notifications = await require('../endpoints/i/notifications')({ + limit: 5, + max_id: this.next ? this.next : undefined + }, this.bot.user); + + if (notifications.length > 0) { + this.next = notifications[notifications.length - 1].id; + this.emit('updated'); + + const text = notifications + .map(notification => getNotificationSummary(notification)) + .join('\n-----\n'); + + return text; + } else { + return '通知はありません'; + } + } + + public export() { + return { + type: 'notifications', + content: { + next: this.next, + } + }; + } + + public static import(bot: BotCore, data: any) { + const context = new NotificationsContext(bot); + context.next = data.next; + return context; + } +} + class GuessingGameContext extends Context { private secret: number; private history: number[] = []; -- cgit v1.2.3-freya From f3dd5da931fb4cb946d9243b6735b68927ca8a61 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 11 Nov 2017 01:15:11 +0900 Subject: Improve readability --- src/api/bot/core.ts | 2 +- src/common/get-notification-summary.ts | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src/api') diff --git a/src/api/bot/core.ts b/src/api/bot/core.ts index 4b8d244471..ddae6405f5 100644 --- a/src/api/bot/core.ts +++ b/src/api/bot/core.ts @@ -313,7 +313,7 @@ class TlContext extends Context { this.emit('updated'); const text = tl - .map(post => post.user.name + ': ' + getPostSummary(post)) + .map(post => `${post.user.name}\n「${getPostSummary(post)}」`) .join('\n-----\n'); return text; diff --git a/src/common/get-notification-summary.ts b/src/common/get-notification-summary.ts index b49a170b65..03db722c84 100644 --- a/src/common/get-notification-summary.ts +++ b/src/common/get-notification-summary.ts @@ -10,17 +10,17 @@ export default function(notification: any): string { case 'follow': return `${notification.user.name}にフォローされました`; case 'mention': - return `言及されました: ${notification.user.name}「${getPostSummary(notification.post)}」`; + return `言及されました:\n${notification.user.name}「${getPostSummary(notification.post)}」`; case 'reply': - return `返信されました: ${notification.user.name}「${getPostSummary(notification.post)}」`; + return `返信されました:\n${notification.user.name}「${getPostSummary(notification.post)}」`; case 'repost': - return `Repostされました: ${notification.user.name}「${getPostSummary(notification.post)}」`; + return `Repostされました:\n${notification.user.name}「${getPostSummary(notification.post)}」`; case 'quote': - return `引用されました: ${notification.user.name}「${getPostSummary(notification.post)}」`; + return `引用されました:\n${notification.user.name}「${getPostSummary(notification.post)}」`; case 'reaction': - return `リアクションされました: ${notification.user.name} <${getReactionEmoji(notification.reaction)}>「${getPostSummary(notification.post)}」`; + return `リアクションされました:\n${notification.user.name} <${getReactionEmoji(notification.reaction)}>「${getPostSummary(notification.post)}」`; case 'poll_vote': - return `投票されました: ${notification.user.name}「${getPostSummary(notification.post)}」`; + return `投票されました:\n${notification.user.name}「${getPostSummary(notification.post)}」`; default: return `<不明な通知タイプ: ${notification.type}>`; } -- cgit v1.2.3-freya From 10af82b0b39c8816af975a7bb3876cea6fa28c19 Mon Sep 17 00:00:00 2001 From: こぴなたみぽ Date: Sat, 11 Nov 2017 02:25:24 +0900 Subject: Update notification.ts --- src/api/models/notification.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/api') diff --git a/src/api/models/notification.ts b/src/api/models/notification.ts index ecd1c25e10..e3dc6c70a3 100644 --- a/src/api/models/notification.ts +++ b/src/api/models/notification.ts @@ -19,12 +19,12 @@ export interface INotification { notifiee_id: mongo.ObjectID; /** - * イニシエータ(initiator)。通知を行う原因となったユーザー + * イニシエータ(initiator)、Origin。通知を行う原因となったユーザー */ notifier?: IUser; /** - * イニシエータ(initiator)。通知を行う原因となったユーザー + * イニシエータ(initiator)、Origin。通知を行う原因となったユーザー */ notifier_id: mongo.ObjectID; -- cgit v1.2.3-freya From ba85942f76c0b7c0b458b48289569ada9aee2be1 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 11 Nov 2017 10:58:13 +0900 Subject: #905 --- locales/en.yml | 9 ++ locales/ja.yml | 9 ++ src/api/endpoints/posts/timeline.ts | 23 ++- src/web/app/desktop/tags/home-widgets/timeline.tag | 18 ++- .../app/desktop/tags/home-widgets/timemachine.tag | 165 +++++++++++++++++++++ src/web/app/desktop/tags/home.tag | 4 +- src/web/app/desktop/tags/index.js | 1 + 7 files changed, 224 insertions(+), 5 deletions(-) create mode 100644 src/web/app/desktop/tags/home-widgets/timemachine.tag (limited to 'src/api') diff --git a/locales/en.yml b/locales/en.yml index c69dc22b1d..4eae825074 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -13,6 +13,15 @@ common: months_ago: "{}month(s) ago" years_ago: "{}year(s) ago" + weekday-short: + sunday: "S" + monday: "M" + tuesday: "T" + wednesday: "W" + thursday: "T" + friday: "F" + satruday: "S" + reactions: like: "Like" love: "Love" diff --git a/locales/ja.yml b/locales/ja.yml index 782b87bd83..82589aadff 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -13,6 +13,15 @@ common: months_ago: "{}ヶ月前" years_ago: "{}年前" + weekday-short: + sunday: "日" + monday: "月" + tuesday: "火" + wednesday: "水" + thursday: "木" + friday: "金" + satruday: "土" + reactions: like: "いいね" love: "ハート" diff --git a/src/api/endpoints/posts/timeline.ts b/src/api/endpoints/posts/timeline.ts index 7af435e82a..0d08b95463 100644 --- a/src/api/endpoints/posts/timeline.ts +++ b/src/api/endpoints/posts/timeline.ts @@ -29,9 +29,17 @@ module.exports = async (params, user, app) => { const [maxId, maxIdErr] = $(params.max_id).optional.id().$; if (maxIdErr) throw 'invalid max_id param'; - // Check if both of since_id and max_id is specified - if (sinceId && maxId) { - throw 'cannot set since_id and max_id'; + // Get 'since_date' parameter + const [sinceDate, sinceDateErr] = $(params.since_date).optional.number().$; + if (sinceDateErr) throw 'invalid since_date param'; + + // Get 'max_date' parameter + const [maxDate, maxDateErr] = $(params.max_date).optional.number().$; + if (maxDateErr) throw 'invalid max_date param'; + + // Check if only one of since_id, max_id, since_date, max_date specified + if ([sinceId, maxId, sinceDate, maxDate].filter(x => x != null).length > 1) { + throw 'only one of since_id, max_id, since_date, max_date can be specified'; } const { followingIds, watchingChannelIds } = await rap({ @@ -81,6 +89,15 @@ module.exports = async (params, user, app) => { query._id = { $lt: maxId }; + } else if (sinceDate) { + sort._id = 1; + query.created_at = { + $gt: new Date(sinceDate) + }; + } else if (maxDate) { + query.created_at = { + $lt: new Date(maxDate) + }; } //#endregion diff --git a/src/web/app/desktop/tags/home-widgets/timeline.tag b/src/web/app/desktop/tags/home-widgets/timeline.tag index 08d96ad715..735783049c 100644 --- a/src/web/app/desktop/tags/home-widgets/timeline.tag +++ b/src/web/app/desktop/tags/home-widgets/timeline.tag @@ -70,7 +70,13 @@ }; this.load = (cb) => { - this.api('posts/timeline').then(posts => { + this.update({ + isLoading: true + }); + + this.api('posts/timeline', { + max_date: this.date ? this.date.getTime() : undefined + }).then(posts => { this.update({ isLoading: false, isEmpty: posts.length == 0 @@ -114,5 +120,15 @@ const current = window.scrollY + window.innerHeight; if (current > document.body.offsetHeight - 8) this.more(); }; + + this.warp = date => { + console.log(date); + + this.update({ + date: date + }); + + this.load(); + }; diff --git a/src/web/app/desktop/tags/home-widgets/timemachine.tag b/src/web/app/desktop/tags/home-widgets/timemachine.tag new file mode 100644 index 0000000000..b6c53e0284 --- /dev/null +++ b/src/web/app/desktop/tags/home-widgets/timemachine.tag @@ -0,0 +1,165 @@ + + +

{ year }/{ month }

+ + +
+
{ weekdayText[i] }
+
+
{ i + 1 }
+
+ + +
diff --git a/src/web/app/desktop/tags/home.tag b/src/web/app/desktop/tags/home.tag index ecfe23adee..452499d70c 100644 --- a/src/web/app/desktop/tags/home.tag +++ b/src/web/app/desktop/tags/home.tag @@ -5,6 +5,7 @@ @@ -36,6 +36,31 @@ :scope display block + &[data-compact] + font-size 0.8em + + > .history + > a + &:last-child + border-bottom none + + &:not([data-is-me]):not([data-is-read]) + > div + background-image none + border-left solid 4px #3aa2dc + + > div + padding 16px + + > header + > mk-time + font-size 1em + + > .avatar + width 42px + height 42px + margin 0 12px 0 0 + > .search display block position -webkit-sticky @@ -75,7 +100,7 @@ > input margin 0 - padding 0 12px 0 38px + padding 0 0 0 38px width 100% font-size 1em line-height 38px @@ -299,22 +324,59 @@ this.mixin('i'); this.mixin('api'); + this.mixin('messaging-index-stream'); + this.connection = this.messagingIndexStream.getConnection(); + this.connectionId = this.messagingIndexStream.use(); + this.searchResult = []; + this.registerMessage = message => { + message.is_me = message.user_id == this.I.id; + message._click = () => { + this.trigger('navigate-user', message.is_me ? message.recipient : message.user); + }; + }; + this.on('mount', () => { + this.connection.on('message', this.onMessage); + this.connection.on('read', this.onRead); + this.api('messaging/history').then(history => { this.isLoading = false; history.forEach(message => { - message.is_me = message.user_id == this.I.id - message._click = () => { - this.trigger('navigate-user', message.is_me ? message.recipient : message.user); - }; + this.registerMessage(message); }); this.history = history; this.update(); }); }); + this.on('unmount', () => { + this.connection.off('message', this.onMessage); + this.connection.off('read', this.onRead); + this.messagingIndexStream.dispose(this.connectionId); + }); + + this.onMessage = message => { + this.history = this.history.filter(m => !( + (m.recipient_id == message.recipient_id && m.user_id == message.user_id) || + (m.recipient_id == message.user_id && m.user_id == message.recipient_id))); + + this.registerMessage(message); + + this.history.unshift(message); + this.update(); + }; + + this.onRead = ids => { + ids.forEach(id => { + const found = this.history.find(m => m.id == id); + if (found) found.is_read = true; + }); + + this.update(); + }; + this.search = () => { const q = this.refs.search.value; if (q == '') { diff --git a/src/web/app/desktop/tags/home-widgets/messaging.tag b/src/web/app/desktop/tags/home-widgets/messaging.tag new file mode 100644 index 0000000000..d7e8375c8d --- /dev/null +++ b/src/web/app/desktop/tags/home-widgets/messaging.tag @@ -0,0 +1,50 @@ + + +

%i18n:desktop.tags.mk-messaging-home-widget.title%

+
+ + + +
diff --git a/src/web/app/desktop/tags/home.tag b/src/web/app/desktop/tags/home.tag index 88d06d2baa..5a0d194ba5 100644 --- a/src/web/app/desktop/tags/home.tag +++ b/src/web/app/desktop/tags/home.tag @@ -19,6 +19,7 @@ + diff --git a/src/web/app/desktop/tags/index.ts b/src/web/app/desktop/tags/index.ts index 8b5a52d670..cd22dde352 100644 --- a/src/web/app/desktop/tags/index.ts +++ b/src/web/app/desktop/tags/index.ts @@ -44,6 +44,7 @@ require('./home-widgets/channel.tag'); require('./home-widgets/timemachine.tag'); require('./home-widgets/post-form.tag'); require('./home-widgets/access-log.tag'); +require('./home-widgets/messaging.tag'); require('./timeline.tag'); require('./messaging/window.tag'); require('./messaging/room-window.tag'); -- cgit v1.2.3-freya From 64aedcaa6b3e9170e77c428ee306830464b42bcf Mon Sep 17 00:00:00 2001 From: otofune Date: Tue, 14 Nov 2017 03:46:30 +0900 Subject: add-file-to-drive - Promise百烈拳とメモリ削減 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 9 +- src/api/common/add-file-to-drive.ts | 344 ++++++++++++++--------- src/api/endpoints/drive/files/upload_from_url.ts | 74 ++++- 3 files changed, 272 insertions(+), 155 deletions(-) (limited to 'src/api') diff --git a/package.json b/package.json index 5b26ee574a..235e64f7cc 100644 --- a/package.json +++ b/package.json @@ -54,13 +54,14 @@ "@types/node": "8.0.49", "@types/page": "1.5.32", "@types/proxy-addr": "2.0.0", - "@types/seedrandom": "2.4.27", "@types/ratelimiter": "2.1.28", "@types/redis": "2.8.1", - "@types/request": "2.0.7", + "@types/request": "^2.0.7", "@types/rimraf": "2.0.2", "@types/riot": "3.6.1", + "@types/seedrandom": "2.4.27", "@types/serve-favicon": "2.2.29", + "@types/tmp": "0.0.33", "@types/uuid": "3.4.3", "@types/webpack": "3.8.0", "@types/webpack-stream": "3.2.8", @@ -111,7 +112,6 @@ "deep-equal": "1.0.1", "deepcopy": "0.6.3", "diskusage": "0.2.2", - "download": "6.2.5", "elasticsearch": "13.3.1", "escape-regexp": "0.0.1", "express": "4.15.4", @@ -140,7 +140,7 @@ "recaptcha-promise": "0.1.3", "reconnecting-websocket": "3.2.2", "redis": "2.8.0", - "request": "2.83.0", + "request": "^2.83.0", "rimraf": "2.6.2", "riot": "3.7.4", "rndstr": "1.0.0", @@ -152,6 +152,7 @@ "syuilo-password-strength": "0.0.1", "tcp-port-used": "0.1.2", "textarea-caret": "3.0.2", + "tmp": "0.0.33", "ts-node": "3.3.0", "typescript": "2.6.1", "uuid": "3.1.0", diff --git a/src/api/common/add-file-to-drive.ts b/src/api/common/add-file-to-drive.ts index a96906d291..a7c7cb4644 100644 --- a/src/api/common/add-file-to-drive.ts +++ b/src/api/common/add-file-to-drive.ts @@ -9,28 +9,34 @@ import DriveFolder from '../models/drive-folder'; import serialize from '../serializers/drive-file'; import event from '../event'; import config from '../../conf'; -import { Duplex } from 'stream'; +import { Buffer } from 'buffer'; +import * as fs from 'fs'; +import * as tmp from 'tmp'; +import * as stream from 'stream'; const log = debug('misskey:register-drive-file'); -const addToGridFS = (name, binary, type, metadata): Promise => new Promise(async (resolve, reject) => { - const dataStream = new Duplex(); - dataStream.push(binary); - dataStream.push(null); - - const bucket = await getGridFSBucket(); - const writeStream = bucket.openUploadStream(name, { contentType: type, metadata }); - writeStream.once('finish', (doc) => { resolve(doc); }); - writeStream.on('error', reject); - dataStream.pipe(writeStream); -}); +const tmpFile = (): Promise => new Promise((resolve, reject) => { + tmp.file((e, path) => { + if (e) return reject(e) + resolve(path) + }) +}) + +const addToGridFS = (name: string, readable: stream.Readable, type: string, metadata: any): Promise => + getGridFSBucket() + .then(bucket => new Promise((resolve, reject) => { + const writeStream = bucket.openUploadStream(name, { contentType: type, metadata }); + writeStream.once('finish', (doc) => { resolve(doc); }); + writeStream.on('error', reject); + readable.pipe(writeStream); + })) /** * Add file to drive * * @param user User who wish to add file - * @param fileName File name - * @param data Contents + * @param file File path, binary, or readableStream * @param comment Comment * @param type File type * @param folderId Folder ID @@ -39,139 +45,201 @@ const addToGridFS = (name, binary, type, metadata): Promise => new Promise( */ export default ( user: any, - data: Buffer, + file: string | Buffer | stream.Readable, name: string = null, comment: string = null, folderId: mongodb.ObjectID = null, force: boolean = false -) => new Promise(async (resolve, reject) => { +) => new Promise((resolve, reject) => { log(`registering ${name} (user: ${user.username})`); - // File size - const size = data.byteLength; - - log(`size is ${size}`); - - // File type - let mime = 'application/octet-stream'; - const type = fileType(data); - if (type !== null) { - mime = type.mime; - - if (name === null) { - name = `untitled.${type.ext}`; + // Get file path + new Promise((res: (v: string) => void, rej) => { + if (typeof file === 'string') { + res(file) + return } - } else { - if (name === null) { - name = 'untitled'; + if (file instanceof Buffer) { + tmpFile() + .then(path => { + fs.writeFile(path, file, (err) => { + if (err) rej(err) + res(path) + }) + }) + .catch(rej) + return } - } - - log(`type is ${mime}`); - - // Generate hash - const hash = crypto - .createHash('md5') - .update(data) - .digest('hex') as string; - - log(`hash is ${hash}`); - - if (!force) { - // Check if there is a file with the same hash - const much = await DriveFile.findOne({ - md5: hash, - 'metadata.user_id': user._id - }); - - if (much !== null) { - log('file with same hash is found'); - return resolve(much); - } else { - log('file with same hash is not found'); + if (typeof file === 'object' && typeof file.read === 'function') { + tmpFile() + .then(path => { + const readable: stream.Readable = file + const writable = fs.createWriteStream(path) + readable + .on('error', rej) + .on('end', () => { + res(path) + }) + .pipe(writable) + .on('error', rej) + }) + .catch(rej) } - } - - // Calculate drive usage - const usage = ((await DriveFile - .aggregate([ - { $match: { 'metadata.user_id': user._id } }, - { $project: { - length: true - }}, - { $group: { - _id: null, - usage: { $sum: '$length' } - }} - ]))[0] || { - usage: 0 - }).usage; - - log(`drive usage is ${usage}`); - - // If usage limit exceeded - if (usage + size > user.drive_capacity) { - return reject('no-free-space'); - } - - // If the folder is specified - let folder: any = null; - if (folderId !== null) { - folder = await DriveFolder - .findOne({ - _id: folderId, - user_id: user._id - }); - - if (folder === null) { - return reject('folder-not-found'); - } - } - - let properties: any = null; - - // If the file is an image - if (/^image\/.*$/.test(mime)) { - // Calculate width and height to save in property - const g = gm(data, name); - const size = await prominence(g).size(); - properties = { - width: size.width, - height: size.height - }; - - log('image width and height is calculated'); - } - - // Create DriveFile document - const file = await addToGridFS(name, data, mime, { - user_id: user._id, - folder_id: folder !== null ? folder._id : null, - comment: comment, - properties: properties - }); - - log(`drive file has been created ${file._id}`); - - resolve(file); - - // Serialize - const fileObj = await serialize(file); - - // Publish drive_file_created event - event(user._id, 'drive_file_created', fileObj); + rej(new Error('un-compatible file.')) + }) + // Calculate hash, get content type and get file size + .then(path => Promise.all([ + path, + // hash + ((): Promise => new Promise((res, rej) => { + const readable = fs.createReadStream(path) + const hash = crypto.createHash('md5') + readable + .on('error', rej) + .on('end', () => { + res(hash.digest('hex')) + }) + .pipe(hash) + .on('error', rej) + }))(), + // mime + ((): Promise<[string, string | null]> => new Promise((res, rej) => { + const readable = fs.createReadStream(path) + readable + .on('error', rej) + .once('data', (buffer: Buffer) => { + readable.destroy() + const type = fileType(buffer) + if (!type) { + return res(['application/octet-stream', null]) + } + return res([type.mime, type.ext]) + }) + }))(), + // size + ((): Promise => new Promise((res, rej) => { + fs.stat(path, (err, stats) => { + if (err) return rej(err) + res(stats.size) + }) + }))() + ])) + .then(async ([path, hash, [mime, ext], size]) => { + log(`hash: ${hash}, mime: ${mime}, ext: ${ext}, size: ${size}`) + + // detect name + const detectedName: string = name || (ext ? `untitled.${ext}` : 'untitled'); + + if (!force) { + // Check if there is a file with the same hash + const much = await DriveFile.findOne({ + md5: hash, + 'metadata.user_id': user._id + }); + + if (much !== null) { + log('file with same hash is found'); + return resolve(much); + } else { + log('file with same hash is not found'); + } + } - // Register to search database - if (config.elasticsearch.enable) { - const es = require('../../db/elasticsearch'); - es.index({ - index: 'misskey', - type: 'drive_file', - id: file._id.toString(), - body: { - name: file.name, - user_id: user._id.toString() + const [properties, folder] = await Promise.all([ + // properties + (async () => { + if (!/^image\/.*$/.test(mime)) { + return null + } + // If the file is an image, calculate width and height to save in property + const g = gm(data, name); + const size = await prominence(g).size(); + const properties = { + width: size.width, + height: size.height + }; + log('image width and height is calculated'); + return properties + })(), + // folder + (async () => { + if (!folderId) { + return null + } + const driveFolder = await DriveFolder.findOne({ + _id: folderId, + user_id: user._id + }) + if (!driveFolder) { + throw 'folder-not-found' + } + return driveFolder + })(), + // usage checker + (async () => { + // Calculate drive usage + const usage = await DriveFile + .aggregate([ + { $match: { 'metadata.user_id': user._id } }, + { + $project: { + length: true + } + }, + { + $group: { + _id: null, + usage: { $sum: '$length' } + } + } + ]) + .then((aggregates: any[]) => { + if (aggregates.length > 0) { + return aggregates[0].usage + } + return 0 + }); + + log(`drive usage is ${usage}`); + + // If usage limit exceeded + if (usage + size > user.drive_capacity) { + throw 'no-free-space'; + } + })() + ]) + + const readable = fs.createReadStream(path) + + return addToGridFS(name, readable, mime, { + user_id: user._id, + folder_id: folder !== null ? folder._id : null, + comment: comment, + properties: properties + }) + }) + .then(file => { + log(`drive file has been created ${file._id}`); + resolve(file) + return serialize(file) + }) + .then(serializedFile => { + // Publish drive_file_created event + event(user._id, 'drive_file_created', fileObj); + + // Register to search database + if (config.elasticsearch.enable) { + const es = require('../../db/elasticsearch'); + es.index({ + index: 'misskey', + type: 'drive_file', + id: file._id.toString(), + body: { + name: file.name, + user_id: user._id.toString() + } + }); } - }); - } + }) + .catch(reject) }); diff --git a/src/api/endpoints/drive/files/upload_from_url.ts b/src/api/endpoints/drive/files/upload_from_url.ts index 46cfffb69c..9c759994e0 100644 --- a/src/api/endpoints/drive/files/upload_from_url.ts +++ b/src/api/endpoints/drive/files/upload_from_url.ts @@ -2,11 +2,17 @@ * Module dependencies */ import * as URL from 'url'; -const download = require('download'); import $ from 'cafy'; import { validateFileName } from '../../../models/drive-file'; import serialize from '../../../serializers/drive-file'; import create from '../../../common/add-file-to-drive'; +import * as debug from 'debug'; +import * as tmp from 'tmp'; +import * as fs from 'fs'; +import * as request from 'request'; +import * as crypto from 'crypto'; + +const log = debug('misskey:endpoint:upload_from_url') /** * Create a file from a URL @@ -15,7 +21,7 @@ import create from '../../../common/add-file-to-drive'; * @param {any} user * @return {Promise} */ -module.exports = (params, user) => new Promise(async (res, rej) => { +module.exports = (params, user) => new Promise((res, rej) => { // Get 'url' parameter // TODO: Validate this url const [url, urlErr] = $(params.url).string().$; @@ -30,15 +36,57 @@ module.exports = (params, user) => new Promise(async (res, rej) => { const [folderId = null, folderIdErr] = $(params.folder_id).optional.nullable.id().$; if (folderIdErr) return rej('invalid folder_id param'); - // Download file - const data = await download(url); - - // Create file - const driveFile = await create(user, data, name, null, folderId); - - // Serialize - const fileObj = await serialize(driveFile); - - // Response - res(fileObj); + // Create temp file + new Promise((res, rej) => { + tmp.file((e, path) => { + if (e) return rej(e) + res(path) + }) + }) + // Download file + .then((path: string) => new Promise((res, rej) => { + const writable = fs.createWriteStream(path) + request(url) + .on('error', rej) + .on('end', () => { + writable.close() + res(path) + }) + .pipe(writable) + .on('error', rej) + })) + // Calculate hash & content-type + .then((path: string) => new Promise((res, rej) => { + const readable = fs.createReadStream(path) + const hash = crypto.createHash('md5') + readable + .on('error', rej) + .on('end', () => { + hash.end() + res([path, hash.digest('hex')]) + }) + .pipe(hash) + .on('error', rej) + })) + // Create file + .then((rv: string[]) => new Promise((res, rej) => { + const [path, hash] = rv + create(user, { + stream: fs.createReadStream(path), + name, + hash + }, null, folderId) + .then(driveFile => { + res(driveFile) + // crean-up + fs.unlink(path, (e) => { + if (e) log(e.stack) + }) + }) + .catch(rej) + })) + // Serialize + .then(serialize) + .then(res) + .catch(rej) }); -- cgit v1.2.3-freya From e56f716a89227c76dfc05e48b3ca438f766f85b4 Mon Sep 17 00:00:00 2001 From: otofune Date: Tue, 14 Nov 2017 03:47:42 +0900 Subject: format --- src/api/common/add-file-to-drive.ts | 92 ++++++++++++------------ src/api/endpoints/drive/files/upload_from_url.ts | 38 +++++----- 2 files changed, 65 insertions(+), 65 deletions(-) (limited to 'src/api') diff --git a/src/api/common/add-file-to-drive.ts b/src/api/common/add-file-to-drive.ts index a7c7cb4644..c6a4c4791d 100644 --- a/src/api/common/add-file-to-drive.ts +++ b/src/api/common/add-file-to-drive.ts @@ -18,10 +18,10 @@ const log = debug('misskey:register-drive-file'); const tmpFile = (): Promise => new Promise((resolve, reject) => { tmp.file((e, path) => { - if (e) return reject(e) - resolve(path) - }) -}) + if (e) return reject(e); + resolve(path); + }); +}); const addToGridFS = (name: string, readable: stream.Readable, type: string, metadata: any): Promise => getGridFSBucket() @@ -30,7 +30,7 @@ const addToGridFS = (name: string, readable: stream.Readable, type: string, meta writeStream.once('finish', (doc) => { resolve(doc); }); writeStream.on('error', reject); readable.pipe(writeStream); - })) + })); /** * Add file to drive @@ -56,76 +56,76 @@ export default ( // Get file path new Promise((res: (v: string) => void, rej) => { if (typeof file === 'string') { - res(file) - return + res(file); + return; } if (file instanceof Buffer) { tmpFile() .then(path => { fs.writeFile(path, file, (err) => { - if (err) rej(err) - res(path) - }) + if (err) rej(err); + res(path); + }); }) - .catch(rej) - return + .catch(rej); + return; } if (typeof file === 'object' && typeof file.read === 'function') { tmpFile() .then(path => { - const readable: stream.Readable = file - const writable = fs.createWriteStream(path) + const readable: stream.Readable = file; + const writable = fs.createWriteStream(path); readable .on('error', rej) .on('end', () => { - res(path) + res(path); }) .pipe(writable) - .on('error', rej) + .on('error', rej); }) - .catch(rej) + .catch(rej); } - rej(new Error('un-compatible file.')) + rej(new Error('un-compatible file.')); }) // Calculate hash, get content type and get file size .then(path => Promise.all([ path, // hash ((): Promise => new Promise((res, rej) => { - const readable = fs.createReadStream(path) - const hash = crypto.createHash('md5') + const readable = fs.createReadStream(path); + const hash = crypto.createHash('md5'); readable .on('error', rej) .on('end', () => { - res(hash.digest('hex')) + res(hash.digest('hex')); }) .pipe(hash) - .on('error', rej) + .on('error', rej); }))(), // mime ((): Promise<[string, string | null]> => new Promise((res, rej) => { - const readable = fs.createReadStream(path) + const readable = fs.createReadStream(path); readable .on('error', rej) .once('data', (buffer: Buffer) => { - readable.destroy() - const type = fileType(buffer) + readable.destroy(); + const type = fileType(buffer); if (!type) { - return res(['application/octet-stream', null]) + return res(['application/octet-stream', null]); } - return res([type.mime, type.ext]) - }) + return res([type.mime, type.ext]); + }); }))(), // size ((): Promise => new Promise((res, rej) => { fs.stat(path, (err, stats) => { - if (err) return rej(err) - res(stats.size) - }) + if (err) return rej(err); + res(stats.size); + }); }))() ])) .then(async ([path, hash, [mime, ext], size]) => { - log(`hash: ${hash}, mime: ${mime}, ext: ${ext}, size: ${size}`) + log(`hash: ${hash}, mime: ${mime}, ext: ${ext}, size: ${size}`); // detect name const detectedName: string = name || (ext ? `untitled.${ext}` : 'untitled'); @@ -149,7 +149,7 @@ export default ( // properties (async () => { if (!/^image\/.*$/.test(mime)) { - return null + return null; } // If the file is an image, calculate width and height to save in property const g = gm(data, name); @@ -159,21 +159,21 @@ export default ( height: size.height }; log('image width and height is calculated'); - return properties + return properties; })(), // folder (async () => { if (!folderId) { - return null + return null; } const driveFolder = await DriveFolder.findOne({ _id: folderId, user_id: user._id - }) + }); if (!driveFolder) { - throw 'folder-not-found' + throw 'folder-not-found'; } - return driveFolder + return driveFolder; })(), // usage checker (async () => { @@ -195,9 +195,9 @@ export default ( ]) .then((aggregates: any[]) => { if (aggregates.length > 0) { - return aggregates[0].usage + return aggregates[0].usage; } - return 0 + return 0; }); log(`drive usage is ${usage}`); @@ -207,21 +207,21 @@ export default ( throw 'no-free-space'; } })() - ]) + ]); - const readable = fs.createReadStream(path) + const readable = fs.createReadStream(path); return addToGridFS(name, readable, mime, { user_id: user._id, folder_id: folder !== null ? folder._id : null, comment: comment, properties: properties - }) + }); }) .then(file => { log(`drive file has been created ${file._id}`); - resolve(file) - return serialize(file) + resolve(file); + return serialize(file); }) .then(serializedFile => { // Publish drive_file_created event @@ -241,5 +241,5 @@ export default ( }); } }) - .catch(reject) + .catch(reject); }); diff --git a/src/api/endpoints/drive/files/upload_from_url.ts b/src/api/endpoints/drive/files/upload_from_url.ts index 9c759994e0..60332b4afe 100644 --- a/src/api/endpoints/drive/files/upload_from_url.ts +++ b/src/api/endpoints/drive/files/upload_from_url.ts @@ -12,7 +12,7 @@ import * as fs from 'fs'; import * as request from 'request'; import * as crypto from 'crypto'; -const log = debug('misskey:endpoint:upload_from_url') +const log = debug('misskey:endpoint:upload_from_url'); /** * Create a file from a URL @@ -39,54 +39,54 @@ module.exports = (params, user) => new Promise((res, rej) => { // Create temp file new Promise((res, rej) => { tmp.file((e, path) => { - if (e) return rej(e) - res(path) - }) + if (e) return rej(e); + res(path); + }); }) // Download file .then((path: string) => new Promise((res, rej) => { - const writable = fs.createWriteStream(path) + const writable = fs.createWriteStream(path); request(url) .on('error', rej) .on('end', () => { - writable.close() - res(path) + writable.close(); + res(path); }) .pipe(writable) - .on('error', rej) + .on('error', rej); })) // Calculate hash & content-type .then((path: string) => new Promise((res, rej) => { - const readable = fs.createReadStream(path) - const hash = crypto.createHash('md5') + const readable = fs.createReadStream(path); + const hash = crypto.createHash('md5'); readable .on('error', rej) .on('end', () => { - hash.end() - res([path, hash.digest('hex')]) + hash.end(); + res([path, hash.digest('hex')]); }) .pipe(hash) - .on('error', rej) + .on('error', rej); })) // Create file .then((rv: string[]) => new Promise((res, rej) => { - const [path, hash] = rv + const [path, hash] = rv; create(user, { stream: fs.createReadStream(path), name, hash }, null, folderId) .then(driveFile => { - res(driveFile) + res(driveFile); // crean-up fs.unlink(path, (e) => { - if (e) log(e.stack) - }) + if (e) log(e.stack); + }); }) - .catch(rej) + .catch(rej); })) // Serialize .then(serialize) .then(res) - .catch(rej) + .catch(rej); }); -- cgit v1.2.3-freya From f6c63fbe14e47a01a9b09c3911a7c128375440c9 Mon Sep 17 00:00:00 2001 From: otofune Date: Tue, 14 Nov 2017 03:56:39 +0900 Subject: add-file-to-drive - gmに渡す引数を正しくする MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/common/add-file-to-drive.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/api') diff --git a/src/api/common/add-file-to-drive.ts b/src/api/common/add-file-to-drive.ts index c6a4c4791d..1aa21f71ad 100644 --- a/src/api/common/add-file-to-drive.ts +++ b/src/api/common/add-file-to-drive.ts @@ -152,7 +152,7 @@ export default ( return null; } // If the file is an image, calculate width and height to save in property - const g = gm(data, name); + const g = gm(fs.createReadStream(path), name); const size = await prominence(g).size(); const properties = { width: size.width, -- cgit v1.2.3-freya From 54804f4a642176134fa835eca86a047a87a704d2 Mon Sep 17 00:00:00 2001 From: otofune Date: Tue, 14 Nov 2017 04:11:53 +0900 Subject: add-file-to-drive - hashがstreamを受ける時、hashもまたstreamなのだ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/common/add-file-to-drive.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src/api') diff --git a/src/api/common/add-file-to-drive.ts b/src/api/common/add-file-to-drive.ts index 1aa21f71ad..5f7d255e4b 100644 --- a/src/api/common/add-file-to-drive.ts +++ b/src/api/common/add-file-to-drive.ts @@ -94,13 +94,16 @@ export default ( ((): Promise => new Promise((res, rej) => { const readable = fs.createReadStream(path); const hash = crypto.createHash('md5'); + const chunks = []; readable .on('error', rej) - .on('end', () => { - res(hash.digest('hex')); - }) .pipe(hash) - .on('error', rej); + .on('error', rej) + .on('data', (chunk) => chunks.push(chunk)) + .on('end', () => { + const buffer = Buffer.concat(chunks); + res(buffer.toString('hex')); + }); }))(), // mime ((): Promise<[string, string | null]> => new Promise((res, rej) => { -- cgit v1.2.3-freya From 47f98fbab76e8680f5a7c99037b3b237c7256ca2 Mon Sep 17 00:00:00 2001 From: otofune Date: Tue, 14 Nov 2017 04:28:51 +0900 Subject: バグ修正 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/common/add-file-to-drive.ts | 35 +++++----- src/api/endpoints/drive/files/upload_from_url.ts | 81 ++++++++---------------- 2 files changed, 46 insertions(+), 70 deletions(-) (limited to 'src/api') diff --git a/src/api/common/add-file-to-drive.ts b/src/api/common/add-file-to-drive.ts index 5f7d255e4b..1c8965e31d 100644 --- a/src/api/common/add-file-to-drive.ts +++ b/src/api/common/add-file-to-drive.ts @@ -214,7 +214,7 @@ export default ( const readable = fs.createReadStream(path); - return addToGridFS(name, readable, mime, { + return addToGridFS(detectedName, readable, mime, { user_id: user._id, folder_id: folder !== null ? folder._id : null, comment: comment, @@ -224,25 +224,26 @@ export default ( .then(file => { log(`drive file has been created ${file._id}`); resolve(file); - return serialize(file); - }) - .then(serializedFile => { - // Publish drive_file_created event - event(user._id, 'drive_file_created', fileObj); - // Register to search database - if (config.elasticsearch.enable) { - const es = require('../../db/elasticsearch'); - es.index({ - index: 'misskey', - type: 'drive_file', - id: file._id.toString(), - body: { - name: file.name, - user_id: user._id.toString() + serialize(file) + .then(serializedFile => { + // Publish drive_file_created event + event(user._id, 'drive_file_created', serializedFile); + + // Register to search database + if (config.elasticsearch.enable) { + const es = require('../../db/elasticsearch'); + es.index({ + index: 'misskey', + type: 'drive_file', + id: file._id.toString(), + body: { + name: file.name, + user_id: user._id.toString() + } + }); } }); - } }) .catch(reject); }); diff --git a/src/api/endpoints/drive/files/upload_from_url.ts b/src/api/endpoints/drive/files/upload_from_url.ts index 60332b4afe..519e0bdf65 100644 --- a/src/api/endpoints/drive/files/upload_from_url.ts +++ b/src/api/endpoints/drive/files/upload_from_url.ts @@ -10,7 +10,6 @@ import * as debug from 'debug'; import * as tmp from 'tmp'; import * as fs from 'fs'; import * as request from 'request'; -import * as crypto from 'crypto'; const log = debug('misskey:endpoint:upload_from_url'); @@ -21,11 +20,11 @@ const log = debug('misskey:endpoint:upload_from_url'); * @param {any} user * @return {Promise} */ -module.exports = (params, user) => new Promise((res, rej) => { +module.exports = async (params, user): Promise => { // Get 'url' parameter // TODO: Validate this url const [url, urlErr] = $(params.url).string().$; - if (urlErr) return rej('invalid url param'); + if (urlErr) throw 'invalid url param'; let name = URL.parse(url).pathname.split('/').pop(); if (!validateFileName(name)) { @@ -34,59 +33,35 @@ module.exports = (params, user) => new Promise((res, rej) => { // Get 'folder_id' parameter const [folderId = null, folderIdErr] = $(params.folder_id).optional.nullable.id().$; - if (folderIdErr) return rej('invalid folder_id param'); + if (folderIdErr) throw 'invalid folder_id param'; // Create temp file - new Promise((res, rej) => { + const path = await new Promise((res: (string) => void, rej) => { tmp.file((e, path) => { if (e) return rej(e); res(path); }); - }) - // Download file - .then((path: string) => new Promise((res, rej) => { - const writable = fs.createWriteStream(path); - request(url) - .on('error', rej) - .on('end', () => { - writable.close(); - res(path); - }) - .pipe(writable) - .on('error', rej); - })) - // Calculate hash & content-type - .then((path: string) => new Promise((res, rej) => { - const readable = fs.createReadStream(path); - const hash = crypto.createHash('md5'); - readable - .on('error', rej) - .on('end', () => { - hash.end(); - res([path, hash.digest('hex')]); - }) - .pipe(hash) - .on('error', rej); - })) - // Create file - .then((rv: string[]) => new Promise((res, rej) => { - const [path, hash] = rv; - create(user, { - stream: fs.createReadStream(path), - name, - hash - }, null, folderId) - .then(driveFile => { - res(driveFile); - // crean-up - fs.unlink(path, (e) => { - if (e) log(e.stack); - }); - }) - .catch(rej); - })) - // Serialize - .then(serialize) - .then(res) - .catch(rej); -}); + }); + + // write content at URL to temp file + await new Promise((res, rej) => { + const writable = fs.createWriteStream(path); + request(url) + .on('error', rej) + .on('end', () => { + writable.close(); + res(path); + }) + .pipe(writable) + .on('error', rej); + }); + + const driveFile = await create(user, path, name, null, folderId); + + // clean-up + fs.unlink(path, (e) => { + if (e) log(e.stack); + }); + + return serialize(driveFile); +}; -- cgit v1.2.3-freya From 342345cc2fb95e86d65095e0c9996441950ad628 Mon Sep 17 00:00:00 2001 From: otofune Date: Tue, 14 Nov 2017 04:35:25 +0900 Subject: create - バッファを使用しないように MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/endpoints/drive/files/create.ts | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) (limited to 'src/api') diff --git a/src/api/endpoints/drive/files/create.ts b/src/api/endpoints/drive/files/create.ts index 7967c31187..7546eca309 100644 --- a/src/api/endpoints/drive/files/create.ts +++ b/src/api/endpoints/drive/files/create.ts @@ -1,7 +1,6 @@ /** * Module dependencies */ -import * as fs from 'fs'; import $ from 'cafy'; import { validateFileName } from '../../../models/drive-file'; import serialize from '../../../serializers/drive-file'; @@ -15,15 +14,11 @@ import create from '../../../common/add-file-to-drive'; * @param {any} user * @return {Promise} */ -module.exports = (file, params, user) => new Promise(async (res, rej) => { +module.exports = async (file, params, user): Promise => { if (file == null) { - return rej('file is required'); + throw 'file is required'; } - // TODO: 非同期にしたい。Promise対応してないんだろうか... - const buffer = fs.readFileSync(file.path); - fs.unlink(file.path, (err) => { if (err) console.log(err); }); - // Get 'name' parameter let name = file.originalname; if (name !== undefined && name !== null) { @@ -33,7 +28,7 @@ module.exports = (file, params, user) => new Promise(async (res, rej) => { } else if (name === 'blob') { name = null; } else if (!validateFileName(name)) { - return rej('invalid name'); + throw 'invalid name'; } } else { name = null; @@ -41,14 +36,11 @@ module.exports = (file, params, user) => new Promise(async (res, rej) => { // Get 'folder_id' parameter const [folderId = null, folderIdErr] = $(params.folder_id).optional.nullable.id().$; - if (folderIdErr) return rej('invalid folder_id param'); + if (folderIdErr) throw 'invalid folder_id param'; // Create file - const driveFile = await create(user, buffer, name, null, folderId); + const driveFile = await create(user, file.path, name, null, folderId); // Serialize - const fileObj = await serialize(driveFile); - - // Response - res(fileObj); -}); + return serialize(driveFile); +}; -- cgit v1.2.3-freya From 519bb82b039dd037667f106b29f74bc0dffb4b3a Mon Sep 17 00:00:00 2001 From: otofune Date: Tue, 14 Nov 2017 04:39:21 +0900 Subject: add-file-to-drive - バッファ受け付けを削除 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/common/add-file-to-drive.ts | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) (limited to 'src/api') diff --git a/src/api/common/add-file-to-drive.ts b/src/api/common/add-file-to-drive.ts index 1c8965e31d..e5d9dd7c1d 100644 --- a/src/api/common/add-file-to-drive.ts +++ b/src/api/common/add-file-to-drive.ts @@ -36,7 +36,7 @@ const addToGridFS = (name: string, readable: stream.Readable, type: string, meta * Add file to drive * * @param user User who wish to add file - * @param file File path, binary, or readableStream + * @param file File path or readableStream * @param comment Comment * @param type File type * @param folderId Folder ID @@ -45,7 +45,7 @@ const addToGridFS = (name: string, readable: stream.Readable, type: string, meta */ export default ( user: any, - file: string | Buffer | stream.Readable, + file: string | stream.Readable, name: string = null, comment: string = null, folderId: mongodb.ObjectID = null, @@ -59,17 +59,6 @@ export default ( res(file); return; } - if (file instanceof Buffer) { - tmpFile() - .then(path => { - fs.writeFile(path, file, (err) => { - if (err) rej(err); - res(path); - }); - }) - .catch(rej); - return; - } if (typeof file === 'object' && typeof file.read === 'function') { tmpFile() .then(path => { -- cgit v1.2.3-freya From 51faa7a227ad8f8489d03da937c78667442beb0e Mon Sep 17 00:00:00 2001 From: otofune Date: Tue, 14 Nov 2017 04:54:47 +0900 Subject: add-file-to-drive - 見通しを良くする MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/common/add-file-to-drive.ts | 281 ++++++++++++++++++------------------ 1 file changed, 142 insertions(+), 139 deletions(-) (limited to 'src/api') diff --git a/src/api/common/add-file-to-drive.ts b/src/api/common/add-file-to-drive.ts index e5d9dd7c1d..eeb92005ae 100644 --- a/src/api/common/add-file-to-drive.ts +++ b/src/api/common/add-file-to-drive.ts @@ -32,29 +32,18 @@ const addToGridFS = (name: string, readable: stream.Readable, type: string, meta readable.pipe(writeStream); })); -/** - * Add file to drive - * - * @param user User who wish to add file - * @param file File path or readableStream - * @param comment Comment - * @param type File type - * @param folderId Folder ID - * @param force If set to true, forcibly upload the file even if there is a file with the same hash. - * @return Object that represents added file - */ -export default ( +const addFile = async ( user: any, file: string | stream.Readable, name: string = null, comment: string = null, folderId: mongodb.ObjectID = null, force: boolean = false -) => new Promise((resolve, reject) => { +) => { log(`registering ${name} (user: ${user.username})`); // Get file path - new Promise((res: (v: string) => void, rej) => { + const path = await new Promise((res: (v: string) => void, rej) => { if (typeof file === 'string') { res(file); return; @@ -75,141 +64,155 @@ export default ( .catch(rej); } rej(new Error('un-compatible file.')); - }) - // Calculate hash, get content type and get file size - .then(path => Promise.all([ - path, - // hash - ((): Promise => new Promise((res, rej) => { - const readable = fs.createReadStream(path); - const hash = crypto.createHash('md5'); - const chunks = []; - readable - .on('error', rej) - .pipe(hash) - .on('error', rej) - .on('data', (chunk) => chunks.push(chunk)) - .on('end', () => { - const buffer = Buffer.concat(chunks); - res(buffer.toString('hex')); - }); - }))(), - // mime - ((): Promise<[string, string | null]> => new Promise((res, rej) => { - const readable = fs.createReadStream(path); - readable - .on('error', rej) - .once('data', (buffer: Buffer) => { - readable.destroy(); - const type = fileType(buffer); - if (!type) { - return res(['application/octet-stream', null]); - } - return res([type.mime, type.ext]); - }); - }))(), - // size - ((): Promise => new Promise((res, rej) => { - fs.stat(path, (err, stats) => { - if (err) return rej(err); - res(stats.size); + }); + + // Calculate hash, get content type and get file size + const [hash, [mime, ext], size] = await Promise.all([ + // hash + ((): Promise => new Promise((res, rej) => { + const readable = fs.createReadStream(path); + const hash = crypto.createHash('md5'); + const chunks = []; + readable + .on('error', rej) + .pipe(hash) + .on('error', rej) + .on('data', (chunk) => chunks.push(chunk)) + .on('end', () => { + const buffer = Buffer.concat(chunks); + res(buffer.toString('hex')); }); - }))() - ])) - .then(async ([path, hash, [mime, ext], size]) => { - log(`hash: ${hash}, mime: ${mime}, ext: ${ext}, size: ${size}`); - - // detect name - const detectedName: string = name || (ext ? `untitled.${ext}` : 'untitled'); - - if (!force) { - // Check if there is a file with the same hash - const much = await DriveFile.findOne({ - md5: hash, - 'metadata.user_id': user._id + }))(), + // mime + ((): Promise<[string, string | null]> => new Promise((res, rej) => { + const readable = fs.createReadStream(path); + readable + .on('error', rej) + .once('data', (buffer: Buffer) => { + readable.destroy(); + const type = fileType(buffer); + if (!type) { + return res(['application/octet-stream', null]); + } + return res([type.mime, type.ext]); }); + }))(), + // size + ((): Promise => new Promise((res, rej) => { + fs.stat(path, (err, stats) => { + if (err) return rej(err); + res(stats.size); + }); + }))() + ]); - if (much !== null) { - log('file with same hash is found'); - return resolve(much); - } else { - log('file with same hash is not found'); - } - } + log(`hash: ${hash}, mime: ${mime}, ext: ${ext}, size: ${size}`); - const [properties, folder] = await Promise.all([ - // properties - (async () => { - if (!/^image\/.*$/.test(mime)) { - return null; - } - // If the file is an image, calculate width and height to save in property - const g = gm(fs.createReadStream(path), name); - const size = await prominence(g).size(); - const properties = { - width: size.width, - height: size.height - }; - log('image width and height is calculated'); - return properties; - })(), - // folder - (async () => { - if (!folderId) { - return null; + // detect name + const detectedName: string = name || (ext ? `untitled.${ext}` : 'untitled'); + + if (!force) { + // Check if there is a file with the same hash + const much = await DriveFile.findOne({ + md5: hash, + 'metadata.user_id': user._id + }); + + if (much !== null) { + log('file with same hash is found'); + return much; + } else { + log('file with same hash is not found'); + } + } + + const [properties, folder] = await Promise.all([ + // properties + (async () => { + if (!/^image\/.*$/.test(mime)) { + return null; + } + // If the file is an image, calculate width and height to save in property + const g = gm(fs.createReadStream(path), name); + const size = await prominence(g).size(); + const properties = { + width: size.width, + height: size.height + }; + log('image width and height is calculated'); + return properties; + })(), + // folder + (async () => { + if (!folderId) { + return null; + } + const driveFolder = await DriveFolder.findOne({ + _id: folderId, + user_id: user._id + }); + if (!driveFolder) { + throw 'folder-not-found'; + } + return driveFolder; + })(), + // usage checker + (async () => { + // Calculate drive usage + const usage = await DriveFile + .aggregate([ + { $match: { 'metadata.user_id': user._id } }, + { + $project: { + length: true + } + }, + { + $group: { + _id: null, + usage: { $sum: '$length' } + } } - const driveFolder = await DriveFolder.findOne({ - _id: folderId, - user_id: user._id - }); - if (!driveFolder) { - throw 'folder-not-found'; + ]) + .then((aggregates: any[]) => { + if (aggregates.length > 0) { + return aggregates[0].usage; } - return driveFolder; - })(), - // usage checker - (async () => { - // Calculate drive usage - const usage = await DriveFile - .aggregate([ - { $match: { 'metadata.user_id': user._id } }, - { - $project: { - length: true - } - }, - { - $group: { - _id: null, - usage: { $sum: '$length' } - } - } - ]) - .then((aggregates: any[]) => { - if (aggregates.length > 0) { - return aggregates[0].usage; - } - return 0; - }); + return 0; + }); - log(`drive usage is ${usage}`); + log(`drive usage is ${usage}`); - // If usage limit exceeded - if (usage + size > user.drive_capacity) { - throw 'no-free-space'; - } - })() - ]); + // If usage limit exceeded + if (usage + size > user.drive_capacity) { + throw 'no-free-space'; + } + })() + ]); - const readable = fs.createReadStream(path); + const readable = fs.createReadStream(path); - return addToGridFS(detectedName, readable, mime, { - user_id: user._id, - folder_id: folder !== null ? folder._id : null, - comment: comment, - properties: properties - }); - }) + return addToGridFS(detectedName, readable, mime, { + user_id: user._id, + folder_id: folder !== null ? folder._id : null, + comment: comment, + properties: properties + }); +}; + +/** + * Add file to drive + * + * @param user User who wish to add file + * @param file File path or readableStream + * @param comment Comment + * @param type File type + * @param folderId Folder ID + * @param force If set to true, forcibly upload the file even if there is a file with the same hash. + * @return Object that represents added file + */ +export default (user: any, file: string | stream.Readable, ...args) => new Promise((resolve, reject) => { + addFile(user, file, ...args) .then(file => { log(`drive file has been created ${file._id}`); resolve(file); -- cgit v1.2.3-freya From d8a3b4ff1d418eb2aab30237d797dc4844858abe Mon Sep 17 00:00:00 2001 From: otofune Date: Tue, 14 Nov 2017 05:10:28 +0900 Subject: add-file-to-drive - 責務の分割とテンポラリファイルを削除するように MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/common/add-file-to-drive.ts | 61 +++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 26 deletions(-) (limited to 'src/api') diff --git a/src/api/common/add-file-to-drive.ts b/src/api/common/add-file-to-drive.ts index eeb92005ae..6a728d0d1a 100644 --- a/src/api/common/add-file-to-drive.ts +++ b/src/api/common/add-file-to-drive.ts @@ -34,7 +34,7 @@ const addToGridFS = (name: string, readable: stream.Readable, type: string, meta const addFile = async ( user: any, - file: string | stream.Readable, + path: string, name: string = null, comment: string = null, folderId: mongodb.ObjectID = null, @@ -42,30 +42,6 @@ const addFile = async ( ) => { log(`registering ${name} (user: ${user.username})`); - // Get file path - const path = await new Promise((res: (v: string) => void, rej) => { - if (typeof file === 'string') { - res(file); - return; - } - if (typeof file === 'object' && typeof file.read === 'function') { - tmpFile() - .then(path => { - const readable: stream.Readable = file; - const writable = fs.createWriteStream(path); - readable - .on('error', rej) - .on('end', () => { - res(path); - }) - .pipe(writable) - .on('error', rej); - }) - .catch(rej); - } - rej(new Error('un-compatible file.')); - }); - // Calculate hash, get content type and get file size const [hash, [mime, ext], size] = await Promise.all([ // hash @@ -212,7 +188,40 @@ const addFile = async ( * @return Object that represents added file */ export default (user: any, file: string | stream.Readable, ...args) => new Promise((resolve, reject) => { - addFile(user, file, ...args) + // Get file path + new Promise((res: (v: [string, boolean]) => void, rej) => { + if (typeof file === 'string') { + res([file, false]); + return; + } + if (typeof file === 'object' && typeof file.read === 'function') { + tmpFile() + .then(path => { + const readable: stream.Readable = file; + const writable = fs.createWriteStream(path); + readable + .on('error', rej) + .on('end', () => { + res([path, true]); + }) + .pipe(writable) + .on('error', rej); + }) + .catch(rej); + } + rej(new Error('un-compatible file.')); + }).then(([path, remove]): Promise => new Promise((res, rej) => { + addFile(user, path, ...args) + .then(file => { + res(file) + if (remove) { + fs.unlink(path, (e) => { + if (e) log(e.stack) + }) + } + }) + .catch(rej) + })) .then(file => { log(`drive file has been created ${file._id}`); resolve(file); -- cgit v1.2.3-freya From aabfe3c87365952bdffa757ea30d8631daf7de4f Mon Sep 17 00:00:00 2001 From: otofune Date: Tue, 14 Nov 2017 05:12:48 +0900 Subject: format --- src/api/common/add-file-to-drive.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/api') diff --git a/src/api/common/add-file-to-drive.ts b/src/api/common/add-file-to-drive.ts index 6a728d0d1a..7defbc631a 100644 --- a/src/api/common/add-file-to-drive.ts +++ b/src/api/common/add-file-to-drive.ts @@ -213,14 +213,14 @@ export default (user: any, file: string | stream.Readable, ...args) => new Promi }).then(([path, remove]): Promise => new Promise((res, rej) => { addFile(user, path, ...args) .then(file => { - res(file) + res(file); if (remove) { fs.unlink(path, (e) => { - if (e) log(e.stack) - }) + if (e) log(e.stack); + }); } }) - .catch(rej) + .catch(rej); })) .then(file => { log(`drive file has been created ${file._id}`); -- cgit v1.2.3-freya From 28d2d38fce7012c6f9d48dc52a0bf2efd1e8c652 Mon Sep 17 00:00:00 2001 From: otofune Date: Tue, 14 Nov 2017 05:26:27 +0900 Subject: server - multerがテンポラリディレクトリにファイルを展開するように MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DiskStorageをオプション無しで呼ぶといい感じになる またチェーンを改行するようにし、デバッグ表示にパスが出るようにした --- src/api/common/add-file-to-drive.ts | 27 ++++++++++++++------------- src/api/server.ts | 2 +- 2 files changed, 15 insertions(+), 14 deletions(-) (limited to 'src/api') diff --git a/src/api/common/add-file-to-drive.ts b/src/api/common/add-file-to-drive.ts index 7defbc631a..9ed5e88744 100644 --- a/src/api/common/add-file-to-drive.ts +++ b/src/api/common/add-file-to-drive.ts @@ -40,7 +40,7 @@ const addFile = async ( folderId: mongodb.ObjectID = null, force: boolean = false ) => { - log(`registering ${name} (user: ${user.username})`); + log(`registering ${name} (user: ${user.username}, path: ${path})`); // Calculate hash, get content type and get file size const [hash, [mime, ext], size] = await Promise.all([ @@ -210,18 +210,19 @@ export default (user: any, file: string | stream.Readable, ...args) => new Promi .catch(rej); } rej(new Error('un-compatible file.')); - }).then(([path, remove]): Promise => new Promise((res, rej) => { - addFile(user, path, ...args) - .then(file => { - res(file); - if (remove) { - fs.unlink(path, (e) => { - if (e) log(e.stack); - }); - } - }) - .catch(rej); - })) + }) + .then(([path, remove]): Promise => new Promise((res, rej) => { + addFile(user, path, ...args) + .then(file => { + res(file); + if (remove) { + fs.unlink(path, (e) => { + if (e) log(e.stack); + }); + } + }) + .catch(rej); + })) .then(file => { log(`drive file has been created ${file._id}`); resolve(file); diff --git a/src/api/server.ts b/src/api/server.ts index 3de32d9eab..026357b465 100644 --- a/src/api/server.ts +++ b/src/api/server.ts @@ -40,7 +40,7 @@ app.get('/', (req, res) => { endpoints.forEach(endpoint => endpoint.withFile ? app.post(`/${endpoint.name}`, - endpoint.withFile ? multer({ dest: 'uploads/' }).single('file') : null, + endpoint.withFile ? multer({ storage: multer.diskStorage({}) }).single('file') : null, require('./api-handler').default.bind(null, endpoint)) : app.post(`/${endpoint.name}`, require('./api-handler').default.bind(null, endpoint)) -- cgit v1.2.3-freya From 107ecfb07fe81dc5b89d9ef57a7be77657463d31 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 14 Nov 2017 08:21:19 +0900 Subject: なんか MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/endpoints/users/posts.ts | 28 +- src/web/app/desktop/tags/calendar.tag | 241 +++++++++++ .../app/desktop/tags/home-widgets/timemachine.tag | 231 +---------- src/web/app/desktop/tags/index.ts | 6 +- src/web/app/desktop/tags/user-graphs.tag | 41 -- src/web/app/desktop/tags/user-header.tag | 147 ------- src/web/app/desktop/tags/user-home.tag | 46 --- src/web/app/desktop/tags/user-photos.tag | 91 ---- src/web/app/desktop/tags/user-profile.tag | 102 ----- src/web/app/desktop/tags/user-timeline.tag | 9 + src/web/app/desktop/tags/user.tag | 457 ++++++++++++++++++++- 11 files changed, 727 insertions(+), 672 deletions(-) create mode 100644 src/web/app/desktop/tags/calendar.tag delete mode 100644 src/web/app/desktop/tags/user-graphs.tag delete mode 100644 src/web/app/desktop/tags/user-header.tag delete mode 100644 src/web/app/desktop/tags/user-home.tag delete mode 100644 src/web/app/desktop/tags/user-photos.tag delete mode 100644 src/web/app/desktop/tags/user-profile.tag (limited to 'src/api') diff --git a/src/api/endpoints/users/posts.ts b/src/api/endpoints/users/posts.ts index d8204b8b80..fe821cf17a 100644 --- a/src/api/endpoints/users/posts.ts +++ b/src/api/endpoints/users/posts.ts @@ -46,9 +46,17 @@ module.exports = (params, me) => new Promise(async (res, rej) => { const [maxId, maxIdErr] = $(params.max_id).optional.id().$; if (maxIdErr) return rej('invalid max_id param'); - // Check if both of since_id and max_id is specified - if (sinceId && maxId) { - return rej('cannot set since_id and max_id'); + // Get 'since_date' parameter + const [sinceDate, sinceDateErr] = $(params.since_date).optional.number().$; + if (sinceDateErr) throw 'invalid since_date param'; + + // Get 'max_date' parameter + const [maxDate, maxDateErr] = $(params.max_date).optional.number().$; + if (maxDateErr) throw 'invalid max_date param'; + + // Check if only one of since_id, max_id, since_date, max_date specified + if ([sinceId, maxId, sinceDate, maxDate].filter(x => x != null).length > 1) { + throw 'only one of since_id, max_id, since_date, max_date can be specified'; } const q = userId !== undefined @@ -66,13 +74,15 @@ module.exports = (params, me) => new Promise(async (res, rej) => { return rej('user not found'); } - // Construct query + //#region Construct query const sort = { _id: -1 }; + const query = { user_id: user._id } as any; + if (sinceId) { sort._id = 1; query._id = { @@ -82,6 +92,15 @@ module.exports = (params, me) => new Promise(async (res, rej) => { query._id = { $lt: maxId }; + } else if (sinceDate) { + sort._id = 1; + query.created_at = { + $gt: new Date(sinceDate) + }; + } else if (maxDate) { + query.created_at = { + $lt: new Date(maxDate) + }; } if (!includeReplies) { @@ -94,6 +113,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => { $ne: null }; } + //#endregion // Issue query const posts = await Post diff --git a/src/web/app/desktop/tags/calendar.tag b/src/web/app/desktop/tags/calendar.tag new file mode 100644 index 0000000000..3535e81901 --- /dev/null +++ b/src/web/app/desktop/tags/calendar.tag @@ -0,0 +1,241 @@ + + + +

{ '%i18n:desktop.tags.mk-calendar.title%'.replace('{1}', year).replace('{2}', month) }

+ +
+ +
+
{ weekdayText[i] }
+
+
{ i + 1 }
+
+ + +
diff --git a/src/web/app/desktop/tags/home-widgets/timemachine.tag b/src/web/app/desktop/tags/home-widgets/timemachine.tag index 984258d2ba..2199cf4c67 100644 --- a/src/web/app/desktop/tags/home-widgets/timemachine.tag +++ b/src/web/app/desktop/tags/home-widgets/timemachine.tag @@ -1,139 +1,10 @@ - - - -

{ '%i18n:desktop.tags.mk-timemachine-home-widget.title%'.replace('{1}', year).replace('{2}', month) }

- -
- -
-
{ weekdayText[i] }
-
-
{ i + 1 }
-
+ + - diff --git a/src/web/app/desktop/tags/user-header.tag b/src/web/app/desktop/tags/user-header.tag deleted file mode 100644 index ea7ea6bb37..0000000000 --- a/src/web/app/desktop/tags/user-header.tag +++ /dev/null @@ -1,147 +0,0 @@ - - avatar -
-

{ user.name }

-

@{ user.username }

-

{ user.profile.location }

-
- - - -
diff --git a/src/web/app/desktop/tags/user-home.tag b/src/web/app/desktop/tags/user-home.tag deleted file mode 100644 index a879db5bb6..0000000000 --- a/src/web/app/desktop/tags/user-home.tag +++ /dev/null @@ -1,46 +0,0 @@ - -
- - -
-
- -
- - -
diff --git a/src/web/app/desktop/tags/user-photos.tag b/src/web/app/desktop/tags/user-photos.tag deleted file mode 100644 index dce1e50add..0000000000 --- a/src/web/app/desktop/tags/user-photos.tag +++ /dev/null @@ -1,91 +0,0 @@ - -

フォト

-

読み込んでいます

-
0 }> - -
-
-
-

写真はありません

- - -
diff --git a/src/web/app/desktop/tags/user-profile.tag b/src/web/app/desktop/tags/user-profile.tag deleted file mode 100644 index 7472a47801..0000000000 --- a/src/web/app/desktop/tags/user-profile.tag +++ /dev/null @@ -1,102 +0,0 @@ - -
- -

フォローされています

-
-
{ user.description }
-
-

{ user.profile.birthday.replace('-', '年').replace('-', '月') + '日' } ({ age(user.profile.birthday) }歳)

-
- -
-

{ user.posts_count }ポスト

-

{ user.following_count }人をフォロー

-

{ user.followers_count }人のフォロワー

-
- - -
diff --git a/src/web/app/desktop/tags/user-timeline.tag b/src/web/app/desktop/tags/user-timeline.tag index 08ab47b160..5df13c436c 100644 --- a/src/web/app/desktop/tags/user-timeline.tag +++ b/src/web/app/desktop/tags/user-timeline.tag @@ -91,6 +91,7 @@ this.fetch = cb => { this.api('users/posts', { user_id: this.user.id, + max_date: this.date ? this.date.getTime() : undefined, with_replies: this.mode == 'with-replies' }).then(posts => { this.update({ @@ -132,5 +133,13 @@ }); this.fetch(); }; + + this.warp = date => { + this.update({ + date: date + }); + + this.fetch(); + }; diff --git a/src/web/app/desktop/tags/user.tag b/src/web/app/desktop/tags/user.tag index 2e69872b78..c9bb53fedd 100644 --- a/src/web/app/desktop/tags/user.tag +++ b/src/web/app/desktop/tags/user.tag @@ -3,10 +3,8 @@
-
- - -
+ +
+ + + + avatar +
+

{ user.name }

+

@{ user.username }

+

{ user.profile.location }

+
+ + + +
+ + +
+ +

フォローされています

+
+
{ user.description }
+
+

{ user.profile.birthday.replace('-', '年').replace('-', '月') + '日' } ({ age(user.profile.birthday) }歳)

+
+ +
+

{ user.posts_count }ポスト

+

{ user.following_count }人をフォロー

+

{ user.followers_count }人のフォロワー

+
+ + +
+ + +

フォト

+

読み込んでいます

+
0 }> + +
+
+
+

写真はありません

+ + +
+ + +
+ + +
+
+ +
+
+ +
+ + +
+ + +
+

投稿

+ +
+
+

フォロー/フォロワー

+ +
+
+

いいね

+ +
+ + +
-- cgit v1.2.3-freya From 47e9e888164b6eafd341bba7459fe41cfa91b525 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 14 Nov 2017 23:38:18 +0900 Subject: :v: --- src/api/endpoints/users/get_frequently_replied_users.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/api') diff --git a/src/api/endpoints/users/get_frequently_replied_users.ts b/src/api/endpoints/users/get_frequently_replied_users.ts index bb0f3b4cea..a8add623d4 100644 --- a/src/api/endpoints/users/get_frequently_replied_users.ts +++ b/src/api/endpoints/users/get_frequently_replied_users.ts @@ -11,6 +11,10 @@ module.exports = (params, me) => new Promise(async (res, rej) => { const [userId, userIdErr] = $(params.user_id).id().$; if (userIdErr) return rej('invalid user_id param'); + // Get 'limit' parameter + const [limit = 10, limitErr] = $(params.limit).optional.number().range(1, 100).$; + if (limitErr) return rej('invalid limit param'); + // Lookup user const user = await User.findOne({ _id: userId @@ -82,8 +86,8 @@ module.exports = (params, me) => new Promise(async (res, rej) => { // Sort replies by frequency const repliedUsersSorted = Object.keys(repliedUsers).sort((a, b) => repliedUsers[b] - repliedUsers[a]); - // Lookup top 10 replies - const topRepliedUsers = repliedUsersSorted.slice(0, 10); + // Extract top replied users + const topRepliedUsers = repliedUsersSorted.slice(0, limit); // Make replies object (includes weights) const repliesObj = await Promise.all(topRepliedUsers.map(async (user) => ({ -- cgit v1.2.3-freya From c46850a69a5f6f33df2cf0b9299c5f0dcf56bcfa Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 15 Nov 2017 09:47:47 +0900 Subject: :v: --- src/api/endpoints/meta.ts | 6 +++++- src/api/models/meta.ts | 7 +++++++ src/web/app/desktop/tags/pages/entrance.tag | 11 ++++++++--- 3 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 src/api/models/meta.ts (limited to 'src/api') diff --git a/src/api/endpoints/meta.ts b/src/api/endpoints/meta.ts index a3f1d50329..4f626efa6f 100644 --- a/src/api/endpoints/meta.ts +++ b/src/api/endpoints/meta.ts @@ -4,6 +4,7 @@ import * as os from 'os'; import version from '../../version'; import config from '../../conf'; +import Meta from '../models/meta'; /** * @swagger @@ -39,6 +40,8 @@ import config from '../../conf'; * @return {Promise} */ module.exports = (params) => new Promise(async (res, rej) => { + const meta = (await Meta.findOne()) || {}; + res({ maintainer: config.maintainer, version: version, @@ -49,6 +52,7 @@ module.exports = (params) => new Promise(async (res, rej) => { cpu: { model: os.cpus()[0].model, cores: os.cpus().length - } + }, + top_image: meta.top_image }); }); diff --git a/src/api/models/meta.ts b/src/api/models/meta.ts new file mode 100644 index 0000000000..c7dba8fcba --- /dev/null +++ b/src/api/models/meta.ts @@ -0,0 +1,7 @@ +import db from '../../db/mongodb'; + +export default db.get('meta') as any; // fuck type definition + +export type IMeta = { + top_image: string; +}; diff --git a/src/web/app/desktop/tags/pages/entrance.tag b/src/web/app/desktop/tags/pages/entrance.tag index 914e6b0d58..0dfca1f7a7 100644 --- a/src/web/app/desktop/tags/pages/entrance.tag +++ b/src/web/app/desktop/tags/pages/entrance.tag @@ -113,9 +113,14 @@ this.mode = 'signin'; this.on('mount', () => { - document.documentElement.style.backgroundImage = 'url("/assets/desktop/index.jpg")'; - document.documentElement.style.backgroundSize = 'cover'; - document.documentElement.style.backgroundPosition = 'center'; + document.documentElement.style.backgroundColor = '#444'; + + this.api('meta').then(meta => { + const img = meta.top_image ? meta.top_image : '/assets/desktop/index.jpg'; + document.documentElement.style.backgroundImage = `url("${ img }")`; + document.documentElement.style.backgroundSize = 'cover'; + document.documentElement.style.backgroundPosition = 'center'; + }); this.api('stats').then(stats => { this.update({ -- cgit v1.2.3-freya From 0cd69cabc9f01f19754ad08ed32a15eb4d4518be Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 16 Nov 2017 14:19:14 +0900 Subject: お知らせをブロードキャストできるように MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/en.yml | 6 ++ locales/ja.yml | 6 ++ src/api/endpoints/meta.ts | 3 +- src/web/app/common/tags/raw.tag | 4 ++ .../app/desktop/tags/home-widgets/broadcast.tag | 71 ++++++++++++++++++++-- 5 files changed, 84 insertions(+), 6 deletions(-) (limited to 'src/api') diff --git a/locales/en.yml b/locales/en.yml index a2bd9aa1be..9a54eed674 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -396,6 +396,12 @@ desktop: mk-messaging-home-widget: title: "Messaging" + mk-broadcast-home-widget: + fetching: "Fetching" + no-broadcasts: "No broadcasts" + have-a-nice-day: "Have a nice day!" + next: "Next" + mk-repost-form: quote: "Quote..." cancel: "Cancel" diff --git a/locales/ja.yml b/locales/ja.yml index d0292e8c42..dcf4663395 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -396,6 +396,12 @@ desktop: mk-messaging-home-widget: title: "メッセージ" + mk-broadcast-home-widget: + fetching: "確認中" + no-broadcasts: "お知らせはありません" + have-a-nice-day: "良い一日を!" + next: "次" + mk-repost-form: quote: "引用する..." cancel: "キャンセル" diff --git a/src/api/endpoints/meta.ts b/src/api/endpoints/meta.ts index 4f626efa6f..e27ca39e7e 100644 --- a/src/api/endpoints/meta.ts +++ b/src/api/endpoints/meta.ts @@ -53,6 +53,7 @@ module.exports = (params) => new Promise(async (res, rej) => { model: os.cpus()[0].model, cores: os.cpus().length }, - top_image: meta.top_image + top_image: meta.top_image, + broadcasts: meta.broadcasts }); }); diff --git a/src/web/app/common/tags/raw.tag b/src/web/app/common/tags/raw.tag index e1285694e4..adc6de5a3b 100644 --- a/src/web/app/common/tags/raw.tag +++ b/src/web/app/common/tags/raw.tag @@ -5,5 +5,9 @@ diff --git a/src/web/app/desktop/tags/home-widgets/broadcast.tag b/src/web/app/desktop/tags/home-widgets/broadcast.tag index 7caf4dcddd..00fef83742 100644 --- a/src/web/app/desktop/tags/home-widgets/broadcast.tag +++ b/src/web/app/desktop/tags/home-widgets/broadcast.tag @@ -1,4 +1,4 @@ - +
@@ -8,22 +8,35 @@
-

開発者募集中!

-

Misskeyはオープンソースで開発されています。リポジトリはこちら。

+

%i18n:desktop.tags.mk-broadcast-home-widget.fetching%

+

{ + broadcasts.length == 0 ? '%i18n:desktop.tags.mk-broadcast-home-widget.no-broadcasts%' : broadcasts[i].title + }

+

%i18n:desktop.tags.mk-broadcast-home-widget.have-a-nice-day%

+ 1 } onclick={ next }>%i18n:desktop.tags.mk-broadcast-home-widget.next% >>
-- cgit v1.2.3-freya From 598500223a762ada9abe084f81248432731792e4 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 16 Nov 2017 22:49:58 +0900 Subject: Fix #924 and some readability improvements --- src/api/common/add-file-to-drive.ts | 120 ++++++++++++++++++++---------------- 1 file changed, 66 insertions(+), 54 deletions(-) (limited to 'src/api') diff --git a/src/api/common/add-file-to-drive.ts b/src/api/common/add-file-to-drive.ts index 9ed5e88744..f96f58cb66 100644 --- a/src/api/common/add-file-to-drive.ts +++ b/src/api/common/add-file-to-drive.ts @@ -1,18 +1,20 @@ +import { Buffer } from 'buffer'; +import * as fs from 'fs'; +import * as tmp from 'tmp'; +import * as stream from 'stream'; + import * as mongodb from 'mongodb'; import * as crypto from 'crypto'; import * as gm from 'gm'; import * as debug from 'debug'; import fileType = require('file-type'); import prominence = require('prominence'); + import DriveFile, { getGridFSBucket } from '../models/drive-file'; import DriveFolder from '../models/drive-folder'; import serialize from '../serializers/drive-file'; import event from '../event'; import config from '../../conf'; -import { Buffer } from 'buffer'; -import * as fs from 'fs'; -import * as tmp from 'tmp'; -import * as stream from 'stream'; const log = debug('misskey:register-drive-file'); @@ -67,10 +69,12 @@ const addFile = async ( .once('data', (buffer: Buffer) => { readable.destroy(); const type = fileType(buffer); - if (!type) { + if (type) { + return res([type.mime, type.ext]); + } else { + // 種類が同定できなかったら application/octet-stream にする return res(['application/octet-stream', null]); } - return res([type.mime, type.ext]); }); }))(), // size @@ -105,9 +109,18 @@ const addFile = async ( const [properties, folder] = await Promise.all([ // properties (async () => { + // 画像かどうか if (!/^image\/.*$/.test(mime)) { return null; } + + const imageType = mime.split('/')[1]; + + // 画像でもPNGかJPEGでないならスキップ + if (imageType != 'png' && imageType != 'jpeg') { + return null; + } + // If the file is an image, calculate width and height to save in property const g = gm(fs.createReadStream(path), name); const size = await prominence(g).size(); @@ -115,7 +128,9 @@ const addFile = async ( width: size.width, height: size.height }; + log('image width and height is calculated'); + return properties; })(), // folder @@ -136,20 +151,18 @@ const addFile = async ( (async () => { // Calculate drive usage const usage = await DriveFile - .aggregate([ - { $match: { 'metadata.user_id': user._id } }, - { - $project: { - length: true - } - }, - { - $group: { - _id: null, - usage: { $sum: '$length' } - } + .aggregate([{ + $match: { 'metadata.user_id': user._id } + }, { + $project: { + length: true } - ]) + }, { + $group: { + _id: null, + usage: { $sum: '$length' } + } + }]) .then((aggregates: any[]) => { if (aggregates.length > 0) { return aggregates[0].usage; @@ -211,41 +224,40 @@ export default (user: any, file: string | stream.Readable, ...args) => new Promi } rej(new Error('un-compatible file.')); }) - .then(([path, remove]): Promise => new Promise((res, rej) => { - addFile(user, path, ...args) - .then(file => { - res(file); - if (remove) { - fs.unlink(path, (e) => { - if (e) log(e.stack); - }); - } - }) - .catch(rej); - })) - .then(file => { - log(`drive file has been created ${file._id}`); - resolve(file); - - serialize(file) - .then(serializedFile => { - // Publish drive_file_created event - event(user._id, 'drive_file_created', serializedFile); - - // Register to search database - if (config.elasticsearch.enable) { - const es = require('../../db/elasticsearch'); - es.index({ - index: 'misskey', - type: 'drive_file', - id: file._id.toString(), - body: { - name: file.name, - user_id: user._id.toString() - } - }); + .then(([path, remove]): Promise => new Promise((res, rej) => { + addFile(user, path, ...args) + .then(file => { + res(file); + if (remove) { + fs.unlink(path, (e) => { + if (e) log(e.stack); + }); + } + }) + .catch(rej); + })) + .then(file => { + log(`drive file has been created ${file._id}`); + resolve(file); + + serialize(file).then(serializedFile => { + // Publish drive_file_created event + event(user._id, 'drive_file_created', serializedFile); + + // Register to search database + if (config.elasticsearch.enable) { + const es = require('../../db/elasticsearch'); + es.index({ + index: 'misskey', + type: 'drive_file', + id: file._id.toString(), + body: { + name: file.name, + user_id: user._id.toString() } }); - }) - .catch(reject); + } + }); + }) + .catch(reject); }); -- cgit v1.2.3-freya From a8c6d0ed9023f7e0699cfe5c519532231e49cc3b Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 16 Nov 2017 23:46:36 +0900 Subject: #465 --- src/api/common/add-file-to-drive.ts | 3 ++- src/api/endpoints/drive/files/update.ts | 6 +++--- src/api/endpoints/drive/folders/create.ts | 6 +++--- src/api/endpoints/drive/folders/update.ts | 6 +++--- src/api/event.ts | 6 ++++++ src/api/stream/drive.ts | 10 ++++++++++ src/api/streaming.ts | 2 ++ src/web/app/common/mixins.ts | 3 ++- src/web/app/common/scripts/drive-stream-manager.ts | 20 ++++++++++++++++++++ src/web/app/common/scripts/drive-stream.ts | 14 ++++++++++++++ src/web/app/desktop/tags/drive/browser.tag | 22 +++++++++++++--------- src/web/app/mobile/tags/drive.tag | 22 +++++++++++++--------- 12 files changed, 91 insertions(+), 29 deletions(-) create mode 100644 src/api/stream/drive.ts create mode 100644 src/web/app/common/scripts/drive-stream-manager.ts create mode 100644 src/web/app/common/scripts/drive-stream.ts (limited to 'src/api') diff --git a/src/api/common/add-file-to-drive.ts b/src/api/common/add-file-to-drive.ts index f96f58cb66..2a649788af 100644 --- a/src/api/common/add-file-to-drive.ts +++ b/src/api/common/add-file-to-drive.ts @@ -13,7 +13,7 @@ import prominence = require('prominence'); import DriveFile, { getGridFSBucket } from '../models/drive-file'; import DriveFolder from '../models/drive-folder'; import serialize from '../serializers/drive-file'; -import event from '../event'; +import event, { publishDriveStream } from '../event'; import config from '../../conf'; const log = debug('misskey:register-drive-file'); @@ -243,6 +243,7 @@ export default (user: any, file: string | stream.Readable, ...args) => new Promi serialize(file).then(serializedFile => { // Publish drive_file_created event event(user._id, 'drive_file_created', serializedFile); + publishDriveStream(user._id, 'file_created', serializedFile); // Register to search database if (config.elasticsearch.enable) { diff --git a/src/api/endpoints/drive/files/update.ts b/src/api/endpoints/drive/files/update.ts index f265142c4d..f39a420d6e 100644 --- a/src/api/endpoints/drive/files/update.ts +++ b/src/api/endpoints/drive/files/update.ts @@ -6,7 +6,7 @@ import DriveFolder from '../../../models/drive-folder'; import DriveFile from '../../../models/drive-file'; import { validateFileName } from '../../../models/drive-file'; import serialize from '../../../serializers/drive-file'; -import event from '../../../event'; +import { publishDriveStream } from '../../../event'; /** * Update a file @@ -72,6 +72,6 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Response res(fileObj); - // Publish drive_file_updated event - event(user._id, 'drive_file_updated', fileObj); + // Publish file_updated event + publishDriveStream(user._id, 'file_updated', fileObj); }); diff --git a/src/api/endpoints/drive/folders/create.ts b/src/api/endpoints/drive/folders/create.ts index 8c875db164..be847b2153 100644 --- a/src/api/endpoints/drive/folders/create.ts +++ b/src/api/endpoints/drive/folders/create.ts @@ -5,7 +5,7 @@ import $ from 'cafy'; import DriveFolder from '../../../models/drive-folder'; import { isValidFolderName } from '../../../models/drive-folder'; import serialize from '../../../serializers/drive-folder'; -import event from '../../../event'; +import { publishDriveStream } from '../../../event'; /** * Create drive folder @@ -52,6 +52,6 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Response res(folderObj); - // Publish drive_folder_created event - event(user._id, 'drive_folder_created', folderObj); + // Publish folder_created event + publishDriveStream(user._id, 'folder_created', folderObj); }); diff --git a/src/api/endpoints/drive/folders/update.ts b/src/api/endpoints/drive/folders/update.ts index 4f2e3d2a7a..ff673402ab 100644 --- a/src/api/endpoints/drive/folders/update.ts +++ b/src/api/endpoints/drive/folders/update.ts @@ -5,7 +5,7 @@ import $ from 'cafy'; import DriveFolder from '../../../models/drive-folder'; import { isValidFolderName } from '../../../models/drive-folder'; import serialize from '../../../serializers/drive-folder'; -import event from '../../../event'; +import { publishDriveStream } from '../../../event'; /** * Update a folder @@ -96,6 +96,6 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Response res(folderObj); - // Publish drive_folder_updated event - event(user._id, 'drive_folder_updated', folderObj); + // Publish folder_updated event + publishDriveStream(user._id, 'folder_updated', folderObj); }); diff --git a/src/api/event.ts b/src/api/event.ts index 927883737e..8605a0f1e4 100644 --- a/src/api/event.ts +++ b/src/api/event.ts @@ -17,6 +17,10 @@ class MisskeyEvent { this.publish(`user-stream:${userId}`, type, typeof value === 'undefined' ? null : value); } + public publishDriveStream(userId: ID, type: string, value?: any): void { + this.publish(`drive-stream:${userId}`, type, typeof value === 'undefined' ? null : value); + } + public publishPostStream(postId: ID, type: string, value?: any): void { this.publish(`post-stream:${postId}`, type, typeof value === 'undefined' ? null : value); } @@ -46,6 +50,8 @@ const ev = new MisskeyEvent(); export default ev.publishUserStream.bind(ev); +export const publishDriveStream = ev.publishDriveStream.bind(ev); + export const publishPostStream = ev.publishPostStream.bind(ev); export const publishMessagingStream = ev.publishMessagingStream.bind(ev); diff --git a/src/api/stream/drive.ts b/src/api/stream/drive.ts new file mode 100644 index 0000000000..c97ab80dcc --- /dev/null +++ b/src/api/stream/drive.ts @@ -0,0 +1,10 @@ +import * as websocket from 'websocket'; +import * as redis from 'redis'; + +export default function(request: websocket.request, connection: websocket.connection, subscriber: redis.RedisClient, user: any): void { + // Subscribe drive stream + subscriber.subscribe(`misskey:drive-stream:${user._id}`); + subscriber.on('message', (_, data) => { + connection.send(data); + }); +} diff --git a/src/api/streaming.ts b/src/api/streaming.ts index 1f0ba848c1..c06d64c245 100644 --- a/src/api/streaming.ts +++ b/src/api/streaming.ts @@ -7,6 +7,7 @@ import AccessToken from './models/access-token'; import isNativeToken from './common/is-native-token'; import homeStream from './stream/home'; +import driveStream from './stream/drive'; import messagingStream from './stream/messaging'; import messagingIndexStream from './stream/messaging-index'; import serverStream from './stream/server'; @@ -58,6 +59,7 @@ module.exports = (server: http.Server) => { const channel = request.resourceURL.pathname === '/' ? homeStream : + request.resourceURL.pathname === '/drive' ? driveStream : request.resourceURL.pathname === '/messaging' ? messagingStream : request.resourceURL.pathname === '/messaging-index' ? messagingIndexStream : null; diff --git a/src/web/app/common/mixins.ts b/src/web/app/common/mixins.ts index b5eb1acc78..5949069624 100644 --- a/src/web/app/common/mixins.ts +++ b/src/web/app/common/mixins.ts @@ -4,6 +4,7 @@ import MiOS from './mios'; import ServerStreamManager from './scripts/server-stream-manager'; import RequestsStreamManager from './scripts/requests-stream-manager'; import MessagingIndexStream from './scripts/messaging-index-stream-manager'; +import DriveStreamManager from './scripts/drive-stream-manager'; export default (mios: MiOS) => { (riot as any).mixin('os', { @@ -30,7 +31,7 @@ export default (mios: MiOS) => { (riot as any).mixin('api', { api: mios.api }); - + (riot as any).mixin('drive-stream', { driveStream: new DriveStreamManager(mios.i) }); (riot as any).mixin('stream', { stream: mios.stream }); (riot as any).mixin('server-stream', { serverStream: new ServerStreamManager() }); diff --git a/src/web/app/common/scripts/drive-stream-manager.ts b/src/web/app/common/scripts/drive-stream-manager.ts new file mode 100644 index 0000000000..8acdd7cbba --- /dev/null +++ b/src/web/app/common/scripts/drive-stream-manager.ts @@ -0,0 +1,20 @@ +import StreamManager from './stream-manager'; +import Connection from './drive-stream'; + +export default class DriveStreamManager extends StreamManager { + private me; + + constructor(me) { + super(); + + this.me = me; + } + + public getConnection() { + if (this.connection == null) { + this.connection = new Connection(this.me); + } + + return this.connection; + } +} diff --git a/src/web/app/common/scripts/drive-stream.ts b/src/web/app/common/scripts/drive-stream.ts new file mode 100644 index 0000000000..1b33435578 --- /dev/null +++ b/src/web/app/common/scripts/drive-stream.ts @@ -0,0 +1,14 @@ +import Stream from './stream'; + +/** + * Drive stream connection + */ +class Connection extends Stream { + constructor(me) { + super('drive', { + i: me.token + }); + } +} + +export default Connection; diff --git a/src/web/app/desktop/tags/drive/browser.tag b/src/web/app/desktop/tags/drive/browser.tag index 18e27f24bc..311209fcb6 100644 --- a/src/web/app/desktop/tags/drive/browser.tag +++ b/src/web/app/desktop/tags/drive/browser.tag @@ -247,7 +247,10 @@ this.mixin('i'); this.mixin('api'); - this.mixin('stream'); + + this.mixin('drive-stream'); + this.connection = this.driveStream.getConnection(); + this.connectionId = this.driveStream.use(); this.files = []; this.folders = []; @@ -280,10 +283,10 @@ }); }); - this.stream.on('drive_file_created', this.onStreamDriveFileCreated); - this.stream.on('drive_file_updated', this.onStreamDriveFileUpdated); - this.stream.on('drive_folder_created', this.onStreamDriveFolderCreated); - this.stream.on('drive_folder_updated', this.onStreamDriveFolderUpdated); + this.connection.on('file_created', this.onStreamDriveFileCreated); + this.connection.on('file_updated', this.onStreamDriveFileUpdated); + this.connection.on('folder_created', this.onStreamDriveFolderCreated); + this.connection.on('folder_updated', this.onStreamDriveFolderUpdated); if (this.opts.folder) { this.move(this.opts.folder); @@ -293,10 +296,11 @@ }); this.on('unmount', () => { - this.stream.off('drive_file_created', this.onStreamDriveFileCreated); - this.stream.off('drive_file_updated', this.onStreamDriveFileUpdated); - this.stream.off('drive_folder_created', this.onStreamDriveFolderCreated); - this.stream.off('drive_folder_updated', this.onStreamDriveFolderUpdated); + this.connection.off('file_created', this.onStreamDriveFileCreated); + this.connection.off('file_updated', this.onStreamDriveFileUpdated); + this.connection.off('folder_created', this.onStreamDriveFolderCreated); + this.connection.off('folder_updated', this.onStreamDriveFolderUpdated); + this.driveStream.dispose(this.connectionId); }); this.onStreamDriveFileCreated = file => { diff --git a/src/web/app/mobile/tags/drive.tag b/src/web/app/mobile/tags/drive.tag index 870a451acb..2c36c43ac5 100644 --- a/src/web/app/mobile/tags/drive.tag +++ b/src/web/app/mobile/tags/drive.tag @@ -172,7 +172,10 @@ diff --git a/src/web/app/desktop/tags/messaging/room-window.tag b/src/web/app/desktop/tags/messaging/room-window.tag index dca0172be3..1c6ff7c4bc 100644 --- a/src/web/app/desktop/tags/messaging/room-window.tag +++ b/src/web/app/desktop/tags/messaging/room-window.tag @@ -19,11 +19,9 @@ diff --git a/src/web/app/mobile/tags/timeline.tag b/src/web/app/mobile/tags/timeline.tag index 1d6ce23598..074422a20e 100644 --- a/src/web/app/mobile/tags/timeline.tag +++ b/src/web/app/mobile/tags/timeline.tag @@ -164,7 +164,7 @@
-

{ p.channel.title }:

+

{ p.channel.title }:

diff --git a/src/web/app/mobile/tags/ui.tag b/src/web/app/mobile/tags/ui.tag index 0c969d3902..bad6bf73fe 100644 --- a/src/web/app/mobile/tags/ui.tag +++ b/src/web/app/mobile/tags/ui.tag @@ -239,7 +239,7 @@
  • %i18n:mobile.tags.mk-ui-nav.messaging%
  • -

    %i18n:mobile.tags.mk-ui-nav.about%

    +

    %i18n:mobile.tags.mk-ui-nav.about%