parent
de7a4b6d52
commit
92fd933c60
@ -0,0 +1,17 @@ |
|||||||
|
export interface AuthenticationInterface { |
||||||
|
key: string |
||||||
|
token(): string |
||||||
|
} |
||||||
|
|
||||||
|
export class BearerAuthentication implements AuthenticationInterface { |
||||||
|
key = 'Authorization' |
||||||
|
_token: string |
||||||
|
|
||||||
|
constructor(token: string) { |
||||||
|
this._token = token |
||||||
|
} |
||||||
|
|
||||||
|
token(): string { |
||||||
|
return `Bearer ${this._token}`; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,6 @@ |
|||||||
|
import React from 'react' |
||||||
|
import {UseTokenInterface} from "../hooks/use-token" |
||||||
|
|
||||||
|
const Context = React.createContext({} as UseTokenInterface) |
||||||
|
export const Provider = Context.Provider |
||||||
|
export default Context |
@ -0,0 +1,120 @@ |
|||||||
|
|
||||||
|
|
||||||
|
export interface TokenData<T=any> { |
||||||
|
payload: T |
||||||
|
headers: Record<string, any> |
||||||
|
} |
||||||
|
|
||||||
|
const isClient = () => { |
||||||
|
return typeof window !== 'undefined' |
||||||
|
} |
||||||
|
|
||||||
|
export const storeToken = (token: string) => { |
||||||
|
if (!isClient()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
let tokenData = grabTokenData(token) |
||||||
|
let tokenId = tokenData?.payload?.id; |
||||||
|
if (!tokenId) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
let tokens: string | null = window.localStorage.getItem('tokens') |
||||||
|
let tokensData: string[] = JSON.parse(tokens || '[]'); |
||||||
|
if (!tokensData) { |
||||||
|
return |
||||||
|
} |
||||||
|
window.localStorage.setItem('token', tokenId) |
||||||
|
window.localStorage.setItem(tokenId, token) |
||||||
|
tokensData.push(tokenId) |
||||||
|
window.localStorage.setItem('tokens', JSON.stringify(tokensData)) |
||||||
|
} |
||||||
|
|
||||||
|
export const cleanToken = (id: string) => { |
||||||
|
if (!isClient()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
let tokens: string | null = window.localStorage.getItem('tokens') |
||||||
|
let tokensData: string[] = JSON.parse(tokens || '[]'); |
||||||
|
if (!tokensData) { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
tokensData = tokensData.filter((storedTokenId: string) => storedTokenId !== id) |
||||||
|
window.localStorage.setItem('tokens', JSON.stringify(tokensData)) |
||||||
|
window.localStorage.removeItem(id) |
||||||
|
|
||||||
|
// Чистим текущий токен
|
||||||
|
let activeTokenId: string | null = window.localStorage.getItem('token') |
||||||
|
if (id === activeTokenId) { |
||||||
|
window.localStorage.removeItem('token') |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const selectToken = (id: string) => { |
||||||
|
if (!isClient()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
window.localStorage.setItem('token', id) |
||||||
|
} |
||||||
|
|
||||||
|
export const grabTokenData = (token: string): TokenData|null => { |
||||||
|
if (!isClient()) { |
||||||
|
return null |
||||||
|
} |
||||||
|
const parts = token.split('.') |
||||||
|
let payload = 3 === parts.length && parts[1] ? JSON.parse(window.atob(parts[1])) : null |
||||||
|
let headers = 3 === parts.length && parts[0] ? JSON.parse(window.atob(parts[0])) : null |
||||||
|
if (!payload || !headers){ |
||||||
|
return null |
||||||
|
} |
||||||
|
return { |
||||||
|
payload, |
||||||
|
headers, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export const grabRawToken = (): string | null => { |
||||||
|
if (!isClient()) { |
||||||
|
return null |
||||||
|
} |
||||||
|
const tokenId = window.localStorage.getItem('token') |
||||||
|
if (!tokenId) { |
||||||
|
return null |
||||||
|
} |
||||||
|
return window.localStorage.getItem(tokenId) |
||||||
|
} |
||||||
|
|
||||||
|
export const grabToken = (): TokenData | null => { |
||||||
|
const token = grabRawToken() |
||||||
|
if (!token) { |
||||||
|
return null |
||||||
|
} |
||||||
|
return grabTokenData(token); |
||||||
|
} |
||||||
|
|
||||||
|
export const grabTokens = (): TokenData[] => { |
||||||
|
if (!isClient()) { |
||||||
|
return [] |
||||||
|
} |
||||||
|
const storedTokens = window.localStorage.getItem('tokens') |
||||||
|
let tokens: string[] = JSON.parse(storedTokens || '[]'); |
||||||
|
if (!tokens) { |
||||||
|
return [] |
||||||
|
} |
||||||
|
|
||||||
|
let result: TokenData[] = [] |
||||||
|
|
||||||
|
tokens.forEach((tokenId) => { |
||||||
|
let token = window.localStorage.getItem(tokenId) |
||||||
|
if (!token) { |
||||||
|
return true |
||||||
|
} |
||||||
|
let tokenData = grabTokenData(token) |
||||||
|
if (tokenData) { |
||||||
|
result.push(tokenData) |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
return result |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
import {useContext, useEffect, useState} from 'react' |
||||||
|
import {SMClient} from "../api/sm/sm-client"; |
||||||
|
import Context from "../context/token"; |
||||||
|
import {Token} from "./use-token"; |
||||||
|
import {BearerAuthentication} from "../api/authentication"; |
||||||
|
import {grabRawToken} from "../functions/token"; |
||||||
|
|
||||||
|
const grabClient = (token: Token | null, rawToken: string | null): SMClient => { |
||||||
|
let authentication = {} |
||||||
|
if (rawToken) { |
||||||
|
authentication = { |
||||||
|
authentication: new BearerAuthentication(rawToken) |
||||||
|
} |
||||||
|
} |
||||||
|
return new SMClient({ |
||||||
|
baseUrl: 'http://' + (token?.host || 'localhost'), |
||||||
|
...authentication |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
export function useApi(): SMClient { |
||||||
|
let {token} = useContext(Context) |
||||||
|
let [client, setClient] = useState<SMClient>(grabClient(token, grabRawToken())) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
setClient(grabClient(token, grabRawToken())) |
||||||
|
}, [token]) |
||||||
|
|
||||||
|
return client |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
import {useState, useEffect} from 'react' |
||||||
|
import {cleanToken, grabToken, grabTokens, selectToken, storeToken} from "../functions/token"; |
||||||
|
|
||||||
|
export class Token { |
||||||
|
id!: string |
||||||
|
host!: string |
||||||
|
permissions!: string[] |
||||||
|
} |
||||||
|
|
||||||
|
export interface UseTokenInterface { |
||||||
|
token: Token|null |
||||||
|
tokens: Token[] |
||||||
|
switchToken(tokenId: string): void |
||||||
|
deleteToken(tokenId: string): void |
||||||
|
addToken(tokenId: string): void |
||||||
|
} |
||||||
|
|
||||||
|
export function useToken(): UseTokenInterface { |
||||||
|
const [token, setToken] = useState<Token|null>(null); |
||||||
|
const [tokens, setTokens] = useState<Token[]>([]); |
||||||
|
useEffect(() => { |
||||||
|
setToken(grabToken()?.payload) |
||||||
|
|
||||||
|
let tokens = grabTokens().map((tokenData) => tokenData.payload) |
||||||
|
setTokens(tokens) |
||||||
|
|
||||||
|
return () => { |
||||||
|
|
||||||
|
} |
||||||
|
}, []) |
||||||
|
|
||||||
|
const switchToken = (tokenId: string) => { |
||||||
|
selectToken(tokenId) |
||||||
|
setToken(grabToken()?.payload) |
||||||
|
} |
||||||
|
|
||||||
|
const deleteToken = (tokenId: string) => { |
||||||
|
cleanToken(tokenId) |
||||||
|
setToken(grabToken()?.payload) |
||||||
|
} |
||||||
|
|
||||||
|
const addToken = (token: string) => { |
||||||
|
storeToken(token) |
||||||
|
setToken(grabToken()?.payload) |
||||||
|
} |
||||||
|
|
||||||
|
return {token, tokens, switchToken, deleteToken, addToken} |
||||||
|
} |
@ -1,6 +1,13 @@ |
|||||||
import '../styles/globals.css' |
import '../styles/globals.css' |
||||||
import type { AppProps } from 'next/app' |
import type { AppProps } from 'next/app' |
||||||
|
import {Provider} from "../context/token"; |
||||||
|
import {useToken} from "../hooks/use-token"; |
||||||
|
|
||||||
export default function App({ Component, pageProps }: AppProps) { |
export default function App({ Component, pageProps }: AppProps) { |
||||||
return <Component {...pageProps} /> |
let {token, tokens, switchToken, deleteToken, addToken} = useToken() |
||||||
|
return ( |
||||||
|
<Provider value={{token, tokens, switchToken, deleteToken, addToken}}> |
||||||
|
<Component {...pageProps} /> |
||||||
|
</Provider> |
||||||
|
) |
||||||
} |
} |
||||||
|
Loading…
Reference in new issue