ADD Google OAuth login and logout functionality on frontend

This commit is contained in:
2025-08-11 15:16:47 +02:00
parent 662966f138
commit 0c11abc7b9
7 changed files with 113 additions and 44 deletions

View File

@@ -15,6 +15,7 @@
"clsx": "^2.1.1",
"dotenv": "^16.5.0",
"flowbite-react": "^0.11.8",
"js-cookie": "^3.0.5",
"lucide-react": "^0.511.0",
"path": "^0.12.7",
"react": "^19.1.0",
@@ -3358,6 +3359,15 @@
"jiti": "lib/jiti-cli.mjs"
}
},
"node_modules/js-cookie": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
"integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
"license": "MIT",
"engines": {
"node": ">=14"
}
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",

View File

@@ -18,6 +18,7 @@
"clsx": "^2.1.1",
"dotenv": "^16.5.0",
"flowbite-react": "^0.11.8",
"js-cookie": "^3.0.5",
"lucide-react": "^0.511.0",
"path": "^0.12.7",
"react": "^19.1.0",

View File

@@ -7,6 +7,7 @@ import Home from './pages/Home';
import {useEffect} from "react";
import MyClips from './pages/MyClips';
import VideoPlayer from "./pages/VideoPlayer.tsx";
import { GoogleOAuthProvider } from '@react-oauth/google';
function App() {
@@ -15,17 +16,19 @@ function App() {
}, []);
return (
<Router>
<Routes>
<Route element={<MainLayout />}>
<Route path="/" element={<Home />} />
<Route path="/create" element={<ClipUpload />} />
<Route path="/create/:id" element={<ClipEdit />} />
<Route path="/my-clips" element={<MyClips />} />
<Route path="/video/:id" element={<VideoPlayer />} />
</Route>
</Routes>
</Router>
<GoogleOAuthProvider clientId={import.meta.env.VITE_GOOGLE_CLIENT_ID}>
<Router>
<Routes>
<Route element={<MainLayout />}>
<Route path="/" element={<Home />} />
<Route path="/create" element={<ClipUpload />} />
<Route path="/create/:id" element={<ClipEdit />} />
<Route path="/my-clips" element={<MyClips />} />
<Route path="/video/:id" element={<VideoPlayer />} />
</Route>
</Routes>
</Router>
</ GoogleOAuthProvider>
);
}

View File

@@ -1,25 +1,53 @@
import { Menu, X } from 'lucide-react';
import { login, logout } from "../utils/endpoints.ts";
import { Dropdown, DropdownItem } from "./Dropdown.tsx";
import { GoogleLogin } from '@react-oauth/google';
import type { User } from "../utils/types.ts";
import type { CredentialResponse } from '@react-oauth/google';
import MenuButton from "./buttons/MenuButton.tsx";
import clsx from "clsx";
import type {User} from "../utils/types.ts";
import { login } from "../utils/endpoints.ts";
import { Dropdown, DropdownItem } from "./Dropdown.tsx";
import { GoogleOAuthProvider, GoogleLogin } from '@react-oauth/google';
import {useNavigate} from "react-router-dom";
type props = {
sidebarToggled: boolean;
setSidebarToggled: Function;
setSidebarToggled: (toggled: boolean) => void;
user: User | null;
fetchUser: () => void;
className?: string;
}
const Topbar = ({sidebarToggled, setSidebarToggled, user, className}: props) => {
const navigate = useNavigate();
const Topbar = ({
sidebarToggled,
setSidebarToggled,
user,
fetchUser,
className}: props) => {
const handleLogin = (response: CredentialResponse) => {
if (!response.credential) {
console.error("No credential received from Google login.");
return;
}
login(response.credential)
.then(() => {
fetchUser();
})
.catch((error) => {
console.error("Login failed:", error);
});
}
const handleLogout = () => {
// delete token cookie
document.cookie = "token=; Secure; SameSite=None; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
logout()
.then(() => {
fetchUser();
})
.catch((error) => {
console.error("Logout failed:", error);
});
}
return (
@@ -44,20 +72,11 @@ const Topbar = ({sidebarToggled, setSidebarToggled, user, className}: props) =>
</div>
) :
(
<GoogleOAuthProvider
clientId={import.meta.env.VITE_GOOGLE_CLIENT_ID}>
<GoogleLogin
onSuccess={(credentialResponse) => {
if (!credentialResponse.credential) {
console.error("No credential received from Google Login");
return;
}
login(credentialResponse.credential).then(() => {navigate(0)});
}}
/>
</GoogleOAuthProvider>
<GoogleLogin
shape={"pill"}
useOneTap={false}
onSuccess={(credentialResponse) => handleLogin(credentialResponse)} />
)}
</div>
)
}

View File

@@ -10,16 +10,16 @@ const MainLayout = () => {
const [sidebarToggled, setSidebarToggled] = useState(false);
const [user, setUser] = useState<null | User>(null);
useEffect(() => {
const fetchUser = async () => {
try {
const userData = await getUser();
setUser(userData);
} catch (error) {
console.error('Failed to fetch user:', error);
}
};
const fetchUser = async () => {
try {
const userData = await getUser();
setUser(userData);
} catch (error) {
console.error('Failed to fetch user:', error);
}
};
useEffect(() => {
fetchUser();
}, []);
@@ -32,7 +32,8 @@ const MainLayout = () => {
className={"transition-all duration-300"}
sidebarToggled={sidebarToggled}
setSidebarToggled={setSidebarToggled}
user={user}/>
user={user}
fetchUser={fetchUser}/>
<div className="overflow-auto">
<Outlet />
</div>

View File

@@ -30,6 +30,23 @@ const login = async (GoogleToken: string): Promise<string> => {
return result.data.token;
}
const logout = async () => {
const response = await fetch(API_URL + '/api/v1/auth/logout', {
method: 'POST',
credentials: 'include'
});
if (!response.ok) {
throw new Error(`Logout failed: ${response.status}`);
}
const result: APIResponse = await response.json();
if (result.status === "error") {
throw new Error(`Logout failed: ${result.message}`);
}
}
/**
* Uploads a file to the backend.
* @param file - The file to upload.
@@ -233,6 +250,7 @@ const isThumbnailAvailable = async (id: number): Promise<boolean> => {
export {
login,
logout,
uploadFile,
editFile,
processFile,