diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index ede9b5e..9acc807 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -31,12 +31,5 @@ jobs: if: success() run: | ssh -p ${{ secrets.SSH_PORT }} ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_IP }} ' - cd /home/coding-family/__app__ && \ - git fetch origin production && \ - git pull origin production && \ - git reset --hard origin/production && \ - node -v && \ - yarn app:install && \ - yarn app:build && \ - yarn app:restart + ${{ secrets.DEPLOY_SCRIPT }} ' diff --git a/.gitignore b/.gitignore index 4e36700..30bc162 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ -/node_modules -ssh-temp \ No newline at end of file +/node_modules \ No newline at end of file diff --git a/backend/src/models/User.model.ts b/backend/src/models/User.model.ts index 56d075d..12dd1a3 100644 --- a/backend/src/models/User.model.ts +++ b/backend/src/models/User.model.ts @@ -17,7 +17,12 @@ const userSchema = new Schema( lowercase: true, trim: true, match: [/^[^\s@]+@[^\s@]+\.[^\s@]+$/, 'Please enter a valid email address.'], - required: [true, 'Email is required.'], + required: [ + function (this: IUserModel) { + return !this.githubID && !this.googleID; + }, + 'Email is required.', + ], }, password: { type: String, @@ -25,7 +30,12 @@ const userSchema = new Schema( /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,}/, 'Password must have at least 6 characters and contain at least one number, one lowercase, and one uppercase letter.', ], - required: [true, 'Password is required.'], + required: [ + function (this: IUserModel) { + return !this.githubID && !this.googleID; + }, + 'Password is required.', + ], }, avatar: { type: String, diff --git a/frontend/features/auth-flow/context/Auth.context.jsx b/frontend/features/auth-flow/context/Auth.context.jsx index c9e8eaf..8f131e7 100644 --- a/frontend/features/auth-flow/context/Auth.context.jsx +++ b/frontend/features/auth-flow/context/Auth.context.jsx @@ -1,7 +1,7 @@ import { createContext, useContext, useEffect, useState } from "react"; import AuthService from "/common/services/AuthService"; import { LoadingSpinner } from "/common/components"; -import { useUserContext } from "../../user-section/context"; +import { useUserContext } from "/features/user-section/context"; const AuthContext = createContext(); diff --git a/frontend/features/auth-flow/hooks/useAuth.hook.js b/frontend/features/auth-flow/hooks/useAuth.hook.js index d59dc9f..e53080c 100644 --- a/frontend/features/auth-flow/hooks/useAuth.hook.js +++ b/frontend/features/auth-flow/hooks/useAuth.hook.js @@ -28,18 +28,20 @@ export function useAuthHook() { }, // OAuth providers - logGithubUserIn: (code) => + logGithubUserIn: (code, isFetching) => fetcher({ method: "POST", endPoint: "/auth/github", reqBody: { code }, + isFetching, }), - logGoogleUserIn: (code) => + logGoogleUserIn: (code, isFetching) => fetcher({ method: "POST", endPoint: "/auth/google", reqBody: { code }, + isFetching, }), // Token management diff --git a/frontend/features/auth-flow/pages/LoginGithub.page.jsx b/frontend/features/auth-flow/pages/LoginGithub.page.jsx index fee784c..f4fa23a 100644 --- a/frontend/features/auth-flow/pages/LoginGithub.page.jsx +++ b/frontend/features/auth-flow/pages/LoginGithub.page.jsx @@ -1,28 +1,42 @@ -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { useSearchParams } from "react-router-dom"; -import { useAuthContext } from "../context"; -import AuthService from "/common/services/AuthService"; +import { useAuthContext } from "/features/auth-flow/context"; +import { useAuthHook } from "/features/auth-flow/hooks"; +import { LoadingSpinner } from "/common/components"; export function LoginGithub() { - const { storeToken, authenticateUser } = useAuthContext(); + const [isFetching, setIsFetching] = useState(false); + const { authenticateUser } = useAuthContext(); const [searchParams] = useSearchParams(); const code = searchParams.get("code"); + const { logGithubUserIn, storeUserToken } = useAuthHook(); + const { data, isLoading, isValidating, error } = logGithubUserIn( + code, + isFetching + ); + + const headersAuth = data?.headers?.authorization; useEffect(() => { - if (code) { - AuthService.loginGithub(code) - .then((response) => { - const accessToken = response.headers.authorization.split(" ")[1]; - storeToken(accessToken); - authenticateUser(); - }) - .catch((error) => { - console.error("GithubAuth : ", error); - }); + if (code) setIsFetching(true); + + if (headersAuth) { + const accessToken = headersAuth.split(" ")[1]; + storeUserToken(accessToken); + authenticateUser(); + return; + } + + if (error) { + console.log(error); return; } - window.location.replace(`${import.meta.env.VITE_SERVER_URL}/auth/github`); - }, [code, authenticateUser, storeToken]); + + if (!code) + window.location.replace(`${import.meta.env.VITE_SERVER_URL}/auth/github`); + }, [code, headersAuth, error]); + + if (isLoading || isValidating) return ; return (
diff --git a/frontend/features/auth-flow/pages/LoginGoogle.page.jsx b/frontend/features/auth-flow/pages/LoginGoogle.page.jsx index 493dbfe..98968b9 100644 --- a/frontend/features/auth-flow/pages/LoginGoogle.page.jsx +++ b/frontend/features/auth-flow/pages/LoginGoogle.page.jsx @@ -1,28 +1,42 @@ -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { useSearchParams } from "react-router-dom"; import { useAuthContext } from "../context"; -import AuthService from "/common/services/AuthService"; +import { useAuthHook } from "/features/auth-flow/hooks"; +import { LoadingSpinner } from "/common/components"; export function LoginGoogle() { - const { storeToken, authenticateUser } = useAuthContext(); + const [isFetching, setIsFetching] = useState(false); + const { authenticateUser } = useAuthContext(); const [searchParams] = useSearchParams(); const code = searchParams.get("code"); + const { logGoogleUserIn, storeUserToken } = useAuthHook(); + const { data, isLoading, isValidating, error } = logGoogleUserIn( + code, + isFetching + ); + + const headersAuth = data?.headers?.authorization; useEffect(() => { - if (code) { - AuthService.loginGoogle(code) - .then((response) => { - const accessToken = response.headers.authorization.split(" ")[1]; - storeToken(accessToken); - authenticateUser(); - }) - .catch((error) => { - console.error("GoogleAuth:", error); - }); + if (code) setIsFetching(true); + + if (headersAuth) { + const accessToken = headersAuth.split(" ")[1]; + storeUserToken(accessToken); + authenticateUser(); + return; + } + + if (error) { + console.log(error); return; } - window.location.replace(`${import.meta.env.VITE_SERVER_URL}/auth/google`); - }, [code, authenticateUser, storeToken]); + + if (!code) + window.location.replace(`${import.meta.env.VITE_SERVER_URL}/auth/google`); + }, [code, headersAuth, error]); + + if (isLoading || isValidating) return ; return (
diff --git a/frontend/features/user-section/pages/UserEditProfile.page.jsx b/frontend/features/user-section/pages/UserEditProfile.page.jsx index 086926d..79d8454 100644 --- a/frontend/features/user-section/pages/UserEditProfile.page.jsx +++ b/frontend/features/user-section/pages/UserEditProfile.page.jsx @@ -15,16 +15,12 @@ import { NavLink } from "react-router-dom"; import validator from "validator"; import countries from "/common/assets/countries.json"; import { useAuthContext } from "../../auth-flow/context"; +import { useUserContext } from "../context"; export function UserEditProfile() { - const { - user, - authenticateUser, - updateUserInfo, - errorMessage, - setErrorMessage, - } = useAuthContext(); - + const { authenticateUser, updateUserInfo, errorMessage, setErrorMessage } = + useAuthContext(); + const { user } = useUserContext(); useEffect(() => { authenticateUser(); }, [authenticateUser]); diff --git a/package.json b/package.json index b213a62..ab743de 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "app:dev": "yarn kill-port && concurrently \"yarn --cwd ./backend dev\" \"yarn --cwd frontend dev\"", "app:build": "yarn --cwd ./frontend build && yarn --cwd ./backend build", "app:start": "yarn --cwd ./backend start", - "app:restart": "pm2 restart all", + "app:restart": "pm2 stop all && pm2 flush && pm2 start all", "format": "yarn --cwd ./frontend format && yarn --cwd ./backend format" }, "keywords": [],