feat: add match slugs for security and fix message history loading
Security improvements: - Add random CUID slugs to Match model to prevent ID enumeration attacks - Update all match URLs from /matches/:id to /matches/:slug - Keep numeric IDs for internal Socket.IO operations only Backend changes: - Add slug field to matches table with unique index - Update all match endpoints to use slug-based lookups (GET, PUT, DELETE) - Add GET /api/matches/:slug/messages endpoint to fetch message history - Include matchSlug in all Socket.IO notifications Frontend changes: - Update all match routes to use slug parameter - Update MatchesPage to use slug for accept/reject/navigate operations - Update MatchChatPage to fetch match data by slug and load message history - Update RatePartnerPage to use slug parameter - Add matchesAPI.getMatchMessages() function Bug fixes: - Fix MatchChatPage not loading message history from database on mount - Messages now persist and display correctly when users reconnect
This commit is contained in:
@@ -66,13 +66,13 @@ const MatchesPage = () => {
|
||||
|
||||
const handleMatchCancelled = (data) => {
|
||||
// Remove cancelled match from list
|
||||
setMatches(prev => prev.filter(m => m.id !== data.matchId));
|
||||
setMatches(prev => prev.filter(m => m.slug !== data.matchSlug));
|
||||
};
|
||||
|
||||
const handleAccept = async (matchId) => {
|
||||
const handleAccept = async (matchSlug) => {
|
||||
try {
|
||||
setProcessingMatchId(matchId);
|
||||
await matchesAPI.acceptMatch(matchId);
|
||||
setProcessingMatchId(matchSlug);
|
||||
await matchesAPI.acceptMatch(matchSlug);
|
||||
|
||||
// Reload matches
|
||||
await loadMatches();
|
||||
@@ -87,17 +87,17 @@ const MatchesPage = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleReject = async (matchId) => {
|
||||
const handleReject = async (matchSlug) => {
|
||||
if (!confirm('Are you sure you want to reject this match request?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setProcessingMatchId(matchId);
|
||||
await matchesAPI.deleteMatch(matchId);
|
||||
setProcessingMatchId(matchSlug);
|
||||
await matchesAPI.deleteMatch(matchSlug);
|
||||
|
||||
// Remove from list
|
||||
setMatches(prev => prev.filter(m => m.id !== matchId));
|
||||
setMatches(prev => prev.filter(m => m.slug !== matchSlug));
|
||||
} catch (error) {
|
||||
console.error('Failed to reject match:', error);
|
||||
alert('Failed to reject match. Please try again.');
|
||||
@@ -108,7 +108,7 @@ const MatchesPage = () => {
|
||||
|
||||
const handleOpenChat = (match) => {
|
||||
if (match.status === 'accepted' && match.roomId) {
|
||||
navigate(`/matches/${match.id}/chat`);
|
||||
navigate(`/matches/${match.slug}/chat`);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -187,7 +187,7 @@ const MatchesPage = () => {
|
||||
onAccept={handleAccept}
|
||||
onReject={handleReject}
|
||||
onOpenChat={handleOpenChat}
|
||||
processing={processingMatchId === match.id}
|
||||
processing={processingMatchId === match.slug}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -208,7 +208,7 @@ const MatchesPage = () => {
|
||||
onAccept={handleAccept}
|
||||
onReject={handleReject}
|
||||
onOpenChat={handleOpenChat}
|
||||
processing={processingMatchId === match.id}
|
||||
processing={processingMatchId === match.slug}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -295,7 +295,7 @@ const MatchCard = ({ match, onAccept, onReject, onOpenChat, processing }) => {
|
||||
{isIncoming && (
|
||||
<>
|
||||
<button
|
||||
onClick={() => onAccept(match.id)}
|
||||
onClick={() => onAccept(match.slug)}
|
||||
disabled={processing}
|
||||
className="p-2 bg-green-600 text-white rounded-full hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
title="Accept"
|
||||
@@ -307,7 +307,7 @@ const MatchCard = ({ match, onAccept, onReject, onOpenChat, processing }) => {
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onReject(match.id)}
|
||||
onClick={() => onReject(match.slug)}
|
||||
disabled={processing}
|
||||
className="p-2 bg-red-600 text-white rounded-full hover:bg-red-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
title="Reject"
|
||||
@@ -319,7 +319,7 @@ const MatchCard = ({ match, onAccept, onReject, onOpenChat, processing }) => {
|
||||
|
||||
{isOutgoing && (
|
||||
<button
|
||||
onClick={() => onReject(match.id)}
|
||||
onClick={() => onReject(match.slug)}
|
||||
disabled={processing}
|
||||
className="px-3 py-1.5 text-sm border border-gray-300 text-gray-700 rounded-md hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user