Skip to content

Commit 2f18048

Browse files
authored
feat: migrate to pnpm, enhance Docker and deployment workflows, and improve clipboard OTP UX (#362)
* chore: migrate project to pnpm and update dependencies - Updated package manager from Yarn to pnpm in package.json and Dockerfile. - Adjusted dependency specifications to use workspace:* for local packages. - Removed yarn.lock and added pnpm-lock.yaml for dependency management. - Updated Dockerfile to install pnpm globally and configure it for better network settings. - Refactored middleware and cookie imports to align with new structure. - Cleaned up unused code and comments in various components. * refactor: update docker-compose configuration for core-client service - Renamed core service to core-client for clarity. - Added environment variables for base URL configurations. - Removed commented-out network definitions to clean up the file. * feat: add docker-compose configuration for core-client service - Introduced a new docker-compose file for the core-client service. - Configured build context and Dockerfile path. - Set environment variables for base URL configurations. * feat: add environment variables to Dockerfile for base URL configurations - Introduced environment variables NEXT_PUBLIC_BASE_URL and NEXT_PUBLIC_BASE_URL_ATTACHMENT in the Dockerfile. - Enhanced configuration for better integration with the core-client service. * fix: update base URL in coreApi and guestApi to a hardcoded value - Changed the base URL in coreApi and guestApi from the environment variable to a hardcoded URL for immediate testing. - Added TODO comments to remind future updates to revert to using the environment variable. * feat: enhance authentication components with clipboard OTP functionality - Added a reusable `useClipboardOtp` hook for extracting OTP from the clipboard. - Introduced `PasteOtpButton` component to facilitate pasting OTP directly into forms. - Updated `SetPasswordPage`, `SignupOtpForm`, and other components to utilize the new clipboard functionality. - Enhanced user experience by allowing OTP pasting and auto-submission. - Refactored `PasswordInput` component for better integration with the new features. * feat: add clipboard permission request functionality to useClipboardOtp hook - Introduced a new `requestPermission` method to explicitly request clipboard access. - Added `isRequestingPermission` state to track the permission request status. - Updated `checkClipboard` method to utilize the new permission request logic. - Enhanced the overall clipboard OTP functionality for better user experience. * feat: implement clipboard permission listener in useClipboardOtp hook - Added a useEffect to listen for changes in clipboard permission status. - Automatically checks the clipboard when permission is granted. * feat: add deploy action * feat: add production docker-compose configuration for core-client service - Introduced a new docker-compose.prod.yml file for deploying the core-client service. - Configured service settings including build context, Dockerfile path, and environment variables for base URL configurations. * refactor: update docker-compose configuration for client service - Renamed core-client service to client for clarity. - Updated image reference to use the latest version of the pixel-client. - Adjusted service settings in docker-compose.prod.yml for improved deployment. * chore: enhance Docker Hub deployment process in workflow - Added a step to log in to Docker Hub before building and pushing the Docker image. - Cleaned up the push command by removing redundant login steps from the push action. - Improved overall clarity and efficiency of the deployment workflow. * chore: clean up Docker deployment workflow - Removed unnecessary blank lines in the deploy.yml file for improved readability. - Ensured consistency in the Docker push command formatting. * fix: update Docker deployment workflow to pull client service - Changed the deployment command to pull the client service instead of the web service in the Docker Compose workflow. - Ensured the deployment process aligns with the recent renaming of the core-client service. * chore: remove commented-out Docker build and push steps from deployment workflow - Eliminated unnecessary commented-out lines in the deploy.yml file to enhance clarity and maintainability of the workflow. - Streamlined the deployment process by focusing on active commands. * chore: add concurrency configuration to deployment workflow - Introduced a concurrency group in the deploy.yml file to manage simultaneous workflow runs. - Set the concurrency group to use the branch name, enhancing control over deployment processes. * chore: update deployment workflow to use self-hosted runners - Changed the runner configuration in deploy.yml to use self-hosted runners instead of the default group. - Aimed to optimize deployment performance and resource management. * chore: expose port for core service in production Docker Compose - Added port mapping for the core service to expose port 3000 in docker-compose.prod.yml. - This change allows external access to the service, facilitating communication with other services or clients. * fix: update API base URLs from HTTPS to HTTP - Changed the base URL for both coreApi and guestApi from "https://api.pixelgenius.ir" to "http://api.pixelgenius.ir". - This adjustment ensures consistency in API endpoint usage across the application. * refactor: simplify post-login schema transformation and update docker-compose configuration - Modified the post-login schema transformation to return the original data without modifications. - Updated the docker-compose.yml to rename the core-client service to client and changed the image reference to the latest version, enhancing clarity and consistency. * feat: add development and preview Docker Compose configurations - Introduced docker-compose.dev.yml for development environment with client-dev service. - Added docker-compose.preview.yml for PR previews, allowing dynamic port assignment and image tagging. - Updated deploy.yml to handle deployments for development and PR previews, enhancing CI/CD workflow. * refactor: update PR comment actions in deployment workflow - Replaced the GitHub script action with a sticky pull request comment action for posting deployment and cleanup messages. - Enhanced the preview deployment comment to include a structured message with the preview URL and container details. - Improved the cleanup confirmation comment to provide a clear summary of the resources removed after the PR is closed. * chore: update permissions in deployment workflow - Added permissions for reading contents and writing to pull requests and issues in deploy.yml. - This change enhances the workflow's ability to interact with GitHub resources during deployment processes.
1 parent f42431e commit 2f18048

File tree

42 files changed

+17604
-12127
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+17604
-12127
lines changed

.github/workflows/deploy.yml

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
name: CI/CD Pipeline
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- dev
8+
pull_request:
9+
types: [opened, synchronize, reopened, closed]
10+
11+
concurrency:
12+
group: "CI-${{ github.ref_name }}-${{ github.event.number || github.sha }}"
13+
cancel-in-progress: true
14+
15+
permissions:
16+
contents: read
17+
pull-requests: write
18+
issues: write
19+
20+
jobs:
21+
# Build job for all scenarios
22+
build:
23+
runs-on: [self-hosted, core]
24+
if: github.event.action != 'closed'
25+
outputs:
26+
image-tag: ${{ steps.image-tag.outputs.tag }}
27+
port: ${{ steps.port.outputs.port }}
28+
steps:
29+
- name: Checkout Repository
30+
uses: actions/checkout@v4
31+
32+
- name: Determine image tag and port
33+
id: image-tag
34+
run: |
35+
if [ "${{ github.ref_name }}" = "main" ]; then
36+
echo "tag=latest" >> $GITHUB_OUTPUT
37+
elif [ "${{ github.ref_name }}" = "dev" ]; then
38+
echo "tag=dev" >> $GITHUB_OUTPUT
39+
else
40+
echo "tag=pr-${{ github.event.number }}" >> $GITHUB_OUTPUT
41+
fi
42+
43+
- name: Determine port for PR
44+
id: port
45+
run: |
46+
if [ "${{ github.event_name }}" = "pull_request" ]; then
47+
# Calculate port: 3100 + PR number (ensures unique ports)
48+
PORT=$((3100 + ${{ github.event.number }}))
49+
echo "port=$PORT" >> $GITHUB_OUTPUT
50+
fi
51+
52+
- name: Create .env file
53+
run: |
54+
echo "NEXT_PUBLIC_BASE_URL=${{ secrets.NEXT_PUBLIC_BASE_URL }}" >> .env
55+
echo "NEXT_PUBLIC_BASE_URL_ATTACHMENT=${{ secrets.NEXT_PUBLIC_BASE_URL_ATTACHMENT }}" >> .env
56+
57+
- name: Login to Docker Hub
58+
run: docker login -u mrbadri -p ${{ secrets.DOCKER_TOKEN }}
59+
60+
- name: Build Docker Image
61+
run: |
62+
TAG=${{ steps.image-tag.outputs.tag }}
63+
if [ "${{ github.ref_name }}" = "main" ]; then
64+
docker compose -f docker-compose.prod.yml build
65+
elif [ "${{ github.ref_name }}" = "dev" ]; then
66+
docker compose -f docker-compose.dev.yml build
67+
docker tag mrbadri/pixel-client:dev mrbadri/pixel-client:$TAG
68+
else
69+
# For PR - build and tag
70+
docker build -t mrbadri/pixel-client:$TAG -f ./apps/core/Dockerfile .
71+
fi
72+
73+
- name: Push Docker Image to Docker Hub
74+
run: |
75+
TAG=${{ steps.image-tag.outputs.tag }}
76+
docker push mrbadri/pixel-client:$TAG
77+
78+
# Deploy to production (main branch)
79+
deploy-production:
80+
runs-on: [self-hosted, core]
81+
needs: build
82+
if: github.ref_name == 'main' && github.event_name == 'push'
83+
steps:
84+
- name: Checkout Repository
85+
uses: actions/checkout@v4
86+
87+
- name: Create .env file
88+
run: |
89+
echo "NEXT_PUBLIC_BASE_URL=${{ secrets.NEXT_PUBLIC_BASE_URL }}" >> .env
90+
echo "NEXT_PUBLIC_BASE_URL_ATTACHMENT=${{ secrets.NEXT_PUBLIC_BASE_URL_ATTACHMENT }}" >> .env
91+
92+
- name: Deploy Production
93+
run: |
94+
docker compose -f docker-compose.prod.yml pull client
95+
docker compose -f docker-compose.prod.yml down || true
96+
docker compose -f docker-compose.prod.yml up -d
97+
98+
# Deploy to development (dev branch)
99+
deploy-development:
100+
runs-on: [self-hosted, core]
101+
needs: build
102+
if: github.ref_name == 'dev' && github.event_name == 'push'
103+
steps:
104+
- name: Checkout Repository
105+
uses: actions/checkout@v4
106+
107+
- name: Create .env file
108+
run: |
109+
echo "NEXT_PUBLIC_BASE_URL=${{ secrets.NEXT_PUBLIC_BASE_URL }}" >> .env
110+
echo "NEXT_PUBLIC_BASE_URL_ATTACHMENT=${{ secrets.NEXT_PUBLIC_BASE_URL_ATTACHMENT }}" >> .env
111+
112+
- name: Deploy Development
113+
run: |
114+
docker compose -f docker-compose.dev.yml pull client-dev
115+
docker compose -f docker-compose.dev.yml down || true
116+
docker compose -f docker-compose.dev.yml up -d
117+
118+
# Deploy PR preview
119+
deploy-preview:
120+
runs-on: [self-hosted, core]
121+
needs: build
122+
if: github.event_name == 'pull_request' && github.event.action != 'closed'
123+
steps:
124+
- name: Checkout Repository
125+
uses: actions/checkout@v4
126+
127+
- name: Create .env file
128+
run: |
129+
echo "NEXT_PUBLIC_BASE_URL=${{ secrets.NEXT_PUBLIC_BASE_URL }}" >> .env
130+
echo "NEXT_PUBLIC_BASE_URL_ATTACHMENT=${{ secrets.NEXT_PUBLIC_BASE_URL_ATTACHMENT }}" >> .env
131+
132+
- name: Create PR-specific compose file
133+
run: |
134+
PR_NUMBER=${{ github.event.number }}
135+
PORT=${{ needs.build.outputs.port }}
136+
137+
# Create PR-specific docker-compose file
138+
sed "s/PR_NUMBER/$PR_NUMBER/g; s/PORT_NUMBER/$PORT/g" docker-compose.preview.yml > docker-compose.pr-$PR_NUMBER.yml
139+
140+
- name: Deploy Preview
141+
run: |
142+
PR_NUMBER=${{ github.event.number }}
143+
144+
# Pull the image
145+
docker pull mrbadri/pixel-client:pr-$PR_NUMBER
146+
147+
# Stop and remove existing preview if exists
148+
docker compose -f docker-compose.pr-$PR_NUMBER.yml down || true
149+
150+
# Start the preview
151+
docker compose -f docker-compose.pr-$PR_NUMBER.yml up -d
152+
153+
- name: Comment PR with preview link
154+
uses: marocchino/sticky-pull-request-comment@v2
155+
with:
156+
header: preview-deployment
157+
message: |
158+
### 🚀 Preview Deployed Successfully!
159+
160+
Your pull request preview is now available:
161+
- 📍 **Preview URL**: http://82.115.24.87:${{ needs.build.outputs.port }}
162+
- 🔗 **Container**: `client-preview-${{ github.event.number }}`
163+
- 🐳 **Image**: `mrbadri/pixel-client:pr-${{ github.event.number }}`
164+
165+
This preview will be automatically cleaned up when the PR is closed.
166+
167+
# Cleanup PR preview when closed
168+
cleanup-preview:
169+
runs-on: [self-hosted, core]
170+
if: github.event_name == 'pull_request' && github.event.action == 'closed'
171+
steps:
172+
- name: Checkout Repository
173+
uses: actions/checkout@v4
174+
175+
- name: Cleanup PR preview
176+
run: |
177+
PR_NUMBER=${{ github.event.number }}
178+
179+
# Stop and remove preview containers
180+
docker compose -f docker-compose.pr-$PR_NUMBER.yml down || true
181+
182+
# Remove the compose file
183+
rm -f docker-compose.pr-$PR_NUMBER.yml || true
184+
185+
# Remove the Docker image
186+
docker rmi mrbadri/pixel-client:pr-$PR_NUMBER || true
187+
188+
- name: Post Cleanup Confirmation Comment
189+
if: always()
190+
uses: marocchino/sticky-pull-request-comment@v2
191+
with:
192+
header: preview-cleanup
193+
message: |
194+
### 🧹 Preview Cleanup Complete
195+
196+
All preview deployment resources for this pull request have been successfully removed:
197+
- 🐳 Docker container `client-preview-${{ github.event.number }}` stopped and removed
198+
- 📦 Docker image `mrbadri/pixel-client:pr-${{ github.event.number }}` cleaned up
199+
- 📁 Docker compose file removed
200+
201+
The cleanup process has finished for PR #${{ github.event.number }}.

apps/core/Dockerfile

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ RUN apk update
99
RUN apk add --no-cache libc6-compat
1010
# Set working directory
1111
WORKDIR /app
12-
RUN yarn global add turbo
12+
RUN npm install -g pnpm turbo
1313
COPY . .
1414
# RUN npm run prepare:ci
1515
RUN turbo prune core --docker
@@ -19,13 +19,12 @@ FROM base AS installer
1919
RUN apk update
2020
RUN apk add --no-cache libc6-compat
2121
WORKDIR /app
22+
RUN npm install -g pnpm turbo
2223

2324
# First install the dependencies (as they change less often)
2425
COPY --from=builder /app/out/json/ .
25-
# Configure Yarn with better network settings and use Taobao registry
26-
RUN yarn config set registry https://registry.npmmirror.com/ && \
27-
yarn config set network-timeout 600000 && \
28-
yarn install --network-timeout 600000 --no-network-concurrency
26+
# Configure pnpm with better network settings and use Taobao registry
27+
RUN pnpm install --frozen-lockfile
2928

3029
# Build the project
3130
COPY --from=builder /app/out/full/ .
@@ -37,7 +36,7 @@ COPY --from=builder /app/out/full/ .
3736
# ARG TURBO_TOKEN
3837
# ENV TURBO_TOKEN=$TURBO_TOKEN
3938

40-
RUN yarn turbo build --filter=core
39+
RUN pnpm turbo build
4140

4241
FROM base AS runner
4342
WORKDIR /app
@@ -47,6 +46,10 @@ RUN addgroup --system --gid 1001 nodejs
4746
RUN adduser --system --uid 1001 nextjs
4847
USER nextjs
4948

49+
# Environment variables
50+
ENV NEXT_PUBLIC_BASE_URL=${NEXT_PUBLIC_BASE_URL}
51+
ENV NEXT_PUBLIC_BASE_URL_ATTACHMENT=${NEXT_PUBLIC_BASE_URL_ATTACHMENT}
52+
5053
# Automatically leverage output traces to reduce image size
5154
# https://nextjs.org/docs/advanced-features/output-file-tracing
5255
COPY --from=installer --chown=nextjs:nodejs /app/apps/core/.next/standalone ./

apps/core/app/(landing)/_components/mobile-menu/BrowseSheet.tsx

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
1-
import {
2-
IconDeviceMobile, IconUsersGroup, IconEye, IconCube, IconVector, IconBox, IconLayout, IconMessage, IconTypography, IconDiamond
1+
import {
2+
IconDeviceMobile,
3+
IconUsersGroup,
4+
IconEye,
5+
IconCube,
6+
IconVector,
7+
IconBox,
8+
IconLayout,
9+
IconMessage,
10+
IconTypography,
11+
IconDiamond,
312
} from "@tabler/icons-react";
413

514
const categories = [
@@ -18,10 +27,15 @@ const categories = [
1827
const BrowseSheet = () => (
1928
<div className="flex flex-col items-center w-full">
2029
<div className="w-12 h-1.5 bg-[#fff]/20 rounded-full mt-1 mb-4" />
21-
<h2 className="text-2xl font-bold text-white mb-6 w-full text-left">Browse</h2>
30+
<h2 className="text-2xl font-bold text-white mb-6 w-full text-left">
31+
Browse
32+
</h2>
2233
<div className="grid grid-cols-2 gap-x-8 gap-y-5 w-full">
2334
{categories.map((cat) => (
24-
<div key={cat.label} className="flex items-center gap-3 text-white/90 text-base font-medium">
35+
<div
36+
key={cat.label}
37+
className="flex items-center gap-3 text-white/90 text-base font-medium"
38+
>
2539
<span className="bg-[#232228] rounded-md p-2 flex items-center justify-center">
2640
{cat.icon}
2741
</span>
@@ -32,4 +46,4 @@ const BrowseSheet = () => (
3246
</div>
3347
);
3448

35-
export default BrowseSheet;
49+
export default BrowseSheet;

apps/core/app/(landing)/become-auther/_components/auther-form.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ const AutherForm = () => {
102102
/>
103103
</div>
104104
<div>
105-
106105
<Textarea
107106
placeholder="Write a short message about yourself"
108107
className=" bg-card h-40 resize-none p-3"

apps/core/app/auth/_components/auth-card.tsx

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,35 @@
1-
// styles
2-
// props : children
3-
// <authCard> form </authCard>
4-
1+
"use client";
52
import PixelIcon from "@repo/icons/pxiel";
3+
import { Button } from "@repo/ui/components";
4+
import { ArrowLeft } from "lucide-react";
5+
import { useRouter } from "next/navigation";
66

77
import { ReactNode } from "react";
88

99
const AuthCard = ({ children }: { children: ReactNode }) => {
10+
const router = useRouter();
11+
12+
const handleBackClick = () => {
13+
router.back();
14+
};
15+
1016
return (
1117
<div className="flex items-center relative z-10 flex-col gap-4 bg-card w-[calc(100% - 32px)] sm:w-[450px] rounded-xl mx-4">
18+
{/* add back button */}
19+
<div className="absolute top-4 left-4 z-10">
20+
<Button
21+
variant="tertiary"
22+
size="icon"
23+
onClick={handleBackClick}
24+
className="hover:bg-secondary/50"
25+
aria-label="Go back"
26+
>
27+
<ArrowLeft size={20} />
28+
</Button>
29+
</div>
30+
1231
{/* logo */}
13-
<div className="w-full px-3 sm:p-7 py-7 flex flex-col items-center gap-4">
32+
<div className="w-full px-3 sm:p-7 py-8 flex flex-col items-center gap-4">
1433
<div>
1534
<PixelIcon size={86} color="currentColor" />
1635
</div>

apps/core/app/auth/forget-password/_components/form/forgetPasswordForm.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,37 +8,46 @@ import { Button, Input } from "@repo/ui/components";
88
import { postForgetPasswordSchema } from "@repo/apis/core/accounts/users/forgot-password/post/post-forget-password.schema";
99
import type { PostForgetPasswordRequest } from "@repo/apis/core/accounts/users/forgot-password/post/post-forget-password.types";
1010
import { UsePostForgetPassword } from "@repo/apis/core/accounts/users/forgot-password/post/use-post-forget-password";
11+
import { useEffect } from "react";
1112

1213
const ForgetPasswordForm = () => {
1314
const router = useRouter();
1415

1516
const form = useForm<PostForgetPasswordRequest>({
1617
resolver: zodResolver(postForgetPasswordSchema.request),
1718
});
18-
19+
1920
const {
2021
register,
2122
formState: { errors },
2223
handleSubmit,
2324
} = form;
24-
25+
2526
const mutation = UsePostForgetPassword({
2627
onSuccess: (res, context) => {
2728
toast.info(res.data.message);
2829
router.replace(`/auth/set-password?username=${context.username}`);
2930
},
31+
onError: (err) => {
32+
toast.error(err.response?.data.message ?? "Something went wrong");
33+
},
3034
});
3135

3236
const onSubmit = (data: PostForgetPasswordRequest) => {
3337
mutation.mutate(data);
3438
};
3539

40+
useEffect(() => {
41+
router.prefetch("/auth/set-password");
42+
}, [router]);
43+
3644
return (
3745
<form onSubmit={handleSubmit(onSubmit)} className="w-full pb-7">
3846
<Input
3947
label="Username"
4048
className="font-normal text-xs"
41-
placeholder="[email protected]"
49+
placeholder="Enter your username"
50+
autoFocus
4251
{...register("username")}
4352
error={errors.username?.message}
4453
/>

0 commit comments

Comments
 (0)