Added big fixes allowing dynamic obtaining of the token.
This commit is contained in:
parent
a20235a953
commit
08311a4a94
@ -66,7 +66,7 @@
|
||||
"eject": "react-scripts eject",
|
||||
"minify-svgs": "node scripts/minify-svgs.js",
|
||||
|
||||
"dev:electron": "concurrently \"react-scripts start\" \"ELECTRON_START_URL=http://192.168.68.53:3000 electron src/electron/main.js\"",
|
||||
"dev:electron": "concurrently \"react-scripts start\" \"ELECTRON_START_URL=http://192.168.68.53:3000 electron public/electron.js\"",
|
||||
"build:electron": "npm run build && electron-builder"
|
||||
},
|
||||
"eslintConfig": {
|
||||
|
||||
@ -68,6 +68,10 @@
|
||||
line-height: 32.5px;
|
||||
}
|
||||
|
||||
.loading-modal .ant-modal-footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
:root {
|
||||
--unit-100vh: 100vh;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState, useCallback } from 'react'
|
||||
import React, { useEffect, useState, useCallback, useContext } from 'react'
|
||||
import {
|
||||
Descriptions,
|
||||
Space,
|
||||
@ -21,10 +21,12 @@ import ReloadIcon from '../../Icons/ReloadIcon'
|
||||
import useCollapseState from '../hooks/useCollapseState'
|
||||
|
||||
import config from '../../../config'
|
||||
import { AuthContext } from '../context/AuthContext'
|
||||
|
||||
const { Title, Text } = Typography
|
||||
|
||||
const ProductionOverview = () => {
|
||||
const { token } = useContext(AuthContext)
|
||||
const [messageApi, contextHolder] = message.useMessage()
|
||||
const [error, setError] = useState(null)
|
||||
const [fetchPrinterStatsLoading, setFetchPrinterStatsLoading] = useState(true)
|
||||
@ -68,9 +70,9 @@ const ProductionOverview = () => {
|
||||
setFetchPrinterStatsLoading(true)
|
||||
const response = await axios.get(`${config.backendUrl}/printers/stats`, {
|
||||
headers: {
|
||||
Accept: 'application/json'
|
||||
},
|
||||
withCredentials: true
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
})
|
||||
const printStats = response.data
|
||||
setStats((prev) => ({ ...prev, printers: printStats }))
|
||||
@ -88,9 +90,9 @@ const ProductionOverview = () => {
|
||||
setFetchPrinterStatsLoading(true)
|
||||
const response = await axios.get(`${config.backendUrl}/jobs/stats`, {
|
||||
headers: {
|
||||
Accept: 'application/json'
|
||||
},
|
||||
withCredentials: true
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
})
|
||||
const jobstats = response.data
|
||||
setStats((prev) => ({ ...prev, jobs: jobstats }))
|
||||
@ -107,9 +109,9 @@ const ProductionOverview = () => {
|
||||
try {
|
||||
const response = await axios.get(`${config.backendUrl}/stats/history`, {
|
||||
headers: {
|
||||
Accept: 'application/json'
|
||||
},
|
||||
withCredentials: true
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
})
|
||||
setChartData(response.data)
|
||||
} catch (err) {
|
||||
@ -118,8 +120,10 @@ const ProductionOverview = () => {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fetchAllStats()
|
||||
}, [fetchAllStats])
|
||||
if (token != null) {
|
||||
fetchAllStats()
|
||||
}
|
||||
}, [fetchAllStats, token])
|
||||
|
||||
if (fetchPrinterStatsLoading || fetchPrinterStatsLoading) {
|
||||
return (
|
||||
|
||||
@ -402,9 +402,6 @@ const ApiServerProvider = ({ children }) => {
|
||||
sorter = {},
|
||||
onDataChange
|
||||
} = params
|
||||
if (token == null) {
|
||||
return []
|
||||
}
|
||||
logger.debug('Fetching table data from:', type, {
|
||||
page,
|
||||
limit,
|
||||
@ -456,9 +453,6 @@ const ApiServerProvider = ({ children }) => {
|
||||
|
||||
// Fetch table data with pagination, filtering, and sorting
|
||||
const fetchObjectsByProperty = async (type, params = {}) => {
|
||||
if (token == null) {
|
||||
return []
|
||||
}
|
||||
const { filter = {}, properties = [] } = params
|
||||
|
||||
logger.debug('Fetching property object data from:', type, {
|
||||
@ -571,9 +565,6 @@ const ApiServerProvider = ({ children }) => {
|
||||
|
||||
// Download GCode file content
|
||||
const fetchObjectContent = async (id, type, fileName) => {
|
||||
if (!token) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${config.backendUrl}/${type.toLowerCase()}s/${id}/content`,
|
||||
|
||||
@ -7,7 +7,16 @@ import React, {
|
||||
useContext
|
||||
} from 'react'
|
||||
import axios from 'axios'
|
||||
import { message, Modal, notification, Progress, Button, Space } from 'antd'
|
||||
import {
|
||||
message,
|
||||
Modal,
|
||||
notification,
|
||||
Progress,
|
||||
Button,
|
||||
Space,
|
||||
Typography
|
||||
} from 'antd'
|
||||
import { LoadingOutlined } from '@ant-design/icons'
|
||||
import PropTypes from 'prop-types'
|
||||
import ExclamationOctogonIcon from '../../Icons/ExclamationOctagonIcon'
|
||||
import InfoCircleIcon from '../../Icons/InfoCircleIcon'
|
||||
@ -21,12 +30,16 @@ logger.setLevel(config.logLevel)
|
||||
|
||||
const AuthContext = createContext()
|
||||
|
||||
const Title = Typography
|
||||
|
||||
const AuthProvider = ({ children }) => {
|
||||
const [messageApi, contextHolder] = message.useMessage()
|
||||
const [notificationApi, notificationContextHolder] =
|
||||
notification.useNotification()
|
||||
const [authenticated, setAuthenticated] = useState(false)
|
||||
const [initialized, setInitialized] = useState(false)
|
||||
const [retreivedTokenFromSession, setRetreivedTokenFromSession] =
|
||||
useState(false)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [token, setToken] = useState(null)
|
||||
const [expiresAt, setExpiresAt] = useState(null)
|
||||
@ -46,7 +59,11 @@ const AuthProvider = ({ children }) => {
|
||||
setToken(storedToken)
|
||||
setExpiresAt(storedExpiresAt)
|
||||
setAuthenticated(true)
|
||||
} else {
|
||||
setAuthenticated(false)
|
||||
setShowUnauthorizedModal(true)
|
||||
}
|
||||
setRetreivedTokenFromSession(true)
|
||||
}, [])
|
||||
|
||||
const logout = useCallback((redirectUri = '/login') => {
|
||||
@ -61,18 +78,19 @@ const AuthProvider = ({ children }) => {
|
||||
|
||||
// Login using query parameters
|
||||
const loginWithSSO = useCallback(
|
||||
(redirectUri = window.location.pathname + window.location.search) => {
|
||||
(redirectUri = location.pathname + location.search) => {
|
||||
messageApi.info('Logging in with tombutcher.work')
|
||||
const loginUrl = `${config.backendUrl}/auth/${isElectron ? 'app/' : ''}login?redirect_uri=${encodeURIComponent(redirectUri)}`
|
||||
if (isElectron) {
|
||||
console.log('Opening external url...')
|
||||
openExternalUrl(loginUrl)
|
||||
setLoading(true)
|
||||
} else {
|
||||
console.log('Redirecting...')
|
||||
window.location.href = loginUrl
|
||||
}
|
||||
},
|
||||
[messageApi, openExternalUrl, isElectron]
|
||||
[messageApi, openExternalUrl, isElectron, location.search]
|
||||
)
|
||||
|
||||
const getLoginToken = useCallback(
|
||||
@ -94,6 +112,11 @@ const AuthProvider = ({ children }) => {
|
||||
setUserProfile(response.data)
|
||||
sessionStorage.setItem('authToken', response.data.access_token)
|
||||
sessionStorage.setItem('authExpiresAt', response.data.expires_at)
|
||||
const searchParams = new URLSearchParams(location.search)
|
||||
searchParams.delete('authCode')
|
||||
const newSearch = searchParams.toString()
|
||||
const newPath = location.pathname + (newSearch ? `?${newSearch}` : '')
|
||||
navigate(newPath, { replace: true })
|
||||
} else {
|
||||
setAuthenticated(false)
|
||||
setAuthError('Failed to authenticate user.')
|
||||
@ -261,23 +284,20 @@ const AuthProvider = ({ children }) => {
|
||||
}, [expiresAt, authenticated, notificationApi, refreshToken])
|
||||
|
||||
useEffect(() => {
|
||||
if (initialized == false) {
|
||||
const authCode =
|
||||
new URLSearchParams(location.search).get('authCode') || null
|
||||
if (authCode != null) {
|
||||
getLoginToken(authCode)
|
||||
const searchParams = new URLSearchParams(location.search)
|
||||
if (searchParams.has('authCode')) {
|
||||
searchParams.delete('authCode')
|
||||
const newSearch = searchParams.toString()
|
||||
const newPath = location.pathname + (newSearch ? `?${newSearch}` : '')
|
||||
navigate(newPath, { replace: true })
|
||||
}
|
||||
} else if (token == null) {
|
||||
setShowUnauthorizedModal(true)
|
||||
setAuthenticated(false)
|
||||
}
|
||||
const authCode =
|
||||
new URLSearchParams(location.search).get('authCode') || null
|
||||
if (authCode != null) {
|
||||
getLoginToken(authCode)
|
||||
} else if (
|
||||
token == null &&
|
||||
retreivedTokenFromSession == true &&
|
||||
initialized == false &&
|
||||
authCode == null
|
||||
) {
|
||||
setInitialized(true)
|
||||
console.log('Showing unauth')
|
||||
setShowUnauthorizedModal(true)
|
||||
setAuthenticated(false)
|
||||
}
|
||||
}, [
|
||||
checkAuthStatus,
|
||||
@ -286,7 +306,8 @@ const AuthProvider = ({ children }) => {
|
||||
initialized,
|
||||
location.pathname,
|
||||
navigate,
|
||||
token
|
||||
token,
|
||||
retreivedTokenFromSession
|
||||
])
|
||||
|
||||
if (authError) {
|
||||
@ -370,18 +391,22 @@ const AuthProvider = ({ children }) => {
|
||||
tombutcher.work to continue.
|
||||
</Modal>
|
||||
<Modal
|
||||
title={
|
||||
<Space size={'middle'}>
|
||||
<ExclamationOctogonIcon />
|
||||
Loading...
|
||||
</Space>
|
||||
}
|
||||
open={loading}
|
||||
style={{ maxWidth: 200, top: '50%', transform: 'translateY(-50%)' }}
|
||||
className={'loading-modal'}
|
||||
title={false}
|
||||
height={20}
|
||||
style={{ maxWidth: 220, top: '50%', transform: 'translateY(-50%)' }}
|
||||
closable={false}
|
||||
maskClosable={false}
|
||||
footer={false}
|
||||
/>
|
||||
>
|
||||
<Space size={'middle'}>
|
||||
<LoadingOutlined />
|
||||
<Title level={5} style={{ margin: 0 }}>
|
||||
Loading, please wait...
|
||||
</Title>
|
||||
</Space>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,45 +1,16 @@
|
||||
// PrivateRoute.js
|
||||
import PropTypes from 'prop-types'
|
||||
import React, { useContext, useState, useEffect } from 'react'
|
||||
import React, { useContext } from 'react'
|
||||
import { AuthContext } from './Dashboard/context/AuthContext'
|
||||
import AuthLoading from './App/AppLoading'
|
||||
import { useThemeContext } from './Dashboard/context/ThemeContext'
|
||||
|
||||
const PrivateRoute = ({ component: Component }) => {
|
||||
const { isDarkMode } = useThemeContext()
|
||||
const { authenticated, loading, showSessionExpiredModal } =
|
||||
useContext(AuthContext)
|
||||
const [fadeIn, setFadeIn] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading) {
|
||||
// Small delay to ensure smooth transition
|
||||
const timer = setTimeout(() => setFadeIn(true), 50)
|
||||
return () => clearTimeout(timer)
|
||||
}
|
||||
}, [loading])
|
||||
|
||||
// Show loading state while auth state is being determined
|
||||
if (loading) {
|
||||
return <AuthLoading />
|
||||
}
|
||||
const { authenticated, showSessionExpiredModal } = useContext(AuthContext)
|
||||
|
||||
// Redirect to login if not authenticated
|
||||
return (
|
||||
<div style={{ background: isDarkMode ? '#000000' : '#ffffff' }}>
|
||||
<div
|
||||
style={{
|
||||
opacity: fadeIn ? 1 : 0,
|
||||
transition: 'opacity 0.3s ease-in-out'
|
||||
}}
|
||||
>
|
||||
{authenticated || showSessionExpiredModal ? (
|
||||
<Component />
|
||||
) : (
|
||||
<Component />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<>
|
||||
{authenticated || showSessionExpiredModal ? <Component /> : <Component />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -3,15 +3,9 @@ import PropTypes from 'prop-types'
|
||||
import React, { useContext } from 'react'
|
||||
import { Navigate } from 'react-router-dom'
|
||||
import { AuthContext } from './Dashboard/context/AuthContext'
|
||||
import AuthLoading from './App/AppLoading'
|
||||
|
||||
const PublicRoute = ({ component: Component }) => {
|
||||
const { authenticated, loading } = useContext(AuthContext)
|
||||
|
||||
// Show loading state while auth state is being determined
|
||||
if (loading) {
|
||||
return <AuthLoading />
|
||||
}
|
||||
const { authenticated } = useContext(AuthContext)
|
||||
|
||||
// Redirect to login if not authenticated
|
||||
return !authenticated ? (
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user