Skip to content

Commit 113aa69

Browse files
authored
Merge branch 'develop' into feature-fe-#295
2 parents f734740 + b63ee0f commit 113aa69

34 files changed

+447
-33
lines changed

apps/backend/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"@nestjs/swagger": "^8.0.5",
3535
"@nestjs/typeorm": "^10.0.2",
3636
"@nestjs/websockets": "^10.4.8",
37+
"@theinternetfolks/snowflake": "^1.3.0",
3738
"@types/multer": "^1.4.12",
3839
"class-transformer": "^0.5.1",
3940
"class-validator": "^0.14.1",
@@ -43,6 +44,7 @@
4344
"passport-naver": "^1.0.6",
4445
"path": "^0.12.7",
4546
"pg": "^8.13.1",
47+
"prosemirror-view": "^1.37.0",
4648
"reflect-metadata": "^0.1.13",
4749
"rxjs": "^7.8.1",
4850
"socket.io": "^4.8.1",
@@ -96,4 +98,4 @@
9698
"coverageDirectory": "../coverage",
9799
"testEnvironment": "node"
98100
}
99-
}
101+
}

apps/backend/src/app.module.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,16 @@ import { Page } from './page/page.entity';
1010
import { Edge } from './edge/edge.entity';
1111
import { Node } from './node/node.entity';
1212
import { User } from './user/user.entity';
13+
import { Workspace } from './workspace/workspace.entity';
14+
import { Role } from './role/role.entity';
1315
import { YjsModule } from './yjs/yjs.module';
1416
import * as path from 'path';
1517
import { ServeStaticModule } from '@nestjs/serve-static';
1618
import { UploadModule } from './upload/upload.module';
1719
import { AuthModule } from './auth/auth.module';
1820
import { UserModule } from './user/user.module';
21+
import { WorkspaceModule } from './workspace/workspace.module';
22+
import { RoleModule } from './role/role.module';
1923

2024
@Module({
2125
imports: [
@@ -36,7 +40,7 @@ import { UserModule } from './user/user.module';
3640
username: configService.get('DB_USER'),
3741
password: configService.get('DB_PASSWORD'),
3842
database: configService.get('DB_NAME'),
39-
entities: [Node, Page, Edge, User],
43+
entities: [Node, Page, Edge, User, Workspace, Role],
4044
logging: true,
4145
synchronize: true,
4246
}),
@@ -48,6 +52,8 @@ import { UserModule } from './user/user.module';
4852
UploadModule,
4953
AuthModule,
5054
UserModule,
55+
WorkspaceModule,
56+
RoleModule,
5157
],
5258
controllers: [AppController],
5359
providers: [AppService],

apps/backend/src/auth/auth.controller.spec.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,18 @@ describe('AuthController', () => {
8989
.spyOn(jwtService, 'verify')
9090
.mockReturnValue({ sub: 1, provider: 'naver' });
9191
const req = { body: { refreshToken: 'valid-refresh-token' } } as any;
92+
const res = {
93+
cookie: jest.fn(),
94+
json: jest.fn(),
95+
} as any;
9296

93-
const result = await authController.refreshAccessToken(req);
94-
expect(result).toEqual({
97+
await authController.refreshAccessToken(req, res);
98+
expect(res.cookie).toHaveBeenCalledWith('accessToken', 'test-token', {
99+
httpOnly: true,
100+
maxAge: 3600000,
101+
});
102+
expect(res.json).toHaveBeenCalledWith({
95103
message: '새로운 Access Token 발급 성공',
96-
accessToken: 'test-token',
97104
});
98105
});
99106
});

apps/backend/src/auth/auth.controller.ts

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
import { Controller, Get, UseGuards, Req, Post } from '@nestjs/common';
1+
import { Controller, Get, UseGuards, Req, Res, Post } from '@nestjs/common';
22
import { AuthGuard } from '@nestjs/passport';
33
import { AuthService } from './auth.service';
44
import { JwtService } from '@nestjs/jwt';
55
import { JwtAuthGuard } from './guards/jwt-auth.guard';
6+
import { Response } from 'express';
7+
8+
const HOUR = 60 * 60 * 1000;
9+
const WEEK = 7 * 24 * 60 * 60 * 1000;
610

