feat: :art: Expose public REST API endpoints #203
jump to
@@ -1,5 +1,5 @@
{ "latest": "3.6.8", - "lastUpdateCheck": 1634735452497, + "lastUpdateCheck": 1635235488108, "lastNotification": 1634735452492 }
@@ -5,7 +5,7 @@ "method": "GET",
"path": "/cars", "handler": "car.find", "config": { - "policies": [] + "policies": ["plugins::users-permissions.ratelimit"] } }, {@@ -21,7 +21,7 @@ "method": "GET",
"path": "/cars/:id", "handler": "car.findOne", "config": { - "policies": [] + "policies": ["plugins::users-permissions.ratelimit"] } }, {@@ -29,7 +29,7 @@ "method": "POST",
"path": "/cars", "handler": "car.create", "config": { - "policies": [] + "policies": ["plugins::users-permissions.ratelimit"] } }, {@@ -37,7 +37,7 @@ "method": "PUT",
"path": "/cars/:id", "handler": "car.update", "config": { - "policies": [] + "policies": ["plugins::users-permissions.ratelimit"] } }, {@@ -45,7 +45,7 @@ "method": "DELETE",
"path": "/cars/:id", "handler": "car.delete", "config": { - "policies": [] + "policies": ["plugins::users-permissions.ratelimit"] } } ]
@@ -1,8 +1,20 @@
'use strict'; -/** - * Read the documentation (https://strapi.io/documentation/v3.x/concepts/controllers.html#core-controllers) - * to customize this controller - */ +module.exports = { + async create(ctx) { + const car = await strapi.services.car.create(ctx.request.body); + return strapi.services.car.sanitize(car); + }, -module.exports = {}; + async update(ctx) { + const {id} = ctx.params; + const car = await strapi.services.car.update({id}, ctx.request.body); + return strapi.services.car.sanitize(car); + }, + + async delete(ctx) { + const {id} = ctx.params; + const car = await strapi.services.car.delete({id}); + return strapi.services.car.sanitize(car); + }, +};
@@ -1,8 +1,25 @@
'use strict'; +const _pick = require('lodash/pick'); -/** - * Read the documentation (https://strapi.io/documentation/v3.x/concepts/services.html#core-services) - * to customize this service - */ +const PUBLIC_FIELDS = [ + 'id', + 'name', + 'seats', + 'meeting', + 'departure', + 'phone_number', + 'details', + 'passengers', + 'created_at', + 'updated_at', +]; -module.exports = {}; +module.exports = { + sanitize: car => { + const passengers = car?.passengers?.map(passenger => + _pick(passenger, ['id', 'name']) + ); + const sanitizedCar = _pick(car, PUBLIC_FIELDS); + return {...sanitizedCar, passengers}; + }, +};
@@ -2,26 +2,10 @@ {
"routes": [ { "method": "GET", - "path": "/events", - "handler": "event.find", - "config": { - "policies": [] - } - }, - { - "method": "GET", - "path": "/events/count", - "handler": "event.count", - "config": { - "policies": [] - } - }, - { - "method": "GET", - "path": "/events/:id", + "path": "/events/:uuid", "handler": "event.findOne", "config": { - "policies": [] + "policies": ["plugins::users-permissions.ratelimit"] } }, {@@ -29,23 +13,15 @@ "method": "POST",
"path": "/events", "handler": "event.create", "config": { - "policies": [] + "policies": ["plugins::users-permissions.ratelimit"] } }, { "method": "PUT", - "path": "/events/:id", + "path": "/events/:uuid", "handler": "event.update", "config": { - "policies": [] - } - }, - { - "method": "DELETE", - "path": "/events/:id", - "handler": "event.delete", - "config": { - "policies": [] + "policies": ["plugins::users-permissions.ratelimit"] } } ]
@@ -3,16 +3,34 @@ definition: `
extend input EventInput { newsletter: Boolean } + + input updateEventByUUIDInput { + where: InputUUID + data: editEventInput + } + + input InputUUID { + uuid: String! + } `, query: ` eventByUUID(uuid: String!): Event `, + mutation: ` + updateEventByUUID(input: updateEventByUUIDInput): updateEventPayload + `, type: {}, resolver: { Query: { eventByUUID: { description: 'Retrieve an event using its UUID', - resolver: 'application::event.event.getByUUID', + resolver: 'application::event.event.findOne', + }, + }, + Mutation: { + updateEventByUUID: { + description: 'Update an event using its UUID', + resolver: 'application::event.event.update', }, }, },
@@ -1,12 +1,10 @@
'use strict'; -const {sanitizeEntity} = require('strapi-utils'); - module.exports = { - async getByUUID(ctx) { - const {_uuid} = ctx.params; - const event = await strapi.services.event.findOne({uuid: _uuid}); - return sanitizeEntity(event, {model: strapi.models.event}); + async findOne(ctx) { + const uuid = ctx.params._uuid || ctx.params.uuid; + const event = await strapi.services.event.findOne({uuid}); + return strapi.services.event.sanitize(event); }, async create(ctx) {@@ -16,6 +14,17 @@
if (user) event = {...event, users: [user.id]}; const entity = await strapi.services.event.create(event); - return sanitizeEntity(entity, {model: strapi.models.event}); + return strapi.services.event.sanitize(entity); + }, + + async update(ctx) { + const uuid = ctx.params._uuid || ctx.params.uuid; + const eventUpdate = ctx.request.body; + + const updatedEvent = await strapi.services.event.update( + {uuid}, + eventUpdate + ); + return strapi.services.event.sanitize(updatedEvent); }, };
@@ -1,11 +1,34 @@
'use strict'; const moment = require('moment'); +const _pick = require('lodash/pick'); const TEMPLATE_NAME_RECAP = 'event_recap'; +const PUBLIC_FIELDS = [ + 'uuid', + 'name', + 'email', + 'id', + 'date', + 'address', + 'position', + 'waitingList', + 'cars', + 'created_at', + 'updated_at', +]; const {STRAPI_URL = ''} = process.env; module.exports = { + sanitize: event => { + const cars = event?.cars?.map(strapi.services.car.sanitize); + const waitingList = event?.waitingList?.map(list => + _pick(list, ['id', 'name']) + ); + const sanitizedEvent = _pick(event, PUBLIC_FIELDS); + return {...sanitizedEvent, cars, waitingList}; + }, + sendDailyRecap: async event => { const referenceDate = moment().subtract(1, 'day'); const hasBeenModified = referenceDate.isSameOrBefore(event.updated_at);
@@ -6,11 +6,11 @@ "type": "application",
"controllers": [ { "name": "car", - "actions": ["create", "delete", "find", "findone", "update"] + "actions": ["create", "delete", "update"] }, { "name": "event", - "actions": ["create", "findone", "update", "getbyuuid"] + "actions": ["create", "update", "findone"] }, { "name": "page",@@ -29,11 +29,11 @@ "type": "application",
"controllers": [ { "name": "car", - "actions": ["create", "delete", "find", "findone", "update"] + "actions": ["create", "delete", "update"] }, { "name": "event", - "actions": ["create", "findone", "update", "getbyuuid"] + "actions": ["create", "update", "findone"] }, { "name": "page",
@@ -64,7 +64,7 @@ const lostPassengers = car.passengers.slice(seats);
if (lostPassengers.length > 0) await updateEvent({ variables: { - id: event.id, + uuid: event.uuid, eventUpdate: { waiting_list: [ ...(event.waiting_list ?? []),@@ -106,7 +106,7 @@ // Put passengers in event waiting list (if any)
if (Array.isArray(car?.passengers) && car.passengers.length > 0) await updateEvent({ variables: { - id: event.id, + uuid: event.uuid, eventUpdate: { waiting_list: [...(event.waiting_list ?? []), ...car.passengers], },
@@ -67,7 +67,7 @@ item => item.id !== passengerId
); await updateEvent({ variables: { - id: event.id, + uuid: event.uuid, eventUpdate: { waitingList, },@@ -80,6 +80,7 @@ carUpdate: {
passengers, }, }, + refetchQueries: ['eventByUUID'], }); } catch (error) { console.error(error);
@@ -44,7 +44,7 @@ id={`${idPrefix}Date`}
fullWidth label={t('event.creation.date')} value={event.date} - onChange={date => setEventUpdate({date})} + onChange={e => setEventUpdate({date: e.target.value})} name="date" type="date" InputLabelProps={{shrink: true}}
@@ -45,7 +45,7 @@ const waitingList = [...event.waitingList, passenger].map(
({__typename, ...item}) => item ); await updateEvent({ - variables: {id: event.id, eventUpdate: {waitingList}}, + variables: {uuid: event.uuid, eventUpdate: {waitingList}}, }); addToEvent(event.id); } catch (error) {@@ -63,7 +63,7 @@ const waitingList = event.waitingList
.filter((_, idx) => idx !== passengerIndex) .map(({__typename, ...item}) => item); await updateEvent({ - variables: {id: event.id, eventUpdate: {waitingList}}, + variables: {uuid: event.uuid, eventUpdate: {waitingList}}, }); addToEvent(event.id); } catch (error) {@@ -94,7 +94,7 @@ .filter(item => item.id !== id)
.map(({__typename, ...item}) => item); await updateEvent({ variables: { - id: event.id, + uuid: event.uuid, eventUpdate: { waitingList, },
@@ -196,6 +196,11 @@ export enum Enum_Page_Type {
Tos = 'tos' } +export enum Enum_Userspermissionsuser_Lang { + Fr = 'FR', + En = 'EN' +} + export type EmailDesignerEmailTemplate = { __typename?: 'EmailDesignerEmailTemplate'; id: Scalars['ID'];@@ -399,9 +404,13 @@ export type InputId = {
id: Scalars['ID']; }; +export type InputUuid = { + uuid: Scalars['String']; +}; -export type Morph = Dependency | Info | UsersPermissionsMe | UsersPermissionsMeRole | UsersPermissionsLoginPayload | UserPermissionsPasswordPayload | Car | CarConnection | CarAggregator | CarAggregatorSum | CarAggregatorAvg | CarAggregatorMin | CarAggregatorMax | CarGroupBy | CarConnectionId | CarConnectionCreated_At | CarConnectionUpdated_At | CarConnectionName | CarConnectionSeats | CarConnectionMeeting | CarConnectionDeparture | CarConnectionPhone_Number | CarConnectionDetails | CarConnectionEvent | CreateCarPayload | UpdateCarPayload | DeleteCarPayload | Event | EventConnection | EventAggregator | EventGroupBy | EventConnectionId | EventConnectionCreated_At | EventConnectionUpdated_At | EventConnectionName | EventConnectionEmail | EventConnectionDate | EventConnectionAddress | EventConnectionPosition | EventConnectionUuid | CreateEventPayload | UpdateEventPayload | DeleteEventPayload | Page | PageConnection | PageAggregator | PageGroupBy | PageConnectionId | PageConnectionCreated_At | PageConnectionUpdated_At | PageConnectionName | PageConnectionContent | PageConnectionType | CreatePagePayload | UpdatePagePayload | DeletePagePayload | Settings | UpdateSettingPayload | DeleteSettingPayload | EmailDesignerEmailTemplate | UploadFile | UploadFileConnection | UploadFileAggregator | UploadFileAggregatorSum | UploadFileAggregatorAvg | UploadFileAggregatorMin | UploadFileAggregatorMax | UploadFileGroupBy | UploadFileConnectionId | UploadFileConnectionCreated_At | UploadFileConnectionUpdated_At | UploadFileConnectionName | UploadFileConnectionAlternativeText | UploadFileConnectionCaption | UploadFileConnectionWidth | UploadFileConnectionHeight | UploadFileConnectionFormats | UploadFileConnectionHash | UploadFileConnectionExt | UploadFileConnectionMime | UploadFileConnectionSize | UploadFileConnectionUrl | UploadFileConnectionPreviewUrl | UploadFileConnectionProvider | UploadFileConnectionProvider_Metadata | DeleteFilePayload | UsersPermissionsPermission | UsersPermissionsRole | UsersPermissionsRoleConnection | UsersPermissionsRoleAggregator | UsersPermissionsRoleGroupBy | UsersPermissionsRoleConnectionId | UsersPermissionsRoleConnectionName | UsersPermissionsRoleConnectionDescription | UsersPermissionsRoleConnectionType | CreateRolePayload | UpdateRolePayload | DeleteRolePayload | UsersPermissionsUser | UsersPermissionsUserConnection | UsersPermissionsUserAggregator | UsersPermissionsUserGroupBy | UsersPermissionsUserConnectionId | UsersPermissionsUserConnectionCreated_At | UsersPermissionsUserConnectionUpdated_At | UsersPermissionsUserConnectionUsername | UsersPermissionsUserConnectionFirstName | UsersPermissionsUserConnectionLastName | UsersPermissionsUserConnectionEmail | UsersPermissionsUserConnectionProvider | UsersPermissionsUserConnectionConfirmed | UsersPermissionsUserConnectionBlocked | UsersPermissionsUserConnectionRole | UsersPermissionsUserConnectionOnboardingUser | UsersPermissionsUserConnectionOnboardingCreator | CreateUserPayload | UpdateUserPayload | DeleteUserPayload | ComponentPassengerPassenger; + +export type Morph = Dependency | Info | UsersPermissionsMe | UsersPermissionsMeRole | UsersPermissionsLoginPayload | UserPermissionsPasswordPayload | Car | CarConnection | CarAggregator | CarAggregatorSum | CarAggregatorAvg | CarAggregatorMin | CarAggregatorMax | CarGroupBy | CarConnectionId | CarConnectionCreated_At | CarConnectionUpdated_At | CarConnectionName | CarConnectionSeats | CarConnectionMeeting | CarConnectionDeparture | CarConnectionPhone_Number | CarConnectionDetails | CarConnectionEvent | CreateCarPayload | UpdateCarPayload | DeleteCarPayload | Event | EventConnection | EventAggregator | EventGroupBy | EventConnectionId | EventConnectionCreated_At | EventConnectionUpdated_At | EventConnectionName | EventConnectionEmail | EventConnectionDate | EventConnectionAddress | EventConnectionPosition | EventConnectionUuid | CreateEventPayload | UpdateEventPayload | DeleteEventPayload | Page | PageConnection | PageAggregator | PageGroupBy | PageConnectionId | PageConnectionCreated_At | PageConnectionUpdated_At | PageConnectionName | PageConnectionContent | PageConnectionType | CreatePagePayload | UpdatePagePayload | DeletePagePayload | Settings | UpdateSettingPayload | DeleteSettingPayload | EmailDesignerEmailTemplate | UploadFile | UploadFileConnection | UploadFileAggregator | UploadFileAggregatorSum | UploadFileAggregatorAvg | UploadFileAggregatorMin | UploadFileAggregatorMax | UploadFileGroupBy | UploadFileConnectionId | UploadFileConnectionCreated_At | UploadFileConnectionUpdated_At | UploadFileConnectionName | UploadFileConnectionAlternativeText | UploadFileConnectionCaption | UploadFileConnectionWidth | UploadFileConnectionHeight | UploadFileConnectionFormats | UploadFileConnectionHash | UploadFileConnectionExt | UploadFileConnectionMime | UploadFileConnectionSize | UploadFileConnectionUrl | UploadFileConnectionPreviewUrl | UploadFileConnectionProvider | UploadFileConnectionProvider_Metadata | DeleteFilePayload | UsersPermissionsPermission | UsersPermissionsRole | UsersPermissionsRoleConnection | UsersPermissionsRoleAggregator | UsersPermissionsRoleGroupBy | UsersPermissionsRoleConnectionId | UsersPermissionsRoleConnectionName | UsersPermissionsRoleConnectionDescription | UsersPermissionsRoleConnectionType | CreateRolePayload | UpdateRolePayload | DeleteRolePayload | UsersPermissionsUser | UsersPermissionsUserConnection | UsersPermissionsUserAggregator | UsersPermissionsUserGroupBy | UsersPermissionsUserConnectionId | UsersPermissionsUserConnectionCreated_At | UsersPermissionsUserConnectionUpdated_At | UsersPermissionsUserConnectionUsername | UsersPermissionsUserConnectionFirstName | UsersPermissionsUserConnectionLastName | UsersPermissionsUserConnectionEmail | UsersPermissionsUserConnectionProvider | UsersPermissionsUserConnectionConfirmed | UsersPermissionsUserConnectionBlocked | UsersPermissionsUserConnectionRole | UsersPermissionsUserConnectionOnboardingUser | UsersPermissionsUserConnectionOnboardingCreator | UsersPermissionsUserConnectionLang | CreateUserPayload | UpdateUserPayload | DeleteUserPayload | ComponentPassengerPassenger; export type Mutation = { __typename?: 'Mutation';@@ -439,6 +448,7 @@ forgotPassword?: Maybe<UserPermissionsPasswordPayload>;
resetPassword?: Maybe<UsersPermissionsLoginPayload>; emailConfirmation?: Maybe<UsersPermissionsLoginPayload>; updateMe: UpdateUserPayload; + updateEventByUUID?: Maybe<UpdateEventPayload>; };@@ -581,6 +591,11 @@
export type MutationUpdateMeArgs = { input?: Maybe<EditUserInput>; +}; + + +export type MutationUpdateEventByUuidArgs = { + input?: Maybe<UpdateEventByUuidInput>; }; export type Page = {@@ -1074,6 +1089,7 @@ confirmationToken?: Maybe<Scalars['String']>;
events?: Maybe<Array<Maybe<Scalars['ID']>>>; onboardingUser?: Maybe<Scalars['Boolean']>; onboardingCreator?: Maybe<Scalars['Boolean']>; + lang?: Maybe<Enum_Userspermissionsuser_Lang>; created_by?: Maybe<Scalars['ID']>; updated_by?: Maybe<Scalars['ID']>; };@@ -1217,6 +1233,7 @@ blocked?: Maybe<Scalars['Boolean']>;
role?: Maybe<UsersPermissionsRole>; onboardingUser?: Maybe<Scalars['Boolean']>; onboardingCreator?: Maybe<Scalars['Boolean']>; + lang?: Maybe<Enum_Userspermissionsuser_Lang>; events?: Maybe<Array<Maybe<Event>>>; };@@ -1277,6 +1294,12 @@ key?: Maybe<Scalars['ID']>;
connection?: Maybe<UsersPermissionsUserConnection>; }; +export type UsersPermissionsUserConnectionLang = { + __typename?: 'UsersPermissionsUserConnectionLang'; + key?: Maybe<Scalars['String']>; + connection?: Maybe<UsersPermissionsUserConnection>; +}; + export type UsersPermissionsUserConnectionLastName = { __typename?: 'UsersPermissionsUserConnectionLastName'; key?: Maybe<Scalars['String']>;@@ -1334,6 +1357,7 @@ blocked?: Maybe<Array<Maybe<UsersPermissionsUserConnectionBlocked>>>;
role?: Maybe<Array<Maybe<UsersPermissionsUserConnectionRole>>>; onboardingUser?: Maybe<Array<Maybe<UsersPermissionsUserConnectionOnboardingUser>>>; onboardingCreator?: Maybe<Array<Maybe<UsersPermissionsUserConnectionOnboardingCreator>>>; + lang?: Maybe<Array<Maybe<UsersPermissionsUserConnectionLang>>>; }; export type CreateCarInput = {@@ -1546,6 +1570,7 @@ confirmationToken?: Maybe<Scalars['String']>;
events?: Maybe<Array<Maybe<Scalars['ID']>>>; onboardingUser?: Maybe<Scalars['Boolean']>; onboardingCreator?: Maybe<Scalars['Boolean']>; + lang?: Maybe<Enum_Userspermissionsuser_Lang>; created_by?: Maybe<Scalars['ID']>; updated_by?: Maybe<Scalars['ID']>; old_password?: Maybe<Scalars['String']>;@@ -1561,6 +1586,11 @@ __typename?: 'updateCarPayload';
car?: Maybe<Car>; }; +export type UpdateEventByUuidInput = { + where?: Maybe<InputUuid>; + data?: Maybe<EditEventInput>; +}; + export type UpdateEventInput = { where?: Maybe<InputId>; data?: Maybe<EditEventInput>;@@ -1782,14 +1812,14 @@ )> }
); export type UpdateEventMutationVariables = Exact<{ - id: Scalars['ID']; + uuid: Scalars['String']; eventUpdate?: Maybe<EditEventInput>; }>; export type UpdateEventMutation = ( { __typename?: 'Mutation' } - & { updateEvent?: Maybe<( + & { updateEventByUUID?: Maybe<( { __typename?: 'updateEventPayload' } & { event?: Maybe<( { __typename?: 'Event' }@@ -1798,19 +1828,6 @@ )> }
)> } ); -export type EventQueryVariables = Exact<{ - id: Scalars['ID']; -}>; - - -export type EventQuery = ( - { __typename?: 'Query' } - & { event?: Maybe<( - { __typename?: 'Event' } - & EventFieldsFragment - )> } -); - export type EventByUuidQueryVariables = Exact<{ uuid: Scalars['String']; }>;@@ -2238,8 +2255,8 @@ export type CreateEventMutationHookResult = ReturnType<typeof useCreateEventMutation>;
export type CreateEventMutationResult = Apollo.MutationResult<CreateEventMutation>; export type CreateEventMutationOptions = Apollo.BaseMutationOptions<CreateEventMutation, CreateEventMutationVariables>; export const UpdateEventDocument = gql` - mutation updateEvent($id: ID!, $eventUpdate: editEventInput) { - updateEvent(input: {where: {id: $id}, data: $eventUpdate}) { + mutation updateEvent($uuid: String!, $eventUpdate: editEventInput) { + updateEventByUUID(input: {where: {uuid: $uuid}, data: $eventUpdate}) { event { ...EventFields }@@ -2261,7 +2278,7 @@ *
* @example * const [updateEventMutation, { data, loading, error }] = useUpdateEventMutation({ * variables: { - * id: // value for 'id' + * uuid: // value for 'uuid' * eventUpdate: // value for 'eventUpdate' * }, * });@@ -2272,39 +2289,6 @@ }
export type UpdateEventMutationHookResult = ReturnType<typeof useUpdateEventMutation>; export type UpdateEventMutationResult = Apollo.MutationResult<UpdateEventMutation>; export type UpdateEventMutationOptions = Apollo.BaseMutationOptions<UpdateEventMutation, UpdateEventMutationVariables>; -export const EventDocument = gql` - query event($id: ID!) { - event(id: $id) { - ...EventFields - } -} - ${EventFieldsFragmentDoc}`; - -/** - * __useEventQuery__ - * - * To run a query within a React component, call `useEventQuery` and pass it any options that fit your needs. - * When your component renders, `useEventQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useEventQuery({ - * variables: { - * id: // value for 'id' - * }, - * }); - */ -export function useEventQuery(baseOptions: Apollo.QueryHookOptions<EventQuery, EventQueryVariables>) { - return Apollo.useQuery<EventQuery, EventQueryVariables>(EventDocument, baseOptions); - } -export function useEventLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<EventQuery, EventQueryVariables>) { - return Apollo.useLazyQuery<EventQuery, EventQueryVariables>(EventDocument, baseOptions); - } -export type EventQueryHookResult = ReturnType<typeof useEventQuery>; -export type EventLazyQueryHookResult = ReturnType<typeof useEventLazyQuery>; -export type EventQueryResult = Apollo.QueryResult<EventQuery, EventQueryVariables>; export const EventByUuidDocument = gql` query eventByUUID($uuid: String!) { eventByUUID(uuid: $uuid) {
@@ -49,17 +49,11 @@ }
} } -mutation updateEvent($id: ID!, $eventUpdate: editEventInput) { - updateEvent(input: {where: {id: $id}, data: $eventUpdate}) { +mutation updateEvent($uuid: String!, $eventUpdate: editEventInput) { + updateEventByUUID(input: {where: {uuid: $uuid}, data: $eventUpdate}) { event { ...EventFields } - } -} - -query event($id: ID!) { - event(id: $id) { - ...EventFields } }
@@ -55,11 +55,12 @@ }, [event]);
const onSave = async e => { try { - const {id, ...data} = eventUpdate; + const {uuid, ...data} = eventUpdate; + delete data.id; delete data.__typename; delete data.cars; delete data.waitingList; - await updateEvent({variables: {id, eventUpdate: data}}); + await updateEvent({variables: {uuid, eventUpdate: data}}); setIsEditing(false); } catch (error) { console.error(error);
@@ -18,7 +18,9 @@ if (hasStorage) localStorage.setItem('token', token);
set({token}); }, user: - hasStorage && localStorage.getItem('user') + hasStorage && + localStorage.getItem('user') && + localStorage.getItem('user') !== 'undefined' ? JSON.parse(localStorage.getItem('user')) : null, setUser: user => {