summaryrefslogtreecommitdiff
path: root/src/server/api
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/api')
-rw-r--r--src/server/api/common/read-messaging-message.ts9
-rw-r--r--src/server/api/common/read-notification.ts7
-rw-r--r--src/server/api/common/signin.ts4
-rw-r--r--src/server/api/endpoints/ap/show.ts10
-rw-r--r--src/server/api/endpoints/drive/files/check_existence.ts3
-rw-r--r--src/server/api/endpoints/drive/files/show.ts3
-rw-r--r--src/server/api/endpoints/drive/files/update.ts6
-rw-r--r--src/server/api/endpoints/i/read_all_unread_notes.ts36
-rw-r--r--src/server/api/endpoints/i/update.ts9
-rw-r--r--src/server/api/endpoints/meta.ts23
-rw-r--r--src/server/api/endpoints/notes/global-timeline.ts2
-rw-r--r--src/server/api/endpoints/notes/hybrid-timeline.ts2
-rw-r--r--src/server/api/endpoints/notes/local-timeline.ts2
-rw-r--r--src/server/api/endpoints/notes/mentions.ts2
-rw-r--r--src/server/api/endpoints/notes/timeline.ts2
-rw-r--r--src/server/api/endpoints/notes/user-list-timeline.ts2
-rw-r--r--src/server/api/endpoints/users/notes.ts18
-rw-r--r--src/server/api/endpoints/users/recommendation.ts92
-rw-r--r--src/server/api/index.ts4
-rw-r--r--src/server/api/limitter.ts6
-rw-r--r--src/server/api/mastodon.ts14
-rw-r--r--src/server/api/private/signin.ts5
-rw-r--r--src/server/api/private/signup.ts10
-rw-r--r--src/server/api/service/github.ts1
-rw-r--r--src/server/api/service/twitter.ts2
-rw-r--r--src/server/api/stream/channel.ts2
-rw-r--r--src/server/api/stream/channels/drive.ts3
-rw-r--r--src/server/api/stream/channels/games/reversi-game.ts16
-rw-r--r--src/server/api/stream/channels/games/reversi.ts3
-rw-r--r--src/server/api/stream/channels/global-timeline.ts3
-rw-r--r--src/server/api/stream/channels/hashtag.ts3
-rw-r--r--src/server/api/stream/channels/home-timeline.ts3
-rw-r--r--src/server/api/stream/channels/hybrid-timeline.ts3
-rw-r--r--src/server/api/stream/channels/local-timeline.ts3
-rw-r--r--src/server/api/stream/channels/main.ts3
-rw-r--r--src/server/api/stream/channels/messaging-index.ts3
-rw-r--r--src/server/api/stream/channels/messaging.ts3
-rw-r--r--src/server/api/stream/channels/notes-stats.ts3
-rw-r--r--src/server/api/stream/channels/server-stats.ts3
-rw-r--r--src/server/api/stream/channels/user-list.ts3
-rw-r--r--src/server/api/stream/index.ts30
-rw-r--r--src/server/api/streaming.ts45
42 files changed, 318 insertions, 88 deletions
diff --git a/src/server/api/common/read-messaging-message.ts b/src/server/api/common/read-messaging-message.ts
index 075e369832..63080d22a4 100644
--- a/src/server/api/common/read-messaging-message.ts
+++ b/src/server/api/common/read-messaging-message.ts
@@ -1,4 +1,5 @@
import * as mongo from 'mongodb';
+import isObjectId from '../../../misc/is-objectid';
import Message from '../../../models/messaging-message';
import { IMessagingMessage as IMessage } from '../../../models/messaging-message';
import { publishMainStream } from '../../../stream';
@@ -15,21 +16,21 @@ export default (
message: string | string[] | IMessage | IMessage[] | mongo.ObjectID | mongo.ObjectID[]
) => new Promise<any>(async (resolve, reject) => {
- const userId = mongo.ObjectID.prototype.isPrototypeOf(user)
+ const userId = isObjectId(user)
? user
: new mongo.ObjectID(user);
- const otherpartyId = mongo.ObjectID.prototype.isPrototypeOf(otherparty)
+ const otherpartyId = isObjectId(otherparty)
? otherparty
: new mongo.ObjectID(otherparty);
const ids: mongo.ObjectID[] = Array.isArray(message)
- ? mongo.ObjectID.prototype.isPrototypeOf(message[0])
+ ? isObjectId(message[0])
? (message as mongo.ObjectID[])
: typeof message[0] === 'string'
? (message as string[]).map(m => new mongo.ObjectID(m))
: (message as IMessage[]).map(m => m._id)
- : mongo.ObjectID.prototype.isPrototypeOf(message)
+ : isObjectId(message)
? [(message as mongo.ObjectID)]
: typeof message === 'string'
? [new mongo.ObjectID(message)]
diff --git a/src/server/api/common/read-notification.ts b/src/server/api/common/read-notification.ts
index 2d58ada4ce..27d3f1be32 100644
--- a/src/server/api/common/read-notification.ts
+++ b/src/server/api/common/read-notification.ts
@@ -1,4 +1,5 @@
import * as mongo from 'mongodb';
+import isObjectId from '../../../misc/is-objectid';
import { default as Notification, INotification } from '../../../models/notification';
import { publishMainStream } from '../../../stream';
import Mute from '../../../models/mute';
@@ -12,17 +13,17 @@ export default (
message: string | string[] | INotification | INotification[] | mongo.ObjectID | mongo.ObjectID[]
) => new Promise<any>(async (resolve, reject) => {
- const userId = mongo.ObjectID.prototype.isPrototypeOf(user)
+ const userId = isObjectId(user)
? user
: new mongo.ObjectID(user);
const ids: mongo.ObjectID[] = Array.isArray(message)
- ? mongo.ObjectID.prototype.isPrototypeOf(message[0])
+ ? isObjectId(message[0])
? (message as mongo.ObjectID[])
: typeof message[0] === 'string'
? (message as string[]).map(m => new mongo.ObjectID(m))
: (message as INotification[]).map(m => m._id)
- : mongo.ObjectID.prototype.isPrototypeOf(message)
+ : isObjectId(message)
? [(message as mongo.ObjectID)]
: typeof message === 'string'
? [new mongo.ObjectID(message)]
diff --git a/src/server/api/common/signin.ts b/src/server/api/common/signin.ts
index 44e1336f27..8d44b377fe 100644
--- a/src/server/api/common/signin.ts
+++ b/src/server/api/common/signin.ts
@@ -8,7 +8,9 @@ export default function(ctx: Koa.Context, user: ILocalUser, redirect = false) {
ctx.cookies.set('i', user.token, {
path: '/',
domain: config.hostname,
- secure: config.url.startsWith('https'),
+ // SEE: https://github.com/koajs/koa/issues/974
+ //secure: config.url.startsWith('https'),
+ secure: false,
httpOnly: false,
expires: new Date(Date.now() + expires),
maxAge: expires
diff --git a/src/server/api/endpoints/ap/show.ts b/src/server/api/endpoints/ap/show.ts
index 1f390d01aa..6cbd4ef87e 100644
--- a/src/server/api/endpoints/ap/show.ts
+++ b/src/server/api/endpoints/ap/show.ts
@@ -24,15 +24,15 @@ export const meta = {
},
};
-export default (params: any) => new Promise(async (res, rej) => {
+export default async (params: any) => {
const [ps, psErr] = getParams(meta, params);
- if (psErr) return rej(psErr);
+ if (psErr) throw psErr;
const object = await fetchAny(ps.uri);
- if (object !== null) return res(object);
+ if (object !== null) return object;
- return rej('object not found');
-});
+ throw new Error('object not found');
+};
/***
* URIからUserかNoteを解決する
diff --git a/src/server/api/endpoints/drive/files/check_existence.ts b/src/server/api/endpoints/drive/files/check_existence.ts
index 73d75b7caf..a024701655 100644
--- a/src/server/api/endpoints/drive/files/check_existence.ts
+++ b/src/server/api/endpoints/drive/files/check_existence.ts
@@ -27,7 +27,8 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
const file = await DriveFile.findOne({
md5: md5,
- 'metadata.userId': user._id
+ 'metadata.userId': user._id,
+ 'metadata.deletedAt': { $exists: false }
});
if (file === null) {
diff --git a/src/server/api/endpoints/drive/files/show.ts b/src/server/api/endpoints/drive/files/show.ts
index 718fb8c2d7..e35c5e3ca2 100644
--- a/src/server/api/endpoints/drive/files/show.ts
+++ b/src/server/api/endpoints/drive/files/show.ts
@@ -22,7 +22,8 @@ export default async (params: any, user: ILocalUser) => {
const file = await DriveFile
.findOne({
_id: fileId,
- 'metadata.userId': user._id
+ 'metadata.userId': user._id,
+ 'metadata.deletedAt': { $exists: false }
});
if (file === null) {
diff --git a/src/server/api/endpoints/drive/files/update.ts b/src/server/api/endpoints/drive/files/update.ts
index 3c7932c341..9b8c53caf7 100644
--- a/src/server/api/endpoints/drive/files/update.ts
+++ b/src/server/api/endpoints/drive/files/update.ts
@@ -100,8 +100,10 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
}).then(notes => {
notes.forEach(note => {
note._files[note._files.findIndex(f => f._id.equals(file._id))] = file;
- Note.findOneAndUpdate({ _id: note._id }, {
- _files: note._files
+ Note.update({ _id: note._id }, {
+ $set: {
+ _files: note._files
+ }
});
});
});
diff --git a/src/server/api/endpoints/i/read_all_unread_notes.ts b/src/server/api/endpoints/i/read_all_unread_notes.ts
new file mode 100644
index 0000000000..fae98b9816
--- /dev/null
+++ b/src/server/api/endpoints/i/read_all_unread_notes.ts
@@ -0,0 +1,36 @@
+import User, { ILocalUser } from '../../../../models/user';
+import { publishMainStream } from '../../../../stream';
+import NoteUnread from '../../../../models/note-unread';
+
+export const meta = {
+ desc: {
+ 'ja-JP': '未読の投稿をすべて既読にします。'
+ },
+
+ requireCredential: true,
+
+ kind: 'account-write',
+
+ params: {
+ }
+};
+
+export default async (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
+ // Remove documents
+ await NoteUnread.remove({
+ userId: user._id
+ });
+
+ User.update({ _id: user._id }, {
+ $set: {
+ hasUnreadMentions: false,
+ hasUnreadSpecifiedNotes: false
+ }
+ });
+
+ // 全て既読になったイベントを発行
+ publishMainStream(user._id, 'readAllUnreadMentions');
+ publishMainStream(user._id, 'readAllUnreadSpecifiedNotes');
+
+ res();
+});
diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts
index 548ce5cadb..7b8431f0ee 100644
--- a/src/server/api/endpoints/i/update.ts
+++ b/src/server/api/endpoints/i/update.ts
@@ -67,6 +67,12 @@ export const meta = {
}
}),
+ carefulBot: $.bool.optional.note({
+ desc: {
+ 'ja-JP': 'Botからのフォローを承認制にするか'
+ }
+ }),
+
isBot: $.bool.optional.note({
desc: {
'ja-JP': 'Botか否か'
@@ -95,7 +101,7 @@ export const meta = {
export default async (params: any, user: ILocalUser, app: IApp) => new Promise(async (res, rej) => {
const [ps, psErr] = getParams(meta, params);
- if (psErr) throw psErr;
+ if (psErr) return rej(psErr);
const isSecure = user != null && app == null;
@@ -110,6 +116,7 @@ export default async (params: any, user: ILocalUser, app: IApp) => new Promise(a
if (ps.wallpaperId !== undefined) updates.wallpaperId = ps.wallpaperId;
if (typeof ps.isLocked == 'boolean') updates.isLocked = ps.isLocked;
if (typeof ps.isBot == 'boolean') updates.isBot = ps.isBot;
+ if (typeof ps.carefulBot == 'boolean') updates.carefulBot = ps.carefulBot;
if (typeof ps.isCat == 'boolean') updates.isCat = ps.isCat;
if (typeof ps.autoWatch == 'boolean') updates['settings.autoWatch'] = ps.autoWatch;
if (typeof ps.alwaysMarkNsfw == 'boolean') updates['settings.alwaysMarkNsfw'] = ps.alwaysMarkNsfw;
diff --git a/src/server/api/endpoints/meta.ts b/src/server/api/endpoints/meta.ts
index c76d7f2e8f..ecd5817581 100644
--- a/src/server/api/endpoints/meta.ts
+++ b/src/server/api/endpoints/meta.ts
@@ -1,6 +1,3 @@
-/**
- * Module dependencies
- */
import * as os from 'os';
import config from '../../../config';
import Meta from '../../../models/meta';
@@ -9,9 +6,17 @@ import { ILocalUser } from '../../../models/user';
const pkg = require('../../../../package.json');
const client = require('../../../../built/client/meta.json');
-/**
- * Show core info
- */
+export const meta = {
+ desc: {
+ 'ja-JP': 'インスタンス情報を取得します。',
+ 'en-US': 'Get the information of this instance.'
+ },
+
+ requireCredential: false,
+
+ params: {},
+};
+
export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => {
const meta: any = (await Meta.findOne()) || {};
@@ -28,10 +33,12 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
machine: os.hostname(),
os: os.platform(),
node: process.version,
+
cpu: {
model: os.cpus()[0].model,
cores: os.cpus().length
},
+
broadcasts: meta.broadcasts || [],
disableRegistration: meta.disableRegistration,
disableLocalTimeline: meta.disableLocalTimeline,
@@ -40,6 +47,7 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
swPublickey: config.sw ? config.sw.public_key : null,
hidedTags: (me && me.isAdmin) ? meta.hidedTags : undefined,
bannerUrl: meta.bannerUrl,
+
features: {
registration: !meta.disableRegistration,
localTimeLine: !meta.disableLocalTimeline,
@@ -47,7 +55,8 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
recaptcha: config.recaptcha ? true : false,
objectStorage: config.drive && config.drive.storage === 'minio',
twitter: config.twitter ? true : false,
- serviceWorker: config.sw ? true : false
+ serviceWorker: config.sw ? true : false,
+ userRecommendation: config.user_recommendation ? config.user_recommendation : {}
}
});
});
diff --git a/src/server/api/endpoints/notes/global-timeline.ts b/src/server/api/endpoints/notes/global-timeline.ts
index 8362143bb2..8a6c848943 100644
--- a/src/server/api/endpoints/notes/global-timeline.ts
+++ b/src/server/api/endpoints/notes/global-timeline.ts
@@ -58,6 +58,8 @@ export default async (params: any, user: ILocalUser) => {
};
const query = {
+ deletedAt: null,
+
// public only
visibility: 'public',
diff --git a/src/server/api/endpoints/notes/hybrid-timeline.ts b/src/server/api/endpoints/notes/hybrid-timeline.ts
index 14b4432b33..b2ea9c60ac 100644
--- a/src/server/api/endpoints/notes/hybrid-timeline.ts
+++ b/src/server/api/endpoints/notes/hybrid-timeline.ts
@@ -129,6 +129,8 @@ export default async (params: any, user: ILocalUser) => {
const query = {
$and: [{
+ deletedAt: null,
+
$or: [{
// フォローしている人の投稿
$or: followQuery
diff --git a/src/server/api/endpoints/notes/local-timeline.ts b/src/server/api/endpoints/notes/local-timeline.ts
index 8ab07d8ea7..510564129c 100644
--- a/src/server/api/endpoints/notes/local-timeline.ts
+++ b/src/server/api/endpoints/notes/local-timeline.ts
@@ -71,6 +71,8 @@ export default async (params: any, user: ILocalUser) => {
};
const query = {
+ deletedAt: null,
+
// public only
visibility: 'public',
diff --git a/src/server/api/endpoints/notes/mentions.ts b/src/server/api/endpoints/notes/mentions.ts
index 592a94263d..27b6245eb6 100644
--- a/src/server/api/endpoints/notes/mentions.ts
+++ b/src/server/api/endpoints/notes/mentions.ts
@@ -45,6 +45,8 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
// Construct query
const query = {
+ deletedAt: null,
+
$or: [{
mentions: user._id
}, {
diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts
index 44a504eb18..31a4978407 100644
--- a/src/server/api/endpoints/notes/timeline.ts
+++ b/src/server/api/endpoints/notes/timeline.ts
@@ -132,6 +132,8 @@ export default async (params: any, user: ILocalUser) => {
const query = {
$and: [{
+ deletedAt: null,
+
// フォローしている人の投稿
$or: followQuery,
diff --git a/src/server/api/endpoints/notes/user-list-timeline.ts b/src/server/api/endpoints/notes/user-list-timeline.ts
index 6758b4eb73..7dddc4834e 100644
--- a/src/server/api/endpoints/notes/user-list-timeline.ts
+++ b/src/server/api/endpoints/notes/user-list-timeline.ts
@@ -137,6 +137,8 @@ export default async (params: any, user: ILocalUser) => {
const query = {
$and: [{
+ deletedAt: null,
+
// リストに入っている人のタイムラインへの投稿
$or: listQuery,
diff --git a/src/server/api/endpoints/users/notes.ts b/src/server/api/endpoints/users/notes.ts
index 1bfe832c51..0b2fc06b04 100644
--- a/src/server/api/endpoints/users/notes.ts
+++ b/src/server/api/endpoints/users/notes.ts
@@ -99,6 +99,12 @@ export const meta = {
'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
}
}),
+
+ fileType: $.arr($.str).optional.note({
+ desc: {
+ 'ja-JP': '指定された種類のファイルが添付された投稿のみを取得します'
+ }
+ }),
}
};
@@ -136,7 +142,9 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
};
const query = {
- userId: user._id
+ deletedAt: null,
+ userId: user._id,
+ visibility: { $in: ['public', 'home'] }
} as any;
if (ps.sinceId) {
@@ -171,6 +179,14 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
$ne: []
};
}
+
+ if (ps.fileType) {
+ query.fileIds = { $exists: true, $ne: [] };
+
+ query['_files.contentType'] = {
+ $in: ps.fileType
+ };
+ }
//#endregion
// Issue query
diff --git a/src/server/api/endpoints/users/recommendation.ts b/src/server/api/endpoints/users/recommendation.ts
index e0a5cb9e36..df85343f0f 100644
--- a/src/server/api/endpoints/users/recommendation.ts
+++ b/src/server/api/endpoints/users/recommendation.ts
@@ -3,6 +3,8 @@ import $ from 'cafy';
import User, { pack, ILocalUser } from '../../../../models/user';
import { getFriendIds } from '../../common/get-friends';
import Mute from '../../../../models/mute';
+import * as request from 'request';
+import config from '../../../../config';
export const meta = {
desc: {
@@ -15,44 +17,70 @@ export const meta = {
};
export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => {
- // Get 'limit' parameter
- const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit);
- if (limitErr) return rej('invalid limit param');
+ if (config.user_recommendation && config.user_recommendation.external) {
+ const userName = me.username;
+ const hostName = config.hostname;
+ const limit = params.limit;
+ const offset = params.offset;
+ const timeout = config.user_recommendation.timeout;
+ const engine = config.user_recommendation.engine;
+ const url = engine
+ .replace('{{host}}', hostName)
+ .replace('{{user}}', userName)
+ .replace('{{limit}}', limit)
+ .replace('{{offset}}', offset);
- // Get 'offset' parameter
- const [offset = 0, offsetErr] = $.num.optional.min(0).get(params.offset);
- if (offsetErr) return rej('invalid offset param');
+ request({
+ url: url,
+ proxy: config.proxy,
+ timeout: timeout,
+ json: true,
+ followRedirect: true,
+ followAllRedirects: true
+ }, (error: any, response: any, body: any) => {
+ if (!error && response.statusCode == 200) {
+ res(body);
+ } else {
+ res([]);
+ }
+ });
+ } else {
+ // Get 'limit' parameter
+ const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit);
+ if (limitErr) return rej('invalid limit param');
+
+ // Get 'offset' parameter
+ const [offset = 0, offsetErr] = $.num.optional.min(0).get(params.offset);
+ if (offsetErr) return rej('invalid offset param');
- // ID list of the user itself and other users who the user follows
- const followingIds = await getFriendIds(me._id);
+ // ID list of the user itself and other users who the user follows
+ const followingIds = await getFriendIds(me._id);
- // ミュートしているユーザーを取得
- const mutedUserIds = (await Mute.find({
- muterId: me._id
- })).map(m => m.muteeId);
+ // ミュートしているユーザーを取得
+ const mutedUserIds = (await Mute.find({
+ muterId: me._id
+ })).map(m => m.muteeId);
- const users = await User
- .find({
- _id: {
- $nin: followingIds.concat(mutedUserIds)
- },
- isLocked: false,
- $or: [{
+ const users = await User
+ .find({
+ _id: {
+ $nin: followingIds.concat(mutedUserIds)
+ },
+ isLocked: { $ne: true },
lastUsedAt: {
$gte: new Date(Date.now() - ms('7days'))
- }
- }, {
+ },
host: null
- }]
- }, {
- limit: limit,
- skip: offset,
- sort: {
- followersCount: -1
- }
- });
+ }, {
+ limit: limit,
+ skip: offset,
+ sort: {
+ followersCount: -1
+ }
+ });
- // Serialize
- res(await Promise.all(users.map(async user =>
- await pack(user, me, { detail: true }))));
+ // Serialize
+ res(await Promise.all(users.map(async user =>
+ await pack(user, me, { detail: true }))));
+ }
});
diff --git a/src/server/api/index.ts b/src/server/api/index.ts
index a8f6455d9a..33e98f650a 100644
--- a/src/server/api/index.ts
+++ b/src/server/api/index.ts
@@ -46,6 +46,8 @@ router.post('/signin', require('./private/signin').default);
router.use(require('./service/github').routes());
router.use(require('./service/twitter').routes());
+router.use(require('./mastodon').routes());
+
// Return 404 for unknown API
router.all('*', async ctx => {
ctx.status = 404;
@@ -54,4 +56,4 @@ router.all('*', async ctx => {
// Register router
app.use(router.routes());
-module.exports = app;
+export default app;
diff --git a/src/server/api/limitter.ts b/src/server/api/limitter.ts
index 20a18a7098..abf7627ab8 100644
--- a/src/server/api/limitter.ts
+++ b/src/server/api/limitter.ts
@@ -8,6 +8,12 @@ import { IUser } from '../../models/user';
const log = debug('misskey:limitter');
export default (endpoint: IEndpoint, user: IUser) => new Promise((ok, reject) => {
+ // Redisがインストールされてない場合は常に許可
+ if (limiterDB == null) {
+ ok();
+ return;
+ }
+
const limitation = endpoint.meta.limit;
const key = limitation.hasOwnProperty('key')
diff --git a/src/server/api/mastodon.ts b/src/server/api/mastodon.ts
new file mode 100644
index 0000000000..f2ce1c384f
--- /dev/null
+++ b/src/server/api/mastodon.ts
@@ -0,0 +1,14 @@
+import * as Router from 'koa-router';
+import User from '../../models/user';
+import { toASCII } from 'punycode';
+
+// Init router
+const router = new Router();
+
+router.get('/v1/instance/peers', async ctx => {
+ const peers = await User.distinct('host', { host: { $ne: null } }) as any as string[];
+ const punyCodes = peers.map(peer => toASCII(peer));
+ ctx.body = punyCodes;
+});
+
+module.exports = router;
diff --git a/src/server/api/private/signin.ts b/src/server/api/private/signin.ts
index 0e44c2ddd6..0a0f9ae6f9 100644
--- a/src/server/api/private/signin.ts
+++ b/src/server/api/private/signin.ts
@@ -12,9 +12,8 @@ export default async (ctx: Koa.Context) => {
ctx.set('Access-Control-Allow-Credentials', 'true');
const body = ctx.request.body as any;
- // See: https://github.com/syuilo/misskey/issues/2384
- const username = body['username'] || body['x'];
- const password = body['password'] || body['y'];
+ const username = body['username'];
+ const password = body['password'];
const token = body['token'];
if (typeof username != 'string') {
diff --git a/src/server/api/private/signup.ts b/src/server/api/private/signup.ts
index e3e8f044b5..13ca16eb9f 100644
--- a/src/server/api/private/signup.ts
+++ b/src/server/api/private/signup.ts
@@ -132,6 +132,12 @@ export default async (ctx: Koa.Context) => {
updateUserStats(account, true);
- // Response
- ctx.body = await pack(account);
+ const res = await pack(account, account, {
+ detail: true,
+ includeSecrets: true
+ });
+
+ res.token = secret;
+
+ ctx.body = res;
};
diff --git a/src/server/api/service/github.ts b/src/server/api/service/github.ts
index c8d588eaaf..ac18cf90ae 100644
--- a/src/server/api/service/github.ts
+++ b/src/server/api/service/github.ts
@@ -63,6 +63,7 @@ handler.on('status', event => {
// Fetch parent status
request({
url: `${parent.url}/statuses`,
+ proxy: config.proxy,
headers: {
'User-Agent': 'misskey'
}
diff --git a/src/server/api/service/twitter.ts b/src/server/api/service/twitter.ts
index f71e588628..6c3cdaa138 100644
--- a/src/server/api/service/twitter.ts
+++ b/src/server/api/service/twitter.ts
@@ -55,7 +55,7 @@ router.get('/disconnect/twitter', async ctx => {
}));
});
-if (config.twitter == null) {
+if (config.twitter == null || redis == null) {
router.get('/connect/twitter', ctx => {
ctx.body = '現在Twitterへ接続できません (このインスタンスではTwitterはサポートされていません)';
});
diff --git a/src/server/api/stream/channel.ts b/src/server/api/stream/channel.ts
index e2726060dc..75914964cb 100644
--- a/src/server/api/stream/channel.ts
+++ b/src/server/api/stream/channel.ts
@@ -7,6 +7,8 @@ import Connection from '.';
export default abstract class Channel {
protected connection: Connection;
public id: string;
+ public abstract readonly chName: string;
+ public static readonly shouldShare: boolean;
protected get user() {
return this.connection.user;
diff --git a/src/server/api/stream/channels/drive.ts b/src/server/api/stream/channels/drive.ts
index 807fc93cd0..7425a620ff 100644
--- a/src/server/api/stream/channels/drive.ts
+++ b/src/server/api/stream/channels/drive.ts
@@ -2,6 +2,9 @@ import autobind from 'autobind-decorator';
import Channel from '../channel';
export default class extends Channel {
+ public readonly chName = 'drive';
+ public static shouldShare = true;
+
@autobind
public async init(params: any) {
// Subscribe drive stream
diff --git a/src/server/api/stream/channels/games/reversi-game.ts b/src/server/api/stream/channels/games/reversi-game.ts
index 11f1fb1feb..5dc9ca0608 100644
--- a/src/server/api/stream/channels/games/reversi-game.ts
+++ b/src/server/api/stream/channels/games/reversi-game.ts
@@ -1,5 +1,6 @@
import autobind from 'autobind-decorator';
import * as CRC32 from 'crc-32';
+import * as mongo from 'mongodb';
import ReversiGame, { pack } from '../../../../../models/games/reversi/game';
import { publishReversiGameStream } from '../../../../../stream';
import Reversi from '../../../../../games/reversi/core';
@@ -7,11 +8,14 @@ import * as maps from '../../../../../games/reversi/maps';
import Channel from '../../channel';
export default class extends Channel {
- private gameId: string;
+ public readonly chName = 'gamesReversiGame';
+ public static shouldShare = false;
+
+ private gameId: mongo.ObjectID;
@autobind
public async init(params: any) {
- this.gameId = params.gameId as string;
+ this.gameId = new mongo.ObjectID(params.gameId as string);
// Subscribe game stream
this.subscriber.on(`reversiGameStream:${this.gameId}`, data => {
@@ -23,10 +27,10 @@ export default class extends Channel {
public onMessage(type: string, body: any) {
switch (type) {
case 'accept': this.accept(true); break;
- case 'cancel-accept': this.accept(false); break;
- case 'update-settings': this.updateSettings(body.settings); break;
- case 'init-form': this.initForm(body); break;
- case 'update-form': this.updateForm(body.id, body.value); break;
+ case 'cancelAccept': this.accept(false); break;
+ case 'updateSettings': this.updateSettings(body.settings); break;
+ case 'initForm': this.initForm(body); break;
+ case 'updateForm': this.updateForm(body.id, body.value); break;
case 'message': this.message(body); break;
case 'set': this.set(body.pos); break;
case 'check': this.check(body.crc32); break;
diff --git a/src/server/api/stream/channels/games/reversi.ts b/src/server/api/stream/channels/games/reversi.ts
index d75025c944..51cb264d98 100644
--- a/src/server/api/stream/channels/games/reversi.ts
+++ b/src/server/api/stream/channels/games/reversi.ts
@@ -5,6 +5,9 @@ import { publishMainStream } from '../../../../../stream';
import Channel from '../../channel';
export default class extends Channel {
+ public readonly chName = 'gamesReversi';
+ public static shouldShare = true;
+
@autobind
public async init(params: any) {
// Subscribe reversi stream
diff --git a/src/server/api/stream/channels/global-timeline.ts b/src/server/api/stream/channels/global-timeline.ts
index ab0fe5d094..e39ea269a6 100644
--- a/src/server/api/stream/channels/global-timeline.ts
+++ b/src/server/api/stream/channels/global-timeline.ts
@@ -5,6 +5,9 @@ import shouldMuteThisNote from '../../../../misc/should-mute-this-note';
import Channel from '../channel';
export default class extends Channel {
+ public readonly chName = 'globalTimeline';
+ public static shouldShare = true;
+
private mutedUserIds: string[] = [];
@autobind
diff --git a/src/server/api/stream/channels/hashtag.ts b/src/server/api/stream/channels/hashtag.ts
index 652b0caa5b..cf652c166c 100644
--- a/src/server/api/stream/channels/hashtag.ts
+++ b/src/server/api/stream/channels/hashtag.ts
@@ -5,6 +5,9 @@ import shouldMuteThisNote from '../../../../misc/should-mute-this-note';
import Channel from '../channel';
export default class extends Channel {
+ public readonly chName = 'hashtag';
+ public static shouldShare = false;
+
@autobind
public async init(params: any) {
const mute = this.user ? await Mute.find({ muterId: this.user._id }) : null;
diff --git a/src/server/api/stream/channels/home-timeline.ts b/src/server/api/stream/channels/home-timeline.ts
index 4c674e75ef..3fa887f1e5 100644
--- a/src/server/api/stream/channels/home-timeline.ts
+++ b/src/server/api/stream/channels/home-timeline.ts
@@ -5,6 +5,9 @@ import shouldMuteThisNote from '../../../../misc/should-mute-this-note';
import Channel from '../channel';
export default class extends Channel {
+ public readonly chName = 'homeTimeline';
+ public static shouldShare = true;
+
private mutedUserIds: string[] = [];
@autobind
diff --git a/src/server/api/stream/channels/hybrid-timeline.ts b/src/server/api/stream/channels/hybrid-timeline.ts
index 0b12ab3a8f..d72545e4c8 100644
--- a/src/server/api/stream/channels/hybrid-timeline.ts
+++ b/src/server/api/stream/channels/hybrid-timeline.ts
@@ -5,6 +5,9 @@ import shouldMuteThisNote from '../../../../misc/should-mute-this-note';
import Channel from '../channel';
export default class extends Channel {
+ public readonly chName = 'hybridTimeline';
+ public static shouldShare = true;
+
private mutedUserIds: string[] = [];
@autobind
diff --git a/src/server/api/stream/channels/local-timeline.ts b/src/server/api/stream/channels/local-timeline.ts
index 769ec6392f..0ba0b1b195 100644
--- a/src/server/api/stream/channels/local-timeline.ts
+++ b/src/server/api/stream/channels/local-timeline.ts
@@ -5,6 +5,9 @@ import shouldMuteThisNote from '../../../../misc/should-mute-this-note';
import Channel from '../channel';
export default class extends Channel {
+ public readonly chName = 'localTimeline';
+ public static shouldShare = true;
+
private mutedUserIds: string[] = [];
@autobind
diff --git a/src/server/api/stream/channels/main.ts b/src/server/api/stream/channels/main.ts
index fd0984c833..7d5462c092 100644
--- a/src/server/api/stream/channels/main.ts
+++ b/src/server/api/stream/channels/main.ts
@@ -3,6 +3,9 @@ import Mute from '../../../../models/mute';
import Channel from '../channel';
export default class extends Channel {
+ public readonly chName = 'main';
+ public static shouldShare = true;
+
@autobind
public async init(params: any) {
const mute = await Mute.find({ muterId: this.user._id });
diff --git a/src/server/api/stream/channels/messaging-index.ts b/src/server/api/stream/channels/messaging-index.ts
index 6e87cca7f4..0211d702cf 100644
--- a/src/server/api/stream/channels/messaging-index.ts
+++ b/src/server/api/stream/channels/messaging-index.ts
@@ -2,6 +2,9 @@ import autobind from 'autobind-decorator';
import Channel from '../channel';
export default class extends Channel {
+ public readonly chName = 'messagingIndex';
+ public static shouldShare = true;
+
@autobind
public async init(params: any) {
// Subscribe messaging index stream
diff --git a/src/server/api/stream/channels/messaging.ts b/src/server/api/stream/channels/messaging.ts
index e1a78c8678..ab04a332cf 100644
--- a/src/server/api/stream/channels/messaging.ts
+++ b/src/server/api/stream/channels/messaging.ts
@@ -3,6 +3,9 @@ import read from '../../common/read-messaging-message';
import Channel from '../channel';
export default class extends Channel {
+ public readonly chName = 'messaging';
+ public static shouldShare = false;
+
private otherpartyId: string;
@autobind
diff --git a/src/server/api/stream/channels/notes-stats.ts b/src/server/api/stream/channels/notes-stats.ts
index cc68d9886d..2282f8bc70 100644
--- a/src/server/api/stream/channels/notes-stats.ts
+++ b/src/server/api/stream/channels/notes-stats.ts
@@ -5,6 +5,9 @@ import Channel from '../channel';
const ev = new Xev();
export default class extends Channel {
+ public readonly chName = 'notesStats';
+ public static shouldShare = true;
+
@autobind
public async init(params: any) {
ev.addListener('notesStats', this.onStats);
diff --git a/src/server/api/stream/channels/server-stats.ts b/src/server/api/stream/channels/server-stats.ts
index 28a566e8ae..912dcf5305 100644
--- a/src/server/api/stream/channels/server-stats.ts
+++ b/src/server/api/stream/channels/server-stats.ts
@@ -5,6 +5,9 @@ import Channel from '../channel';
const ev = new Xev();
export default class extends Channel {
+ public readonly chName = 'serverStats';
+ public static shouldShare = true;
+
@autobind
public async init(params: any) {
ev.addListener('serverStats', this.onStats);
diff --git a/src/server/api/stream/channels/user-list.ts b/src/server/api/stream/channels/user-list.ts
index 4ace308923..b526a5f986 100644
--- a/src/server/api/stream/channels/user-list.ts
+++ b/src/server/api/stream/channels/user-list.ts
@@ -2,6 +2,9 @@ import autobind from 'autobind-decorator';
import Channel from '../channel';
export default class extends Channel {
+ public readonly chName = 'userList';
+ public static shouldShare = false;
+
@autobind
public async init(params: any) {
const listId = params.listId as string;
diff --git a/src/server/api/stream/index.ts b/src/server/api/stream/index.ts
index ef6397fcd9..e376b7a29e 100644
--- a/src/server/api/stream/index.ts
+++ b/src/server/api/stream/index.ts
@@ -1,6 +1,5 @@
import autobind from 'autobind-decorator';
import * as websocket from 'websocket';
-import Xev from 'xev';
import * as debug from 'debug';
import User, { IUser } from '../../../models/user';
@@ -11,6 +10,7 @@ import readNote from '../../../services/note/read';
import Channel from './channel';
import channels from './channels';
+import { EventEmitter } from 'events';
const log = debug('misskey');
@@ -21,14 +21,14 @@ export default class Connection {
public user?: IUser;
public app: IApp;
private wsConnection: websocket.connection;
- public subscriber: Xev;
+ public subscriber: EventEmitter;
private channels: Channel[] = [];
private subscribingNotes: any = {};
public sendMessageToWsOverride: any = null; // 後方互換性のため
constructor(
wsConnection: websocket.connection,
- subscriber: Xev,
+ subscriber: EventEmitter,
user: IUser,
app: IApp
) {
@@ -58,6 +58,7 @@ export default class Connection {
case 'connect': this.onChannelConnectRequested(body); break;
case 'disconnect': this.onChannelDisconnectRequested(body); break;
case 'channel': this.onChannelMessageRequested(body); break;
+ case 'ch': this.onChannelMessageRequested(body); break; // alias
}
}
@@ -145,9 +146,9 @@ export default class Connection {
*/
@autobind
private onChannelConnectRequested(payload: any) {
- const { channel, id, params } = payload;
+ const { channel, id, params, pong } = payload;
log(`CH CONNECT: ${id} ${channel} by @${this.user.username}`);
- this.connectChannel(id, params, (channels as any)[channel]);
+ this.connectChannel(id, params, channel, pong);
}
/**
@@ -176,10 +177,21 @@ export default class Connection {
* チャンネルに接続
*/
@autobind
- public connectChannel(id: string, params: any, channelClass: { new(id: string, connection: Connection): Channel }) {
- const channel = new channelClass(id, this);
- this.channels.push(channel);
- channel.init(params);
+ public connectChannel(id: string, params: any, channel: string, pong = false) {
+ // 共有可能チャンネルに接続しようとしていて、かつそのチャンネルに既に接続していたら無意味なので無視
+ if ((channels as any)[channel].shouldShare && this.channels.some(c => c.chName === channel)) {
+ return;
+ }
+
+ const ch: Channel = new (channels as any)[channel](id, this);
+ this.channels.push(ch);
+ ch.init(params);
+
+ if (pong) {
+ this.sendMessageToWs('connected', {
+ id: id
+ });
+ }
}
/**
diff --git a/src/server/api/streaming.ts b/src/server/api/streaming.ts
index c8c4a8a294..8c0e6f6372 100644
--- a/src/server/api/streaming.ts
+++ b/src/server/api/streaming.ts
@@ -1,11 +1,13 @@
import * as http from 'http';
import * as websocket from 'websocket';
+import * as redis from 'redis';
import Xev from 'xev';
import MainStreamConnection from './stream';
import { ParsedUrlQuery } from 'querystring';
import authenticate from './authenticate';
-import channels from './stream/channels';
+import { EventEmitter } from 'events';
+import config from '../../config';
module.exports = (server: http.Server) => {
// Init websocket server
@@ -16,11 +18,34 @@ module.exports = (server: http.Server) => {
ws.on('request', async (request) => {
const connection = request.accept();
- const ev = new Xev();
-
const q = request.resourceURL.query as ParsedUrlQuery;
const [user, app] = await authenticate(q.i as string);
+ let ev: EventEmitter;
+
+ if (config.redis) {
+ // Connect to Redis
+ const subscriber = redis.createClient(
+ config.redis.port, config.redis.host);
+
+ subscriber.subscribe('misskey');
+
+ ev = new EventEmitter();
+
+ subscriber.on('message', async (_, data) => {
+ const obj = JSON.parse(data);
+
+ ev.emit(obj.channel, obj.message);
+ });
+
+ connection.once('close', () => {
+ subscriber.unsubscribe();
+ subscriber.quit();
+ });
+ } else {
+ ev = new Xev();
+ }
+
const main = new MainStreamConnection(connection, ev, user, app);
// 後方互換性のため
@@ -39,11 +64,15 @@ module.exports = (server: http.Server) => {
}));
};
- main.connectChannel(Math.random().toString(), null,
- request.resourceURL.pathname === '/' ? channels.homeTimeline :
- request.resourceURL.pathname === '/local-timeline' ? channels.localTimeline :
- request.resourceURL.pathname === '/hybrid-timeline' ? channels.hybridTimeline :
- request.resourceURL.pathname === '/global-timeline' ? channels.globalTimeline : null);
+ main.connectChannel(Math.random().toString().substr(2, 8), null,
+ request.resourceURL.pathname === '/' ? 'homeTimeline' :
+ request.resourceURL.pathname === '/local-timeline' ? 'localTimeline' :
+ request.resourceURL.pathname === '/hybrid-timeline' ? 'hybridTimeline' :
+ request.resourceURL.pathname === '/global-timeline' ? 'globalTimeline' : null);
+
+ if (request.resourceURL.pathname === '/') {
+ main.connectChannel(Math.random().toString().substr(2, 8), null, 'main');
+ }
}
connection.once('close', () => {