diff --git a/.github/workflows/boolock-dev-cicd.yml b/.github/workflows/boolock-dev-cicd.yml
index 51cd35b..c703073 100644
--- a/.github/workflows/boolock-dev-cicd.yml
+++ b/.github/workflows/boolock-dev-cicd.yml
@@ -1,4 +1,4 @@
-name: ci/cd action
+name: dev ci/cd action
on:
push:
@@ -10,110 +10,90 @@ jobs:
steps:
- uses: actions/checkout@v4
- - name: Set up Node.js
- uses: actions/setup-node@v4
- with:
- node-version: '20.x'
-
- - name: Install Pnpm
- run: npm install -g pnpm
- - name: Package install with pnpm
- run: pnpm install:all
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v2
+ with:
+ buildkitd-flags: --debug
- name: Set BE .env
run: |
- echo "MONGO_URI=${{ secrets.MONGO_URI }}" > apps/server/.env
- echo "SSH_HOST=${{ secrets.SSH_HOST }}" >> apps/server/.env
- echo "SSH_PORT=${{ secrets.SSH_PORT }}" >> apps/server/.env
- echo "SSH_USER=${{ secrets.SSH_USER }}" >> apps/server/.env
- echo "SSH_PASSWORD=${{ secrets.SSH_PASSWORD }}" >> apps/server/.env
- echo "SSH_DATABASE_HOST=${{ secrets.SSH_DATABASE_HOST }}" >> apps/server/.env
- echo "SSH_DATABASE_PORT=${{ secrets.SSH_DATABASE_PORT }}" >> apps/server/.env
- echo "LOCAL_PORT=${{ secrets.LOCAL_PORT }}" >> apps/server/.env
- echo "IS_LOCAL=true" >> apps/server/.env
- echo "SERVER_CORS_ACCEPT=http://localhost:5173" >> apps/server/.env
+ echo "MONGO_URI=${{ secrets.TEST_MONGO_URI }}" > apps/server/.env
+ echo "IS_LOCAL=false" >> apps/server/.env
+ echo "SERVER_CORS_ACCEPT=${{ secrets.TEST_SERVER_CORS_ACCEPT }}" >> apps/server/.env
echo "S3_ACCESS_KEY=${{ secrets.S3_ACCESS_KEY }}" >> apps/server/.env
echo "S3_SECRET_KEY=${{ secrets.S3_SECRET_KEY }}" >> apps/server/.env
echo "S3_BUCKET_NAME=${{ secrets.S3_BUCKET_NAME }}" >> apps/server/.env
-
+ echo "NODE_ENV=production" >> apps/server/.env
+
- name: Set FE .env
run: |
- echo "VITE_SERVER_URL=http://localhost:3000" > apps/client/.env
+ echo "VITE_SERVER_URL=${{ secrets.DEPLOY_VITE_SERVER_URL }}" > apps/client/.env
echo "VITE_MIXPANEL_TOKEN=${{ secrets.VITE_MIXPANEL_TOKEN }}" >> apps/client/.env
echo "VITE_STATIC_STORAGE_URL=${{ secrets.VITE_STATIC_STORAGE_URL }}" >> apps/client/.env
- - name: Set Nginx SSL files
- run: |
- mkdir -p apps/client/ssl
- echo "${{ secrets.SSL_FULLCHAIN }}" > apps/client/ssl/fullchain.pem
- echo "${{ secrets.SSL_PRIVKEY }}" > apps/client/ssl/privkey.pem
-
- - name: Buld Frontend
- run: |
- cd apps/client
- pnpm run build
-
- - name: Build Backend
- run: |
- cd apps/server
- pnpm run swagger-auto
- pnpm run build
-
- - name: Build base image
- run: |
- docker build . --file Dockerfile.base --tag ${{ secrets.DOCKERHUB_USERNAME }}/boolock_base_test:1.0
- docker tag ${{ secrets.DOCKERHUB_USERNAME }}/boolock_base_test:1.0 base-image
-
- - name: Build frontend and backend images
- run: |
- docker build . --file apps/client/Dockerfile --tag ${{ secrets.DOCKERHUB_USERNAME }}/boolock_client_test:1.0
- docker build . --file apps/server/Dockerfile --tag ${{ secrets.DOCKERHUB_USERNAME }}/boolock_server_test:1.0
-
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
- password: ${{ secrets.DOCKERHUB_PASSWORD }}
+ password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN }}
- - name: Docker Hub push
- run: |
- docker push ${{ secrets.DOCKERHUB_USERNAME }}/boolock_base_test:1.0
- docker push ${{ secrets.DOCKERHUB_USERNAME }}/boolock_client_test:1.0
- docker push ${{ secrets.DOCKERHUB_USERNAME }}/boolock_server_test:1.0
+ - name: Build and Push Base Image
+ uses: docker/build-push-action@v4
+ with:
+ context: .
+ file: Dockerfile.base
+ push: true
+ tags: ${{ secrets.DOCKERHUB_USERNAME }}/base-image-test:latest
+ cache-from: type=gha,scope=base-image-test
+ cache-to: type=gha,mode=max,scope=base-image-test
+
+ - name: Build and Push Frontend Image
+ uses: docker/build-push-action@v4
+ with:
+ context: .
+ file: apps/client/Dockerfile.test
+ push: true
+ tags: ${{ secrets.DOCKERHUB_USERNAME }}/boolock_client_test:latest
+ cache-from: type=gha,scope=frontend-test
+ cache-to: type=gha,mode=max,scope=frontend-test
+ build-args: |
+ DOCKERHUB_USERNAME=${{ secrets.DOCKERHUB_USERNAME }}
+ TYPE=test
+
+ - name: Build and Push Backend Image
+ uses: docker/build-push-action@v4
+ with:
+ context: .
+ file: apps/server/Dockerfile
+ push: true
+ tags: ${{ secrets.DOCKERHUB_USERNAME }}/boolock_server_test:latest
+ cache-from: type=gha,scope=backend-test
+ cache-to: type=gha,mode=max,scope=backend-test
+ build-args: |
+ DOCKERHUB_USERNAME=${{ secrets.DOCKERHUB_USERNAME }}
+ TYPE=test
deploy:
+ if: github.event_name == 'push'
needs: build
runs-on: ubuntu-latest
steps:
- - name: Login to Docker Hub
- run: sudo docker login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_PASSWORD }}
-
- name: Deploy with docker
uses: appleboy/ssh-action@v0.1.6
with:
- host: ${{ secrets.SSH_HOST }}
- username: ${{ secrets.SSH_USER }}
- password: ${{ secrets.SSH_PASSWORD }}
- port: ${{ secrets.SSH_PORT }}
+ host: ${{ secrets.TEST_SSH_HOST }}
+ username: ${{ secrets.TEST_SSH_USER }}
+ password: ${{ secrets.TEST_SSH_PASSWORD }}
+ port: ${{ secrets.TEST_SSH_PORT }}
script: |
cd boolock/refactor-web31-BooLock
- git checkout main
- git pull origin main
-
- echo "MONGO_URI=${{ secrets.DEPLOY_MONGO_URI }}" > apps/server/.env
- echo "IS_LOCAL=false" >> apps/server/.env
- echo "SERVER_CORS_ACCEPT=${{ secrets.DEPLOY_SERVER_CORS_ACCEPT }}" >> apps/server/.env
- echo "S3_ACCESS_KEY=${{ secrets.S3_ACCESS_KEY }}" >> apps/server/.env
- echo "S3_SECRET_KEY=${{ secrets.S3_SECRET_KEY }}" >> apps/server/.env
- echo "S3_BUCKET_NAME=${{ secrets.S3_BUCKET_NAME }}" >> apps/server/.env
- echo "NODE_ENV=production" >> apps/server/.env
+ git fetch origin
+ git checkout dev
+ git pull origin dev
- echo "VITE_SERVER_URL=${{ secrets.DEPLOY_VITE_SERVER_URL }}" > apps/client/.env
- echo "VITE_MIXPANEL_TOKEN=${{ secrets.VITE_MIXPANEL_TOKEN }}" >> apps/client/.env
- echo "VITE_STATIC_STORAGE_URL=${{ secrets.VITE_STATIC_STORAGE_URL }}" >> apps/client/.env
+ echo "DOCKERHUB_USERNAME=${{ secrets.DOCKERHUB_USERNAME }}" > .env
+ echo "${{ secrets.DOCKERHUB_ACCESS_TOKEN }}" | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin
- sudo docker compose pull
- sudo docker compose down
- sudo docker compose up -d --build
- sudo docker image prune -f
+ sudo docker compose -f docker-compose.test.yml pull
+ sudo docker compose -f docker-compose.test.yml up -d --force-recreate --remove-orphans
diff --git a/.github/workflows/boolock-main-cicd.yml b/.github/workflows/boolock-main-cicd.yml
new file mode 100644
index 0000000..44508d7
--- /dev/null
+++ b/.github/workflows/boolock-main-cicd.yml
@@ -0,0 +1,107 @@
+name: main ci/cd action
+
+on:
+ push:
+ branches: ['main']
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v2
+ with:
+ buildkitd-flags: --debug
+
+ - name: Set BE .env
+ run: |
+ echo "MONGO_URI=${{ secrets.DEPLOY_MONGO_URI }}" > apps/server/.env
+ echo "IS_LOCAL=false" >> apps/server/.env
+ echo "SERVER_CORS_ACCEPT=${{ secrets.DEPLOY_SERVER_CORS_ACCEPT }}" >> apps/server/.env
+ echo "S3_ACCESS_KEY=${{ secrets.S3_ACCESS_KEY }}" >> apps/server/.env
+ echo "S3_SECRET_KEY=${{ secrets.S3_SECRET_KEY }}" >> apps/server/.env
+ echo "S3_BUCKET_NAME=${{ secrets.S3_BUCKET_NAME }}" >> apps/server/.env
+ echo "NODE_ENV=production" >> apps/server/.env
+
+ - name: Set FE .env
+ run: |
+ echo "VITE_SERVER_URL=${{ secrets.DEPLOY_VITE_SERVER_URL }}" > apps/client/.env
+ echo "VITE_MIXPANEL_TOKEN=${{ secrets.VITE_MIXPANEL_TOKEN }}" >> apps/client/.env
+ echo "VITE_STATIC_STORAGE_URL=${{ secrets.VITE_STATIC_STORAGE_URL }}" >> apps/client/.env
+
+ - name: Set Nginx SSL files
+ run: |
+ mkdir -p apps/client/ssl
+ echo "${{ secrets.SSL_FULLCHAIN }}" > apps/client/ssl/fullchain.pem
+ echo "${{ secrets.SSL_PRIVKEY }}" > apps/client/ssl/privkey.pem
+
+ - name: Login to Docker Hub
+ uses: docker/login-action@v2
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN }}
+
+ - name: Build and Push Base Image
+ uses: docker/build-push-action@v4
+ with:
+ context: .
+ file: Dockerfile.base
+ push: true
+ tags: ${{ secrets.DOCKERHUB_USERNAME }}/base-image-prod:latest
+ cache-from: type=gha,scope=base-image-prod
+ cache-to: type=gha,mode=max,scope=base-image-prod
+
+ - name: Build and Push Frontend Image
+ uses: docker/build-push-action@v4
+ with:
+ context: .
+ file: apps/client/Dockerfile
+ push: true
+ tags: ${{ secrets.DOCKERHUB_USERNAME }}/boolock_client_prod:latest
+ cache-from: type=gha,scope=frontend
+ cache-to: type=gha,mode=max,scope=frontend
+ build-args: |
+ DOCKERHUB_USERNAME=${{ secrets.DOCKERHUB_USERNAME }}
+ TYPE=prod
+
+ - name: Build and Push Backend Image
+ uses: docker/build-push-action@v4
+ with:
+ context: .
+ file: apps/server/Dockerfile
+ push: true
+ tags: ${{ secrets.DOCKERHUB_USERNAME }}/boolock_server_prod:latest
+ cache-from: type=gha,scope=backend
+ cache-to: type=gha,mode=max,scope=backend
+ build-args: |
+ DOCKERHUB_USERNAME=${{ secrets.DOCKERHUB_USERNAME }}
+ TYPE=prod
+
+ deploy:
+ needs: build
+ runs-on: ubuntu-latest
+ steps:
+ - name: Login to Docker Hub
+ run: sudo docker login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_PASSWORD }}
+
+ - name: Deploy with docker
+ uses: appleboy/ssh-action@v0.1.6
+ with:
+ host: ${{ secrets.SSH_HOST }}
+ username: ${{ secrets.SSH_USER }}
+ password: ${{ secrets.SSH_PASSWORD }}
+ port: ${{ secrets.SSH_PORT }}
+ script: |
+ cd boolock/refactor-web31-BooLock
+ git fetch origin
+ git checkout main
+ git pull origin main
+
+ echo "DOCKERHUB_USERNAME=${{ secrets.DOCKERHUB_USERNAME }}" > .env
+ echo "${{ secrets.DOCKERHUB_ACCESS_TOKEN }}" | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin
+
+ sudo docker compose pull
+ sudo docker compose up -d --force-recreate --remove-orphans
\ No newline at end of file
diff --git a/README.md b/README.md
index 8d7ef2a..7d868ea 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,11 @@
BooLock
-
-
-
+![image](https://github.com/user-attachments/assets/c58af651-8a5d-4b09-a98e-04ce50f27fca)
- 웹 개발, 블록으로 쉽고 즐겁게!
+ 웹 개발, 블록 조립으로 쉽고 재밌게!
HTML과 CSS를 배우는 블록코딩 플랫폼
나만의 창작물을 완성하고, 새로운 웹 개발의 가능성을 경험해보세요. ♥️
👇 배포 사이트
diff --git a/apps/client/Dockerfile b/apps/client/Dockerfile
index 6f9915d..c234586 100644
--- a/apps/client/Dockerfile
+++ b/apps/client/Dockerfile
@@ -1,4 +1,7 @@
-FROM base-image AS frontend-build
+
+ARG DOCKERHUB_USERNAME
+ARG TYPE
+FROM ${DOCKERHUB_USERNAME}/base-image-${TYPE} AS frontend-build
WORKDIR /app/apps/client
COPY ./apps/client .
@@ -7,7 +10,7 @@ RUN pnpm install --offline --frozen-lockfile &&\
FROM nginx:alpine AS frontend
-COPY /apps/client/nginx.conf /etc/nginx/conf.d/default.conf
+COPY /apps/client/production/nginx.conf /etc/nginx/conf.d/default.conf
COPY /apps/client/ssl /etc/nginx/ssl
RUN chmod 644 /etc/nginx/ssl/fullchain.pem &&\
chmod 600 /etc/nginx/ssl/privkey.pem &&\
diff --git a/apps/client/Dockerfile.test b/apps/client/Dockerfile.test
new file mode 100644
index 0000000..1270603
--- /dev/null
+++ b/apps/client/Dockerfile.test
@@ -0,0 +1,19 @@
+ARG DOCKERHUB_USERNAME
+ARG TYPE
+FROM ${DOCKERHUB_USERNAME}/base-image-${TYPE} AS frontend-build
+
+WORKDIR /app/apps/client
+COPY ./apps/client .
+RUN pnpm install --offline --frozen-lockfile &&\
+ pnpm run build
+
+FROM nginx:alpine AS frontend
+
+COPY /apps/client/test/nginx.conf /etc/nginx/conf.d/default.conf
+RUN chown -R nginx:nginx /usr/share/nginx/html
+
+COPY --from=frontend-build /app/apps/client/dist /usr/share/nginx/html/
+RUN chmod -R 755 /usr/share/nginx/html
+
+EXPOSE 80
+CMD ["nginx", "-g", "daemon off;"]
\ No newline at end of file
diff --git a/apps/client/nginx.conf b/apps/client/production/nginx.conf
similarity index 87%
rename from apps/client/nginx.conf
rename to apps/client/production/nginx.conf
index 3bb2383..50b2bcb 100644
--- a/apps/client/nginx.conf
+++ b/apps/client/production/nginx.conf
@@ -9,10 +9,18 @@ server {
server {
listen 443 ssl;
+ http2 on;
+ http3 on;
+ quic_retry off;
+
server_name boolock.site;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
+ ssl_protocols TLSv1.3;
+ ssl_prefer_server_ciphers off;
+
+ add_header Alt-Svc 'h3-23=":443"; ma=86400';
location / {
root /usr/share/nginx/html;
diff --git a/apps/client/src/entities/index.ts b/apps/client/src/entities/index.ts
index adb8eeb..7d89b56 100644
--- a/apps/client/src/entities/index.ts
+++ b/apps/client/src/entities/index.ts
@@ -19,3 +19,4 @@ export { ImageTagModalImg } from './workspace/ImageTagModalImg/ImageTagModalImg'
export { ImageTagModalButton } from './workspace/ImageTagModalButton/ImageTagModalButton';
export { ImageTagModalListItem } from './workspace/ImageTagModalListItem/ImageTagModalListItem';
export { CodeExportButton } from './workspace/CodeExportButton/CodeExportButton';
+export { HelpButton } from './workspace/HelpButton/HelpButton';
diff --git a/apps/client/src/entities/workspace/CodeExportButton/CodeExportButton.tsx b/apps/client/src/entities/workspace/CodeExportButton/CodeExportButton.tsx
index f407d8e..112c3c0 100644
--- a/apps/client/src/entities/workspace/CodeExportButton/CodeExportButton.tsx
+++ b/apps/client/src/entities/workspace/CodeExportButton/CodeExportButton.tsx
@@ -1,9 +1,9 @@
import { Spinner } from '@/shared/ui';
import { exportPreviewHtml } from '@/shared/utils';
import toast from 'react-hot-toast';
-import { useState } from 'react';
+import { memo, useState } from 'react';
-export const CodeExportButton = () => {
+export const CodeExportButton = memo(() => {
const [isLoading, setIsLoading] = useState(false);
const handleClick = () => {
@@ -32,4 +32,4 @@ export const CodeExportButton = () => {
)}
);
-};
+});
diff --git a/apps/client/src/entities/workspace/CssCategoryButton/CssCategoryButton.tsx b/apps/client/src/entities/workspace/CssCategoryButton/CssCategoryButton.tsx
index 2a94421..f0ec73e 100644
--- a/apps/client/src/entities/workspace/CssCategoryButton/CssCategoryButton.tsx
+++ b/apps/client/src/entities/workspace/CssCategoryButton/CssCategoryButton.tsx
@@ -1,6 +1,5 @@
import { TCssCategory } from '@/shared/types';
import { useCssPropsStore } from '@/shared/store';
-
type CssCategoryButtonProps = {
cssCategory: TCssCategory;
};
@@ -11,7 +10,8 @@ type CssCategoryButtonProps = {
* CSS 카테고리를 선택할 수 있는 버튼 컴포넌트
*/
export const CssCategoryButton = ({ cssCategory }: CssCategoryButtonProps) => {
- const { selectedCssCategory, setSelectedCssCategory } = useCssPropsStore();
+ const selectedCssCategory = useCssPropsStore((state) => state.selectedCssCategory);
+ const setSelectedCssCategory = useCssPropsStore((state) => state.setSelectedCssCategory);
return (