Skip to content

Commit

Permalink
Merge branch 'develop' into feat/#289/ncp-object-storage
Browse files Browse the repository at this point in the history
  • Loading branch information
Jieun1ee authored Dec 1, 2024
2 parents 094f222 + 414f9ef commit 2116d41
Show file tree
Hide file tree
Showing 121 changed files with 2,614 additions and 900 deletions.
37 changes: 21 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@
<img width="600" alt="메인 배너" src="https://github.com/user-attachments/assets/eb4c89c8-6870-4114-bddc-796b51bd7163">
<br/>

### [:ledger: 팀 노션](https://www.notion.so/simeunseo/9-Ticle-12e599a6f0d2804682ccd2251248a435?pvs=4) | [:mag: 위키](https://github.com/boostcampwm-2024/web21-boostproject/wiki) | [🎨 피그마](https://www.figma.com/design/nw74detTvjXGrDP2cfdmwp/TICLE-%EB%94%94%EC%9E%90%EC%9D%B8?node-id=32-4477&t=3FCCnBpgQXMZs63X-1) | [🗓️ 스프린트 백로그](https://github.com/orgs/boostcampwm-2024/projects/82/views/7) | [🧪 스토리북](https://673a0cccd15a760db778c591-kxdpixadfg.chromatic.com/?path=/docs/common-dialog--docs)
### [:ledger: 팀 노션](https://www.notion.so/simeunseo/9-Ticle-12e599a6f0d2804682ccd2251248a435?pvs=4) | [:mag: 위키](https://github.com/boostcampwm-2024/web21-boostproject/wiki) | [🎨 피그마](https://www.figma.com/design/nw74detTvjXGrDP2cfdmwp/TICLE-%EB%94%94%EC%9E%90%EC%9D%B8?node-id=32-4477&t=3FCCnBpgQXMZs63X-1) | [🗓️ 스프린트 백로그](https://github.com/orgs/boostcampwm-2024/projects/82/views/7) | [🧪 스토리북](https://673a0cccd15a760db778c591-ttyfhdnavn.chromatic.com/)

</div>

# 🖧 시스템 아키텍처

