Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle refresh token #182

Merged
merged 10 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions backend-client/openapi-generator.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ function clean_up() {
}

function generate_client() {
openapi-generator-cli generate \
-i $API_DOCS_FILE \
-o src/ \
docker run --rm -v ${PWD}:/local -u `id -u`:`id -g` openapitools/openapi-generator-cli generate \
-i /local/$API_DOCS_FILE \
-o /local/src/ \
-g typescript-fetch \
--language-specific-primitives \
--reserved-words-mappings \
Expand Down
1 change: 0 additions & 1 deletion backend-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
"build": "tsc"
},
"devDependencies": {
"@openapitools/openapi-generator-cli": "^2.7.0",
"@types/node": "^18.15.11",
"ts-node": "^10.9.1",
"tsc": "^2.0.4",
Expand Down
50 changes: 48 additions & 2 deletions frontend/src/api/backendApi.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import useStore from '@/store/store'
import {
ClientTokenFromJSON,
Configuration,
ConfigurationParameters,
FetchAPI,
FileImportApi,
HTTPHeaders,
InfoApi,
Expand All @@ -11,14 +14,56 @@ import {
UserApi,
} from '@green-ecolution/backend-client'

const basePath = import.meta.env.VITE_BACKEND_BASEURL ?? '/api-local'

const headers: HTTPHeaders = {
'Content-Type': 'application/json',
Accept: 'application/json',
}

const backendFetch: FetchAPI = async (...args) => {
const [resource, config] = args
let response = await fetch(resource, config)
if (response.status === 401) {
const params: RequestInit = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
refresh_token: useStore.getState().auth.token?.refreshToken || '',
}),
}
const res = await fetch(`${basePath}/v1/user/token/refresh`, params)
if (res.status !== 200) {
return response
}
const data = ClientTokenFromJSON(await res.json())
useStore.getState().auth.setToken(data)
useStore.getState().user.setFromJwt(data.accessToken);

response = await fetch(resource, {
...config,
headers: {
...config?.headers,
Authorization: `Bearer ${data.accessToken}`,
},
})
}
return response
}

const configParams: ConfigurationParameters = {
basePath: import.meta.env.VITE_BACKEND_BASEURL ?? '/api-local',
basePath,
headers,
fetchApi: backendFetch,
accessToken() {
const token = useStore.getState().auth.token?.accessToken
if (!token) {
return ''
}
return `Bearer ${token}`
},
}

const config = new Configuration(configParams)
Expand All @@ -31,7 +76,8 @@ export const regionApi = new RegionApi(config)
export const sensorApi = new SensorApi(config)
export const importApi = new FileImportApi(
new Configuration({
basePath: import.meta.env.VITE_BACKEND_BASEURL ?? '/api-local',
...configParams,
headers: {},
})
)

Expand Down
24 changes: 5 additions & 19 deletions frontend/src/api/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,43 +12,32 @@ import {
TreeClusterList,
TreeList,
} from './backendApi'
import useStore from '@/store/store'

const authHeader = () => ({
authorization: `Bearer ${useStore.getState().auth.token?.accessToken}`,
})

export const treeClusterQuery = () =>
queryOptions<TreeClusterList>({
queryKey: ['cluster'],
queryFn: () =>
clusterApi.getAllTreeClusters({
authorization: authHeader().authorization,
}),
queryFn: () => clusterApi.getAllTreeClusters(),
})

export const treeClusterIdQuery = (id: string) =>
queryOptions<TreeCluster>({
queryKey: ['treescluster', id],
queryFn: () =>
clusterApi.getTreeClusterById({
authorization: authHeader().authorization,
clusterId: id,
}),
})

export const sensorQuery = () =>
queryOptions<SensorList>({
queryKey: ['sensors'],
queryFn: () =>
sensorApi.getAllSensors({ authorization: authHeader().authorization }),
queryFn: () => sensorApi.getAllSensors(),
})

export const treeQuery = () =>
queryOptions<TreeList>({
queryKey: ['tree'],
queryFn: () =>
treeApi.getAllTrees({ authorization: authHeader().authorization }),
queryFn: () => treeApi.getAllTrees(),
})

export const treeIdQuery = (id: string) =>
Expand All @@ -57,19 +46,16 @@ export const treeIdQuery = (id: string) =>
queryFn: () =>
treeApi.getTrees({
treeId: id,
authorization: authHeader().authorization,
}),
})
export const regionsQuery = () =>
queryOptions({
queryKey: ['regions'],
queryFn: () =>
regionApi.v1RegionGet({ authorization: authHeader().authorization }),
queryFn: () => regionApi.v1RegionGet(),
})

