fixed authorization

This commit is contained in:
slawk0
2025-01-02 23:38:04 +01:00
parent 0ac655b83a
commit 9da939c66f
14 changed files with 92 additions and 101 deletions

View File

@@ -1,19 +1,17 @@
import { import {
RouterProvider,
createBrowserRouter, createBrowserRouter,
Navigate, Navigate,
RouterProvider,
} from 'react-router-dom'; } from 'react-router-dom';
import { Suspense } from 'react'; import Chat from './pages/Chat.tsx';
import Chat from './pages/Chat'; import Login from './pages/Login.tsx';
import Login from './pages/Login'; import Signup from './pages/Signup.tsx';
import Signup from './pages/Signup'; import Settings from './pages/Settings.tsx';
import Settings from './pages/Settings'; import Lost from './pages/404.tsx';
import Lost from './pages/404'; import { AuthProvider } from './utils/AuthProvider.tsx';
import { AuthProvider } from '@/utils/AuthProvider.tsx'; import ProtectedRoutes from './utils/ProtectedRoutes.tsx';
import ProtectedRoutes from './utils/ProtectedRoutes'; import { ChatProvider } from '@/context/chat/ChatProvider.tsx';
import PublicRoute from './utils/PublicRoute'; import PublicRoute from '@/utils/PublicRoute.tsx';
import { ChatProvider } from './context/chat/ChatProvider';
import LoadingScreen from './components/LoadingScreen';
const router = createBrowserRouter([ const router = createBrowserRouter([
{ {
@@ -26,11 +24,9 @@ const router = createBrowserRouter([
{ {
path: '/chat', path: '/chat',
element: ( element: (
<Suspense fallback={<LoadingScreen />}> <ChatProvider>
<ChatProvider> <Chat />
<Chat /> </ChatProvider>
</ChatProvider>
</Suspense>
), ),
}, },
{ {

View File

@@ -1,9 +1,9 @@
import { useState } from 'react'; import { useContext, useState } from 'react';
import { Trash2 } from 'lucide-react'; import { Trash2 } from 'lucide-react';
import AttachmentPreview from './AttachmentPreview.tsx'; import AttachmentPreview from './AttachmentPreview.tsx';
import { ChatMessagesProps, UserType } from '@/types/types.ts'; import { ChatMessagesProps } from '@/types/types.ts';
import { useOutletContext } from 'react-router-dom';
import { useChat } from '@/context/chat/useChat.ts'; import { useChat } from '@/context/chat/useChat.ts';
import { AuthContext } from '@/utils/AuthProvider.tsx';
type AnimatedMessageProps = { type AnimatedMessageProps = {
message: ChatMessagesProps; message: ChatMessagesProps;
@@ -11,7 +11,7 @@ type AnimatedMessageProps = {
}; };
const AnimatedMessage = ({ onDelete, message }: AnimatedMessageProps) => { const AnimatedMessage = ({ onDelete, message }: AnimatedMessageProps) => {
const user: UserType = useOutletContext(); const { user } = useContext(AuthContext);
const { me, groupOwner } = useChat(); const { me, groupOwner } = useChat();
const [isRemoving, setIsRemoving] = useState(false); const [isRemoving, setIsRemoving] = useState(false);
@@ -51,7 +51,7 @@ const AnimatedMessage = ({ onDelete, message }: AnimatedMessageProps) => {
)} )}
</div> </div>
{me.isGroupOwner || {me.isGroupOwner ||
message.sender == user.username || message.sender == user?.username ||
(me.isGroupAdmin && (me.isGroupAdmin &&
me.isGroupOwner && me.isGroupOwner &&
message.sender_id !== groupOwner) ? ( message.sender_id !== groupOwner) ? (

View File

@@ -100,6 +100,12 @@ const MessageForm = () => {
}, },
); );
setIsUploading(false); setIsUploading(false);
console.error(response.data);
if (!response.data) {
setErrorMessage('Failed to upload attachments');
return console.error('Failed to upload attachments');
}
return response.data; return response.data;
} catch (e) { } catch (e) {
setIsUploading(false); setIsUploading(false);

View File

@@ -1,11 +1,11 @@
import { useEffect, useRef, useState } from 'react'; import { useContext, useEffect, useRef, useState } from 'react';
import { socket } from '@/socket/socket.ts'; import { socket } from '@/socket/socket.ts';
import { useOutletContext } from 'react-router-dom';
import { sendContact } from '@/api/contactsApi.tsx'; import { sendContact } from '@/api/contactsApi.tsx';
import LoadingWheel from '../LoadingWheel.tsx'; import LoadingWheel from '../LoadingWheel.tsx';
import AnimatedMessage from '@/components/chat/chatArea/AnimatedMessage.tsx'; import AnimatedMessage from '@/components/chat/chatArea/AnimatedMessage.tsx';
import { ChatMessagesProps, UserType } from '@/types/types.ts'; import { ChatMessagesProps } from '@/types/types.ts';
import { useChat } from '@/context/chat/useChat.ts'; import { useChat } from '@/context/chat/useChat.ts';
import { AuthContext } from '@/utils/AuthProvider.tsx';
function MessagesArea() { function MessagesArea() {
const { const {
@@ -19,7 +19,7 @@ function MessagesArea() {
fetchPreviousMessages, fetchPreviousMessages,
} = useChat(); } = useChat();
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const user: UserType = useOutletContext(); const { user } = useContext(AuthContext);
const [isLoading, setIsLoading] = useState<boolean>(false); const [isLoading, setIsLoading] = useState<boolean>(false);
const [shouldScrollToBottom, setShouldScrollToBottom] = useState(true); const [shouldScrollToBottom, setShouldScrollToBottom] = useState(true);
const previousMessagesLength = useRef(messages.length); const previousMessagesLength = useRef(messages.length);
@@ -90,7 +90,7 @@ function MessagesArea() {
console.log('Received message: ', msg); console.log('Received message: ', msg);
if ( if (
msg.conversation_id !== currentContact?.conversation_id && msg.conversation_id !== currentContact?.conversation_id &&
msg.sender !== user.username msg.sender !== user?.username
) { ) {
setContactsList((prevContacts) => { setContactsList((prevContacts) => {
// Find if contact already exists1 // Find if contact already exists1
@@ -197,10 +197,10 @@ function MessagesArea() {
currentContainer.removeEventListener('scroll', handleScroll); currentContainer.removeEventListener('scroll', handleScroll);
} }
socket.off('chat message'); socket.off('chat message');
socket.off('delete message');
}; };
}, [currentContact, user.username, setContactsList, updateContactStatus]); }, [currentContact, user?.username, setContactsList, updateContactStatus]);
// Handle auto-scrolling when new messages arrive
useEffect(() => { useEffect(() => {
const hasNewMessages = messages.length > previousMessagesLength.current; const hasNewMessages = messages.length > previousMessagesLength.current;
previousMessagesLength.current = messages.length; previousMessagesLength.current = messages.length;
@@ -222,7 +222,11 @@ function MessagesArea() {
<ul className="flex-grow list-none"> <ul className="flex-grow list-none">
{isLoading ? <LoadingWheel /> : null} {isLoading ? <LoadingWheel /> : null}
{messages.map((msg: ChatMessagesProps) => ( {messages.map((msg: ChatMessagesProps) => (
<AnimatedMessage message={msg} onDelete={deleteMessage} /> <AnimatedMessage
key={msg.message_id}
message={msg}
onDelete={deleteMessage}
/>
))} ))}
</ul> </ul>
</div> </div>

View File

@@ -12,6 +12,9 @@ const LastActiveTime = ({ contact }: LastActiveTimeProps) => {
useEffect(() => { useEffect(() => {
const updateTime = () => { const updateTime = () => {
if (!contact?.last_message_time) {
return;
}
const lastActiveDate = new Date(contact.last_message_time); const lastActiveDate = new Date(contact.last_message_time);
const secondsDiff = differenceInSeconds(new Date(), lastActiveDate); const secondsDiff = differenceInSeconds(new Date(), lastActiveDate);

View File

@@ -1,11 +1,11 @@
import zdjecie from '../../../../assets/turtleProfileImg3.webp'; import zdjecie from '../../../../assets/turtleProfileImg3.webp';
import logoutIcon from '../../../../assets/logout.svg'; import logoutIcon from '../../../../assets/logout.svg';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import { useOutletContext } from 'react-router-dom'; import { useContext } from 'react';
import { UserType } from '@/types/types.ts'; import { AuthContext } from '@/utils/AuthProvider.tsx';
function UserProfile() { function UserProfile() {
const user: UserType = useOutletContext(); const { user } = useContext(AuthContext);
function logout() { function logout() {
Cookies.remove('token'); Cookies.remove('token');
localStorage.removeItem('contact'); localStorage.removeItem('contact');
@@ -27,7 +27,7 @@ function UserProfile() {
/> />
</div> </div>
<div className="text-gray-200"> <div className="text-gray-200">
<p>{user.username}</p> <p>{user?.username}</p>
</div> </div>
</div> </div>

View File

@@ -1,4 +1,4 @@
import { useEffect, useMemo, useState } from 'react'; import { useContext, useEffect, useMemo, useState } from 'react';
import { socket } from '@/socket/socket.ts'; import { socket } from '@/socket/socket.ts';
import { Crown, Sword } from 'lucide-react'; import { Crown, Sword } from 'lucide-react';
import { import {
@@ -7,17 +7,17 @@ import {
ContextMenuItem, ContextMenuItem,
ContextMenuTrigger, ContextMenuTrigger,
} from '@/components/ui/context-menu.tsx'; } from '@/components/ui/context-menu.tsx';
import { useOutletContext } from 'react-router-dom';
import zdjecie from '../../../../assets/turtleProfileImg3.webp'; import zdjecie from '../../../../assets/turtleProfileImg3.webp';
import { ParticipantsProps, UserType } from '@/types/types.ts'; import { ParticipantsProps } from '@/types/types.ts';
import { useChat } from '@/context/chat/useChat.ts'; import { useChat } from '@/context/chat/useChat.ts';
import { axiosClient } from '@/utils/axiosClient.ts'; import { axiosClient } from '@/utils/axiosClient.ts';
import { AuthContext } from '@/utils/AuthProvider.tsx';
function ParticipantsBar() { function ParticipantsBar() {
const { setMe, initializeContact, setGroupOwner, currentContact, me } = const { setMe, initializeContact, setGroupOwner, currentContact, me } =
useChat(); useChat();
const [participants, setParticipants] = useState<ParticipantsProps[]>([]); const [participants, setParticipants] = useState<ParticipantsProps[]>([]);
const user: UserType = useOutletContext(); const { user } = useContext(AuthContext);
const getParticipants = async () => { const getParticipants = async () => {
try { try {
const response = await axiosClient.get( const response = await axiosClient.get(
@@ -90,7 +90,6 @@ function ParticipantsBar() {
}; };
useEffect(() => { useEffect(() => {
console.error(participants.length, user.id);
if (participants.length > 0 && user?.id) { if (participants.length > 0 && user?.id) {
const userIsAdmin = participants.some( const userIsAdmin = participants.some(
(participant) => participant.user_id === user.id && participant.isadmin, (participant) => participant.user_id === user.id && participant.isadmin,
@@ -104,7 +103,6 @@ function ParticipantsBar() {
); );
setGroupOwner(whoIsOwner?.user_id); setGroupOwner(whoIsOwner?.user_id);
setMe({ isGroupAdmin: userIsAdmin, isGroupOwner: userIsOwner }); setMe({ isGroupAdmin: userIsAdmin, isGroupOwner: userIsOwner });
console.error('SETME: ', userIsAdmin, userIsOwner);
} }
}, [participants, user?.id]); }, [participants, user?.id]);
@@ -256,7 +254,7 @@ function ParticipantsBar() {
</span> </span>
</li> </li>
</ContextMenuTrigger> </ContextMenuTrigger>
{user.id !== participant.user_id && {user?.id !== participant.user_id &&
me.isGroupAdmin && me.isGroupAdmin &&
(!participant.isadmin || me.isGroupOwner) ? ( (!participant.isadmin || me.isGroupOwner) ? (
<ContextMenuContent className="p-0"> <ContextMenuContent className="p-0">

View File

@@ -2,10 +2,10 @@ import { useForm, SubmitHandler } from 'react-hook-form';
import icon from '../../assets/icon.png'; import icon from '../../assets/icon.png';
import { Link, useNavigate } from 'react-router-dom'; import { Link, useNavigate } from 'react-router-dom';
import { useContext, useState } from 'react'; import { useContext, useState } from 'react';
import { AuthContext } from '@/utils/AuthContext.tsx';
import LoadingWheel from '../components/chat/LoadingWheel.tsx'; import LoadingWheel from '../components/chat/LoadingWheel.tsx';
import { axiosClient } from '@/utils/axiosClient.ts'; import { axiosClient } from '@/utils/axiosClient.ts';
import { AuthContext } from '@/utils/AuthProvider.tsx';
export type Inputs = { export type Inputs = {
username: string; username: string;
@@ -31,7 +31,7 @@ export default function Login() {
setAuthorized(true); setAuthorized(true);
setIsLoading(false); setIsLoading(false);
console.log('redirecting'); console.log('redirecting');
navigate('/chat'); navigate('/');
}) })
.catch((err) => { .catch((err) => {
if (err.response) { if (err.response) {

View File

@@ -2,9 +2,9 @@ import icon from '../../assets/icon.png';
import { useForm, SubmitHandler } from 'react-hook-form'; import { useForm, SubmitHandler } from 'react-hook-form';
import { Link, useNavigate } from 'react-router-dom'; import { Link, useNavigate } from 'react-router-dom';
import { useContext, useState } from 'react'; import { useContext, useState } from 'react';
import { AuthContext } from '@/utils/AuthContext.tsx';
import LoadingWheel from '../components/chat/LoadingWheel.tsx'; import LoadingWheel from '../components/chat/LoadingWheel.tsx';
import { axiosClient } from '@/utils/axiosClient.ts'; import { axiosClient } from '@/utils/axiosClient.ts';
import { AuthContext } from '@/utils/AuthProvider.tsx';
type Inputs = { type Inputs = {
username: string; username: string;
@@ -44,7 +44,7 @@ export default function Signup() {
.then(() => { .then(() => {
setAuthorized(true); setAuthorized(true);
setIsLoading(false); setIsLoading(false);
navigate('/chat'); navigate('/');
console.log('Signed up'); console.log('Signed up');
}) })
.catch((err) => { .catch((err) => {

View File

@@ -1,16 +0,0 @@
import { createContext } from 'react';
import { UserType } from '@/types/types.ts';
interface AuthContextType {
authorized: boolean;
isLoading: boolean;
user: UserType | null;
setAuthorized: (value: boolean) => void;
}
export const AuthContext = createContext<AuthContextType>({
authorized: false,
isLoading: true,
user: null,
setAuthorized: () => {},
});

View File

@@ -1,47 +1,52 @@
import { AuthContext } from './AuthContext'; import { createContext, useState, useEffect, ReactNode } from 'react';
import Cookies from 'js-cookie'; import Cookie from 'js-cookie';
import { axiosClient } from '@/utils/axiosClient.ts'; import { axiosClient } from '@/utils/axiosClient';
import { ReactNode, useEffect, useState } from 'react';
import { UserType } from '@/types/types.ts'; import { UserType } from '@/types/types.ts';
type AuthContextType = {
authorized: boolean | null;
setAuthorized: (value: boolean) => void;
user: UserType | null;
setUser: (user: UserType | null) => void;
};
export const AuthContext = createContext<AuthContextType>({
authorized: null,
setAuthorized: () => {},
user: null,
setUser: () => {},
});
export function AuthProvider({ children }: { children: ReactNode }) { export function AuthProvider({ children }: { children: ReactNode }) {
const [authorized, setAuthorized] = useState(false); const [authorized, setAuthorized] = useState<boolean | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [user, setUser] = useState<UserType | null>(null); const [user, setUser] = useState<UserType | null>(null);
const token = Cookies.get('token');
useEffect(() => { useEffect(() => {
async function validateAuth() { async function validateToken() {
const token = Cookie.get('token');
if (!token) { if (!token) {
setIsLoading(false); setAuthorized(false);
return; return;
} }
try { try {
const response = await axiosClient.get('/api/auth/validate', { const res = await axiosClient.get('/api/auth/validate', {
withCredentials: true, withCredentials: true,
}); });
setUser({ setUser({ username: res.data.username, id: res.data.user_id });
username: response.data.username,
id: response.data.user_id,
});
setAuthorized(true); setAuthorized(true);
} catch (error) { } catch (err) {
console.error('Auth validation failed:', error);
setAuthorized(false); setAuthorized(false);
setUser(null); setUser(null);
} finally { console.error('Token validation failed:', err);
setIsLoading(false);
} }
} }
validateAuth(); validateToken();
}, [token]); }, []);
return ( return (
<AuthContext.Provider <AuthContext.Provider value={{ authorized, setAuthorized, user, setUser }}>
value={{ authorized, isLoading, user, setAuthorized }}
>
{children} {children}
</AuthContext.Provider> </AuthContext.Provider>
); );

View File

@@ -1,18 +1,16 @@
import { Navigate, Outlet } from 'react-router-dom'; import { Navigate, Outlet } from 'react-router-dom';
import { useAuth } from '@/utils/useAuth.tsx'; import { useContext } from 'react';
import LoadingScreen from '@/components/LoadingScreen'; import { AuthContext } from './AuthProvider';
import LoadingScreen from '../components/LoadingScreen';
function ProtectedRoutes() { function ProtectedRoutes() {
const { authorized, isLoading, user } = useAuth(); const { authorized } = useContext(AuthContext);
if (isLoading) {
if (authorized === null) {
return <LoadingScreen />; return <LoadingScreen />;
} }
return authorized ? ( return authorized ? <Outlet /> : <Navigate to="/login" replace />;
<Outlet context={user} />
) : (
<Navigate to="/login" replace />
);
} }
export default ProtectedRoutes; export default ProtectedRoutes;

View File

@@ -1,11 +1,12 @@
import { Navigate, Outlet } from 'react-router-dom'; import { Navigate, Outlet } from 'react-router-dom';
import { useAuth } from '@/utils/useAuth.tsx'; import { useContext } from 'react';
import LoadingScreen from '@/components/LoadingScreen'; import { AuthContext } from './AuthProvider';
import LoadingScreen from '../components/LoadingScreen';
function PublicRoute() { function PublicRoute() {
const { authorized, isLoading } = useAuth(); const { authorized } = useContext(AuthContext);
if (isLoading) { if (authorized === null) {
return <LoadingScreen />; return <LoadingScreen />;
} }

View File

@@ -1,4 +0,0 @@
import { useContext } from 'react';
import { AuthContext } from '@/utils/AuthContext.tsx';
export const useAuth = () => useContext(AuthContext);