From efd916a8a262aa356059c4e3df41c051dca7568c Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Sat, 4 Sep 2021 18:02:29 +0300 Subject: [PATCH 01/32] generic functions for backend queries --- src/api/backend.ts | 122 +++++++++++++++++++++++++++++++ src/api/eventApi.ts | 68 +++++++---------- src/hooks/useFetchBackend.ts | 27 +++++++ src/hooks/useFetchEvents.ts | 56 -------------- src/hooks/useFetchFeed.ts | 53 -------------- src/hooks/useFetchJobAds.ts | 56 -------------- src/pages/admin/events/index.tsx | 5 +- src/pages/admin/feed/index.tsx | 5 +- src/pages/admin/jobads/index.tsx | 5 +- src/pages/in_english.tsx | 10 +-- src/pages/index.tsx | 10 +-- src/pages/kilta/toiminta.tsx | 8 +- src/pages/yritysyhteistyo.tsx | 5 +- 13 files changed, 200 insertions(+), 230 deletions(-) create mode 100644 src/api/backend.ts create mode 100644 src/hooks/useFetchBackend.ts delete mode 100644 src/hooks/useFetchEvents.ts delete mode 100644 src/hooks/useFetchFeed.ts delete mode 100644 src/hooks/useFetchJobAds.ts diff --git a/src/api/backend.ts b/src/api/backend.ts new file mode 100644 index 0000000..38b4507 --- /dev/null +++ b/src/api/backend.ts @@ -0,0 +1,122 @@ +import axios, { AxiosInstance, AxiosRequestConfig } from "axios"; +import { getTokenCookie } from "@utils/auth"; + +export const HOST = `${process.env.NEXT_PUBLIC_API_URL}`; + +const axiosInstance: AxiosInstance = axios.create({ + baseURL: process.env.NEXT_PUBLIC_API_URL, +}); + +export enum APIPath { + TAGS = "/tags/:id", + EVENTS = "/events/:id", + FEED = "/feed/:id", + JOBADS = "/jobads/:id", + SIGNUPS = "/signup/:id", + SIGNUP_FORMS = "/signupForm/:id", + AUTH_TOKEN = "/api-token-auth", + AUTH_TOKEN_VERIFY = "/api-token-verify", +} + +export type API = { + path: APIPath; + urlParams?: { + id?: string | number; + }; + queryParams?: { + limit?: number; + offset?: number; + since?: Date; + }; + authenticated?: boolean; +}; + +type Headers = { + Authorization?: string; +}; + +const getAuthHeader = (): string => { + const jwt = getTokenCookie(); + return `JWT ${jwt}`; +}; + +const getHeaders = (auth?: boolean): Headers => { + if (auth) { + return { + Authorization: getAuthHeader(), + }; + } + return {}; +}; + +const fillUrlParams = (apiPath: APIPath, params: API["urlParams"] = {}): string => { + const path = apiPath + .split("/") + .filter(Boolean) + .map((urlComponent) => { + // fill in each placeholder component like ':id' with value from params + if (urlComponent.startsWith(":")) { + const key = urlComponent.substring(1); + const value = params[key]; + return value; + } + return urlComponent; + }) + .join("/"); + // code above strips leading '/' from path + return `/${path}`; +}; + +async function callBackendAPI( + path: APIPath, + urlParams: API["urlParams"] = {}, + queryParams: API["queryParams"] = {}, + method: AxiosRequestConfig["method"], + headers: Headers, + requestBody: RequestType, +): Promise { + const url = fillUrlParams(path, urlParams); + const request: AxiosRequestConfig = { + url, + method, + headers, + params: queryParams, + data: requestBody, + responseType: "json", + }; + const response = await axiosInstance.request(request); + + const arrayResp = (response.data as { results?: ResponseType }); + if (Array.isArray(arrayResp.results)) { + return arrayResp.results; + } + return response.data; +} + +export async function getBackendAPI({ + path, urlParams, queryParams, authenticated, +}: API): Promise { + const headers = getHeaders(authenticated); + return callBackendAPI(path, urlParams, queryParams, "GET", headers, undefined); +} + +export async function postBackendAPI({ + path, urlParams, queryParams, authenticated, +}: API, body: RequestType): Promise { + const headers = getHeaders(authenticated); + return callBackendAPI(path, urlParams, queryParams, "POST", headers, body); +} + +export async function putBackendAPI({ + path, urlParams, queryParams, authenticated, +}: API, body: RequestType): Promise { + const headers = getHeaders(authenticated); + return callBackendAPI(path, urlParams, queryParams, "PUT", headers, body); +} + +export async function deleteBackendAPI({ + path, urlParams, queryParams, authenticated, +}: API): Promise { + const headers = getHeaders(authenticated); + return callBackendAPI(path, urlParams, queryParams, "DELETE", headers, undefined); +} diff --git a/src/api/eventApi.ts b/src/api/eventApi.ts index b4074ad..f6f81fe 100644 --- a/src/api/eventApi.ts +++ b/src/api/eventApi.ts @@ -1,11 +1,10 @@ /* eslint-disable no-console */ -import axios from "axios"; import Event from "@models/Event"; -import { getAuthHeader } from "@utils/auth"; +import { + APIPath, deleteBackendAPI, getBackendAPI, postBackendAPI, putBackendAPI, +} from "./backend"; -export const URL = `${process.env.NEXT_PUBLIC_API_URL}/events/`; - -export interface Options { +interface Options { limit?: number; offset?: number; auth?: boolean; @@ -15,33 +14,28 @@ export interface Options { class EventApi { static async getEvent(id: number, auth = false): Promise { try { - const headers = auth ? { Authorization: getAuthHeader() } : null; - const resp = await axios.get(`${URL}${id}/`, { - headers, + return await getBackendAPI({ + path: APIPath.EVENTS, urlParams: { id }, authenticated: auth, }); - return resp.data; } catch (err) { console.error(err); throw err; } } - static async getEvents(options: Options = {}): Promise { - const { - since, limit, offset, auth, - } = options; + static async getEvents({ + since, limit, offset, auth, + }: Options = {}): 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.EVENTS, + queryParams: { + since, + limit, + offset, + }, + authenticated: auth, }); - return resp.data.results; } catch (err) { console.error(err); throw err; @@ -50,12 +44,9 @@ class EventApi { static async createEvent(data: Event): Promise { try { - const resp = await axios.post(URL, data, { - headers: { - Authorization: getAuthHeader(), - }, - }); - return resp.data; + return await postBackendAPI({ + path: APIPath.EVENTS, authenticated: true, + }, data); } catch (err) { console.error(err); throw err; @@ -64,27 +55,18 @@ class EventApi { static async updateEvent(data: Event): 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.EVENTS, urlParams: { id: data.id }, authenticated: true, + }, data); } catch (err) { console.error(err); throw err; } } - static async deleteEvent(id: number) { + static async deleteEvent(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 } }); } catch (err) { console.error(err); throw err; diff --git a/src/hooks/useFetchBackend.ts b/src/hooks/useFetchBackend.ts new file mode 100644 index 0000000..e6c1308 --- /dev/null +++ b/src/hooks/useFetchBackend.ts @@ -0,0 +1,27 @@ +import useSWR from "swr"; +import { APIPath, getBackendAPI } from "@api/backend"; + +function useFetchBackend({ + apiPath: path, + fallbackData, + options, +}: { + apiPath: APIPath, + fallbackData?: DataType, + options?: { + limit?: number; + auth?: boolean; + } +}): { + data?: DataType, + error?: any + } { + const fetcher = (limit: number, authenticated: boolean) => getBackendAPI({ path, queryParams: { limit }, authenticated }); + const { data, error } = useSWR([options?.limit, options?.auth], fetcher, { fallbackData }); + return { + data, + error, + }; +} + +export default useFetchBackend; diff --git a/src/hooks/useFetchEvents.ts b/src/hooks/useFetchEvents.ts deleted file mode 100644 index 2a810ba..0000000 --- a/src/hooks/useFetchEvents.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { useRef } from "react"; -import useSWR from "swr"; -import isDeepEqual from "fast-deep-equal/react"; -import axios, { AxiosRequestConfig } from "axios"; -import Event from "@models/Event"; -import { getAuthHeader } from "@utils/auth"; -import { URL, Options } from "@api/eventApi"; - -const fetcher = (url: string, config: AxiosRequestConfig) => axios.get(url, config).then((res) => res.data); - -const generateFetchParams = (id = "", options: Options = {}) => { - const url = `${URL}${id}`; - const { - auth, since, limit, offset, - } = options; - - return { - url, - config: { - params: { - since, - limit, - offset, - }, - headers: auth ? { Authorization: getAuthHeader() } : null, - }, - }; -}; - -interface FetchArguments { - fallbackData?: Event | Event[], - id?: string; - options?: Options -} - -const useFetchEvents = ({ - fallbackData, - id = "", - options = {}, -}: FetchArguments) => { - const { url, config } = generateFetchParams(id, options); - - // Use ref, since config dependency is non-primitive => without this we have infinite fetch loop - const configRef = useRef(config); - if (!isDeepEqual(configRef.current, config)) { - configRef.current = config; - } - - const { data, error } = useSWR([url, configRef.current], fetcher, { fallbackData }); - return { - data: data?.results || data, - error, - }; -}; - -export default useFetchEvents; diff --git a/src/hooks/useFetchFeed.ts b/src/hooks/useFetchFeed.ts deleted file mode 100644 index cf0fe2a..0000000 --- a/src/hooks/useFetchFeed.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { useRef } from "react"; -import useSWR from "swr"; -import isDeepEqual from "fast-deep-equal/react"; -import axios, { AxiosRequestConfig } from "axios"; -import Post from "@models/Feed"; -import { getAuthHeader } from "@utils/auth"; -import { URL, Options } from "@api/feedApi"; - -const feedFetcher = (url: string, config?: AxiosRequestConfig) => axios.get(url, config).then((res) => res.data); - -const generateFetchParams = (id = "", options: Options = {}) => { - const url = `${URL}${id}`; - const { auth, limit, offset } = options; - - return { - url, - config: { - params: { - limit, - offset, - }, - headers: auth ? { Authorization: getAuthHeader() } : null, - }, - }; -}; - -interface FetchArguments { - fallbackData?: Post | Post[], - id?: string; - options?: Options -} - -const useFetchFeed = ({ - fallbackData, - id = "", - options = {}, -}: FetchArguments) => { - const { url, config } = generateFetchParams(id, options); - - // Use ref, since config dependency is non-primitive => without this we have infinite fetch loop - const configRef = useRef(config); - if (!isDeepEqual(configRef.current, config)) { - configRef.current = config; - } - - const { data, error } = useSWR([url, configRef.current], feedFetcher, { fallbackData }); - return { - data: data?.results || data, - error, - }; -}; - -export default useFetchFeed; diff --git a/src/hooks/useFetchJobAds.ts b/src/hooks/useFetchJobAds.ts deleted file mode 100644 index b422832..0000000 --- a/src/hooks/useFetchJobAds.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { useRef } from "react"; -import useSWR from "swr"; -import isDeepEqual from "fast-deep-equal/react"; -import axios, { AxiosRequestConfig } from "axios"; -import JobAd from "@models/JobAd"; -import { getAuthHeader } from "@utils/auth"; -import { URL, Options } from "@api/jobAdApi"; - -const jobAdFetcher = (url: string, config?: AxiosRequestConfig) => axios.get(url, config).then((res) => res.data); - -const generateFetchParams = (id = "", options: Options = {}) => { - const url = `${URL}${id}`; - const { - since, limit, offset, auth, - } = options; - - return { - url, - config: { - params: { - since, - limit, - offset, - }, - headers: auth ? { Authorization: getAuthHeader() } : null, - }, - }; -}; - -interface FetchArguments { - fallbackData?: JobAd | JobAd[], - id?: string; - options?: Options; -} - -const useFetchJobAds = ({ - fallbackData, - id = "", - options = {}, -}: FetchArguments) => { - const { url, config } = generateFetchParams(id, options); - - // Use ref, since config dependency is non-primitive => without this we have infinite fetch loop - const configRef = useRef(config); - if (!isDeepEqual(configRef.current, config)) { - configRef.current = config; - } - - const { data, error } = useSWR([url, configRef.current], jobAdFetcher, { fallbackData }); - return { - data: data?.results || data, - error, - }; -}; - -export default useFetchJobAds; diff --git a/src/pages/admin/events/index.tsx b/src/pages/admin/events/index.tsx index edfc3f4..6cdddd0 100644 --- a/src/pages/admin/events/index.tsx +++ b/src/pages/admin/events/index.tsx @@ -8,7 +8,8 @@ import { Button, Link } from "@components/index"; import AddLink from "@components/AddLink"; import Event from "@models/Event"; import EventApi from "@api/eventApi"; -import useFetchEvents from "@hooks/useFetchEvents"; +import useFetchBackend from "@hooks/useFetchBackend"; +import { APIPath } from "@api/backend"; const URL = "/admin/events"; @@ -65,7 +66,7 @@ const renderData = (events: Event[]) => { }; const AdminEventPage: NextPage = () => { - const { data } = useFetchEvents({ options: { auth: true } }); + const { data } = useFetchBackend({ apiPath: APIPath.EVENTS, options: { auth: true } }); return (

Events

diff --git a/src/pages/admin/feed/index.tsx b/src/pages/admin/feed/index.tsx index 793f27b..30092e5 100644 --- a/src/pages/admin/feed/index.tsx +++ b/src/pages/admin/feed/index.tsx @@ -8,7 +8,8 @@ import { Button, Link } from "@components/index"; import AddLink from "@components/AddLink"; import Post from "@models/Feed"; import PostApi from "@api/feedApi"; -import useFetchFeed from "@hooks/useFetchFeed"; +import useFetchBackend from "@hooks/useFetchBackend"; +import { APIPath } from "@api/backend"; const URL = "/admin/feed"; @@ -65,7 +66,7 @@ const renderData = (feed: Post[]) => { }; const AdminFeedPage: NextPage = () => { - const { data } = useFetchFeed({ options: { auth: true } }); + const { data } = useFetchBackend({ apiPath: APIPath.FEED, options: { auth: true } }); return (

Feed

diff --git a/src/pages/admin/jobads/index.tsx b/src/pages/admin/jobads/index.tsx index cc6e7fa..3de3931 100644 --- a/src/pages/admin/jobads/index.tsx +++ b/src/pages/admin/jobads/index.tsx @@ -7,8 +7,9 @@ import AdminListCommon from "@views/admin/AdminListCommon"; import { Button, Link } from "@components/index"; import AddLink from "@components/AddLink"; import JobAd from "@models/JobAd"; -import useFetchJobAds from "@hooks/useFetchJobAds"; +import useFetchBackend from "@hooks/useFetchBackend"; import JobAdApi from "@api/jobAdApi"; +import { APIPath } from "@api/backend"; const URL = "/admin/jobads"; @@ -69,7 +70,7 @@ const renderData = (jobAds: JobAd[]) => { }; const AdminJobAdPage: NextPage = () => { - const { data } = useFetchJobAds({ options: { auth: true } }); + const { data } = useFetchBackend({ apiPath: APIPath.JOBADS, options: { auth: true } }); return (

Job advertisements

diff --git a/src/pages/in_english.tsx b/src/pages/in_english.tsx index d1cfdef..db62e76 100644 --- a/src/pages/in_english.tsx +++ b/src/pages/in_english.tsx @@ -3,12 +3,12 @@ import { NextPage, GetStaticProps } from "next"; import Head from "next/head"; import Event from "@models/Event"; import EventApi from "@api/eventApi"; -import useFetchEvents from "@hooks/useFetchEvents"; +import useFetchBackend from "@hooks/useFetchBackend"; import Post from "@models/Feed"; import FeedApi from "@api/feedApi"; -import useFetchFeed from "@hooks/useFetchFeed"; import InEnglishPageView from "@views/InEnglishPage/InEnglishPageView"; import PageWrapper from "@views/common/PageWrapper"; +import { APIPath } from "@api/backend"; const eventOptions = { limit: 4, @@ -24,8 +24,8 @@ interface InitialProps { } const InEnglishPage: NextPage = ({ initialEvents, initialFeed }) => { - const eventResult = useFetchEvents({ fallbackData: initialEvents, options: eventOptions }); - const feedResult = useFetchFeed({ fallbackData: initialFeed, options: feedOptions }); + const eventResult = useFetchBackend({ apiPath: APIPath.EVENTS, fallbackData: initialEvents, options: eventOptions }); + const feedResult = useFetchBackend({ apiPath: APIPath.FEED, fallbackData: initialFeed, options: feedOptions }); return ( <> @@ -33,7 +33,7 @@ const InEnglishPage: NextPage = ({ initialEvents, initialFeed }) = - + ); diff --git a/src/pages/index.tsx b/src/pages/index.tsx index b18dc68..309ee90 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -3,12 +3,12 @@ import { NextPage, GetStaticProps } from "next"; import Head from "next/head"; import Event from "@models/Event"; import EventApi from "@api/eventApi"; -import useFetchEvents from "@hooks/useFetchEvents"; +import useFetchBackend from "@hooks/useFetchBackend"; import Post from "@models/Feed"; import FeedApi from "@api/feedApi"; -import useFetchFeed from "@hooks/useFetchFeed"; import FrontPageView from "@views/FrontPage/FrontPageView"; import PageWrapper from "@views/common/PageWrapper"; +import { APIPath } from "@api/backend"; const eventOptions = { limit: 4, @@ -24,8 +24,8 @@ interface InitialProps { } const FrontPage: NextPage = ({ initialEvents, initialFeed }) => { - const eventResult = useFetchEvents({ fallbackData: initialEvents, options: eventOptions }); - const feedResult = useFetchFeed({ fallbackData: initialFeed, options: feedOptions }); + const eventResult = useFetchBackend({ apiPath: APIPath.EVENTS, fallbackData: initialEvents, options: eventOptions }); + const feedResult = useFetchBackend({ apiPath: APIPath.FEED, fallbackData: initialFeed, options: feedOptions }); return ( <> @@ -33,7 +33,7 @@ const FrontPage: NextPage = ({ initialEvents, initialFeed }) => { - + ); diff --git a/src/pages/kilta/toiminta.tsx b/src/pages/kilta/toiminta.tsx index e0efd46..3a35b82 100644 --- a/src/pages/kilta/toiminta.tsx +++ b/src/pages/kilta/toiminta.tsx @@ -3,12 +3,12 @@ import { NextPage, GetStaticProps } from "next"; import Head from "next/head"; import Event from "@models/Event"; import EventApi from "@api/eventApi"; -import useFetchEvents from "@hooks/useFetchEvents"; +import useFetchBackend from "@hooks/useFetchBackend"; import Post from "@models/Feed"; import FeedApi from "@api/feedApi"; -import useFetchFeed from "@hooks/useFetchFeed"; import ActualPageView from "@views/ActualPage/ActualPageView"; import PageWrapper from "@views/common/PageWrapper"; +import { APIPath } from "@api/backend"; interface InitialProps { initialEvents: Event[]; @@ -16,8 +16,8 @@ interface InitialProps { } const ActualPage: NextPage = ({ initialEvents, initialFeed }) => { - const eventResult = useFetchEvents({ fallbackData: initialEvents }); - const feedResult = useFetchFeed({ fallbackData: initialFeed }); + const eventResult = useFetchBackend({ apiPath: APIPath.EVENTS, fallbackData: initialEvents }); + const feedResult = useFetchBackend({ apiPath: APIPath.FEED, fallbackData: initialFeed }); return ( <> diff --git a/src/pages/yritysyhteistyo.tsx b/src/pages/yritysyhteistyo.tsx index 5eed22b..b81c759 100644 --- a/src/pages/yritysyhteistyo.tsx +++ b/src/pages/yritysyhteistyo.tsx @@ -3,9 +3,10 @@ import { NextPage, GetStaticProps } from "next"; import Head from "next/head"; import JobAd from "@models/JobAd"; import JobAdApi from "@api/jobAdApi"; -import useFetchJobAds from "@hooks/useFetchJobAds"; +import useFetchBackend from "@hooks/useFetchBackend"; import CorporatePageView from "@views/CorporatePage/CorporatePageView"; import PageWrapper from "@views/common/PageWrapper"; +import { APIPath } from "@api/backend"; interface InitialProps { initialJobAds: JobAd[]; @@ -13,7 +14,7 @@ interface InitialProps { const CorporatePage: NextPage = ({ initialJobAds }) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { data, error } = useFetchJobAds({ fallbackData: initialJobAds }); + const { data, error } = useFetchBackend({ apiPath: APIPath.JOBADS, fallbackData: initialJobAds }); return ( <> From b7518d9bed091bcb1b34b08449b822defb847f5e Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Sat, 4 Sep 2021 18:52:00 +0300 Subject: [PATCH 02/32] refactor all api files --- src/api/auth.ts | 19 ++++ src/api/backend.ts | 4 + src/api/eventApi.ts | 4 +- src/api/feedApi.ts | 64 +++++------- src/api/jobAdApi.ts | 70 +++++-------- src/api/signupApi.ts | 133 ++++++++++--------------- src/api/tagApi.ts | 14 +-- src/pages/admin/feed/[id].tsx | 2 +- src/pages/admin/login.tsx | 3 +- src/pages/admin/signups/[id]/email.tsx | 4 +- src/utils/auth.ts | 31 ------ src/views/common/AdminPageWrapper.tsx | 4 +- 12 files changed, 133 insertions(+), 219 deletions(-) create mode 100644 src/api/auth.ts 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` From 8fb4dd9000f2cf8d0544633e474e3b0a4179f011 Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Sat, 4 Sep 2021 18:52:36 +0300 Subject: [PATCH 03/32] improve few types --- src/components/Button.tsx | 2 +- src/components/Icon.tsx | 2 +- src/pages/admin/events/[id].tsx | 2 +- src/views/admin/AdminCreateCommon.tsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Button.tsx b/src/components/Button.tsx index a845c6a..5c0ee99 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -8,7 +8,7 @@ interface ButtonProps { selected?: boolean; } -const StyledButton = styled.button<{ $selected: boolean }>` +const StyledButton = styled.button<{ $selected?: boolean }>` border-radius: none; padding: 0.8rem 2rem; margin: 0.5rem; diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx index 8290aea..cae974b 100644 --- a/src/components/Icon.tsx +++ b/src/components/Icon.tsx @@ -15,7 +15,7 @@ interface IconProps { onClick?: React.MouseEventHandler; } -const nameToIcon = (name: IconType): JSX.Element | string => { +const nameToIcon = (name: IconType): JSX.Element | null => { if (name === IconType.Facebook) { return ( { +const buildSchema = (formData: Event | undefined, signupForms: SignupForm[], tags: Tag[]) => { const date = new Date(); const tomorrowDate = new Date(); const currentDatetime = date.toISOString(); diff --git a/src/views/admin/AdminCreateCommon.tsx b/src/views/admin/AdminCreateCommon.tsx index f3a5646..88b4a1e 100644 --- a/src/views/admin/AdminCreateCommon.tsx +++ b/src/views/admin/AdminCreateCommon.tsx @@ -38,7 +38,7 @@ type AdminCreateCommonProps = { onChange?: (e: IChangeEvent, es?: ErrorSchema) => unknown; onFocus?: (id: string, value: string | number | boolean) => void; onSubmit: (e: ISubmitEvent) => unknown; - error: string; + error?: string; widgets: { [name: string]: any; }; From 44ccdd87de0cf703703606803083709dc89995be Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Fri, 14 Jan 2022 00:46:41 +0200 Subject: [PATCH 04/32] arrow functions --- src/api/auth.ts | 8 ++-- src/api/backend.ts | 30 ++++++------ src/api/eventApi.ts | 22 ++++----- src/api/feedApi.ts | 20 ++++---- src/api/jobAdApi.ts | 22 ++++----- src/api/signupApi.ts | 48 +++++++++---------- src/api/tagApi.ts | 4 +- .../Widgets/Checkbox/Checkboxes.tsx | 8 ++-- src/hooks/useFetchBackend.ts | 8 ++-- src/pages/_document.tsx | 4 +- src/pages/admin/login.tsx | 6 +-- src/utils/auth.ts | 12 ++--- src/views/common/AdminPageWrapper.tsx | 6 +-- 13 files changed, 97 insertions(+), 101 deletions(-) diff --git a/src/api/auth.ts b/src/api/auth.ts index cdc3df8..16fa3a5 100644 --- a/src/api/auth.ts +++ b/src/api/auth.ts @@ -1,12 +1,12 @@ import { deleteTokenCookie, getTokenCookie } from "@utils/auth"; import { APIPath, postBackendAPI } from "./backend"; -export async function generateToken(username: string, password: string): Promise { +export const generateToken = async (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 { +export const authenticate = async (): Promise => { try { const token = getTokenCookie(); await postBackendAPI({ path: APIPath.AUTH_TOKEN_VERIFY }, { token }); @@ -16,4 +16,4 @@ export async function isAuthenticated(): Promise { deleteTokenCookie(); return false; } -} +}; diff --git a/src/api/backend.ts b/src/api/backend.ts index c138cac..cf6c927 100644 --- a/src/api/backend.ts +++ b/src/api/backend.ts @@ -71,14 +71,14 @@ const fillUrlParams = (apiPath: APIPath, params: API["urlParams"] = {}): string return `/${path}`; }; -async function callBackendAPI( +const callBackendAPI = async ( path: APIPath, urlParams: API["urlParams"] = {}, queryParams: API["queryParams"] = {}, method: AxiosRequestConfig["method"], headers: Headers, requestBody: RequestType, -): Promise { +): Promise => { const url = fillUrlParams(path, urlParams); const request: AxiosRequestConfig = { url, @@ -95,32 +95,32 @@ async function callBackendAPI( return arrayResp.results; } return response.data; -} +}; -export async function getBackendAPI({ +export const getBackendAPI = async ({ path, urlParams, queryParams, authenticated, -}: API): Promise { +}: API): Promise => { const headers = getHeaders(authenticated); return callBackendAPI(path, urlParams, queryParams, "GET", headers, undefined); -} +}; -export async function postBackendAPI({ +export const postBackendAPI = async ({ path, urlParams, queryParams, authenticated, -}: API, body: RequestType): Promise { +}: API, body: RequestType): Promise => { const headers = getHeaders(authenticated); return callBackendAPI(path, urlParams, queryParams, "POST", headers, body); -} +}; -export async function putBackendAPI({ +export const putBackendAPI = async ({ path, urlParams, queryParams, authenticated, -}: API, body: RequestType): Promise { +}: API, body: RequestType): Promise => { const headers = getHeaders(authenticated); return callBackendAPI(path, urlParams, queryParams, "PUT", headers, body); -} +}; -export async function deleteBackendAPI({ +export const deleteBackendAPI = async ({ path, urlParams, queryParams, authenticated, -}: API): Promise { +}: API): Promise => { const headers = getHeaders(authenticated); return callBackendAPI(path, urlParams, queryParams, "DELETE", headers, undefined); -} +}; diff --git a/src/api/eventApi.ts b/src/api/eventApi.ts index c5038bf..13c978f 100644 --- a/src/api/eventApi.ts +++ b/src/api/eventApi.ts @@ -12,7 +12,7 @@ interface Options { } class EventApi { - static async getEvent(id: number, auth = false): Promise { + static getEvent = async (id: number, auth = false): Promise => { try { return await getBackendAPI({ path: APIPath.EVENTS, urlParams: { id }, authenticated: auth, @@ -21,11 +21,11 @@ class EventApi { console.error(err); throw err; } - } + }; - static async getEvents({ + static getEvents = async ({ since, limit, offset, auth, - }: Options = {}): Promise { + }: Options = {}): Promise => { try { return await getBackendAPI({ path: APIPath.EVENTS, @@ -40,9 +40,9 @@ class EventApi { console.error(err); throw err; } - } + }; - static async createEvent(data: Event): Promise { + static createEvent = async (data: Event): Promise => { try { return await postBackendAPI({ path: APIPath.EVENTS, authenticated: true, @@ -51,9 +51,9 @@ class EventApi { console.error(err); throw err; } - } + }; - static async updateEvent(data: Event): Promise { + static updateEvent = async (data: Event): Promise => { try { return await putBackendAPI({ path: APIPath.EVENTS, urlParams: { id: data.id }, authenticated: true, @@ -62,16 +62,16 @@ class EventApi { console.error(err); throw err; } - } + }; - static async deleteEvent(id: number): Promise { + static deleteEvent = async (id: number): Promise => { try { await deleteBackendAPI<{ message: "OK" }>({ path: APIPath.EVENTS, urlParams: { id }, authenticated: true }); } catch (err) { console.error(err); throw err; } - } + }; } export default EventApi; diff --git a/src/api/feedApi.ts b/src/api/feedApi.ts index af4c323..e198b47 100644 --- a/src/api/feedApi.ts +++ b/src/api/feedApi.ts @@ -11,7 +11,7 @@ interface Options { } class FeedApi { - static async getPost(id: number, auth?: boolean): Promise { + static getPost = async (id: number, auth?: boolean): Promise => { try { return await getBackendAPI({ path: APIPath.FEED, urlParams: { id }, authenticated: auth, @@ -20,9 +20,9 @@ class FeedApi { console.error(err); throw err; } - } + }; - static async getFeed({ limit, offset, auth }: Options = {}): Promise { + static getFeed = async ({ limit, offset, auth }: Options = {}): Promise => { try { return await getBackendAPI({ path: APIPath.FEED, @@ -36,18 +36,18 @@ class FeedApi { console.error(err); throw err; } - } + }; - static async createPost(data: Post): Promise { + static createPost = async (data: Post): Promise => { try { return await postBackendAPI({ path: APIPath.FEED, authenticated: true }, data); } catch (err) { console.error(err); throw err; } - } + }; - static async updatePost(data: Post): Promise { + static updatePost = async (data: Post): Promise => { try { return await putBackendAPI({ path: APIPath.FEED, urlParams: { id: data.id }, authenticated: true, @@ -56,16 +56,16 @@ class FeedApi { console.error(err); throw err; } - } + }; - static async deletePost(id: number): Promise { + static deletePost = async (id: number): Promise => { try { await deleteBackendAPI<{ message: "OK" }>({ path: APIPath.EVENTS, urlParams: { id }, authenticated: true }); } catch (err) { console.error(err); throw err; } - } + }; } export default FeedApi; diff --git a/src/api/jobAdApi.ts b/src/api/jobAdApi.ts index 9ee70ff..4b74db5 100644 --- a/src/api/jobAdApi.ts +++ b/src/api/jobAdApi.ts @@ -12,7 +12,7 @@ interface Options { } class JobAdApi { - static async getJobAd(id: number, auth = false): Promise { + static getJobAd = async (id: number, auth = false): Promise => { try { return await getBackendAPI({ path: APIPath.JOBADS, urlParams: { id }, authenticated: auth, @@ -21,11 +21,11 @@ class JobAdApi { console.error(err); throw err; } - } + }; - static async getJobAds({ + static getJobAds = async ({ since, limit, offset, auth, - }: Options = {}): Promise { + }: Options = {}): Promise => { try { return await getBackendAPI({ path: APIPath.JOBADS, @@ -40,9 +40,9 @@ class JobAdApi { console.error(err); throw err; } - } + }; - static async createJobAd(data: JobAd): Promise { + static createJobAd = async (data: JobAd): Promise => { try { return await postBackendAPI({ path: APIPath.JOBADS, authenticated: true, @@ -51,9 +51,9 @@ class JobAdApi { console.error(err); throw err; } - } + }; - static async updateJobAd(data: JobAd): Promise { + static updateJobAd = async (data: JobAd): Promise => { try { return await putBackendAPI({ path: APIPath.JOBADS, urlParams: { id: data.id }, authenticated: true, @@ -62,16 +62,16 @@ class JobAdApi { console.error(err); throw err; } - } + }; - static async deleteJobAd(id: number): Promise { + static deleteJobAd = async (id: number): Promise => { try { await deleteBackendAPI<{ message: "OK" }>({ path: APIPath.JOBADS, urlParams: { id }, authenticated: true }); } catch (err) { console.error(err); throw err; } - } + }; } export default JobAdApi; diff --git a/src/api/signupApi.ts b/src/api/signupApi.ts index c726b45..908a3b9 100644 --- a/src/api/signupApi.ts +++ b/src/api/signupApi.ts @@ -11,7 +11,7 @@ export type EmailRequest = { }; class SignupApi { - static async getSignup(id: number): Promise { + static getSignup = async (id: number): Promise => { try { return await getBackendAPI({ path: APIPath.SIGNUPS, urlParams: { id }, authenticated: true, @@ -20,9 +20,9 @@ class SignupApi { console.error(err); throw err; } - } + }; - static async createSignup(data: Signup): Promise { + static createSignup = async (data: Signup): Promise => { try { return await postBackendAPI({ path: APIPath.SIGNUPS, @@ -31,9 +31,9 @@ class SignupApi { console.error(err); throw err; } - } + }; - static async updateSignup(data: Signup, uuid: string): Promise { + static updateSignup = async (data: Signup, uuid: string): Promise => { try { const { id } = data; if (!id) throw new Error("SignupId required!"); @@ -48,9 +48,9 @@ class SignupApi { console.error(err); throw err; } - } + }; - static async getSignupUUID(id: number, uuid: string): Promise { + static getSignupUUID = async (id: number, uuid: string): Promise => { try { return await getBackendAPI({ path: APIPath.SIGNUPS_EDIT, @@ -63,18 +63,18 @@ class SignupApi { console.error(err); throw err; } - } + }; - static async deleteSignup(id: number): Promise { + static deleteSignup = async (id: number): Promise => { try { await deleteBackendAPI<{ message: "OK" }>({ path: APIPath.SIGNUPS, urlParams: { id }, authenticated: true }); } catch (err) { console.error(err); throw err; } - } + }; - static async getForm(id: number, auth = false): Promise { + static getForm = async (id: number, auth = false): Promise => { try { return await getBackendAPI({ path: APIPath.SIGNUP_FORMS, urlParams: { id }, authenticated: auth, @@ -83,9 +83,9 @@ class SignupApi { console.error(err); throw err; } - } + }; - static async getForms(auth = false): Promise { + static getForms = async (auth = false): Promise => { try { return await getBackendAPI({ path: APIPath.SIGNUP_FORMS, authenticated: auth, @@ -94,9 +94,9 @@ class SignupApi { console.error(err); throw err; } - } + }; - static async createForm(data: SignupForm): Promise { + static createForm = async (data: SignupForm): Promise => { try { return await postBackendAPI({ path: APIPath.SIGNUP_FORMS, authenticated: true, @@ -105,9 +105,9 @@ class SignupApi { console.error(err); throw err; } - } + }; - static async updateForm(data: SignupForm): Promise { + static updateForm = async (data: SignupForm): Promise => { try { return await putBackendAPI({ path: APIPath.SIGNUP_FORMS, urlParams: { id: data.id }, authenticated: true, @@ -116,34 +116,34 @@ class SignupApi { console.error(err); throw err; } - } + }; - static async deleteForm(id: number): Promise { + static deleteForm = async (id: number): Promise => { try { await deleteBackendAPI<{ message: "OK" }>({ path: APIPath.SIGNUP_FORMS, urlParams: { id }, authenticated: true }); } catch (err) { console.error(err); throw err; } - } + }; - static async signupFormSendEmail(data: EmailRequest, id: number): Promise { + static signupFormSendEmail = async (data: EmailRequest, id: number): Promise => { try { await postBackendAPI({ path: APIPath.SIGNUP_FORMS_EMAIL, urlParams: { id }, authenticated: true }, data); } catch (err) { console.error(err); throw err; } - } + }; - static async getSignups(id: number): Promise { + static getSignups = async (id: number): Promise => { try { return await getBackendAPI({ path: APIPath.SIGNUP_FORMS_SIGNUPS, urlParams: { id }, authenticated: true }); } catch (err) { console.error(err); throw err; } - } + }; } export default SignupApi; diff --git a/src/api/tagApi.ts b/src/api/tagApi.ts index 48c2b76..1ac4c6b 100644 --- a/src/api/tagApi.ts +++ b/src/api/tagApi.ts @@ -3,14 +3,14 @@ import Tag from "@models/Tag"; import { APIPath, getBackendAPI } from "./backend"; class TagApi { - static async getTags(): Promise { + static getTags = async (): Promise => { try { return await getBackendAPI({ path: APIPath.TAGS }); } catch (err) { console.error(err); throw err; } - } + }; } export default TagApi; diff --git a/src/components/Widgets/Checkbox/Checkboxes.tsx b/src/components/Widgets/Checkbox/Checkboxes.tsx index f54e6bc..539a9f3 100644 --- a/src/components/Widgets/Checkbox/Checkboxes.tsx +++ b/src/components/Widgets/Checkbox/Checkboxes.tsx @@ -5,17 +5,15 @@ import Checkbox from "./Checkbox"; // See https://github.com/rjsf-team/react-jsonschema-form/blob/master/packages/core/src/components/widgets/CheckboxesWidget.js -function selectValue(value, selected, all) { +const selectValue = (value, selected, all) => { const at = all.indexOf(value); const updated = selected.slice(0, at).concat(value, selected.slice(at)); // As inserting values at predefined index positions doesn't work with empty // arrays, we need to reorder the updated selection to match the initial order return updated.sort((a, b) => all.indexOf(a) > all.indexOf(b)); -} +}; -function deselectValue(value, selected) { - return selected.filter((v) => v !== value); -} +const deselectValue = (value, selected) => selected.filter((v) => v !== value); type CheckboxesProps = Omit & { options: any; diff --git a/src/hooks/useFetchBackend.ts b/src/hooks/useFetchBackend.ts index e6c1308..86be5d7 100644 --- a/src/hooks/useFetchBackend.ts +++ b/src/hooks/useFetchBackend.ts @@ -1,7 +1,7 @@ import useSWR from "swr"; import { APIPath, getBackendAPI } from "@api/backend"; -function useFetchBackend({ +const useFetchBackend = ({ apiPath: path, fallbackData, options, @@ -14,14 +14,14 @@ function useFetchBackend({ } }): { data?: DataType, - error?: any - } { + error?: Error + } => { const fetcher = (limit: number, authenticated: boolean) => getBackendAPI({ path, queryParams: { limit }, authenticated }); const { data, error } = useSWR([options?.limit, options?.auth], fetcher, { fallbackData }); return { data, error, }; -} +}; export default useFetchBackend; diff --git a/src/pages/_document.tsx b/src/pages/_document.tsx index fac394e..42939ab 100644 --- a/src/pages/_document.tsx +++ b/src/pages/_document.tsx @@ -6,7 +6,7 @@ import { ServerStyleSheet } from "styled-components"; import Favicons from "@components/Favicons"; export default class MyDocument extends Document<{ styleTags: unknown }> { - static async getInitialProps(ctx: DocumentContext): Promise { + static getInitialProps = async (ctx: DocumentContext): Promise => { const sheet = new ServerStyleSheet(); const originalRenderPage = ctx.renderPage; try { @@ -26,7 +26,7 @@ export default class MyDocument extends Document<{ styleTags: unknown }> { } finally { sheet.seal(); } - } + }; render(): JSX.Element { const { styleTags } = this.props; diff --git a/src/pages/admin/login.tsx b/src/pages/admin/login.tsx index 31e6967..6c97845 100644 --- a/src/pages/admin/login.tsx +++ b/src/pages/admin/login.tsx @@ -3,7 +3,7 @@ import { NextPage } from "next"; import { useRouter } from "next/router"; import styled from "styled-components"; import { setTokenCookie } from "@utils/auth"; -import { generateToken, isAuthenticated } from "@api/auth"; +import { generateToken, authenticate } from "@api/auth"; import AdminPageWrapper from "@views/common/AdminPageWrapper"; const Main = styled.div` @@ -21,8 +21,8 @@ const AdminLoginPage: NextPage = () => { const next = router.query.next as string || DEFAULT_REDIRECT; useEffect(() => { - isAuthenticated().then((res) => { - if (res) { + authenticate().then((authResult) => { + if (authResult) { router.push(next); } }); diff --git a/src/utils/auth.ts b/src/utils/auth.ts index 2790a6d..7c22859 100644 --- a/src/utils/auth.ts +++ b/src/utils/auth.ts @@ -1,15 +1,13 @@ import Cookies from "js-cookie"; -export function setTokenCookie(token: string): void { +export const setTokenCookie = (token: string): void => { Cookies.set("jwt", token); Cookies.set("jwt", token, { domain: ".sahkoinsinoorikilta.fi" }); -} +}; -export function getTokenCookie(): string { - return Cookies.get("jwt"); -} +export const getTokenCookie = (): string => Cookies.get("jwt"); -export function deleteTokenCookie(): void { +export const deleteTokenCookie = (): void => { Cookies.remove("jwt", { domain: ".sahkoinsinoorikilta.fi" }); Cookies.remove("jwt"); -} +}; diff --git a/src/views/common/AdminPageWrapper.tsx b/src/views/common/AdminPageWrapper.tsx index 5fa156c..26c6eae 100644 --- a/src/views/common/AdminPageWrapper.tsx +++ b/src/views/common/AdminPageWrapper.tsx @@ -5,7 +5,7 @@ import colors from "@theme/colors"; import breakpoints from "@theme/breakpoints"; import AdminHeader from "@components/AdminHeader"; import AdminSidebar from "@components/AdminSidebar"; -import { isAuthenticated } from "@api/auth"; +import { authenticate } from "@api/auth"; import LoadingView from "./LoadingView"; const Main = styled.main` @@ -43,8 +43,8 @@ const useShouldRedirect = (enabled = true) => { useEffect(() => { if (enabled) { - isAuthenticated().then((result) => { - setRedirect(!result); + authenticate().then((authResult) => { + setRedirect(!authResult); setCompleted(true); }); } From 394b7300afd63c6357da9db513ebb36620e27f95 Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Fri, 14 Jan 2022 01:17:57 +0200 Subject: [PATCH 05/32] fix lint --- .eslintrc.js | 1 + src/components/CrossFadeImages.tsx | 1 + src/components/Favicons.tsx | 2 +- src/components/Widgets/Checkbox/Checkboxes.tsx | 9 +++++---- src/components/Widgets/RadioButton/RadioButtonWidget.tsx | 9 +++++---- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 609a6e1..d390281 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -46,5 +46,6 @@ module.exports = { "jsx-a11y/click-events-have-key-events": "off", "jsx-a11y/no-noninteractive-element-interactions": "off", "jsx-a11y/no-static-element-interactions": "off", + "@typescript-eslint/default-param-last": "warn", }, }; diff --git a/src/components/CrossFadeImages.tsx b/src/components/CrossFadeImages.tsx index adc1c75..78a72c1 100644 --- a/src/components/CrossFadeImages.tsx +++ b/src/components/CrossFadeImages.tsx @@ -70,6 +70,7 @@ const CrossFadeImages: React.FC = ({ $duration={len * SINGLE_IMAGE_TIME} > { images.map((image, idx) => ( + // eslint-disable-next-line react/no-array-index-key
0 ? "not-first" : undefined}> ( <> - + diff --git a/src/components/Widgets/Checkbox/Checkboxes.tsx b/src/components/Widgets/Checkbox/Checkboxes.tsx index 539a9f3..e37bca8 100644 --- a/src/components/Widgets/Checkbox/Checkboxes.tsx +++ b/src/components/Widgets/Checkbox/Checkboxes.tsx @@ -16,7 +16,7 @@ const selectValue = (value, selected, all) => { const deselectValue = (value, selected) => selected.filter((v) => v !== value); type CheckboxesProps = Omit & { - options: any; + options: Record; }; const CheckboxContainer = styled.div` @@ -30,12 +30,13 @@ const Checkboxes: React.FC = ({ return (
{enumOptions.map((option, index) => { + const key = `${id}_${index}`; const checked = value.indexOf(option.value) !== -1; const itemDisabled = enumDisabled && enumDisabled.indexOf(option.value) !== -1; const disabledCls = disabled || itemDisabled || readonly ? "disabled" : ""; const checkbox = ( = ({ ); return inline ? ( -