export const infoQuery = () =>
queryOptions<AppInfo>({
queryKey: ['info'],
queryFn: () =>
infoApi.getAppInfo({ authorization: authHeader().authorization }),
queryFn: () => infoApi.getAppInfo(),
})
4 changes: 0 additions & 4 deletions frontend/src/components/tree/TreeUpdate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { Tree, TreeUpdate as TreeUpdateReq } from "@green-ecolution/backend-clie
import { useInitFormQuery } from "@/hooks/form/useInitForm"
import { sensorQuery, treeClusterQuery, treeIdQuery } from "@/api/queries"
import { treeApi } from "@/api/backendApi"
import { useAuthHeader } from "@/hooks/useAuthHeader"
import { useMapStore } from "@/store/store"
import { useNavigate } from "@tanstack/react-router"
import { zodResolver } from "@hookform/resolvers/zod"
Expand All @@ -20,7 +19,6 @@ interface TreeUpdateProps {
}

const TreeUpdate = ({ treeId, onUpdateSuccess, onUpdateError }: TreeUpdateProps) => {
const authorization = useAuthHeader()
const navigate = useNavigate()
const { data: sensors } = useSuspenseQuery(sensorQuery())
const { data: treeClusters } = useSuspenseQuery(treeClusterQuery())
Expand All @@ -47,7 +45,6 @@ const TreeUpdate = ({ treeId, onUpdateSuccess, onUpdateError }: TreeUpdateProps)
const { isError, mutate } = useMutation({
mutationFn: (tree: TreeUpdateReq) =>
treeApi.updateTree({
authorization,
treeId: treeId,
body: tree,
}),
Expand All @@ -70,7 +67,6 @@ const TreeUpdate = ({ treeId, onUpdateSuccess, onUpdateError }: TreeUpdateProps)

const handleDeleteTree = () => {
return treeApi.deleteTree({
authorization,
treeId: String(treeId),
})
}
Expand Down
5 changes: 1 addition & 4 deletions frontend/src/components/treecluster/TreeClusterUpdate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { useMutation } from '@tanstack/react-query'
import FormForTreecluster from '../general/form/FormForTreecluster'
import BackLink from '../general/links/BackLink'
import DeleteSection from './DeleteSection'
import { useAuthHeader } from '@/hooks/useAuthHeader'
import {
TreeCluster,
TreeClusterUpdate as ClusterUpdate,
Expand Down Expand Up @@ -31,7 +30,6 @@ const TreeClusterUpdate = ({
onUpdateError,
onUpdateSuccess,
}: TreeClusterUpdateProps) => {
const authorization = useAuthHeader()
const navigate = useNavigate()
const { initForm, loadedData } = useInitFormQuery<
TreeCluster,
Expand Down Expand Up @@ -60,7 +58,7 @@ const TreeClusterUpdate = ({

const { isError, mutate } = useMutation({
mutationFn: (body: ClusterUpdate) =>
clusterApi.updateTreeCluster({ authorization, clusterId, body }),
clusterApi.updateTreeCluster({ clusterId, body }),
onSuccess: (data) => onUpdateSuccess(data),
onError: () => onUpdateError(),
throwOnError: true,
Expand All @@ -75,7 +73,6 @@ const TreeClusterUpdate = ({

const handleDeleteTreeCluster = () => {
return clusterApi.deleteTreeCluster({
authorization,
clusterId: String(clusterId),
})
}
Expand Down
16 changes: 10 additions & 6 deletions frontend/src/context/FilterContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,18 @@ const FilterProvider: React.FC<FilterProviderProps> = ({

const handleClusterChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { value } = event.target
setHasCluster(value === "true");
setHasCluster(value === 'true')
}

const handlePlantingYearChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { checked, value } = event.target;
setPlantingYears((prev) =>
checked ? [...prev, Number(value)] : prev.filter((year) => year !== Number(value))
);
const handlePlantingYearChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
const { checked, value } = event.target
setPlantingYears((prev) =>
checked
? [...prev, Number(value)]
: prev.filter((year) => year !== Number(value))
)
}

const applyOldStateToTags = (oldValues: Filters) => {
Expand Down
8 changes: 0 additions & 8 deletions frontend/src/hooks/useAuthHeader.ts

This file was deleted.

17 changes: 17 additions & 0 deletions frontend/src/routes/__root.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
import { userApi } from '@/api/backendApi'
import App from '@/App'
import useStore from '@/store/store'
import { createRootRoute } from '@tanstack/react-router'

export const Route = createRootRoute({
component: Root,
beforeLoad: async () => {
if (!useStore.getState().auth.isAuthenticated) {
return
}
const token = await userApi.v1UserTokenRefreshPost({
body: {
refreshToken: useStore.getState().auth.token?.refreshToken || '',
},
})
if (!token) {
return
}
useStore.getState().auth.setToken(token)
useStore.getState().user.setFromJwt(token.accessToken)
}
})

function Root() {
Expand Down
4 changes: 1 addition & 3 deletions frontend/src/routes/_protected/settings/import.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import Modal from '@/components/general/Modal'
import { useMutation, useQueryClient, useSuspenseQuery } from '@tanstack/react-query'
import { treeQuery } from '@/api/queries'
import { importApi } from '@/api/backendApi'
import { useAuthHeader } from '@/hooks/useAuthHeader'
import useToast from '@/hooks/useToast'

export const Route = createFileRoute('/_protected/settings/import')({
Expand All @@ -16,7 +15,6 @@ export const Route = createFileRoute('/_protected/settings/import')({
})

function ImportFile() {
const authorization = useAuthHeader()
const [isModalOpen, setIsModalOpen] = useState(false)
const [file, setFile] = useState<File | null>(null)
const [message, setMessage] = useState('')
Expand All @@ -26,7 +24,7 @@ function ImportFile() {

const { mutate } = useMutation({
mutationFn: (file: File) =>
importApi.importTreesFromCsv({ file, authorization }),
importApi.importTreesFromCsv({ file }),
onSuccess: () => {
queryClient.invalidateQueries(treeQuery())
setMessage('Die Bäume wurden erfolgreich importiert.')
Expand Down
3 changes: 0 additions & 3 deletions frontend/src/routes/_protected/tree/_formular/new.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { treeApi, TreeCreate } from '@/api/backendApi'
import FormForTree from '@/components/general/form/FormForTree'
import { useFormSync } from '@/hooks/form/useFormSync'
import { useInitForm } from '@/hooks/form/useInitForm'
import { useAuthHeader } from '@/hooks/useAuthHeader'
import { TreeForm, TreeSchema } from '@/schema/treeSchema'
import useFormStore, { FormStore } from '@/store/form/useFormStore'
import { zodResolver } from '@hookform/resolvers/zod'
Expand Down Expand Up @@ -37,7 +36,6 @@ export const Route = createFileRoute('/_protected/tree/_formular/new')({
function NewTree() {
const { lat, lng } = Route.useLoaderData()
const navigate = useNavigate({ from: Route.fullPath })
const authorization = useAuthHeader()
const showToast = useToast()
const map = useMapStore()
const { data: sensors } = useSuspenseQuery(sensorQuery())
Expand Down Expand Up @@ -67,7 +65,6 @@ function NewTree() {
const { isError, mutate } = useMutation({
mutationFn: (tree: TreeCreate) =>
treeApi.createTree({
authorization,
body: tree,
}),
onSuccess: (data) => {
Expand Down
3 changes: 0 additions & 3 deletions frontend/src/routes/_protected/treecluster/_formular/new.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
TreeClusterCreate,
} from '@/api/backendApi'
import { SubmitHandler } from 'react-hook-form'
import { useAuthHeader } from '@/hooks/useAuthHeader'
import { TreeclusterSchema } from '@/schema/treeclusterSchema'
import { useMutation } from '@tanstack/react-query'
import FormForTreecluster from '@/components/general/form/FormForTreecluster'
Expand All @@ -26,7 +25,6 @@ export const Route = createFileRoute('/_protected/treecluster/_formular/new')({
})

function NewTreecluster() {
const authorization = useAuthHeader()
const showToast = useToast()
const navigate = useNavigate({ from: Route.fullPath })
const { initForm } = useInitForm<TreeclusterSchema>({
Expand All @@ -53,7 +51,6 @@ function NewTreecluster() {
const { isError, mutate } = useMutation({
mutationFn: (cluster: TreeClusterCreate) =>
clusterApi.createTreeCluster({
authorization,
body: cluster,
}),
onSuccess: (data) => {
Expand Down
17 changes: 2 additions & 15 deletions frontend/src/routes/auth/callback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,8 @@ export const Route = createFileRoute("/auth/callback")({
throw new Error("Error while fetching token");
}


useStore.setState((state) => {
state.auth.isAuthenticated = true;
state.auth.token = token;
});

const jwtInfo = decodeJWT<KeycloakJWT>(token.accessToken);
if (jwtInfo) {
useStore.setState((state) => {
state.user.email = jwtInfo.email;
state.user.username = jwtInfo.preferred_username;
state.user.firstName = jwtInfo.given_name;
state.user.lastName = jwtInfo.family_name;
});
}
useStore.getState().auth.setToken(token);
useStore.getState().user.setFromJwt(token.accessToken);

throw routerRedirect({
to: redirect,
Expand Down
Loading