diff --git a/apps/entry-app/src/app/api/agents/agents.ts b/apps/entry-app/src/app/api/agents/agents.ts index 3586e03..3c093ea 100644 --- a/apps/entry-app/src/app/api/agents/agents.ts +++ b/apps/entry-app/src/app/api/agents/agents.ts @@ -1,13 +1,12 @@ -import { getAuthData } from '@lifeis/common-ui'; +import { utilFetch } from '@lifeis/common-ui'; import { ILog } from '../../domains/log.domain'; import { CONFIG } from '../../../../src/config'; export const createAgent = async (data: { name: string; prefix: string }): Promise => { - const response = await fetch(`${CONFIG.BE_URL}/agents`, { + const response = await utilFetch(`${CONFIG.BE_URL}/agents`, { method: 'POST', headers: { 'Content-Type': 'application/json', - Authorization: `Bearer ${getAuthData().accessToken}`, }, body: JSON.stringify(data), }); @@ -20,11 +19,10 @@ export const createAgent = async (data: { name: string; prefix: string }): Promi }; export const removeAgent = async (id: string): Promise => { - const response = await fetch(`${CONFIG.BE_URL}/agents/${id}`, { + const response = await utilFetch(`${CONFIG.BE_URL}/agents/${id}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json', - Authorization: `Bearer ${getAuthData().accessToken}`, }, }); @@ -36,11 +34,7 @@ export const removeAgent = async (id: string): Promise => { }; export const getAllAgents = async (): Promise => { - const response = await fetch(`${CONFIG.BE_URL}/agents`, { - headers: { - Authorization: `Bearer ${getAuthData().accessToken}`, - }, - }); + const response = await utilFetch(`${CONFIG.BE_URL}/agents`); if (!response.ok) { throw new Error('Failed to get agents'); @@ -58,11 +52,10 @@ export const submitMessage = async ({ }): Promise<{ answer: string; }> => { - const response = await fetch(`${CONFIG.BE_URL}/agents/${id}`, { + const response = await utilFetch(`${CONFIG.BE_URL}/agents/${id}`, { method: 'POST', headers: { 'Content-Type': 'application/json', - Authorization: `Bearer ${getAuthData().accessToken}`, }, body: JSON.stringify({ message, diff --git a/apps/entry-app/src/app/api/assistants/assistants.api.ts b/apps/entry-app/src/app/api/assistants/assistants.api.ts index 0c3c914..e47e8eb 100644 --- a/apps/entry-app/src/app/api/assistants/assistants.api.ts +++ b/apps/entry-app/src/app/api/assistants/assistants.api.ts @@ -1,3 +1,4 @@ +import { utilFetch } from '@lifeis/common-ui'; import { CONFIG } from '../../../config'; export const checkPolishGrammar = async (text: string): Promise => { @@ -5,7 +6,7 @@ export const checkPolishGrammar = async (text: string): Promise => { try { // post message - const checkData = await fetch(`${CONFIG.BE_URL}/openai/check-polish-grammar`, { + const checkData = await utilFetch(`${CONFIG.BE_URL}/openai/check-polish-grammar`, { method: 'POST', body: JSON.stringify({ message: text }), headers: { @@ -19,23 +20,20 @@ export const checkPolishGrammar = async (text: string): Promise => { return new Promise((resolve, reject) => { const intervalId = setInterval(async () => { try { - const runResponse = await fetch(`${CONFIG.BE_URL}/openai/thread/run?threadId=${threadId}&runId=${runId}`, { - method: 'GET', - headers: { - Authorization: `Bearer ${accessToken}`, + const runResponse = await utilFetch( + `${CONFIG.BE_URL}/openai/thread/run?threadId=${threadId}&runId=${runId}`, + { + method: 'GET', }, - }); + ); const run = await runResponse.json(); if (run.status === 'completed') { clearInterval(intervalId); - const messagesResponse = await fetch(`${CONFIG.BE_URL}/openai/thread/messages?threadId=${threadId}`, { + const messagesResponse = await utilFetch(`${CONFIG.BE_URL}/openai/thread/messages?threadId=${threadId}`, { method: 'GET', - headers: { - Authorization: `Bearer ${accessToken}`, - }, }); const messagesData = await messagesResponse.json(); @@ -53,14 +51,11 @@ export const checkPolishGrammar = async (text: string): Promise => { }; export const translateToPolish = async (text: string): Promise => { - const accessToken = localStorage.getItem('accessToken'); - try { // post message - const checkData = await fetch(`${CONFIG.BE_URL}/gemini/translate-to-polish`, { + const checkData = await utilFetch(`${CONFIG.BE_URL}/gemini/translate-to-polish`, { method: 'POST', headers: { - Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ message: text }), diff --git a/apps/entry-app/src/app/api/audio/audio.api.ts b/apps/entry-app/src/app/api/audio/audio.api.ts index ee16db5..73104f4 100644 --- a/apps/entry-app/src/app/api/audio/audio.api.ts +++ b/apps/entry-app/src/app/api/audio/audio.api.ts @@ -1,16 +1,14 @@ +import { utilFetch } from '@lifeis/common-ui'; import { CONFIG } from '../../../config'; export const transcript = async (blob: Blob): Promise => { - const accessToken = localStorage.getItem('accessToken'); - const formData = new FormData(); formData.append('audio', blob); - return fetch(`${CONFIG.BE_URL}/openai/transcribe`, { + return utilFetch(`${CONFIG.BE_URL}/openai/transcribe`, { method: 'POST', body: formData, headers: { - Authorization: `Bearer ${accessToken}`, MIME: blob.type, }, }); diff --git a/apps/entry-app/src/app/api/insights/insights.api.ts b/apps/entry-app/src/app/api/insights/insights.api.ts index 15e4b4f..9f8f4e3 100644 --- a/apps/entry-app/src/app/api/insights/insights.api.ts +++ b/apps/entry-app/src/app/api/insights/insights.api.ts @@ -1,15 +1,9 @@ -import { getAuthData } from '@lifeis/common-ui'; -import { ILog } from '../../domains/log.domain'; +import { utilFetch } from '@lifeis/common-ui'; import { CONFIG } from '../../../../src/config'; -export const createLog = async (message: string): Promise => { - const response = await fetch(`${CONFIG.BE_URL}/api/logs`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${getAuthData().accessToken}`, - }, - body: JSON.stringify({ message }), +export const getAllInsights = async (): Promise => { + const response = await utilFetch(`${CONFIG.BE_URL}/insights`, { + method: 'GET', }); if (!response.ok) { @@ -18,17 +12,3 @@ export const createLog = async (message: string): Promise => { return await response.json(); }; - -export const getInsights = async (): Promise => { - const response = await fetch(`${CONFIG.BE_URL}/api/insights`, { - headers: { - Authorization: `Bearer ${getAuthData().accessToken}`, - }, - }); - - if (!response.ok) { - throw new Error('Failed to get insights'); - } - - return await response.json(); -}; diff --git a/apps/entry-app/src/app/api/logs/logs.ts b/apps/entry-app/src/app/api/logs/logs.api.ts similarity index 52% rename from apps/entry-app/src/app/api/logs/logs.ts rename to apps/entry-app/src/app/api/logs/logs.api.ts index 454f2d5..dd0ffcd 100644 --- a/apps/entry-app/src/app/api/logs/logs.ts +++ b/apps/entry-app/src/app/api/logs/logs.api.ts @@ -1,13 +1,12 @@ -import { getAuthData } from '@lifeis/common-ui'; +import { utilFetch } from '@lifeis/common-ui'; import { ILog } from '../../domains/log.domain'; -import { CONFIG } from '../../../../src/config'; +import { CONFIG } from '../../../config'; export const createLog = async (message: string): Promise => { - const response = await fetch(`${CONFIG.BE_URL}/api/logs`, { + const response = await utilFetch(`${CONFIG.BE_URL}/logs`, { method: 'POST', headers: { 'Content-Type': 'application/json', - Authorization: `Bearer ${getAuthData().accessToken}`, }, body: JSON.stringify({ message }), }); @@ -19,12 +18,8 @@ export const createLog = async (message: string): Promise => { return await response.json(); }; -export const getLogs = async (): Promise => { - const response = await fetch(`${CONFIG.BE_URL}/api/logs`, { - headers: { - Authorization: `Bearer ${getAuthData().accessToken}`, - }, - }); +export const getAllLogs = async (): Promise => { + const response = await utilFetch(`${CONFIG.BE_URL}/logs`); if (!response.ok) { throw new Error('Failed to get logs'); diff --git a/apps/entry-app/src/app/app.tsx b/apps/entry-app/src/app/app.tsx index 2a50ce3..e1fdfef 100644 --- a/apps/entry-app/src/app/app.tsx +++ b/apps/entry-app/src/app/app.tsx @@ -1,9 +1,9 @@ // eslint-disable-next-line @typescript-eslint/no-unused-vars -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { GoogleOAuthProvider } from '@react-oauth/google'; -import { UserSession } from '@lifeis/common-ui'; +import { isUserLoggedIn, UserSession } from '@lifeis/common-ui'; import { CONFIG } from '../config'; import { Route, Routes } from 'react-router-dom'; @@ -16,6 +16,8 @@ import { init } from '@lifeis/common-ui'; import './styles/reset.css'; export default function App() { + const [isLoggedIn, setIsLoggedIn] = useState(isUserLoggedIn()); + useEffect(() => { init({ beUrl: CONFIG.BE_URL, @@ -26,7 +28,11 @@ export default function App() { return (
- + setIsLoggedIn(true)} + onLogOut={() => setIsLoggedIn(false)} + />
diff --git a/apps/entry-app/src/app/components/insights/insights.tsx b/apps/entry-app/src/app/components/insights/insights.tsx index f5fe603..a0c3626 100644 --- a/apps/entry-app/src/app/components/insights/insights.tsx +++ b/apps/entry-app/src/app/components/insights/insights.tsx @@ -1,24 +1,13 @@ -import { CONFIG } from '../../../config'; import React, { useEffect, useState } from 'react'; +import { getAllInsights } from '../../api/insights/insights.api'; -export const Insights = () => { - const [insights, setInsights] = useState([]); +export const AllInsights = () => { + const [insights, setInsights] = useState([]); useEffect(() => { const fetchLogs = async () => { - const accessToken = localStorage.getItem('accessToken'); - try { - const data = await fetch(`${CONFIG.BE_URL}/insights`, { - method: 'GET', - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }); - - setInsights(await data.json()); - } catch (e) { - console.log('error happened during fetch'); - } + const insights = await getAllInsights(); + setInsights(insights); }; fetchLogs(); }, []); diff --git a/apps/entry-app/src/app/components/log-form/log-form.tsx b/apps/entry-app/src/app/components/log-form/log-form.tsx index 66a9413..1b26d9c 100644 --- a/apps/entry-app/src/app/components/log-form/log-form.tsx +++ b/apps/entry-app/src/app/components/log-form/log-form.tsx @@ -1,5 +1,5 @@ -import { CONFIG } from '../../../../src/config'; import React from 'react'; +import { createLog } from '../../api/logs/logs.api'; export const LogForm = () => { const [message, setMessage] = React.useState(''); @@ -9,17 +9,9 @@ export const LogForm = () => { const handleSubmit = async (event: React.FormEvent) => { event.preventDefault(); - const accessToken = localStorage.getItem('accessToken'); try { - await fetch(`${CONFIG.BE_URL}/logs`, { - method: 'POST', - headers: { - Authorization: `Bearer ${accessToken}`, - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ message }), - }); + await createLog(message); setMessage(''); } catch (e) { console.log('error happened during fetch'); diff --git a/apps/entry-app/src/app/pages/insights.page.tsx b/apps/entry-app/src/app/pages/insights.page.tsx index 86e5645..9ec9fa4 100644 --- a/apps/entry-app/src/app/pages/insights.page.tsx +++ b/apps/entry-app/src/app/pages/insights.page.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Insights } from '../components/insights/insights'; +import { AllInsights } from '../components/insights/insights'; import { Link } from 'react-router-dom'; export const InsightsPage = () => { @@ -8,7 +8,7 @@ export const InsightsPage = () => {
Click here to go back to root page.
- + ); }; diff --git a/apps/entry-server/src/routes/auth-routes.ts b/apps/entry-server/src/routes/auth-routes.ts index 80fb8c0..01dfe95 100644 --- a/apps/entry-server/src/routes/auth-routes.ts +++ b/apps/entry-server/src/routes/auth-routes.ts @@ -42,7 +42,7 @@ router.post('/google', (req, res) => { }); router.post('/google/refresh', (req, res) => { - const { code } = req.body; + const { refreshToken } = req.body; const client_id = CLIENT_ID; const client_secret = CLIENT_SECRET; const redirect_uri = REDIRECT_URL; @@ -54,7 +54,7 @@ router.post('/google/refresh', (req, res) => { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ - refresh_token: code, + refresh_token: refreshToken, client_id, client_secret, redirect_uri, diff --git a/libs/common-ui/src/index.ts b/libs/common-ui/src/index.ts index 5224b8b..9a5ed79 100644 --- a/libs/common-ui/src/index.ts +++ b/libs/common-ui/src/index.ts @@ -1,5 +1,6 @@ export * from './lib/components/button/button'; export * from './lib/components/user-session/user-session'; -export * from './lib/services/local-storage.service'; +export * from './lib/services/auth-storage.service'; export * from './lib/common-ui'; +export * from './lib/utils/util-fetch'; export * from './main'; diff --git a/libs/common-ui/src/lib/api/auth/auth.ts b/libs/common-ui/src/lib/api/auth/auth.api.ts similarity index 52% rename from libs/common-ui/src/lib/api/auth/auth.ts rename to libs/common-ui/src/lib/api/auth/auth.api.ts index d768dfb..302c027 100644 --- a/libs/common-ui/src/lib/api/auth/auth.ts +++ b/libs/common-ui/src/lib/api/auth/auth.api.ts @@ -1,4 +1,5 @@ import { CONFIG } from '../../../main'; +import { getAuthData, saveAuthData } from '../../services/auth-storage.service'; import { AuthRawResponse } from './domain/auth.domain'; export const authGoogle = async (code: string): Promise => { @@ -14,21 +15,38 @@ export const authGoogle = async (code: string): Promise => { throw new Error('Failed to authenticate with Google'); } - return await response.json(); + const authResponse = await response.json(); + + saveAuthData({ + accessToken: authResponse.access_token, + refreshToken: authResponse.refresh_token, + }); + + return authResponse; }; -export const refreshAuthGoogle = async (code: string): Promise => { +export const refreshAuthGoogle = async (): Promise => { + const oldTokens = getAuthData(); + const response = await fetch(`${CONFIG.BE_URL}/auth/google/refresh`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ code }), + // How long does refreshToken last? + body: JSON.stringify({ refreshToken: oldTokens.refreshToken }), }); if (!response.ok) { throw new Error('Failed to authenticate with Google'); } - return await response.json(); + const authResponse = await response.json(); + + saveAuthData({ + ...oldTokens, + accessToken: authResponse.access_token, + }); + + return authResponse; }; diff --git a/libs/common-ui/src/lib/components/user-session/components/sign-in.tsx b/libs/common-ui/src/lib/components/user-session/components/sign-in.tsx index 2b3b2e4..01ebf6d 100644 --- a/libs/common-ui/src/lib/components/user-session/components/sign-in.tsx +++ b/libs/common-ui/src/lib/components/user-session/components/sign-in.tsx @@ -1,23 +1,19 @@ import React from 'react'; import { useGoogleLogin } from '@react-oauth/google'; -import { authGoogle } from '../../../api/auth/auth'; -import { AuthResponse } from '../../../domains/auth.domain'; +import { authGoogle } from '../../../api/auth/auth.api'; interface ISignInProps { - onSuccess: (codes: AuthResponse) => void; + onSuccess: () => void; } export function SignIn({ onSuccess }: ISignInProps) { - const googleLogin = useGoogleLogin({ + const handleGoogleLogin = useGoogleLogin({ onSuccess: async (googleResponse) => { try { - const authResponse = await authGoogle(googleResponse.code); - onSuccess({ - accessToken: authResponse.access_token, - refreshToken: authResponse.refresh_token, - }); + await authGoogle(googleResponse.code); + onSuccess(); } catch (error) { - console.error('Error while making request to own service:', error); + console.error('Error while making request to own auth service:', error); } }, onError: (err) => { @@ -27,5 +23,5 @@ export function SignIn({ onSuccess }: ISignInProps) { flow: 'auth-code', }); - return ; + return ; } diff --git a/libs/common-ui/src/lib/components/user-session/user-session.tsx b/libs/common-ui/src/lib/components/user-session/user-session.tsx index 65f2040..310b2d8 100644 --- a/libs/common-ui/src/lib/components/user-session/user-session.tsx +++ b/libs/common-ui/src/lib/components/user-session/user-session.tsx @@ -1,67 +1,31 @@ -import { useState } from 'react'; import { SignIn } from './components/sign-in'; import css from './user-session.module.scss'; -import { AuthResponse } from '../../domains/auth.domain'; -import { IUserState } from '../../domains/user.domain'; -import { getAuthData, removeAuthData, saveAuthData } from '../../services/local-storage.service'; -import { refreshAuthGoogle } from '../../api/auth/auth'; -import OwnButton from '../button/button'; -import { CONFIG } from '../../../main'; +import { removeAuthData } from '../../services/auth-storage.service'; -export const UserSession = () => { - const [user, setUser] = useState(getAuthData()); +interface IUserSessionProps { + isLoggedIn: boolean; + onLoginSuccess: () => void; + onLogOut: () => void; +} - const handleLoginSuccess = (response: AuthResponse): void => { - setUser({ - accessToken: response.accessToken, - refreshToken: response.refreshToken, - }); - - saveAuthData(response); +export const UserSession = ({ isLoggedIn, onLoginSuccess, onLogOut }: IUserSessionProps) => { + const handleLoginSuccess = (): void => { + onLoginSuccess(); }; const handleLogout = (): void => { removeAuthData(); - setUser(getAuthData()); - }; - - const handleRefresh = async () => { - const authResponse = await refreshAuthGoogle(String(user?.refreshToken)); - - const authData = { - ...user, - accessToken: authResponse.access_token, - }; - - setUser(authData); - saveAuthData(authData); - }; - - // TODO: refactor - const handleBEPing = async () => { - const accessToken = localStorage.getItem('accessToken'); - try { - await fetch(`${CONFIG.BE_URL}/ping`, { - method: 'GET', - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }); - } catch (e) { - console.log('error happened during fetch'); - } + onLogOut(); }; return (
- {user?.accessToken && user?.refreshToken ? ( + {isLoggedIn ? (

Welcome, User!

- - Ping BE
) : ( diff --git a/libs/common-ui/src/lib/services/local-storage.service.ts b/libs/common-ui/src/lib/services/auth-storage.service.ts similarity index 79% rename from libs/common-ui/src/lib/services/local-storage.service.ts rename to libs/common-ui/src/lib/services/auth-storage.service.ts index 407cc34..c2247f2 100644 --- a/libs/common-ui/src/lib/services/local-storage.service.ts +++ b/libs/common-ui/src/lib/services/auth-storage.service.ts @@ -16,3 +16,8 @@ export const removeAuthData = () => { localStorage.removeItem('accessToken'); localStorage.removeItem('refreshToken'); }; + +export const isUserLoggedIn = (): boolean => { + const authData = getAuthData(); + return !!authData.accessToken && !!authData.refreshToken; +}; diff --git a/libs/common-ui/src/lib/utils/util-fetch.ts b/libs/common-ui/src/lib/utils/util-fetch.ts new file mode 100644 index 0000000..59f4eb2 --- /dev/null +++ b/libs/common-ui/src/lib/utils/util-fetch.ts @@ -0,0 +1,28 @@ +import { refreshAuthGoogle } from '../api/auth/auth.api'; +import { getAuthData } from '../services/auth-storage.service'; + +export const utilFetch = async (input: RequestInfo | URL, init?: RequestInit): Promise => { + const oldTokens = getAuthData(); + + const response = await fetch(input, { + ...init, + headers: { + ...init?.headers, + Authorization: `Bearer ${oldTokens.accessToken}`, + }, + }); + + if (response.status === 401) { + const authResponse = await refreshAuthGoogle(); + + return await fetch(input, { + ...init, + headers: { + ...init?.headers, + Authorization: `Bearer ${authResponse.access_token}`, + }, + }); + } + + return response; +};