Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[merge] be-develop 브랜치를 main에 반영함 #466

Merged
merged 17 commits into from
Aug 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 30 additions & 6 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,35 @@ jobs:
with:
host: ${{ secrets.SERVER_IP }}
username: ${{ secrets.SERVER_USERNAME }}
password: ${{ secrets.SERVER_PASSWORD }}
key: ${{ secrets.SERVER_KEY }}
port: ${{ secrets.SERVER_PORT }}
script: |
docker login --username ${{ secrets.DOCKER_ID }} --password ${{ secrets.DOCKER_PASSWORD }}
docker pull traveline/traveline-docker
docker stop traveline-container || true
docker rm traveline-container || true
docker run -e DB_HOST=${{ secrets.DB_HOST }} -e DB_PORT=${{ secrets.DB_PORT }} -e DB_USER=${{ secrets.DB_USER }} -e DB_PASSWORD=${{ secrets.DB_PASSWORD }} -e DB_DATABASE=${{ secrets.DB_DATABASE }} -e NCP_ACCESS_KEY_ID=${{ secrets.NCP_ACCESS_KEY_ID }} -e NCP_SECRET_ACCESS_KEY=${{ secrets.NCP_SECRET_ACCESS_KEY }} -e NCP_REGION=${{ secrets.NCP_REGION }} -e JWT_SECRET_ACCESS=${{ secrets.JWT_SECRET_ACCESS }} -e JWT_SECRET_REFRESH=${{ secrets.JWT_SECRET_REFRESH }} -e CLIENT_ID=${{ secrets.CLIENT_ID }} -e TEAM_ID=${{ secrets.TEAM_ID }} -e KEY_ID=${{ secrets.KEY_ID }} -e AUTH_KEY_LINE1=${{ secrets.AUTH_KEY_LINE1 }} -e AUTH_KEY_LINE2=${{ secrets.AUTH_KEY_LINE2 }} -e AUTH_KEY_LINE3=${{ secrets.AUTH_KEY_LINE3 }} -e AUTH_KEY_LINE4=${{ secrets.AUTH_KEY_LINE4 }} -e KAKAO_REST_API_KEY=${{ secrets.KAKAO_REST_API_KEY }} -e AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID }} -e AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }} -e AWS_REGION=${{ secrets.AWS_REGION }} -e GREENEYE_SECRET_KEY=${{ secrets.GREENEYE_SECRET_KEY }} -e GREENEYE_DOMAIN_ID=${{ secrets.GREENEYE_DOMAIN_ID }} -e GREENEYE_SIGNATURE=${{ secrets.GREENEYE_SIGNATURE }} -d -p ${{secrets.EXTERNAL_PORT}}:${{secrets.INTERNAL_PORT}} --name traveline-container traveline/traveline-docker
sudo docker login --username ${{ secrets.DOCKER_ID }} --password ${{ secrets.DOCKER_PASSWORD }}
sudo docker pull traveline/traveline-docker
sudo docker stop traveline-container || true
sudo docker rm traveline-container || true
sudo docker run -e DB_HOST=${{ secrets.DB_HOST }} \
-e DB_PORT=${{ secrets.DB_PORT }} \
-e DB_USER=${{ secrets.DB_USER }} \
-e DB_PASSWORD=${{ secrets.DB_PASSWORD }} \
-e DB_DATABASE=${{ secrets.DB_DATABASE }} \
-e NCP_ACCESS_KEY_ID=${{ secrets.NCP_ACCESS_KEY_ID }} \
-e NCP_SECRET_ACCESS_KEY=${{ secrets.NCP_SECRET_ACCESS_KEY }} \
-e NCP_REGION=${{ secrets.NCP_REGION }} \
-e JWT_SECRET_ACCESS=${{ secrets.JWT_SECRET_ACCESS }} \
-e JWT_SECRET_REFRESH=${{ secrets.JWT_SECRET_REFRESH }} \
-e CLIENT_ID=${{ secrets.CLIENT_ID }} \
-e TEAM_ID=${{ secrets.TEAM_ID }} \
-e KEY_ID=${{ secrets.KEY_ID }} \
-e AUTH_KEY_LINE1=${{ secrets.AUTH_KEY_LINE1 }} \
-e AUTH_KEY_LINE2=${{ secrets.AUTH_KEY_LINE2 }} \
-e AUTH_KEY_LINE3=${{ secrets.AUTH_KEY_LINE3 }} \
-e AUTH_KEY_LINE4=${{ secrets.AUTH_KEY_LINE4 }} \
-e KAKAO_REST_API_KEY=${{ secrets.KAKAO_REST_API_KEY }} \
-e AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID }} \
-e AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }} \
-e AWS_REGION=${{ secrets.AWS_REGION }} \
-e GREENEYE_SECRET_KEY=${{ secrets.GREENEYE_SECRET_KEY }} \
-e GREENEYE_DOMAIN_ID=${{ secrets.GREENEYE_DOMAIN_ID }} \
-e GREENEYE_SIGNATURE=${{ secrets.GREENEYE_SIGNATURE }} \
-d -p ${{secrets.EXTERNAL_PORT}}:${{secrets.INTERNAL_PORT}} --name traveline-container traveline/traveline-docker
4 changes: 3 additions & 1 deletion BE/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
FROM node:18

