diff --git a/src/api/auth.ts b/src/api/auth.ts new file mode 100644 index 0000000..cdc3df8 --- /dev/null +++ b/src/api/auth.ts @@ -0,0 +1,19 @@ +import { deleteTokenCookie, getTokenCookie } from "@utils/auth"; +import { APIPath, postBackendAPI } from "./backend"; + +export async function generateToken(username: string, password: string): Promise { + const { token } = await postBackendAPI<{ username: string, password: string }, { token: string }>({ path: APIPath.AUTH_TOKEN }, { username, password }); + return token; +} + +export async function isAuthenticated(): Promise { + try { + const token = getTokenCookie(); + await postBackendAPI({ path: APIPath.AUTH_TOKEN_VERIFY }, { token }); + return true; + } catch (err) { + // remove the cookie since it's invalid + deleteTokenCookie(); + return false; + } +} diff --git a/src/api/backend.ts b/src/api/backend.ts index 38b4507..c138cac 100644 --- a/src/api/backend.ts +++ b/src/api/backend.ts @@ -13,7 +13,10 @@ export enum APIPath { FEED = "/feed/:id", JOBADS = "/jobads/:id", SIGNUPS = "/signup/:id", + SIGNUPS_EDIT = "/signup/:id/edit/:uuid", SIGNUP_FORMS = "/signupForm/:id", + SIGNUP_FORMS_EMAIL = "/signupForm/:id/sendemail", + SIGNUP_FORMS_SIGNUPS = "/signupForm/:id/signups", AUTH_TOKEN = "/api-token-auth", AUTH_TOKEN_VERIFY = "/api-token-verify", } @@ -22,6 +25,7 @@ export type API = { path: APIPath; urlParams?: { id?: string | number; + uuid?: string; }; queryParams?: { limit?: number; diff --git a/src/api/eventApi.ts b/src/api/eventApi.ts index f6f81fe..c5038bf 100644 --- a/src/api/eventApi.ts +++ b/src/api/eventApi.ts @@ -14,7 +14,7 @@ interface Options { class EventApi { static async getEvent(id: number, auth = false): Promise { try { - return await getBackendAPI({ + return await getBackendAPI({ path: APIPath.EVENTS, urlParams: { id }, authenticated: auth, }); } catch (err) { @@ -66,7 +66,7 @@ class EventApi { static async deleteEvent(id: number): Promise { try { - await deleteBackendAPI<{ message: "OK" }>({ path: APIPath.EVENTS, urlParams: { id } }); + await deleteBackendAPI<{ message: "OK" }>({ path: APIPath.EVENTS, urlParams: { id }, authenticated: true }); } catch (err) { console.error(err); throw err; diff --git a/src/api/feedApi.ts b/src/api/feedApi.ts index 2f00aa2..af4c323 100644 --- a/src/api/feedApi.ts +++ b/src/api/feedApi.ts @@ -1,41 +1,37 @@ /* eslint-disable no-console */ -import axios from "axios"; import Post from "@models/Feed"; -import { getAuthHeader } from "@utils/auth"; +import { + APIPath, deleteBackendAPI, getBackendAPI, postBackendAPI, putBackendAPI, +} from "./backend"; -export const URL = `${process.env.NEXT_PUBLIC_API_URL}/feed/`; - -export interface Options { +interface Options { limit?: number; offset?: number; auth?: boolean; } class FeedApi { - static async getFeed(options: Options = {}): Promise { - const { - limit, offset, auth, - } = options; - const params = { - limit, - offset, - }; - const headers = auth ? { Authorization: getAuthHeader() } : null; + static async getPost(id: number, auth?: boolean): Promise { try { - const resp = await axios.get(URL, { params, headers }); - return resp.data.results; + return await getBackendAPI({ + path: APIPath.FEED, urlParams: { id }, authenticated: auth, + }); } catch (err) { console.error(err); throw err; } } - static async getPost(id: number, options: Options = {}): Promise { - const { auth } = options; - const headers = auth ? { Authorization: getAuthHeader() } : null; + static async getFeed({ limit, offset, auth }: Options = {}): Promise { try { - const resp = await axios.get(`${URL}${id}/`, { headers }); - return resp.data; + return await getBackendAPI({ + path: APIPath.FEED, + queryParams: { + limit, + offset, + }, + authenticated: auth, + }); } catch (err) { console.error(err); throw err; @@ -44,12 +40,7 @@ class FeedApi { static async createPost(data: Post): Promise { try { - const resp = await axios.post(URL, data, { - headers: { - Authorization: getAuthHeader(), - }, - }); - return resp.data; + return await postBackendAPI({ path: APIPath.FEED, authenticated: true }, data); } catch (err) { console.error(err); throw err; @@ -58,27 +49,18 @@ class FeedApi { static async updatePost(data: Post): Promise { try { - const putUrl = `${URL}${data.id}/`; - const resp = await axios.put(putUrl, data, { - headers: { - Authorization: getAuthHeader(), - }, - }); - return resp.data; + return await putBackendAPI({ + path: APIPath.FEED, urlParams: { id: data.id }, authenticated: true, + }, data); } catch (err) { console.error(err); throw err; } } - static async deletePost(id: number) { + static async deletePost(id: number): Promise { try { - const resp = await axios.delete(`${URL}${id}`, { - headers: { - Authorization: getAuthHeader(), - }, - }); - return resp.data; + await deleteBackendAPI<{ message: "OK" }>({ path: APIPath.EVENTS, urlParams: { id }, authenticated: true }); } catch (err) { console.error(err); throw err; diff --git a/src/api/jobAdApi.ts b/src/api/jobAdApi.ts index 5c71cdd..9ee70ff 100644 --- a/src/api/jobAdApi.ts +++ b/src/api/jobAdApi.ts @@ -1,11 +1,10 @@ /* eslint-disable no-console */ -import axios from "axios"; import JobAd from "@models/JobAd"; -import { getAuthHeader } from "@utils/auth"; +import { + APIPath, deleteBackendAPI, getBackendAPI, postBackendAPI, putBackendAPI, +} from "./backend"; -export const URL = `${process.env.NEXT_PUBLIC_API_URL}/jobads/`; - -export interface Options { +interface Options { since?: Date; limit?: number; offset?: number; @@ -13,35 +12,30 @@ export interface Options { } class JobAdApi { - static async getJobAds(options: Options = {}): Promise { - const { - since, limit, offset, auth, - } = options; + static async getJobAd(id: number, auth = false): Promise { try { - const params = { - since, - limit, - offset, - }; - const headers = auth ? { Authorization: getAuthHeader() } : null; - const resp = await axios.get(`${URL}`, { - headers, - params, + return await getBackendAPI({ + path: APIPath.JOBADS, urlParams: { id }, authenticated: auth, }); - return resp.data.results; } catch (err) { console.error(err); throw err; } } - static async getJobAd(id: number, auth = false): Promise { + static async getJobAds({ + since, limit, offset, auth, + }: Options = {}): Promise { try { - const headers = auth ? { Authorization: getAuthHeader() } : null; - const resp = await axios.get(`${URL}${id}/`, { - headers, + return await getBackendAPI({ + path: APIPath.JOBADS, + queryParams: { + since, + limit, + offset, + }, + authenticated: auth, }); - return resp.data; } catch (err) { console.error(err); throw err; @@ -50,12 +44,9 @@ class JobAdApi { static async createJobAd(data: JobAd): Promise { try { - const resp = await axios.post(URL, data, { - headers: { - Authorization: getAuthHeader(), - }, - }); - return resp.data; + return await postBackendAPI({ + path: APIPath.JOBADS, authenticated: true, + }, data); } catch (err) { console.error(err); throw err; @@ -64,27 +55,18 @@ class JobAdApi { static async updateJobAd(data: JobAd): Promise { try { - const putUrl = `${URL}${data.id}/`; - const resp = await axios.put(putUrl, data, { - headers: { - Authorization: getAuthHeader(), - }, - }); - return resp.data; + return await putBackendAPI({ + path: APIPath.JOBADS, urlParams: { id: data.id }, authenticated: true, + }, data); } catch (err) { console.error(err); throw err; } } - static async deleteJobAd(id: number) { + static async deleteJobAd(id: number): Promise { try { - const resp = await axios.delete(`${URL}${id}`, { - headers: { - Authorization: getAuthHeader(), - }, - }); - return resp.data; + await deleteBackendAPI<{ message: "OK" }>({ path: APIPath.JOBADS, urlParams: { id }, authenticated: true }); } catch (err) { console.error(err); throw err; diff --git a/src/api/signupApi.ts b/src/api/signupApi.ts index 66fb599..c726b45 100644 --- a/src/api/signupApi.ts +++ b/src/api/signupApi.ts @@ -1,27 +1,21 @@ /* eslint-disable no-console */ -import axios from "axios"; import { Signup, SignupForm } from "@models/Signup"; -import { getAuthHeader } from "@utils/auth"; +import { + APIPath, deleteBackendAPI, getBackendAPI, postBackendAPI, putBackendAPI, +} from "./backend"; -export const URL = `${process.env.NEXT_PUBLIC_API_URL}/signup/`; -export const FORM_URL = `${process.env.NEXT_PUBLIC_API_URL}/signupForm/`; - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface Options { - // limit?: number; - // offset?: number; - // auth?: boolean; -} +export type EmailRequest = { + mode: "all" | "actual" | "reserve"; + subject: string; + content: string; +}; class SignupApi { static async getSignup(id: number): Promise { try { - const resp = await axios.get(`${URL}${id}`, { - headers: { - Authorization: getAuthHeader(), - }, + return await getBackendAPI({ + path: APIPath.SIGNUPS, urlParams: { id }, authenticated: true, }); - return resp.data; } catch (err) { console.error(err); throw err; @@ -30,8 +24,9 @@ class SignupApi { static async createSignup(data: Signup): Promise { try { - const resp = await axios.post(URL, data); - return resp.data; + return await postBackendAPI({ + path: APIPath.SIGNUPS, + }, data); } catch (err) { console.error(err); throw err; @@ -42,10 +37,13 @@ class SignupApi { try { const { id } = data; if (!id) throw new Error("SignupId required!"); - const resp = await axios.put(`${URL}${id}/edit/`, data, { - params: { uuid }, - }); - return resp.data; + return await putBackendAPI({ + path: APIPath.SIGNUPS_EDIT, + urlParams: { + id, + uuid, + }, + }, data); } catch (err) { console.error(err); throw err; @@ -54,40 +52,22 @@ class SignupApi { static async getSignupUUID(id: number, uuid: string): Promise { try { - const resp = await axios.get(`${URL}${id}/edit/`, { - params: { + return await getBackendAPI({ + path: APIPath.SIGNUPS_EDIT, + urlParams: { + id, uuid, }, }); - return resp.data; } catch (err) { console.error(err); throw err; } } - static async deleteSignup(id: number) { + static async deleteSignup(id: number): Promise { try { - const resp = await axios.delete(`${URL}${id}`, { - headers: { - Authorization: getAuthHeader(), - }, - }); - return resp.data; - } catch (err) { - console.error(err); - throw err; - } - } - - static async getForms(auth = false): Promise { - try { - const headers = auth ? { Authorization: getAuthHeader() } : null; - const resp = await axios.get(FORM_URL, { - headers, - }); - const { results } = resp.data; - return results; + await deleteBackendAPI<{ message: "OK" }>({ path: APIPath.SIGNUPS, urlParams: { id }, authenticated: true }); } catch (err) { console.error(err); throw err; @@ -96,11 +76,20 @@ class SignupApi { static async getForm(id: number, auth = false): Promise { try { - const headers = auth ? { Authorization: getAuthHeader() } : null; - const resp = await axios.get(`${FORM_URL}${id}/`, { - headers, + return await getBackendAPI({ + path: APIPath.SIGNUP_FORMS, urlParams: { id }, authenticated: auth, + }); + } catch (err) { + console.error(err); + throw err; + } + } + + static async getForms(auth = false): Promise { + try { + return await getBackendAPI({ + path: APIPath.SIGNUP_FORMS, authenticated: auth, }); - return resp.data; } catch (err) { console.error(err); throw err; @@ -109,12 +98,9 @@ class SignupApi { static async createForm(data: SignupForm): Promise { try { - const resp = await axios.post(FORM_URL, data, { - headers: { - Authorization: getAuthHeader(), - }, - }); - return resp.data; + return await postBackendAPI({ + path: APIPath.SIGNUP_FORMS, authenticated: true, + }, data); } catch (err) { console.error(err); throw err; @@ -123,41 +109,27 @@ class SignupApi { static async updateForm(data: SignupForm): Promise { try { - const putUrl = `${FORM_URL}${data.id}/`; - const resp = await axios.put(putUrl, data, { - headers: { - Authorization: getAuthHeader(), - }, - }); - return resp.data; + return await putBackendAPI({ + path: APIPath.SIGNUP_FORMS, urlParams: { id: data.id }, authenticated: true, + }, data); } catch (err) { console.error(err); throw err; } } - static async deleteForm(id: number) { + static async deleteForm(id: number): Promise { try { - const resp = await axios.delete(`${FORM_URL}${id}`, { - headers: { - Authorization: getAuthHeader(), - }, - }); - return resp.data; + await deleteBackendAPI<{ message: "OK" }>({ path: APIPath.SIGNUP_FORMS, urlParams: { id }, authenticated: true }); } catch (err) { console.error(err); throw err; } } - static async signupFormSendEmail(data: any, id: number): Promise { + static async signupFormSendEmail(data: EmailRequest, id: number): Promise { try { - const resp = await axios.post(`${FORM_URL}${id}/sendemail/`, data, { - headers: { - Authorization: getAuthHeader(), - }, - }); - return resp.data; + await postBackendAPI({ path: APIPath.SIGNUP_FORMS_EMAIL, urlParams: { id }, authenticated: true }, data); } catch (err) { console.error(err); throw err; @@ -166,12 +138,7 @@ class SignupApi { static async getSignups(id: number): Promise { try { - const resp = await axios.get(`${FORM_URL}${id}/signups/`, { - headers: { - Authorization: getAuthHeader(), - }, - }); - return resp.data; + return await getBackendAPI({ path: APIPath.SIGNUP_FORMS_SIGNUPS, urlParams: { id }, authenticated: true }); } catch (err) { console.error(err); throw err; diff --git a/src/api/tagApi.ts b/src/api/tagApi.ts index df41d10..48c2b76 100644 --- a/src/api/tagApi.ts +++ b/src/api/tagApi.ts @@ -1,21 +1,11 @@ /* eslint-disable no-console */ -import axios from "axios"; import Tag from "@models/Tag"; - -export const URL = `${process.env.NEXT_PUBLIC_API_URL}/tags/`; - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface Options { - // limit?: number; - // offset?: number; - // auth?: boolean; -} +import { APIPath, getBackendAPI } from "./backend"; class TagApi { static async getTags(): Promise { try { - const resp = await axios.get(URL); - return resp.data.results; + return await getBackendAPI({ path: APIPath.TAGS }); } catch (err) { console.error(err); throw err; diff --git a/src/pages/admin/feed/[id].tsx b/src/pages/admin/feed/[id].tsx index 798066e..2563bdb 100644 --- a/src/pages/admin/feed/[id].tsx +++ b/src/pages/admin/feed/[id].tsx @@ -150,7 +150,7 @@ const FeedCreatePage: NextPage = () => { const feedId = id && Number(id); if (feedId !== undefined) { - FeedApi.getPost(feedId, { auth: true }) + FeedApi.getPost(feedId, true) .then((res) => setFormData({ ...res, tags: (res.tags).map((inst) => inst.id) as any, diff --git a/src/pages/admin/login.tsx b/src/pages/admin/login.tsx index 90eb3cc..31e6967 100644 --- a/src/pages/admin/login.tsx +++ b/src/pages/admin/login.tsx @@ -2,7 +2,8 @@ import React, { useState, useEffect } from "react"; import { NextPage } from "next"; import { useRouter } from "next/router"; import styled from "styled-components"; -import { generateToken, setTokenCookie, isAuthenticated } from "@utils/auth"; +import { setTokenCookie } from "@utils/auth"; +import { generateToken, isAuthenticated } from "@api/auth"; import AdminPageWrapper from "@views/common/AdminPageWrapper"; const Main = styled.div` diff --git a/src/pages/admin/signups/[id]/email.tsx b/src/pages/admin/signups/[id]/email.tsx index 45f2c4b..f05ff11 100644 --- a/src/pages/admin/signups/[id]/email.tsx +++ b/src/pages/admin/signups/[id]/email.tsx @@ -5,7 +5,7 @@ import { toast } from "react-toastify"; import AdminCreateCommon from "@views/admin/AdminCreateCommon"; import MarkdownEditorWidget from "@components/Widgets/MarkdownEditorWidget"; import { SignupForm } from "@models/Signup"; -import SignupApi from "@api/signupApi"; +import SignupApi, { EmailRequest } from "@api/signupApi"; const widgets = { markdownEditor: MarkdownEditorWidget, @@ -67,7 +67,7 @@ const SignupEmailPage: NextPage = () => { const onSubmit = async (data) => { try { - const payload = data.formData; + const payload: EmailRequest = data.formData; await SignupApi.signupFormSendEmail(payload, Number(id)); toast.success("Email sent successfully 😎"); } catch (err) { diff --git a/src/utils/auth.ts b/src/utils/auth.ts index 4300fff..2790a6d 100644 --- a/src/utils/auth.ts +++ b/src/utils/auth.ts @@ -1,17 +1,5 @@ -import axios from "axios"; import Cookies from "js-cookie"; -const tokenUrl = `${process.env.NEXT_PUBLIC_API_URL}/api-token-auth/`; -const checkUrl = `${process.env.NEXT_PUBLIC_API_URL}/api-token-verify/`; - -export async function generateToken(username: string, password: string): Promise { - const resp = await axios.post(tokenUrl, { - username, - password, - }); - return resp.data.token; -} - export function setTokenCookie(token: string): void { Cookies.set("jwt", token); Cookies.set("jwt", token, { domain: ".sahkoinsinoorikilta.fi" }); @@ -25,22 +13,3 @@ export function deleteTokenCookie(): void { Cookies.remove("jwt", { domain: ".sahkoinsinoorikilta.fi" }); Cookies.remove("jwt"); } - -export async function isAuthenticated(): Promise { - try { - const token = getTokenCookie(); - await axios.post(checkUrl, { - token, - }); - return true; - } catch (err) { - // remove the cookie since it's invalid - deleteTokenCookie(); - return false; - } -} - -export function getAuthHeader(): string { - const jwt = getTokenCookie(); - return `JWT ${jwt}`; -} diff --git a/src/views/common/AdminPageWrapper.tsx b/src/views/common/AdminPageWrapper.tsx index 5406e62..5fa156c 100644 --- a/src/views/common/AdminPageWrapper.tsx +++ b/src/views/common/AdminPageWrapper.tsx @@ -1,11 +1,11 @@ import React, { useEffect, useState } from "react"; +import { useRouter } from "next/router"; import styled from "styled-components"; import colors from "@theme/colors"; import breakpoints from "@theme/breakpoints"; import AdminHeader from "@components/AdminHeader"; import AdminSidebar from "@components/AdminSidebar"; -import { isAuthenticated } from "@utils/auth"; -import { useRouter } from "next/router"; +import { isAuthenticated } from "@api/auth"; import LoadingView from "./LoadingView"; const Main = styled.main`