Skip to content

Commit

Permalink
Merge pull request #55 from codex-team/feat/reply
Browse files Browse the repository at this point in the history
chore(fastify): router handlers updated
  • Loading branch information
neSpecc authored Aug 23, 2023
2 parents 1ee8a84 + bdbc77a commit cc90ee2
Show file tree
Hide file tree
Showing 12 changed files with 192 additions and 134 deletions.
16 changes: 15 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
{
"extends": [
"codex/ts"
]
],
"rules": {
"@typescript-eslint/naming-convention": [
"error",
{
"selector": "property",
"format": ["camelCase", "PascalCase"],
"filter": {
// Allow "2xx" as a property name, used in the API response schema
"regex": "^(2xx)$",
"match": false
}
}
]
}
}
23 changes: 23 additions & 0 deletions src/presentation/http/decorators/notFound.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { StatusCodes } from 'http-status-codes';
import type { FastifyReply } from 'fastify';

/**
* Custom method for sending 404 error
*
* @example
*
* if (note === null) {
* return fastify.notFound(reply, 'Note not found');
* }
*
* @param reply - fastify reply instance
* @param message - custom message
*/
export default async function notFound(reply: FastifyReply, message = 'Not found'): Promise<void> {
await reply
.code(StatusCodes.NOT_FOUND)
.type('application/json')
.send({
message,
});
}
25 changes: 25 additions & 0 deletions src/presentation/http/fastify.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* eslint-disable @typescript-eslint/no-unused-vars, no-unused-vars */
import type * as fastify from 'fastify';
import type * as http from 'http';

declare module 'fastify' {
export interface FastifyInstance<
HttpServer = http.Server,
HttpRequest = http.IncomingMessage,
HttpResponse = http.ServerResponse
> {
/**
* Custom method for sending 404 error
*
* @example
*
* if (note === null) {
* return fastify.notFound(reply, 'Note not found');
* }
*
* @param reply - Fastify reply object
* @param message - Optional message to send. If not specified, default message will be sent
*/
notFound: (reply: fastify.FastifyReply, message?: string) => Promise<void>;
}
}
12 changes: 12 additions & 0 deletions src/presentation/http/http-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import cookie from '@fastify/cookie';
import UserRouter from '@presentation/http/router/user.js';
import AIRouter from './router/ai.js';
import EditorToolsRouter from './router/editorTools.js';
import NotFoundDecorator from './decorators/notFound.js';
import { addSchema } from './schema/index.js';

const appServerLogger = getLogger('appServer');

Expand Down Expand Up @@ -141,6 +143,16 @@ export default class HttpServer implements API {
origin: this.config.allowedOrigins,
});

/**
* Add Fastify schema for validation and serialization
*/
addSchema(this.server);

/**
* Custom method for sending 404 error
*/
this.server.decorate('notFound', NotFoundDecorator);

await this.server.listen({
host: this.config.host,
port: this.config.port,
Expand Down
23 changes: 10 additions & 13 deletions src/presentation/http/middlewares/authRequired.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { preHandlerHookHandler } from 'fastify';
import type AuthService from '@domain/service/auth.js';
import { StatusCodes } from 'http-status-codes';
import type { ErrorResponse } from '@presentation/http/types/HttpResponse.js';
import notEmpty from '@infrastructure/utils/notEmpty.js';

/**
Expand All @@ -25,12 +24,11 @@ export default (authService: AuthService): preHandlerHookHandler => {
* If authorization header is not present, return unauthorized response
*/
if (!notEmpty(authorizationHeader)) {
const response: ErrorResponse = {
status: StatusCodes.UNAUTHORIZED,
message: 'Missing authorization header',
};

return reply.send(response);
return reply
.code(StatusCodes.UNAUTHORIZED)
.send({
message: 'Missing authorization header',
});
}

/**
Expand All @@ -43,12 +41,11 @@ export default (authService: AuthService): preHandlerHookHandler => {

done();
} catch (error) {
const response: ErrorResponse = {
status: StatusCodes.UNAUTHORIZED,
message: 'Invalid access token',
};

return reply.send(response);
return reply
.code(StatusCodes.UNAUTHORIZED)
.send({
message: 'Invalid access token',
});
}
};
};
35 changes: 15 additions & 20 deletions src/presentation/http/router/auth.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { FastifyPluginCallback } from 'fastify';
import type AuthService from '@domain/service/auth.js';
import type { ErrorResponse, SuccessResponse } from '@presentation/http/types/HttpResponse.js';
import type { ErrorResponse } from '@presentation/http/types/HttpResponse.js';
import { StatusCodes } from 'http-status-codes';
import type AuthSession from '@domain/entities/authSession.js';

Expand Down Expand Up @@ -38,6 +38,7 @@ const AuthRouter: FastifyPluginCallback<AuthRouterOptions> = (fastify, opts, don
*/
fastify.post<{
Body: AuthOptions;
Reply: AuthSession | ErrorResponse;
}>('/', async (request, reply) => {
const { token } = request.body;

Expand All @@ -47,42 +48,36 @@ const AuthRouter: FastifyPluginCallback<AuthRouterOptions> = (fastify, opts, don
* Check if session is valid
*/
if (!userSession) {
const response: ErrorResponse = {
status: StatusCodes.UNAUTHORIZED,
message: 'Session is not valid',
};

return reply.send(response);
return reply
.code(StatusCodes.UNAUTHORIZED)
.send({
message: 'Session is not valid',
});
}

const accessToken = opts.authService.signAccessToken({ id: userSession.userId });

await opts.authService.removeSessionByRefreshToken(token);
const refreshToken = await opts.authService.signRefreshToken(userSession.userId);

const response: SuccessResponse<AuthSession> = {
data: {
accessToken,
refreshToken,
},
};

return reply.send(response);
return reply.send({
accessToken,
refreshToken,
});
});

/**
* Route for logout, removes session from database by refresh token
*/
fastify.delete<{
Body: AuthOptions;
Reply: { ok: boolean }
}>('/', async (request, reply) => {
await opts.authService.removeSessionByRefreshToken(request.body.token);

const response: SuccessResponse<string> = {
data: 'OK',
};

await reply.status(StatusCodes.OK).send(response);
return reply.status(StatusCodes.OK).send({
ok: true,
});
});
done();
};
Expand Down
Loading

0 comments on commit cc90ee2

Please sign in to comment.