fixing link issues
This commit is contained in:
parent
cde6f0495a
commit
37c6c497ff
|
|
@ -23,10 +23,10 @@ SQLITE_STORAGE=./data/luckychit.sqlite
|
||||||
|
|
||||||
JWT_SECRET=change-me-in-production
|
JWT_SECRET=change-me-in-production
|
||||||
|
|
||||||
# Public draw links (/share/draw/...): origin WITHOUT /api. Optional if Nginx sends Host + X-Forwarded-Proto.
|
# Public draw links: /share/draw?t=<jwt> (origin WITHOUT /api). Optional if Nginx sends Host + X-Forwarded-Proto.
|
||||||
# PUBLIC_BASE_URL=https://your-domain.com
|
# PUBLIC_BASE_URL=https://your-domain.com
|
||||||
|
|
||||||
# Signed tokens for /share/draw/:token — defaults to permanent links (no JWT expiry)
|
# Signed tokens for /share/draw — defaults to permanent links (no JWT expiry)
|
||||||
# Set to false to use SHARE_TOKEN_TTL_DAYS instead
|
# Set to false to use SHARE_TOKEN_TTL_DAYS instead
|
||||||
# SHARE_DRAW_PERMANENT_LINKS=true
|
# SHARE_DRAW_PERMANENT_LINKS=true
|
||||||
# SHARE_TOKEN_TTL_DAYS=365
|
# SHARE_TOKEN_TTL_DAYS=365
|
||||||
|
|
|
||||||
|
|
@ -25,61 +25,59 @@ function formatInr(amount) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Public, unauthenticated page — opened via signed token from POST /api/share/draw-link.
|
* Render public draw page from signed JWT (same secret as POST /api/share/draw-link).
|
||||||
* Anyone with the link can view draw results (no login).
|
* Token in query avoids ENAMETOOLONG / proxy issues from huge path segments.
|
||||||
*/
|
*/
|
||||||
router.get('/draw/:token', async (req, res) => {
|
async function renderDrawSharePage(req, res, token) {
|
||||||
|
const shareSecret = process.env.SHARE_TOKEN_SECRET || process.env.JWT_SECRET;
|
||||||
|
if (!shareSecret) {
|
||||||
|
return res.status(500).send('Share is not configured.');
|
||||||
|
}
|
||||||
|
|
||||||
|
let payload;
|
||||||
try {
|
try {
|
||||||
const { token } = req.params;
|
payload = jwt.verify(token, shareSecret);
|
||||||
const shareSecret = process.env.SHARE_TOKEN_SECRET || process.env.JWT_SECRET;
|
} catch {
|
||||||
if (!shareSecret) {
|
return res.status(400).send('Invalid or expired share link.');
|
||||||
return res.status(500).send('Share is not configured.');
|
}
|
||||||
|
|
||||||
|
if (!payload?.type || payload.type !== 'draw_result' || !payload.drawId) {
|
||||||
|
return res.status(400).send('Invalid share link.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const draw = await MonthlyDraw.findByPk(payload.drawId, {
|
||||||
|
include: [
|
||||||
|
{ model: ChitGroup, attributes: ['id', 'name'] },
|
||||||
|
{ model: User, as: 'winner', attributes: ['id', 'full_name', 'mobile_number'] }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!draw) {
|
||||||
|
return res.status(404).send('Draw not found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalMembers = await GroupMember.count({
|
||||||
|
where: { group_id: draw.group_id, status: 'active' }
|
||||||
|
});
|
||||||
|
|
||||||
|
const title = `Draw Result — ${draw.ChitGroup ? draw.ChitGroup.name : 'Chit Group'}`;
|
||||||
|
const winnerName = draw.winner ? draw.winner.full_name : 'TBD';
|
||||||
|
const groupName = draw.ChitGroup ? draw.ChitGroup.name : 'Chit Group';
|
||||||
|
const subtitle = `${draw.month}/${draw.year} • Winner: ${winnerName}`;
|
||||||
|
|
||||||
|
let drawWhen = '';
|
||||||
|
try {
|
||||||
|
const d = draw.draw_date ? new Date(draw.draw_date) : null;
|
||||||
|
if (d && !Number.isNaN(d.getTime())) {
|
||||||
|
drawWhen = d.toLocaleString('en-IN', { dateStyle: 'medium', timeStyle: 'short' });
|
||||||
}
|
}
|
||||||
|
} catch {
|
||||||
|
drawWhen = '';
|
||||||
|
}
|
||||||
|
|
||||||
let payload;
|
const prizeHtml = formatInr(draw.prize_amount);
|
||||||
try {
|
|
||||||
payload = jwt.verify(token, shareSecret);
|
|
||||||
} catch {
|
|
||||||
return res.status(400).send('Invalid or expired share link.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!payload?.type || payload.type !== 'draw_result' || !payload.drawId) {
|
const html = `<!doctype html>
|
||||||
return res.status(400).send('Invalid share link.');
|
|
||||||
}
|
|
||||||
|
|
||||||
const draw = await MonthlyDraw.findByPk(payload.drawId, {
|
|
||||||
include: [
|
|
||||||
{ model: ChitGroup, attributes: ['id', 'name'] },
|
|
||||||
{ model: User, as: 'winner', attributes: ['id', 'full_name', 'mobile_number'] }
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!draw) {
|
|
||||||
return res.status(404).send('Draw not found.');
|
|
||||||
}
|
|
||||||
|
|
||||||
const totalMembers = await GroupMember.count({
|
|
||||||
where: { group_id: draw.group_id, status: 'active' }
|
|
||||||
});
|
|
||||||
|
|
||||||
const title = `Draw Result — ${draw.ChitGroup ? draw.ChitGroup.name : 'Chit Group'}`;
|
|
||||||
const winnerName = draw.winner ? draw.winner.full_name : 'TBD';
|
|
||||||
const groupName = draw.ChitGroup ? draw.ChitGroup.name : 'Chit Group';
|
|
||||||
const subtitle = `${draw.month}/${draw.year} • Winner: ${winnerName}`;
|
|
||||||
|
|
||||||
let drawWhen = '';
|
|
||||||
try {
|
|
||||||
const d = draw.draw_date ? new Date(draw.draw_date) : null;
|
|
||||||
if (d && !Number.isNaN(d.getTime())) {
|
|
||||||
drawWhen = d.toLocaleString('en-IN', { dateStyle: 'medium', timeStyle: 'short' });
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
drawWhen = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
const prizeHtml = formatInr(draw.prize_amount);
|
|
||||||
|
|
||||||
const html = `<!doctype html>
|
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
|
|
@ -136,9 +134,40 @@ router.get('/draw/:token', async (req, res) => {
|
||||||
</body>
|
</body>
|
||||||
</html>`;
|
</html>`;
|
||||||
|
|
||||||
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
||||||
res.setHeader('Cache-Control', 'public, max-age=300');
|
res.setHeader('Cache-Control', 'public, max-age=300');
|
||||||
return res.status(200).send(html);
|
return res.status(200).send(html);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preferred: GET /share/draw?t=<jwt> — short path; avoids ENAMETOOLONG behind Nginx/static rules.
|
||||||
|
*/
|
||||||
|
router.get('/draw', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const raw = req.query.t ?? req.query.token;
|
||||||
|
const token = typeof raw === 'string' ? raw.trim() : '';
|
||||||
|
if (!token) {
|
||||||
|
return res
|
||||||
|
.status(400)
|
||||||
|
.send('Missing token. Use the full link from WhatsApp (it should include ?t=…).');
|
||||||
|
}
|
||||||
|
return await renderDrawSharePage(req, res, token);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Public draw share error:', error);
|
||||||
|
return res.status(500).send('Server error.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legacy: GET /share/draw/:token — may fail on some hosts (path segment too long → ENAMETOOLONG).
|
||||||
|
*/
|
||||||
|
router.get('/draw/:token', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const token = String(req.params.token || '').trim();
|
||||||
|
if (!token) {
|
||||||
|
return res.status(400).send('Missing token.');
|
||||||
|
}
|
||||||
|
return await renderDrawSharePage(req, res, token);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Public draw share error:', error);
|
console.error('Public draw share error:', error);
|
||||||
return res.status(500).send('Server error.');
|
return res.status(500).send('Server error.');
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,8 @@ router.post('/draw-link', auth, requireManager, async (req, res) => {
|
||||||
'PUBLIC_BASE_URL not configured (set to your site origin without /api, e.g. https://chitfund.deepteklabs.com)'
|
'PUBLIC_BASE_URL not configured (set to your site origin without /api, e.g. https://chitfund.deepteklabs.com)'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const shareUrl = `${publicBaseUrl}/share/draw/${token}`;
|
// Query param keeps URL path short (avoids ENAMETOOLONG / static-file lookup on long JWT paths).
|
||||||
|
const shareUrl = `${publicBaseUrl}/share/draw?t=${encodeURIComponent(token)}`;
|
||||||
|
|
||||||
const message = WhatsAppHelper.generateDrawResult(
|
const message = WhatsAppHelper.generateDrawResult(
|
||||||
draw.ChitGroup,
|
draw.ChitGroup,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue