- freeAccessStore.js: JSON-persisted whitelist of shops with optional expiry dates; isShopAllowed(), addShop(), removeShop(), listShops() - routes/adminPanel.js: password-protected single-page admin dashboard served at /d4a-admin; cookie-based session auth (no extra deps); add / remove / list shops with expiry dates and notes - server.js: mount /d4a-admin panel; expose GET /free-access/:shop public API for frontend loaders; import freeAccessStore Credentials: d4a-admin / Data4autos@2026. Migrates racewerksengg.myshopify.com from hardcode into the JSON store. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
71 lines
1.9 KiB
JavaScript
71 lines
1.9 KiB
JavaScript
// freeAccessStore.js — persistent whitelist of shops granted free access
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const dataFile = path.resolve(__dirname, 'data', 'freeAccess.json');
|
|
const dataDir = path.dirname(dataFile);
|
|
|
|
if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true });
|
|
if (!fs.existsSync(dataFile)) {
|
|
// seed with the shop that was previously hardcoded
|
|
fs.writeFileSync(dataFile, JSON.stringify({
|
|
"racewerksengg.myshopify.com": {
|
|
grantedAt: new Date().toISOString(),
|
|
expiresAt: null,
|
|
note: "Internal test shop (migrated from hardcode)"
|
|
}
|
|
}, null, 2), 'utf8');
|
|
}
|
|
|
|
function readStore() {
|
|
try { return JSON.parse(fs.readFileSync(dataFile, 'utf8')); }
|
|
catch { return {}; }
|
|
}
|
|
|
|
function saveStore(store) {
|
|
fs.writeFileSync(dataFile, JSON.stringify(store, null, 2), 'utf8');
|
|
}
|
|
|
|
function isShopAllowed(shop) {
|
|
if (!shop) return false;
|
|
const store = readStore();
|
|
const entry = store[shop.toLowerCase().trim()];
|
|
if (!entry) return false;
|
|
if (!entry.expiresAt) return true; // permanent grant
|
|
return new Date(entry.expiresAt) > new Date();
|
|
}
|
|
|
|
function addShop(shop, expiresAt, note = '') {
|
|
const store = readStore();
|
|
const key = shop.toLowerCase().trim();
|
|
store[key] = {
|
|
grantedAt: new Date().toISOString(),
|
|
expiresAt: expiresAt || null,
|
|
note: note || '',
|
|
};
|
|
saveStore(store);
|
|
return store[key];
|
|
}
|
|
|
|
function removeShop(shop) {
|
|
const store = readStore();
|
|
const key = shop.toLowerCase().trim();
|
|
delete store[key];
|
|
saveStore(store);
|
|
}
|
|
|
|
function listShops() {
|
|
const store = readStore();
|
|
const now = new Date();
|
|
return Object.entries(store).map(([shop, entry]) => ({
|
|
shop,
|
|
grantedAt: entry.grantedAt,
|
|
expiresAt: entry.expiresAt,
|
|
note: entry.note || '',
|
|
expired: entry.expiresAt ? new Date(entry.expiresAt) <= now : false,
|
|
permanent: !entry.expiresAt,
|
|
}));
|
|
}
|
|
|
|
module.exports = { isShopAllowed, addShop, removeShop, listShops };
|