// authentication.ts // Copyright (C) 2020 Dwayne Harris // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see . import { Action } from 'redux' import { apiFetch } from '../api' import { setEntities } from '../actions/entities' import { startRequest, finishRequest } from '../actions/requests' import { normalize } from '../utils/normalization' import { LOCAL_STORAGE_ACCESS_TOKEN_KEY, LOCAL_STORAGE_REFRESH_TOKEN_KEY, LOCAL_STORAGE_ACCESS_TOKEN_EXPIRES_AT_KEY, } from '../constants' import { AppThunkAction, Entity, RequestKey, EntityType, Settings } from '../types' export interface SetCheckedAction extends Action { type: 'AUTHENTICATION_SET_CHECKED' } export interface SetAuthenticatedAction extends Action { type: 'AUTHENTICATION_SET_AUTHENTICATED' payload: boolean } export interface SetUserAction extends Action { type: 'AUTHENTICATION_SET_USER' payload: string } export interface UnauthenticateAction extends Action { type: 'AUTHENTICATION_UNAUTHENTICATE' } export type AuthenticationActions = SetCheckedAction | SetAuthenticatedAction | SetUserAction | UnauthenticateAction export const setChecked = (): SetCheckedAction => ({ type: 'AUTHENTICATION_SET_CHECKED', }) export const setAuthenticated = (authenticated: boolean): SetAuthenticatedAction => ({ type: 'AUTHENTICATION_SET_AUTHENTICATED', payload: authenticated, }) export const setUser = (userId: string): SetUserAction => ({ type: 'AUTHENTICATION_SET_USER', payload: userId, }) export const unauthenticate = (): UnauthenticateAction => ({ type: 'AUTHENTICATION_UNAUTHENTICATE', }) export const fetchSelf = (): AppThunkAction => async dispatch => { dispatch(startRequest(RequestKey.FetchSelf)) try { const self = await apiFetch({ path: '/v1/self', }) const result = normalize([self], EntityType.User) dispatch(setEntities(result.entities)) dispatch(setUser(self.id)) dispatch(setAuthenticated(true)) dispatch(finishRequest(RequestKey.FetchSelf, true)) } catch (err) { dispatch(setAuthenticated(false)) dispatch(finishRequest(RequestKey.FetchSelf, false)) throw err } } interface AuthenticateResponse { id: string access: string refresh: string expires: number } export const authenticate = (name: string, password: string): AppThunkAction => async dispatch => { dispatch(startRequest(RequestKey.Authenticate)) try { const response = await apiFetch({ path: '/v1/authenticate', method: 'post', body: { id: name, password, }, }) localStorage.setItem(LOCAL_STORAGE_ACCESS_TOKEN_KEY, response.access) localStorage.setItem(LOCAL_STORAGE_REFRESH_TOKEN_KEY, response.refresh) if (response.expires) localStorage.setItem(LOCAL_STORAGE_ACCESS_TOKEN_EXPIRES_AT_KEY, response.expires.toString()) dispatch(finishRequest(RequestKey.Authenticate, true)) await dispatch(fetchSelf()) return response.id } catch (err) { dispatch(finishRequest(RequestKey.Authenticate, false)) throw err } } interface UpdateSelfOptions { name: string about: string requiresApproval: boolean privacy: string imageUrl: string coverImageUrl: string theme: string settings: Settings } export const updateSelf = (options: UpdateSelfOptions): AppThunkAction => async dispatch => { const { name, about, requiresApproval, privacy, imageUrl, coverImageUrl, theme, settings } = options dispatch(startRequest(RequestKey.UpdateSelf)) try { const self = await apiFetch({ path: '/v1/self', method: 'put', body: { name, about, requiresApproval, privacy, imageUrl, coverImageUrl, theme, settings, }, }) const result = normalize([self], EntityType.User) dispatch(setEntities(result.entities)) dispatch(setUser(self.id)) dispatch(setAuthenticated(true)) dispatch(finishRequest(RequestKey.UpdateSelf, true)) } catch (err) { dispatch(finishRequest(RequestKey.UpdateSelf, false)) throw err } }