ENV NODE_OPTIONS="--max-old-space-size=4096"

WORKDIR /app

COPY package.json package-lock.json ./
Expand All @@ -8,4 +10,4 @@ RUN npm ci

COPY . .

ENTRYPOINT ["npm", "run", "start"]
ENTRYPOINT ["npm", "run", "start"]
5 changes: 3 additions & 2 deletions BE/src/postings/postings.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,9 @@ export class PostingsController {
description: '검색어와 선택 태그의 교집합에 해당하는 게시글을 반환합니다.',
})
@ApiOkResponse({ schema: { example: search_OK } })
async search(@Query() searchPostingDto: SearchPostingDto) {
return this.postingsService.findAll(searchPostingDto);
async search(@Req() request, @Query() searchPostingDto: SearchPostingDto) {
const userId = request['user'].id;
return this.postingsService.findAll(userId, searchPostingDto);
}

@Get('titles')
Expand Down
11 changes: 10 additions & 1 deletion BE/src/postings/postings.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ import { Report } from './entities/report.entity';
import { Period, Season } from './postings.types';
import { BLOCKING_LIMIT } from './postings.constants';
import { StorageService } from 'src/storage/storage.service';
import { BlockRepository } from 'src/users/block.repository';

@Injectable()
export class PostingsService {
constructor(
private readonly userRepository: UserRepository,
private readonly blockRepository: BlockRepository,
private readonly postingsRepository: PostingsRepository,
private readonly likedsRepository: LikedsRepository,
private readonly reportsRepository: ReportsRepository,
Expand All @@ -44,8 +46,10 @@ export class PostingsService {
return this.postingsRepository.save(posting);
}

async findAll(dto: SearchPostingDto) {
async findAll(userId: string, dto: SearchPostingDto) {
const blockedIds = await this.findBlockedIds(userId);
const postings = await this.postingsRepository.findAll(
blockedIds,
dto.keyword,
dto.sorting,
dto.offset,
Expand Down Expand Up @@ -226,4 +230,9 @@ export class PostingsService {
reports: posting.reports,
};
}

private async findBlockedIds(blocker: string) {
const blocks = await this.blockRepository.findByBlocker(blocker);
return blocks.map((block) => block.blocked.id);
}
}
10 changes: 9 additions & 1 deletion BE/src/postings/repositories/postings.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export class PostingsRepository {
}

async findAll(
blockedIds: string[],
keyword: string,
sorting: Sorting,
offset: number,
Expand All @@ -50,7 +51,14 @@ export class PostingsRepository {
withWhos: WithWho[]
) {
const conditions = ['p.title LIKE :keyword'];
let params: { [key: string]: string } = { keyword: `%${keyword}%` };
let params: { [key: string]: string | string[] } = {
keyword: `%${keyword}%`,
};

if (blockedIds.length > 0) {
conditions.push('p.writer NOT IN (:...blockedIds)');
params = { ...params, blockedIds };
}

if (budget) {
conditions.push('p.budget = :budget');
Expand Down
5 changes: 4 additions & 1 deletion BE/src/socialLogin/apple-login-strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@ export class AppleLoginStrategy implements SocialLoginStrategy {

async login(
socialLoginRequestDto: SocialLoginRequestDto
): Promise<{ resourceId: string; email: string }> {
): Promise<{ resourceId: string; email?: string }> {
try {
const { idToken, email } = socialLoginRequestDto;
const resourceId = (await this.decodeIdToken(idToken)).sub;
if (!email) {
return { resourceId };
}
return { resourceId, email };
} catch (error) {
throw new UnauthorizedException('유효하지 않은 형식의 토큰입니다.');
Expand Down
2 changes: 1 addition & 1 deletion BE/src/socialLogin/social-login-strategy.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { SocialLoginRequestDto } from './dto/social-login-request.dto';
export interface SocialLoginStrategy {
login(
socialLoginRequestDto: SocialLoginRequestDto
): Promise<{ resourceId: string; email: string }>;
): Promise<{ resourceId: string; email?: string }>;
withdraw(
resourceId: string,
socialWithdrawRequestDto: SocialWithdrawRequestDto
Expand Down
38 changes: 38 additions & 0 deletions BE/src/users/block.repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Inject, Injectable } from '@nestjs/common';
import { Block } from './entities/block.entity';
import { BLOCK_REPOSITORY } from './users.constants';
import { Repository } from 'typeorm';
import { User } from './entities/user.entity';

@Injectable()
export class BlockRepository {
constructor(
@Inject(BLOCK_REPOSITORY)
private blockRepository: Repository<Block>
) {}

save(blocker: User, blocked: User) {
return this.blockRepository.save({ blocker, blocked });
}

findByBlocker(blocker: string) {
return this.blockRepository
.createQueryBuilder('b')
.leftJoinAndSelect('b.blocker', 'x')
.leftJoinAndSelect('b.blocked', 'y')
.where('b.blocker = :blocker', { blocker })
.getMany();
}

findByBlockerAndBlocked(blocker: string, blocked: string) {
return this.blockRepository
.createQueryBuilder('b')
.leftJoinAndSelect('b.blocker', 'x')
.leftJoinAndSelect('b.blocked', 'y')
.where('b.blocker = :blocker AND b.blocked = :blocked', {
blocker,
blocked,
})
.getOne();
}
}
16 changes: 16 additions & 0 deletions BE/src/users/entities/block.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Entity, PrimaryGeneratedColumn, ManyToOne, JoinColumn } from 'typeorm';
import { User } from './user.entity';

@Entity()
export class Block {
@PrimaryGeneratedColumn('uuid')
id: string;

@ManyToOne(() => User, (user) => user.blockers, { nullable: false })
@JoinColumn({ name: 'blocker', referencedColumnName: 'id' })
blocker: User;

@ManyToOne(() => User, (user) => user.blockeds, { nullable: false })
@JoinColumn({ name: 'blocked', referencedColumnName: 'id' })
blocked: User;
}
7 changes: 7 additions & 0 deletions BE/src/users/entities/user.entity.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Liked } from 'src/postings/entities/liked.entity';
import { Posting } from 'src/postings/entities/posting.entity';
import { Report } from 'src/postings/entities/report.entity';
import { Block } from './block.entity';
import {
Entity,
Column,
Expand Down Expand Up @@ -49,4 +50,10 @@ export class User {
@ManyToOne(() => SocialLogin, (socialLogin) => socialLogin.users)
@JoinColumn({ name: 'social_type', referencedColumnName: 'id' })
socials: SocialLogin;

@OneToMany(() => Block, (block) => block.blocker)
blockers: Block[];

@OneToMany(() => Block, (block) => block.blocked)
blockeds: Block[];
}
1 change: 1 addition & 0 deletions BE/src/users/users.constants.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export const DATA_SOURCE = 'DATA_SOURCE';
export const USERS_REPOSITORY = 'USERS_REPOSITORY';
export const BLOCK_REPOSITORY = 'BLOCK_REPOSITORY';
13 changes: 13 additions & 0 deletions BE/src/users/users.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import {
UploadedFile,
UseGuards,
Req,
Post,
Param,
ParseUUIDPipe,
} from '@nestjs/common';
import { UsersService } from './users.service';
import {
Expand Down Expand Up @@ -110,4 +113,14 @@ export class UsersController {
checkDuplicatedName(@Query('name') name: string) {
return this.usersService.checkDuplicatedName(name);
}

@Post(':id/block')
@ApiOperation({
summary: '특정 회원 차단',
description: '현재 유저가 id에 해당하는 회원을 차단합니다.',
})
blockUser(@Req() request, @Param('id', ParseUUIDPipe) id: string) {
const userId = request['user'].id;
return this.usersService.blockUser(userId, id);
}
}
5 changes: 3 additions & 2 deletions BE/src/users/users.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import { usersProvider } from './users.providers';
import { UserRepository } from './users.repository';
import { StorageModule } from '../storage/storage.module';
import { DatabaseModule } from '../database/database.module';
import { BlockRepository } from './block.repository';

@Module({
imports: [DatabaseModule, StorageModule],
controllers: [UsersController],
providers: [UsersService, ...usersProvider, UserRepository],
exports: [UserRepository, UsersService],
providers: [UsersService, ...usersProvider, UserRepository, BlockRepository],
exports: [UserRepository, UsersService, BlockRepository],
})
export class UsersModule {}
12 changes: 11 additions & 1 deletion BE/src/users/users.providers.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import { Block } from './entities/block.entity';
import { User } from './entities/user.entity';
import { DataSource } from 'typeorm';
import { DATA_SOURCE, USERS_REPOSITORY } from './users.constants';
import {
BLOCK_REPOSITORY,
DATA_SOURCE,
USERS_REPOSITORY,
} from './users.constants';

export const usersProvider = [
{
provide: USERS_REPOSITORY,
useFactory: (dataSource: DataSource) => dataSource.getRepository(User),
inject: [DATA_SOURCE],
},
{
provide: BLOCK_REPOSITORY,
useFactory: (dataSource: DataSource) => dataSource.getRepository(Block),
inject: [DATA_SOURCE],
},
];
24 changes: 24 additions & 0 deletions BE/src/users/users.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import { UserRepository } from './users.repository';
import { CheckDuplicatedNameResponseDto } from './dto/check-duplicated-name-response.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { UpdateUserIpDto } from './dto/update-user-ip.dto';
import { BlockRepository } from './block.repository';

@Injectable()
export class UsersService {
constructor(
private userRepository: UserRepository,
private blockRepository: BlockRepository,
private readonly storageService: StorageService
) {}

Expand Down Expand Up @@ -113,4 +115,26 @@ export class UsersService {
async updateUserIp(id: string, updateUserIpDto: UpdateUserIpDto) {
return this.userRepository.update(id, updateUserIpDto);
}

async blockUser(blocker: string, blocked: string) {
if (blocker === blocked) {
throw new BadRequestException('자기 자신을 차단할 수 없습니다.');
}

const blockedUser = await this.userRepository.findById(blocked);
if (!blockedUser) {
throw new BadRequestException('존재하지 않는 회원을 차단할 수 없습니다.');
}

const block = await this.blockRepository.findByBlockerAndBlocked(
blocker,
blocked
);
if (block) {
throw new BadRequestException('이미 차단한 회원입니다.');
}

const blockerUser = await this.userRepository.findById(blocker);
return this.blockRepository.save(blockerUser, blockedUser);
}
}
Loading