Change pins to alphanumeric
This commit is contained in:
parent
a5f2f19898
commit
bfcc33cc50
3 changed files with 65 additions and 32 deletions
|
|
@ -568,8 +568,8 @@ export const Landing: React.FC<LandingProps> = ({ onGenerate, onCreateManual, on
|
|||
type="text"
|
||||
placeholder="Game PIN"
|
||||
value={pin}
|
||||
onChange={(e) => setPin(e.target.value)}
|
||||
className="w-full p-4 text-xl font-bold border-2 border-gray-200 rounded-2xl focus:border-theme-primary focus:ring-4 focus:ring-theme-primary/20 outline-none text-center"
|
||||
onChange={(e) => setPin(e.target.value.toUpperCase().replace(/[^A-Z0-9]/g, '').slice(0, 6))}
|
||||
className="w-full p-4 text-xl font-bold border-2 border-gray-200 rounded-2xl focus:border-theme-primary focus:ring-4 focus:ring-theme-primary/20 outline-none text-center uppercase tracking-widest"
|
||||
/>
|
||||
{gameInfo?.randomNamesEnabled ? (
|
||||
<div className="p-4 bg-theme-primary/10 rounded-2xl border-2 border-theme-primary/20">
|
||||
|
|
|
|||
|
|
@ -180,7 +180,14 @@ export const useGame = () => {
|
|||
}
|
||||
}, [auth.isLoading, auth.isAuthenticated, location.pathname]);
|
||||
|
||||
const generateGamePin = () => Math.floor(Math.random() * 900000) + 100000 + "";
|
||||
const generateGamePin = () => {
|
||||
const chars = 'ABCDEFGHJKMNPQRSTUVWXYZ23456789';
|
||||
let pin = '';
|
||||
for (let i = 0; i < 6; i++) {
|
||||
pin += chars.charAt(Math.floor(Math.random() * chars.length));
|
||||
}
|
||||
return pin;
|
||||
};
|
||||
|
||||
const generateRandomName = (): string => {
|
||||
return uniqueNamesGenerator({
|
||||
|
|
@ -214,30 +221,43 @@ export const useGame = () => {
|
|||
}
|
||||
}, []);
|
||||
|
||||
const createGameSession = async (pin: string, peerId: string, quizData: Quiz, config: GameConfig): Promise<string | null> => {
|
||||
try {
|
||||
const response = await fetch(`${BACKEND_URL}/api/games`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
pin,
|
||||
hostPeerId: peerId,
|
||||
quiz: quizData,
|
||||
gameConfig: config,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('Failed to create game session');
|
||||
const createGameSession = async (pin: string, peerId: string, quizData: Quiz, config: GameConfig): Promise<{ hostSecret: string; pin: string } | null> => {
|
||||
const maxRetries = 5;
|
||||
let currentPin = pin;
|
||||
|
||||
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
||||
try {
|
||||
const response = await fetch(`${BACKEND_URL}/api/games`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
pin: currentPin,
|
||||
hostPeerId: peerId,
|
||||
quiz: quizData,
|
||||
gameConfig: config,
|
||||
}),
|
||||
});
|
||||
|
||||
if (response.status === 409) {
|
||||
currentPin = generateGamePin();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('Failed to create game session');
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return { hostSecret: data.hostSecret, pin: currentPin };
|
||||
} catch (err) {
|
||||
console.error('Error creating game session:', err);
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data.hostSecret;
|
||||
} catch (err) {
|
||||
console.error('Error creating game session:', err);
|
||||
return null;
|
||||
}
|
||||
|
||||
console.error('Failed to create game after max retries');
|
||||
return null;
|
||||
};
|
||||
|
||||
const updateHostPeerId = async (pin: string, secret: string, newPeerId: string) => {
|
||||
|
|
@ -340,18 +360,23 @@ export const useGame = () => {
|
|||
|
||||
const initializeHostGame = async (newQuiz: Quiz, hostParticipates: boolean = true) => {
|
||||
setQuiz(newQuiz);
|
||||
const pin = generateGamePin();
|
||||
setGamePin(pin);
|
||||
const initialPin = generateGamePin();
|
||||
setGamePin(initialPin);
|
||||
|
||||
setupHostPeer(pin, async (peerId) => {
|
||||
const secret = await createGameSession(pin, peerId, newQuiz, gameConfigRef.current);
|
||||
setupHostPeer(initialPin, async (peerId) => {
|
||||
const result = await createGameSession(initialPin, peerId, newQuiz, gameConfigRef.current);
|
||||
|
||||
if (!secret) {
|
||||
if (!result) {
|
||||
setError("Failed to create game. Please try again.");
|
||||
setGameState('LANDING');
|
||||
return;
|
||||
}
|
||||
|
||||
const { hostSecret: secret, pin } = result;
|
||||
if (pin !== initialPin) {
|
||||
setGamePin(pin);
|
||||
}
|
||||
|
||||
setHostSecret(secret);
|
||||
storeSession({ pin, role: 'HOST', hostSecret: secret });
|
||||
|
||||
|
|
@ -498,8 +523,8 @@ export const useGame = () => {
|
|||
return;
|
||||
}
|
||||
|
||||
const hostMatch = path.match(/^\/host\/(\d+)$/);
|
||||
const playMatch = path.match(/^\/play\/(\d+)$/);
|
||||
const hostMatch = path.match(/^\/host\/([A-Z0-9]+)$/i);
|
||||
const playMatch = path.match(/^\/play\/([A-Z0-9]+)$/i);
|
||||
const session = getStoredSession();
|
||||
const pinFromUrl = hostMatch ? hostMatch[1] : (playMatch ? playMatch[1] : null);
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,14 @@ const gameCreationLimiter = rateLimit({
|
|||
message: { error: 'Too many game creations, please try again later.' },
|
||||
});
|
||||
|
||||
const gameLookupLimiter = rateLimit({
|
||||
windowMs: 60 * 1000,
|
||||
max: isDev ? 500 : 100,
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
message: { error: 'Too many requests, please try again later.' },
|
||||
});
|
||||
|
||||
const SESSION_TTL_MINUTES = parseInt(process.env.GAME_SESSION_TTL_MINUTES || '5', 10);
|
||||
|
||||
interface GameSession {
|
||||
|
|
@ -71,7 +79,7 @@ router.post('/', gameCreationLimiter, (req: Request, res: Response) => {
|
|||
}
|
||||
});
|
||||
|
||||
router.get('/:pin', (req: Request, res: Response) => {
|
||||
router.get('/:pin', gameLookupLimiter, (req: Request, res: Response) => {
|
||||
try {
|
||||
const { pin } = req.params;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue