From 35b8771532939a27ca796f6c1a394457a8d45540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Gessler?= <36667834+gessfred@users.noreply.github.com> Date: Sat, 6 Jan 2024 10:29:30 +0000 Subject: [PATCH] add top sites to insights and improve token refresh --- web/package-lock.json | 43 +++++++++++++++++++++++++---- web/package.json | 4 +++ web/src/App.js | 1 - web/src/foundation/api.js | 56 ++++++++++++++++++++------------------ web/src/pages/Insights.css | 5 ++++ web/src/pages/Insights.js | 53 ++++++++++++++++++++++++++++++------ 6 files changed, 120 insertions(+), 42 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index cd8c706..5c0dceb 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -12,12 +12,16 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "axios": "^1.6.2", "chart.js": "^4.4.0", "react": "^18.2.0", "react-chartjs-2": "^5.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" + }, + "devDependencies": { + "tailwindcss": "^3.3.7" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -5234,6 +5238,29 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/axobject-query": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", @@ -14192,6 +14219,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -15896,19 +15928,19 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" }, "node_modules/tailwindcss": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz", - "integrity": "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.7.tgz", + "integrity": "sha512-pjgQxDZPvyS/nG3ZYkyCvsbONJl7GdOejfm24iMt2ElYQQw8Jc4p0m8RdMp7mznPD0kUhfzwV3zAwa80qI0zmQ==", "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.5.3", "didyoumean": "^1.2.2", "dlv": "^1.1.3", - "fast-glob": "^3.2.12", + "fast-glob": "^3.3.0", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", - "jiti": "^1.18.2", + "jiti": "^1.19.1", "lilconfig": "^2.1.0", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", @@ -15920,7 +15952,6 @@ "postcss-load-config": "^4.0.1", "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.11", - "postcss-value-parser": "^4.2.0", "resolve": "^1.22.2", "sucrase": "^3.32.0" }, diff --git a/web/package.json b/web/package.json index 0a46b7c..66a5b4e 100644 --- a/web/package.json +++ b/web/package.json @@ -7,6 +7,7 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "axios": "^1.6.2", "chart.js": "^4.4.0", "react": "^18.2.0", "react-chartjs-2": "^5.2.0", @@ -37,5 +38,8 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "tailwindcss": "^3.3.7" } } diff --git a/web/src/App.js b/web/src/App.js index 0f9ec76..e8237e9 100644 --- a/web/src/App.js +++ b/web/src/App.js @@ -10,7 +10,6 @@ import api, { isAuthenticated } from './foundation/api' function Pages() { const [flag, setFlag] = useState(false) const authenticated = isAuthenticated() - console.log('auth', authenticated) return (
{!authenticated && setFlag(p => !p)} />} diff --git a/web/src/foundation/api.js b/web/src/foundation/api.js index a8e5293..dc8f7f2 100644 --- a/web/src/foundation/api.js +++ b/web/src/foundation/api.js @@ -1,59 +1,61 @@ import axios from 'axios'; const url = 'https://keylogg.pub.gessfred.xyz/api' +let failures = 0 const api = axios.create({ - baseURL: url, + baseURL: url }) api.interceptors.request.use( (config) => { const token = localStorage.getItem('token') - console.log('token', token) if (token) { config.headers.Authorization = `Bearer ${token}` } - console.log('request', config) + console.log('token', token, 'request', config.url) return config }, (error) => Promise.reject(error) ) +const refreshToken = async () => { + const refreshToken = localStorage.getItem('refreshToken') + const response = await axios.post('/api/token', { refreshToken }) + const { token } = response.data + + localStorage.setItem('token', token) + return token +} + // Add a response interceptor api.interceptors.response.use( - (response) => response, - /*async (error) => { - console.log('ERROR TOKEN REFRESH', error, error.response.status) - const originalRequest = error.config - console.log('original request', originalRequest) - - // If the error status is 401 and there is no originalRequest._retry flag, - // it means the token has expired and we need to refresh it - if (error.response.status === 401 && !originalRequest._retry) { - originalRequest._retry = true - - try { - const refreshToken = localStorage.getItem('refreshToken') - const response = await axios.post('/api/token', { refreshToken }) - const { token } = response.data - - localStorage.setItem('token', token) - // Retry the original request with the new token + (response) => { + ++failures + return response + }, + async (error) => { + try { + const originalRequest = error.config + console.log("Response error:", error, "original request:", originalRequest) + if (error.response.status === 401 && !originalRequest._retry) { + originalRequest._retry = true + originalRequest._retry_count = originalRequest._retry_count ? originalRequest._retry_count + 1 : 1 + const token = await refreshToken() originalRequest.headers.Authorization = `Bearer ${token}` return axios(originalRequest) - } catch (error) { - // Handle refresh token error or redirect to login } + } catch (e) { + console.log("Fatal error during response error resolution. Clearing token cache. Previous") } - + localStorage.removeItem('token') + localStorage.removeItem('refreshToken') return Promise.reject(error) - }*/ - async (error) => { - console.log('ERROR TOKEN REFRESH', error) } ) export async function login(username, password) { + console.log('try login for user', username) let formData = new URLSearchParams({ username: username, password: password diff --git a/web/src/pages/Insights.css b/web/src/pages/Insights.css index b28e17c..559824b 100644 --- a/web/src/pages/Insights.css +++ b/web/src/pages/Insights.css @@ -19,4 +19,9 @@ flex-direction: row; align-items: center; justify-content: space-between; +} + +#top-site-doughnut { + width: 400px !important; + height: 400px !important; } \ No newline at end of file diff --git a/web/src/pages/Insights.js b/web/src/pages/Insights.js index 0444dd4..ae80cb0 100644 --- a/web/src/pages/Insights.js +++ b/web/src/pages/Insights.js @@ -13,14 +13,16 @@ import { Title, Tooltip, Legend, + ArcElement } from 'chart.js' -import { Line } from 'react-chartjs-2' +import { Line, Pie, Doughnut } from 'react-chartjs-2' ChartJS.register( CategoryScale, LinearScale, PointElement, LineElement, + ArcElement, Title, Tooltip, Legend @@ -64,6 +66,36 @@ function TypingDashboard({ data, labels, title }) { ); } +function TopSites({data}) { + // npm install chroma-js + // + const colors = Array.from({ length: data.length }).map((_, i) => + `hsl(${i * (360 / data.length)}, 100%, 70%)` + ) + return ( + url), + datasets: [ + { + id: 1, + label: '???', + data: data.map(({count}) => count), + borderWidth: 2, + backgroundColor: colors + } + ], + }} + options={{ + responsive: true + }} + /> + ) +} + export function Insights({show}) { const isAuthenticated = false const [state, setState] = useState({}) @@ -89,13 +121,18 @@ export function Insights({show}) {

Top sites

-
- {topSites.map(({url, count}) => ( -
- {url} - {count} -
- ))} +
+ + + {topSites.map(({url, count}) => ( + + + + + ))} + +
{url}{count}
+
i)} data={typingData.map(x => x.event_count).reverse()} />