This commit is contained in:
seb
2026-07-27 01:31:03 +02:00
parent fc7afd1be8
commit ba705c7d08
5 changed files with 3044 additions and 11 deletions

3
src/demo/mode.js Normal file
View File

@@ -0,0 +1,3 @@
export function isDemoMode() {
return String(process.env.DEMO_MODE || '').toLowerCase() === 'true';
}

139
src/demo/store.js Normal file
View File

@@ -0,0 +1,139 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { setActiveShop, setActiveShopSubshop } from '../shop.js';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const DEMO_ROOT = path.join(__dirname, '..', '..', 'demo');
const CATALOG_PATH = path.join(DEMO_ROOT, 'catalog.json');
const IMAGES_DIR = path.join(DEMO_ROOT, 'images');
let catalog = null;
let nextDemoOrderId = 1;
function ensureLoaded() {
if (!catalog) {
throw new Error('Demo catalog is not loaded. Call loadDemoCatalog() at startup.');
}
return catalog;
}
function afterCursor(rows, cursor, lastChangedKey = 'lastChanged') {
const c = Number(cursor) || 0;
return rows
.filter((row) => Number(row[lastChangedKey]) > c)
.sort((a, b) => Number(a[lastChangedKey]) - Number(b[lastChangedKey]));
}
export async function loadDemoCatalog() {
if (!fs.existsSync(CATALOG_PATH)) {
throw new Error(
`Demo catalog missing at ${CATALOG_PATH}. Run: npm run demo:generate`
);
}
const raw = fs.readFileSync(CATALOG_PATH, 'utf8');
catalog = JSON.parse(raw);
if (!Array.isArray(catalog.products) || catalog.products.length === 0) {
throw new Error('Demo catalog has no products.');
}
setActiveShop(1);
setActiveShopSubshop(1);
nextDemoOrderId = Number(catalog.maxOrderId || 0) + 1;
return {
categories: catalog.categories.length,
products: catalog.products.length,
customerGroups: catalog.customerGroups.length,
composites: catalog.composites.length,
imagesDir: IMAGES_DIR,
};
}
export function getDemoProductList({ cursor = 0, limit = 20 } = {}) {
return afterCursor(ensureLoaded().products, cursor).slice(0, limit);
}
export function getDemoProductCount({ cursor = 0 } = {}) {
return afterCursor(ensureLoaded().products, cursor).length;
}
export function getDemoCategoryList({ cursor = 0, limit = 20 } = {}) {
const data = ensureLoaded();
return afterCursor(data.categories, cursor).slice(0, limit).map((category) => ({
_id: category._id,
imghash: category.imghash,
imgsrc: category.imgsrc,
name: category.name,
pid: category.pid,
discounts: category.discounts ?? [],
sort: category.sort,
lastChanged: category.lastChanged,
}));
}
export function getDemoCategoryCount({ cursor = 0 } = {}) {
return afterCursor(ensureLoaded().categories, cursor).length;
}
export function getDemoCustomerGroupIds() {
return ensureLoaded().customerGroups.map((g) => Number(g.customerGroupId));
}
export function getDemoCustomerGroupList({ cursor = 0 } = {}) {
return afterCursor(ensureLoaded().customerGroups, cursor);
}
export function getDemoCustomerGroupCount({ cursor = 0 } = {}) {
return afterCursor(ensureLoaded().customerGroups, cursor).length;
}
export function getDemoCompositeProductList({ cursor = 0, limit = 100 } = {}) {
return afterCursor(ensureLoaded().composites, cursor).slice(0, limit);
}
export function getDemoCompositeProductCount({ cursor = 0 } = {}) {
// Match MSSQL semantics: count distinct composite parent products after cursor
const rows = afterCursor(ensureLoaded().composites, cursor);
return new Set(rows.map((r) => r.productId)).size;
}
export function getDemoDeletedEntityList({ cursor = 0, limit = 600 } = {}) {
return afterCursor(ensureLoaded().deletedEntities, cursor).slice(0, limit);
}
export function getDemoDeletedEntityCount({ cursor = 0 } = {}) {
return afterCursor(ensureLoaded().deletedEntities, cursor).length;
}
export function getDemoMaxOrderIdCount() {
return Number(ensureLoaded().maxOrderId || 0);
}
export async function getDemoImageByHash(hash) {
ensureLoaded();
if (!hash) {
return null;
}
const filePath = path.join(IMAGES_DIR, `${hash}.jpg`);
if (!fs.existsSync(filePath)) {
return null;
}
const buffer = fs.readFileSync(filePath);
return { buffer, contentType: 'image/jpeg' };
}
export async function createDemoOrder(order) {
ensureLoaded();
const orderId = nextDemoOrderId++;
const externalId = String(order?.externalId ?? orderId);
return {
orderId: String(orderId),
orderNumber: `DEMO-${externalId}`,
alreadyExists: false,
};
}