Skip to content

Commit

Permalink
feat: Port from Express to Fastify
Browse files Browse the repository at this point in the history
  • Loading branch information
EntraptaJ committed Jun 12, 2020
1 parent b06a809 commit 9deb006
Show file tree
Hide file tree
Showing 14 changed files with 598 additions and 544 deletions.
654 changes: 242 additions & 412 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
"actions-on-google": "^2.12.0"
},
"dependencies": {
"body-parser": "^1.19.0",
"express": "^4.17.1"
"fastify": "^2.14.1",
"fastify-formbody": "^3.2.0"
},
"devDependencies": {
"@k-foss/ts-esnode": "1.6.0",
Expand Down
89 changes: 0 additions & 89 deletions src/Lab/Auth.ts

This file was deleted.

103 changes: 103 additions & 0 deletions src/Lab/Library/Fastify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Lab/Library/Fastify.ts
import fastify, {
FastifyInstance,
RequestHandler,
RouteOptions,
DefaultBody,
DefaultHeaders,
DefaultParams,
DefaultQuery,
} from 'fastify';
import fastifyFormBody from 'fastify-formbody';
import { findModuleFiles } from '../Utils/moduleFileFinder';
import { IncomingMessage, ServerResponse } from 'http';

/**
* Fastify Route
*/
export abstract class Route<
Body = DefaultBody,
Query = DefaultQuery,
HttpRequest = IncomingMessage,
HttpResponse = ServerResponse,
Params = DefaultParams,
Headers = DefaultHeaders
> {
/**
* Fastify Route Options
* https://www.fastify.io/docs/latest/Routes/#routes-option
*/
public options: Omit<RouteOptions, 'handler' | 'preHandler'>;

/**
* Fastify Route Handler
*/
abstract handler: RequestHandler<
HttpRequest,
HttpResponse,
Query,
Params,
Headers,
Body
>;
}

/**
* Example route so that the findModuleFiles type isn't messed up
*/
export class ExampleRoute extends Route {
public handler: Route['handler'] = async (request, reply) => {
console.log('HelloWorld');

return 'example';
};
}

interface RouteModule {
default: typeof ExampleRoute;
}

export async function getRoutes(): Promise<Route[]> {
/**
* Get all Modules under `Modules` that match `*Route.ts`
*/
const routeModules = await findModuleFiles<RouteModule>(/.*Route\.ts/);

// Destructure the default export from all matching route Modules, and construct the class
return routeModules.flatMap(({ default: RouteClass }) => new RouteClass());
}

/**
* Create a Fastify Web Server
*/
export async function createFastifyServer(): Promise<FastifyInstance> {
const webServer = fastify();

webServer.register(fastifyFormBody);

/**
* Get All Route Modules.
*/
const routes = await getRoutes();

/**
* For each Route Module in routes destructure handler and options and register as a webServer Route.
*/
routes.map(({ handler, options }) => {
return webServer.route({
...options,
handler,
});
});

return webServer;
}

/**
* Creates a fastify Testing Chain https://www.fastify.io/docs/latest/Testing/
*/
export async function createFastifyTestServer() {
const webServer = await createFastifyServer();

return webServer;
}
26 changes: 26 additions & 0 deletions src/Lab/Modules/Auth/AuthRoute.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Lab/Modules/Auth/AuthRoute.ts
import { Route } from '../../Library/Fastify';
import * as util from 'util';

/**
* Example route so that the findModuleFiles type isn't messed up
*/
export default class AuthRoute extends Route {
public options: Route['options'] = {
method: 'GET',
url: '/auth',
};

public handler: Route['handler'] = async (request, reply) => {
const responseurl = util.format(
'%s?code=%s&state=%s',
decodeURIComponent(request.query.redirect_uri),
'xxxxxx',
request.query.state,
);
console.log(`Set redirect as ${responseurl}`);
return reply.redirect(
`/login?responseurl=${encodeURIComponent(responseurl)}`,
);
};
}
44 changes: 44 additions & 0 deletions src/Lab/Modules/Auth/LoginPageRoute.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Lab/Modules/Auth/AuthRoute.ts
import { Route } from '../../Library/Fastify';

/**
* Example route so that the findModuleFiles type isn't messed up
*/
export default class LoginPageRoute extends Route<
any,
{ responseurl: string }
> {
public options: Route['options'] = {
method: 'GET',
url: '/login',
schema: {
querystring: {
type: 'object',
required: ['responseurl'],
properties: {
name: { type: 'string' },
},
},
},
};

public handler: Route<any, { responseurl: string }>['handler'] = async (
request,
reply,
) => {
const responseUrl = request.query.responseurl;

console.debug(`loginPage:`, request, reply);

reply.type('text/html');

return `<html>
<body>
<form action="/login" method="post">
<input type="hidden" name="responseurl" value="${responseUrl}" />
<button type="submit" style="font-size:14pt">Link this service to Google</button>
</form>
</body>
</html>`;
};
}
34 changes: 34 additions & 0 deletions src/Lab/Modules/Auth/LoginRoute.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Lab/Modules/Auth/AuthRoute.ts
import { Route } from '../../Library/Fastify';

/**
* Example route so that the findModuleFiles type isn't messed up
*/
export default class LoginRoute extends Route<{ responseurl: string }> {
public options: Route['options'] = {
method: 'POST',
url: '/login',
schema: {
body: {
type: 'object',
required: ['responseurl'],
properties: {
responseurl: { type: 'string' },
},
},
},
};

public handler: Route<{ responseurl: string }>['handler'] = async (
request,
reply,
) => {
// Here, you should validate the user account.
// In this sample, we do not do that.
const responseurl = decodeURIComponent(request.body.responseurl);

console.log(request, reply);

reply.redirect(responseurl);
};
}
47 changes: 47 additions & 0 deletions src/Lab/Modules/Auth/TokenRoute.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Lab/Modules/Auth/TokenRoute.ts
import { Route } from '../../Library/Fastify';

/**
* Example route so that the findModuleFiles type isn't messed up
*/
export default class LoginPageRoute extends Route<
{ grant_type: string },
{ grant_type: string }
> {
public options: Route['options'] = {
method: 'POST',
url: '/token',
};

public handler: Route<
{ grant_type: string },
{ grant_type: string }
>['handler'] = async (request, reply) => {
console.log('token');

const grantType = request.query.grant_type
? request.query.grant_type
: request.body.grant_type;

const secondsInDay = 86400; // 60 * 60 * 24
const HTTP_STATUS_OK = 200;

let obj;
if (grantType === 'authorization_code') {
obj = {
token_type: 'bearer',
access_token: '123access',
refresh_token: '123refresh',
expires_in: secondsInDay,
};
} else if (grantType === 'refresh_token') {
obj = {
token_type: 'bearer',
access_token: '123access',
expires_in: secondsInDay,
};
}

reply.status(200).type('application/json').send(obj);
};
}
Loading

0 comments on commit 9deb006

Please sign in to comment.