Implement backend for blackjack game with Discord authentication, database integration, and WebSocket support
This commit is contained in:
80
src/routes/auth.js
Normal file
80
src/routes/auth.js
Normal file
@@ -0,0 +1,80 @@
|
||||
import { Router } from 'express';
|
||||
import crypto from 'crypto';
|
||||
import { getDiscordAuthUrl, exchangeCodeForToken, fetchDiscordUser } from '../discord.js';
|
||||
import { query } from '../db.js';
|
||||
import { signToken } from '../auth.js';
|
||||
|
||||
const router = Router();
|
||||
const stateStore = new Map();
|
||||
const STATE_TTL_MS = 10 * 60 * 1000;
|
||||
|
||||
function storeState(state, redirect) {
|
||||
stateStore.set(state, { redirect, createdAt: Date.now() });
|
||||
}
|
||||
|
||||
function consumeState(state) {
|
||||
const entry = stateStore.get(state);
|
||||
stateStore.delete(state);
|
||||
if (!entry) {
|
||||
return null;
|
||||
}
|
||||
if (Date.now() - entry.createdAt > STATE_TTL_MS) {
|
||||
return null;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
router.get('/auth/discord/url', (req, res) => {
|
||||
const state = crypto.randomUUID();
|
||||
const redirect = req.query.redirect?.toString() || process.env.DEFAULT_APP_REDIRECT || '';
|
||||
storeState(state, redirect);
|
||||
const url = getDiscordAuthUrl(state);
|
||||
res.json({ url });
|
||||
});
|
||||
|
||||
router.get('/auth/discord/callback', async (req, res) => {
|
||||
try {
|
||||
const { code, state } = req.query;
|
||||
if (!code || !state) {
|
||||
return res.status(400).send('Hibas visszairanyitas.');
|
||||
}
|
||||
|
||||
const entry = consumeState(state.toString());
|
||||
if (!entry) {
|
||||
return res.status(400).send('Ervenytelen state.');
|
||||
}
|
||||
|
||||
const tokenResponse = await exchangeCodeForToken(code.toString());
|
||||
const discordUser = await fetchDiscordUser(tokenResponse.access_token);
|
||||
|
||||
const existing = await query('SELECT id FROM users WHERE discord_id = ?', [discordUser.id]);
|
||||
let userId;
|
||||
if (existing.length > 0) {
|
||||
userId = existing[0].id;
|
||||
await query('UPDATE users SET username = ?, avatar = ? WHERE id = ?', [
|
||||
discordUser.username,
|
||||
discordUser.avatar || '',
|
||||
userId
|
||||
]);
|
||||
} else {
|
||||
const result = await query(
|
||||
'INSERT INTO users (discord_id, username, avatar, balance) VALUES (?, ?, ?, ?)',
|
||||
[discordUser.id, discordUser.username, discordUser.avatar || '', 0]
|
||||
);
|
||||
userId = result.insertId;
|
||||
}
|
||||
|
||||
const token = signToken({ id: userId, username: discordUser.username });
|
||||
|
||||
if (entry.redirect) {
|
||||
const separator = entry.redirect.includes('?') ? '&' : '?';
|
||||
return res.redirect(`${entry.redirect}${separator}token=${encodeURIComponent(token)}`);
|
||||
}
|
||||
|
||||
return res.json({ token });
|
||||
} catch (err) {
|
||||
return res.status(500).send('Hiba a bejelentkezes kozben.');
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
20
src/routes/lobby.js
Normal file
20
src/routes/lobby.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Router } from 'express';
|
||||
import { authMiddleware } from '../auth.js';
|
||||
import { query } from '../db.js';
|
||||
import { listTables } from '../game/tables.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get('/api/me', authMiddleware, async (req, res) => {
|
||||
const rows = await query('SELECT id, username, avatar, balance FROM users WHERE id = ?', [req.userId]);
|
||||
if (!rows[0]) {
|
||||
return res.status(404).json({ error: 'Nincs felhasznalo.' });
|
||||
}
|
||||
return res.json(rows[0]);
|
||||
});
|
||||
|
||||
router.get('/api/tables', authMiddleware, (req, res) => {
|
||||
return res.json({ tables: listTables() });
|
||||
});
|
||||
|
||||
export default router;
|
||||
49
src/routes/stripe.js
Normal file
49
src/routes/stripe.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import express, { Router } from 'express';
|
||||
import Stripe from 'stripe';
|
||||
import { query } from '../db.js';
|
||||
|
||||
const router = Router();
|
||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY || '', {
|
||||
apiVersion: '2024-06-20'
|
||||
});
|
||||
|
||||
router.post('/api/stripe/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
|
||||
const signature = req.headers['stripe-signature'];
|
||||
let event;
|
||||
|
||||
try {
|
||||
event = stripe.webhooks.constructEvent(
|
||||
req.body,
|
||||
signature,
|
||||
process.env.STRIPE_WEBHOOK_SECRET || ''
|
||||
);
|
||||
} catch (err) {
|
||||
return res.status(400).send('Webhook alairas hiba.');
|
||||
}
|
||||
|
||||
if (event.type === 'payment_intent.succeeded') {
|
||||
const intent = event.data.object;
|
||||
const amount = Number(intent.amount || 0);
|
||||
const userId = Number(intent.metadata?.userId || 0);
|
||||
|
||||
if (userId && amount) {
|
||||
const rows = await query(
|
||||
'SELECT status FROM deposits WHERE stripe_payment_intent_id = ?',
|
||||
[intent.id]
|
||||
);
|
||||
const status = rows[0]?.status;
|
||||
|
||||
if (status !== 'succeeded') {
|
||||
await query(
|
||||
'UPDATE deposits SET status = ? WHERE stripe_payment_intent_id = ?',
|
||||
['succeeded', intent.id]
|
||||
);
|
||||
await query('UPDATE users SET balance = balance + ? WHERE id = ?', [amount, userId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res.json({ received: true });
|
||||
});
|
||||
|
||||
export default router;
|
||||
38
src/routes/wallet.js
Normal file
38
src/routes/wallet.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import { Router } from 'express';
|
||||
import Stripe from 'stripe';
|
||||
import { authMiddleware } from '../auth.js';
|
||||
import { query } from '../db.js';
|
||||
|
||||
const router = Router();
|
||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY || '', {
|
||||
apiVersion: '2024-06-20'
|
||||
});
|
||||
|
||||
router.post('/api/wallet/deposit-intent', authMiddleware, async (req, res) => {
|
||||
try {
|
||||
const amount = Number(req.body.amount);
|
||||
if (!Number.isFinite(amount) || amount < 50 || amount > 100) {
|
||||
return res.status(400).json({ error: 'A feltoltes 50 es 100 Ft kozott lehet.' });
|
||||
}
|
||||
|
||||
const paymentIntent = await stripe.paymentIntents.create({
|
||||
amount,
|
||||
currency: 'huf',
|
||||
automatic_payment_methods: { enabled: true },
|
||||
metadata: {
|
||||
userId: String(req.userId)
|
||||
}
|
||||
});
|
||||
|
||||
await query(
|
||||
'INSERT INTO deposits (user_id, amount, stripe_payment_intent_id, status) VALUES (?, ?, ?, ?)',
|
||||
[req.userId, amount, paymentIntent.id, 'created']
|
||||
);
|
||||
|
||||
return res.json({ clientSecret: paymentIntent.client_secret });
|
||||
} catch (err) {
|
||||
return res.status(500).json({ error: 'Nem sikerult letrehozni a fizetest.' });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user