diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 84131bb..4bb2441 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10,7 +10,9 @@ "dependencies": { "@tailwindcss/vite": "^4.1.7", "clsx": "^2.1.1", + "dotenv": "^16.5.0", "lucide-react": "^0.511.0", + "path": "^0.12.7", "react": "^19.1.0", "react-dom": "^19.1.0", "react-range-slider-input": "^3.2.1", @@ -2242,6 +2244,18 @@ "node": ">=8" } }, + "node_modules/dotenv": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.159", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.159.tgz", @@ -2738,6 +2752,12 @@ "node": ">=0.8.19" } }, + "node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "license": "ISC" + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -3332,6 +3352,16 @@ "node": ">=6" } }, + "node_modules/path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", + "license": "MIT", + "dependencies": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -3409,6 +3439,15 @@ "node": ">= 0.8.0" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -3892,6 +3931,15 @@ "punycode": "^2.1.0" } }, + "node_modules/util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "license": "MIT", + "dependencies": { + "inherits": "2.0.3" + } + }, "node_modules/vite": { "version": "6.3.5", "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", diff --git a/frontend/package.json b/frontend/package.json index d1a89d6..d62b39a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,7 +13,9 @@ "dependencies": { "@tailwindcss/vite": "^4.1.7", "clsx": "^2.1.1", + "dotenv": "^16.5.0", "lucide-react": "^0.511.0", + "path": "^0.12.7", "react": "^19.1.0", "react-dom": "^19.1.0", "react-range-slider-input": "^3.2.1", diff --git a/frontend/src/components/Topbar.tsx b/frontend/src/components/Topbar.tsx index 8567637..9d1c19e 100644 --- a/frontend/src/components/Topbar.tsx +++ b/frontend/src/components/Topbar.tsx @@ -9,13 +9,18 @@ type props = { } const Topbar = ({sidebarToggled, setSidebarToggled, className}: props) => { + const apiUrl = import.meta.env.VITE_API_URL; + const redirectUri = encodeURIComponent(window.location.href); + const loginUrl = `${apiUrl}/oauth2/authorization/google?redirect_uri=${redirectUri}`; + return (
setSidebarToggled(!sidebarToggled)}> {sidebarToggled ? : } - + globalThis.location.href = loginUrl}> Login
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index e75ff99..c311e63 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -5,7 +5,15 @@ import tailwindcss from '@tailwindcss/vite' // https://vite.dev/config/ export default defineConfig({ plugins: [react(), tailwindcss()], + preview: { + port: 5173, + strictPort: true, + }, server: { + port: 5173, + strictPort: true, + host: true, + origin: "http://0.0.0.0:5173", proxy: { '/api/v1': { target: 'http://localhost:8080', diff --git a/src/main/java/com/ddf/vodsystem/security/CustomOAuth2UserService.java b/src/main/java/com/ddf/vodsystem/configuration/CustomOAuth2UserService.java similarity index 80% rename from src/main/java/com/ddf/vodsystem/security/CustomOAuth2UserService.java rename to src/main/java/com/ddf/vodsystem/configuration/CustomOAuth2UserService.java index 0fa7b13..1c788d9 100644 --- a/src/main/java/com/ddf/vodsystem/security/CustomOAuth2UserService.java +++ b/src/main/java/com/ddf/vodsystem/configuration/CustomOAuth2UserService.java @@ -1,4 +1,4 @@ -package com.ddf.vodsystem.security; +package com.ddf.vodsystem.configuration; import com.ddf.vodsystem.entities.User; import com.ddf.vodsystem.repositories.UserRepository; @@ -8,6 +8,9 @@ import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.stereotype.Service; +import java.time.LocalDateTime; +import java.util.Optional; + @Service public class CustomOAuth2UserService extends DefaultOAuth2UserService { @@ -26,15 +29,18 @@ public class CustomOAuth2UserService extends DefaultOAuth2UserService { String name = oAuth2User.getAttribute("name"); String googleId = oAuth2User.getAttribute("sub"); - userRepository.findByGoogleId(googleId).orElseGet(() -> { + Optional existingUser = userRepository.findByGoogleId(googleId); + + if (existingUser.isEmpty()) { User user = new User(); user.setEmail(email); user.setName(name); user.setGoogleId(googleId); user.setUsername(email); user.setRole(0); - return userRepository.save(user); - }); + user.setCreatedAt(LocalDateTime.now()); + userRepository.save(user); + } return oAuth2User; } diff --git a/src/main/java/com/ddf/vodsystem/security/SecurityConfig.java b/src/main/java/com/ddf/vodsystem/configuration/SecurityConfig.java similarity index 81% rename from src/main/java/com/ddf/vodsystem/security/SecurityConfig.java rename to src/main/java/com/ddf/vodsystem/configuration/SecurityConfig.java index 469db63..6e3bd10 100644 --- a/src/main/java/com/ddf/vodsystem/security/SecurityConfig.java +++ b/src/main/java/com/ddf/vodsystem/configuration/SecurityConfig.java @@ -1,5 +1,6 @@ -package com.ddf.vodsystem.security; +package com.ddf.vodsystem.configuration; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -7,7 +8,6 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; -import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; @Configuration @EnableWebSecurity @@ -15,6 +15,9 @@ public class SecurityConfig { private final CustomOAuth2UserService customOAuth2UserService; + @Value("${frontend.url}") + private String frontendUrl; + public SecurityConfig(CustomOAuth2UserService customOAuth2UserService) { this.customOAuth2UserService = customOAuth2UserService; } @@ -24,6 +27,7 @@ public class SecurityConfig { http .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(auth -> auth + .requestMatchers("/api/v1/auth/login", "/api/v1/auth/user").permitAll() .anyRequest().authenticated() ) .oauth2Login(oauth2 -> oauth2 @@ -39,7 +43,7 @@ public class SecurityConfig { @Bean public AuthenticationSuccessHandler successHandler() { - return new SimpleUrlAuthenticationSuccessHandler("/api/v1/auth/user"); + return (request, response, authentication) -> response.sendRedirect(frontendUrl); } } diff --git a/src/main/java/com/ddf/vodsystem/controllers/AuthController.java b/src/main/java/com/ddf/vodsystem/controllers/AuthController.java index 311d506..56a4e00 100644 --- a/src/main/java/com/ddf/vodsystem/controllers/AuthController.java +++ b/src/main/java/com/ddf/vodsystem/controllers/AuthController.java @@ -16,9 +16,4 @@ public class AuthController { public Map user(@AuthenticationPrincipal OAuth2User principal) { return principal.getAttributes(); } - - @GetMapping("/login") - public String login() { - return "login"; - } } diff --git a/src/main/resources/application-local.properties b/src/main/resources/application-local.properties index 8a5dd26..99edc4e 100644 --- a/src/main/resources/application-local.properties +++ b/src/main/resources/application-local.properties @@ -13,3 +13,6 @@ spring.sql.init.data-locations=classpath:db/data.sql spring.security.oauth2.client.registration.google.client-id=${GOOGLE_CLIENT_ID} spring.security.oauth2.client.registration.google.client-secret=${GOOGLE_CLIENT_SECRET} spring.security.oauth2.client.registration.google.scope=profile,email + +# Frontend +frontend.url=http://localhost:5173