Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fastify #2285

Closed
wants to merge 5 commits into from
Closed

Fastify #2285

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@
"elasticsearch": "16.7.2",
"escape-regexp": "0.0.1",
"eventemitter3": "4.0.7",
"fastify": "3.12.0",
"fastify-cookie": "5.1.0",
"fastify-cors": "5.2.0",
"fastify-favicon": "3.1.0",
"fastify-sensible": "3.1.0",
"fastify-static": "4.0.1",
"file-type": "16.2.0",
"fluent-ffmpeg": "2.1.2",
"got": "11.8.1",
Expand Down Expand Up @@ -170,6 +176,7 @@
"os-utils": "0.0.14",
"parse5": "6.0.1",
"parsimmon": "1.16.0",
"point-of-view": "4.13.0",
"postcss-loader": "3.0.0",
"probe-image-size": "6.0.0",
"progress-bar-webpack-plugin": "2.1.0",
Expand Down
23 changes: 11 additions & 12 deletions src/server/api/api-handler.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,38 @@
import * as Router from '@koa/router';

import * as Fastify from 'fastify';
import { IEndpoint } from './endpoints';
import authenticate from './authenticate';
import call from './call';
import { ApiError } from './error';

export default (endpoint: IEndpoint, ctx: Router.RouterContext) => new Promise((res) => {
const body = ctx.method === 'GET' ? ctx.query : ctx.request.body;
export default (endpoint: IEndpoint, req: Fastify.FastifyRequest, rep: Fastify.FastifyReply) => new Promise((res) => {
const body: any = req.method === 'GET' ? req.query : (req.body || {});

const reply = (x?: any, y?: ApiError) => {
if (x == null) {
ctx.status = 204;
rep.code(204);
} else if (typeof x === 'number') {
ctx.status = x;
ctx.body = {
rep.code(x);
rep.send({
error: {
message: y.message,
code: y.code,
id: y.id,
kind: y.kind,
...(y.info ? { info: y.info } : {})
}
};
});
} else {
ctx.body = x;
rep.send(x);
}
res();
};

// Authentication
authenticate(body['i']).then(([user, app]) => {
// API invoking
call(endpoint.name, user, app, body, (ctx as any).file, ctx.ip).then((res: any) => {
if (ctx.method === 'GET' && endpoint.meta.cacheSec && !body['i'] && !user) {
ctx.set('Cache-Control', `public, max-age=${endpoint.meta.cacheSec}`);
call(endpoint.name, user, app, body, (req as any).file, req.ip).then((res: any) => {
if (req.method === 'GET' && endpoint.meta.cacheSec && !body['i'] && !user) {
rep.header('Cache-Control', `public, max-age=${endpoint.meta.cacheSec}`);
}
reply(res);
}).catch(e => {
Expand Down
12 changes: 6 additions & 6 deletions src/server/api/common/signin.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import * as Router from '@koa/router';
import * as Fastify from 'fastify';

import config from '../../../config';
import { ILocalUser } from '../../../models/user';

export default function(ctx: Router.RouterContext, user: ILocalUser, redirect = false) {
export default function(request: Fastify.FastifyRequest, reply: Fastify.FastifyReply, user: ILocalUser, redirect = false) {
if (redirect) {
//#region Cookie
const expires = 1000 * 60 * 60 * 24 * 365; // One Year
ctx.cookies.set('i', user.token, {

reply.setCookie('i', user.token, {
path: '/',
// SEE: https://github.com/koajs/koa/issues/974
// When using a SSL proxy it should be configured to add the "X-Forwarded-Proto: https" header
Expand All @@ -18,9 +19,8 @@ export default function(ctx: Router.RouterContext, user: ILocalUser, redirect =
});
//#endregion

ctx.redirect(config.url);
reply.redirect(config.url);
} else {
ctx.body = { i: user.token };
ctx.status = 200;
reply.send({ i: user.token });
}
}
102 changes: 52 additions & 50 deletions src/server/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@
* API Server
*/

import * as Koa from 'koa';
import * as Router from '@koa/router';
import * as multer from '@koa/multer';
import * as bodyParser from 'koa-bodyparser';
import * as cors from '@koa/cors';

import endpoints from './endpoints';
import handler from './api-handler';
Expand All @@ -19,10 +14,59 @@ import Instance from '../../models/instance';
import { toApHost } from '../../misc/convert-host';
import { unique } from '../../prelude/array';
import config from '../../config';
import * as Fastify from 'fastify';
import fastifyStatic from 'fastify-static';
import pointOfView from 'point-of-view';
const httpSignature = require('http-signature');
import * as path from 'path';
import * as pug from 'pug';
import cors from 'fastify-cors';

export default async (server: Fastify.FastifyInstance, opts: Fastify.FastifyPluginOptions, done: (err?: Error) => void) => {
server.register(cors); // scoped

// Misskey Webが送ってくるtext/plainはjsonとして扱わないといけない
server.addContentTypeParser('text/plain', { parseAs: 'string' }, (server as any).getDefaultJsonParser('ignore', 'ignore'));

// default response header
server.addHook('preHandler', async (request, reply) => {
reply
.header('Cache-Control', 'private, max-age=0, must-revalidate');
});

for (const endpoint of endpoints) {
if (endpoint.meta.requireFile) {
// server.post(`/${endpoint.name}`, upload.single('file'), handler.bind(null, endpoint)); TODO
} else {
if (endpoint.name.includes('-')) {
// 後方互換性のため
server.post(`/${endpoint.name.replace(/\-/g, '_')}`, handler.bind(null, endpoint));
}
server.post(`/${endpoint.name}`, handler.bind(null, endpoint));

if (endpoint.meta.allowGet) {
server.get(`/${endpoint.name}`, handler.bind(null, endpoint));
} else {
server.get(`/${endpoint.name}`, async (request, reply) => {
reply
.header('Allow', 'POST')
.methodNotAllowed('Must be a POST');
});
}
}
}

server.post('/signup', signup);
server.post('/signin', signin);

server.get('*', async (request, reply) => {
reply.notFound('Unknown API');
});

// Init app
const app = new Koa();
done();
};

/*
// Handle error
app.use(async (ctx, next) => {
try {
Expand All @@ -36,21 +80,6 @@ app.use(async (ctx, next) => {
}
});

app.use(cors({
origin: '*'
}));

// No caching
app.use(async (ctx, next) => {
ctx.set('Cache-Control', 'private, max-age=0, must-revalidate');
await next();
});

app.use(bodyParser({
// リクエストが multipart/form-data でない限りはJSONだと見なす
detectJSON: ctx => !ctx.is('multipart/form-data')
}));

// Init multer instance
const upload = multer({
storage: multer.diskStorage({}),
Expand All @@ -63,29 +92,6 @@ const upload = multer({
// Init router
const router = new Router();

/**
* Register endpoint handlers
*/
for (const endpoint of endpoints) {
if (endpoint.meta.requireFile) {
router.post(`/${endpoint.name}`, upload.single('file'), handler.bind(null, endpoint));
} else {
if (endpoint.name.includes('-')) {
// 後方互換性のため
router.post(`/${endpoint.name.replace(/\-/g, '_')}`, handler.bind(null, endpoint));
}
router.post(`/${endpoint.name}`, handler.bind(null, endpoint));

if (endpoint.meta.allowGet) {
router.get(`/${endpoint.name}`, handler.bind(null, endpoint));
} else {
router.get(`/${endpoint.name}`, async ctx => { ctx.status = 405; });
}
}
}

router.post('/signup', signup);
router.post('/signin', signin);

router.use(discord.routes());
router.use(github.routes());
Expand All @@ -109,8 +115,4 @@ router.get('/v1/instance/peers', async ctx => {
router.all('*', async ctx => {
ctx.status = 404;
});

// Register router
app.use(router.routes());

export default app;
*/
42 changes: 18 additions & 24 deletions src/server/api/private/signin.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as Router from '@koa/router';
import * as Fastify from 'fastify';
import * as bcrypt from 'bcryptjs';
import * as speakeasy from 'speakeasy';
import User, { ILocalUser } from '../../../models/user';
Expand All @@ -9,9 +9,9 @@ import config from '../../../config';
import limiter from '../limiter';
import { IEndpoint } from '../endpoints';

export default async (ctx: Router.RouterContext) => {
ctx.set('Access-Control-Allow-Origin', config.url);
ctx.set('Access-Control-Allow-Credentials', 'true');
export default async (request: Fastify.FastifyRequest, reply: Fastify.FastifyReply) => {
reply.header('Access-Control-Allow-Origin', config.url);
reply.header('Access-Control-Allow-Credentials', 'true');

const ep = {
name: 'signin',
Expand All @@ -24,27 +24,27 @@ export default async (ctx: Router.RouterContext) => {
}
} as IEndpoint;

await limiter(ep, undefined, ctx.ip).catch(e => {
ctx.throw(429);
await limiter(ep, undefined, request.ip).catch(e => {
reply.tooManyRequests();
});

const body = ctx.request.body;
const body = request.body as any; // TODO
const username = body['username'];
const password = body['password'];
const token = body['token'];

if (typeof username != 'string') {
ctx.status = 400;
reply.badRequest();
return;
}

if (typeof password != 'string') {
ctx.status = 400;
reply.badRequest();
return;
}

if (token != null && typeof token != 'string') {
ctx.status = 400;
reply.badRequest();
return;
}

Expand All @@ -59,10 +59,8 @@ export default async (ctx: Router.RouterContext) => {
}
}) as ILocalUser;

if (user === null) {
ctx.throw(404, {
error: 'user not found'
});
if (user == null) {
reply.notFound('user not found');
return;
}

Expand All @@ -78,27 +76,23 @@ export default async (ctx: Router.RouterContext) => {
});

if (verified) {
signin(ctx, user);
signin(request, reply, user); // TODO
} else {
ctx.throw(403, {
error: 'invalid token'
});
reply.unauthorized('invalid token');
}
} else {
signin(ctx, user);
signin(request, reply, user);
}
} else {
ctx.throw(403, {
error: 'incorrect password'
});
reply.unauthorized('incorrect password');
}

// Append signin history
const record = await Signin.insert({
createdAt: new Date(),
userId: user._id,
ip: ctx.ip,
headers: ctx.headers,
ip: request.ip,
headers: request.headers,
success: same
});

Expand Down
Loading