import envData from '../../../config/env.json'; import Tokens from 'csrf'; import cookies from 'browser-cookies'; import type { UserType } from '../redux/prop-types'; const { apiLocation } = envData; const base = apiLocation; const tokens = new Tokens(); const defaultOptions: RequestInit = { credentials: 'include' }; // _csrf is passed to the client as a cookie. Tokens are sent back to the server // via headers: function getCSRFToken() { const _csrf = typeof window !== 'undefined' && cookies.get('_csrf'); if (!_csrf) { return ''; } else { return tokens.create(_csrf); } } async function get(path: string): Promise { return fetch(`${base}${path}`, defaultOptions).then(res => res.json()); } export function post(path: string, body: unknown): Promise { return request('POST', path, body); } function put(path: string, body: unknown): Promise { return request('PUT', path, body); } async function request( method: 'POST' | 'PUT', path: string, body: unknown ): Promise { const options: RequestInit = { ...defaultOptions, method, headers: { 'CSRF-Token': getCSRFToken(), 'Content-Type': 'application/json' }, body: JSON.stringify(body) }; return fetch(`${base}${path}`, options).then(res => res.json()); } /** GET **/ interface SessionUser { user: UserType; sessionMeta: { activeDonations: number }; result: string; } export function getSessionUser(): Promise { return get('/user/get-session-user'); } export function getUserProfile(username: string): Promise { return get(`/api/users/get-public-profile?username=${username}`); } interface Cert { certTitle: string; username: string; date: Date; completionTime: string; } export function getShowCert(username: string, certSlug: string): Promise { return get(`/certificate/showCert/${username}/${certSlug}`); } export function getUsernameExists(username: string): Promise { return get(`/api/users/exists?username=${username}`); } // TODO: Does a GET return a bolean? export function getVerifyCanClaimCert( username: string, superBlock: string ): Promise { return get( `/certificate/verify-can-claim-cert?username=${username}&superBlock=${superBlock}` ); } /** POST **/ interface Donation { email: string; amount: number; duration: string; provider: string; subscriptionId: string; customerId: string; startDate: Date; } export function addDonation(body: Donation): Promise { return post('/donate/add-donation', body); } interface Report { username: string; reportDescription: string; } export function postReportUser(body: Report): Promise { return post('/user/report-user', body); } // Both are called without a payload in danger-zone-saga, // which suggests both are sent without any body // TODO: Convert to DELETE export function postDeleteAccount(): Promise { return post('/account/delete', {}); } export function postResetProgress(): Promise { return post('/account/reset-progress', {}); } /** PUT **/ interface MyAbout { name: string; location: string; about: string; picture: string; } export function putUpdateMyAbout(values: MyAbout): Promise { return put('/update-my-about', { ...values }); } export function putUpdateMyUsername(username: string): Promise { return put('/update-my-username', { username }); } export function putUpdateMyProfileUI( profileUI: UserType['profileUI'] ): Promise { return put('/update-my-profileui', { profileUI }); } // Update should contain only one flag and one new value // It's possible to constrain to only one key with TS, but is overkill for this // https://stackoverflow.com/a/60807986 export function putUpdateUserFlag( update: Record ): Promise { return put('/update-user-flag', update); } export function putUserAcceptsTerms(quincyEmails: boolean): Promise { return put('/update-privacy-terms', { quincyEmails }); } export function putUserUpdateEmail(email: string): Promise { return put('/update-my-email', { email }); } export function putVerifyCert(certSlug: string): Promise { return put('/certificate/verify', { certSlug }); }