711
@Controller('auth')
812
export class AuthController {
@@ -20,19 +24,21 @@ export class AuthController {
2024

2125
@Get('naver/callback')
2226
@UseGuards(AuthGuard('naver'))
23-
async naverCallback(@Req() req) {
27+
async naverCallback(@Req() req, @Res() res: Response) {
2428
// 네이버 인증 후 사용자 정보 반환
2529
const user = req.user;
2630
// TODO: 후에 권한 (workspace 조회, 편집 기능)도 payload에 추가
2731
const payload = { sub: user.id, provider: user.provider };
2832
const accessToken = this.jwtService.sign(payload, { expiresIn: '1h' });
2933
const refreshToken = this.jwtService.sign(payload, { expiresIn: '7d' });
30-
return {
31-
message: '네이버 로그인 성공',
32-
user,
33-
accessToken,
34-
refreshToken,
35-
};
34+
35+
// 토큰을 쿠키에 담아서 메인 페이지로 리디렉션
36+
res.cookie('accessToken', accessToken, { httpOnly: true, maxAge: HOUR });
37+
res.cookie('refreshToken', refreshToken, {
38+
httpOnly: true,
39+
maxAge: WEEK,
40+
});
41+
res.redirect(302, '/');
3642
}
3743

3844
@Get('kakao')
@@ -44,34 +50,39 @@ export class AuthController {
4450

4551
@Get('kakao/callback')
4652
@UseGuards(AuthGuard('kakao'))
47-
async kakaoCallback(@Req() req) {
53+
async kakaoCallback(@Req() req, @Res() res: Response) {
4854
// 카카오 인증 후 사용자 정보 반환
4955
const user = req.user;
5056
// TODO: 후에 권한 (workspace 조회, 편집 기능)도 payload에 추가
5157
const payload = { sub: user.id, provider: user.provider };
5258
const accessToken = this.jwtService.sign(payload, { expiresIn: '1h' });
5359
const refreshToken = this.jwtService.sign(payload, { expiresIn: '7d' });
54-
return {
55-
message: '카카오 로그인 성공',
56-
user,
57-
accessToken,
58-
refreshToken,
59-
};
60+
61+
// 토큰을 쿠키에 담아서 메인 페이지로 리디렉션
62+
res.cookie('accessToken', accessToken, { httpOnly: true, maxAge: HOUR });
63+
res.cookie('refreshToken', refreshToken, {
64+
httpOnly: true,
65+
maxAge: WEEK,
66+
});
67+
res.redirect(302, '/');
6068
}
6169

6270
@Post('refresh')
63-
async refreshAccessToken(@Req() req) {
71+
async refreshAccessToken(@Req() req, @Res() res: Response) {
6472
const { refreshToken } = req.body;
6573

6674
const decoded = this.jwtService.verify(refreshToken, {
6775
secret: process.env.JWT_SECRET,
6876
});
6977
const payload = { sub: decoded.sub, provider: decoded.provider };
7078
const newAccessToken = this.jwtService.sign(payload, { expiresIn: '1h' });
71-
return {
79+
res.cookie('accessToken', newAccessToken, {
80+
httpOnly: true,
81+
maxAge: HOUR,
82+
});
83+
return res.json({
7284
message: '새로운 Access Token 발급 성공',
73-
accessToken: newAccessToken,
74-
};
85+
});
7586
}
7687

7788
// Example: 로그인한 사용자만 접근할 수 있는 엔드포인트

apps/backend/src/auth/auth.service.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,8 @@ export class AuthService {
2121
const user = this.userRepository.create(dto);
2222
return this.userRepository.save(user);
2323
}
24+
25+
async findUserBySnowflakeId(snowflakeId: string): Promise<User | null> {
26+
return await this.userRepository.findOneBy({ snowflakeId });
27+
}
2428
}

apps/backend/src/auth/strategies/naver.strategy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,6 @@ export class NaverStrategy extends PassportStrategy(Strategy, 'naver') {
2626
if (!user) {
2727
user = await this.authService.signUp(createUserDto);
2828
}
29-
return user; // req.user로 반환
29+
return user;
3030
}
3131
}

apps/backend/src/edge/edge.controller.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ describe('EdgeController', () => {
8383
page: null,
8484
outgoingEdges: [],
8585
incomingEdges: [],
86+
workspace: null,
8687
} as Node;
8788
const node4 = {
8889
id: 4,
@@ -92,6 +93,7 @@ describe('EdgeController', () => {
9293
page: null,
9394
outgoingEdges: [],
9495
incomingEdges: [],
96+
workspace: null,
9597
} as Node;
9698
const node5 = {
9799
id: 5,
@@ -101,6 +103,7 @@ describe('EdgeController', () => {
101103
page: null,
102104
outgoingEdges: [],
103105
incomingEdges: [],
106+
workspace: null,
104107
} as Node;
105108

106109
const expectedEdges = [

apps/backend/src/edge/edge.entity.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
JoinColumn,
88
} from 'typeorm';
99
import { Node } from '../node/node.entity';
10+
import { Workspace } from '../workspace/workspace.entity';
1011

1112
@Entity()
1213
export class Edge {
@@ -21,6 +22,12 @@ export class Edge {
2122
@JoinColumn({ name: 'to_node_id' })
2223
toNode: Node;
2324

25+
@ManyToOne(() => Workspace, (workspace) => workspace.edges, {
26+
onDelete: 'CASCADE',
27+
})
28+
@JoinColumn({ name: 'workspace_id' })
29+
workspace: Workspace;
30+
2431
// @Column({ nullable: true })
2532
// type: string;
2633

apps/backend/src/edge/edge.module.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { EdgeController } from './edge.controller';
44
import { TypeOrmModule } from '@nestjs/typeorm';
55
import { Edge } from './edge.entity';
66
import { EdgeRepository } from './edge.repository';
7-
import { NodeModule } from 'src/node/node.module';
7+
import { NodeModule } from '../node/node.module';
88

99
@Module({
1010
imports: [TypeOrmModule.forFeature([Edge]), forwardRef(() => NodeModule)],

apps/backend/src/edge/edge.repository.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,11 @@ export class EdgeRepository extends Repository<Edge> {
88
constructor(@InjectDataSource() private dataSource: DataSource) {
99
super(Edge, dataSource.createEntityManager());
1010
}
11+
12+
async findEdgesByWorkspace(workspaceId: number): Promise<Edge[]> {
13+
return this.find({
14+
where: { workspace: { id: workspaceId } },
15+
relations: ['fromNode', 'toNode'],
16+
});
17+
}
1118
}

0 commit comments

Comments
 (0)