Changeset b248810
- Timestamp:
- 06/10/24 22:34:32 (5 months ago)
- Branches:
- main
- Children:
- befb988
- Parents:
- 28b3398
- Files:
-
- 9 added
- 22 edited
Legend:
- Unmodified
- Added
- Removed
-
jobvista-backend/src/main/java/mk/ukim/finki/predmeti/internettehnologii/jobvistabackend/JobvistaBackendApplication.java
r28b3398 rb248810 26 26 admin.setRole(Role.ROLE_ADMIN); 27 27 admin.setEmail("admin@admin.com"); 28 admin.setHasAccess(true); 28 29 // admin.setName("admin"); 29 30 // admin.setSurname("admin"); -
jobvista-backend/src/main/java/mk/ukim/finki/predmeti/internettehnologii/jobvistabackend/config/SecurityConfiguration.java
r28b3398 rb248810 32 32 // TO DO: FIX PERMISSIONS 33 33 .requestMatchers("/api/job-advertisements/**","/api/job-advertisements/view/**","/api/recruiter/info/**", 34 "/api/job-advertisements/apply/**","/api/auth/**", "/api/resume/**", "/api/my-applications/**", "/api/applications/{id}/update" ).permitAll()34 "/api/job-advertisements/apply/**","/api/auth/**", "/api/resume/**", "/api/my-applications/**", "/api/applications/{id}/update", "/api/admin/**").permitAll() 35 35 //.requestMatchers("/api/job-advertisements/**").hasAnyAuthority(Role.ROLE_RECRUITER.name()) 36 36 .anyRequest().authenticated()) -
jobvista-backend/src/main/java/mk/ukim/finki/predmeti/internettehnologii/jobvistabackend/controllers/AdminController.java
r28b3398 rb248810 2 2 3 3 import lombok.RequiredArgsConstructor; 4 import org.springframework.web.bind.annotation.RequestMapping; 5 import org.springframework.web.bind.annotation.RestController; 4 import mk.ukim.finki.predmeti.internettehnologii.jobvistabackend.models.users.DTO.RecruiterDetailsDTO; 5 import mk.ukim.finki.predmeti.internettehnologii.jobvistabackend.service.intef.AdminService; 6 import org.springframework.http.HttpStatus; 7 import org.springframework.http.ResponseEntity; 8 import org.springframework.web.bind.annotation.*; 9 10 import java.util.List; 6 11 7 12 @RestController 8 13 @RequestMapping("/api/admin") 9 14 @RequiredArgsConstructor 15 @CrossOrigin(origins = "*") 10 16 public class AdminController { 11 17 18 private final AdminService adminService; 19 20 @PostMapping("/change-access/{recruiter_id}") 21 public ResponseEntity<?> changeAccess(@PathVariable("recruiter_id") Long recruiterId, @RequestBody boolean access) { 22 RecruiterDetailsDTO recruiterDetailsDTO = adminService.changeAccess(recruiterId, access); 23 return new ResponseEntity<>(recruiterDetailsDTO, HttpStatus.OK); 24 } 25 26 @GetMapping("/recruiters") 27 public ResponseEntity<?> findAllRecruiters() { 28 List<RecruiterDetailsDTO> recruiterDetailsDTOList = adminService.findAllRecruiters(); 29 return new ResponseEntity<>(recruiterDetailsDTOList, HttpStatus.OK); 30 } 12 31 } -
jobvista-backend/src/main/java/mk/ukim/finki/predmeti/internettehnologii/jobvistabackend/models/users/DTO/JwtAuthResponse.java
r28b3398 rb248810 14 14 private String name; 15 15 private String role; 16 private boolean hasAccess; 16 17 private String token; 17 18 private String refreshToken; -
jobvista-backend/src/main/java/mk/ukim/finki/predmeti/internettehnologii/jobvistabackend/models/users/DTO/RecruiterDetailsDTO.java
r28b3398 rb248810 5 5 import lombok.NoArgsConstructor; 6 6 7 import java.time.LocalDateTime; 8 7 9 @Data 8 10 @AllArgsConstructor 9 11 @NoArgsConstructor 10 12 public class RecruiterDetailsDTO { 13 private Long id; 11 14 private String email; 12 15 private String companyName; 13 16 private String companyDescription; 14 17 private String phoneNumber; 18 private boolean hasAccess; 19 private LocalDateTime registeredOn; 15 20 } -
jobvista-backend/src/main/java/mk/ukim/finki/predmeti/internettehnologii/jobvistabackend/models/users/User.java
r28b3398 rb248810 11 11 import org.springframework.security.core.userdetails.UserDetails; 12 12 13 import java.time.LocalDateTime; 13 14 import java.util.Collection; 14 15 import java.util.List; … … 32 33 @Enumerated(EnumType.STRING) 33 34 protected Role role; 35 36 @Column(name = "has_access") 37 protected boolean hasAccess; 38 39 protected LocalDateTime registeredOn; 34 40 35 41 -
jobvista-backend/src/main/java/mk/ukim/finki/predmeti/internettehnologii/jobvistabackend/models/users/mappers/RecruiterMapper.java
r28b3398 rb248810 18 18 public static RecruiterDetailsDTO mapToRecruiterDetailsDTO(Recruiter recruiter) { 19 19 return new RecruiterDetailsDTO( 20 recruiter.getId(), 20 21 recruiter.getEmail(), 21 22 recruiter.getCompanyName(), 22 23 recruiter.getCompanyDescription(), 23 recruiter.getPhoneNumber() 24 recruiter.getPhoneNumber(), 25 recruiter.isHasAccess(), 26 recruiter.getRegisteredOn() 24 27 ); 25 28 } -
jobvista-backend/src/main/java/mk/ukim/finki/predmeti/internettehnologii/jobvistabackend/service/impl/AuthServiceImpl.java
r28b3398 rb248810 18 18 import org.springframework.stereotype.Service; 19 19 20 import java.time.LocalDateTime; 20 21 import java.util.HashMap; 21 22 … … 34 35 public User signUpJobSeeker(JobSeeker jobSeeker) { 35 36 jobSeeker.setPassword(passwordEncoder.encode(jobSeeker.getPassword())); 37 jobSeeker.setHasAccess(true); 38 jobSeeker.setRegisteredOn(LocalDateTime.now()); 36 39 return jobSeekerRepository.save(jobSeeker); 37 40 } … … 39 42 public User signUpRecruiter(Recruiter recruiter) { 40 43 recruiter.setPassword(passwordEncoder.encode(recruiter.getPassword())); 44 recruiter.setHasAccess(false); 45 recruiter.setRegisteredOn(LocalDateTime.now()); 41 46 return recruiterRepository.save(recruiter); 42 47 } … … 49 54 String refreshJwt = jwtService.generateRefreshToken(new HashMap<>(), user); 50 55 51 return new JwtAuthResponse(user.getId(), user.getEmail(), user.getName(), user.getRole().name(), jwt, refreshJwt);56 return new JwtAuthResponse(user.getId(), user.getEmail(), user.getName(), user.getRole().name(), user.isHasAccess(), jwt, refreshJwt); 52 57 } 53 58 … … 58 63 String jwt = jwtService.generateToken(user); 59 64 60 return new JwtAuthResponse(user.getId(), user.getEmail(), user.getName(), user.getRole().name(), jwt, refreshTokenRequest.getToken());65 return new JwtAuthResponse(user.getId(), user.getEmail(), user.getName(), user.getRole().name(), user.isHasAccess(), jwt, refreshTokenRequest.getToken()); 61 66 } 62 67 return null; -
jobvista-backend/src/main/java/mk/ukim/finki/predmeti/internettehnologii/jobvistabackend/service/impl/JwtServiceImpl.java
r28b3398 rb248810 6 6 import io.jsonwebtoken.io.Decoders; 7 7 import io.jsonwebtoken.security.Keys; 8 import mk.ukim.finki.predmeti.internettehnologii.jobvistabackend.models.users.User; 8 9 import mk.ukim.finki.predmeti.internettehnologii.jobvistabackend.service.intef.JwtService; 9 10 import org.springframework.security.core.userdetails.UserDetails; … … 19 20 private final static String SECRET_KEY = "7191b1d33668d4a2316a02f9a40798b77bccd22173bd882c93a0a916a5e921d1"; 20 21 21 public String generateToken(UserDetails userDetails) { 22 return Jwts.builder().setSubject(userDetails.getUsername()) 22 public String generateToken(User user) { 23 return Jwts.builder().setSubject(user.getUsername()) 24 .claim("name", user.getName()) 25 .claim("role", user.getRole()) 26 .claim("access", user.isHasAccess()) 23 27 .setIssuedAt(new Date()) 24 28 .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24)) -
jobvista-backend/src/main/java/mk/ukim/finki/predmeti/internettehnologii/jobvistabackend/service/intef/JwtService.java
r28b3398 rb248810 1 1 package mk.ukim.finki.predmeti.internettehnologii.jobvistabackend.service.intef; 2 2 3 import mk.ukim.finki.predmeti.internettehnologii.jobvistabackend.models.users.User; 3 4 import org.springframework.security.core.userdetails.UserDetails; 4 5 … … 6 7 7 8 public interface JwtService { 8 String generateToken(User Details userDetails);9 String generateToken(User user); 9 10 String generateRefreshToken(Map<String, Object> extraClaims, UserDetails userDetails); 10 11 String extractUsername(String token); -
jobvista-frontend/package-lock.json
r28b3398 rb248810 19 19 "axios": "^1.6.8", 20 20 "formik": "^2.4.6", 21 "jwt-decode": "^4.0.0", 21 22 "primereact": "^10.6.6", 22 23 "quill": "^2.0.2", … … 12963 12964 } 12964 12965 }, 12966 "node_modules/jwt-decode": { 12967 "version": "4.0.0", 12968 "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", 12969 "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", 12970 "engines": { 12971 "node": ">=18" 12972 } 12973 }, 12965 12974 "node_modules/keyv": { 12966 12975 "version": "4.5.4", -
jobvista-frontend/package.json
r28b3398 rb248810 14 14 "axios": "^1.6.8", 15 15 "formik": "^2.4.6", 16 "jwt-decode": "^4.0.0", 16 17 "primereact": "^10.6.6", 17 18 "quill": "^2.0.2", -
jobvista-frontend/src/App.js
r28b3398 rb248810 1 1 import logo from './logo.svg'; 2 2 import './App.css'; 3 import {useDispatch } from "react-redux";3 import {useDispatch, useSelector} from "react-redux"; 4 4 import {BrowserRouter} from "react-router-dom"; 5 5 import {Header} from "./views/static/Header"; 6 6 import {RoutesConfig} from "./auth/RoutesConfig"; 7 import {useEffect } from "react";7 import {useEffect, useState} from "react"; 8 8 import {AuthActions} from "./redux/actions/authActions"; 9 9 import {AUTH_TOKEN} from "./axios/axiosInstance"; 10 import {jwtDecode} from "jwt-decode"; 11 import {NoAccess} from "./views/static/NoAccess"; 10 12 11 13 function App() { … … 16 18 }, [dispatch]) 17 19 20 const [user, setUser] = useState(null); 21 const [loading, setLoading] = useState(true); 22 const auth = useSelector(state => state.auth); 23 24 useEffect(() => { 25 const token = localStorage.getItem(AUTH_TOKEN); 26 if (token !== null) { 27 try { 28 const decodedToken = jwtDecode(token); 29 setUser({ 30 name: decodedToken.name, 31 role: decodedToken.role, 32 hasAccess: auth.currentUser.access, 33 }); 34 setLoading(false); 35 } catch (error) { 36 console.error('Failed to decode token', error); 37 setLoading(false); 38 } 39 } else { 40 setLoading(false); 41 } 42 }, [auth]); 43 44 if (loading) { 45 return <NoAccess />; // Replace LoadingSpinner with your loading indicator component 46 } 47 18 48 return ( 19 49 <div className="App"> 20 50 <BrowserRouter> 21 <Header/> 22 <RoutesConfig/> 51 {user === null ? ( 52 <> 53 <Header /> 54 <RoutesConfig /> 55 </> 56 ) : user.hasAccess ? ( 57 <> 58 <Header /> 59 <RoutesConfig /> 60 </> 61 ) : ( 62 <NoAccess user={user}/> 63 )} 64 65 23 66 </BrowserRouter> 24 67 </div> -
jobvista-frontend/src/auth/RoutesConfig.js
r28b3398 rb248810 9 9 import {ApplicationsByJobAd} from "../views/applications/ApplicationsByJobAd"; 10 10 import {ApplicationsByJobSeeker} from "../views/applications/ApplicationsByJobSeeker"; 11 import {useEffect, useState} from "react"; 12 import {AUTH_TOKEN} from "../axios/axiosInstance"; 13 import {jwtDecode} from "jwt-decode"; 14 import {useSelector} from "react-redux"; 15 import {AdminPanel} from "../views/admin_panel/AdminPanel"; 11 16 export const RoutesConfig = () => { 12 17 … … 21 26 <Route path="/job-advertisements/:id" element={<JobAdDetails/>}></Route> 22 27 <Route path="/my-job-advertisements/:advertisement_id/applications" element={<ApplicationsByJobAd/>}></Route> 28 <Route path="/admin-panel" element={<AdminPanel/>}></Route> 23 29 </Routes> 24 30 ) -
jobvista-frontend/src/redux/actionTypes.js
r28b3398 rb248810 18 18 export const DOWNLOAD_RESUME = "DOWNLOAD_RESUME" 19 19 20 export const FETCH_RECRUITERS = "FETCH_RECRUITERS" 21 export const CHANGE_ACCESS = "CHANGE_ACCESS" 20 22 23 -
jobvista-frontend/src/redux/actions/authActions.js
r28b3398 rb248810 48 48 email: response.email, 49 49 name: response.name, 50 role: response.role 50 role: response.role, 51 access: response.hasAccess, 51 52 }; 52 53 dispatch({ -
jobvista-frontend/src/redux/reducers/jobAdvertisementReducer.js
r28b3398 rb248810 5 5 FETCH_JOB_ADVERTISEMENTS_BY_RECRUITER, FILTER_JOB_ADVERTISEMENTS, FILTER_JOB_ADVERTISEMENTS_BY_RECRUITER 6 6 } from "../actionTypes"; 7 import {sortElementsBy DateCreated} from "../../utils/utils";7 import {sortElementsBy} from "../../utils/utils"; 8 8 import {useSelector} from "react-redux"; 9 9 … … 22 22 return { 23 23 ...state, 24 jobAdvertisements: sortElementsBy DateCreated([...state.jobAdvertisements, action.jobAdvertisement]),25 jobAdvertisementsByRecruiter: sortElementsBy DateCreated([...state.jobAdvertisementsByRecruiter, action.jobAdvertisement])24 jobAdvertisements: sortElementsBy([...state.jobAdvertisements, action.jobAdvertisement]), 25 jobAdvertisementsByRecruiter: sortElementsBy([...state.jobAdvertisementsByRecruiter, action.jobAdvertisement], "postedOn") 26 26 } 27 27 case EDIT_JOB_ADVERTISEMENT: … … 30 30 31 31 return { 32 jobAdvertisements: sortElementsBy DateCreated([...jobAdvertisements, action.jobAdvertisement]),33 jobAdvertisementsByRecruiter: sortElementsBy DateCreated([...jobAdvertisementsByRecruiter, action.jobAdvertisement])32 jobAdvertisements: sortElementsBy([...jobAdvertisements, action.jobAdvertisement], "postedOn"), 33 jobAdvertisementsByRecruiter: sortElementsBy([...jobAdvertisementsByRecruiter, action.jobAdvertisement], "postedOn") 34 34 } 35 35 case DELETE_JOB_ADVERTISEMENT: … … 38 38 39 39 return { 40 jobAdvertisements: sortElementsBy DateCreated([...jobAdvertisements]),41 jobAdvertisementsByRecruiter: sortElementsBy DateCreated([...jobAdvertisementsByRecruiter])40 jobAdvertisements: sortElementsBy([...jobAdvertisements], "postedOn"), 41 jobAdvertisementsByRecruiter: sortElementsBy([...jobAdvertisementsByRecruiter], "postedOn") 42 42 } 43 43 … … 45 45 return { 46 46 ...state, 47 jobAdvertisements: sortElementsBy DateCreated(action.jobAdvertisements)47 jobAdvertisements: sortElementsBy(action.jobAdvertisements, "postedOn") 48 48 } 49 49 … … 52 52 return { 53 53 ...state, 54 jobAdvertisementsByRecruiter: sortElementsBy DateCreated(action.jobAdvertisementsByRecruiter)54 jobAdvertisementsByRecruiter: sortElementsBy(action.jobAdvertisementsByRecruiter, "postedOn") 55 55 } 56 56 -
jobvista-frontend/src/redux/store.js
r28b3398 rb248810 4 4 import jobAdReducer from "./reducers/jobAdvertisementReducer"; 5 5 import applicationReducer from "./reducers/applicationReducer" 6 import adminReducer from "./reducers/adminReducer" 7 import {AdminActions} from "./actions/adminActions"; 6 8 7 9 // const rootReducer = combineReducers({ … … 18 20 auth: authReducer, 19 21 jobAd: jobAdReducer, 20 appl: applicationReducer 22 appl: applicationReducer, 23 admin: adminReducer 21 24 }, 22 25 }); -
jobvista-frontend/src/utils/utils.js
r28b3398 rb248810 1 1 2 2 3 export const sortElementsBy DateCreated = (array) => {3 export const sortElementsBy = (array, column) => { 4 4 return array.slice().sort((a, b) => { 5 return new Date(b .postedOn).getTime() - new Date(a.postedOn).getTime()5 return new Date(b[column]).getTime() - new Date(a[column]).getTime() 6 6 }); 7 7 } … … 9 9 export const sortElementsBySubmissionDate = (array) => { 10 10 return array.slice().sort((a, b) => { 11 return new Date(b .postedOn).getTime() - new Date(a.postedOn).getTime()11 return new Date(b).getTime() - new Date(a.postedOn).getTime() 12 12 }); 13 13 } -
jobvista-frontend/src/views/dashboard/Dashboard.js
r28b3398 rb248810 4 4 import {useEffect, useState} from "react"; 5 5 import {JobAdvertisementActions} from "../../redux/actions/jobAdvertisementActions"; 6 import {formatRelativeTime, sortElementsBy DateCreated} from "../../utils/utils";6 import {formatRelativeTime, sortElementsBy} from "../../utils/utils"; 7 7 import {dataRangeOptions, industryOptions, industryOptionsFilter, sortOptions} from "../selectOptions"; 8 8 import Select from "react-select"; … … 10 10 import {Link} from "react-router-dom"; 11 11 import JobType from "../../enumerations/JobType"; 12 import {AUTH_TOKEN} from "../../axios/axiosInstance"; 13 import {jwtDecode} from "jwt-decode"; 12 14 13 export const Dashboard = ( ) => {15 export const Dashboard = (props) => { 14 16 15 17 const dispatch = useDispatch(); … … 17 19 const [jobAdvertisements, setJobAdvertisements] = useState([]); 18 20 let jobAdvertisementsState = useSelector(state => state.jobAd.jobAdvertisements) 19 const auth = useSelector(state => state.auth .currentUser);21 const auth = useSelector(state => state.auth); 20 22 21 const [role, setRole] = useState("");23 // const [role, setRole] = useState(""); 22 24 const [selectedSortOrder, setSelectedSortOrder] = useState("date_newest"); 23 25 const [selectedIndustry, setSelectedIndustry] = useState("all"); … … 25 27 const [dispatched, setDispatched] = useState(false) 26 28 27 useEffect(() => { 28 if (auth) { 29 setRole(auth.role); 30 } 31 }, [auth]); 29 // const [user, setUser] = useState(null); 30 // 31 // useEffect(() => { 32 // const token = localStorage.getItem(AUTH_TOKEN); 33 // if (token!=null) { 34 // try { 35 // const decodedToken = jwtDecode(token); 36 // setUser({ 37 // name: decodedToken.name, 38 // role: decodedToken.role, 39 // hasAccess: decodedToken.access, 40 // }); 41 // } catch (error) { 42 // console.error('Failed to decode token', error); 43 // } 44 // } 45 // console.log(user) 46 // }, [auth]); 47 48 // useEffect(() => { 49 // if (auth) { 50 // setRole(auth.role); 51 // } 52 // console.log(props) 53 // }, [auth]); 32 54 33 55 useEffect(() => { … … 35 57 dispatch(JobAdvertisementActions.fetchJobAdvertisements((success, response) => { 36 58 if (success && response.data.length > 0) { 37 setJobAdvertisements(sortElementsBy DateCreated(response.data))59 setJobAdvertisements(sortElementsBy(response.data)) 38 60 } 39 61 setDispatched(true) … … 62 84 63 85 return ( 86 64 87 <div className="container"> 65 88 <div className="head-dashboard-box"> -
jobvista-frontend/src/views/job_advertisements/JobAdvertisements.js
r28b3398 rb248810 5 5 import {useEffect, useState} from "react"; 6 6 import {JobAdvertisementActions} from "../../redux/actions/jobAdvertisementActions"; 7 import {formatRelativeTime, sortElementsBy DateCreated} from "../../utils/utils";7 import {formatRelativeTime, sortElementsBy} from "../../utils/utils"; 8 8 import {dataRangeOptions, industryOptions, industryOptionsFilter, sortOptions} from "../selectOptions"; 9 9 import Select from "react-select"; … … 37 37 dispatch(JobAdvertisementActions.fetchJobAdvertisementsByRecruiter((success, response) => { 38 38 if (success && response.data.length > 0) { 39 setJobAdvertisementsByRecruiter(sortElementsBy DateCreated(response.data))39 setJobAdvertisementsByRecruiter(sortElementsBy(response.data)) 40 40 } 41 41 console.log("Fetch job advertisements by recruiter GET") -
jobvista-frontend/src/views/static/Header.js
r28b3398 rb248810 1 1 import {Link, NavLink} from "react-router-dom"; 2 2 import "./Header.css" 3 import { jwtDecode } from "jwt-decode"; 3 4 import {useDispatch, useSelector} from 'react-redux'; 4 5 import {useEffect, useState} from "react"; … … 6 7 import Roles from "../../enumerations/Roles"; 7 8 import {useNavigate} from "react-router"; 9 import {AUTH_TOKEN} from "../../axios/axiosInstance"; 8 10 9 11 export const Header = (props) => { … … 16 18 const [username, setUsername] = useState(""); 17 19 20 const [user, setUser] = useState(""); 21 18 22 const signOut = () => { 19 23 dispatch(AuthActions.signOut()); 20 24 window.location = "/"; 21 25 } 26 27 useEffect(() => { 28 const token = localStorage.getItem(AUTH_TOKEN); 29 if (token!=null) { 30 try { 31 const decodedToken = jwtDecode(token); 32 setUser({ 33 name: decodedToken.name, 34 role: decodedToken.role, 35 hasAccess: decodedToken.hasAccess, 36 }); 37 } catch (error) { 38 console.error('Failed to decode token', error); 39 } 40 } 41 }, [auth]); 22 42 23 43 useEffect(() => { … … 49 69 </> 50 70 } 71 {role===Roles.ADMIN && 72 <> 73 <NavLink to="/admin-panel" className="nav-item nav-link">Admin Panel</NavLink> 74 </> 75 76 } 51 77 <NavLink to="/about" className="nav-item nav-link">About</NavLink> 52 78 <NavLink to="/contact" className="nav-item nav-link">Support</NavLink> … … 57 83 <img src="/images/user.png" width="45" height="45"/> 58 84 <div className="auth-box"> 59 <p className="user"><b>{username}</b></p> 60 <p className="role">{role==Roles.RECRUITER ? "Recruiter" : "Job Seeker"}</p> 85 <p className="user"><b>{user.name}</b></p> 86 {user.role==Roles.RECRUITER && <p className="role">Recruiter</p>} 87 {user.role==Roles.JOBSEEKER && <p className="role">Job Seeker</p>} 88 {user.role==Roles.ADMIN && <p className="role">Admin</p>} 89 {/*<p className="role">{user.role==Roles.RECRUITER ? "Recruiter" : "Job Seeker"}</p>*/} 61 90 </div> 62 91
Note:
See TracChangeset
for help on using the changeset viewer.