From 8fd78aebbf01755ffdecb9aa17dff1f842b194ff Mon Sep 17 00:00:00 2001 From: こぴなたみぽ Date: Fri, 22 Dec 2017 04:50:50 +0900 Subject: wip --- src/api/models/mute.ts | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/api/models/mute.ts (limited to 'src/api/models') diff --git a/src/api/models/mute.ts b/src/api/models/mute.ts new file mode 100644 index 0000000000..16018b82f7 --- /dev/null +++ b/src/api/models/mute.ts @@ -0,0 +1,3 @@ +import db from '../../db/mongodb'; + +export default db.get('mute') as any; // fuck type definition -- cgit v1.2.3-freya From 9a282e37be5ba847718d198d30eb97f31d11f2a0 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 2 Feb 2018 08:06:01 +0900 Subject: wip --- src/api/models/app.ts | 95 ++++++++++++++- src/api/models/auth-session.ts | 44 ++++++- src/api/models/channel.ts | 66 ++++++++++- src/api/models/drive-file.ts | 84 ++++++++++++- src/api/models/drive-folder.ts | 67 ++++++++++- src/api/models/messaging-message.ts | 66 ++++++++++- src/api/models/notification.ts | 64 +++++++++- src/api/models/post-reaction.ts | 47 +++++++- src/api/models/post.ts | 189 ++++++++++++++++++++++++++++- src/api/models/signin.ts | 28 ++++- src/api/models/user.ts | 196 ++++++++++++++++++++++++++++++- src/api/serializers/app.ts | 83 ------------- src/api/serializers/auth-session.ts | 40 ------- src/api/serializers/channel.ts | 66 ----------- src/api/serializers/drive-file.ts | 78 ------------ src/api/serializers/drive-folder.ts | 64 ---------- src/api/serializers/drive-tag.ts | 35 ------ src/api/serializers/messaging-message.ts | 68 ----------- src/api/serializers/notification.ts | 65 ---------- src/api/serializers/post-reaction.ts | 43 ------- src/api/serializers/post.ts | 192 ------------------------------ src/api/serializers/signin.ts | 23 ---- src/api/serializers/user.ts | 190 ------------------------------ 23 files changed, 920 insertions(+), 973 deletions(-) delete mode 100644 src/api/serializers/app.ts delete mode 100644 src/api/serializers/auth-session.ts delete mode 100644 src/api/serializers/channel.ts delete mode 100644 src/api/serializers/drive-file.ts delete mode 100644 src/api/serializers/drive-folder.ts delete mode 100644 src/api/serializers/drive-tag.ts delete mode 100644 src/api/serializers/messaging-message.ts delete mode 100644 src/api/serializers/notification.ts delete mode 100644 src/api/serializers/post-reaction.ts delete mode 100644 src/api/serializers/post.ts delete mode 100644 src/api/serializers/signin.ts delete mode 100644 src/api/serializers/user.ts (limited to 'src/api/models') diff --git a/src/api/models/app.ts b/src/api/models/app.ts index 68f2f448b0..fe9d49ff67 100644 --- a/src/api/models/app.ts +++ b/src/api/models/app.ts @@ -1,13 +1,96 @@ +import * as mongo from 'mongodb'; +import deepcopy = require('deepcopy'); +import AccessToken from './access-token'; import db from '../../db/mongodb'; +import config from '../../conf'; -const collection = db.get('apps'); +const App = db.get('apps'); +App.createIndex('name_id'); +App.createIndex('name_id_lower'); +App.createIndex('secret'); +export default App; -(collection as any).createIndex('name_id'); // fuck type definition -(collection as any).createIndex('name_id_lower'); // fuck type definition -(collection as any).createIndex('secret'); // fuck type definition - -export default collection as any; // fuck type definition +export type IApp = { + _id: mongo.ObjectID; + created_at: Date; + user_id: mongo.ObjectID; +}; export function isValidNameId(nameId: string): boolean { return typeof nameId == 'string' && /^[a-zA-Z0-9\-]{3,30}$/.test(nameId); } + +/** + * Pack an app for API response + * + * @param {any} app + * @param {any} me? + * @param {any} options? + * @return {Promise} + */ +export const pack = ( + app: any, + me?: any, + options?: { + includeSecret?: boolean, + includeProfileImageIds?: boolean + } +) => new Promise(async (resolve, reject) => { + const opts = options || { + includeSecret: false, + includeProfileImageIds: false + }; + + let _app: any; + + // Populate the app if 'app' is ID + if (mongo.ObjectID.prototype.isPrototypeOf(app)) { + _app = await App.findOne({ + _id: app + }); + } else if (typeof app === 'string') { + _app = await App.findOne({ + _id: new mongo.ObjectID(app) + }); + } else { + _app = deepcopy(app); + } + + // Me + if (me && !mongo.ObjectID.prototype.isPrototypeOf(me)) { + if (typeof me === 'string') { + me = new mongo.ObjectID(me); + } else { + me = me._id; + } + } + + // Rename _id to id + _app.id = _app._id; + delete _app._id; + + delete _app.name_id_lower; + + // Visible by only owner + if (!opts.includeSecret) { + delete _app.secret; + } + + _app.icon_url = _app.icon != null + ? `${config.drive_url}/${_app.icon}` + : `${config.drive_url}/app-default.jpg`; + + if (me) { + // 既に連携しているか + const exist = await AccessToken.count({ + app_id: _app.id, + user_id: me, + }, { + limit: 1 + }); + + _app.is_authorized = exist === 1; + } + + resolve(_app); +}); diff --git a/src/api/models/auth-session.ts b/src/api/models/auth-session.ts index b264a133e9..997ec61c20 100644 --- a/src/api/models/auth-session.ts +++ b/src/api/models/auth-session.ts @@ -1,3 +1,45 @@ +import * as mongo from 'mongodb'; +import deepcopy = require('deepcopy'); import db from '../../db/mongodb'; +import { pack as packApp } from './app'; -export default db.get('auth_sessions') as any; // fuck type definition +const AuthSession = db.get('auth_sessions'); +export default AuthSession; + +export interface IAuthSession { + _id: mongo.ObjectID; +} + +/** + * Pack an auth session for API response + * + * @param {any} session + * @param {any} me? + * @return {Promise} + */ +export const pack = ( + session: any, + me?: any +) => new Promise(async (resolve, reject) => { + let _session: any; + + // TODO: Populate session if it ID + + _session = deepcopy(session); + + // Me + if (me && !mongo.ObjectID.prototype.isPrototypeOf(me)) { + if (typeof me === 'string') { + me = new mongo.ObjectID(me); + } else { + me = me._id; + } + } + + delete _session._id; + + // Populate app + _session.app = await packApp(_session.app_id, me); + + resolve(_session); +}); diff --git a/src/api/models/channel.ts b/src/api/models/channel.ts index c80e84dbc8..815d53593c 100644 --- a/src/api/models/channel.ts +++ b/src/api/models/channel.ts @@ -1,9 +1,11 @@ import * as mongo from 'mongodb'; +import deepcopy = require('deepcopy'); +import { IUser } from './user'; +import Watching from './channel-watching'; import db from '../../db/mongodb'; -const collection = db.get('channels'); - -export default collection as any; // fuck type definition +const Channel = db.get('channels'); +export default Channel; export type IChannel = { _id: mongo.ObjectID; @@ -12,3 +14,61 @@ export type IChannel = { user_id: mongo.ObjectID; index: number; }; + +/** + * Pack a channel for API response + * + * @param channel target + * @param me? serializee + * @return response + */ +export const pack = ( + channel: string | mongo.ObjectID | IChannel, + me?: string | mongo.ObjectID | IUser +) => new Promise(async (resolve, reject) => { + + let _channel: any; + + // Populate the channel if 'channel' is ID + if (mongo.ObjectID.prototype.isPrototypeOf(channel)) { + _channel = await Channel.findOne({ + _id: channel + }); + } else if (typeof channel === 'string') { + _channel = await Channel.findOne({ + _id: new mongo.ObjectID(channel) + }); + } else { + _channel = deepcopy(channel); + } + + // Rename _id to id + _channel.id = _channel._id; + delete _channel._id; + + // Remove needless properties + delete _channel.user_id; + + // Me + const meId: mongo.ObjectID = me + ? mongo.ObjectID.prototype.isPrototypeOf(me) + ? me as mongo.ObjectID + : typeof me === 'string' + ? new mongo.ObjectID(me) + : (me as IUser)._id + : null; + + if (me) { + //#region Watchしているかどうか + const watch = await Watching.findOne({ + user_id: meId, + channel_id: _channel.id, + deleted_at: { $exists: false } + }); + + _channel.is_watching = watch !== null; + //#endregion + } + + resolve(_channel); +}); diff --git a/src/api/models/drive-file.ts b/src/api/models/drive-file.ts index 802ee5a5fe..6a8db3ad42 100644 --- a/src/api/models/drive-file.ts +++ b/src/api/models/drive-file.ts @@ -1,9 +1,12 @@ import * as mongodb from 'mongodb'; +import deepcopy = require('deepcopy'); +import { pack as packFolder } from './drive-folder'; +import config from '../../conf'; import monkDb, { nativeDbConn } from '../../db/mongodb'; -const collection = monkDb.get('drive_files.files'); +const DriveFile = monkDb.get('drive_files.files'); -export default collection as any; // fuck type definition +export default DriveFile; const getGridFSBucket = async (): Promise => { const db = await nativeDbConn(); @@ -15,6 +18,12 @@ const getGridFSBucket = async (): Promise => { export { getGridFSBucket }; +export type IDriveFile = { + _id: mongodb.ObjectID; + created_at: Date; + user_id: mongodb.ObjectID; +}; + export function validateFileName(name: string): boolean { return ( (name.trim().length > 0) && @@ -24,3 +33,74 @@ export function validateFileName(name: string): boolean { (name.indexOf('..') === -1) ); } + +/** + * Pack a drive file for API response + * + * @param {any} file + * @param {any} options? + * @return {Promise} + */ +export const pack = ( + file: any, + options?: { + detail: boolean + } +) => new Promise(async (resolve, reject) => { + const opts = Object.assign({ + detail: false + }, options); + + let _file: any; + + // Populate the file if 'file' is ID + if (mongodb.ObjectID.prototype.isPrototypeOf(file)) { + _file = await DriveFile.findOne({ + _id: file + }); + } else if (typeof file === 'string') { + _file = await DriveFile.findOne({ + _id: new mongodb.ObjectID(file) + }); + } else { + _file = deepcopy(file); + } + + if (!_file) return reject('invalid file arg.'); + + // rendered target + let _target: any = {}; + + _target.id = _file._id; + _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); + + _target.url = `${config.drive_url}/${_target.id}/${encodeURIComponent(_target.name)}`; + + if (_target.properties == null) _target.properties = {}; + + if (opts.detail) { + if (_target.folder_id) { + // Populate folder + _target.folder = await packFolder(_target.folder_id, { + detail: true + }); + } + + /* + if (_target.tags) { + // Populate tags + _target.tags = await _target.tags.map(async (tag: any) => + await serializeDriveTag(tag) + ); + } + */ + } + + resolve(_target); +}); diff --git a/src/api/models/drive-folder.ts b/src/api/models/drive-folder.ts index f81ffe855d..48b26c2bd6 100644 --- a/src/api/models/drive-folder.ts +++ b/src/api/models/drive-folder.ts @@ -1,6 +1,16 @@ +import * as mongo from 'mongodb'; +import deepcopy = require('deepcopy'); import db from '../../db/mongodb'; +import DriveFile from './drive-file'; -export default db.get('drive_folders') as any; // fuck type definition +const DriveFolder = db.get('drive_folders'); +export default DriveFolder; + +export type IDriveFolder = { + _id: mongo.ObjectID; + created_at: Date; + user_id: mongo.ObjectID; +}; export function isValidFolderName(name: string): boolean { return ( @@ -8,3 +18,58 @@ export function isValidFolderName(name: string): boolean { (name.length <= 200) ); } + +/** + * Pack a drive folder for API response + * + * @param {any} folder + * @param {any} options? + * @return {Promise} + */ +export const pack = ( + folder: any, + options?: { + detail: boolean + } +) => new Promise(async (resolve, reject) => { + const opts = Object.assign({ + detail: false + }, options); + + let _folder: any; + + // Populate the folder if 'folder' is ID + if (mongo.ObjectID.prototype.isPrototypeOf(folder)) { + _folder = await DriveFolder.findOne({ _id: folder }); + } else if (typeof folder === 'string') { + _folder = await DriveFolder.findOne({ _id: new mongo.ObjectID(folder) }); + } else { + _folder = deepcopy(folder); + } + + // Rename _id to id + _folder.id = _folder._id; + delete _folder._id; + + if (opts.detail) { + const childFoldersCount = await DriveFolder.count({ + parent_id: _folder.id + }); + + const childFilesCount = await DriveFile.count({ + 'metadata.folder_id': _folder.id + }); + + _folder.folders_count = childFoldersCount; + _folder.files_count = childFilesCount; + } + + if (opts.detail && _folder.parent_id) { + // Populate parent folder + _folder.parent = await pack(_folder.parent_id, { + detail: true + }); + } + + resolve(_folder); +}); diff --git a/src/api/models/messaging-message.ts b/src/api/models/messaging-message.ts index 18afa57e44..ffdda1db21 100644 --- a/src/api/models/messaging-message.ts +++ b/src/api/models/messaging-message.ts @@ -1,7 +1,12 @@ import * as mongo from 'mongodb'; +import deepcopy = require('deepcopy'); +import { pack as packUser } from './user'; +import { pack as packFile } from './drive-file'; import db from '../../db/mongodb'; +import parse from '../common/text'; -export default db.get('messaging_messages') as any; // fuck type definition +const MessagingMessage = db.get('messaging_messages'); +export default MessagingMessage; export interface IMessagingMessage { _id: mongo.ObjectID; @@ -10,3 +15,62 @@ export interface IMessagingMessage { export function isValidText(text: string): boolean { return text.length <= 1000 && text.trim() != ''; } + +/** + * Pack a messaging message for API response + * + * @param {any} message + * @param {any} me? + * @param {any} options? + * @return {Promise} + */ +export const pack = ( + message: any, + me?: any, + options?: { + populateRecipient: boolean + } +) => new Promise(async (resolve, reject) => { + const opts = options || { + populateRecipient: true + }; + + let _message: any; + + // Populate the message if 'message' is ID + if (mongo.ObjectID.prototype.isPrototypeOf(message)) { + _message = await MessagingMessage.findOne({ + _id: message + }); + } else if (typeof message === 'string') { + _message = await MessagingMessage.findOne({ + _id: new mongo.ObjectID(message) + }); + } else { + _message = deepcopy(message); + } + + // Rename _id to id + _message.id = _message._id; + delete _message._id; + + // Parse text + if (_message.text) { + _message.ast = parse(_message.text); + } + + // Populate user + _message.user = await packUser(_message.user_id, me); + + if (_message.file) { + // Populate file + _message.file = await packFile(_message.file_id); + } + + if (opts.populateRecipient) { + // Populate recipient + _message.recipient = await packUser(_message.recipient_id, me); + } + + resolve(_message); +}); diff --git a/src/api/models/notification.ts b/src/api/models/notification.ts index e3dc6c70a3..fa7049d312 100644 --- a/src/api/models/notification.ts +++ b/src/api/models/notification.ts @@ -1,8 +1,11 @@ import * as mongo from 'mongodb'; +import deepcopy = require('deepcopy'); import db from '../../db/mongodb'; -import { IUser } from './user'; +import { IUser, pack as packUser } from './user'; +import { pack as packPost } from './post'; -export default db.get('notifications') as any; // fuck type definition +const Notification = db.get('notifications'); +export default Notification; export interface INotification { _id: mongo.ObjectID; @@ -45,3 +48,60 @@ export interface INotification { */ is_read: Boolean; } + +/** + * Pack a notification for API response + * + * @param {any} notification + * @return {Promise} + */ +export const pack = (notification: any) => new Promise(async (resolve, reject) => { + let _notification: any; + + // Populate the notification if 'notification' is ID + if (mongo.ObjectID.prototype.isPrototypeOf(notification)) { + _notification = await Notification.findOne({ + _id: notification + }); + } else if (typeof notification === 'string') { + _notification = await Notification.findOne({ + _id: new mongo.ObjectID(notification) + }); + } else { + _notification = deepcopy(notification); + } + + // Rename _id to id + _notification.id = _notification._id; + delete _notification._id; + + // Rename notifier_id to user_id + _notification.user_id = _notification.notifier_id; + delete _notification.notifier_id; + + const me = _notification.notifiee_id; + delete _notification.notifiee_id; + + // Populate notifier + _notification.user = await packUser(_notification.user_id, me); + + switch (_notification.type) { + case 'follow': + // nope + break; + case 'mention': + case 'reply': + case 'repost': + case 'quote': + case 'reaction': + case 'poll_vote': + // Populate post + _notification.post = await packPost(_notification.post_id, me); + break; + default: + console.error(`Unknown type: ${_notification.type}`); + break; + } + + resolve(_notification); +}); diff --git a/src/api/models/post-reaction.ts b/src/api/models/post-reaction.ts index 282ae5bd21..568bfc89a2 100644 --- a/src/api/models/post-reaction.ts +++ b/src/api/models/post-reaction.ts @@ -1,3 +1,48 @@ +import * as mongo from 'mongodb'; +import deepcopy = require('deepcopy'); import db from '../../db/mongodb'; +import Reaction from './post-reaction'; +import { pack as packUser } from './user'; -export default db.get('post_reactions') as any; // fuck type definition +const PostReaction = db.get('post_reactions'); +export default PostReaction; + +export interface IPostReaction { + _id: mongo.ObjectID; +} + +/** + * Pack a reaction for API response + * + * @param {any} reaction + * @param {any} me? + * @return {Promise} + */ +export const pack = ( + reaction: any, + me?: any +) => new Promise(async (resolve, reject) => { + let _reaction: any; + + // Populate the reaction if 'reaction' is ID + if (mongo.ObjectID.prototype.isPrototypeOf(reaction)) { + _reaction = await Reaction.findOne({ + _id: reaction + }); + } else if (typeof reaction === 'string') { + _reaction = await Reaction.findOne({ + _id: new mongo.ObjectID(reaction) + }); + } else { + _reaction = deepcopy(reaction); + } + + // Rename _id to id + _reaction.id = _reaction._id; + delete _reaction._id; + + // Populate user + _reaction.user = await packUser(_reaction.user_id, me); + + resolve(_reaction); +}); diff --git a/src/api/models/post.ts b/src/api/models/post.ts index 7584ce182d..ecc5e1a5e4 100644 --- a/src/api/models/post.ts +++ b/src/api/models/post.ts @@ -1,8 +1,18 @@ import * as mongo from 'mongodb'; - +import deepcopy = require('deepcopy'); +import rap from '@prezzemolo/rap'; import db from '../../db/mongodb'; +import { IUser, pack as packUser } from './user'; +import { pack as packApp } from './app'; +import { pack as packChannel } from './channel'; +import Vote from './poll-vote'; +import Reaction from './post-reaction'; +import { pack as packFile } from './drive-file'; +import parse from '../common/text'; + +const Post = db.get('posts'); -export default db.get('posts') as any; // fuck type definition +export default Post; export function isValidText(text: string): boolean { return text.length <= 1000 && text.trim() != ''; @@ -20,3 +30,178 @@ export type IPost = { user_id: mongo.ObjectID; app_id: mongo.ObjectID; }; + +/** + * Pack a post for API response + * + * @param post target + * @param me? serializee + * @param options? serialize options + * @return response + */ +export const pack = async ( + post: string | mongo.ObjectID | IPost, + me?: string | mongo.ObjectID | IUser, + options?: { + detail: boolean + } +) => { + const opts = options || { + detail: true, + }; + + // Me + const meId: mongo.ObjectID = me + ? mongo.ObjectID.prototype.isPrototypeOf(me) + ? me as mongo.ObjectID + : typeof me === 'string' + ? new mongo.ObjectID(me) + : (me as IUser)._id + : null; + + let _post: any; + + // Populate the post if 'post' is ID + if (mongo.ObjectID.prototype.isPrototypeOf(post)) { + _post = await Post.findOne({ + _id: post + }); + } else if (typeof post === 'string') { + _post = await Post.findOne({ + _id: new mongo.ObjectID(post) + }); + } else { + _post = deepcopy(post); + } + + if (!_post) throw 'invalid post arg.'; + + const id = _post._id; + + // Rename _id to id + _post.id = _post._id; + delete _post._id; + + delete _post.mentions; + + // Parse text + if (_post.text) { + _post.ast = parse(_post.text); + } + + // Populate user + _post.user = packUser(_post.user_id, meId); + + // Populate app + if (_post.app_id) { + _post.app = packApp(_post.app_id); + } + + // Populate channel + if (_post.channel_id) { + _post.channel = packChannel(_post.channel_id); + } + + // Populate media + if (_post.media_ids) { + _post.media = Promise.all(_post.media_ids.map(fileId => + packFile(fileId) + )); + } + + // When requested a detailed post data + if (opts.detail) { + // Get previous post info + _post.prev = (async () => { + const prev = await 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 + _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 = pack(_post.reply_id, meId, { + detail: false + }); + } + + if (_post.repost_id) { + // Populate repost + _post.repost = pack(_post.repost_id, meId, { + detail: _post.text == null + }); + } + + // Poll + if (meId && _post.poll) { + _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) { + _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); + + return _post; +}; diff --git a/src/api/models/signin.ts b/src/api/models/signin.ts index 385a348f2e..262c8707ed 100644 --- a/src/api/models/signin.ts +++ b/src/api/models/signin.ts @@ -1,3 +1,29 @@ +import * as mongo from 'mongodb'; +import deepcopy = require('deepcopy'); import db from '../../db/mongodb'; -export default db.get('signin') as any; // fuck type definition +const Signin = db.get('signin'); +export default Signin; + +export interface ISignin { + _id: mongo.ObjectID; +} + +/** + * Pack a signin record for API response + * + * @param {any} record + * @return {Promise} + */ +export const pack = ( + record: any +) => new Promise(async (resolve, reject) => { + + const _record = deepcopy(record); + + // Rename _id to id + _record.id = _record._id; + delete _record._id; + + resolve(_record); +}); diff --git a/src/api/models/user.ts b/src/api/models/user.ts index 018979158f..48a45ac2f7 100644 --- a/src/api/models/user.ts +++ b/src/api/models/user.ts @@ -1,14 +1,19 @@ import * as mongo from 'mongodb'; - +import deepcopy = require('deepcopy'); +import rap from '@prezzemolo/rap'; import db from '../../db/mongodb'; -import { IPost } from './post'; +import { IPost, pack as packPost } from './post'; +import Following from './following'; +import Mute from './mute'; +import getFriends from '../common/get-friends'; +import config from '../../conf'; -const collection = db.get('users'); +const User = db.get('users'); -(collection as any).createIndex('username'); // fuck type definition -(collection as any).createIndex('token'); // fuck type definition +User.createIndex('username'); +User.createIndex('token'); -export default collection as any; // fuck type definition +export default User; export function validateUsername(username: string): boolean { return typeof username == 'string' && /^[a-zA-Z0-9\-]{3,20}$/.test(username); @@ -83,3 +88,182 @@ export function init(user): IUser { user.pinned_post_id = new mongo.ObjectID(user.pinned_post_id); return user; } + +/** + * Pack a user for API response + * + * @param user target + * @param me? serializee + * @param options? serialize options + * @return Packed user + */ +export const pack = ( + user: string | mongo.ObjectID | IUser, + me?: string | mongo.ObjectID | IUser, + options?: { + detail?: boolean, + includeSecrets?: boolean + } +) => new Promise(async (resolve, reject) => { + + const opts = Object.assign({ + detail: false, + includeSecrets: false + }, options); + + let _user: any; + + const fields = opts.detail ? { + settings: false + } : { + settings: false, + client_settings: false, + profile: false, + keywords: false, + domains: false + }; + + // Populate the user if 'user' is ID + if (mongo.ObjectID.prototype.isPrototypeOf(user)) { + _user = await User.findOne({ + _id: user + }, { fields }); + } else if (typeof user === 'string') { + _user = await User.findOne({ + _id: new mongo.ObjectID(user) + }, { fields }); + } else { + _user = deepcopy(user); + } + + if (!_user) return reject('invalid user arg.'); + + // Me + const meId: mongo.ObjectID = me + ? mongo.ObjectID.prototype.isPrototypeOf(me) + ? me as mongo.ObjectID + : typeof me === 'string' + ? new mongo.ObjectID(me) + : (me as IUser)._id + : null; + + // Rename _id to id + _user.id = _user._id; + delete _user._id; + + // Remove needless properties + delete _user.latest_post; + + // Remove private properties + delete _user.password; + delete _user.token; + delete _user.two_factor_temp_secret; + delete _user.two_factor_secret; + delete _user.username_lower; + if (_user.twitter) { + delete _user.twitter.access_token; + delete _user.twitter.access_token_secret; + } + delete _user.line; + + // Visible via only the official client + if (!opts.includeSecrets) { + delete _user.email; + delete _user.client_settings; + } + + if (!opts.detail) { + delete _user.two_factor_enabled; + } + + _user.avatar_url = _user.avatar_id != null + ? `${config.drive_url}/${_user.avatar_id}` + : `${config.drive_url}/default-avatar.jpg`; + + _user.banner_url = _user.banner_id != null + ? `${config.drive_url}/${_user.banner_id}` + : null; + + if (!meId || !meId.equals(_user.id) || !opts.detail) { + delete _user.avatar_id; + delete _user.banner_id; + + delete _user.drive_capacity; + } + + if (meId && !meId.equals(_user.id)) { + // Whether the user is following + _user.is_following = (async () => { + const follow = await Following.findOne({ + follower_id: meId, + followee_id: _user.id, + deleted_at: { $exists: false } + }); + return follow !== null; + })(); + + // Whether the user is followed + _user.is_followed = (async () => { + const follow2 = await Following.findOne({ + follower_id: _user.id, + followee_id: meId, + deleted_at: { $exists: false } + }); + return follow2 !== null; + })(); + + // Whether the user is muted + _user.is_muted = (async () => { + const mute = await Mute.findOne({ + muter_id: meId, + mutee_id: _user.id, + deleted_at: { $exists: false } + }); + return mute !== null; + })(); + } + + if (opts.detail) { + if (_user.pinned_post_id) { + // Populate pinned post + _user.pinned_post = packPost(_user.pinned_post_id, meId, { + detail: true + }); + } + + if (meId && !meId.equals(_user.id)) { + const myFollowingIds = await getFriends(meId); + + // Get following you know count + _user.following_you_know_count = Following.count({ + followee_id: { $in: myFollowingIds }, + follower_id: _user.id, + deleted_at: { $exists: false } + }); + + // Get followers you know count + _user.followers_you_know_count = Following.count({ + followee_id: _user.id, + follower_id: { $in: myFollowingIds }, + deleted_at: { $exists: false } + }); + } + } + + // resolve promises in _user object + _user = await rap(_user); + + resolve(_user); +}); + +/* +function img(url) { + return { + thumbnail: { + large: `${url}`, + medium: '', + small: '' + } + }; +} +*/ diff --git a/src/api/serializers/app.ts b/src/api/serializers/app.ts deleted file mode 100644 index 9d1c46dca4..0000000000 --- a/src/api/serializers/app.ts +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Module dependencies - */ -import * as mongo from 'mongodb'; -import deepcopy = require('deepcopy'); -import App from '../models/app'; -import AccessToken from '../models/access-token'; -import config from '../../conf'; - -/** - * Serialize an app - * - * @param {any} app - * @param {any} me? - * @param {any} options? - * @return {Promise} - */ -export default ( - app: any, - me?: any, - options?: { - includeSecret?: boolean, - includeProfileImageIds?: boolean - } -) => new Promise(async (resolve, reject) => { - const opts = options || { - includeSecret: false, - includeProfileImageIds: false - }; - - let _app: any; - - // Populate the app if 'app' is ID - if (mongo.ObjectID.prototype.isPrototypeOf(app)) { - _app = await App.findOne({ - _id: app - }); - } else if (typeof app === 'string') { - _app = await App.findOne({ - _id: new mongo.ObjectID(app) - }); - } else { - _app = deepcopy(app); - } - - // Me - if (me && !mongo.ObjectID.prototype.isPrototypeOf(me)) { - if (typeof me === 'string') { - me = new mongo.ObjectID(me); - } else { - me = me._id; - } - } - - // Rename _id to id - _app.id = _app._id; - delete _app._id; - - delete _app.name_id_lower; - - // Visible by only owner - if (!opts.includeSecret) { - delete _app.secret; - } - - _app.icon_url = _app.icon != null - ? `${config.drive_url}/${_app.icon}` - : `${config.drive_url}/app-default.jpg`; - - if (me) { - // 既に連携しているか - const exist = await AccessToken.count({ - app_id: _app.id, - user_id: me, - }, { - limit: 1 - }); - - _app.is_authorized = exist === 1; - } - - resolve(_app); -}); diff --git a/src/api/serializers/auth-session.ts b/src/api/serializers/auth-session.ts deleted file mode 100644 index a9acf1243a..0000000000 --- a/src/api/serializers/auth-session.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Module dependencies - */ -import * as mongo from 'mongodb'; -import deepcopy = require('deepcopy'); -import serializeApp from './app'; - -/** - * Serialize an auth session - * - * @param {any} session - * @param {any} me? - * @return {Promise} - */ -export default ( - session: any, - me?: any -) => new Promise(async (resolve, reject) => { - let _session: any; - - // TODO: Populate session if it ID - - _session = deepcopy(session); - - // Me - if (me && !mongo.ObjectID.prototype.isPrototypeOf(me)) { - if (typeof me === 'string') { - me = new mongo.ObjectID(me); - } else { - me = me._id; - } - } - - delete _session._id; - - // Populate app - _session.app = await serializeApp(_session.app_id, me); - - resolve(_session); -}); diff --git a/src/api/serializers/channel.ts b/src/api/serializers/channel.ts deleted file mode 100644 index 3cba39aa16..0000000000 --- a/src/api/serializers/channel.ts +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Module dependencies - */ -import * as mongo from 'mongodb'; -import deepcopy = require('deepcopy'); -import { IUser } from '../models/user'; -import { default as Channel, IChannel } from '../models/channel'; -import Watching from '../models/channel-watching'; - -/** - * Serialize a channel - * - * @param channel target - * @param me? serializee - * @return response - */ -export default ( - channel: string | mongo.ObjectID | IChannel, - me?: string | mongo.ObjectID | IUser -) => new Promise(async (resolve, reject) => { - - let _channel: any; - - // Populate the channel if 'channel' is ID - if (mongo.ObjectID.prototype.isPrototypeOf(channel)) { - _channel = await Channel.findOne({ - _id: channel - }); - } else if (typeof channel === 'string') { - _channel = await Channel.findOne({ - _id: new mongo.ObjectID(channel) - }); - } else { - _channel = deepcopy(channel); - } - - // Rename _id to id - _channel.id = _channel._id; - delete _channel._id; - - // Remove needless properties - delete _channel.user_id; - - // Me - const meId: mongo.ObjectID = me - ? mongo.ObjectID.prototype.isPrototypeOf(me) - ? me as mongo.ObjectID - : typeof me === 'string' - ? new mongo.ObjectID(me) - : (me as IUser)._id - : null; - - if (me) { - //#region Watchしているかどうか - const watch = await Watching.findOne({ - user_id: meId, - channel_id: _channel.id, - deleted_at: { $exists: false } - }); - - _channel.is_watching = watch !== null; - //#endregion - } - - resolve(_channel); -}); diff --git a/src/api/serializers/drive-file.ts b/src/api/serializers/drive-file.ts deleted file mode 100644 index 003e09ee77..0000000000 --- a/src/api/serializers/drive-file.ts +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Module dependencies - */ -import * as mongo from 'mongodb'; -import DriveFile from '../models/drive-file'; -import serializeDriveFolder from './drive-folder'; -import serializeDriveTag from './drive-tag'; -import deepcopy = require('deepcopy'); -import config from '../../conf'; - -/** - * Serialize a drive file - * - * @param {any} file - * @param {any} options? - * @return {Promise} - */ -export default ( - file: any, - options?: { - detail: boolean - } -) => new Promise(async (resolve, reject) => { - const opts = Object.assign({ - detail: false - }, options); - - let _file: any; - - // Populate the file if 'file' is ID - if (mongo.ObjectID.prototype.isPrototypeOf(file)) { - _file = await DriveFile.findOne({ - _id: file - }); - } else if (typeof file === 'string') { - _file = await DriveFile.findOne({ - _id: new mongo.ObjectID(file) - }); - } else { - _file = deepcopy(file); - } - - if (!_file) return reject('invalid file arg.'); - - // rendered target - let _target: any = {}; - - _target.id = _file._id; - _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); - - _target.url = `${config.drive_url}/${_target.id}/${encodeURIComponent(_target.name)}`; - - if (_target.properties == null) _target.properties = {}; - - if (opts.detail) { - if (_target.folder_id) { - // Populate folder - _target.folder = await serializeDriveFolder(_target.folder_id, { - detail: true - }); - } - - if (_target.tags) { - // Populate tags - _target.tags = await _target.tags.map(async (tag: any) => - await serializeDriveTag(tag) - ); - } - } - - resolve(_target); -}); diff --git a/src/api/serializers/drive-folder.ts b/src/api/serializers/drive-folder.ts deleted file mode 100644 index 6ebf454a28..0000000000 --- a/src/api/serializers/drive-folder.ts +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Module dependencies - */ -import * as mongo from 'mongodb'; -import DriveFolder from '../models/drive-folder'; -import DriveFile from '../models/drive-file'; -import deepcopy = require('deepcopy'); - -/** - * Serialize a drive folder - * - * @param {any} folder - * @param {any} options? - * @return {Promise} - */ -const self = ( - folder: any, - options?: { - detail: boolean - } -) => new Promise(async (resolve, reject) => { - const opts = Object.assign({ - detail: false - }, options); - - let _folder: any; - - // Populate the folder if 'folder' is ID - if (mongo.ObjectID.prototype.isPrototypeOf(folder)) { - _folder = await DriveFolder.findOne({ _id: folder }); - } else if (typeof folder === 'string') { - _folder = await DriveFolder.findOne({ _id: new mongo.ObjectID(folder) }); - } else { - _folder = deepcopy(folder); - } - - // Rename _id to id - _folder.id = _folder._id; - delete _folder._id; - - if (opts.detail) { - const childFoldersCount = await DriveFolder.count({ - parent_id: _folder.id - }); - - const childFilesCount = await DriveFile.count({ - 'metadata.folder_id': _folder.id - }); - - _folder.folders_count = childFoldersCount; - _folder.files_count = childFilesCount; - } - - if (opts.detail && _folder.parent_id) { - // Populate parent folder - _folder.parent = await self(_folder.parent_id, { - detail: true - }); - } - - resolve(_folder); -}); - -export default self; diff --git a/src/api/serializers/drive-tag.ts b/src/api/serializers/drive-tag.ts deleted file mode 100644 index 2f152381bd..0000000000 --- a/src/api/serializers/drive-tag.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Module dependencies - */ -import * as mongo from 'mongodb'; -import DriveTag from '../models/drive-tag'; -import deepcopy = require('deepcopy'); - -/** - * Serialize a drive tag - * - * @param {any} tag - * @return {Promise} - */ -const self = ( - tag: any -) => new Promise(async (resolve, reject) => { - let _tag: any; - - // Populate the tag if 'tag' is ID - if (mongo.ObjectID.prototype.isPrototypeOf(tag)) { - _tag = await DriveTag.findOne({ _id: tag }); - } else if (typeof tag === 'string') { - _tag = await DriveTag.findOne({ _id: new mongo.ObjectID(tag) }); - } else { - _tag = deepcopy(tag); - } - - // Rename _id to id - _tag.id = _tag._id; - delete _tag._id; - - resolve(_tag); -}); - -export default self; diff --git a/src/api/serializers/messaging-message.ts b/src/api/serializers/messaging-message.ts deleted file mode 100644 index 4ab95e42a3..0000000000 --- a/src/api/serializers/messaging-message.ts +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Module dependencies - */ -import * as mongo from 'mongodb'; -import deepcopy = require('deepcopy'); -import Message from '../models/messaging-message'; -import serializeUser from './user'; -import serializeDriveFile from './drive-file'; -import parse from '../common/text'; - -/** - * Serialize a message - * - * @param {any} message - * @param {any} me? - * @param {any} options? - * @return {Promise} - */ -export default ( - message: any, - me?: any, - options?: { - populateRecipient: boolean - } -) => new Promise(async (resolve, reject) => { - const opts = options || { - populateRecipient: true - }; - - let _message: any; - - // Populate the message if 'message' is ID - if (mongo.ObjectID.prototype.isPrototypeOf(message)) { - _message = await Message.findOne({ - _id: message - }); - } else if (typeof message === 'string') { - _message = await Message.findOne({ - _id: new mongo.ObjectID(message) - }); - } else { - _message = deepcopy(message); - } - - // Rename _id to id - _message.id = _message._id; - delete _message._id; - - // Parse text - if (_message.text) { - _message.ast = parse(_message.text); - } - - // Populate user - _message.user = await serializeUser(_message.user_id, me); - - if (_message.file) { - // Populate file - _message.file = await serializeDriveFile(_message.file_id); - } - - if (opts.populateRecipient) { - // Populate recipient - _message.recipient = await serializeUser(_message.recipient_id, me); - } - - resolve(_message); -}); diff --git a/src/api/serializers/notification.ts b/src/api/serializers/notification.ts deleted file mode 100644 index ac919dc8b0..0000000000 --- a/src/api/serializers/notification.ts +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Module dependencies - */ -import * as mongo from 'mongodb'; -import Notification from '../models/notification'; -import serializeUser from './user'; -import serializePost from './post'; -import deepcopy = require('deepcopy'); - -/** - * Serialize a notification - * - * @param {any} notification - * @return {Promise} - */ -export default (notification: any) => new Promise(async (resolve, reject) => { - let _notification: any; - - // Populate the notification if 'notification' is ID - if (mongo.ObjectID.prototype.isPrototypeOf(notification)) { - _notification = await Notification.findOne({ - _id: notification - }); - } else if (typeof notification === 'string') { - _notification = await Notification.findOne({ - _id: new mongo.ObjectID(notification) - }); - } else { - _notification = deepcopy(notification); - } - - // Rename _id to id - _notification.id = _notification._id; - delete _notification._id; - - // Rename notifier_id to user_id - _notification.user_id = _notification.notifier_id; - delete _notification.notifier_id; - - const me = _notification.notifiee_id; - delete _notification.notifiee_id; - - // Populate notifier - _notification.user = await serializeUser(_notification.user_id, me); - - switch (_notification.type) { - case 'follow': - // nope - break; - case 'mention': - case 'reply': - case 'repost': - case 'quote': - case 'reaction': - case 'poll_vote': - // Populate post - _notification.post = await serializePost(_notification.post_id, me); - break; - default: - console.error(`Unknown type: ${_notification.type}`); - break; - } - - resolve(_notification); -}); diff --git a/src/api/serializers/post-reaction.ts b/src/api/serializers/post-reaction.ts deleted file mode 100644 index b8807a741c..0000000000 --- a/src/api/serializers/post-reaction.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Module dependencies - */ -import * as mongo from 'mongodb'; -import deepcopy = require('deepcopy'); -import Reaction from '../models/post-reaction'; -import serializeUser from './user'; - -/** - * Serialize a reaction - * - * @param {any} reaction - * @param {any} me? - * @return {Promise} - */ -export default ( - reaction: any, - me?: any -) => new Promise(async (resolve, reject) => { - let _reaction: any; - - // Populate the reaction if 'reaction' is ID - if (mongo.ObjectID.prototype.isPrototypeOf(reaction)) { - _reaction = await Reaction.findOne({ - _id: reaction - }); - } else if (typeof reaction === 'string') { - _reaction = await Reaction.findOne({ - _id: new mongo.ObjectID(reaction) - }); - } else { - _reaction = deepcopy(reaction); - } - - // Rename _id to id - _reaction.id = _reaction._id; - delete _reaction._id; - - // Populate user - _reaction.user = await serializeUser(_reaction.user_id, me); - - resolve(_reaction); -}); diff --git a/src/api/serializers/post.ts b/src/api/serializers/post.ts deleted file mode 100644 index 03fd120772..0000000000 --- a/src/api/serializers/post.ts +++ /dev/null @@ -1,192 +0,0 @@ -/** - * Module dependencies - */ -import * as mongo from 'mongodb'; -import deepcopy = require('deepcopy'); -import { default as Post, IPost } from '../models/post'; -import Reaction from '../models/post-reaction'; -import { IUser } from '../models/user'; -import Vote from '../models/poll-vote'; -import serializeApp from './app'; -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 - * - * @param post target - * @param me? serializee - * @param options? serialize options - * @return response - */ -const self = async ( - post: string | mongo.ObjectID | IPost, - me?: string | mongo.ObjectID | IUser, - options?: { - detail: boolean - } -) => { - const opts = options || { - detail: true, - }; - - // Me - const meId: mongo.ObjectID = me - ? mongo.ObjectID.prototype.isPrototypeOf(me) - ? me as mongo.ObjectID - : typeof me === 'string' - ? new mongo.ObjectID(me) - : (me as IUser)._id - : null; - - let _post: any; - - // Populate the post if 'post' is ID - if (mongo.ObjectID.prototype.isPrototypeOf(post)) { - _post = await Post.findOne({ - _id: post - }); - } else if (typeof post === 'string') { - _post = await Post.findOne({ - _id: new mongo.ObjectID(post) - }); - } else { - _post = deepcopy(post); - } - - if (!_post) throw 'invalid post arg.'; - - const id = _post._id; - - // Rename _id to id - _post.id = _post._id; - delete _post._id; - - delete _post.mentions; - - // Parse text - if (_post.text) { - _post.ast = parse(_post.text); - } - - // Populate user - _post.user = serializeUser(_post.user_id, meId); - - // Populate app - if (_post.app_id) { - _post.app = serializeApp(_post.app_id); - } - - // Populate channel - if (_post.channel_id) { - _post.channel = serializeChannel(_post.channel_id); - } - - // Populate media - if (_post.media_ids) { - _post.media = Promise.all(_post.media_ids.map(fileId => - serializeDriveFile(fileId) - )); - } - - // When requested a detailed post data - if (opts.detail) { - // Get previous post info - _post.prev = (async () => { - const prev = await 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 - _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 = self(_post.reply_id, meId, { - detail: false - }); - } - - if (_post.repost_id) { - // Populate repost - _post.repost = self(_post.repost_id, meId, { - detail: _post.text == null - }); - } - - // Poll - if (meId && _post.poll) { - _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) { - _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); - - return _post; -}; - -export default self; diff --git a/src/api/serializers/signin.ts b/src/api/serializers/signin.ts deleted file mode 100644 index 4068067678..0000000000 --- a/src/api/serializers/signin.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Module dependencies - */ -import deepcopy = require('deepcopy'); - -/** - * Serialize a signin record - * - * @param {any} record - * @return {Promise} - */ -export default ( - record: any -) => new Promise(async (resolve, reject) => { - - const _record = deepcopy(record); - - // Rename _id to id - _record.id = _record._id; - delete _record._id; - - resolve(_record); -}); diff --git a/src/api/serializers/user.ts b/src/api/serializers/user.ts deleted file mode 100644 index ac157097a8..0000000000 --- a/src/api/serializers/user.ts +++ /dev/null @@ -1,190 +0,0 @@ -/** - * Module dependencies - */ -import * as mongo from 'mongodb'; -import deepcopy = require('deepcopy'); -import { default as User, IUser } from '../models/user'; -import serializePost from './post'; -import Following from '../models/following'; -import Mute from '../models/mute'; -import getFriends from '../common/get-friends'; -import config from '../../conf'; -import rap from '@prezzemolo/rap'; - -/** - * Serialize a user - * - * @param user target - * @param me? serializee - * @param options? serialize options - * @return response - */ -export default ( - user: string | mongo.ObjectID | IUser, - me?: string | mongo.ObjectID | IUser, - options?: { - detail?: boolean, - includeSecrets?: boolean - } -) => new Promise(async (resolve, reject) => { - - const opts = Object.assign({ - detail: false, - includeSecrets: false - }, options); - - let _user: any; - - const fields = opts.detail ? { - settings: false - } : { - settings: false, - client_settings: false, - profile: false, - keywords: false, - domains: false - }; - - // Populate the user if 'user' is ID - if (mongo.ObjectID.prototype.isPrototypeOf(user)) { - _user = await User.findOne({ - _id: user - }, { fields }); - } else if (typeof user === 'string') { - _user = await User.findOne({ - _id: new mongo.ObjectID(user) - }, { fields }); - } else { - _user = deepcopy(user); - } - - if (!_user) return reject('invalid user arg.'); - - // Me - const meId: mongo.ObjectID = me - ? mongo.ObjectID.prototype.isPrototypeOf(me) - ? me as mongo.ObjectID - : typeof me === 'string' - ? new mongo.ObjectID(me) - : (me as IUser)._id - : null; - - // Rename _id to id - _user.id = _user._id; - delete _user._id; - - // Remove needless properties - delete _user.latest_post; - - // Remove private properties - delete _user.password; - delete _user.token; - delete _user.two_factor_temp_secret; - delete _user.two_factor_secret; - delete _user.username_lower; - if (_user.twitter) { - delete _user.twitter.access_token; - delete _user.twitter.access_token_secret; - } - delete _user.line; - - // Visible via only the official client - if (!opts.includeSecrets) { - delete _user.email; - delete _user.client_settings; - } - - if (!opts.detail) { - delete _user.two_factor_enabled; - } - - _user.avatar_url = _user.avatar_id != null - ? `${config.drive_url}/${_user.avatar_id}` - : `${config.drive_url}/default-avatar.jpg`; - - _user.banner_url = _user.banner_id != null - ? `${config.drive_url}/${_user.banner_id}` - : null; - - if (!meId || !meId.equals(_user.id) || !opts.detail) { - delete _user.avatar_id; - delete _user.banner_id; - - delete _user.drive_capacity; - } - - if (meId && !meId.equals(_user.id)) { - // Whether the user is following - _user.is_following = (async () => { - const follow = await Following.findOne({ - follower_id: meId, - followee_id: _user.id, - deleted_at: { $exists: false } - }); - return follow !== null; - })(); - - // Whether the user is followed - _user.is_followed = (async () => { - const follow2 = await Following.findOne({ - follower_id: _user.id, - followee_id: meId, - deleted_at: { $exists: false } - }); - return follow2 !== null; - })(); - - // Whether the user is muted - _user.is_muted = (async () => { - const mute = await Mute.findOne({ - muter_id: meId, - mutee_id: _user.id, - deleted_at: { $exists: false } - }); - return mute !== null; - })(); - } - - if (opts.detail) { - if (_user.pinned_post_id) { - // Populate pinned post - _user.pinned_post = serializePost(_user.pinned_post_id, meId, { - detail: true - }); - } - - if (meId && !meId.equals(_user.id)) { - const myFollowingIds = await getFriends(meId); - - // Get following you know count - _user.following_you_know_count = Following.count({ - followee_id: { $in: myFollowingIds }, - follower_id: _user.id, - deleted_at: { $exists: false } - }); - - // Get followers you know count - _user.followers_you_know_count = Following.count({ - followee_id: _user.id, - follower_id: { $in: myFollowingIds }, - deleted_at: { $exists: false } - }); - } - } - - // resolve promises in _user object - _user = await rap(_user); - - resolve(_user); -}); -/* -function img(url) { - return { - thumbnail: { - large: `${url}`, - medium: '', - small: '' - } - }; -} -*/ -- cgit v1.2.3-freya From 718060dc855e09f270b8e19c089ed3c3743665e0 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 2 Feb 2018 08:21:30 +0900 Subject: wip --- src/api/common/add-file-to-drive.ts | 4 ++-- src/api/common/notify.ts | 6 +++--- src/api/endpoints/app/create.ts | 4 ++-- src/api/endpoints/app/show.ts | 4 ++-- src/api/endpoints/auth/session/show.ts | 4 ++-- src/api/endpoints/auth/session/userkey.ts | 4 ++-- src/api/endpoints/channels.ts | 4 ++-- src/api/endpoints/channels/create.ts | 4 ++-- src/api/endpoints/channels/posts.ts | 4 ++-- src/api/endpoints/channels/show.ts | 4 ++-- src/api/endpoints/drive/files.ts | 4 ++-- src/api/endpoints/drive/files/create.ts | 4 ++-- src/api/endpoints/drive/files/find.ts | 4 ++-- src/api/endpoints/drive/files/show.ts | 4 ++-- src/api/endpoints/drive/files/update.ts | 4 ++-- src/api/endpoints/drive/files/upload_from_url.ts | 4 ++-- src/api/endpoints/drive/folders.ts | 4 ++-- src/api/endpoints/drive/folders/create.ts | 4 ++-- src/api/endpoints/drive/folders/find.ts | 4 ++-- src/api/endpoints/drive/folders/show.ts | 4 ++-- src/api/endpoints/drive/folders/update.ts | 4 ++-- src/api/endpoints/drive/stream.ts | 4 ++-- src/api/endpoints/i.ts | 4 ++-- src/api/endpoints/i/authorized_apps.ts | 4 ++-- src/api/endpoints/i/favorites.ts | 4 ++-- src/api/endpoints/i/notifications.ts | 4 ++-- src/api/endpoints/i/pin.ts | 4 ++-- src/api/endpoints/i/signin_history.ts | 4 ++-- src/api/endpoints/i/update.ts | 4 ++-- src/api/endpoints/messaging/history.ts | 4 ++-- src/api/endpoints/messaging/messages.ts | 4 ++-- src/api/endpoints/messaging/messages/create.ts | 4 ++-- src/api/endpoints/mute/list.ts | 4 ++-- src/api/endpoints/my/apps.ts | 4 ++-- src/api/endpoints/posts.ts | 4 ++-- src/api/endpoints/posts/context.ts | 4 ++-- src/api/endpoints/posts/create.ts | 4 ++-- src/api/endpoints/posts/mentions.ts | 4 ++-- src/api/endpoints/posts/polls/recommendation.ts | 4 ++-- src/api/endpoints/posts/reactions.ts | 4 ++-- src/api/endpoints/posts/replies.ts | 4 ++-- src/api/endpoints/posts/reposts.ts | 4 ++-- src/api/endpoints/posts/search.ts | 4 ++-- src/api/endpoints/posts/show.ts | 4 ++-- src/api/endpoints/posts/timeline.ts | 4 ++-- src/api/endpoints/posts/trend.ts | 4 ++-- src/api/endpoints/users.ts | 4 ++-- src/api/endpoints/users/followers.ts | 4 ++-- src/api/endpoints/users/following.ts | 4 ++-- src/api/endpoints/users/get_frequently_replied_users.ts | 4 ++-- src/api/endpoints/users/posts.ts | 4 ++-- src/api/endpoints/users/recommendation.ts | 4 ++-- src/api/endpoints/users/search.ts | 4 ++-- src/api/endpoints/users/search_by_username.ts | 4 ++-- src/api/endpoints/users/show.ts | 4 ++-- src/api/models/drive-file.ts | 10 ++++++++-- src/api/models/drive-folder.ts | 2 ++ src/api/models/messaging-message.ts | 5 +++++ src/api/private/signin.ts | 4 ++-- src/api/private/signup.ts | 4 ++-- src/api/service/twitter.ts | 4 ++-- 61 files changed, 132 insertions(+), 119 deletions(-) (limited to 'src/api/models') diff --git a/src/api/common/add-file-to-drive.ts b/src/api/common/add-file-to-drive.ts index 23cbc44e61..1ee455c092 100644 --- a/src/api/common/add-file-to-drive.ts +++ b/src/api/common/add-file-to-drive.ts @@ -12,7 +12,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 { pack } from '../models/drive-file'; import event, { publishDriveStream } from '../event'; import config from '../../conf'; @@ -282,7 +282,7 @@ export default (user: any, file: string | stream.Readable, ...args) => new Promi log(`drive file has been created ${file._id}`); resolve(file); - serialize(file).then(serializedFile => { + pack(file).then(serializedFile => { // Publish drive_file_created event event(user._id, 'drive_file_created', serializedFile); publishDriveStream(user._id, 'file_created', serializedFile); diff --git a/src/api/common/notify.ts b/src/api/common/notify.ts index 2b79416a30..ae5669b84c 100644 --- a/src/api/common/notify.ts +++ b/src/api/common/notify.ts @@ -2,7 +2,7 @@ import * as mongo from 'mongodb'; import Notification from '../models/notification'; import Mute from '../models/mute'; import event from '../event'; -import serialize from '../serializers/notification'; +import { pack } from '../models/notification'; export default ( notifiee: mongo.ObjectID, @@ -27,7 +27,7 @@ export default ( // Publish notification event event(notifiee, 'notification', - await serialize(notification)); + await pack(notification)); // 3秒経っても(今回作成した)通知が既読にならなかったら「未読の通知がありますよ」イベントを発行する setTimeout(async () => { @@ -44,7 +44,7 @@ export default ( } //#endregion - event(notifiee, 'unread_notification', await serialize(notification)); + event(notifiee, 'unread_notification', await pack(notification)); } }, 3000); }); diff --git a/src/api/endpoints/app/create.ts b/src/api/endpoints/app/create.ts index ca684de02d..320163ebd9 100644 --- a/src/api/endpoints/app/create.ts +++ b/src/api/endpoints/app/create.ts @@ -5,7 +5,7 @@ import rndstr from 'rndstr'; import $ from 'cafy'; import App from '../../models/app'; import { isValidNameId } from '../../models/app'; -import serialize from '../../serializers/app'; +import { pack } from '../../models/app'; /** * @swagger @@ -106,5 +106,5 @@ module.exports = async (params, user) => new Promise(async (res, rej) => { }); // Response - res(await serialize(app)); + res(await pack(app)); }); diff --git a/src/api/endpoints/app/show.ts b/src/api/endpoints/app/show.ts index 054aab8596..a3ef24717d 100644 --- a/src/api/endpoints/app/show.ts +++ b/src/api/endpoints/app/show.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import App from '../../models/app'; -import serialize from '../../serializers/app'; +import { pack } from '../../models/app'; /** * @swagger @@ -67,7 +67,7 @@ module.exports = (params, user, _, isSecure) => new Promise(async (res, rej) => } // Send response - res(await serialize(app, user, { + res(await pack(app, user, { includeSecret: isSecure && app.user_id.equals(user._id) })); }); diff --git a/src/api/endpoints/auth/session/show.ts b/src/api/endpoints/auth/session/show.ts index ede8a67634..1fe3b873fe 100644 --- a/src/api/endpoints/auth/session/show.ts +++ b/src/api/endpoints/auth/session/show.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import AuthSess from '../../../models/auth-session'; -import serialize from '../../../serializers/auth-session'; +import { pack } from '../../../models/auth-session'; /** * @swagger @@ -67,5 +67,5 @@ module.exports = (params, user) => new Promise(async (res, rej) => { } // Response - res(await serialize(session, user)); + res(await pack(session, user)); }); diff --git a/src/api/endpoints/auth/session/userkey.ts b/src/api/endpoints/auth/session/userkey.ts index afd3250b04..fc989bf8c2 100644 --- a/src/api/endpoints/auth/session/userkey.ts +++ b/src/api/endpoints/auth/session/userkey.ts @@ -5,7 +5,7 @@ import $ from 'cafy'; import App from '../../../models/app'; import AuthSess from '../../../models/auth-session'; import AccessToken from '../../../models/access-token'; -import serialize from '../../../serializers/user'; +import { pack } from '../../../models/user'; /** * @swagger @@ -102,7 +102,7 @@ module.exports = (params) => new Promise(async (res, rej) => { // Response res({ access_token: accessToken.token, - user: await serialize(session.user_id, null, { + user: await pack(session.user_id, null, { detail: true }) }); diff --git a/src/api/endpoints/channels.ts b/src/api/endpoints/channels.ts index 14817d9bd8..92dcee83db 100644 --- a/src/api/endpoints/channels.ts +++ b/src/api/endpoints/channels.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import Channel from '../models/channel'; -import serialize from '../serializers/channel'; +import { pack } from '../models/channel'; /** * Get all channels @@ -55,5 +55,5 @@ module.exports = (params, me) => new Promise(async (res, rej) => { // Serialize res(await Promise.all(channels.map(async channel => - await serialize(channel, me)))); + await pack(channel, me)))); }); diff --git a/src/api/endpoints/channels/create.ts b/src/api/endpoints/channels/create.ts index a8d7c29dc1..695b4515b3 100644 --- a/src/api/endpoints/channels/create.ts +++ b/src/api/endpoints/channels/create.ts @@ -4,7 +4,7 @@ import $ from 'cafy'; import Channel from '../../models/channel'; import Watching from '../../models/channel-watching'; -import serialize from '../../serializers/channel'; +import { pack } from '../../models/channel'; /** * Create a channel @@ -28,7 +28,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => { }); // Response - res(await serialize(channel)); + res(await pack(channel)); // Create Watching await Watching.insert({ diff --git a/src/api/endpoints/channels/posts.ts b/src/api/endpoints/channels/posts.ts index 9c2d607edb..3feee51f76 100644 --- a/src/api/endpoints/channels/posts.ts +++ b/src/api/endpoints/channels/posts.ts @@ -4,7 +4,7 @@ import $ from 'cafy'; import { default as Channel, IChannel } from '../../models/channel'; import Post from '../../models/post'; -import serialize from '../../serializers/post'; +import { pack } from '../../models/post'; /** * Show a posts of a channel @@ -74,6 +74,6 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Serialize res(await Promise.all(posts.map(async (post) => - await serialize(post, user) + await pack(post, user) ))); }); diff --git a/src/api/endpoints/channels/show.ts b/src/api/endpoints/channels/show.ts index 8861e54594..89c48379a4 100644 --- a/src/api/endpoints/channels/show.ts +++ b/src/api/endpoints/channels/show.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import { default as Channel, IChannel } from '../../models/channel'; -import serialize from '../../serializers/channel'; +import { pack } from '../../models/channel'; /** * Show a channel @@ -27,5 +27,5 @@ module.exports = (params, user) => new Promise(async (res, rej) => { } // Serialize - res(await serialize(channel, user)); + res(await pack(channel, user)); }); diff --git a/src/api/endpoints/drive/files.ts b/src/api/endpoints/drive/files.ts index 3d5f81339a..3bd80e7282 100644 --- a/src/api/endpoints/drive/files.ts +++ b/src/api/endpoints/drive/files.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import DriveFile from '../../models/drive-file'; -import serialize from '../../serializers/drive-file'; +import { pack } from '../../models/drive-file'; /** * Get drive files @@ -69,6 +69,6 @@ module.exports = async (params, user, app) => { }); // Serialize - const _files = await Promise.all(files.map(file => serialize(file))); + const _files = await Promise.all(files.map(file => pack(file))); return _files; }; diff --git a/src/api/endpoints/drive/files/create.ts b/src/api/endpoints/drive/files/create.ts index 437348a1ef..6fa76d7e95 100644 --- a/src/api/endpoints/drive/files/create.ts +++ b/src/api/endpoints/drive/files/create.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import { validateFileName } from '../../../models/drive-file'; -import serialize from '../../../serializers/drive-file'; +import { pack } from '../../../models/drive-file'; import create from '../../../common/add-file-to-drive'; /** @@ -43,7 +43,7 @@ module.exports = async (file, params, user): Promise => { const driveFile = await create(user, file.path, name, null, folderId); // Serialize - return serialize(driveFile); + return pack(driveFile); } catch (e) { console.error(e); diff --git a/src/api/endpoints/drive/files/find.ts b/src/api/endpoints/drive/files/find.ts index a1cdf1643e..571aba81f4 100644 --- a/src/api/endpoints/drive/files/find.ts +++ b/src/api/endpoints/drive/files/find.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import DriveFile from '../../../models/drive-file'; -import serialize from '../../../serializers/drive-file'; +import { pack } from '../../../models/drive-file'; /** * Find a file(s) @@ -31,5 +31,5 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Serialize res(await Promise.all(files.map(async file => - await serialize(file)))); + await pack(file)))); }); diff --git a/src/api/endpoints/drive/files/show.ts b/src/api/endpoints/drive/files/show.ts index 3c7cf774f9..00f69f1415 100644 --- a/src/api/endpoints/drive/files/show.ts +++ b/src/api/endpoints/drive/files/show.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import DriveFile from '../../../models/drive-file'; -import serialize from '../../../serializers/drive-file'; +import { pack } from '../../../models/drive-file'; /** * Show a file @@ -29,7 +29,7 @@ module.exports = async (params, user) => { } // Serialize - const _file = await serialize(file, { + const _file = await pack(file, { detail: true }); diff --git a/src/api/endpoints/drive/files/update.ts b/src/api/endpoints/drive/files/update.ts index f39a420d6e..9ef8215b1a 100644 --- a/src/api/endpoints/drive/files/update.ts +++ b/src/api/endpoints/drive/files/update.ts @@ -5,7 +5,7 @@ import $ from 'cafy'; 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 { pack } from '../../../models/drive-file'; import { publishDriveStream } from '../../../event'; /** @@ -67,7 +67,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { }); // Serialize - const fileObj = await serialize(file); + const fileObj = await pack(file); // Response res(fileObj); diff --git a/src/api/endpoints/drive/files/upload_from_url.ts b/src/api/endpoints/drive/files/upload_from_url.ts index 519e0bdf65..f0398bfc5d 100644 --- a/src/api/endpoints/drive/files/upload_from_url.ts +++ b/src/api/endpoints/drive/files/upload_from_url.ts @@ -4,7 +4,7 @@ import * as URL from 'url'; import $ from 'cafy'; import { validateFileName } from '../../../models/drive-file'; -import serialize from '../../../serializers/drive-file'; +import { pack } from '../../../models/drive-file'; import create from '../../../common/add-file-to-drive'; import * as debug from 'debug'; import * as tmp from 'tmp'; @@ -63,5 +63,5 @@ module.exports = async (params, user): Promise => { if (e) log(e.stack); }); - return serialize(driveFile); + return pack(driveFile); }; diff --git a/src/api/endpoints/drive/folders.ts b/src/api/endpoints/drive/folders.ts index 7944e2c6a6..e650fb74aa 100644 --- a/src/api/endpoints/drive/folders.ts +++ b/src/api/endpoints/drive/folders.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import DriveFolder from '../../models/drive-folder'; -import serialize from '../../serializers/drive-folder'; +import { pack } from '../../models/drive-folder'; /** * Get drive folders @@ -63,5 +63,5 @@ module.exports = (params, user, app) => new Promise(async (res, rej) => { // Serialize res(await Promise.all(folders.map(async folder => - await serialize(folder)))); + await pack(folder)))); }); diff --git a/src/api/endpoints/drive/folders/create.ts b/src/api/endpoints/drive/folders/create.ts index be847b2153..1953c09ee0 100644 --- a/src/api/endpoints/drive/folders/create.ts +++ b/src/api/endpoints/drive/folders/create.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-folder'; +import { pack } from '../../../models/drive-folder'; import { publishDriveStream } from '../../../event'; /** @@ -47,7 +47,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { }); // Serialize - const folderObj = await serialize(folder); + const folderObj = await pack(folder); // Response res(folderObj); diff --git a/src/api/endpoints/drive/folders/find.ts b/src/api/endpoints/drive/folders/find.ts index a5eb8e015d..caad45d740 100644 --- a/src/api/endpoints/drive/folders/find.ts +++ b/src/api/endpoints/drive/folders/find.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import DriveFolder from '../../../models/drive-folder'; -import serialize from '../../../serializers/drive-folder'; +import { pack } from '../../../models/drive-folder'; /** * Find a folder(s) @@ -30,5 +30,5 @@ module.exports = (params, user) => new Promise(async (res, rej) => { }); // Serialize - res(await Promise.all(folders.map(folder => serialize(folder)))); + res(await Promise.all(folders.map(folder => pack(folder)))); }); diff --git a/src/api/endpoints/drive/folders/show.ts b/src/api/endpoints/drive/folders/show.ts index 9b1c04ca3c..fd3061ca54 100644 --- a/src/api/endpoints/drive/folders/show.ts +++ b/src/api/endpoints/drive/folders/show.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import DriveFolder from '../../../models/drive-folder'; -import serialize from '../../../serializers/drive-folder'; +import { pack } from '../../../models/drive-folder'; /** * Show a folder @@ -29,7 +29,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { } // Serialize - res(await serialize(folder, { + res(await pack(folder, { detail: true })); }); diff --git a/src/api/endpoints/drive/folders/update.ts b/src/api/endpoints/drive/folders/update.ts index ff673402ab..8f50a9d009 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-folder'; +import { pack } from '../../../models/drive-folder'; import { publishDriveStream } from '../../../event'; /** @@ -91,7 +91,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { }); // Serialize - const folderObj = await serialize(folder); + const folderObj = await pack(folder); // Response res(folderObj); diff --git a/src/api/endpoints/drive/stream.ts b/src/api/endpoints/drive/stream.ts index 5b0eb0a0d8..3527d70500 100644 --- a/src/api/endpoints/drive/stream.ts +++ b/src/api/endpoints/drive/stream.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import DriveFile from '../../models/drive-file'; -import serialize from '../../serializers/drive-file'; +import { pack } from '../../models/drive-file'; /** * Get drive stream @@ -64,5 +64,5 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Serialize res(await Promise.all(files.map(async file => - await serialize(file)))); + await pack(file)))); }); diff --git a/src/api/endpoints/i.ts b/src/api/endpoints/i.ts index ae75f11d54..1b6c1e58de 100644 --- a/src/api/endpoints/i.ts +++ b/src/api/endpoints/i.ts @@ -2,7 +2,7 @@ * Module dependencies */ import User from '../models/user'; -import serialize from '../serializers/user'; +import { pack } from '../models/user'; /** * Show myself @@ -15,7 +15,7 @@ import serialize from '../serializers/user'; */ module.exports = (params, user, _, isSecure) => new Promise(async (res, rej) => { // Serialize - res(await serialize(user, user, { + res(await pack(user, user, { detail: true, includeSecrets: isSecure })); diff --git a/src/api/endpoints/i/authorized_apps.ts b/src/api/endpoints/i/authorized_apps.ts index 807ca5b1e7..40ce7a68c8 100644 --- a/src/api/endpoints/i/authorized_apps.ts +++ b/src/api/endpoints/i/authorized_apps.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import AccessToken from '../../models/access-token'; -import serialize from '../../serializers/app'; +import { pack } from '../../models/app'; /** * Get authorized apps of my account @@ -39,5 +39,5 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Serialize res(await Promise.all(tokens.map(async token => - await serialize(token.app_id)))); + await pack(token.app_id)))); }); diff --git a/src/api/endpoints/i/favorites.ts b/src/api/endpoints/i/favorites.ts index a66eaa7546..eb464cf0f0 100644 --- a/src/api/endpoints/i/favorites.ts +++ b/src/api/endpoints/i/favorites.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import Favorite from '../../models/favorite'; -import serialize from '../../serializers/post'; +import { pack } from '../../models/post'; /** * Get followers of a user @@ -39,6 +39,6 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Serialize res(await Promise.all(favorites.map(async favorite => - await serialize(favorite.post) + await pack(favorite.post) ))); }); diff --git a/src/api/endpoints/i/notifications.ts b/src/api/endpoints/i/notifications.ts index fb9be7f61b..688039a0dd 100644 --- a/src/api/endpoints/i/notifications.ts +++ b/src/api/endpoints/i/notifications.ts @@ -4,7 +4,7 @@ import $ from 'cafy'; import Notification from '../../models/notification'; import Mute from '../../models/mute'; -import serialize from '../../serializers/notification'; +import { pack } from '../../models/notification'; import getFriends from '../../common/get-friends'; import read from '../../common/read-notification'; @@ -101,7 +101,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Serialize res(await Promise.all(notifications.map(async notification => - await serialize(notification)))); + await pack(notification)))); // Mark as read all if (notifications.length > 0 && markAsRead) { diff --git a/src/api/endpoints/i/pin.ts b/src/api/endpoints/i/pin.ts index a94950d22b..ff546fc2bd 100644 --- a/src/api/endpoints/i/pin.ts +++ b/src/api/endpoints/i/pin.ts @@ -4,7 +4,7 @@ import $ from 'cafy'; import User from '../../models/user'; import Post from '../../models/post'; -import serialize from '../../serializers/user'; +import { pack } from '../../models/user'; /** * Pin post @@ -35,7 +35,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => { }); // Serialize - const iObj = await serialize(user, user, { + const iObj = await pack(user, user, { detail: true }); diff --git a/src/api/endpoints/i/signin_history.ts b/src/api/endpoints/i/signin_history.ts index e38bfa4d98..3ab59b694d 100644 --- a/src/api/endpoints/i/signin_history.ts +++ b/src/api/endpoints/i/signin_history.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import Signin from '../../models/signin'; -import serialize from '../../serializers/signin'; +import { pack } from '../../models/signin'; /** * Get signin history of my account @@ -58,5 +58,5 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Serialize res(await Promise.all(history.map(async record => - await serialize(record)))); + await pack(record)))); }); diff --git a/src/api/endpoints/i/update.ts b/src/api/endpoints/i/update.ts index c484c51a96..a138832e54 100644 --- a/src/api/endpoints/i/update.ts +++ b/src/api/endpoints/i/update.ts @@ -4,7 +4,7 @@ import $ from 'cafy'; import User from '../../models/user'; import { isValidName, isValidDescription, isValidLocation, isValidBirthday } from '../../models/user'; -import serialize from '../../serializers/user'; +import { pack } from '../../models/user'; import event from '../../event'; import config from '../../../conf'; @@ -65,7 +65,7 @@ module.exports = async (params, user, _, isSecure) => new Promise(async (res, re }); // Serialize - const iObj = await serialize(user, user, { + const iObj = await pack(user, user, { detail: true, includeSecrets: isSecure }); diff --git a/src/api/endpoints/messaging/history.ts b/src/api/endpoints/messaging/history.ts index f14740dff5..1683ca7a89 100644 --- a/src/api/endpoints/messaging/history.ts +++ b/src/api/endpoints/messaging/history.ts @@ -4,7 +4,7 @@ import $ from 'cafy'; import History from '../../models/messaging-history'; import Mute from '../../models/mute'; -import serialize from '../../serializers/messaging-message'; +import { pack } from '../../models/messaging-message'; /** * Show messaging history @@ -39,5 +39,5 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Serialize res(await Promise.all(history.map(async h => - await serialize(h.message, user)))); + await pack(h.message, user)))); }); diff --git a/src/api/endpoints/messaging/messages.ts b/src/api/endpoints/messaging/messages.ts index 3d3c6950a1..67ba5e9d6d 100644 --- a/src/api/endpoints/messaging/messages.ts +++ b/src/api/endpoints/messaging/messages.ts @@ -4,7 +4,7 @@ import $ from 'cafy'; import Message from '../../models/messaging-message'; import User from '../../models/user'; -import serialize from '../../serializers/messaging-message'; +import { pack } from '../../models/messaging-message'; import read from '../../common/read-messaging-message'; /** @@ -87,7 +87,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Serialize res(await Promise.all(messages.map(async message => - await serialize(message, user, { + await pack(message, user, { populateRecipient: false })))); diff --git a/src/api/endpoints/messaging/messages/create.ts b/src/api/endpoints/messaging/messages/create.ts index 4e9d10197c..1b8a5f59e6 100644 --- a/src/api/endpoints/messaging/messages/create.ts +++ b/src/api/endpoints/messaging/messages/create.ts @@ -8,7 +8,7 @@ import History from '../../../models/messaging-history'; import User from '../../../models/user'; import Mute from '../../../models/mute'; import DriveFile from '../../../models/drive-file'; -import serialize from '../../../serializers/messaging-message'; +import { pack } from '../../../models/messaging-message'; import publishUserStream from '../../../event'; import { publishMessagingStream, publishMessagingIndexStream, pushSw } from '../../../event'; import config from '../../../../conf'; @@ -79,7 +79,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { }); // Serialize - const messageObj = await serialize(message); + const messageObj = await pack(message); // Reponse res(messageObj); diff --git a/src/api/endpoints/mute/list.ts b/src/api/endpoints/mute/list.ts index 740e19f0bb..19e3b157e6 100644 --- a/src/api/endpoints/mute/list.ts +++ b/src/api/endpoints/mute/list.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import Mute from '../../models/mute'; -import serialize from '../../serializers/user'; +import { pack } from '../../models/user'; import getFriends from '../../common/get-friends'; /** @@ -63,7 +63,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => { // Serialize const users = await Promise.all(mutes.map(async m => - await serialize(m.mutee_id, me, { detail: true }))); + await pack(m.mutee_id, me, { detail: true }))); // Response res({ diff --git a/src/api/endpoints/my/apps.ts b/src/api/endpoints/my/apps.ts index eb9c758768..fe583db86a 100644 --- a/src/api/endpoints/my/apps.ts +++ b/src/api/endpoints/my/apps.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import App from '../../models/app'; -import serialize from '../../serializers/app'; +import { pack } from '../../models/app'; /** * Get my apps @@ -37,5 +37,5 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Reply res(await Promise.all(apps.map(async app => - await serialize(app)))); + await pack(app)))); }); diff --git a/src/api/endpoints/posts.ts b/src/api/endpoints/posts.ts index db166cd67a..d10c6ab408 100644 --- a/src/api/endpoints/posts.ts +++ b/src/api/endpoints/posts.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import Post from '../models/post'; -import serialize from '../serializers/post'; +import { pack } from '../models/post'; /** * Lists all posts @@ -85,5 +85,5 @@ module.exports = (params) => new Promise(async (res, rej) => { }); // Serialize - res(await Promise.all(posts.map(async post => await serialize(post)))); + res(await Promise.all(posts.map(async post => await pack(post)))); }); diff --git a/src/api/endpoints/posts/context.ts b/src/api/endpoints/posts/context.ts index bad59a6bee..3051e7af17 100644 --- a/src/api/endpoints/posts/context.ts +++ b/src/api/endpoints/posts/context.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import Post from '../../models/post'; -import serialize from '../../serializers/post'; +import { pack } from '../../models/post'; /** * Show a context of a post @@ -60,5 +60,5 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Serialize res(await Promise.all(context.map(async post => - await serialize(post, user)))); + await pack(post, user)))); }); diff --git a/src/api/endpoints/posts/create.ts b/src/api/endpoints/posts/create.ts index a1d05c67c6..0fa52221f9 100644 --- a/src/api/endpoints/posts/create.ts +++ b/src/api/endpoints/posts/create.ts @@ -12,7 +12,7 @@ import Mute from '../../models/mute'; import DriveFile from '../../models/drive-file'; import Watching from '../../models/post-watching'; import ChannelWatching from '../../models/channel-watching'; -import serialize from '../../serializers/post'; +import { pack } from '../../models/post'; import notify from '../../common/notify'; import watch from '../../common/watch-post'; import event, { pushSw, publishChannelStream } from '../../event'; @@ -224,7 +224,7 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { }); // Serialize - const postObj = await serialize(post); + const postObj = await pack(post); // Reponse res({ diff --git a/src/api/endpoints/posts/mentions.ts b/src/api/endpoints/posts/mentions.ts index 3bb4ec3fa0..7127db0ad1 100644 --- a/src/api/endpoints/posts/mentions.ts +++ b/src/api/endpoints/posts/mentions.ts @@ -4,7 +4,7 @@ import $ from 'cafy'; import Post from '../../models/post'; import getFriends from '../../common/get-friends'; -import serialize from '../../serializers/post'; +import { pack } from '../../models/post'; /** * Get mentions of myself @@ -73,6 +73,6 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Serialize res(await Promise.all(mentions.map(async mention => - await serialize(mention, user) + await pack(mention, user) ))); }); diff --git a/src/api/endpoints/posts/polls/recommendation.ts b/src/api/endpoints/posts/polls/recommendation.ts index 9c92d6cac4..5ccb754496 100644 --- a/src/api/endpoints/posts/polls/recommendation.ts +++ b/src/api/endpoints/posts/polls/recommendation.ts @@ -4,7 +4,7 @@ import $ from 'cafy'; import Vote from '../../../models/poll-vote'; import Post from '../../../models/post'; -import serialize from '../../../serializers/post'; +import { pack } from '../../../models/post'; /** * Get recommended polls @@ -56,5 +56,5 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Serialize res(await Promise.all(posts.map(async post => - await serialize(post, user, { detail: true })))); + await pack(post, user, { detail: true })))); }); diff --git a/src/api/endpoints/posts/reactions.ts b/src/api/endpoints/posts/reactions.ts index eab5d9b258..f60334df8a 100644 --- a/src/api/endpoints/posts/reactions.ts +++ b/src/api/endpoints/posts/reactions.ts @@ -4,7 +4,7 @@ import $ from 'cafy'; import Post from '../../models/post'; import Reaction from '../../models/post-reaction'; -import serialize from '../../serializers/post-reaction'; +import { pack } from '../../models/post-reaction'; /** * Show reactions of a post @@ -54,5 +54,5 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Serialize res(await Promise.all(reactions.map(async reaction => - await serialize(reaction, user)))); + await pack(reaction, user)))); }); diff --git a/src/api/endpoints/posts/replies.ts b/src/api/endpoints/posts/replies.ts index 3fd6a46769..1442b8a4c5 100644 --- a/src/api/endpoints/posts/replies.ts +++ b/src/api/endpoints/posts/replies.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import Post from '../../models/post'; -import serialize from '../../serializers/post'; +import { pack } from '../../models/post'; /** * Show a replies of a post @@ -50,5 +50,5 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Serialize res(await Promise.all(replies.map(async post => - await serialize(post, user)))); + await pack(post, user)))); }); diff --git a/src/api/endpoints/posts/reposts.ts b/src/api/endpoints/posts/reposts.ts index bcc6163a11..0fbb0687b9 100644 --- a/src/api/endpoints/posts/reposts.ts +++ b/src/api/endpoints/posts/reposts.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import Post from '../../models/post'; -import serialize from '../../serializers/post'; +import { pack } from '../../models/post'; /** * Show a reposts of a post @@ -70,5 +70,5 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Serialize res(await Promise.all(reposts.map(async post => - await serialize(post, user)))); + await pack(post, user)))); }); diff --git a/src/api/endpoints/posts/search.ts b/src/api/endpoints/posts/search.ts index 31c9a8d3c8..6e26f55390 100644 --- a/src/api/endpoints/posts/search.ts +++ b/src/api/endpoints/posts/search.ts @@ -7,7 +7,7 @@ import Post from '../../models/post'; import User from '../../models/user'; import Mute from '../../models/mute'; import getFriends from '../../common/get-friends'; -import serialize from '../../serializers/post'; +import { pack } from '../../models/post'; /** * Search a post @@ -351,5 +351,5 @@ async function search( // Serialize res(await Promise.all(posts.map(async post => - await serialize(post, me)))); + await pack(post, me)))); } diff --git a/src/api/endpoints/posts/show.ts b/src/api/endpoints/posts/show.ts index 5bfe4f6605..c312449710 100644 --- a/src/api/endpoints/posts/show.ts +++ b/src/api/endpoints/posts/show.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import Post from '../../models/post'; -import serialize from '../../serializers/post'; +import { pack } from '../../models/post'; /** * Show a post @@ -27,7 +27,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { } // Serialize - res(await serialize(post, user, { + res(await pack(post, user, { detail: true })); }); diff --git a/src/api/endpoints/posts/timeline.ts b/src/api/endpoints/posts/timeline.ts index da7ffd0c14..c41cfdb8bd 100644 --- a/src/api/endpoints/posts/timeline.ts +++ b/src/api/endpoints/posts/timeline.ts @@ -7,7 +7,7 @@ import Post from '../../models/post'; import Mute from '../../models/mute'; import ChannelWatching from '../../models/channel-watching'; import getFriends from '../../common/get-friends'; -import serialize from '../../serializers/post'; +import { pack } from '../../models/post'; /** * Get timeline of myself @@ -128,5 +128,5 @@ module.exports = async (params, user, app) => { }); // Serialize - return await Promise.all(timeline.map(post => serialize(post, user))); + return await Promise.all(timeline.map(post => pack(post, user))); }; diff --git a/src/api/endpoints/posts/trend.ts b/src/api/endpoints/posts/trend.ts index 64a195dff1..b2b1d327a8 100644 --- a/src/api/endpoints/posts/trend.ts +++ b/src/api/endpoints/posts/trend.ts @@ -4,7 +4,7 @@ const ms = require('ms'); import $ from 'cafy'; import Post from '../../models/post'; -import serialize from '../../serializers/post'; +import { pack } from '../../models/post'; /** * Get trend posts @@ -76,5 +76,5 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Serialize res(await Promise.all(posts.map(async post => - await serialize(post, user, { detail: true })))); + await pack(post, user, { detail: true })))); }); diff --git a/src/api/endpoints/users.ts b/src/api/endpoints/users.ts index f3c9b66a5e..ba33b1aeb7 100644 --- a/src/api/endpoints/users.ts +++ b/src/api/endpoints/users.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import User from '../models/user'; -import serialize from '../serializers/user'; +import { pack } from '../models/user'; /** * Lists all users @@ -55,5 +55,5 @@ module.exports = (params, me) => new Promise(async (res, rej) => { // Serialize res(await Promise.all(users.map(async user => - await serialize(user, me)))); + await pack(user, me)))); }); diff --git a/src/api/endpoints/users/followers.ts b/src/api/endpoints/users/followers.ts index 4905323ba5..b0fb83c683 100644 --- a/src/api/endpoints/users/followers.ts +++ b/src/api/endpoints/users/followers.ts @@ -4,7 +4,7 @@ import $ from 'cafy'; import User from '../../models/user'; import Following from '../../models/following'; -import serialize from '../../serializers/user'; +import { pack } from '../../models/user'; import getFriends from '../../common/get-friends'; /** @@ -82,7 +82,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => { // Serialize const users = await Promise.all(following.map(async f => - await serialize(f.follower_id, me, { detail: true }))); + await pack(f.follower_id, me, { detail: true }))); // Response res({ diff --git a/src/api/endpoints/users/following.ts b/src/api/endpoints/users/following.ts index dc2ff49bbe..8e88431e92 100644 --- a/src/api/endpoints/users/following.ts +++ b/src/api/endpoints/users/following.ts @@ -4,7 +4,7 @@ import $ from 'cafy'; import User from '../../models/user'; import Following from '../../models/following'; -import serialize from '../../serializers/user'; +import { pack } from '../../models/user'; import getFriends from '../../common/get-friends'; /** @@ -82,7 +82,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => { // Serialize const users = await Promise.all(following.map(async f => - await serialize(f.followee_id, me, { detail: true }))); + await pack(f.followee_id, me, { detail: true }))); // Response res({ diff --git a/src/api/endpoints/users/get_frequently_replied_users.ts b/src/api/endpoints/users/get_frequently_replied_users.ts index a8add623d4..3cbc761322 100644 --- a/src/api/endpoints/users/get_frequently_replied_users.ts +++ b/src/api/endpoints/users/get_frequently_replied_users.ts @@ -4,7 +4,7 @@ import $ from 'cafy'; import Post from '../../models/post'; import User from '../../models/user'; -import serialize from '../../serializers/user'; +import { pack } from '../../models/user'; module.exports = (params, me) => new Promise(async (res, rej) => { // Get 'user_id' parameter @@ -91,7 +91,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => { // Make replies object (includes weights) const repliesObj = await Promise.all(topRepliedUsers.map(async (user) => ({ - user: await serialize(user, me, { detail: true }), + user: await pack(user, me, { detail: true }), weight: repliedUsers[user] / peak }))); diff --git a/src/api/endpoints/users/posts.ts b/src/api/endpoints/users/posts.ts index 0d8384a43d..1f3db3cf79 100644 --- a/src/api/endpoints/users/posts.ts +++ b/src/api/endpoints/users/posts.ts @@ -4,7 +4,7 @@ import $ from 'cafy'; import Post from '../../models/post'; import User from '../../models/user'; -import serialize from '../../serializers/post'; +import { pack } from '../../models/post'; /** * Get posts of a user @@ -124,6 +124,6 @@ module.exports = (params, me) => new Promise(async (res, rej) => { // Serialize res(await Promise.all(posts.map(async (post) => - await serialize(post, me) + await pack(post, me) ))); }); diff --git a/src/api/endpoints/users/recommendation.ts b/src/api/endpoints/users/recommendation.ts index 731d68a7b1..b80fd63ce7 100644 --- a/src/api/endpoints/users/recommendation.ts +++ b/src/api/endpoints/users/recommendation.ts @@ -4,7 +4,7 @@ const ms = require('ms'); import $ from 'cafy'; import User from '../../models/user'; -import serialize from '../../serializers/user'; +import { pack } from '../../models/user'; import getFriends from '../../common/get-friends'; /** @@ -44,5 +44,5 @@ module.exports = (params, me) => new Promise(async (res, rej) => { // Serialize res(await Promise.all(users.map(async user => - await serialize(user, me, { detail: true })))); + await pack(user, me, { detail: true })))); }); diff --git a/src/api/endpoints/users/search.ts b/src/api/endpoints/users/search.ts index 73a5db47e2..213038403b 100644 --- a/src/api/endpoints/users/search.ts +++ b/src/api/endpoints/users/search.ts @@ -4,7 +4,7 @@ import * as mongo from 'mongodb'; import $ from 'cafy'; import User from '../../models/user'; -import serialize from '../../serializers/user'; +import { pack } from '../../models/user'; import config from '../../../conf'; const escapeRegexp = require('escape-regexp'); @@ -94,6 +94,6 @@ async function byElasticsearch(res, rej, me, query, offset, max) { // Serialize res(await Promise.all(users.map(async user => - await serialize(user, me, { detail: true })))); + await pack(user, me, { detail: true })))); }); } diff --git a/src/api/endpoints/users/search_by_username.ts b/src/api/endpoints/users/search_by_username.ts index 7f2f42f0a6..63e206b1f2 100644 --- a/src/api/endpoints/users/search_by_username.ts +++ b/src/api/endpoints/users/search_by_username.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import User from '../../models/user'; -import serialize from '../../serializers/user'; +import { pack } from '../../models/user'; /** * Search a user by username @@ -35,5 +35,5 @@ module.exports = (params, me) => new Promise(async (res, rej) => { // Serialize res(await Promise.all(users.map(async user => - await serialize(user, me, { detail: true })))); + await pack(user, me, { detail: true })))); }); diff --git a/src/api/endpoints/users/show.ts b/src/api/endpoints/users/show.ts index 8e74b0fe3f..a51cb619d4 100644 --- a/src/api/endpoints/users/show.ts +++ b/src/api/endpoints/users/show.ts @@ -3,7 +3,7 @@ */ import $ from 'cafy'; import User from '../../models/user'; -import serialize from '../../serializers/user'; +import { pack } from '../../models/user'; /** * Show a user @@ -41,7 +41,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => { } // Send response - res(await serialize(user, me, { + res(await pack(user, me, { detail: true })); }); diff --git a/src/api/models/drive-file.ts b/src/api/models/drive-file.ts index 6a8db3ad42..9b9df1dac6 100644 --- a/src/api/models/drive-file.ts +++ b/src/api/models/drive-file.ts @@ -20,8 +20,14 @@ export { getGridFSBucket }; export type IDriveFile = { _id: mongodb.ObjectID; - created_at: Date; - user_id: mongodb.ObjectID; + uploadDate: Date; + md5: string; + filename: string; + metadata: { + properties: any; + user_id: mongodb.ObjectID; + folder_id: mongodb.ObjectID; + } }; export function validateFileName(name: string): boolean { diff --git a/src/api/models/drive-folder.ts b/src/api/models/drive-folder.ts index 48b26c2bd6..54b45049b9 100644 --- a/src/api/models/drive-folder.ts +++ b/src/api/models/drive-folder.ts @@ -9,7 +9,9 @@ export default DriveFolder; export type IDriveFolder = { _id: mongo.ObjectID; created_at: Date; + name: string; user_id: mongo.ObjectID; + parent_id: mongo.ObjectID; }; export function isValidFolderName(name: string): boolean { diff --git a/src/api/models/messaging-message.ts b/src/api/models/messaging-message.ts index ffdda1db21..90cf1cd71c 100644 --- a/src/api/models/messaging-message.ts +++ b/src/api/models/messaging-message.ts @@ -10,6 +10,11 @@ export default MessagingMessage; export interface IMessagingMessage { _id: mongo.ObjectID; + created_at: Date; + text: string; + user_id: mongo.ObjectID; + recipient_id: mongo.ObjectID; + is_read: boolean; } export function isValidText(text: string): boolean { diff --git a/src/api/private/signin.ts b/src/api/private/signin.ts index a26c8f6c5a..ab6e93562c 100644 --- a/src/api/private/signin.ts +++ b/src/api/private/signin.ts @@ -3,7 +3,7 @@ import * as bcrypt from 'bcryptjs'; import * as speakeasy from 'speakeasy'; import { default as User, IUser } from '../models/user'; import Signin from '../models/signin'; -import serialize from '../serializers/signin'; +import { pack } from '../models/signin'; import event from '../event'; import signin from '../common/signin'; import config from '../../conf'; @@ -85,5 +85,5 @@ export default async (req: express.Request, res: express.Response) => { }); // Publish signin event - event(user._id, 'signin', await serialize(record)); + event(user._id, 'signin', await pack(record)); }; diff --git a/src/api/private/signup.ts b/src/api/private/signup.ts index 466c6a489f..105fe319a5 100644 --- a/src/api/private/signup.ts +++ b/src/api/private/signup.ts @@ -4,7 +4,7 @@ import * as bcrypt from 'bcryptjs'; import recaptcha = require('recaptcha-promise'); import { default as User, IUser } from '../models/user'; import { validateUsername, validatePassword } from '../models/user'; -import serialize from '../serializers/user'; +import { pack } from '../models/user'; import generateUserToken from '../common/generate-native-user-token'; import config from '../../conf'; @@ -142,7 +142,7 @@ export default async (req: express.Request, res: express.Response) => { }); // Response - res.send(await serialize(account)); + res.send(await pack(account)); // Create search index if (config.elasticsearch.enable) { diff --git a/src/api/service/twitter.ts b/src/api/service/twitter.ts index 0e75ee0bdb..ca4f8abcca 100644 --- a/src/api/service/twitter.ts +++ b/src/api/service/twitter.ts @@ -6,7 +6,7 @@ import * as uuid from 'uuid'; import autwh from 'autwh'; import redis from '../../db/redis'; import User from '../models/user'; -import serialize from '../serializers/user'; +import { pack } from '../models/user'; import event from '../event'; import config from '../../conf'; import signin from '../common/signin'; @@ -50,7 +50,7 @@ module.exports = (app: express.Application) => { res.send(`Twitterの連携を解除しました :v:`); // Publish i updated event - event(user._id, 'i_updated', await serialize(user, user, { + event(user._id, 'i_updated', await pack(user, user, { detail: true, includeSecrets: true })); -- cgit v1.2.3-freya From bcd65d290d25219631bb47570478378a698d0fa0 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 4 Feb 2018 14:52:33 +0900 Subject: wip --- src/api/endpoints/aggregation/posts/reactions.ts | 11 +++++++---- src/api/endpoints/aggregation/users.ts | 13 ++++++++----- src/api/endpoints/app/create.ts | 3 +-- src/api/endpoints/channels/show.ts | 2 +- src/api/endpoints/drive/files/create.ts | 2 +- src/api/endpoints/drive/files/update.ts | 3 +-- src/api/endpoints/drive/files/upload_from_url.ts | 2 +- src/api/endpoints/drive/folders/create.ts | 3 +-- src/api/endpoints/drive/folders/update.ts | 3 +-- src/api/endpoints/following/create.ts | 7 +++---- src/api/endpoints/following/delete.ts | 5 ++--- src/api/endpoints/i/update.ts | 3 +-- src/api/endpoints/posts/reactions/create.ts | 9 ++++----- src/api/endpoints/users/posts.ts | 4 ++-- src/api/endpoints/users/search.ts | 2 +- src/api/models/app.ts | 1 + src/api/models/drive-file.ts | 1 + src/api/models/post-reaction.ts | 3 +++ src/api/models/post.ts | 4 +++- src/api/models/user.ts | 1 + src/api/private/signup.ts | 3 +-- src/api/service/twitter.ts | 2 +- src/api/stream/home.ts | 4 ++-- 23 files changed, 48 insertions(+), 43 deletions(-) (limited to 'src/api/models') diff --git a/src/api/endpoints/aggregation/posts/reactions.ts b/src/api/endpoints/aggregation/posts/reactions.ts index 2cd4588ae1..790b523be9 100644 --- a/src/api/endpoints/aggregation/posts/reactions.ts +++ b/src/api/endpoints/aggregation/posts/reactions.ts @@ -35,10 +35,13 @@ module.exports = (params) => new Promise(async (res, rej) => { { deleted_at: { $gt: startTime } } ] }, { - _id: false, - post_id: false - }, { - sort: { created_at: -1 } + sort: { + _id: -1 + }, + fields: { + _id: false, + post_id: false + } }); const graph = []; diff --git a/src/api/endpoints/aggregation/users.ts b/src/api/endpoints/aggregation/users.ts index 9eb2d035ec..e38ce92ff9 100644 --- a/src/api/endpoints/aggregation/users.ts +++ b/src/api/endpoints/aggregation/users.ts @@ -17,11 +17,14 @@ module.exports = params => new Promise(async (res, rej) => { const users = await User .find({}, { - _id: false, - created_at: true, - deleted_at: true - }, { - sort: { created_at: -1 } + sort: { + _id: -1 + }, + fields: { + _id: false, + created_at: true, + deleted_at: true + } }); const graph = []; diff --git a/src/api/endpoints/app/create.ts b/src/api/endpoints/app/create.ts index 71633f7def..0f688792a7 100644 --- a/src/api/endpoints/app/create.ts +++ b/src/api/endpoints/app/create.ts @@ -3,8 +3,7 @@ */ import rndstr from 'rndstr'; import $ from 'cafy'; -import App from '../../models/app'; -import { isValidNameId }, { pack } from '../../models/app'; +import App, { isValidNameId, pack } from '../../models/app'; /** * @swagger diff --git a/src/api/endpoints/channels/show.ts b/src/api/endpoints/channels/show.ts index 3238616fa5..332da64675 100644 --- a/src/api/endpoints/channels/show.ts +++ b/src/api/endpoints/channels/show.ts @@ -2,7 +2,7 @@ * Module dependencies */ import $ from 'cafy'; -import { default as Channel, IChannel }, { pack } from '../../models/channel'; +import Channel, { IChannel, pack } from '../../models/channel'; /** * Show a channel diff --git a/src/api/endpoints/drive/files/create.ts b/src/api/endpoints/drive/files/create.ts index 7b424f3f5a..96bcace886 100644 --- a/src/api/endpoints/drive/files/create.ts +++ b/src/api/endpoints/drive/files/create.ts @@ -2,7 +2,7 @@ * Module dependencies */ import $ from 'cafy'; -import { validateFileName }, { pack } from '../../../models/drive-file'; +import { validateFileName, pack } from '../../../models/drive-file'; import create from '../../../common/add-file-to-drive'; /** diff --git a/src/api/endpoints/drive/files/update.ts b/src/api/endpoints/drive/files/update.ts index ff65a48f71..83da462113 100644 --- a/src/api/endpoints/drive/files/update.ts +++ b/src/api/endpoints/drive/files/update.ts @@ -3,8 +3,7 @@ */ import $ from 'cafy'; import DriveFolder from '../../../models/drive-folder'; -import DriveFile from '../../../models/drive-file'; -import { validateFileName }, { pack } from '../../../models/drive-file'; +import DriveFile, { validateFileName, pack } from '../../../models/drive-file'; import { publishDriveStream } from '../../../event'; /** diff --git a/src/api/endpoints/drive/files/upload_from_url.ts b/src/api/endpoints/drive/files/upload_from_url.ts index 009f06aaaf..68428747ef 100644 --- a/src/api/endpoints/drive/files/upload_from_url.ts +++ b/src/api/endpoints/drive/files/upload_from_url.ts @@ -3,7 +3,7 @@ */ import * as URL from 'url'; import $ from 'cafy'; -import { validateFileName }, { pack } from '../../../models/drive-file'; +import { validateFileName, pack } from '../../../models/drive-file'; import create from '../../../common/add-file-to-drive'; import * as debug from 'debug'; import * as tmp from 'tmp'; diff --git a/src/api/endpoints/drive/folders/create.ts b/src/api/endpoints/drive/folders/create.ts index 6543b11274..03f396ddc9 100644 --- a/src/api/endpoints/drive/folders/create.ts +++ b/src/api/endpoints/drive/folders/create.ts @@ -2,8 +2,7 @@ * Module dependencies */ import $ from 'cafy'; -import DriveFolder from '../../../models/drive-folder'; -import { isValidFolderName }, { pack } from '../../../models/drive-folder'; +import DriveFolder, { isValidFolderName, pack } from '../../../models/drive-folder'; import { publishDriveStream } from '../../../event'; /** diff --git a/src/api/endpoints/drive/folders/update.ts b/src/api/endpoints/drive/folders/update.ts index 2adcadcb08..d3df8bdae5 100644 --- a/src/api/endpoints/drive/folders/update.ts +++ b/src/api/endpoints/drive/folders/update.ts @@ -2,8 +2,7 @@ * Module dependencies */ import $ from 'cafy'; -import DriveFolder from '../../../models/drive-folder'; -import { isValidFolderName }, { pack } from '../../../models/drive-folder'; +import DriveFolder, { isValidFolderName, pack } from '../../../models/drive-folder'; import { publishDriveStream } from '../../../event'; /** diff --git a/src/api/endpoints/following/create.ts b/src/api/endpoints/following/create.ts index b4a2217b16..8e1aa34713 100644 --- a/src/api/endpoints/following/create.ts +++ b/src/api/endpoints/following/create.ts @@ -2,11 +2,10 @@ * Module dependencies */ import $ from 'cafy'; -import User from '../../models/user'; +import User, { pack as packUser } from '../../models/user'; import Following from '../../models/following'; import notify from '../../common/notify'; import event from '../../event'; -import serializeUser from '../../serializers/user'; /** * Follow a user @@ -77,8 +76,8 @@ module.exports = (params, user) => new Promise(async (res, rej) => { }); // Publish follow event - event(follower._id, 'follow', await serializeUser(followee, follower)); - event(followee._id, 'followed', await serializeUser(follower, followee)); + event(follower._id, 'follow', await packUser(followee, follower)); + event(followee._id, 'followed', await packUser(follower, followee)); // Notify notify(followee._id, follower._id, 'follow'); diff --git a/src/api/endpoints/following/delete.ts b/src/api/endpoints/following/delete.ts index aa1639ef6c..b68cec09dd 100644 --- a/src/api/endpoints/following/delete.ts +++ b/src/api/endpoints/following/delete.ts @@ -2,10 +2,9 @@ * Module dependencies */ import $ from 'cafy'; -import User from '../../models/user'; +import User, { pack as packUser } from '../../models/user'; import Following from '../../models/following'; import event from '../../event'; -import serializeUser from '../../serializers/user'; /** * Unfollow a user @@ -78,5 +77,5 @@ module.exports = (params, user) => new Promise(async (res, rej) => { }); // Publish follow event - event(follower._id, 'unfollow', await serializeUser(followee, follower)); + event(follower._id, 'unfollow', await packUser(followee, follower)); }); diff --git a/src/api/endpoints/i/update.ts b/src/api/endpoints/i/update.ts index cd4b1a13f5..7bbbf95900 100644 --- a/src/api/endpoints/i/update.ts +++ b/src/api/endpoints/i/update.ts @@ -2,8 +2,7 @@ * Module dependencies */ import $ from 'cafy'; -import User from '../../models/user'; -import { isValidName, isValidDescription, isValidLocation, isValidBirthday }, { pack } from '../../models/user'; +import User, { isValidName, isValidDescription, isValidLocation, isValidBirthday, pack } from '../../models/user'; import event from '../../event'; import config from '../../../conf'; diff --git a/src/api/endpoints/posts/reactions/create.ts b/src/api/endpoints/posts/reactions/create.ts index d537463dfe..0b0e0e294d 100644 --- a/src/api/endpoints/posts/reactions/create.ts +++ b/src/api/endpoints/posts/reactions/create.ts @@ -3,13 +3,12 @@ */ import $ from 'cafy'; import Reaction from '../../../models/post-reaction'; -import Post from '../../../models/post'; +import Post, { pack as packPost } from '../../../models/post'; +import { pack as packUser } from '../../../models/user'; import Watching from '../../../models/post-watching'; import notify from '../../../common/notify'; import watch from '../../../common/watch-post'; import { publishPostStream, pushSw } from '../../../event'; -import serializePost from '../../../serializers/post'; -import serializeUser from '../../../serializers/user'; /** * React to a post @@ -90,8 +89,8 @@ module.exports = (params, user) => new Promise(async (res, rej) => { }); pushSw(post.user_id, 'reaction', { - user: await serializeUser(user, post.user_id), - post: await serializePost(post, post.user_id), + user: await packUser(user, post.user_id), + post: await packPost(post, post.user_id), reaction: reaction }); diff --git a/src/api/endpoints/users/posts.ts b/src/api/endpoints/users/posts.ts index 285e5bc46c..0c8bceee3d 100644 --- a/src/api/endpoints/users/posts.ts +++ b/src/api/endpoints/users/posts.ts @@ -2,8 +2,8 @@ * Module dependencies */ import $ from 'cafy'; -import Post from '../../models/post'; -import User, { pack } from '../../models/user'; +import Post, { pack } from '../../models/post'; +import User from '../../models/user'; /** * Get posts of a user diff --git a/src/api/endpoints/users/search.ts b/src/api/endpoints/users/search.ts index 1142db9e9b..39e2ff9890 100644 --- a/src/api/endpoints/users/search.ts +++ b/src/api/endpoints/users/search.ts @@ -51,7 +51,7 @@ async function byNative(res, rej, me, query, offset, max) { // Serialize res(await Promise.all(users.map(async user => - await serialize(user, me, { detail: true })))); + await pack(user, me, { detail: true })))); } // Search by Elasticsearch diff --git a/src/api/models/app.ts b/src/api/models/app.ts index fe9d49ff67..34e9867db7 100644 --- a/src/api/models/app.ts +++ b/src/api/models/app.ts @@ -14,6 +14,7 @@ export type IApp = { _id: mongo.ObjectID; created_at: Date; user_id: mongo.ObjectID; + secret: string; }; export function isValidNameId(nameId: string): boolean { diff --git a/src/api/models/drive-file.ts b/src/api/models/drive-file.ts index 9b9df1dac6..2a46d8dc4d 100644 --- a/src/api/models/drive-file.ts +++ b/src/api/models/drive-file.ts @@ -23,6 +23,7 @@ export type IDriveFile = { uploadDate: Date; md5: string; filename: string; + contentType: string; metadata: { properties: any; user_id: mongodb.ObjectID; diff --git a/src/api/models/post-reaction.ts b/src/api/models/post-reaction.ts index 568bfc89a2..639a70e006 100644 --- a/src/api/models/post-reaction.ts +++ b/src/api/models/post-reaction.ts @@ -9,6 +9,9 @@ export default PostReaction; export interface IPostReaction { _id: mongo.ObjectID; + created_at: Date; + deleted_at: Date; + reaction: string; } /** diff --git a/src/api/models/post.ts b/src/api/models/post.ts index ecc5e1a5e4..0bbacebf66 100644 --- a/src/api/models/post.ts +++ b/src/api/models/post.ts @@ -25,10 +25,12 @@ export type IPost = { media_ids: mongo.ObjectID[]; reply_id: mongo.ObjectID; repost_id: mongo.ObjectID; - poll: {}; // todo + poll: any; // todo text: string; user_id: mongo.ObjectID; app_id: mongo.ObjectID; + category: string; + is_category_verified: boolean; }; /** diff --git a/src/api/models/user.ts b/src/api/models/user.ts index 48a45ac2f7..e92f244dd0 100644 --- a/src/api/models/user.ts +++ b/src/api/models/user.ts @@ -42,6 +42,7 @@ export function isValidBirthday(birthday: string): boolean { export type IUser = { _id: mongo.ObjectID; created_at: Date; + deleted_at: Date; email: string; followers_count: number; following_count: number; diff --git a/src/api/private/signup.ts b/src/api/private/signup.ts index 392f3b1fc7..8efdb6db47 100644 --- a/src/api/private/signup.ts +++ b/src/api/private/signup.ts @@ -2,8 +2,7 @@ import * as uuid from 'uuid'; import * as express from 'express'; import * as bcrypt from 'bcryptjs'; import recaptcha = require('recaptcha-promise'); -import { default as User, IUser } from '../models/user'; -import { validateUsername, validatePassword }, { pack } from '../models/user'; +import User, { IUser, validateUsername, validatePassword, pack } from '../models/user'; import generateUserToken from '../common/generate-native-user-token'; import config from '../../conf'; diff --git a/src/api/service/twitter.ts b/src/api/service/twitter.ts index 7d4964eba6..adcd5ac49b 100644 --- a/src/api/service/twitter.ts +++ b/src/api/service/twitter.ts @@ -163,7 +163,7 @@ module.exports = (app: express.Application) => { res.send(`Twitter: @${result.screenName} を、Misskey: @${user.username} に接続しました!`); // Publish i updated event - event(user._id, 'i_updated', await serialize(user, user, { + event(user._id, 'i_updated', await pack(user, user, { detail: true, includeSecrets: true })); diff --git a/src/api/stream/home.ts b/src/api/stream/home.ts index 7dcdb5ed73..10078337c3 100644 --- a/src/api/stream/home.ts +++ b/src/api/stream/home.ts @@ -4,7 +4,7 @@ import * as debug from 'debug'; import User from '../models/user'; import Mute from '../models/mute'; -import serializePost from '../serializers/post'; +import { pack as packPost } from '../models/post'; import readNotification from '../common/read-notification'; const log = debug('misskey'); @@ -49,7 +49,7 @@ export default async function(request: websocket.request, connection: websocket. case 'post-stream': const postId = channel.split(':')[2]; log(`RECEIVED: ${postId} ${data} by @${user.username}`); - const post = await serializePost(postId, user, { + const post = await packPost(postId, user, { detail: true }); connection.send(JSON.stringify({ -- cgit v1.2.3-freya From d3639b2df4fece251e7cbac6920607ce7dacb302 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 27 Feb 2018 02:10:52 +0900 Subject: #372 --- src/api/models/messaging-message.ts | 2 +- .../views/components/messaging-room.form.vue | 36 +++++-- .../views/components/messaging-room.message.vue | 107 ++++++++++++--------- 3 files changed, 91 insertions(+), 54 deletions(-) (limited to 'src/api/models') diff --git a/src/api/models/messaging-message.ts b/src/api/models/messaging-message.ts index 90cf1cd71c..fcb356c5ca 100644 --- a/src/api/models/messaging-message.ts +++ b/src/api/models/messaging-message.ts @@ -67,7 +67,7 @@ export const pack = ( // Populate user _message.user = await packUser(_message.user_id, me); - if (_message.file) { + if (_message.file_id) { // Populate file _message.file = await packFile(_message.file_id); } diff --git a/src/web/app/common/views/components/messaging-room.form.vue b/src/web/app/common/views/components/messaging-room.form.vue index 9b5f1522f1..48ccaa4d27 100644 --- a/src/web/app/common/views/components/messaging-room.form.vue +++ b/src/web/app/common/views/components/messaging-room.form.vue @@ -8,18 +8,18 @@ placeholder="%i18n:common.input-message-here%" v-autocomplete="'text'" > -
{{ file.name }}
- - - - + @@ -39,6 +39,9 @@ export default Vue.extend({ computed: { draftId(): string { return this.user.id; + }, + canSend(): boolean { + return (this.text != null && this.text != '') || this.file != null; } }, watch: { @@ -88,15 +91,24 @@ export default Vue.extend({ }); }, - upload() { - // TODO + onChangeFile() { + this.upload((this.$refs.file as any).files[0]); + }, + + upload(file) { + (this.$refs.uploader as any).upload(file); + }, + + onUploaded(file) { + this.file = file; }, send() { this.sending = true; (this as any).api('messaging/messages/create', { user_id: this.user.id, - text: this.text + text: this.text ? this.text : undefined, + file_id: this.file ? this.file.id : undefined }).then(message => { this.clear(); }).catch(err => { @@ -158,13 +170,18 @@ export default Vue.extend({ box-shadow none background transparent + > .file + padding 8px + color #444 + background #eee + cursor pointer + > .send position absolute bottom 0 right 0 margin 0 padding 10px 14px - line-height 1em font-size 1em color #aaa transition color 0.1s ease @@ -222,7 +239,6 @@ export default Vue.extend({ .attach-from-drive margin 0 padding 10px 14px - line-height 1em font-size 1em font-weight normal text-decoration none diff --git a/src/web/app/common/views/components/messaging-room.message.vue b/src/web/app/common/views/components/messaging-room.message.vue index 9772c7c0dd..56854ca2ff 100644 --- a/src/web/app/common/views/components/messaging-room.message.vue +++ b/src/web/app/common/views/components/messaging-room.message.vue @@ -3,23 +3,27 @@ -
-
+
+

%i18n:common.tags.mk-messaging-message.is-read%

- -

%i18n:common.tags.mk-messaging-message.deleted%

+
+
@@ -57,13 +61,10 @@ export default Vue.extend({ padding 10px 12px 10px 12px background-color transparent - &:after - content "" - display block - clear both - > .avatar-anchor display block + position absolute + top 10px > .avatar display block @@ -75,18 +76,12 @@ export default Vue.extend({ border-radius 8px transition all 0.1s ease - > .content-container - display block - margin 0 12px - padding 0 - max-width calc(100% - 78px) + > .content > .balloon display block - float inherit - margin 0 padding 0 - max-width 100% + max-width calc(100% - 16px) min-height 38px border-radius 16px @@ -97,6 +92,9 @@ export default Vue.extend({ position absolute top 12px + & + * + clear both + &:hover > .delete-button display block @@ -153,28 +151,43 @@ export default Vue.extend({ font-size 1em color rgba(0, 0, 0, 0.8) - &, * - user-select text - cursor auto - & + .file - &.image - > img - border-radius 0 0 16px 16px + > a + border-radius 0 0 16px 16px > .file - &.image - > img + > a + display block + max-width 100% + max-height 512px + border-radius 16px + overflow hidden + text-decoration none + + &:hover + text-decoration none + + > p + background #ccc + + > * display block - max-width 100% - max-height 512px - border-radius 16px + margin 0 + width 100% + height 100% + + > p + padding 30px + text-align center + color #555 + background #ddd + + > .mk-url-preview + margin 8px 0 > footer display block - clear both - margin 0 - padding 2px + margin 2px 0 0 0 font-size 10px color rgba(0, 0, 0, 0.4) @@ -183,15 +196,19 @@ export default Vue.extend({ &:not([data-is-me]) > .avatar-anchor - float left + left 12px - > .content-container - float left + > .content + padding-left 66px > .balloon + float left background #eee - &:before + &[data-no-text] + background transparent + + &:not([data-no-text]):before left -14px border-top solid 8px transparent border-right solid 8px #eee @@ -203,15 +220,19 @@ export default Vue.extend({ &[data-is-me] > .avatar-anchor - float right + right 12px - > .content-container - float right + > .content + padding-right 66px > .balloon + float right background $me-balloon-color - &:before + &[data-no-text] + background transparent + + &:not([data-no-text]):before right -14px left auto border-top solid 8px transparent @@ -232,7 +253,7 @@ export default Vue.extend({ text-align right &[data-is-deleted] - > .content-container - opacity 0.5 + > .baloon + opacity 0.5 -- cgit v1.2.3-freya From 423f73777cd0b8e6ef916f1ad4e6971c1e560bb7 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 2 Mar 2018 06:26:31 +0900 Subject: nanka iroiro --- package.json | 7 +++++- src/api/endpoints/i/update.ts | 8 ++++++- src/api/models/user.ts | 1 + src/web/app/common/views/components/poll.vue | 12 ++++++---- src/web/app/config.ts | 2 ++ src/web/app/desktop/script.ts | 1 + src/web/app/desktop/style.styl | 1 - .../desktop/views/components/settings.profile.vue | 9 +++++++ src/web/app/desktop/views/components/settings.vue | 28 ++++++++++++++++------ src/web/app/init.ts | 2 ++ src/web/docs/api/entities/user.yaml | 6 +++++ src/web/element.scss | 12 ++++++++++ webpack/webpack.config.ts | 17 +++++++++++++ 13 files changed, 92 insertions(+), 14 deletions(-) create mode 100644 src/web/element.scss (limited to 'src/api/models') diff --git a/package.json b/package.json index 29067b4ea4..f7001725ea 100644 --- a/package.json +++ b/package.json @@ -97,6 +97,7 @@ "deepcopy": "0.6.3", "diskusage": "0.2.4", "elasticsearch": "14.1.0", + "element-ui": "^2.2.0", "emojilib": "^2.2.12", "escape-regexp": "0.0.1", "eslint": "4.18.0", @@ -104,6 +105,7 @@ "eventemitter3": "3.0.1", "exif-js": "2.3.0", "express": "4.16.2", + "file-loader": "^1.1.10", "file-type": "7.6.0", "fuckadblock": "3.2.1", "gm": "1.23.1", @@ -137,6 +139,8 @@ "morgan": "1.9.0", "ms": "2.1.1", "multer": "1.3.0", + "node-sass": "^4.7.2", + "node-sass-json-importer": "^3.1.3", "nprogress": "0.2.0", "os-utils": "0.0.14", "progress-bar-webpack-plugin": "^1.11.0", @@ -152,6 +156,7 @@ "rimraf": "2.6.2", "rndstr": "1.0.0", "s-age": "1.1.2", + "sass-loader": "^6.0.6", "seedrandom": "2.4.3", "serve-favicon": "2.4.5", "speakeasy": "2.0.0", @@ -181,7 +186,7 @@ "vue-template-compiler": "2.5.13", "vuedraggable": "2.16.0", "web-push": "3.3.0", - "webpack": "^3.11.0", + "webpack": "3.11.0", "webpack-cli": "^2.0.8", "webpack-replace-loader": "1.3.0", "websocket": "1.0.25", diff --git a/src/api/endpoints/i/update.ts b/src/api/endpoints/i/update.ts index 43c5245044..fc12665ad6 100644 --- a/src/api/endpoints/i/update.ts +++ b/src/api/endpoints/i/update.ts @@ -46,13 +46,19 @@ module.exports = async (params, user, _, isSecure) => new Promise(async (res, re if (bannerIdErr) return rej('invalid banner_id param'); if (bannerId) user.banner_id = bannerId; + // Get 'is_bot' parameter + const [isBot, isBotErr] = $(params.is_bot).optional.boolean().$; + if (isBotErr) return rej('invalid is_bot param'); + if (isBot) user.is_bot = isBot; + await User.update(user._id, { $set: { name: user.name, description: user.description, avatar_id: user.avatar_id, banner_id: user.banner_id, - profile: user.profile + profile: user.profile, + is_bot: user.is_bot } }); diff --git a/src/api/models/user.ts b/src/api/models/user.ts index e92f244dd0..278b949db5 100644 --- a/src/api/models/user.ts +++ b/src/api/models/user.ts @@ -75,6 +75,7 @@ export type IUser = { last_used_at: Date; latest_post: IPost; pinned_post_id: mongo.ObjectID; + is_bot: boolean; is_pro: boolean; is_suspended: boolean; keywords: string[]; diff --git a/src/web/app/common/views/components/poll.vue b/src/web/app/common/views/components/poll.vue index 7ed5bc6b1e..556d8ebf6c 100644 --- a/src/web/app/common/views/components/poll.vue +++ b/src/web/app/common/views/components/poll.vue @@ -5,14 +5,14 @@
- {{ choice.text }} + {{ choice.text }} ({{ '%i18n:common.tags.mk-poll.vote-count%'.replace('{}', choice.votes) }})

{{ '%i18n:common.tags.mk-poll.total-users%'.replace('{}', total) }} - ・ + {{ showResult ? '%i18n:common.tags.mk-poll.vote%' : '%i18n:common.tags.mk-poll.show-result%' }} %i18n:common.tags.mk-poll.voted%

@@ -98,8 +98,12 @@ export default Vue.extend({ background $theme-color transition width 1s ease - > .votes - margin-left 4px + > span + > [data-fa] + margin-right 4px + + > .votes + margin-left 4px > p a diff --git a/src/web/app/config.ts b/src/web/app/config.ts index 2461b22158..b51279192c 100644 --- a/src/web/app/config.ts +++ b/src/web/app/config.ts @@ -12,6 +12,7 @@ declare const _SW_PUBLICKEY_: string; declare const _THEME_COLOR_: string; declare const _COPYRIGHT_: string; declare const _VERSION_: string; +declare const _LICENSE_: string; export const host = _HOST_; export const url = _URL_; @@ -27,3 +28,4 @@ export const swPublickey = _SW_PUBLICKEY_; export const themeColor = _THEME_COLOR_; export const copyright = _COPYRIGHT_; export const version = _VERSION_; +export const license = _LICENSE_; diff --git a/src/web/app/desktop/script.ts b/src/web/app/desktop/script.ts index f0412805ef..78549741b0 100644 --- a/src/web/app/desktop/script.ts +++ b/src/web/app/desktop/script.ts @@ -4,6 +4,7 @@ // Style import './style.styl'; +import '../../element.scss'; import init from '../init'; import fuckAdBlock from '../common/scripts/fuck-ad-block'; diff --git a/src/web/app/desktop/style.styl b/src/web/app/desktop/style.styl index 4d295035f7..49f71fbde7 100644 --- a/src/web/app/desktop/style.styl +++ b/src/web/app/desktop/style.styl @@ -1,6 +1,5 @@ @import "../app" @import "../reset" -@import "../../../../node_modules/cropperjs/dist/cropper.css" @import "./ui" diff --git a/src/web/app/desktop/views/components/settings.profile.vue b/src/web/app/desktop/views/components/settings.profile.vue index 97a382d798..218da67e81 100644 --- a/src/web/app/desktop/views/components/settings.profile.vue +++ b/src/web/app/desktop/views/components/settings.profile.vue @@ -22,6 +22,10 @@ +
+

その他

+ +
@@ -56,6 +60,11 @@ export default Vue.extend({ }).then(() => { (this as any).apis.notify('プロフィールを更新しました'); }); + }, + onChangeIsBot() { + (this as any).api('i/update', { + is_bot: (this as any).os.i.is_bot + }); } } }); diff --git a/src/web/app/desktop/views/components/settings.vue b/src/web/app/desktop/views/components/settings.vue index c210997c38..b65623e33a 100644 --- a/src/web/app/desktop/views/components/settings.vue +++ b/src/web/app/desktop/views/components/settings.vue @@ -23,10 +23,7 @@
- +
@@ -71,7 +68,8 @@

%i18n:desktop.tags.mk-settings.license%

- %license% +
+ サードパーティ
@@ -84,6 +82,7 @@ import XMute from './settings.mute.vue'; import XPassword from './settings.password.vue'; import X2fa from './settings.2fa.vue'; import XApi from './settings.api.vue'; +import { docsUrl, license, lang } from '../../../config'; export default Vue.extend({ components: { @@ -96,10 +95,15 @@ export default Vue.extend({ data() { return { page: 'profile', - + license, showPostFormOnTopOfTl: false }; }, + computed: { + licenseUrl(): string { + return `${docsUrl}/${lang}/license`; + } + }, created() { this.showPostFormOnTopOfTl = (this as any).os.i.client_settings.showPostFormOnTopOfTl; }, @@ -162,13 +166,23 @@ export default Vue.extend({ color #4a535a > h1 - display block margin 0 0 1em 0 padding 0 0 8px 0 font-size 1em color #555 border-bottom solid 1px #eee + &, >>> * + > section + margin 32px 0 + + > h2 + margin 0 0 1em 0 + padding 0 0 8px 0 + font-size 1em + color #555 + border-bottom solid 1px #eee + > .web > div border-bottom solid 1px #eee diff --git a/src/web/app/init.ts b/src/web/app/init.ts index f9855fd5c9..dc3057935f 100644 --- a/src/web/app/init.ts +++ b/src/web/app/init.ts @@ -5,9 +5,11 @@ import Vue from 'vue'; import VueRouter from 'vue-router'; import VModal from 'vue-js-modal'; +import Element from 'element-ui'; Vue.use(VueRouter); Vue.use(VModal); +Vue.use(Element); // Register global directives require('./common/views/directives'); diff --git a/src/web/docs/api/entities/user.yaml b/src/web/docs/api/entities/user.yaml index e62ad84db8..528b9b0e13 100644 --- a/src/web/docs/api/entities/user.yaml +++ b/src/web/docs/api/entities/user.yaml @@ -111,6 +111,12 @@ props: desc: ja: "ドライブの容量(bytes)" en: "The capacity of drive of this user (bytes)" + - name: "is_bot" + type: "boolean" + optional: true + desc: + ja: "botか否か(自己申告であることに留意)" + en: "Whether is bot or not" - name: "twitter" type: "object" optional: true diff --git a/src/web/element.scss b/src/web/element.scss new file mode 100644 index 0000000000..917198e024 --- /dev/null +++ b/src/web/element.scss @@ -0,0 +1,12 @@ +/* Element variable definitons */ +/* SEE: http://element.eleme.io/#/en-US/component/custom-theme */ + +@import '../const.json'; + +/* theme color */ +$--color-primary: $themeColor; + +/* icon font path, required */ +$--font-path: '~element-ui/lib/theme-chalk/fonts'; + +@import "~element-ui/packages/theme-chalk/src/index"; diff --git a/webpack/webpack.config.ts b/webpack/webpack.config.ts index 775ecbd348..a873419450 100644 --- a/webpack/webpack.config.ts +++ b/webpack/webpack.config.ts @@ -3,6 +3,7 @@ */ import * as fs from 'fs'; +import jsonImporter from 'node-sass-json-importer'; const minify = require('html-minifier').minify; import I18nReplacer from '../src/common/build/i18n'; import { pattern as faPattern, replacement as faReplacement } from '../src/common/build/fa'; @@ -111,12 +112,28 @@ module.exports = Object.keys(langs).map(lang => { { loader: 'css-loader' }, { loader: 'stylus-loader' } ] + }, { + test: /\.scss$/, + exclude: /node_modules/, + use: [{ + loader: 'style-loader' + }, { + loader: 'css-loader' + }, { + loader: 'sass-loader', + options: { + importer: jsonImporter, + } + }] }, { test: /\.css$/, use: [ { loader: 'style-loader' }, { loader: 'css-loader' } ] + }, { + test: /\.(eot|woff|woff2|svg|ttf)([\?]?.*)$/, + loader: 'file-loader' }, { test: /\.ts$/, exclude: /node_modules/, -- cgit v1.2.3-freya From d7c94de07e17e31513780ceabf824a3d0760ff80 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 4 Mar 2018 09:39:25 +0900 Subject: #172 --- src/api/endpoints/posts/create.ts | 5 +++++ src/api/models/post.ts | 1 + src/const.json | 2 +- src/web/app/desktop/views/components/posts.post.vue | 5 +++++ src/web/app/desktop/views/components/settings.vue | 11 +++++++++++ src/web/app/mobile/views/components/post-form.vue | 4 +++- src/web/app/mobile/views/components/posts.post.vue | 19 ++++++++++++++----- src/web/docs/api/entities/post.yaml | 6 ++++++ 8 files changed, 46 insertions(+), 7 deletions(-) (limited to 'src/api/models') diff --git a/src/api/endpoints/posts/create.ts b/src/api/endpoints/posts/create.ts index 075e1ac9f0..57f98fa811 100644 --- a/src/api/endpoints/posts/create.ts +++ b/src/api/endpoints/posts/create.ts @@ -31,6 +31,10 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { const [text, textErr] = $(params.text).optional.string().pipe(isValidText).$; if (textErr) return rej('invalid text'); + // Get 'via_mobile' parameter + const [viaMobile = false, viaMobileErr] = $(params.via_mobile).optional.boolean().$; + if (viaMobileErr) return rej('invalid via_mobile'); + // Get 'tags' parameter const [tags = [], tagsErr] = $(params.tags).optional.array('string').unique().eachQ(t => t.range(1, 32)).$; if (tagsErr) return rej('invalid tags'); @@ -239,6 +243,7 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { tags: tags, user_id: user._id, app_id: app ? app._id : null, + via_mobile: viaMobile, // 以下非正規化データ _reply: reply ? { user_id: reply.user_id } : undefined, diff --git a/src/api/models/post.ts b/src/api/models/post.ts index 0bbacebf66..edb69e0c15 100644 --- a/src/api/models/post.ts +++ b/src/api/models/post.ts @@ -31,6 +31,7 @@ export type IPost = { app_id: mongo.ObjectID; category: string; is_category_verified: boolean; + via_mobile: boolean; }; /** diff --git a/src/const.json b/src/const.json index d8fe4fe6cd..65dc734fab 100644 --- a/src/const.json +++ b/src/const.json @@ -1,5 +1,5 @@ { "copyright": "Copyright (c) 2014-2018 syuilo", - "themeColor": "#ff4e45", + "themeColor": "#5cbb2d", "themeColorForeground": "#fff" } diff --git a/src/web/app/desktop/views/components/posts.post.vue b/src/web/app/desktop/views/components/posts.post.vue index 4a95918c48..ce0a31d183 100644 --- a/src/web/app/desktop/views/components/posts.post.vue +++ b/src/web/app/desktop/views/components/posts.post.vue @@ -26,6 +26,7 @@ @{{ p.user.username }}
via {{ p.app.name }} + %fa:mobile-alt% @@ -399,6 +400,10 @@ export default Vue.extend({ margin-left auto font-size 0.9em + > .mobile + margin-right 8px + color #ccc + > .app margin-right 8px padding-right 8px diff --git a/src/web/app/desktop/views/components/settings.vue b/src/web/app/desktop/views/components/settings.vue index 182a9a1d5e..20d7a77715 100644 --- a/src/web/app/desktop/views/components/settings.vue +++ b/src/web/app/desktop/views/components/settings.vue @@ -26,6 +26,11 @@ +
+

モバイル

+ +
+

言語

@@ -192,6 +197,12 @@ export default Vue.extend({ value: v }); }, + onChangeDisableViaMobile(v) { + (this as any).api('i/update_client_setting', { + name: 'disableViaMobile', + value: v + }); + }, checkForUpdate() { this.checkingForUpdate = true; checkForUpdate((this as any).os, true, true).then(newer => { diff --git a/src/web/app/mobile/views/components/post-form.vue b/src/web/app/mobile/views/components/post-form.vue index 63b75b92f0..009012b0b6 100644 --- a/src/web/app/mobile/views/components/post-form.vue +++ b/src/web/app/mobile/views/components/post-form.vue @@ -91,11 +91,13 @@ export default Vue.extend({ }, post() { this.posting = true; + const viaMobile = (this as any).os.i.client_settings.disableViaMobile !== true; (this as any).api('posts/create', { text: this.text == '' ? undefined : this.text, media_ids: this.files.length > 0 ? this.files.map(f => f.id) : undefined, reply_id: this.reply ? this.reply.id : undefined, - poll: this.poll ? (this.$refs.poll as any).get() : undefined + poll: this.poll ? (this.$refs.poll as any).get() : undefined, + via_mobile: viaMobile }).then(data => { this.$emit('post'); this.$destroy(); diff --git a/src/web/app/mobile/views/components/posts.post.vue b/src/web/app/mobile/views/components/posts.post.vue index 3c02e1e990..d0a897db65 100644 --- a/src/web/app/mobile/views/components/posts.post.vue +++ b/src/web/app/mobile/views/components/posts.post.vue @@ -24,9 +24,12 @@ {{ p.user.name }} bot @{{ p.user.username }} - - - +
+ %fa:mobile-alt% + + + +
@@ -336,10 +339,16 @@ export default Vue.extend({ margin 0 0.5em 0 0 color #ccc - > .created-at + > .info margin-left auto font-size 0.9em - color #c0c0c0 + + > .mobile + margin-right 6px + color #c0c0c0 + + > .created-at + color #c0c0c0 > .body diff --git a/src/web/docs/api/entities/post.yaml b/src/web/docs/api/entities/post.yaml index 551f3b7c3e..e4359ffd0c 100644 --- a/src/web/docs/api/entities/post.yaml +++ b/src/web/docs/api/entities/post.yaml @@ -17,6 +17,12 @@ props: desc: ja: "投稿日時" en: "The posted date of this post" + - name: "via_mobile" + type: "boolean" + optional: true + desc: + ja: "モバイル端末から投稿したか否か(自己申告であることに留意)" + en: "Whether this post sent via a mobile device" - name: "text" type: "string" optional: true -- cgit v1.2.3-freya From cb0d237b6a70ad118d3511930ef6cb1bca91cd76 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 5 Mar 2018 08:07:09 +0900 Subject: #1183 --- src/api/endpoints/i/update.ts | 8 +++++++- src/api/endpoints/posts/create.ts | 6 +++--- src/api/endpoints/posts/polls/vote.ts | 6 +++--- src/api/endpoints/posts/reactions/create.ts | 6 +++--- src/api/models/user.ts | 2 ++ src/api/private/signup.ts | 4 +++- src/web/app/desktop/views/components/settings.vue | 17 +++++++++++++++++ 7 files changed, 38 insertions(+), 11 deletions(-) (limited to 'src/api/models') diff --git a/src/api/endpoints/i/update.ts b/src/api/endpoints/i/update.ts index 2a5dce64a8..76bad2d156 100644 --- a/src/api/endpoints/i/update.ts +++ b/src/api/endpoints/i/update.ts @@ -51,6 +51,11 @@ module.exports = async (params, user, _, isSecure) => new Promise(async (res, re if (isBotErr) return rej('invalid is_bot param'); if (isBot != null) user.is_bot = isBot; + // Get 'auto_watch' parameter + const [autoWatch, autoWatchErr] = $(params.auto_watch).optional.boolean().$; + if (autoWatchErr) return rej('invalid auto_watch param'); + if (autoWatch != null) user.settings.auto_watch = autoWatch; + await User.update(user._id, { $set: { name: user.name, @@ -58,7 +63,8 @@ module.exports = async (params, user, _, isSecure) => new Promise(async (res, re avatar_id: user.avatar_id, banner_id: user.banner_id, profile: user.profile, - is_bot: user.is_bot + is_bot: user.is_bot, + settings: user.settings } }); diff --git a/src/api/endpoints/posts/create.ts b/src/api/endpoints/posts/create.ts index 57f98fa811..a9d52fd128 100644 --- a/src/api/endpoints/posts/create.ts +++ b/src/api/endpoints/posts/create.ts @@ -377,9 +377,9 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { }); // この投稿をWatchする - // TODO: ユーザーが「返信したときに自動でWatchする」設定を - // オフにしていた場合はしない - watch(user._id, reply); + if (user.settings.auto_watch !== false) { + watch(user._id, reply); + } // Add mention addMention(reply.user_id, 'reply'); diff --git a/src/api/endpoints/posts/polls/vote.ts b/src/api/endpoints/posts/polls/vote.ts index 5a4fd1c268..8222fe5326 100644 --- a/src/api/endpoints/posts/polls/vote.ts +++ b/src/api/endpoints/posts/polls/vote.ts @@ -100,9 +100,9 @@ module.exports = (params, user) => new Promise(async (res, rej) => { }); // この投稿をWatchする - // TODO: ユーザーが「投票したときに自動でWatchする」設定を - // オフにしていた場合はしない - watch(user._id, post); + if (user.settings.auto_watch !== false) { + watch(user._id, post); + } }); function findWithAttr(array, attr, value) { diff --git a/src/api/endpoints/posts/reactions/create.ts b/src/api/endpoints/posts/reactions/create.ts index 0b0e0e294d..93d9756d02 100644 --- a/src/api/endpoints/posts/reactions/create.ts +++ b/src/api/endpoints/posts/reactions/create.ts @@ -116,7 +116,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { }); // この投稿をWatchする - // TODO: ユーザーが「リアクションしたときに自動でWatchする」設定を - // オフにしていた場合はしない - watch(user._id, post); + if (user.settings.auto_watch !== false) { + watch(user._id, post); + } }); diff --git a/src/api/models/user.ts b/src/api/models/user.ts index 278b949db5..2fea0566ba 100644 --- a/src/api/models/user.ts +++ b/src/api/models/user.ts @@ -81,6 +81,8 @@ export type IUser = { keywords: string[]; two_factor_secret: string; two_factor_enabled: boolean; + client_settings: any; + settings: any; }; export function init(user): IUser { diff --git a/src/api/private/signup.ts b/src/api/private/signup.ts index 19e3314756..1e3e9fb45f 100644 --- a/src/api/private/signup.ts +++ b/src/api/private/signup.ts @@ -132,7 +132,9 @@ export default async (req: express.Request, res: express.Response) => { location: null, weight: null }, - settings: {}, + settings: { + auto_watch: true + }, client_settings: { home: homeData, show_donation: false diff --git a/src/web/app/desktop/views/components/settings.vue b/src/web/app/desktop/views/components/settings.vue index a0ffc4e0aa..0eb18770a5 100644 --- a/src/web/app/desktop/views/components/settings.vue +++ b/src/web/app/desktop/views/components/settings.vue @@ -62,6 +62,13 @@
+
+

通知

+ + リアクションしたり返信したりした投稿に関する通知を自動的に受け取るようにします。 + +
+

%i18n:desktop.tags.mk-settings.drive%

@@ -173,6 +180,7 @@ export default Vue.extend({ version, latestVersion: undefined, checkingForUpdate: false, + autoWatch: true, enableSounds: localStorage.getItem('enableSounds') == 'true', lang: localStorage.getItem('lang') || '', preventUpdate: localStorage.getItem('preventUpdate') == 'true', @@ -206,12 +214,21 @@ export default Vue.extend({ (this as any).os.getMeta().then(meta => { this.meta = meta; }); + + if ((this as any).os.i.settings.auto_watch != null) { + this.autoWatch = (this as any).os.i.settings.auto_watch; + } }, methods: { customizeHome() { this.$router.push('/i/customize-home'); this.$emit('done'); }, + onChangeAutoWatch(v) { + (this as any).api('i/update', { + auto_watch: v + }); + }, onChangeShowPostFormOnTopOfTl(v) { (this as any).api('i/update_client_setting', { name: 'showPostFormOnTopOfTl', -- cgit v1.2.3-freya From b0c7cb88035b5ca723de2a5a08cd2840214d9b97 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 5 Mar 2018 08:44:37 +0900 Subject: wip --- src/api/endpoints/posts/create.ts | 13 +++++++ src/api/models/post.ts | 9 +++++ .../desktop/views/components/post-form-window.vue | 13 ++++++- src/web/app/desktop/views/components/post-form.vue | 21 ++++++++++- src/web/app/mobile/views/components/post-form.vue | 24 ++++++++++-- src/web/docs/api/entities/post.yaml | 43 ++++++++++++++++++++++ 6 files changed, 117 insertions(+), 6 deletions(-) (limited to 'src/api/models') diff --git a/src/api/endpoints/posts/create.ts b/src/api/endpoints/posts/create.ts index a9d52fd128..15cbc4845c 100644 --- a/src/api/endpoints/posts/create.ts +++ b/src/api/endpoints/posts/create.ts @@ -39,6 +39,18 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { const [tags = [], tagsErr] = $(params.tags).optional.array('string').unique().eachQ(t => t.range(1, 32)).$; if (tagsErr) return rej('invalid tags'); + // Get 'geo' parameter + const [geo, geoErr] = $(params.geo).optional.nullable.strict.object() + .have('latitude', $().number().range(-180, 180)) + .have('longitude', $().number().range(-90, 90)) + .have('altitude', $().nullable.number()) + .have('accuracy', $().nullable.number()) + .have('altitudeAccuracy', $().nullable.number()) + .have('heading', $().nullable.number().range(0, 360)) + .have('speed', $().nullable.number()) + .$; + if (geoErr) return rej('invalid geo'); + // Get 'media_ids' parameter const [mediaIds, mediaIdsErr] = $(params.media_ids).optional.array('id').unique().range(1, 4).$; if (mediaIdsErr) return rej('invalid media_ids'); @@ -244,6 +256,7 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { user_id: user._id, app_id: app ? app._id : null, via_mobile: viaMobile, + geo, // 以下非正規化データ _reply: reply ? { user_id: reply.user_id } : undefined, diff --git a/src/api/models/post.ts b/src/api/models/post.ts index edb69e0c15..c37c8371c0 100644 --- a/src/api/models/post.ts +++ b/src/api/models/post.ts @@ -32,6 +32,15 @@ export type IPost = { category: string; is_category_verified: boolean; via_mobile: boolean; + geo: { + latitude: number; + longitude: number; + altitude: number; + accuracy: number; + altitudeAccuracy: number; + heading: number; + speed: number; + }; }; /** diff --git a/src/web/app/desktop/views/components/post-form-window.vue b/src/web/app/desktop/views/components/post-form-window.vue index 4427f59829..31a07a890e 100644 --- a/src/web/app/desktop/views/components/post-form-window.vue +++ b/src/web/app/desktop/views/components/post-form-window.vue @@ -1,6 +1,7 @@ @@ -24,7 +26,8 @@ export default Vue.extend({ data() { return { uploadings: [], - media: [] + media: [], + geo: null }; }, mounted() { @@ -39,6 +42,9 @@ export default Vue.extend({ onChangeMedia(media) { this.media = media; }, + onGeoAttached(geo) { + this.geo = geo; + }, onPosted() { (this.$refs.window as any).close(); } @@ -47,6 +53,9 @@ export default Vue.extend({ diff --git a/src/web/app/common/views/components/othello.vue b/src/web/app/common/views/components/othello.vue index f5abcfb103..f409f162f6 100644 --- a/src/web/app/common/views/components/othello.vue +++ b/src/web/app/common/views/components/othello.vue @@ -1,21 +1,53 @@
  • @@ -22,12 +29,6 @@

    %i18n:desktop.tags.mk-ui-header-nav.ch%

  • -
  • - - %fa:info% -

    %i18n:desktop.tags.mk-ui-header-nav.info%

    -
    -
  • @@ -36,11 +37,13 @@ import Vue from 'vue'; import { chUrl } from '../../../config'; import MkMessagingWindow from './messaging-window.vue'; +import MkGameWindow from './game-window.vue'; export default Vue.extend({ data() { return { hasUnreadMessagingMessages: false, + hasGameInvitations: false, connection: null, connectionId: null, chUrl @@ -80,6 +83,10 @@ export default Vue.extend({ messaging() { (this as any).os.new(MkMessagingWindow); + }, + + game() { + (this as any).os.new(MkGameWindow); } } }); diff --git a/src/web/app/desktop/views/widgets/channel.channel.vue b/src/web/app/desktop/views/widgets/channel.channel.vue index 70dac316cf..02cdf6de13 100644 --- a/src/web/app/desktop/views/widgets/channel.channel.vue +++ b/src/web/app/desktop/views/widgets/channel.channel.vue @@ -11,7 +11,7 @@ -- cgit v1.2.3-freya From 155012846d8f142515390adb2754b45cdfbb74ef Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 8 Mar 2018 17:57:57 +0900 Subject: #1200 --- src/api/endpoints/othello/games.ts | 9 +- src/api/endpoints/othello/match.ts | 20 +- src/api/models/othello-game.ts | 43 ++- src/api/stream/othello-game.ts | 140 +++++++-- src/common/othello.ts | 325 --------------------- src/common/othello/ai.ts | 42 +++ src/common/othello/core.ts | 239 +++++++++++++++ src/common/othello/maps.ts | 217 ++++++++++++++ .../app/common/views/components/othello.game.vue | 164 ++++++----- .../common/views/components/othello.gameroom.vue | 42 +++ .../app/common/views/components/othello.room.vue | 152 ++++++++++ src/web/app/common/views/components/othello.vue | 18 +- 12 files changed, 955 insertions(+), 456 deletions(-) delete mode 100644 src/common/othello.ts create mode 100644 src/common/othello/ai.ts create mode 100644 src/common/othello/core.ts create mode 100644 src/common/othello/maps.ts create mode 100644 src/web/app/common/views/components/othello.gameroom.vue create mode 100644 src/web/app/common/views/components/othello.room.vue (limited to 'src/api/models') diff --git a/src/api/endpoints/othello/games.ts b/src/api/endpoints/othello/games.ts index 39963fcd29..dd9fb5ef52 100644 --- a/src/api/endpoints/othello/games.ts +++ b/src/api/endpoints/othello/games.ts @@ -7,12 +7,15 @@ module.exports = (params, user) => new Promise(async (res, rej) => { if (myErr) return rej('invalid my param'); const q = my ? { + is_started: true, $or: [{ - black_user_id: user._id + user1_id: user._id }, { - white_user_id: user._id + user2_id: user._id }] - } : {}; + } : { + is_started: true + }; // Fetch games const games = await Game.find(q, { diff --git a/src/api/endpoints/othello/match.ts b/src/api/endpoints/othello/match.ts index cb094bbc65..05b87a541a 100644 --- a/src/api/endpoints/othello/match.ts +++ b/src/api/endpoints/othello/match.ts @@ -3,6 +3,7 @@ import Matching, { pack as packMatching } from '../../models/othello-matching'; import Game, { pack as packGame } from '../../models/othello-game'; import User from '../../models/user'; import { publishOthelloStream } from '../../event'; +import { eighteight } from '../../../common/othello/maps'; module.exports = (params, user) => new Promise(async (res, rej) => { // Get 'user_id' parameter @@ -26,16 +27,21 @@ module.exports = (params, user) => new Promise(async (res, rej) => { _id: exist._id }); - const parentIsBlack = Math.random() > 0.5; - - // Start game + // Create game const game = await Game.insert({ created_at: new Date(), - black_user_id: parentIsBlack ? exist.parent_id : user._id, - white_user_id: parentIsBlack ? user._id : exist.parent_id, - turn_user_id: parentIsBlack ? exist.parent_id : user._id, + user1_id: exist.parent_id, + user2_id: user._id, + user1_accepted: false, + user2_accepted: false, + is_started: false, is_ended: false, - logs: [] + logs: [], + settings: { + map: eighteight, + bw: 'random', + is_llotheo: false + } }); // Reponse diff --git a/src/api/models/othello-game.ts b/src/api/models/othello-game.ts index 73a5c94b21..de7c804c46 100644 --- a/src/api/models/othello-game.ts +++ b/src/api/models/othello-game.ts @@ -2,6 +2,7 @@ import * as mongo from 'mongodb'; import deepcopy = require('deepcopy'); import db from '../../db/mongodb'; import { IUser, pack as packUser } from './user'; +import { Map } from '../../common/othello/maps'; const Game = db.get('othello_games'); export default Game; @@ -9,12 +10,28 @@ export default Game; export interface IGame { _id: mongo.ObjectID; created_at: Date; - black_user_id: mongo.ObjectID; - white_user_id: mongo.ObjectID; - turn_user_id: mongo.ObjectID; + started_at: Date; + user1_id: mongo.ObjectID; + user2_id: mongo.ObjectID; + user1_accepted: boolean; + user2_accepted: boolean; + + /** + * どちらのプレイヤーが先行(黒)か + * 1 ... user1 + * 2 ... user2 + */ + black: number; + + is_started: boolean; is_ended: boolean; winner_id: mongo.ObjectID; logs: any[]; + settings: { + map: Map; + bw: string | number; + is_llotheo: boolean; + }; } /** @@ -24,6 +41,20 @@ export const pack = ( game: any, me?: string | mongo.ObjectID | IUser ) => new Promise(async (resolve, reject) => { + let _game: any; + + // Populate the game if 'game' is ID + if (mongo.ObjectID.prototype.isPrototypeOf(game)) { + _game = await Game.findOne({ + _id: game + }); + } else if (typeof game === 'string') { + _game = await Game.findOne({ + _id: new mongo.ObjectID(game) + }); + } else { + _game = deepcopy(game); + } // Me const meId: mongo.ObjectID = me @@ -34,15 +65,13 @@ export const pack = ( : (me as IUser)._id : null; - const _game = deepcopy(game); - // Rename _id to id _game.id = _game._id; delete _game._id; // Populate user - _game.black_user = await packUser(_game.black_user_id, meId); - _game.white_user = await packUser(_game.white_user_id, meId); + _game.user1 = await packUser(_game.user1_id, meId); + _game.user2 = await packUser(_game.user2_id, meId); if (_game.winner_id) { _game.winner = await packUser(_game.winner_id, meId); } else { diff --git a/src/api/stream/othello-game.ts b/src/api/stream/othello-game.ts index d086478159..1dcd37efa1 100644 --- a/src/api/stream/othello-game.ts +++ b/src/api/stream/othello-game.ts @@ -1,8 +1,8 @@ import * as websocket from 'websocket'; import * as redis from 'redis'; -import Game from '../models/othello-game'; +import Game, { pack } from '../models/othello-game'; import { publishOthelloGameStream } from '../event'; -import Othello from '../../common/othello'; +import Othello from '../../common/othello/core'; export default function(request: websocket.request, connection: websocket.connection, subscriber: redis.RedisClient, user: any): void { const gameId = request.resourceURL.query.game; @@ -17,6 +17,19 @@ export default function(request: websocket.request, connection: websocket.connec const msg = JSON.parse(data.utf8Data); switch (msg.type) { + case 'accept': + accept(true); + break; + + case 'cancel-accept': + accept(false); + break; + + case 'update-settings': + if (msg.settings == null) return; + updateSettings(msg.settings); + break; + case 'set': if (msg.pos == null) return; set(msg.pos); @@ -24,38 +37,118 @@ export default function(request: websocket.request, connection: websocket.connec } }); - async function set(pos) { + async function updateSettings(settings) { const game = await Game.findOne({ _id: gameId }); - if (game.is_ended) return; - if (!game.black_user_id.equals(user._id) && !game.white_user_id.equals(user._id)) return; - - const o = new Othello(); + if (game.is_started) return; + if (!game.user1_id.equals(user._id) && !game.user2_id.equals(user._id)) return; + if (game.user1_id.equals(user._id) && game.user1_accepted) return; + if (game.user2_id.equals(user._id) && game.user2_accepted) return; - game.logs.forEach(log => { - o.set(log.color, log.pos); + await Game.update({ _id: gameId }, { + $set: { + settings + } }); - const myColor = game.black_user_id.equals(user._id) ? 'black' : 'white'; - const opColor = myColor == 'black' ? 'white' : 'black'; + publishOthelloGameStream(gameId, 'update-settings', settings); + } + + async function accept(accept: boolean) { + const game = await Game.findOne({ _id: gameId }); + + if (game.is_started) return; + + let bothAccepted = false; + + if (game.user1_id.equals(user._id)) { + await Game.update({ _id: gameId }, { + $set: { + user1_accepted: accept + } + }); - if (!o.canReverse(myColor, pos)) return; - o.set(myColor, pos); + publishOthelloGameStream(gameId, 'change-accepts', { + user1: accept, + user2: game.user2_accepted + }); - let turn; - if (o.getPattern(opColor).length > 0) { - turn = myColor == 'black' ? game.white_user_id : game.black_user_id; - } else if (o.getPattern(myColor).length > 0) { - turn = myColor == 'black' ? game.black_user_id : game.white_user_id; + if (accept && game.user2_accepted) bothAccepted = true; + } else if (game.user2_id.equals(user._id)) { + await Game.update({ _id: gameId }, { + $set: { + user2_accepted: accept + } + }); + + publishOthelloGameStream(gameId, 'change-accepts', { + user1: game.user1_accepted, + user2: accept + }); + + if (accept && game.user1_accepted) bothAccepted = true; } else { - turn = null; + return; } - const isEnded = turn === null; + if (bothAccepted) { + // 3秒後、まだacceptされていたらゲーム開始 + setTimeout(async () => { + const freshGame = await Game.findOne({ _id: gameId }); + if (freshGame == null || freshGame.is_started || freshGame.is_ended) return; + + let bw: number; + if (freshGame.settings.bw == 'random') { + bw = Math.random() > 0.5 ? 1 : 2; + } else { + bw = freshGame.settings.bw as number; + } + + await Game.update({ _id: gameId }, { + $set: { + started_at: new Date(), + is_started: true, + black: bw + } + }); + + publishOthelloGameStream(gameId, 'started', await pack(gameId)); + }, 3000); + } + } + + async function set(pos) { + const game = await Game.findOne({ _id: gameId }); + + if (!game.is_started) return; + if (game.is_ended) return; + if (!game.user1_id.equals(user._id) && !game.user2_id.equals(user._id)) return; + + const o = new Othello(game.settings.map, { + isLlotheo: game.settings.is_llotheo + }); + + game.logs.forEach(log => { + o.put(log.color, log.pos); + }); + + const myColor = + (game.user1_id.equals(user._id) && game.black == 1) || (game.user2_id.equals(user._id) && game.black == 2) + ? 'black' + : 'white'; + + if (!o.canPut(myColor, pos)) return; + o.put(myColor, pos); let winner; - if (isEnded) { - winner = o.blackCount == o.whiteCount ? null : o.blackCount > o.whiteCount ? game.black_user_id : game.white_user_id; + if (o.isEnded) { + if (o.winner == 'black') { + winner = game.black == 1 ? game.user1_id : game.user2_id; + } else if (o.winner == 'white') { + winner = game.black == 1 ? game.user2_id : game.user1_id; + } else { + winner = null; + } } const log = { @@ -68,8 +161,7 @@ export default function(request: websocket.request, connection: websocket.connec _id: gameId }, { $set: { - turn_user_id: turn, - is_ended: isEnded, + is_ended: o.isEnded, winner_id: winner }, $push: { diff --git a/src/common/othello.ts b/src/common/othello.ts deleted file mode 100644 index 1da8ad36da..0000000000 --- a/src/common/othello.ts +++ /dev/null @@ -1,325 +0,0 @@ -const BOARD_SIZE = 8; - -export default class Othello { - public board: Array<'black' | 'white'>; - - public stats: Array<{ - b: number; - w: number; - }> = []; - - /** - * ゲームを初期化します - */ - constructor() { - this.board = [ - null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, - null, null, null, 'white', 'black', null, null, null, - null, null, null, 'black', 'white', null, null, null, - null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null - ]; - - this.stats.push({ - b: 0.5, - w: 0.5 - }); - } - - public prevPos = -1; - - public get blackCount() { - return this.board.filter(s => s == 'black').length; - } - - public get whiteCount() { - return this.board.filter(s => s == 'white').length; - } - - public get blackP() { - return this.blackCount / (this.blackCount + this.whiteCount); - } - - public get whiteP() { - return this.whiteCount / (this.blackCount + this.whiteCount); - } - - public setByNumber(color, n) { - const ps = this.getPattern(color); - this.set2(color, ps[n][0], ps[n][1]); - } - - private write(color, x, y) { - const pos = x + (y * 8); - this.board[pos] = color; - } - - /** - * 石を配置します - */ - public set2(color, x, y) { - this.prevPos = x + (y * 8); - this.write(color, x, y); - - const reverses = this.getReverse(color, x, y); - - reverses.forEach(r => { - switch (r[0]) { - case 0: // 上 - for (let c = 0, _y = y - 1; c < r[1]; c++, _y--) { - this.write(color, x, _y); - } - break; - - case 1: // 右上 - for (let c = 0, i = 1; c < r[1]; c++, i++) { - this.write(color, x + i, y - i); - } - break; - - case 2: // 右 - for (let c = 0, _x = x + 1; c < r[1]; c++, _x++) { - this.write(color, _x, y); - } - break; - - case 3: // 右下 - for (let c = 0, i = 1; c < r[1]; c++, i++) { - this.write(color, x + i, y + i); - } - break; - - case 4: // 下 - for (let c = 0, _y = y + 1; c < r[1]; c++, _y++) { - this.write(color, x, _y); - } - break; - - case 5: // 左下 - for (let c = 0, i = 1; c < r[1]; c++, i++) { - this.write(color, x - i, y + i); - } - break; - - case 6: // 左 - for (let c = 0, _x = x - 1; c < r[1]; c++, _x--) { - this.write(color, _x, y); - } - break; - - case 7: // 左上 - for (let c = 0, i = 1; c < r[1]; c++, i++) { - this.write(color, x - i, y - i); - } - break; - } - }); - - this.stats.push({ - b: this.blackP, - w: this.whiteP - }); - } - - public set(color, pos) { - const x = pos % BOARD_SIZE; - const y = Math.floor(pos / BOARD_SIZE); - this.set2(color, x, y); - } - - public get(x, y) { - const pos = x + (y * 8); - return this.board[pos]; - } - - /** - * 打つことができる場所を取得します - */ - public getPattern(myColor): number[][] { - const result = []; - this.board.forEach((stone, i) => { - if (stone != null) return; - const x = i % BOARD_SIZE; - const y = Math.floor(i / BOARD_SIZE); - if (this.canReverse2(myColor, x, y)) result.push([x, y]); - }); - return result; - } - - /** - * 指定の位置に石を打つことができるかどうか(相手の石を1つでも反転させられるか)を取得します - */ - public canReverse2(myColor, x, y): boolean { - return this.canReverse(myColor, x + (y * 8)); - } - public canReverse(myColor, pos): boolean { - if (this.board[pos] != null) return false; - const x = pos % BOARD_SIZE; - const y = Math.floor(pos / BOARD_SIZE); - return this.getReverse(myColor, x, y) !== null; - } - - private getReverse(myColor, targetx, targety): number[] { - const opponentColor = myColor == 'black' ? 'white' : 'black'; - - const createIterater = () => { - let opponentStoneFound = false; - let breaked = false; - return (x, y): any => { - if (breaked) { - return; - } else if (this.get(x, y) == myColor && opponentStoneFound) { - return true; - } else if (this.get(x, y) == myColor && !opponentStoneFound) { - breaked = true; - } else if (this.get(x, y) == opponentColor) { - opponentStoneFound = true; - } else { - breaked = true; - } - }; - }; - - const res = []; - - let iterate; - - // 上 - iterate = createIterater(); - for (let c = 0, y = targety - 1; y >= 0; c++, y--) { - if (iterate(targetx, y)) { - res.push([0, c]); - break; - } - } - - // 右上 - iterate = createIterater(); - for (let c = 0, i = 1; i <= Math.min(BOARD_SIZE - targetx, targety); c++, i++) { - if (iterate(targetx + i, targety - i)) { - res.push([1, c]); - break; - } - } - - // 右 - iterate = createIterater(); - for (let c = 0, x = targetx + 1; x < BOARD_SIZE; c++, x++) { - if (iterate(x, targety)) { - res.push([2, c]); - break; - } - } - - // 右下 - iterate = createIterater(); - for (let c = 0, i = 1; i < Math.min(BOARD_SIZE - targetx, BOARD_SIZE - targety); c++, i++) { - if (iterate(targetx + i, targety + i)) { - res.push([3, c]); - break; - } - } - - // 下 - iterate = createIterater(); - for (let c = 0, y = targety + 1; y < BOARD_SIZE; c++, y++) { - if (iterate(targetx, y)) { - res.push([4, c]); - break; - } - } - - // 左下 - iterate = createIterater(); - for (let c = 0, i = 1; i <= Math.min(targetx, BOARD_SIZE - targety); c++, i++) { - if (iterate(targetx - i, targety + i)) { - res.push([5, c]); - break; - } - } - - // 左 - iterate = createIterater(); - for (let c = 0, x = targetx - 1; x >= 0; c++, x--) { - if (iterate(x, targety)) { - res.push([6, c]); - break; - } - } - - // 左上 - iterate = createIterater(); - for (let c = 0, i = 1; i <= Math.min(targetx, targety); c++, i++) { - if (iterate(targetx - i, targety - i)) { - res.push([7, c]); - break; - } - } - - return res.length === 0 ? null : res; - } - - public toString(): string { - //return this.board.map(row => row.map(state => state === 'black' ? '●' : state === 'white' ? '○' : '┼').join('')).join('\n'); - //return this.board.map(row => row.map(state => state === 'black' ? '⚫️' : state === 'white' ? '⚪️' : '🔹').join('')).join('\n'); - return 'wip'; - } - - public toPatternString(color): string { - //const num = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; - /*const num = ['0️⃣', '1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣', '6️⃣', '7️⃣', '8️⃣', '9️⃣', '🔟', '🍏', '🍎', '🍐', '🍊', '🍋', '🍌', '🍉', '🍇', '🍓', '🍈', '🍒', '🍑', '🍍']; - - const pattern = this.getPattern(color); - - return this.board.map((row, y) => row.map((state, x) => { - const i = pattern.findIndex(p => p[0] == x && p[1] == y); - //return state === 'black' ? '●' : state === 'white' ? '○' : i != -1 ? num[i] : '┼'; - return state === 'black' ? '⚫️' : state === 'white' ? '⚪️' : i != -1 ? num[i] : '🔹'; - }).join('')).join('\n');*/ - - return 'wip'; - } -} - -export function ai(color: string, othello: Othello) { - const opponentColor = color == 'black' ? 'white' : 'black'; - - function think() { - // 打てる場所を取得 - const ps = othello.getPattern(color); - - if (ps.length > 0) { // 打てる場所がある場合 - // 角を取得 - const corners = ps.filter(p => - // 左上 - (p[0] == 0 && p[1] == 0) || - // 右上 - (p[0] == (BOARD_SIZE - 1) && p[1] == 0) || - // 右下 - (p[0] == (BOARD_SIZE - 1) && p[1] == (BOARD_SIZE - 1)) || - // 左下 - (p[0] == 0 && p[1] == (BOARD_SIZE - 1)) - ); - - if (corners.length > 0) { // どこかしらの角に打てる場合 - // 打てる角からランダムに選択して打つ - const p = corners[Math.floor(Math.random() * corners.length)]; - othello.set(color, p[0], p[1]); - } else { // 打てる角がない場合 - // 打てる場所からランダムに選択して打つ - const p = ps[Math.floor(Math.random() * ps.length)]; - othello.set(color, p[0], p[1]); - } - - // 相手の打つ場所がない場合続けてAIのターン - if (othello.getPattern(opponentColor).length === 0) { - think(); - } - } - } - - think(); -} diff --git a/src/common/othello/ai.ts b/src/common/othello/ai.ts new file mode 100644 index 0000000000..3943d04bf7 --- /dev/null +++ b/src/common/othello/ai.ts @@ -0,0 +1,42 @@ +import Othello, { Color } from './core'; + +export function ai(color: Color, othello: Othello) { + //const opponentColor = color == 'black' ? 'white' : 'black'; +/* wip + + function think() { + // 打てる場所を取得 + const ps = othello.canPutSomewhere(color); + + if (ps.length > 0) { // 打てる場所がある場合 + // 角を取得 + const corners = ps.filter(p => + // 左上 + (p[0] == 0 && p[1] == 0) || + // 右上 + (p[0] == (BOARD_SIZE - 1) && p[1] == 0) || + // 右下 + (p[0] == (BOARD_SIZE - 1) && p[1] == (BOARD_SIZE - 1)) || + // 左下 + (p[0] == 0 && p[1] == (BOARD_SIZE - 1)) + ); + + if (corners.length > 0) { // どこかしらの角に打てる場合 + // 打てる角からランダムに選択して打つ + const p = corners[Math.floor(Math.random() * corners.length)]; + othello.set(color, p[0], p[1]); + } else { // 打てる角がない場合 + // 打てる場所からランダムに選択して打つ + const p = ps[Math.floor(Math.random() * ps.length)]; + othello.set(color, p[0], p[1]); + } + + // 相手の打つ場所がない場合続けてAIのターン + if (othello.getPattern(opponentColor).length === 0) { + think(); + } + } + } + + think();*/ +} diff --git a/src/common/othello/core.ts b/src/common/othello/core.ts new file mode 100644 index 0000000000..b765860313 --- /dev/null +++ b/src/common/othello/core.ts @@ -0,0 +1,239 @@ +import { Map } from './maps'; + +export type Color = 'black' | 'white'; +export type MapPixel = 'null' | 'empty'; + +export type Options = { + isLlotheo: boolean; +}; + +/** + * オセロエンジン + */ +export default class Othello { + public map: Map; + public mapData: MapPixel[]; + public board: Color[]; + public turn: Color = 'black'; + public opts: Options; + + public stats: Array<{ + b: number; + w: number; + }>; + + /** + * ゲームを初期化します + */ + constructor(map: Map, opts: Options) { + this.map = map; + this.opts = opts; + + // Parse map data + this.board = this.map.data.split('').map(d => { + if (d == '-') return null; + if (d == 'b') return 'black'; + if (d == 'w') return 'white'; + return undefined; + }); + this.mapData = this.map.data.split('').map(d => { + if (d == '-' || d == 'b' || d == 'w') return 'empty'; + return 'null'; + }); + + // Init stats + this.stats = [{ + b: this.blackP, + w: this.whiteP + }]; + } + + public prevPos = -1; + + /** + * 黒石の数 + */ + public get blackCount() { + return this.board.filter(x => x == 'black').length; + } + + /** + * 白石の数 + */ + public get whiteCount() { + return this.board.filter(x => x == 'white').length; + } + + /** + * 黒石の比率 + */ + public get blackP() { + return this.blackCount / (this.blackCount + this.whiteCount); + } + + /** + * 白石の比率 + */ + public get whiteP() { + return this.whiteCount / (this.blackCount + this.whiteCount); + } + + public transformPosToXy(pos: number): number[] { + const x = pos % this.map.size; + const y = Math.floor(pos / this.map.size); + return [x, y]; + } + + public transformXyToPos(x: number, y: number): number { + return x + (y * this.map.size); + } + + /** + * 指定のマスに石を書き込みます + * @param color 石の色 + * @param pos 位置 + */ + private write(color: Color, pos: number) { + this.board[pos] = color; + } + + /** + * 指定のマスに石を打ちます + * @param color 石の色 + * @param pos 位置 + */ + public put(color: Color, pos: number) { + if (!this.canPut(color, pos)) return; + + this.prevPos = pos; + this.write(color, pos); + + // 反転させられる石を取得 + const reverses = this.effects(color, pos); + + // 反転させる + reverses.forEach(pos => { + this.write(color, pos); + }); + + this.stats.push({ + b: this.blackP, + w: this.whiteP + }); + + // ターン計算 + const opColor = color == 'black' ? 'white' : 'black'; + if (this.canPutSomewhere(opColor).length > 0) { + this.turn = color == 'black' ? 'white' : 'black'; + } else if (this.canPutSomewhere(color).length > 0) { + this.turn = color == 'black' ? 'black' : 'white'; + } else { + this.turn = null; + } + } + + /** + * 指定したマスの状態を取得します + * @param pos 位置 + */ + public get(pos: number) { + return this.board[pos]; + } + + /** + * 指定した位置のマップデータのマスを取得します + * @param pos 位置 + */ + public mapDataGet(pos: number): MapPixel { + if (pos < 0 || pos >= this.mapData.length) return 'null'; + return this.mapData[pos]; + } + + /** + * 打つことができる場所を取得します + */ + public canPutSomewhere(color: Color): number[] { + const result = []; + + this.board.forEach((x, i) => { + if (this.canPut(color, i)) result.push(i); + }); + + return result; + } + + /** + * 指定のマスに石を打つことができるかどうか(相手の石を1つでも反転させられるか)を取得します + * @param color 自分の色 + * @param pos 位置 + */ + public canPut(color: Color, pos: number): boolean { + // 既に石が置いてある場所には打てない + if (this.get(pos) !== null) return false; + return this.effects(color, pos).length !== 0; + } + + /** + * 指定のマスに石を置いた時の、反転させられる石を取得します + * @param color 自分の色 + * @param pos 位置 + */ + private effects(color: Color, pos: number): number[] { + const enemyColor = color == 'black' ? 'white' : 'black'; + const [x, y] = this.transformPosToXy(pos); + let stones = []; + + const iterate = (fn: (i: number) => number[]) => { + let i = 1; + const found = []; + while (true) { + const [x, y] = fn(i); + if (x < 0 || y < 0 || x >= this.map.size || y >= this.map.size) break; + const pos = this.transformXyToPos(x, y); + const pixel = this.mapDataGet(pos); + if (pixel == 'null') break; + const stone = this.get(pos); + if (stone == null) break; + if (stone == enemyColor) found.push(pos); + if (stone == color) { + stones = stones.concat(found); + break; + } + i++; + } + }; + + iterate(i => [x , y - i]); // 上 + iterate(i => [x + i, y - i]); // 右上 + iterate(i => [x + i, y ]); // 右 + iterate(i => [x + i, y + i]); // 右下 + iterate(i => [x , y + i]); // 下 + iterate(i => [x - i, y + i]); // 左下 + iterate(i => [x - i, y ]); // 左 + iterate(i => [x - i, y - i]); // 左上 + + return stones; + } + + /** + * ゲームが終了したか否か + */ + public get isEnded(): boolean { + return this.turn === null; + } + + /** + * ゲームの勝者 (null = 引き分け) + */ + public get winner(): Color { + if (!this.isEnded) return undefined; + + if (this.blackCount == this.whiteCount) return null; + + if (this.opts.isLlotheo) { + return this.blackCount > this.whiteCount ? 'white' : 'black'; + } else { + return this.blackCount > this.whiteCount ? 'black' : 'white'; + } + } +} diff --git a/src/common/othello/maps.ts b/src/common/othello/maps.ts new file mode 100644 index 0000000000..e6f3f409ef --- /dev/null +++ b/src/common/othello/maps.ts @@ -0,0 +1,217 @@ +/** + * 組み込みマップ定義 + * + * データ値: + * (スペース) ... マス無し + * - ... マス + * b ... 初期配置される黒石 + * w ... 初期配置される白石 + */ + +export type Map = { + name?: string; + size: number; + data: string; +}; + +export const fourfour: Map = { + name: '4x4', + size: 4, + data: + '----' + + '-wb-' + + '-bw-' + + '----' +}; + +export const sixsix: Map = { + name: '6x6', + size: 6, + data: + '------' + + '------' + + '--wb--' + + '--bw--' + + '------' + + '------' +}; + +export const eighteight: Map = { + name: '8x8', + size: 8, + data: + '--------' + + '--------' + + '--------' + + '---wb---' + + '---bw---' + + '--------' + + '--------' + + '--------' +}; + +export const roundedEighteight: Map = { + name: '8x8 rounded', + size: 8, + data: + ' ------ ' + + '--------' + + '--------' + + '---wb---' + + '---bw---' + + '--------' + + '--------' + + ' ------ ' +}; + +export const roundedEighteight2: Map = { + name: '8x8 rounded 2', + size: 8, + data: + ' ---- ' + + ' ------ ' + + '--------' + + '---wb---' + + '---bw---' + + '--------' + + ' ------ ' + + ' ---- ' +}; + +export const eighteightWithNotch: Map = { + name: '8x8 with notch', + size: 8, + data: + '--- ---' + + '--------' + + '--------' + + ' --wb-- ' + + ' --bw-- ' + + '--------' + + '--------' + + '--- ---' +}; + +export const eighteightWithSomeHoles: Map = { + name: '8x8 with some holes', + size: 8, + data: + '--- ----' + + '----- --' + + '-- -----' + + '---wb---' + + '---bw- -' + + ' -------' + + '--- ----' + + '--------' +}; + +export const sixeight: Map = { + name: '6x8', + size: 8, + data: + ' ------ ' + + ' ------ ' + + ' ------ ' + + ' --wb-- ' + + ' --bw-- ' + + ' ------ ' + + ' ------ ' + + ' ------ ' +}; + +export const tenthtenth: Map = { + name: '10x10', + size: 10, + data: + '----------' + + '----------' + + '----------' + + '----------' + + '----wb----' + + '----bw----' + + '----------' + + '----------' + + '----------' + + '----------' +}; + +export const hole: Map = { + name: 'hole', + size: 10, + data: + '----------' + + '----------' + + '--wb--wb--' + + '--bw--bw--' + + '---- ----' + + '---- ----' + + '--wb--wb--' + + '--bw--bw--' + + '----------' + + '----------' +}; + +export const spark: Map = { + name: 'spark', + size: 10, + data: + ' - - ' + + '----------' + + ' -------- ' + + ' -------- ' + + ' ---wb--- ' + + ' ---bw--- ' + + ' -------- ' + + ' -------- ' + + '----------' + + ' - - ' +}; + +export const islands: Map = { + name: 'islands', + size: 10, + data: + '-------- ' + + '---wb--- ' + + '---bw--- ' + + '-------- ' + + ' - - ' + + ' - - ' + + ' --------' + + ' ---bw---' + + ' ---wb---' + + ' --------' +}; + +export const grid: Map = { + name: 'grid', + size: 10, + data: + '----------' + + '- - -- - -' + + '----------' + + '- - -- - -' + + '----wb----' + + '----bw----' + + '- - -- - -' + + '----------' + + '- - -- - -' + + '----------' +}; + +export const iphonex: Map = { + name: 'iPhone X', + size: 10, + data: + ' -- -- ' + + ' -------- ' + + ' -------- ' + + ' -------- ' + + ' ---wb--- ' + + ' ---bw--- ' + + ' -------- ' + + ' -------- ' + + ' -------- ' + + ' ------ ' +}; diff --git a/src/web/app/common/views/components/othello.game.vue b/src/web/app/common/views/components/othello.game.vue index 1cb2400f7e..2ef6b645ce 100644 --- a/src/web/app/common/views/components/othello.game.vue +++ b/src/web/app/common/views/components/othello.game.vue @@ -1,23 +1,27 @@