Skip to content

Commit

Permalink
Merge pull request #6 from version1-workspace/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
version-1 committed Mar 27, 2024
2 parents 83598e4 + ae76520 commit bfcfa7a
Show file tree
Hide file tree
Showing 22 changed files with 350 additions and 44 deletions.
4 changes: 4 additions & 0 deletions api/e2e
@@ -0,0 +1,4 @@
#!/bin/bash

NODE_ENV=test node_modules/.bin/jest --config ./test/jest-e2e.json --detectOpenHandles $@

13 changes: 13 additions & 0 deletions api/src/db/cli/seeds/index.ts
Expand Up @@ -13,13 +13,26 @@ export const seed = async ({ appFactory, dataSource, logger }) => {
logger.info('connection is establised');

const users = [];
const userIds = [
'fa66f863-1040-48bd-a156-11bb7cce796e',
'4e5eb084-0499-47f8-a0d9-79603002e1bd',
'95bab443-1fda-4d63-90ca-2e13296650af',
'0060f247-3223-4512-9ce1-6bfaa18a2579',
'4195c4df-47fd-4b1b-b952-c7c38aa8f27f',
'f4d43b04-998d-4fcc-9afc-0dcbd0a5673a',
'1fe031c0-bf34-4684-ae29-baa9cf242398',
'03129556-4243-4c68-bdc5-b71c0cb129a3',
'c45beb14-3b18-4841-b1cc-ba6a84ac3067',
'9be550a1-7d6f-4488-af0d-a60989e90d3a',
];
await dataSource.transaction(async (manager: EntityManager) => {
logger.info('[START] users ========');
for (let i = 1; i <= 10; i++) {
const timestamp = new Date();
const user = manager.create(User, {
username: `user ${i}`,
email: `user.${i}@example.com`,
uuid: userIds[i - 1],
createdAt: timestamp,
updatedAt: timestamp,
status: 'active',
Expand Down
11 changes: 5 additions & 6 deletions api/src/domains/auth/auth.controller.ts
Expand Up @@ -28,13 +28,13 @@ export class AuthController {
@Res({ passthrough: true }) res: Response,
) {
const { email, password, rememberMe } = body;
const json = await this.authService.signIn(email, password);
const data = await this.authService.signIn(email, password);

if (rememberMe) {
this.setRefreshToken(res, json.refreshToken);
this.setRefreshToken(res, data.refreshToken);
}

return json;
return { data };
}

@Public()
Expand All @@ -54,12 +54,12 @@ export class AuthController {
});
if (!result) {
response.status(401);
return { message: 'invalid refresh token' };
return { message: 'invalid refresh token or uuid' };
}

this.setRefreshToken(response, result.refreshToken);

return result;
return { data: result };
} catch (e) {
response.status(401);
this.loggerService.logger.info(e);
Expand All @@ -72,7 +72,6 @@ export class AuthController {
@Delete('refresh')
async clearRefreshToken(@Res({ passthrough: true }) response: Response) {
this.removeRefreshToken(response);
response.status(200);
}

private extractTokenFromCookie(request: Request): string | undefined {
Expand Down
10 changes: 5 additions & 5 deletions api/src/domains/auth/auth.service.ts
Expand Up @@ -51,17 +51,17 @@ export class AuthService {
}

async token(user: User) {
await this.usersService.updateRefreshToken(user);
const _user = await this.usersService.updateRefreshToken(user);

const payload = {
sub: user.username,
refreshToken: user.refreshToken,
sub: _user.username,
refreshToken: _user.refreshToken,
};

return {
uuid: user.uuid,
uuid: _user.uuid,
accessToken: await this.jwtService.signAsync(payload),
refreshToken: user.refreshToken,
refreshToken: _user.refreshToken,
};
}

Expand Down
11 changes: 11 additions & 0 deletions api/src/domains/users/user.entity.ts
Expand Up @@ -36,4 +36,15 @@ export class User extends Base {

@OneToMany(() => Task, (task) => task.user)
tasks: Task[];

get serialize() {
return {
uuid: this.uuid,
username: this.username,
email: this.email,
status: this.status,
createdAt: this.createdAt,
updatedAt: this.updatedAt,
};
}
}
2 changes: 1 addition & 1 deletion api/src/domains/users/users.controller.ts
Expand Up @@ -6,6 +6,6 @@ export class UserController {
async show(@Req() request: Request): Promise<Record<string, any>> {
const user = request['user'];

return { data: user };
return { data: user.serialize };
}
}
2 changes: 2 additions & 0 deletions api/src/domains/users/users.service.ts
Expand Up @@ -68,6 +68,8 @@ export class UsersService {
async updateRefreshToken(user: User) {
user.refreshToken = await this.hashRefreshToken();
await this.manager.save(user);

return user;
}

async hash(user: User, password: string) {
Expand Down
105 changes: 105 additions & 0 deletions api/test/auth/auth.e2e-spec.ts
@@ -0,0 +1,105 @@
import { INestApplication } from '@nestjs/common';
import { prepareApp } from '../helper';
import request from '../helper/request';

describe('AuthController (e2e)', () => {
let app: INestApplication;
let server: any;

beforeAll(async () => {
app = await prepareApp([]);

server = app?.getHttpServer();
return;
});

describe('Post /auth/login', () => {
const subject = () => request(server).post('/auth/login');

it(`with right auth info, return 200`, async () => {
const response = await subject().send({
email: 'user.1@example.com',
password: 'AndyBobCharrie',
});

expect(response.status).toEqual(200);
expect(response.body).toMatchObject({
data: {
uuid: 'fa66f863-1040-48bd-a156-11bb7cce796e',
accessToken: expect.any(String),
refreshToken: expect.any(String),
},
});
});

it(`with wrong mail address, return 401: 1`, async () => {
const response = await subject().send({
email: 'wrong@example.com',
password: 'AndyBobCharrie',
});

expect(response.status).toEqual(401);
});

it(`with wrong password, return 401: 2`, async () => {
const response = await subject().send({
email: 'user.1@example.com',
password: 'wrongpassword',
});

expect(response.status).toEqual(401);
});
});

describe('Post /auth/refresh', () => {
const subject = () => request(server).post('/auth/refresh');

it(`with right refreshToken, return 200`, async () => {
const client = request(server);
const r1 = await client.post('/auth/login').send({
email: 'user.1@example.com',
password: 'AndyBobCharrie',
});

expect(r1.status).toEqual(200);
const { uuid, refreshToken } = r1.body.data;

const response = await client
.post('/auth/refresh')
.set('Cookie', `refreshToken=${refreshToken}`)
.send({
uuid,
});
expect(response.status).toEqual(200);
expect(response.body).toMatchObject({
data: {
uuid: 'fa66f863-1040-48bd-a156-11bb7cce796e',
accessToken: expect.any(String),
refreshToken: expect.any(String),
},
});
expect(response.body.data.refreshToken).not.toEqual(refreshToken);
});

it(`with wrong refreshToken, return 401: 1`, async () => {
const uuid = 'fa66f863-1040-48bd-a156-11bb7cce796e';
const response = await subject().withAuth().send({
uuid,
});

expect(response.status).toEqual(401);
});
});

describe('Delete /auth/refresh', () => {
const subject = () => request(server).delete('/auth/refresh');
it(`return 200`, async () => {
const response = await subject();
expect(response.status).toEqual(200);
});
});

afterAll(async () => {
await app.close();
});
});
6 changes: 3 additions & 3 deletions api/test/helper/databse.ts
@@ -1,6 +1,6 @@
import { INestApplication } from '@nestjs/common';
import { getDataSourceToken, getRepositoryToken } from '@nestjs/typeorm';
import { DataSource, EntityManager } from 'typeorm';
import { DataSource, EntityManager, Repository } from 'typeorm';

class RollbackError extends Error {}

Expand Down Expand Up @@ -28,6 +28,6 @@ export const withCleanup = async (
}
};

export const getRepository = (app: INestApplication<any>, token: any) => {
return app.get(getRepositoryToken(token));
export const getRepository = <T>(app: INestApplication<any>, token: any) => {
return app.get(getRepositoryToken(token)) as Repository<T>;
};
2 changes: 2 additions & 0 deletions api/test/helper/index.ts
Expand Up @@ -5,6 +5,7 @@ import { dataSourceOptions } from '../../src/db/config';
import appConfig from '../../src/config/app.config';
import { AppModule } from '../../src/app.module';
import { Test } from './request';
import cookieParser from 'cookie-parser';

export const checkNoAuthBehavior = (test: () => Test): [string, () => Test] => [
'no auth token, return 401',
Expand Down Expand Up @@ -39,6 +40,7 @@ export const prepareApp = async (providers: OverrideProvier[]) => {

const module = await moduleFixture.compile();
const app = module.createNestApplication();
app.use(cookieParser());
await app.init();

return app;
Expand Down
6 changes: 5 additions & 1 deletion api/test/helper/request.ts
Expand Up @@ -27,7 +27,7 @@ export type Test = ExtendedTest & supertest.Test;
const testFactory = (test: supertest.Test): Test =>
new Proxy(new ExtendedTest(test), handler) as Test;

class ExtendedRequest {
export class ExtendedRequest {
_request: supertest.SuperTest<supertest.Test>;

constructor(app: any) {
Expand All @@ -49,6 +49,10 @@ class ExtendedRequest {
put(path: string) {
return testFactory(this._request.put(path));
}

delete(path: string) {
return testFactory(this._request.delete(path));
}
}

export default function (app: any) {
Expand Down
5 changes: 1 addition & 4 deletions api/test/users/users.e2e-spec.ts
Expand Up @@ -31,14 +31,11 @@ describe('UsersController (e2e)', () => {
expect(response.status).toEqual(200);
expect(response.body).toMatchObject({
data: {
id: 1,
uuid: expect.any(String),
uuid: 'fa66f863-1040-48bd-a156-11bb7cce796e',
createdAt: expect.any(String),
updatedAt: expect.any(String),
username: 'user 1',
email: 'user.1@example.com',
password: expect.any(String),
refreshToken: expect.any(String),
status: 'active',
},
});
Expand Down
8 changes: 4 additions & 4 deletions frontend/core/src/app/layout.tsx
Expand Up @@ -3,19 +3,19 @@ import { Inter } from "next/font/google";

const inter = Inter({ subsets: ["latin"] });

import { Metadata } from 'next'
import { Metadata } from "next";

export const metadata: Metadata = {
title: 'Turbo',
}
title: "Turbo",
};

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<html lang="ja">
<body className={inter.className}>{children}</body>
</html>
);
Expand Down
8 changes: 5 additions & 3 deletions frontend/core/src/app/login/content.tsx
@@ -1,6 +1,6 @@
"use client";
import { useSearchParams } from "next/navigation";
import { useEffect } from "react";
import { useEffect, useState } from "react";
import styles from "./page.module.css";
import Header from "@/components/shared/header/public";
import Footer from "@/components/shared/footer";
Expand All @@ -12,15 +12,17 @@ import { useToast } from "@/lib/toast/hook";
const Content = () => {
const searchParams = useSearchParams();
const toast = useToast();
const [rendered, setRendered] = useState(false);

useEffect(() => {
const error = searchParams.get("error");
if (error) {
if (error === "loginRequired") {
if (!rendered && error === "loginRequired") {
toast.error("ログインが必要です");
}
}
}, [searchParams, toast]);
setRendered(true);
}, [searchParams, toast, rendered]);

return (
<>
Expand Down
26 changes: 26 additions & 0 deletions frontend/core/src/app/login/layout.tsx
@@ -0,0 +1,26 @@
import { Inter } from "next/font/google";

const inter = Inter({ subsets: ["latin"] });

import { Metadata } from "next";
import AuthContainer from "@/components/auth";

export const metadata: Metadata = {
title: "Turbo",
};

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<AuthContainer isPublic>
<html lang="ja">
<body className={inter.className}>{children}</body>
</html>
</AuthContainer>
);
}

export const dynamic = "error";
1 change: 0 additions & 1 deletion frontend/core/src/app/main/layout.module.css
@@ -1,5 +1,4 @@
.body {
height: calc(100vh - 72px);
}

.main {
Expand Down

0 comments on commit bfcfa7a

Please sign in to comment.