Skip to content

Commit

Permalink
Merge pull request #223 from boostcampwm2023/develop
Browse files Browse the repository at this point in the history
[Deploy] Week5 ver1 배포
  • Loading branch information
SongJSeop authored Dec 4, 2023
2 parents ad254cf + 1859bf5 commit fa9039f
Show file tree
Hide file tree
Showing 90 changed files with 1,731 additions and 436 deletions.
Binary file modified .yarn/install-state.gz
Binary file not shown.
16 changes: 14 additions & 2 deletions Dockerfile-was
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
FROM node:20-alpine
FROM node:20-alpine AS builder

WORKDIR /app

ADD . /app

RUN SHARP_IGNORE_GLOBAL_LIBVIPS=1 npm_config_arch=x64 npm_config_platform=linuxmusl yarn workspace server add sharp@0.32.1
RUN SHARP_IGNORE_GLOBAL_LIBVIPS=1 npm_config_arch=x64 npm_config_platform=linux yarn workspace server add sharp@0.32.6
RUN yarn workspace server build

FROM node:20-alpine AS runner

WORKDIR /app

COPY --from=builder /app/packages/server/dist /app/packages/server/dist
COPY --from=builder /app/packages/server/package.json /app/packages/server/package.json
COPY --from=builder /app/.yarn /app/.yarn
COPY --from=builder /app/.pnp.cjs /app/.pnp.cjs
COPY --from=builder /app/.yarnrc.yml /app/.yarnrc.yml
COPY --from=builder /app/yarn.lock /app/yarn.lock
COPY --from=builder /app/package.json /app/package.json

EXPOSE 3000

