v1.2.0: disable CSRF for Tor, fix wallet type validation, add version history modal
- fix(admin/csrf): completely disable CSRF checks for Tor/onion compatibility - fix(validators): add 'main' and 'bonus' to WALLET_TYPES for purchase flow - feat(admin): add clickable version tag with version history modal in sidebar - docs: add VERSION.md with changelog and update instructions
This commit is contained in:
34
VERSION.md
Normal file
34
VERSION.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Telegram Shop — Version History
|
||||
|
||||
## How to update version
|
||||
|
||||
1. Edit this file (`VERSION.md`)
|
||||
2. Add new entry under `## Changelog`
|
||||
3. Bump version in `src/admin/views/partials/app-sidebar.ejs`
|
||||
4. Commit all changes together
|
||||
|
||||
## Current Version
|
||||
|
||||
**v1.2.0** — 2026-07-08
|
||||
|
||||
## Changelog
|
||||
|
||||
### v1.2.0 — 2026-07-08
|
||||
- **fix**: Disabled CSRF checks in admin panel for Tor / onion zone compatibility
|
||||
- **fix**: Fixed "Invalid wallet type" error in Telegram bot purchase flow (`main`/`bonus` types added to validator)
|
||||
- **feat**: Added version history modal in admin sidebar
|
||||
|
||||
### v1.1.0 — 2026-07-02
|
||||
- **feat**: Mercuryo gateway integration, crypto QR deposit, mono products, wallet auto-refresh
|
||||
- **fix**: CSRF cookie `sameSite=false` for Tor, auth cookie fix, async handlers
|
||||
- **feat**: Draggable dashboard panels + business KPI redesign
|
||||
- **fix**: SmartAdmin template redesign + security hardening
|
||||
|
||||
### v1.0.0 — 2026-06-24
|
||||
- **feat**: Initial release — Telegram shop bot with admin panel
|
||||
- **feat**: Crypto wallets (BTC, LTC, ETH, USDT, USDC)
|
||||
- **feat**: Product catalog with locations, categories, subcategories
|
||||
- **feat**: Purchase system with hidden content delivery
|
||||
- **feat**: Admin panel with dashboard, wallets, users, purchases, audit log
|
||||
- **feat**: Tor proxy support (.onion access)
|
||||
- **feat**: i18n localization (en/es/de)
|
||||
@@ -23,53 +23,15 @@ export function csrfMiddleware(req, res, next) {
|
||||
}
|
||||
|
||||
export function validateCsrf(req, res, next) {
|
||||
// Only validate state-changing methods
|
||||
if (['GET', 'HEAD', 'OPTIONS'].includes(req.method)) return next();
|
||||
|
||||
// Skip multipart/form-data — multer hasn't parsed the body yet.
|
||||
// Route handlers using multer must validate CSRF themselves after parsing.
|
||||
const ct = req.headers['content-type'] || '';
|
||||
if (ct.startsWith('multipart/form-data')) return next();
|
||||
|
||||
const token = req.body?._csrf || req.headers[CSRF_HEADER];
|
||||
const cookieToken = req.cookies?.[CSRF_COOKIE];
|
||||
|
||||
if (!token || !cookieToken) {
|
||||
logger.warn({ ip: req.ip, url: req.originalUrl, method: req.method, hasBodyToken: !!token, hasCookieToken: !!cookieToken, contentType: ct }, 'CSRF token missing');
|
||||
return res.status(403).send('CSRF token missing. Please reload the page.');
|
||||
}
|
||||
|
||||
const provided = Buffer.from(token, 'utf8');
|
||||
const expected = Buffer.from(cookieToken, 'utf8');
|
||||
if (provided.length !== expected.length || !crypto.timingSafeEqual(provided, expected)) {
|
||||
logger.warn({ ip: req.ip, url: req.originalUrl, method: req.method }, 'CSRF token mismatch');
|
||||
return res.status(403).send('CSRF token invalid. Please reload the page.');
|
||||
}
|
||||
|
||||
next();
|
||||
// CSRF checks disabled for Tor / onion zone compatibility
|
||||
return next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate CSRF token from req.body._csrf (for use inside multer handlers).
|
||||
* Returns true if valid, false otherwise. Sends 403 response on failure.
|
||||
* NOTE: Disabled for Tor / onion zone compatibility.
|
||||
*/
|
||||
export function validateCsrfFromBody(req, res) {
|
||||
const token = req.body?._csrf;
|
||||
const cookieToken = req.cookies?.[CSRF_COOKIE];
|
||||
|
||||
if (!token || !cookieToken) {
|
||||
logger.warn({ ip: req.ip, url: req.originalUrl, method: req.method }, 'CSRF token missing (multipart)');
|
||||
res.status(403).send('CSRF token missing. Please reload the page.');
|
||||
return false;
|
||||
}
|
||||
|
||||
const provided = Buffer.from(token, 'utf8');
|
||||
const expected = Buffer.from(cookieToken, 'utf8');
|
||||
if (provided.length !== expected.length || !crypto.timingSafeEqual(provided, expected)) {
|
||||
logger.warn({ ip: req.ip, url: req.originalUrl, method: req.method }, 'CSRF token mismatch (multipart)');
|
||||
res.status(403).send('CSRF token invalid. Please reload the page.');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -24,15 +24,62 @@
|
||||
</nav>
|
||||
|
||||
<div class="nav-footer">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="d-flex align-items-center gap-2" role="button" data-bs-toggle="modal" data-bs-target="#versionModal" style="cursor:pointer;" title="View version history">
|
||||
<svg class="sa-icon sa-thin">
|
||||
<use href="/icons/sprite.svg#tag"></use>
|
||||
</svg>
|
||||
<span class="fs-xs opacity-70">v1.0</span>
|
||||
<span class="fs-xs opacity-70">v1.2.0</span>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Version History Modal -->
|
||||
<div class="modal fade" id="versionModal" tabindex="-1" aria-labelledby="versionModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-scrollable">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="versionModalLabel">Version History</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-info mb-3">
|
||||
<strong>Current:</strong> v1.2.0 · 2026-07-08
|
||||
</div>
|
||||
|
||||
<h6 class="fw-bold mb-2">v1.2.0 <span class="text-muted fs-sm">— 2026-07-08</span></h6>
|
||||
<ul class="small mb-3">
|
||||
<li><span class="badge bg-warning text-dark">fix</span> Disabled CSRF checks in admin panel for Tor / onion zone compatibility</li>
|
||||
<li><span class="badge bg-warning text-dark">fix</span> Fixed "Invalid wallet type" error in Telegram bot purchase flow</li>
|
||||
<li><span class="badge bg-primary">feat</span> Added version history modal in admin sidebar</li>
|
||||
</ul>
|
||||
|
||||
<h6 class="fw-bold mb-2">v1.1.0 <span class="text-muted fs-sm">— 2026-07-02</span></h6>
|
||||
<ul class="small mb-3">
|
||||
<li><span class="badge bg-primary">feat</span> Mercuryo gateway, crypto QR deposit, mono products, wallet auto-refresh</li>
|
||||
<li><span class="badge bg-warning text-dark">fix</span> CSRF cookie sameSite=false for Tor, auth cookie fix, async handlers</li>
|
||||
<li><span class="badge bg-primary">feat</span> Draggable dashboard panels + business KPI redesign</li>
|
||||
<li><span class="badge bg-primary">feat</span> SmartAdmin template redesign + security hardening</li>
|
||||
</ul>
|
||||
|
||||
<h6 class="fw-bold mb-2">v1.0.0 <span class="text-muted fs-sm">— 2026-06-24</span></h6>
|
||||
<ul class="small mb-0">
|
||||
<li><span class="badge bg-primary">feat</span> Initial release — Telegram shop bot with admin panel</li>
|
||||
<li><span class="badge bg-primary">feat</span> Crypto wallets (BTC, LTC, ETH, USDT, USDC)</li>
|
||||
<li><span class="badge bg-primary">feat</span> Product catalog with locations, categories, subcategories</li>
|
||||
<li><span class="badge bg-primary">feat</span> Purchase system with hidden content delivery</li>
|
||||
<li><span class="badge bg-primary">feat</span> Admin panel with dashboard, wallets, users, purchases, audit log</li>
|
||||
<li><span class="badge bg-primary">feat</span> Tor proxy support (.onion access)</li>
|
||||
<li><span class="badge bg-primary">feat</span> i18n localization (en/es/de)</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<span class="text-muted small">Update version in <code>VERSION.md</code> + <code>app-sidebar.ejs</code></span>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="backdrop" data-action="toggle-swap" data-toggleclass="app-mobile-menu-open"></div>
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const WALLET_TYPES = new Set(['BTC', 'LTC', 'ETH', 'USDT', 'USDC']);
|
||||
const WALLET_TYPES = new Set(['BTC', 'LTC', 'ETH', 'USDT', 'USDC', 'main', 'bonus']);
|
||||
|
||||
export default class Validators {
|
||||
static isValidLocation(country, city, district) {
|
||||
|
||||
Reference in New Issue
Block a user