<img width="6176" alt="아키텍처" src="https://github.com/user-attachments/assets/dc0a4690-e906-4068-b391-f15f6fcdc9a6">
![Cloudcraft Image (4)](https://github.com/user-attachments/assets/1e5874ee-2485-4e89-90a0-cebb47621c77)

# 🏃 작업 진행 상황

- [🆕 4주차 발표자료](https://simeunseo.notion.site/4-1ccf63bab4b14fd8b249f5d7c7cd7e53?pvs=4)
- [🆕 5주차 발표자료](https://www.figma.com/slides/kNBDNutRalcIkuGWo3LHd9/5%EC%A3%BC%EC%B0%A8-%EB%8D%B0%EB%AA%A8-%EB%B0%9C%ED%91%9C%EC%9E%90%EB%A3%8C?node-id=1-23&t=LH1JpAKm2xhJUq9q-1)
- [4주차 발표자료](https://simeunseo.notion.site/4-1ccf63bab4b14fd8b249f5d7c7cd7e53?pvs=4)
- [3주차 발표자료](https://simeunseo.notion.site/3-0df689ca7cd3407b89a93284854a54b8?pvs=4)
- [2주차 발표자료](https://simeunseo.notion.site/2-137599a6f0d2809fa498fa1cc31d97f9?pvs=4)
- [1주차 발표자료](https://simeunseo.notion.site/1-130599a6f0d2804597e0c55e8ee33920?pvs=4)
Expand All @@ -28,29 +29,33 @@
카메라와 음성을 통해 발표자와 참여자가 실시간으로 지식을 공유할 수 있습니다.
화면 공유 기능으로 더욱 효과적인 지식 전달이 가능합니다.

<img width="600" alt="화면공유" src="https://github.com/user-attachments/assets/1e82dd2b-e019-44d8-a85d-12a961de38fc">
![티클 시작](https://github.com/user-attachments/assets/f0c3f9b3-cbda-49b2-a6a3-4c77fd569129)

### **✔️ 티클 신청 시스템**
### **티클 목록**

참여자는 티클 리스트에서 원하는 티클을 선택하고 신청할 수 있습니다.
발표자는 자신의 티클 관리 페이지에서 신청자를 확인하고 발표를 시작할 수 있습니다.
티클 목록을 확인하고 정렬, 필터링할 수 있습니다.

<img width="600" alt="티클 리스트(메인)" src="https://github.com/user-attachments/assets/08449986-eda7-408a-845a-cd21da718837">
<div>
<img width="600" alt="개설한 티클 관리" src="https://github.com/user-attachments/assets/9dc9627e-c806-4560-82d0-a949b1dca153">
<img width="600" alt="신청한 티클 관리" src="https://github.com/user-attachments/assets/81cf68b1-4006-4c05-9984-29d450106b2c">
</div>
![티클목록4](https://github.com/user-attachments/assets/ac4cbba2-5552-43d3-8361-4159f9c3a48b)

### **✔️ 티클 개설**

발표자는 원하는 티클을 개설할 수 있습니다.

![티클개설4](https://github.com/user-attachments/assets/56b33a06-a85d-49a2-b830-c46d44879ce2)

### **✔️ 티클 소개 페이지**
### **✔️ 티클 관리 대시보드**

티클 내용과 발표자 정보를 제공합니다.
참여자는 신청한 티클을 대시보드에서 확인하고 참가할 수 있습니다.
발표자는 대시보드에서 신청자를 확인하고 발표를 시작할 수 있습니다.

<img width="600" alt="티클 개설하기" src="https://github.com/user-attachments/assets/566412dc-2045-4260-bfac-309350ac040e">
![대시보드4](https://github.com/user-attachments/assets/d6f57d72-135e-4e36-bba9-784a1fd368a2)

### **✔️ AI 요약 기능**

CLOVA API를 이용해 티클 내용을 요약하여 제공합니다.

![image](https://github.com/user-attachments/assets/825a5ea0-e873-40e1-a866-5706bde4bf5a)

# ✍️ 학습 정리

| 분야 | 기술 |
Expand Down Expand Up @@ -99,7 +104,7 @@
</tr>
<tr>
<td align="center">Web FE·BE</td>
<td align="center">Web FE·BE</td>
<td align="center">Web FE</td>
<td align="center">Web FE</td>
<td align="center">Web BE</td>
<td align="center">Web BE</td>
Expand Down
70 changes: 44 additions & 26 deletions apps/api/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Body, Controller, Get, Post, Res, UseGuards } from '@nestjs/common';
import { Body, Controller, Get, Post, Query, Res, UseGuards } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ApiBody, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { ThrottlerGuard } from '@nestjs/throttler';
import { Response } from 'express';

import { GetUserId } from '@/common/decorator/get-userId.decorator';
import { CookieConfig } from '@/config/cookie.config';

import { AuthService } from './auth.service';
import { LocalLoginRequestDto } from './dto/localLoginRequest.dto';
Expand All @@ -17,10 +18,15 @@ import { LocalAuthGuard } from './local/local-auth.guard';
@Controller('auth')
@ApiTags('Auth')
export class AuthController {
private readonly redirectUrl: string;

constructor(
private authService: AuthService,
private configService: ConfigService
) {}
private readonly authService: AuthService,
private readonly configService: ConfigService,
private readonly cookieConfig: CookieConfig
) {
this.redirectUrl = this.configService.get<string>('LOGIN_REDIRECT_URL');
}

@Post('signup')
@ApiOperation({ summary: '로컬 회원가입' })
Expand All @@ -37,17 +43,21 @@ export class AuthController {
@ApiResponse({ status: 302, description: '홈으로 리다이렉션' })
@ApiResponse({ status: 401 })
@UseGuards(LocalAuthGuard)
localLogin(@GetUserId() userId: number, @Res() response: Response) {
this.cookieInsertJWT(response, userId);
localLogin(
@GetUserId() userId: number,
@Query('redirect') redirect: string,
@Res() response: Response
) {
this.loginProcess(response, userId, redirect);
}

@Post('guest/login')
@Get('guest/login')
@ApiOperation({ summary: '게스트 로그인' })
@ApiResponse({ status: 302, description: '홈으로 리다이렉션' })
@UseGuards(ThrottlerGuard)
async guestLogin(@Res() response: Response) {
async guestLogin(@Query('redirect') redirect: string, @Res() response: Response) {
const guestUser = await this.authService.createGuestUser();
this.cookieInsertJWT(response, guestUser.id);
this.loginProcess(response, guestUser.id, redirect);
}

@Get('google/login')
Expand All @@ -59,8 +69,12 @@ export class AuthController {

@Get('google/callback')
@UseGuards(GoogleAuthGuard)
googleAuthCallback(@GetUserId() userId: number, @Res() response: Response) {
this.cookieInsertJWT(response, userId);
googleAuthCallback(
@GetUserId() userId: number,
@Query('state') state: string,
@Res() response: Response
) {
this.loginProcess(response, userId, state);
}

@Get('github/login')
Expand All @@ -72,26 +86,30 @@ export class AuthController {

@Get('github/callback')
@UseGuards(GitHubAuthGuard)
githubAuthCallback(@GetUserId() userId: number, @Res() response: Response) {
this.cookieInsertJWT(response, userId);
githubAuthCallback(
@GetUserId() userId: number,
@Query('state') state: string,
@Res() response: Response
) {
this.loginProcess(response, userId, state);
}

private setAuthCookie(response: Response, accessToken: string) {
response.cookie('accessToken', accessToken, {
httpOnly: true,
secure: this.configService.get<string>('NODE_ENV') === 'production',
sameSite: 'lax',
path: '/',
});
@Get('logout')
@ApiOperation({ summary: '로그아웃' })
@ApiResponse({ status: 302, description: '홈으로 리다이렉션' })
logout(@Res() response: Response) {
response.clearCookie('accessToken', this.cookieConfig.getAuthCookieOptions());
this.redirectToHome(response);
}

private cookieInsertJWT(
response: Response,
userId: number,
redirectUrl: string = this.configService.get<string>('LOGIN_REDIRECT_URL')
) {
private loginProcess(response: Response, userId: number, path?: string) {
const { accessToken } = this.authService.createJWT(userId);
this.setAuthCookie(response, accessToken);
response.cookie('accessToken', accessToken, this.cookieConfig.getAuthCookieOptions());
this.redirectToHome(response, path);
}

private redirectToHome(response: Response, path?: string) {
const redirectUrl = `${this.redirectUrl}${path || ''}`;
response.redirect(redirectUrl);
}
}
10 changes: 9 additions & 1 deletion apps/api/src/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';

import { CookieConfig } from '@/config/cookie.config';
import { UserModule } from '@/user/user.module';

import { AuthController } from './auth.controller';
Expand All @@ -27,6 +28,13 @@ import { LocalStrategy } from './local/local.strategy';
}),
],
controllers: [AuthController],
providers: [AuthService, LocalStrategy, JwtStrategy, GitHubStrategy, GoogleStrategy],
providers: [
AuthService,
LocalStrategy,
JwtStrategy,
GitHubStrategy,
GoogleStrategy,
CookieConfig,
],
})
export class AuthModule {}
10 changes: 7 additions & 3 deletions apps/api/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import * as bcrypt from 'bcrypt';
import { Provider } from '@repo/types';

import { CreateSocialUserDto } from '@/user/dto/createSocialUser.dto';
import { UserService } from '@/user/user.service';
Expand All @@ -15,7 +16,7 @@ export class AuthService {
) {}

async signupLocal(signupRequestDto: LocalSignupRequestDto) {
return this.userService.createLocalUser({ provider: 'local', ...signupRequestDto });
return this.userService.createLocalUser({ provider: Provider.local, ...signupRequestDto });
}

async validateLocalLogin(username: string, inputPassword: string) {
Expand All @@ -32,17 +33,20 @@ export class AuthService {

async createGuestUser() {
const randomNum = Math.floor(Math.random() * 10000);
const response = await fetch('https://api.thecatapi.com/v1/images/search');
const catImageUrl = (await response.json())[0].url;

const guestUser = {
username: `guest_${randomNum}`,
password: `guest_password_${randomNum}`,
email: `[email protected]`,
nickname: `guest_${randomNum}`,
introduce: `게스트 사용자입니다. `,
profileImageUrl: `https://cataas.com/cat?${Date.now()}`,
profileImageUrl: catImageUrl,
};
const user = await this.userService.findUserByUsername(guestUser.username);
if (!user) {
return this.userService.createLocalUser({ provider: 'guest', ...guestUser });
return this.userService.createLocalUser({ provider: Provider.guest, ...guestUser });
}
return user;
}
Expand Down
15 changes: 12 additions & 3 deletions apps/api/src/auth/github/github.strategy.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { PassportStrategy } from '@nestjs/passport';
import { Profile, Strategy } from 'passport-github2';
import { Request } from 'express';
import { Profile, Strategy, StrategyOption } from 'passport-github2';
import { Provider } from '@repo/types';

import { AuthService } from '../auth.service';

@Injectable()
export class GitHubStrategy extends PassportStrategy(Strategy, 'github') {
export class GitHubStrategy extends PassportStrategy(Strategy, Provider.github) {
constructor(
private configService: ConfigService,
private authService: AuthService
Expand All @@ -18,12 +20,19 @@ export class GitHubStrategy extends PassportStrategy(Strategy, 'github') {
scope: ['user:email'],
});
}
authenticate(req: Request, options: StrategyOption) {
const returnUrl = req.query.redirect as string;
if (returnUrl) {
options.state = returnUrl;
}
return super.authenticate(req, options);
}

async validate(accessToken: string, refreshToken: string, profile: Profile) {
const { id, username, emails, photos } = profile;

const user = {
provider: 'github',
provider: Provider.github,
socialId: id,
nickname: username,
email: emails[0].value,
Expand Down
15 changes: 13 additions & 2 deletions apps/api/src/auth/google/google.strategy.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { PassportStrategy } from '@nestjs/passport';
import { Request } from 'express';
import { StrategyOption } from 'passport-github2';
import { Profile, Strategy } from 'passport-google-oauth20';
import { Provider } from '@repo/types';

import { AuthService } from '../auth.service';

@Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
export class GoogleStrategy extends PassportStrategy(Strategy, Provider.google) {
constructor(
private configService: ConfigService,
private authService: AuthService
Expand All @@ -19,11 +22,19 @@ export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
});
}

authenticate(req: Request, options: StrategyOption) {
const returnUrl = req.query.redirect as string;
if (returnUrl) {
options.state = returnUrl;
}
return super.authenticate(req, options);
}

async validate(accessToken: string, refreshToken: string, profile: Profile): Promise<any> {
const { id, displayName, emails, photos } = profile;

const user = {
provider: 'google',
provider: Provider.google,
socialId: id,
nickname: displayName,
email: emails[0].value,
Expand Down
18 changes: 18 additions & 0 deletions apps/api/src/config/cookie.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// config/cookie.config.ts
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { CookieOptions } from 'express';

@Injectable()
export class CookieConfig {
constructor(private configService: ConfigService) {}

getAuthCookieOptions(): CookieOptions {
return {
httpOnly: true,
secure: this.configService.get<string>('NODE_ENV') === 'production',
sameSite: 'lax',
path: '/',
};
}
}
15 changes: 13 additions & 2 deletions apps/api/src/dashboard/dashboard.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,19 @@ export class DashboardController {
return await this.dashboardService.getApplicants(ticleId);
}

@Post('start')
startTicle(@Param('ticleId') ticleId: number) {}
@Post(':ticleId/start')
@UseGuards(JwtAuthGuard)
async startTicle(@GetUserId() userId: number, @Param('ticleId') ticleId: number) {
await this.dashboardService.startTicle(userId, ticleId);
return 'success ticle start';
}

@Post(':ticleId/end')
@UseGuards(JwtAuthGuard)
async endTicle(@GetUserId() userId: number, @Param('ticleId') ticleId: number) {
await this.dashboardService.endTicle(userId, ticleId);
return 'success ticle end';
}

@Post('join')
joinTicle(@Param('ticleId') ticleId: number) {}

Check warning on line 49 in apps/api/src/dashboard/dashboard.controller.ts

View workflow job for this annotation

GitHub Actions / check

'ticleId' is defined but never used
Expand Down
Loading

0 comments on commit 2116d41

Please sign in to comment.