ENTRYPOINT ["yarn", "workspace", "server", "start:prod"]
2 changes: 2 additions & 0 deletions nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ server {
root /usr/share/nginx/html;
index index.html;

client_max_body_size 0;

ssl_certificate /etc/letsencrypt/live/www.xn--bj0b03z.site/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.xn--bj0b03z.site/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
Expand Down
4 changes: 2 additions & 2 deletions packages/client/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" href="./src/assets/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>별 하나에 글 하나🌟</title>
<title>별 하나에 글 하나 🌟</title>
</head>
<body>
<div id="root"></div>
Expand Down
6 changes: 5 additions & 1 deletion packages/client/src/app/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import SignUpModal from 'widgets/signupModal/SignUpModal';
import NickNameSetModal from 'widgets/nickNameSetModal/NickNameSetModal';
import LogoAndStart from 'widgets/logoAndStart';
import { PostModal } from 'features/postModal';
import GalaxyCustom from 'widgets/galaxyCustom';

export const router = createBrowserRouter(
createRoutesFromElements(
Expand All @@ -19,14 +20,17 @@ export const router = createBrowserRouter(
<Route index element={<LogoAndStart />} />
<Route path="login" element={<LoginModal />} />
<Route path="signup" element={<SignUpModal />} />
<Route path="nickname" element={<NickNameSetModal />} />
<Route path="nickname" element={<NickNameSetModal />}>
<Route path=":platform" />
</Route>
</Route>

<Route path="/home" element={<Home />}>
<Route path=":postId">
<Route path="detail" element={<PostModal />} />
</Route>
<Route path="writing" element={<WritingModal />} />
<Route path="custom" element={<GalaxyCustom />} />
</Route>
</>,
),
Expand Down
Binary file added packages/client/src/assets/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions packages/client/src/assets/icon-add-24-gray.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions packages/client/src/assets/icon-planetedit-24-gray.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions packages/client/src/assets/icon-writte-24-gray.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/client/src/assets/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/client/src/assets/logo/Github.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/client/src/assets/logo/Google.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/client/src/assets/logo/Naver.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
112 changes: 112 additions & 0 deletions packages/client/src/entities/like/Like.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { useEffect, useReducer, useState } from 'react';
import { Heart } from 'lucide-react';
import { Button } from 'shared/ui';
import theme from 'shared/ui/styles/theme';
import { BASE_URL } from '@constants';
import { useFetch } from 'shared/hooks';
import instance from 'shared/apis/AxiosInterceptor';

interface PropsType {
postId: string;
count: number;
}

const LIKE = 'LIKE';
const UNLIKE = 'UNLIKE';
const SET_LIKE = 'SET_LIKE';

const likeReducer = (
state: { isLiked: boolean; likeCount: number },
action: { type: string; bool?: boolean },
): { isLiked: boolean; likeCount: number } => {
switch (action.type) {
case LIKE:
return {
...state,
isLiked: true,
likeCount: state.likeCount + 1,
};
case UNLIKE:
return {
...state,
isLiked: false,
likeCount: state.likeCount - 1,
};
case SET_LIKE:
return {
...state,
isLiked: action.bool ?? state.isLiked,
};
default:
return state;
}
};

export default function Like({ postId, count }: PropsType) {
const [state, dispatch] = useReducer(likeReducer, {
isLiked: false,
likeCount: count,
});
const [isButtonDisabled, setButtonDisabled] = useState(false);

useEffect(() => {
const fetchLike = async () => {
const { data, error } = useFetch<boolean>(
`${BASE_URL}post/${postId}/is-liked`,
);
if (error) return;
dispatch({ type: SET_LIKE, bool: data });
};
fetchLike();
}, []);

const clickLike = async () => {
if (isButtonDisabled) return;

try {
setButtonDisabled(true);
const res = await instance({
method: 'patch',
url: `/post/${postId}/is-liked`,
});
if (res.status === 200) dispatch({ type: LIKE });
} finally {
setButtonDisabled(false);
}
};

const cancelLike = async () => {
if (isButtonDisabled) return;

try {
setButtonDisabled(true);
const res = await instance({
method: 'patch',
url: `/post/${postId}/unlike`,
});
if (res.status === 200) dispatch({ type: UNLIKE });
} finally {
setButtonDisabled(false);
}
};

return (
<Button
size="m"
buttonType={state.isLiked ? 'warning-border' : 'Button'}
onClick={state.isLiked ? cancelLike : clickLike}
disabled={isButtonDisabled}
>
<Heart
style={{ marginRight: '10px' }}
color={
state.isLiked
? theme.colors.warning.pressed
: theme.colors.stroke.default
}
fill={state.isLiked ? theme.colors.warning.pressed : 'none'}
/>
{state.likeCount}
</Button>
);
}
25 changes: 22 additions & 3 deletions packages/client/src/entities/posts/ui/Posts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,34 @@ import { useFetch } from 'shared/hooks';
import Post from './Post';
import { useState } from 'react';
import { StarData } from 'shared/lib/types/star';
import { useOwnerStore } from 'shared/store/useOwnerStore';
import { getPostListByNickName } from 'shared/apis/star';
import { useEffect } from 'react';

export default function Posts() {
const [post, setPost] = useState(0);
const { data } = useFetch<StarData[]>('star');
const [postData, setPostData] = useState<StarData[]>();

const { isMyPage, pageOwnerNickName } = useOwnerStore();

const myPostData = useFetch<StarData[]>('star').data;

useEffect(() => {
if (isMyPage) {
setPostData(myPostData);
return;
}

(async () => {
const otherPostData = await getPostListByNickName(pageOwnerNickName);
setPostData(otherPostData);
})();
}, [isMyPage, myPostData]);

return (
<>
{data &&
data.map((data, index) => (
{postData &&
postData.map((data, index) => (
<Post
key={index}
data={data}
Expand Down
7 changes: 3 additions & 4 deletions packages/client/src/features/postModal/api/deletePost.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import instance from 'shared/apis/AxiosInterceptor';
import { BASE_URL } from '@constants';

export const deletePost = async (postId: string) => {
const { data } = await instance({
const res = await instance({
method: 'DELETE',
url: `${BASE_URL}/post/${postId}`,
url: `/post/${postId}`,
});

return data;
return res;
};
22 changes: 13 additions & 9 deletions packages/client/src/features/postModal/ui/ImageSlider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,19 @@ export default function ImageSlider({ imageUrls }: PropsType) {
return <Image key={url} src={url} index={imageIndex} />;
})}
</CurrentImage>
<Button onClick={handlePrev} style={{ left: 0 }}>
<ArrowBigLeft />
</Button>
<Button onClick={handleNext} style={{ right: 0 }}>
<ArrowBigRight />
</Button>
<Pagination>
<Dots />
</Pagination>
{imageUrls.length > 1 && (
<>
<Button onClick={handlePrev} style={{ left: 0 }}>
<ArrowBigLeft />
</Button>
<Button onClick={handleNext} style={{ right: 0 }}>
<ArrowBigRight />
</Button>
<Pagination>
<Dots />
</Pagination>
</>
)}
</Layout>
);
}
Expand Down
5 changes: 4 additions & 1 deletion packages/client/src/features/postModal/ui/PostModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useFetch } from 'shared/hooks';
import { PostData } from 'shared/lib/types/post';
import { deletePost } from '../api/deletePost';
import ImageSlider from './ImageSlider';
import Like from 'entities/like/Like';

export default function PostModal() {
const { setView } = useViewStore();
Expand Down Expand Up @@ -37,6 +38,7 @@ export default function PostModal() {
if (res.status === 200) {
setView('MAIN');
navigate('/home');
window.location.reload();
} else {
alert('글 삭제 실패');
}
Expand All @@ -52,9 +54,10 @@ export default function PostModal() {
setView('MAIN');
navigate(`/home/${postId}`);
}}
leftButton={<Like postId={postId!} count={data.like_cnt ?? 0} />}
>
<Container>
{data.images.length && (
{data.images.length > 0 && (
<ImageContainer>
<ImageSlider imageUrls={data.images} />
</ImageContainer>
Expand Down
16 changes: 11 additions & 5 deletions packages/client/src/features/writingModal/api/sendPost.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import instance from 'shared/apis/AxiosInterceptor';
import { BASE_URL } from '@constants';

const dummyStarData = {
color: 'pink',
Expand All @@ -11,17 +10,24 @@ const dummyStarData = {
},
}; // TODO: 별 커스텀 데이터로 변경

export const sendPost = async (text: string, files: FileList | null) => {
export const sendPost = async (
title: string,
content: string,
files: FileList | null,
) => {
try {
const formData = new FormData();
formData.append('title', '제목');
formData.append('content', text);
formData.append('title', title ? title : '제목 없음');
formData.append('content', content ? content : '내용 없음');
formData.append('star', JSON.stringify(dummyStarData));
if (files) {
for (let i = 0; i < files.length; i++) formData.append('file', files[i]);
}

const response = await instance.post(`${BASE_URL}post`, formData, {
const response = await instance({
method: 'POST',
url: '/post',
data: formData,
headers: {
'Content-Type': 'multipart/form-data',
},
Expand Down
Loading

0 comments on commit fa9039f

Please sign in to comment.