mirror_match/components/HelpModal.tsx
ilia df865dca41
All checks were successful
CI / skip-ci-check (push) Successful in 1m25s
CI / lint-and-type-check (push) Successful in 1m50s
CI / test (push) Successful in 1m54s
CI / build (push) Successful in 1m54s
CI / secret-scanning (push) Successful in 1m26s
CI / dependency-scan (push) Successful in 1m31s
CI / sast-scan (push) Successful in 2m34s
CI / workflow-summary (push) Successful in 1m23s
This MR fixes critical authentication issues that prevented login on localhost and improves the developer experience with consolidated rebuild scripts and a working help modal keyboard shortcut. (#5)
# Fix authentication issues and improve developer experience

## Summary

This MR fixes critical authentication issues that prevented login on localhost and improves the developer experience with consolidated rebuild scripts and a working help modal keyboard shortcut.

## Problems Fixed

### 1. Authentication Issues
- **UntrustedHost Error**: NextAuth v5 was rejecting localhost requests with "UntrustedHost: Host must be trusted" error
- **Cookie Prefix Errors**: Cookies were being set with `__Host-` and `__Secure-` prefixes on HTTP (localhost), causing browser rejection
- **MissingCSRF Error**: CSRF token cookies were not being set correctly due to cookie configuration issues

### 2. Help Modal Keyboard Shortcut
- **Shift+? not working**: The help modal keyboard shortcut was not detecting the question mark key correctly

### 3. Developer Experience
- **Multiple rebuild scripts**: Had several overlapping rebuild scripts that were confusing
- **Unused code**: Removed unused `useSecureCookies` variable and misleading comments

## Changes Made

### Authentication Fixes (`lib/auth.ts`)
- Set `trustHost: true` to fix UntrustedHost error (required for NextAuth v5)
- Added explicit cookie configuration for HTTP (localhost) to prevent prefix errors:
  - Cookies use `secure: false` for HTTP
  - Cookie names without prefixes for HTTP
  - Let Auth.js defaults handle HTTPS (with prefixes and Secure flag)
- Removed unused `useSecureCookies` variable
- Simplified debug logging

### Help Modal Fix (`components/HelpModal.tsx`)
- Fixed keyboard shortcut detection to properly handle Shift+? (Shift+/)
- Updated help text to show correct shortcut (Shift+? instead of Ctrl+?)

### Developer Scripts
- **Consolidated rebuild scripts**: Merged `CLEAN_REBUILD.sh`, `FIX_AND_RESTART.sh`, and `start-server.sh` into single `rebuild.sh`
- **Added REBUILD.md**: Documentation for rebuild process
- Removed redundant script files

### Code Cleanup
- Removed unused `useSecureCookies` variable from `lib/auth.ts`
- Removed misleading comment from `app/api/auth/[...nextauth]/route.ts`
- Cleaned up verbose debug logging

## Technical Details

### Cookie Configuration
The fix works by explicitly configuring cookies for HTTP environments:
- **HTTP (localhost)**: Cookies without prefixes, `secure: false`
- **HTTPS (production)**: Let Auth.js defaults handle (prefixes + Secure flag)

This prevents NextAuth v5 from auto-detecting HTTPS from proxy headers and incorrectly adding cookie prefixes.

### Keyboard Shortcut
The question mark key requires Shift+/ on most keyboards. The fix now properly detects:
- `event.shiftKey && event.key === "/"`
- `event.key === "?"` (fallback)
- `event.code === "Slash" && event.shiftKey` (additional fallback)

## Testing

-  Login works on localhost (http://localhost:3000)
-  No cookie prefix errors in browser console
-  No UntrustedHost errors in server logs
-  Help modal opens/closes with Shift+?
-  Rebuild script works in both dev and prod modes

## Files Changed

### Modified
- `lib/auth.ts` - Authentication configuration fixes
- `components/HelpModal.tsx` - Keyboard shortcut fix
- `app/api/auth/[...nextauth]/route.ts` - Removed misleading comment

### Added
- `rebuild.sh` - Consolidated rebuild script
- `REBUILD.md` - Rebuild documentation

## Migration Notes

No database migrations or environment variable changes required. The fix works with existing configuration.

## Related Issues

Fixes authentication issues preventing local development and testing.

Reviewed-on: #5
2026-01-05 19:42:46 -05:00

286 lines
12 KiB
TypeScript

"use client"
import { useState, useEffect } from "react"
export default function HelpModal() {
const [isOpen, setIsOpen] = useState(false)
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
// Check for Shift+? (Shift+/ produces "?")
// Also check for event.key === "?" as fallback for some keyboard layouts
const isQuestionMark =
(event.shiftKey && event.key === "/") ||
event.key === "?" ||
(event.code === "Slash" && event.shiftKey)
// Only trigger if Shift is pressed (not Ctrl/Cmd)
if (event.shiftKey && isQuestionMark && !event.ctrlKey && !event.metaKey) {
event.preventDefault()
setIsOpen((prev) => !prev)
}
// Also close on Escape key
if (event.key === "Escape" && isOpen) {
setIsOpen(false)
}
}
window.addEventListener("keydown", handleKeyDown)
return () => window.removeEventListener("keydown", handleKeyDown)
}, [isOpen])
if (!isOpen) return null
return (
<>
{/* Overlay */}
<div
className="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center p-4"
onClick={() => setIsOpen(false)}
>
{/* Modal */}
<div
className="bg-white rounded-lg shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto z-50"
onClick={(e) => e.stopPropagation()}
>
{/* Header */}
<div className="sticky top-0 bg-gradient-to-r from-purple-600 to-indigo-600 text-white p-6 rounded-t-lg flex justify-between items-center">
<h2 className="text-2xl font-bold">Welcome to MirrorMatch</h2>
<button
onClick={() => setIsOpen(false)}
className="p-2 hover:bg-purple-700 rounded-md transition"
aria-label="Close help"
>
<svg
className="h-6 w-6"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
{/* Content */}
<div className="p-6 space-y-6 text-gray-700">
{/* What is MirrorMatch */}
<section>
<h3 className="text-xl font-semibold text-gray-900 mb-3 flex items-center">
<svg
className="h-5 w-5 mr-2 text-purple-600"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
What is MirrorMatch?
</h3>
<p className="text-gray-700 leading-relaxed">
MirrorMatch is a fun and engaging photo guessing game where you can upload photos
and challenge other players to guess who is in the picture. Test your knowledge of
friends, family, or colleagues while competing for points on the leaderboard!
</p>
</section>
{/* How to Play */}
<section>
<h3 className="text-xl font-semibold text-gray-900 mb-3 flex items-center">
<svg
className="h-5 w-5 mr-2 text-purple-600"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"
/>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
How to Play
</h3>
<ol className="list-decimal list-inside space-y-2 text-gray-700">
<li>
<strong>Upload Photos:</strong> Go to the Upload page and add photos with answer
names. You can upload files or use image URLs.
</li>
<li>
<strong>Guess Photos:</strong> Browse the Photos page and click on any photo to
make your guess. Enter the name of the person you think is in the picture.
</li>
<li>
<strong>Earn Points:</strong> Get points for each correct guess! The more you
guess correctly, the higher you&apos;ll climb on the leaderboard.
</li>
<li>
<strong>Compete:</strong> Check the Leaderboard to see how you rank against other
players.
</li>
</ol>
</section>
{/* Features */}
<section>
<h3 className="text-xl font-semibold text-gray-900 mb-3 flex items-center">
<svg
className="h-5 w-5 mr-2 text-purple-600"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z"
/>
</svg>
Key Features
</h3>
<ul className="space-y-2 text-gray-700">
<li className="flex items-start">
<span className="text-purple-600 mr-2"></span>
<span>
<strong>Photo Upload:</strong> Share photos via file upload or URL
</span>
</li>
<li className="flex items-start">
<span className="text-purple-600 mr-2"></span>
<span>
<strong>Guessing System:</strong> Submit guesses and get instant feedback
</span>
</li>
<li className="flex items-start">
<span className="text-purple-600 mr-2"></span>
<span>
<strong>Email Notifications:</strong> Get notified when new photos are
uploaded
</span>
</li>
<li className="flex items-start">
<span className="text-purple-600 mr-2"></span>
<span>
<strong>Leaderboard:</strong> Track rankings and compete with others
</span>
</li>
<li className="flex items-start">
<span className="text-purple-600 mr-2"></span>
<span>
<strong>Profile Management:</strong> View your points and manage your account
</span>
</li>
</ul>
</section>
{/* Keyboard Shortcuts */}
<section>
<h3 className="text-xl font-semibold text-gray-900 mb-3 flex items-center">
<svg
className="h-5 w-5 mr-2 text-purple-600"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
/>
</svg>
Keyboard Shortcuts
</h3>
<div className="bg-gray-50 rounded-md p-4 space-y-2">
<div className="flex items-center justify-between">
<span className="text-gray-700">
<kbd className="px-2 py-1 bg-white border border-gray-300 rounded text-sm font-mono">
Shift
</kbd>
{" + "}
<kbd className="px-2 py-1 bg-white border border-gray-300 rounded text-sm font-mono">
?
</kbd>
</span>
<span className="text-gray-600">Open/Close this help window</span>
</div>
<div className="flex items-center justify-between">
<span className="text-gray-700">
<kbd className="px-2 py-1 bg-white border border-gray-300 rounded text-sm font-mono">
Esc
</kbd>
</span>
<span className="text-gray-600">Close help window</span>
</div>
</div>
</section>
{/* Tips */}
<section>
<h3 className="text-xl font-semibold text-gray-900 mb-3 flex items-center">
<svg
className="h-5 w-5 mr-2 text-purple-600"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"
/>
</svg>
Tips
</h3>
<ul className="space-y-2 text-gray-700">
<li className="flex items-start">
<span className="text-purple-600 mr-2">💡</span>
<span>Guesses are case-insensitive, so don&apos;t worry about capitalization</span>
</li>
<li className="flex items-start">
<span className="text-purple-600 mr-2">💡</span>
<span>You can&apos;t guess your own photos, but you can still view them</span>
</li>
<li className="flex items-start">
<span className="text-purple-600 mr-2">💡</span>
<span>
You&apos;ll receive email notifications when other users upload new photos
</span>
</li>
<li className="flex items-start">
<span className="text-purple-600 mr-2">💡</span>
<span>Check the leaderboard regularly to see your ranking</span>
</li>
</ul>
</section>
</div>
{/* Footer */}
<div className="bg-gray-50 p-4 rounded-b-lg border-t border-gray-200">
<p className="text-sm text-gray-600 text-center">
Press <kbd className="px-2 py-1 bg-white border border-gray-300 rounded text-xs font-mono">Esc</kbd> or click outside to close
</p>
</div>
</div>
</div>
</>
)
}