summaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
authorsyuilo <syuilotan@yahoo.co.jp>2018-04-11 17:40:01 +0900
committersyuilo <syuilotan@yahoo.co.jp>2018-04-11 17:40:01 +0900
commitbd3d57a67f6d7c6a01516410d2322e6ffbd2f5ad (patch)
treee5caa46997f78a61fb09a821aa0ac210784500fb /src/server
parentv4771 (diff)
downloadsharkey-bd3d57a67f6d7c6a01516410d2322e6ffbd2f5ad.tar.gz
sharkey-bd3d57a67f6d7c6a01516410d2322e6ffbd2f5ad.tar.bz2
sharkey-bd3d57a67f6d7c6a01516410d2322e6ffbd2f5ad.zip
ストリーム経由でAPIにリクエストできるように
Diffstat (limited to 'src/server')
-rw-r--r--src/server/api/api-handler.ts60
-rw-r--r--src/server/api/authenticate.ts46
-rw-r--r--src/server/api/call.ts55
-rw-r--r--src/server/api/endpoints/app/show.ts18
-rw-r--r--src/server/api/endpoints/i.ts4
-rw-r--r--src/server/api/endpoints/i/update.ts10
-rw-r--r--src/server/api/endpoints/meta.ts3
-rw-r--r--src/server/api/endpoints/sw/register.ts8
-rw-r--r--src/server/api/index.ts1
-rw-r--r--src/server/api/limitter.ts12
-rw-r--r--src/server/api/reply.ts13
-rw-r--r--src/server/api/stream/home.ts24
-rw-r--r--src/server/api/streaming.ts45
13 files changed, 126 insertions, 173 deletions
diff --git a/src/server/api/api-handler.ts b/src/server/api/api-handler.ts
index fb603a0e2a..409069b6a0 100644
--- a/src/server/api/api-handler.ts
+++ b/src/server/api/api-handler.ts
@@ -2,55 +2,33 @@ import * as express from 'express';
import { Endpoint } from './endpoints';
import authenticate from './authenticate';
-import { IAuthContext } from './authenticate';
-import _reply from './reply';
-import limitter from './limitter';
+import call from './call';
+import { IUser } from '../../models/user';
+import { IApp } from '../../models/app';
export default async (endpoint: Endpoint, req: express.Request, res: express.Response) => {
- const reply = _reply.bind(null, res);
- let ctx: IAuthContext;
+ const reply = (x?: any, y?: any) => {
+ if (x === undefined) {
+ res.sendStatus(204);
+ } else if (typeof x === 'number') {
+ res.status(x).send({
+ error: x === 500 ? 'INTERNAL_ERROR' : y
+ });
+ } else {
+ res.send(x);
+ }
+ };
+
+ let user: IUser;
+ let app: IApp;
// Authentication
try {
- ctx = await authenticate(req);
+ [user, app] = await authenticate(req.body['i']);
} catch (e) {
return reply(403, 'AUTHENTICATION_FAILED');
}
- if (endpoint.secure && !ctx.isSecure) {
- return reply(403, 'ACCESS_DENIED');
- }
-
- if (endpoint.withCredential && ctx.user == null) {
- return reply(401, 'PLZ_SIGNIN');
- }
-
- if (ctx.app && endpoint.kind) {
- if (!ctx.app.permission.some(p => p === endpoint.kind)) {
- return reply(403, 'ACCESS_DENIED');
- }
- }
-
- if (endpoint.withCredential && endpoint.limit) {
- try {
- await limitter(endpoint, ctx); // Rate limit
- } catch (e) {
- // drop request if limit exceeded
- return reply(429);
- }
- }
-
- let exec = require(`${__dirname}/endpoints/${endpoint.name}`);
-
- if (endpoint.withFile) {
- exec = exec.bind(null, req.file);
- }
-
// API invoking
- try {
- const res = await exec(req.body, ctx.user, ctx.app, ctx.isSecure);
- reply(res);
- } catch (e) {
- reply(400, e);
- }
+ call(endpoint, user, app, req.body, req).then(reply).catch(e => reply(400, e));
};
diff --git a/src/server/api/authenticate.ts b/src/server/api/authenticate.ts
index adbeeb3b34..836fb7cfe8 100644
--- a/src/server/api/authenticate.ts
+++ b/src/server/api/authenticate.ts
@@ -1,50 +1,24 @@
-import * as express from 'express';
-import App from '../../models/app';
+import App, { IApp } from '../../models/app';
import { default as User, IUser } from '../../models/user';
import AccessToken from '../../models/access-token';
import isNativeToken from './common/is-native-token';
-export interface IAuthContext {
- /**
- * App which requested
- */
- app: any;
-
- /**
- * Authenticated user
- */
- user: IUser;
-
- /**
- * Whether requested with a User-Native Token
- */
- isSecure: boolean;
-}
-
-export default (req: express.Request) => new Promise<IAuthContext>(async (resolve, reject) => {
- const token = req.body['i'] as string;
-
+export default (token: string) => new Promise<[IUser, IApp]>(async (resolve, reject) => {
if (token == null) {
- return resolve({
- app: null,
- user: null,
- isSecure: false
- });
+ resolve([null, null]);
+ return;
}
if (isNativeToken(token)) {
+ // Fetch user
const user: IUser = await User
- .findOne({ 'token': token });
+ .findOne({ token });
if (user === null) {
return reject('user not found');
}
- return resolve({
- app: null,
- user: user,
- isSecure: true
- });
+ resolve([user, null]);
} else {
const accessToken = await AccessToken.findOne({
hash: token.toLowerCase()
@@ -60,10 +34,6 @@ export default (req: express.Request) => new Promise<IAuthContext>(async (resolv
const user = await User
.findOne({ _id: accessToken.userId });
- return resolve({
- app: app,
- user: user,
- isSecure: false
- });
+ resolve([user, app]);
}
});
diff --git a/src/server/api/call.ts b/src/server/api/call.ts
new file mode 100644
index 0000000000..1bfe94bb74
--- /dev/null
+++ b/src/server/api/call.ts
@@ -0,0 +1,55 @@
+import * as express from 'express';
+
+import endpoints, { Endpoint } from './endpoints';
+import limitter from './limitter';
+import { IUser } from '../../models/user';
+import { IApp } from '../../models/app';
+
+export default (endpoint: string | Endpoint, user: IUser, app: IApp, data: any, req?: express.Request) => new Promise(async (ok, rej) => {
+ const isSecure = user != null && app == null;
+
+ //console.log(endpoint, user, app, data);
+
+ const ep = typeof endpoint == 'string' ? endpoints.find(e => e.name == endpoint) : endpoint;
+
+ if (ep.secure && !isSecure) {
+ return rej('ACCESS_DENIED');
+ }
+
+ if (ep.withCredential && user == null) {
+ return rej('SIGNIN_REQUIRED');
+ }
+
+ if (app && ep.kind) {
+ if (!app.permission.some(p => p === ep.kind)) {
+ return rej('PERMISSION_DENIED');
+ }
+ }
+
+ if (ep.withCredential && ep.limit) {
+ try {
+ await limitter(ep, user); // Rate limit
+ } catch (e) {
+ // drop request if limit exceeded
+ return rej('RATE_LIMIT_EXCEEDED');
+ }
+ }
+
+ let exec = require(`${__dirname}/endpoints/${ep.name}`);
+
+ if (ep.withFile && req) {
+ exec = exec.bind(null, req.file);
+ }
+
+ let res;
+
+ // API invoking
+ try {
+ res = await exec(data, user, app);
+ } catch (e) {
+ rej(e);
+ return;
+ }
+
+ ok(res);
+});
diff --git a/src/server/api/endpoints/app/show.ts b/src/server/api/endpoints/app/show.ts
index 3a3c25f47c..99a2093b68 100644
--- a/src/server/api/endpoints/app/show.ts
+++ b/src/server/api/endpoints/app/show.ts
@@ -36,14 +36,10 @@ import App, { pack } from '../../../../models/app';
/**
* Show an app
- *
- * @param {any} params
- * @param {any} user
- * @param {any} _
- * @param {any} isSecure
- * @return {Promise<any>}
*/
-module.exports = (params, user, _, isSecure) => new Promise(async (res, rej) => {
+module.exports = (params, user, app) => new Promise(async (res, rej) => {
+ const isSecure = user != null && app == null;
+
// Get 'appId' parameter
const [appId, appIdErr] = $(params.appId).optional.id().$;
if (appIdErr) return rej('invalid appId param');
@@ -57,16 +53,16 @@ module.exports = (params, user, _, isSecure) => new Promise(async (res, rej) =>
}
// Lookup app
- const app = appId !== undefined
+ const ap = appId !== undefined
? await App.findOne({ _id: appId })
: await App.findOne({ nameIdLower: nameId.toLowerCase() });
- if (app === null) {
+ if (ap === null) {
return rej('app not found');
}
// Send response
- res(await pack(app, user, {
- includeSecret: isSecure && app.userId.equals(user._id)
+ res(await pack(ap, user, {
+ includeSecret: isSecure && ap.userId.equals(user._id)
}));
});
diff --git a/src/server/api/endpoints/i.ts b/src/server/api/endpoints/i.ts
index 0be30500c4..379c3c4d88 100644
--- a/src/server/api/endpoints/i.ts
+++ b/src/server/api/endpoints/i.ts
@@ -6,7 +6,9 @@ import User, { pack } from '../../../models/user';
/**
* Show myself
*/
-module.exports = (params, user, _, isSecure) => new Promise(async (res, rej) => {
+module.exports = (params, user, app) => new Promise(async (res, rej) => {
+ const isSecure = user != null && app == null;
+
// Serialize
res(await pack(user, user, {
detail: true,
diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts
index 36be2774f6..f3c9d777b5 100644
--- a/src/server/api/endpoints/i/update.ts
+++ b/src/server/api/endpoints/i/update.ts
@@ -7,14 +7,10 @@ import event from '../../../../publishers/stream';
/**
* Update myself
- *
- * @param {any} params
- * @param {any} user
- * @param {any} _
- * @param {boolean} isSecure
- * @return {Promise<any>}
*/
-module.exports = async (params, user, _, isSecure) => new Promise(async (res, rej) => {
+module.exports = async (params, user, app) => new Promise(async (res, rej) => {
+ const isSecure = user != null && app == null;
+
// Get 'name' parameter
const [name, nameErr] = $(params.name).optional.nullable.string().pipe(isValidName).$;
if (nameErr) return rej('invalid name param');
diff --git a/src/server/api/endpoints/meta.ts b/src/server/api/endpoints/meta.ts
index 8574362fc8..f6a276a2b7 100644
--- a/src/server/api/endpoints/meta.ts
+++ b/src/server/api/endpoints/meta.ts
@@ -35,9 +35,6 @@ import Meta from '../../../models/meta';
/**
* Show core info
- *
- * @param {any} params
- * @return {Promise<any>}
*/
module.exports = (params) => new Promise(async (res, rej) => {
const meta: any = (await Meta.findOne()) || {};
diff --git a/src/server/api/endpoints/sw/register.ts b/src/server/api/endpoints/sw/register.ts
index ef3428057d..3fe0bda4ee 100644
--- a/src/server/api/endpoints/sw/register.ts
+++ b/src/server/api/endpoints/sw/register.ts
@@ -6,14 +6,8 @@ import Subscription from '../../../../models/sw-subscription';
/**
* subscribe service worker
- *
- * @param {any} params
- * @param {any} user
- * @param {any} _
- * @param {boolean} isSecure
- * @return {Promise<any>}
*/
-module.exports = async (params, user, _, isSecure) => new Promise(async (res, rej) => {
+module.exports = async (params, user, app) => new Promise(async (res, rej) => {
// Get 'endpoint' parameter
const [endpoint, endpointErr] = $(params.endpoint).string().$;
if (endpointErr) return rej('invalid endpoint param');
diff --git a/src/server/api/index.ts b/src/server/api/index.ts
index e89d196096..5fbacd8a0e 100644
--- a/src/server/api/index.ts
+++ b/src/server/api/index.ts
@@ -7,7 +7,6 @@ import * as bodyParser from 'body-parser';
import * as cors from 'cors';
import * as multer from 'multer';
-// import authenticate from './authenticate';
import endpoints from './endpoints';
/**
diff --git a/src/server/api/limitter.ts b/src/server/api/limitter.ts
index 638fac78be..b84e16ecde 100644
--- a/src/server/api/limitter.ts
+++ b/src/server/api/limitter.ts
@@ -2,12 +2,12 @@ import * as Limiter from 'ratelimiter';
import * as debug from 'debug';
import limiterDB from '../../db/redis';
import { Endpoint } from './endpoints';
-import { IAuthContext } from './authenticate';
import getAcct from '../../acct/render';
+import { IUser } from '../../models/user';
const log = debug('misskey:limitter');
-export default (endpoint: Endpoint, ctx: IAuthContext) => new Promise((ok, reject) => {
+export default (endpoint: Endpoint, user: IUser) => new Promise((ok, reject) => {
const limitation = endpoint.limit;
const key = limitation.hasOwnProperty('key')
@@ -32,7 +32,7 @@ export default (endpoint: Endpoint, ctx: IAuthContext) => new Promise((ok, rejec
// Short-term limit
function min() {
const minIntervalLimiter = new Limiter({
- id: `${ctx.user._id}:${key}:min`,
+ id: `${user._id}:${key}:min`,
duration: limitation.minInterval,
max: 1,
db: limiterDB
@@ -43,7 +43,7 @@ export default (endpoint: Endpoint, ctx: IAuthContext) => new Promise((ok, rejec
return reject('ERR');
}
- log(`@${getAcct(ctx.user)} ${endpoint.name} min remaining: ${info.remaining}`);
+ log(`@${getAcct(user)} ${endpoint.name} min remaining: ${info.remaining}`);
if (info.remaining === 0) {
reject('BRIEF_REQUEST_INTERVAL');
@@ -60,7 +60,7 @@ export default (endpoint: Endpoint, ctx: IAuthContext) => new Promise((ok, rejec
// Long term limit
function max() {
const limiter = new Limiter({
- id: `${ctx.user._id}:${key}`,
+ id: `${user._id}:${key}`,
duration: limitation.duration,
max: limitation.max,
db: limiterDB
@@ -71,7 +71,7 @@ export default (endpoint: Endpoint, ctx: IAuthContext) => new Promise((ok, rejec
return reject('ERR');
}
- log(`@${getAcct(ctx.user)} ${endpoint.name} max remaining: ${info.remaining}`);
+ log(`@${getAcct(user)} ${endpoint.name} max remaining: ${info.remaining}`);
if (info.remaining === 0) {
reject('RATE_LIMIT_EXCEEDED');
diff --git a/src/server/api/reply.ts b/src/server/api/reply.ts
deleted file mode 100644
index e47fc85b9b..0000000000
--- a/src/server/api/reply.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import * as express from 'express';
-
-export default (res: express.Response, x?: any, y?: any) => {
- if (x === undefined) {
- res.sendStatus(204);
- } else if (typeof x === 'number') {
- res.status(x).send({
- error: x === 500 ? 'INTERNAL_ERROR' : y
- });
- } else {
- res.send(x);
- }
-};
diff --git a/src/server/api/stream/home.ts b/src/server/api/stream/home.ts
index 359ef74aff..e9c0924f31 100644
--- a/src/server/api/stream/home.ts
+++ b/src/server/api/stream/home.ts
@@ -2,14 +2,22 @@ import * as websocket from 'websocket';
import * as redis from 'redis';
import * as debug from 'debug';
-import User from '../../../models/user';
+import User, { IUser } from '../../../models/user';
import Mute from '../../../models/mute';
import { pack as packNote } from '../../../models/note';
import readNotification from '../common/read-notification';
+import call from '../call';
+import { IApp } from '../../../models/app';
const log = debug('misskey');
-export default async function(request: websocket.request, connection: websocket.connection, subscriber: redis.RedisClient, user: any) {
+export default async function(
+ request: websocket.request,
+ connection: websocket.connection,
+ subscriber: redis.RedisClient,
+ user: IUser,
+ app: IApp
+) {
// Subscribe Home stream channel
subscriber.subscribe(`misskey:user-stream:${user._id}`);
@@ -67,7 +75,17 @@ export default async function(request: websocket.request, connection: websocket.
switch (msg.type) {
case 'api':
- // TODO
+ call(msg.endpoint, user, app, msg.data).then(res => {
+ connection.send(JSON.stringify({
+ type: `api-res:${msg.id}`,
+ body: { res }
+ }));
+ }).catch(e => {
+ connection.send(JSON.stringify({
+ type: `api-res:${msg.id}`,
+ body: { e }
+ }));
+ });
break;
case 'alive':
diff --git a/src/server/api/streaming.ts b/src/server/api/streaming.ts
index 26946b524e..d586d7c08f 100644
--- a/src/server/api/streaming.ts
+++ b/src/server/api/streaming.ts
@@ -2,9 +2,6 @@ import * as http from 'http';
import * as websocket from 'websocket';
import * as redis from 'redis';
import config from '../../config';
-import { default as User, IUser } from '../../models/user';
-import AccessToken from '../../models/access-token';
-import isNativeToken from './common/is-native-token';
import homeStream from './stream/home';
import driveStream from './stream/drive';
@@ -16,6 +13,7 @@ import serverStream from './stream/server';
import requestsStream from './stream/requests';
import channelStream from './stream/channel';
import { ParsedUrlQuery } from 'querystring';
+import authenticate from './authenticate';
module.exports = (server: http.Server) => {
/**
@@ -53,7 +51,7 @@ module.exports = (server: http.Server) => {
}
const q = request.resourceURL.query as ParsedUrlQuery;
- const user = await authenticate(q.i as string);
+ const [user, app] = await authenticate(q.i as string);
if (request.resourceURL.pathname === '/othello-game') {
othelloGameStream(request, connection, subscriber, user);
@@ -75,46 +73,9 @@ module.exports = (server: http.Server) => {
null;
if (channel !== null) {
- channel(request, connection, subscriber, user);
+ channel(request, connection, subscriber, user, app);
} else {
connection.close();
}
});
};
-
-/**
- * 接続してきたユーザーを取得します
- * @param token 送信されてきたトークン
- */
-function authenticate(token: string): Promise<IUser> {
- if (token == null) {
- return Promise.resolve(null);
- }
-
- return new Promise(async (resolve, reject) => {
- if (isNativeToken(token)) {
- // Fetch user
- const user: IUser = await User
- .findOne({
- host: null,
- 'token': token
- });
-
- resolve(user);
- } else {
- const accessToken = await AccessToken.findOne({
- hash: token
- });
-
- if (accessToken == null) {
- return reject('invalid signature');
- }
-
- // Fetch user
- const user: IUser = await User
- .findOne({ _id: accessToken.userId });
-
- resolve(user);
- }
- });
-}