diff --git a/.dockerignore b/.dockerignore index 1c360636e..2046102cd 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,13 +1,19 @@ # Build Artifacts dist .next +.turbo # IDEs .vscode # Project Metadata +.crowdin.yml + +# Documentation README.md +SECURITY.md CHANGELOG.md +CODE_OF_CONDUCT.md # Project Dependencies node_modules @@ -18,4 +24,4 @@ Dockerfile docker-compose.yml # Android App -/app \ No newline at end of file +/app diff --git a/.env.example b/.env.example index c6dc04ba6..b714672a8 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,11 @@ +# Turbo Cache (Optional) +TURBO_TEAM= +TURBO_TOKEN= + # Server + Client TZ=UTC -PUBLIC_URL=http://localhost:3000 -PUBLIC_SERVER_URL=http://localhost:3000/api +PUBLIC_URL=http://client:3000 +PUBLIC_SERVER_URL=http://server:3100 PUBLIC_GOOGLE_CLIENT_ID= # Server + Database @@ -18,10 +22,12 @@ JWT_SECRET= JWT_EXPIRY_TIME=604800 GOOGLE_CLIENT_SECRET= GOOGLE_API_KEY= -SENDGRID_API_KEY= -SENDGRID_FORGOT_PASSWORD_TEMPLATE_ID= -SENDGRID_FROM_NAME= -SENDGRID_FROM_EMAIL= +MAIL_FROM_NAME= +MAIL_FROM_EMAIL= +MAIL_HOST= +MAIL_PORT= +MAIL_USERNAME= +MAIL_PASSWORD= STORAGE_BUCKET= STORAGE_REGION= STORAGE_ENDPOINT= diff --git a/.eslintrc.json b/.eslintrc.json index c9c7e52f8..d500d56ce 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -5,27 +5,27 @@ "extends": ["plugin:@typescript-eslint/recommended", "plugin:prettier/recommended"], "plugins": ["@typescript-eslint/eslint-plugin", "simple-import-sort", "unused-imports"], "rules": { + // Unused Imports + "no-unused-vars": "off", + // Simple Import Sort + "simple-import-sort/imports": "error", + "simple-import-sort/exports": "error", + "unused-imports/no-unused-vars": [ + "warn", + { + "vars": "all", + "args": "none", + "varsIgnorePattern": "^_", + "argsIgnorePattern": "^_" + } + ], + "unused-imports/no-unused-imports": "error", // TypeScript ESLint "@typescript-eslint/no-unused-vars": "off", "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/interface-name-prefix": "off", "@typescript-eslint/explicit-function-return-type": "off", - "@typescript-eslint/explicit-module-boundary-types": "off", - // Simple Import Sort - "simple-import-sort/imports": "error", - "simple-import-sort/exports": "error", - // Unused Imports - "no-unused-vars": "off", - "unused-imports/no-unused-imports": "error", - "unused-imports/no-unused-vars": [ - "warn", - { - "vars": "all", - "varsIgnorePattern": "^_", - "args": "none", - "argsIgnorePattern": "^_" - } - ] + "@typescript-eslint/explicit-module-boundary-types": "off" }, "overrides": [ { diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index bc40df868..9bb7695fe 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -1,36 +1,43 @@ --- name: Bug Report about: Create a report to help improve -title: "[BUG] " +title: '[BUG] ' labels: bug assignees: '' - --- **Describe the bug** + **Product Flavor** + - [ ] Managed (https://rxresu.me) - [ ] Self Hosted **To Reproduce** + + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** + **Screenshots** + **Desktop (please complete the following information):** - - OS: - - Browser - - Version + +- OS: +- Browser +- Version **Additional context** + diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md index e9ec9757f..f90431adf 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.md +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -1,20 +1,23 @@ --- name: Feature Request about: Suggest an idea for this project -title: "[FEATURE] " +title: '[FEATURE] ' labels: enhancement assignees: '' - --- **Is your feature request related to a problem? Please describe.** + **Describe the solution you'd like** + **Describe alternatives you've considered** + **Additional context** + diff --git a/.github/workflows/docker-build-push.yml b/.github/workflows/docker-build-push.yml index 68fddcd4b..1ffbd75fc 100644 --- a/.github/workflows/docker-build-push.yml +++ b/.github/workflows/docker-build-push.yml @@ -9,9 +9,15 @@ jobs: name: Client runs-on: ubuntu-latest + env: + TURBO_TEAM: ${{ secrets.TURBO_TEAM }} + TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} + steps: - name: Checkout the repository uses: actions/checkout@v3.0.2 + with: + fetch-depth: 2 - id: version name: Get Version @@ -46,9 +52,15 @@ jobs: name: Server runs-on: ubuntu-latest + env: + TURBO_TEAM: ${{ secrets.TURBO_TEAM }} + TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} + steps: - name: Checkout the repository uses: actions/checkout@v3.0.2 + with: + fetch-depth: 2 - id: version name: Get Version diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 000000000..e70769c22 --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,88 @@ +name: Build Docker Image + +on: pull_request + +jobs: + client: + name: Client + runs-on: ubuntu-latest + + env: + TURBO_TEAM: ${{ secrets.TURBO_TEAM }} + TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} + + steps: + - name: Checkout the repository + uses: actions/checkout@v3.0.2 + with: + fetch-depth: 2 + + - id: vars + name: Get Short SHA + run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)" + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2.0.0 + + - id: buildx + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2.0.0 + with: + install: true + + - id: version + name: Get Version + run: echo ::set-output name=tag::${GITHUB_REF#refs/*/} + + - name: Build Client Image + uses: docker/build-push-action@v3.1.1 + with: + context: . + push: false + file: client/Dockerfile + platforms: linux/amd64,linux/arm64 + tags: | + amruthpillai/reactive-resume:client-latest + amruthpillai/reactive-resume:client-${{ steps.vars.outputs.sha_short }} + ghcr.io/amruthpillai/reactive-resume:client-latest + ghcr.io/amruthpillai/reactive-resume:client-${{ steps.vars.outputs.sha_short }} + + server: + name: Server + runs-on: ubuntu-latest + + env: + TURBO_TEAM: ${{ secrets.TURBO_TEAM }} + TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} + + steps: + - name: Checkout the repository + uses: actions/checkout@v3.0.2 + with: + fetch-depth: 2 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2.0.0 + + - id: buildx + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2.0.0 + with: + install: true + + - id: version + name: Get Version + run: echo ::set-output name=tag::${GITHUB_REF#refs/*/} + + - name: Build Server Image + uses: docker/build-push-action@v3.1.1 + with: + context: . + push: false + file: server/Dockerfile + platforms: linux/amd64,linux/arm64 + tags: | + amruthpillai/reactive-resume:server-latest + amruthpillai/reactive-resume:server-${{ steps.vars.outputs.sha_short }} + ghcr.io/amruthpillai/reactive-resume:server-latest + ghcr.io/amruthpillai/reactive-resume:server-${{ steps.vars.outputs.sha_short }} diff --git a/.gitignore b/.gitignore index bd682b051..b76b46fe7 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,7 @@ node_modules # macOS -.DS_Store \ No newline at end of file +.DS_Store + +# Turbo +.turbo \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100755 index 02444ade8..000000000 --- a/.husky/pre-commit +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" - -pnpm install -pnpm run lint -pnpm run format \ No newline at end of file diff --git a/.prettierignore b/.prettierignore index 3f4fce07d..970c781db 100644 --- a/.prettierignore +++ b/.prettierignore @@ -18,6 +18,7 @@ CHANGELOG.md # Project Dependencies node_modules +pnpm-lock.yaml # Docker Dockerfile diff --git a/.vscode/extensions.json b/.vscode/extensions.json index f93861ebb..d8fa4ecaf 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,3 +1,7 @@ { - "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode", "lokalise.i18n-ally"] -} + "recommendations": [ + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "lokalise.i18n-ally" + ] +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index c19ed6d80..c0261c28c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -3,12 +3,12 @@ "configurations": [ { "type": "node", - "request": "attach", "name": "Debug: Server", "port": 9229, + "request": "attach", "restart": true, - "stopOnEntry": false, - "protocol": "inspector" + "protocol": "inspector", + "stopOnEntry": false }, { "name": "Debug: Client", @@ -17,10 +17,10 @@ "command": "pnpm run dev:client", "console": "integratedTerminal", "serverReadyAction": { + "action": "debugWithChrome", "pattern": "started server on .+, url: (https?://.+)", - "uriFormat": "%s", - "action": "debugWithChrome" + "uriFormat": "%s" } } ] -} +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 0f057d927..fba606381 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,27 +1,18 @@ { "css.validate": false, + "scss.validate": false, + "editor.wordWrap": "on", + "npm.packageManager": "pnpm", + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.codeActionsOnSave": { "source.fixAll.eslint": true }, - "editor.defaultFormatter": "esbenp.prettier-vscode", - "editor.formatOnSave": true, - "editor.wordWrap": "on", "eslint.workingDirectories": [ "schema", "client", "server" ], - "i18n-ally.enabledFrameworks": [ - "react" - ], - "i18n-ally.keystyle": "nested", - "i18n-ally.localesPaths": [ - "client/public/locales" - ], - "i18n-ally.namespace": true, - "i18n-ally.pathMatcher": "{locale}/{namespaces}.{ext}", - "i18n-ally.sortKeys": true, - "scss.validate": false, "conventionalCommits.scopes": [ "client", "server", diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 92dfcd50a..cbefc2ac5 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -17,23 +17,23 @@ diverse, inclusive, and healthy community. Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -* Focusing on what is best not just for us as individuals, but for the +- Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or +- The use of sexualized language or imagery, and sexual attention or advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities @@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an +standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within diff --git a/client/Dockerfile b/client/Dockerfile index 64be8a0f9..26fdc3394 100644 --- a/client/Dockerfile +++ b/client/Dockerfile @@ -1,22 +1,19 @@ -FROM node:lts-alpine as dependencies - -RUN apk add --no-cache curl g++ make python3 \ - && curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm +FROM node:lts-alpine AS base WORKDIR /app -COPY package.json pnpm-*.yaml ./ +RUN apk add --no-cache g++ git curl make python3 \ + && curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm + +FROM base as dependencies + +COPY package.json pnpm-*.yaml turbo.json ./ COPY ./schema/package.json ./schema/package.json COPY ./client/package.json ./client/package.json RUN pnpm install --frozen-lockfile -FROM node:lts-alpine as builder - -RUN apk add --no-cache curl g++ make python3 \ - && curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm - -WORKDIR /app +FROM base as builder COPY . . @@ -24,21 +21,20 @@ COPY --from=dependencies /app/node_modules ./node_modules COPY --from=dependencies /app/schema/node_modules ./schema/node_modules COPY --from=dependencies /app/client/node_modules ./client/node_modules -RUN pnpm run build:schema -RUN pnpm run build:client +ARG TURBO_TEAM +ARG TURBO_TOKEN -FROM node:lts-alpine as production +ENV TURBO_TEAM $TURBO_TEAM +ENV TURBO_TOKEN $TURBO_TOKEN -WORKDIR /app +RUN pnpm run build --filter client -RUN apk add --no-cache curl \ - && curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm +FROM base as production -COPY --from=builder /app/pnpm-*.yaml ./ -COPY --from=builder /app/package.json ./ +COPY --from=builder /app/package.json /app/pnpm-*.yaml /app/turbo.json ./ COPY --from=builder /app/client/package.json ./client/package.json -RUN pnpm install -F client --frozen-lockfile --prod +RUN pnpm install --filter client --prod --frozen-lockfile --workspace-root COPY --from=builder /app/client/.next ./client/.next COPY --from=builder /app/client/public ./client/public @@ -52,4 +48,4 @@ ENV PORT 3000 HEALTHCHECK --interval=30s --timeout=20s --retries=3 --start-period=15s \ CMD curl -fSs 127.0.0.1:3000 || exit 1 -CMD [ "pnpm", "run", "start:client" ] \ No newline at end of file +CMD [ "pnpm", "run", "start", "--filter", "client" ] \ No newline at end of file diff --git a/client/components/build/RightSidebar/sections/Settings.tsx b/client/components/build/RightSidebar/sections/Settings.tsx index b7c381073..2697a3456 100644 --- a/client/components/build/RightSidebar/sections/Settings.tsx +++ b/client/components/build/RightSidebar/sections/Settings.tsx @@ -97,7 +97,7 @@ const Settings = () => { {/* Global Settings */} <> - + {t('builder.rightSidebar.sections.settings.global.heading')} @@ -155,7 +155,7 @@ const Settings = () => { {/* Page Settings */} <> - + {t('builder.rightSidebar.sections.settings.page.heading')} @@ -187,7 +187,7 @@ const Settings = () => { {/* Resume Settings */} <> - + {t('builder.rightSidebar.sections.settings.resume.heading')} diff --git a/client/config/theme.ts b/client/config/theme.ts index 2ab7b4167..7e6ebf34b 100644 --- a/client/config/theme.ts +++ b/client/config/theme.ts @@ -1,6 +1,6 @@ -import { createTheme } from '@mui/material'; +import { createTheme, ThemeOptions } from '@mui/material/styles'; -const theme = createTheme({ +const theme: ThemeOptions = { typography: { fontSize: 12, fontFamily: 'Inter, sans-serif', @@ -49,7 +49,7 @@ const theme = createTheme({ }, }, }, -}); +}; export const lightTheme = createTheme({ ...theme, diff --git a/client/modals/auth/LoginModal.tsx b/client/modals/auth/LoginModal.tsx index 33b6c6c0c..bf7b8f7d8 100644 --- a/client/modals/auth/LoginModal.tsx +++ b/client/modals/auth/LoginModal.tsx @@ -1,7 +1,8 @@ import env from '@beam-australia/react-env'; import { joiResolver } from '@hookform/resolvers/joi'; -import { Google, Login, Visibility, VisibilityOff } from '@mui/icons-material'; +import { Login, Visibility, VisibilityOff } from '@mui/icons-material'; import { Button, IconButton, InputAdornment, TextField } from '@mui/material'; +import { CredentialResponse, GoogleLogin } from '@react-oauth/google'; import Joi from 'joi'; import { isEmpty } from 'lodash'; import { Trans, useTranslation } from 'next-i18next'; @@ -17,8 +18,6 @@ import { ServerError } from '@/services/axios'; import { useAppDispatch, useAppSelector } from '@/store/hooks'; import { setModalState } from '@/store/modal/modalSlice'; -declare const google: any; - type FormData = { identifier: string; password: string; @@ -85,28 +84,16 @@ const LoginModal: React.FC = () => { dispatch(setModalState({ modal: 'auth.forgot', state: { open: true } })); }; - const handleLoginWithGoogle = async () => { - console.log(process.env.PUBLIC_GOOGLE_CLIENT_ID, env('GOOGLE_CLIENT_ID')); + const handleLoginWithGoogle = async (response: CredentialResponse) => { + if (response.credential) { + await loginWithGoogleMutation({ credential: response.credential }, { onError: handleLoginWithGoogleError }); - google.accounts.id.initialize({ - auto_select: true, - itp_support: true, - client_id: env('GOOGLE_CLIENT_ID'), - callback: async (response: any) => { - await loginWithGoogleMutation({ credential: response.credential }); + handleClose(); + } + }; - handleClose(); - }, - }); - - google.accounts.id.prompt((notification: any) => { - if (notification.isNotDisplayed() || notification.isSkippedMoment()) { - const reason = notification.getNotDisplayedReason() || notification.getSkippedReason(); - - toast.error(`Google returned an error while trying to sign in: ${reason}.`); - toast("Please try logging in using email/password, or use another browser that supports Google's One Tap API."); - } - }); + const handleLoginWithGoogleError = () => { + toast("Please try logging in using email/password, or use another browser that supports Google's One Tap API."); }; const PasswordVisibility = (): React.ReactElement => { @@ -130,15 +117,7 @@ const LoginModal: React.FC = () => { footerChildren={
{!isEmpty(env('GOOGLE_CLIENT_ID')) && ( - + )} + )}