|
|
|
@ -1,5 +1,6 @@
|
|
|
|
|
import { List as ImmutableList, Map as ImmutableMap, Record as ImmutableRecord, fromJS } from 'immutable';
|
|
|
|
|
import { Map as ImmutableMap, fromJS } from 'immutable';
|
|
|
|
|
import trim from 'lodash/trim';
|
|
|
|
|
import { create, Draft } from 'mutative';
|
|
|
|
|
import { applicationSchema, PlApiClient, tokenSchema, type CredentialAccount, type CredentialApplication, type Token } from 'pl-api';
|
|
|
|
|
import * as v from 'valibot';
|
|
|
|
|
|
|
|
|
@ -26,22 +27,25 @@ import type { AnyAction } from 'redux';
|
|
|
|
|
|
|
|
|
|
const backendUrl = (isURL(BuildConfig.BACKEND_URL) ? BuildConfig.BACKEND_URL : '');
|
|
|
|
|
|
|
|
|
|
const AuthUserRecord = ImmutableRecord({
|
|
|
|
|
access_token: '',
|
|
|
|
|
id: '',
|
|
|
|
|
url: '',
|
|
|
|
|
const authUserSchema = v.object({
|
|
|
|
|
access_token: v.string(),
|
|
|
|
|
id: v.string(),
|
|
|
|
|
url: v.string(),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const ReducerRecord = ImmutableRecord({
|
|
|
|
|
app: null as CredentialApplication | null,
|
|
|
|
|
tokens: ImmutableMap<string, Token>(),
|
|
|
|
|
users: ImmutableMap<string, AuthUser>(),
|
|
|
|
|
me: null as string | null,
|
|
|
|
|
client: new PlApiClient(backendUrl),
|
|
|
|
|
});
|
|
|
|
|
interface AuthUser {
|
|
|
|
|
access_token: string;
|
|
|
|
|
id: string;
|
|
|
|
|
url: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type AuthUser = ReturnType<typeof AuthUserRecord>;
|
|
|
|
|
type State = ReturnType<typeof ReducerRecord>;
|
|
|
|
|
interface State {
|
|
|
|
|
app: CredentialApplication | null;
|
|
|
|
|
tokens: Record<string, Token>;
|
|
|
|
|
users: Record<string, AuthUser>;
|
|
|
|
|
me: string | null;
|
|
|
|
|
client: InstanceType<typeof PlApiClient>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const buildKey = (parts: string[]) => parts.join(':');
|
|
|
|
|
|
|
|
|
@ -56,15 +60,15 @@ const getSessionUser = () => {
|
|
|
|
|
return validId(id) ? id : undefined;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const getLocalState = () => {
|
|
|
|
|
const getLocalState = (): State | undefined => {
|
|
|
|
|
const state = JSON.parse(localStorage.getItem(STORAGE_KEY)!);
|
|
|
|
|
|
|
|
|
|
if (!state) return undefined;
|
|
|
|
|
|
|
|
|
|
return ReducerRecord({
|
|
|
|
|
return ({
|
|
|
|
|
app: state.app && v.parse(applicationSchema, state.app),
|
|
|
|
|
tokens: ImmutableMap(Object.entries(state.tokens).map(([key, value]) => [key, v.parse(tokenSchema, value)])),
|
|
|
|
|
users: ImmutableMap(Object.entries(state.users).map(([key, value]) => [key, AuthUserRecord(value as any)])),
|
|
|
|
|
tokens: Object.fromEntries(Object.entries(state.tokens).map(([key, value]) => [key, v.parse(tokenSchema, value)])),
|
|
|
|
|
users: Object.fromEntries(Object.entries(state.users).map(([key, value]) => [key, v.parse(authUserSchema, value)])),
|
|
|
|
|
me: state.me,
|
|
|
|
|
client: new PlApiClient(parseBaseURL(state.me) || backendUrl, state.users[state.me]?.access_token),
|
|
|
|
|
});
|
|
|
|
@ -83,44 +87,45 @@ const validUser = (user?: AuthUser) => {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Finds the first valid user in the state
|
|
|
|
|
const firstValidUser = (state: State) => state.users.find(validUser);
|
|
|
|
|
const firstValidUser = (state: State | Draft<State>) => Object.values(state.users).find(validUser);
|
|
|
|
|
|
|
|
|
|
// For legacy purposes. IDs get upgraded to URLs further down.
|
|
|
|
|
const getUrlOrId = (user?: AuthUser): string | null => {
|
|
|
|
|
try {
|
|
|
|
|
const { id, url } = user!.toJS();
|
|
|
|
|
return (url || id) as string;
|
|
|
|
|
if (!user) return null;
|
|
|
|
|
const { id, url } = user;
|
|
|
|
|
return (url || id);
|
|
|
|
|
} catch {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// If `me` doesn't match an existing user, attempt to shift it.
|
|
|
|
|
const maybeShiftMe = (state: State) => {
|
|
|
|
|
const maybeShiftMe = (state: State | Draft<State>) => {
|
|
|
|
|
const me = state.me!;
|
|
|
|
|
const user = state.users.get(me);
|
|
|
|
|
const user = state.users[me];
|
|
|
|
|
|
|
|
|
|
if (!validUser(user)) {
|
|
|
|
|
const nextUser = firstValidUser(state);
|
|
|
|
|
return state.set('me', getUrlOrId(nextUser));
|
|
|
|
|
state.me = getUrlOrId(nextUser);
|
|
|
|
|
} else {
|
|
|
|
|
return state;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Set the user from the session or localStorage, whichever is valid first
|
|
|
|
|
const setSessionUser = (state: State) => state.update('me', me => {
|
|
|
|
|
const user = ImmutableList<AuthUser>([
|
|
|
|
|
state.users.get(sessionUser!)!,
|
|
|
|
|
state.users.get(me!)!,
|
|
|
|
|
]).find(validUser);
|
|
|
|
|
const setSessionUser = (state: State) => {
|
|
|
|
|
const me = getUrlOrId([
|
|
|
|
|
state.users[sessionUser!]!,
|
|
|
|
|
state.users[state.me!]!,
|
|
|
|
|
].find(validUser));
|
|
|
|
|
|
|
|
|
|
return getUrlOrId(user);
|
|
|
|
|
});
|
|
|
|
|
state.me = me;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const isUpgradingUrlId = (state: State) => {
|
|
|
|
|
const me = state.me;
|
|
|
|
|
const user = state.users.get(me!);
|
|
|
|
|
const user = state.users[me!];
|
|
|
|
|
return validId(me) && user && !isURL(me);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@ -129,24 +134,17 @@ const sanitizeState = (state: State) => {
|
|
|
|
|
// Skip sanitation during ID to URL upgrade
|
|
|
|
|
if (isUpgradingUrlId(state)) return state;
|
|
|
|
|
|
|
|
|
|
return state.withMutations(state => {
|
|
|
|
|
// Remove invalid users, ensure ID match
|
|
|
|
|
state.update('users', users => (
|
|
|
|
|
users.filter((user, url) => (
|
|
|
|
|
validUser(user) && user.get('url') === url
|
|
|
|
|
))
|
|
|
|
|
));
|
|
|
|
|
// Remove mismatched tokens
|
|
|
|
|
state.update('tokens', tokens => (
|
|
|
|
|
tokens.filter((token, id) => (
|
|
|
|
|
validId(id) && token.access_token === id
|
|
|
|
|
))
|
|
|
|
|
));
|
|
|
|
|
});
|
|
|
|
|
state.users = Object.fromEntries(Object.entries(state.users).filter(([url, user]) => (
|
|
|
|
|
validUser(user) && user.url === url
|
|
|
|
|
)));
|
|
|
|
|
// Remove mismatched tokens
|
|
|
|
|
state.tokens = Object.fromEntries(Object.entries(state.tokens).filter(([id, token]) => (
|
|
|
|
|
validId(id) && token.access_token === id
|
|
|
|
|
)));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const persistAuth = (state: State) => {
|
|
|
|
|
const { client, ...data } = state.toJS();
|
|
|
|
|
const { client, ...data } = state;
|
|
|
|
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@ -162,103 +160,102 @@ const persistState = (state: State) => {
|
|
|
|
|
persistSession(state);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const initialize = (state: State) =>
|
|
|
|
|
state.withMutations(state => {
|
|
|
|
|
maybeShiftMe(state);
|
|
|
|
|
setSessionUser(state);
|
|
|
|
|
sanitizeState(state);
|
|
|
|
|
persistState(state);
|
|
|
|
|
});
|
|
|
|
|
const initialize = (state: State) => {
|
|
|
|
|
maybeShiftMe(state);
|
|
|
|
|
setSessionUser(state);
|
|
|
|
|
sanitizeState(state);
|
|
|
|
|
persistState(state);
|
|
|
|
|
|
|
|
|
|
const initialState = initialize(ReducerRecord().merge(localState as any));
|
|
|
|
|
return state;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const importToken = (state: State, token: Token) =>
|
|
|
|
|
state.setIn(['tokens', token.access_token], token);
|
|
|
|
|
const initialState: State = initialize({
|
|
|
|
|
app: null,
|
|
|
|
|
tokens: {},
|
|
|
|
|
users: {},
|
|
|
|
|
me: null,
|
|
|
|
|
client: new PlApiClient(backendUrl),
|
|
|
|
|
...localState,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const importToken = (state: State | Draft<State>, token: Token) => {
|
|
|
|
|
state.tokens[token.access_token] = token;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Users are now stored by their ActivityPub ID instead of their
|
|
|
|
|
// primary key to support auth against multiple hosts.
|
|
|
|
|
const upgradeNonUrlId = (state: State, account: CredentialAccount) => {
|
|
|
|
|
const upgradeNonUrlId = (state: State | Draft<State>, account: CredentialAccount) => {
|
|
|
|
|
const me = state.me;
|
|
|
|
|
if (isURL(me)) return state;
|
|
|
|
|
|
|
|
|
|
return state.withMutations(state => {
|
|
|
|
|
state.update('me', me => me === account.id ? account.url : me);
|
|
|
|
|
state.deleteIn(['users', account.id]);
|
|
|
|
|
});
|
|
|
|
|
state.me = state.me === account.id ? account.url : state.me;
|
|
|
|
|
delete state.users[account.id];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Returns a predicate function for filtering a mismatched user/token
|
|
|
|
|
const userMismatch = (token: string, account: CredentialAccount) =>
|
|
|
|
|
(user: AuthUser, url: string) => {
|
|
|
|
|
const sameToken = user.get('access_token') === token;
|
|
|
|
|
const differentUrl = url !== account.url || user.get('url') !== account.url;
|
|
|
|
|
const differentId = user.get('id') !== account.id;
|
|
|
|
|
const sameToken = user.access_token === token;
|
|
|
|
|
const differentUrl = url !== account.url || user.url !== account.url;
|
|
|
|
|
const differentId = user.id !== account.id;
|
|
|
|
|
|
|
|
|
|
return sameToken && (differentUrl || differentId);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const importCredentials = (state: State, token: string, account: CredentialAccount) =>
|
|
|
|
|
state.withMutations(state => {
|
|
|
|
|
state.setIn(['users', account.url], AuthUserRecord({
|
|
|
|
|
id: account.id,
|
|
|
|
|
access_token: token,
|
|
|
|
|
url: account.url,
|
|
|
|
|
}));
|
|
|
|
|
state.setIn(['tokens', token, 'account'], account.id);
|
|
|
|
|
state.setIn(['tokens', token, 'me'], account.url);
|
|
|
|
|
state.update('users', users => users.filterNot(userMismatch(token, account)));
|
|
|
|
|
state.update('client', client =>
|
|
|
|
|
state.me
|
|
|
|
|
? client
|
|
|
|
|
: client.baseURL === parseBaseURL(account.url)
|
|
|
|
|
? (client.accessToken = token, client)
|
|
|
|
|
: new PlApiClient(parseBaseURL(account.url) || backendUrl, token),
|
|
|
|
|
);
|
|
|
|
|
state.update('me', me => me || account.url);
|
|
|
|
|
upgradeNonUrlId(state, account);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const deleteToken = (state: State, token: string) =>
|
|
|
|
|
state.withMutations(state => {
|
|
|
|
|
state.update('tokens', tokens => tokens.delete(token));
|
|
|
|
|
state.update('users', users => users.filterNot(user => user.get('access_token') === token));
|
|
|
|
|
maybeShiftMe(state);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const deleteUser = (state: State, account: Pick<AccountEntity, 'url'>) => {
|
|
|
|
|
const accountUrl = account.url;
|
|
|
|
|
|
|
|
|
|
return state.withMutations(state => {
|
|
|
|
|
state.update('users', users => users.delete(accountUrl));
|
|
|
|
|
state.update('tokens', tokens => tokens.filterNot(token => token.me === accountUrl));
|
|
|
|
|
maybeShiftMe(state);
|
|
|
|
|
const importCredentials = (state: State | Draft<State>, token: string, account: CredentialAccount) => {
|
|
|
|
|
state.users[account.url] = v.parse(authUserSchema, {
|
|
|
|
|
id: account.id,
|
|
|
|
|
access_token: token,
|
|
|
|
|
url: account.url,
|
|
|
|
|
});
|
|
|
|
|
// state.tokens[token].account = account.id;
|
|
|
|
|
state.tokens[token].me = account.url;
|
|
|
|
|
state.users = Object.fromEntries(Object.entries(state.users).filter(([url, user]) => userMismatch(token, account)(user, url)));
|
|
|
|
|
if (!state.me) {
|
|
|
|
|
if (state.client.baseURL === parseBaseURL(account.url)) state.client.accessToken = token;
|
|
|
|
|
else state.client = new PlApiClient(parseBaseURL(account.url) || backendUrl, token);
|
|
|
|
|
}
|
|
|
|
|
state.me = state.me || account.url;
|
|
|
|
|
upgradeNonUrlId(state, account);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const importMastodonPreload = (state: State, data: ImmutableMap<string, any>) =>
|
|
|
|
|
state.withMutations(state => {
|
|
|
|
|
const accountId = data.getIn(['meta', 'me']) as string;
|
|
|
|
|
const accountUrl = data.getIn(['accounts', accountId, 'url']) as string;
|
|
|
|
|
const accessToken = data.getIn(['meta', 'access_token']) as string;
|
|
|
|
|
const deleteToken = (state: State | Draft<State>, token: string) => {
|
|
|
|
|
delete state.tokens[token];
|
|
|
|
|
state.users = Object.fromEntries(Object.entries(state.users).filter(([_, user]) => user.access_token !== token));
|
|
|
|
|
maybeShiftMe(state);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (validId(accessToken) && validId(accountId) && isURL(accountUrl)) {
|
|
|
|
|
state.setIn(['tokens', accessToken], v.parse(tokenSchema, {
|
|
|
|
|
access_token: accessToken,
|
|
|
|
|
account: accountId,
|
|
|
|
|
me: accountUrl,
|
|
|
|
|
scope: 'read write follow push',
|
|
|
|
|
token_type: 'Bearer',
|
|
|
|
|
}));
|
|
|
|
|
const deleteUser = (state: State | Draft<State>, account: Pick<AccountEntity, 'url'>) => {
|
|
|
|
|
const accountUrl = account.url;
|
|
|
|
|
|
|
|
|
|
state.setIn(['users', accountUrl], AuthUserRecord({
|
|
|
|
|
id: accountId,
|
|
|
|
|
access_token: accessToken,
|
|
|
|
|
url: accountUrl,
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
delete state.users[accountUrl];
|
|
|
|
|
state.tokens = Object.fromEntries(Object.entries(state.tokens).filter(([_, token]) => token.me !== accountUrl));
|
|
|
|
|
maybeShiftMe(state);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
maybeShiftMe(state);
|
|
|
|
|
});
|
|
|
|
|
const importMastodonPreload = (state: State | Draft<State>, data: ImmutableMap<string, any>) => {
|
|
|
|
|
const accountId = data.getIn(['meta', 'me']) as string;
|
|
|
|
|
const accountUrl = data.getIn(['accounts', accountId, 'url']) as string;
|
|
|
|
|
const accessToken = data.getIn(['meta', 'access_token']) as string;
|
|
|
|
|
|
|
|
|
|
if (validId(accessToken) && validId(accountId) && isURL(accountUrl)) {
|
|
|
|
|
state.tokens[accessToken] = v.parse(tokenSchema, {
|
|
|
|
|
access_token: accessToken,
|
|
|
|
|
account: accountId,
|
|
|
|
|
me: accountUrl,
|
|
|
|
|
scope: 'read write follow push',
|
|
|
|
|
token_type: 'Bearer',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
state.users[accountUrl] = v.parse(authUserSchema, {
|
|
|
|
|
id: accountId,
|
|
|
|
|
access_token: accessToken,
|
|
|
|
|
url: accountUrl,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
maybeShiftMe(state);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const persistAuthAccount = (account: CredentialAccount) => {
|
|
|
|
|
const persistedAccount = { ...account };
|
|
|
|
@ -276,7 +273,7 @@ const persistAuthAccount = (account: CredentialAccount) => {
|
|
|
|
|
return persistedAccount;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const deleteForbiddenToken = (state: State, error: { response: PlfeResponse }, token: string) => {
|
|
|
|
|
const deleteForbiddenToken = (state: State | Draft<State>, error: { response: PlfeResponse }, token: string) => {
|
|
|
|
|
if ([401, 403].includes(error.response?.status!)) {
|
|
|
|
|
return deleteToken(state, token);
|
|
|
|
|
} else {
|
|
|
|
@ -284,32 +281,49 @@ const deleteForbiddenToken = (state: State, error: { response: PlfeResponse }, t
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const reducer = (state: State, action: AnyAction | AuthAction | MeAction | PreloadAction) => {
|
|
|
|
|
const reducer = (state: State, action: AnyAction | AuthAction | MeAction | PreloadAction): State => {
|
|
|
|
|
switch (action.type) {
|
|
|
|
|
case AUTH_APP_CREATED:
|
|
|
|
|
return state.set('app', action.app);
|
|
|
|
|
return create(state, (draft) => {
|
|
|
|
|
draft.app = action.app;
|
|
|
|
|
});
|
|
|
|
|
case AUTH_APP_AUTHORIZED:
|
|
|
|
|
return state.update('app', app => ({ ...app, ...action.token }));
|
|
|
|
|
return create(state, (draft) => {
|
|
|
|
|
draft.app = ({ ...draft.app, ...action.token });
|
|
|
|
|
});
|
|
|
|
|
case AUTH_LOGGED_IN:
|
|
|
|
|
return importToken(state, action.token);
|
|
|
|
|
return create(state, (draft) => {
|
|
|
|
|
importToken(draft, action.token);
|
|
|
|
|
});
|
|
|
|
|
case AUTH_LOGGED_OUT:
|
|
|
|
|
return deleteUser(state, action.account);
|
|
|
|
|
return create(state, (draft) => {
|
|
|
|
|
deleteUser(draft, action.account);
|
|
|
|
|
});
|
|
|
|
|
case VERIFY_CREDENTIALS_SUCCESS:
|
|
|
|
|
return importCredentials(state, action.token, persistAuthAccount(action.account));
|
|
|
|
|
return create(state, (draft) => {
|
|
|
|
|
importCredentials(draft, action.token, persistAuthAccount(action.account));
|
|
|
|
|
});
|
|
|
|
|
case VERIFY_CREDENTIALS_FAIL:
|
|
|
|
|
return deleteForbiddenToken(state, action.error, action.token);
|
|
|
|
|
return create(state, (draft) => {
|
|
|
|
|
deleteForbiddenToken(draft, action.error, action.token);
|
|
|
|
|
});
|
|
|
|
|
case SWITCH_ACCOUNT:
|
|
|
|
|
return state
|
|
|
|
|
.set('me', action.account.url)
|
|
|
|
|
.update('client', client =>
|
|
|
|
|
client.baseURL === parseBaseURL(action.account.url)
|
|
|
|
|
? (client.accessToken = action.account.access_token, client)
|
|
|
|
|
: new PlApiClient(parseBaseURL(action.account.url) || backendUrl, action.account.access_token),
|
|
|
|
|
);
|
|
|
|
|
return create(state, (draft) => {
|
|
|
|
|
draft.me = action.account.url;
|
|
|
|
|
if (draft.client.baseURL === parseBaseURL(action.account.url)) {
|
|
|
|
|
draft.client.accessToken = action.account.access_token;
|
|
|
|
|
} else {
|
|
|
|
|
draft.client = new PlApiClient(parseBaseURL(action.account.url) || backendUrl, action.account.access_token);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
case ME_FETCH_SKIP:
|
|
|
|
|
return state.set('me', null);
|
|
|
|
|
return create(state, (draft) => {
|
|
|
|
|
draft.me = null;
|
|
|
|
|
});
|
|
|
|
|
case MASTODON_PRELOAD_IMPORT:
|
|
|
|
|
return importMastodonPreload(state, fromJS(action.data) as ImmutableMap<string, any>);
|
|
|
|
|
return create(state, (draft) => {
|
|
|
|
|
importMastodonPreload(draft, fromJS<ImmutableMap<string, any>>(action.data));
|
|
|
|
|
});
|
|
|
|
|
default:
|
|
|
|
|
return state;
|
|
|
|
|
}
|
|
|
|
@ -330,7 +344,7 @@ const userSwitched = (oldState: State, state: State) => {
|
|
|
|
|
|
|
|
|
|
const stillValid = validMe(oldState) && validMe(state);
|
|
|
|
|
const didChange = oldMe !== me;
|
|
|
|
|
const userUpgradedUrl = state.users.get(me!)?.id === oldMe;
|
|
|
|
|
const userUpgradedUrl = state.users[me!]?.id === oldMe;
|
|
|
|
|
|
|
|
|
|
return stillValid && didChange && !userUpgradedUrl;
|
|
|
|
|
};
|
|
|
|
@ -344,10 +358,10 @@ const maybeReload = (oldState: State, state: State, action: AnyAction) => {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const auth = (oldState: State = initialState, action: AnyAction) => {
|
|
|
|
|
const auth = (oldState: State = initialState, action: AnyAction): State => {
|
|
|
|
|
const state = reducer(oldState, action);
|
|
|
|
|
|
|
|
|
|
if (!state.equals(oldState)) {
|
|
|
|
|
if (state !== oldState) {
|
|
|
|
|
// Persist the state in localStorage
|
|
|
|
|
persistAuth(state);
|
|
|
|
|
|
|
|
|
@ -367,8 +381,4 @@ const auth = (oldState: State = initialState, action: AnyAction) => {
|
|
|
|
|
return state;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export {
|
|
|
|
|
AuthUserRecord,
|
|
|
|
|
ReducerRecord,
|
|
|
|
|
auth as default,
|
|
|
|
|
};
|
|
|
|
|
export { auth as default };
|
|
|
|
|