diff --git a/frontend/src/lib/@api/admin.ts b/frontend/src/lib/@api/admin.ts index 860804d..6ae2c9b 100644 --- a/frontend/src/lib/@api/admin.ts +++ b/frontend/src/lib/@api/admin.ts @@ -1,13 +1,17 @@ -import type { AdminLogin } from "$lib/stores/admin"; -import type { CandidateData, CandidatePreview, CreateCandidate, CreateCandidateLogin } from "$lib/stores/candidate"; -import axios from "axios"; -import { API_URL, errorHandler, type Fetch } from "."; - +import type { AdminLogin } from '$lib/stores/admin'; +import type { + CandidateData, + CandidatePreview, + CreateCandidate, + CreateCandidateLogin +} from '$lib/stores/candidate'; +import axios from 'axios'; +import { API_URL, errorHandler, type Fetch } from '.'; // Login as admin /admin/login export const apiLogin = async (data: AdminLogin): Promise => { try { - const res = await axios.post(API_URL + '/admin/login', data, { withCredentials: true }); + await axios.post(API_URL + '/admin/login', data, { withCredentials: true }); return data.adminId; } catch (e: any) { throw errorHandler(e, 'Login failed'); @@ -17,97 +21,92 @@ export const apiLogin = async (data: AdminLogin): Promise => { // Create new candidate /admin/create // return created candidate's applicationId, personalIdNumber and password export const apiCreateCandidate = async (data: CreateCandidate): Promise => { - try { - const res = await axios.post(API_URL + '/admin/create', data, { withCredentials: true }); - return res.data; - } catch (e: any) { - throw errorHandler(e, 'Candidate creation failed'); - } -} + try { + const res = await axios.post(API_URL + '/admin/create', data, { withCredentials: true }); + return res.data; + } catch (e: any) { + throw errorHandler(e, 'Candidate creation failed'); + } +}; // Reset candidate password /admin/candidate/{id}/reset_password export const apiResetCandidatePassword = async (id: number): Promise => { - try { - const res = await axios.post(API_URL + '/admin/candidate/' + id + '/reset_password', - {}, - { withCredentials: true } - ); - return res.data; - } catch (e: any) { - throw errorHandler(e, 'Candidate creation failed'); - } -} + try { + const res = await axios.post( + API_URL + '/admin/candidate/' + id + '/reset_password', + {}, + { withCredentials: true } + ); + return res.data; + } catch (e: any) { + throw errorHandler(e, 'Candidate creation failed'); + } +}; export const apiGetCandidatePortfolio = async (id: number): Promise => { try { const res = await fetch(API_URL + '/admin/candidate/' + id + '/portfolio', { method: 'GET', - credentials: 'include', + credentials: 'include' }); return await res.blob(); } catch (e: any) { throw errorHandler(e, 'Candidate portfolio failed'); } -} +}; // SSR compatible // Logout as admin /admin/logout export const apiLogout = async (fetchSsr?: Fetch) => { + const apiFetch = fetchSsr || fetch; + try { - if (fetchSsr) { - const res = await fetchSsr(API_URL + '/admin/logout', { - method: 'POST', - credentials: 'include' - }); - return await res.text(); - } - const res = await axios.post(API_URL + '/admin/logout', { withCredentials: true }); - return res.data; - } catch (e: any) { + const res = await apiFetch(API_URL + '/admin/logout', { + method: 'POST', + credentials: 'include' + }); + return await res.text(); + } catch (e) { throw errorHandler(e, 'Logout failed'); } }; // SSR compatible // List all candidates /admin/list/candidates -export const apiListCandidates = async (fetchSsr?: Fetch, field?: string): Promise<[CandidatePreview]> => { +export const apiListCandidates = async ( + fetchSsr?: Fetch, + field?: string +): Promise> => { + const apiFetch = fetchSsr || fetch; const params = new URLSearchParams(); if (field) { params.append('field', field); } try { - if (fetchSsr) { - const res = await fetchSsr(API_URL + '/admin/list/candidates?' + params.toString(), { - method: 'GET', - credentials: 'include' - }); - return await res.json(); - } - const res = await axios.get(API_URL + '/admin/list/candidates?' + params.toString(), { - withCredentials: true + const res = await apiFetch(API_URL + '/admin/list/candidates?' + params.toString(), { + method: 'GET', + credentials: 'include' }); - return res.data; - } catch (e: any) { - throw errorHandler(e, 'Failed to fetch submission progress'); + if (res.status != 200) { + throw Error(await res.text()); + } + return await res.json(); + } catch (e) { + throw errorHandler(e, 'List candidates failed'); } }; // SSR compatible // Get candidate data /admin/candidate/{id} export const apiFetchCandidate = async (id: number, fetchSsr?: Fetch): Promise => { - try { - if (fetchSsr) { - const res = await fetchSsr(API_URL + '/admin/candidate/' + id, { - method: 'GET', - credentials: 'include' - }); - return await res.json(); - } - const res = await axios.get(API_URL + '/admin/candidate/' + id, { - withCredentials: true + const apiFetch = fetchSsr || fetch; + try { + const res = await apiFetch(API_URL + '/admin/candidate/' + id, { + method: 'GET', + credentials: 'include' }); - return res.data; - } catch (e: any) { + return await res.json(); + } catch (e) { throw errorHandler(e, 'Failed to fetch candidate data'); } -} \ No newline at end of file +}; diff --git a/frontend/src/lib/@api/candidate.ts b/frontend/src/lib/@api/candidate.ts index 73fbfc5..eb1ae20 100644 --- a/frontend/src/lib/@api/candidate.ts +++ b/frontend/src/lib/@api/candidate.ts @@ -6,64 +6,66 @@ import DOMPurify from 'isomorphic-dompurify'; // SSR Compatible export const apiLogout = async (fetchSsr?: Fetch) => { + const apiFetch = fetchSsr || fetch; try { - fetchSsr - ? await fetchSsr(API_URL + '/candidate/logout', { method: 'POST', credentials: 'include' }) - : await axios.post(API_URL + '/candidate/logout', { withCredentials: true }); - } catch (e: any) { + const res = await apiFetch(API_URL + '/candidate/logout', { + method: 'POST', + credentials: 'include' + }); + return await res.json(); + } catch (e) { throw errorHandler(e, 'Logout failed'); } }; // SSR Compatible export const apiFetchDetails = async (fetchSsr?: Fetch): Promise => { + const apiFetch = fetchSsr || fetch; try { - if (fetchSsr) { - const res = await fetchSsr(API_URL + '/candidate/details', { - method: 'GET', - credentials: 'include' - }); - if (res.status != 200) { - throw new Error(await res.text()); - } - return await res.json(); + const res = await apiFetch(API_URL + '/candidate/details', { + method: 'GET', + credentials: 'include' + }); + if (res.status != 200) { + throw new Error(await res.text()); } - const res = await axios.get(API_URL + '/candidate/details', { withCredentials: true }); - return res.data; - } catch (e: any) { - console.log(e); - throw errorHandler(e, 'Failed to fill details'); + return await res.json(); + } catch (e) { + throw errorHandler(e, 'Fetch details failed'); } }; // SSR Compatible export const apiFetchSubmissionProgress = async (fetchSsr?: Fetch): Promise => { + const apiFetch = fetchSsr || fetch; try { - if (fetchSsr) { - const res = await fetchSsr(API_URL + '/candidate/portfolio/submission_progress', { - method: 'GET', - credentials: 'include' - }); - if (res.status != 200) { - throw Error(await res.text()); - } - return await res.json(); - } - const res = await axios.get(API_URL + '/candidate/portfolio/submission_progress', { - withCredentials: true + const res = await apiFetch(API_URL + '/candidate/portfolio/submission_progress', { + method: 'GET', + credentials: 'include' }); - return res.data; - } catch (e: any) { + if (res.status != 200) { + throw Error(await res.text()); + } + return await res.json(); + } catch (e) { throw errorHandler(e, 'Failed to fetch submission progress'); } }; -export const apiWhoami = async (): Promise => { +export const apiWhoami = async (fetchSsr?: Fetch): Promise => { + const apiFetch = fetchSsr || fetch; try { - const res = await axios.get(`${API_URL}/whoami`); - return res.data; - } catch (e: any) { - throw errorHandler(e, 'Whoami failed'); + console.log(API_URL + '/candidate/whoami'); + const res = await apiFetch(API_URL + '/candidate/whoami', { + method: 'GET', + credentials: 'include' + }); + if (res.status != 200) { + throw Error(await res.text()); + } + return await res.text(); + } catch (e) { + throw errorHandler(e, 'Failed to fetch whoami'); } }; @@ -77,10 +79,11 @@ export const apiLogin = async (data: CandidateLogin): Promise => { }; export const apiFillDetails = async (data: CandidateData): Promise => { - Object.keys(data).forEach(key => { + Object.keys(data).forEach((key) => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore data[key] = DOMPurify.sanitize(data[key]); - }); + }); try { const res = await axios.post(API_URL + '/candidate/details', data, { withCredentials: true }); return res.data; @@ -162,4 +165,4 @@ export const apiDeltePortfolio = async (): Promise => { } catch (e: any) { throw errorHandler(e, 'Failed to delete portfolio'); } -}; \ No newline at end of file +}; diff --git a/frontend/src/lib/@api/index.ts b/frontend/src/lib/@api/index.ts index 5e4bcc2..9a16f8a 100644 --- a/frontend/src/lib/@api/index.ts +++ b/frontend/src/lib/@api/index.ts @@ -5,10 +5,10 @@ export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise { +export const errorHandler = (error: AxiosError | unknown, msg: string): ApiError => { return { error, msg }; } diff --git a/frontend/src/routes/(admin)/admin/(authenticated)/dashboard/+page.server.ts b/frontend/src/routes/(admin)/admin/(authenticated)/dashboard/+page.server.ts index 16e5a0a..a89df2f 100644 --- a/frontend/src/routes/(admin)/admin/(authenticated)/dashboard/+page.server.ts +++ b/frontend/src/routes/(admin)/admin/(authenticated)/dashboard/+page.server.ts @@ -4,13 +4,13 @@ import type { PageServerLoad } from './$types'; export const load: PageServerLoad = async ({ fetch }) => { let candidatePreview: Array = [{}]; - try { - candidatePreview = await apiListCandidates(fetch); - } catch (e) { - console.error(e); - } + + candidatePreview = + (await apiListCandidates(fetch).catch((e) => { + console.error(e); + })) || []; return { - preview: candidatePreview, + preview: candidatePreview }; }; diff --git a/frontend/src/routes/(candidate)/(authenticated)/+layout.server.ts b/frontend/src/routes/(candidate)/(authenticated)/+layout.server.ts index b38a7f2..8196422 100644 --- a/frontend/src/routes/(candidate)/(authenticated)/+layout.server.ts +++ b/frontend/src/routes/(candidate)/(authenticated)/+layout.server.ts @@ -1,10 +1,16 @@ import type { LayoutServerLoad } from './$types'; import { redirect } from '@sveltejs/kit'; +import { apiWhoami } from '$lib/@api/candidate'; -export const load: LayoutServerLoad = ({ cookies }) => { +export const load: LayoutServerLoad = async ({ cookies, fetch }) => { const isAuthenticated = cookies.get('id'); - if (!isAuthenticated) { - throw redirect(302, '/auth/login'); + + if (isAuthenticated) { + await apiWhoami(fetch).catch((e) => { + throw redirect(302, '/auth/logout'); + }); + } else { + throw redirect(302, '/auth/logout'); } }; diff --git a/frontend/src/routes/(candidate)/(authenticated)/dashboard/+layout.server.ts b/frontend/src/routes/(candidate)/(authenticated)/dashboard/+layout.server.ts deleted file mode 100644 index f75ac05..0000000 --- a/frontend/src/routes/(candidate)/(authenticated)/dashboard/+layout.server.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { apiFetchDetails, apiFetchSubmissionProgress } from '$lib/@api/candidate'; -import type { CandidateData } from '$lib/stores/candidate'; -import { redirect } from '@sveltejs/kit'; -import type { LayoutServerLoad } from './$types'; - -export const load: LayoutServerLoad = async ({ fetch }) => { - let details: CandidateData; - try { - details = await apiFetchDetails(fetch); - } catch (e: any) { - if (e.code === 401) { - throw redirect(302, '/auth/login'); - } else { - throw redirect(302, '/register'); - } - } - - let submissionProgress; - try { - submissionProgress = await apiFetchSubmissionProgress(fetch); - } catch (e) { - console.log(e); - } - - return { - candidate: details, - submission: { - ...submissionProgress - } - }; -}; diff --git a/frontend/src/routes/(candidate)/(authenticated)/dashboard/+page.server.ts b/frontend/src/routes/(candidate)/(authenticated)/dashboard/+page.server.ts new file mode 100644 index 0000000..9148f3e --- /dev/null +++ b/frontend/src/routes/(candidate)/(authenticated)/dashboard/+page.server.ts @@ -0,0 +1,22 @@ +import { apiFetchDetails, apiFetchSubmissionProgress } from '$lib/@api/candidate'; +import type { CandidateData } from '$lib/stores/candidate'; +import { redirect } from '@sveltejs/kit'; +import type { PageServerLoad } from './$types'; + +export const load: PageServerLoad = async ({ fetch }) => { + const details: CandidateData = await apiFetchDetails(fetch).catch((e) => { + console.error(e); + throw redirect(302, '/register'); + }); + + const submissionProgress = await apiFetchSubmissionProgress(fetch).catch((e) => { + console.log(e); + }); + + return { + candidate: details, + submission: { + ...submissionProgress + } + }; +}; diff --git a/frontend/src/routes/(candidate)/auth/logout/+page.server.ts b/frontend/src/routes/(candidate)/auth/logout/+page.server.ts index 90f31a7..78dc0a5 100644 --- a/frontend/src/routes/(candidate)/auth/logout/+page.server.ts +++ b/frontend/src/routes/(candidate)/auth/logout/+page.server.ts @@ -4,7 +4,9 @@ import { redirect } from '@sveltejs/kit'; import { apiLogout } from '$lib/@api/candidate'; export const load: PageServerLoad = async ({ fetch, cookies }) => { - await apiLogout(fetch); + await apiLogout(fetch).catch(() => { + // TODO: Handle error + }); cookies.delete('id', { path: '/' }); cookies.delete('key', { path: '/' });