hd-commerce: neutrales SQLite-Commerce-Backend (Admin + API + Demo-Storefront)

This commit is contained in:
2026-06-17 12:05:29 +00:00
commit 4e8a3ab105
43 changed files with 2689 additions and 0 deletions
+74
View File
@@ -0,0 +1,74 @@
---
import Admin from '../../../layouts/Admin.astro';
import { getSettings, setSetting } from '../../../lib/store.js';
let flash = '';
if (Astro.request.method === 'POST') {
const f = await Astro.request.formData();
setSetting('shop_name', f.get('shop_name') || 'hd-commerce');
setSetting('shop_tagline', f.get('shop_tagline') || '');
setSetting('shop_email', f.get('shop_email') || '');
setSetting('brand_accent', f.get('brand_accent') || '#b8566a');
setSetting('brand_accent_dark', f.get('brand_accent_dark') || '#8d3f50');
setSetting('currency', f.get('currency') || 'EUR');
setSetting('free_shipping_cents', String(Math.round(parseFloat(String(f.get('free_shipping') || '49').replace(',', '.')) * 100) || 4900));
flash = 'Einstellungen gespeichert.';
}
const s = getSettings();
const stripeSecret = (process.env.STRIPE_SECRET_KEY || '').trim();
const stripeReal = /^sk_(test|live)_[A-Za-z0-9]{16,}/.test(stripeSecret);
const stripeMode = stripeReal ? (stripeSecret.startsWith('sk_live') ? 'Live' : 'Test') : 'Demo-Fallback';
const freeShipStr = ((Number(s.free_shipping_cents) || 4900) / 100).toFixed(2).replace('.', ',');
const currencies = ['EUR', 'CHF', 'USD', 'GBP'];
---
<Admin title="Einstellungen" active="einstellungen" crumbs={[{ label: 'Einstellungen' }]}>
<div class="s-stack">
{flash && <div class="s-flash">✓ {flash}</div>}
<form method="POST" class="s-two-col">
<div class="s-stack">
<div class="s-card s-card-pad">
<div class="s-section-title" style="margin-bottom:14px;font-size:15px">Shop</div>
<div class="s-field"><label class="s-label">Shop-Name</label><input class="s-input" name="shop_name" value={s.shop_name || ''} required /></div>
<div class="s-field"><label class="s-label">Tagline</label><input class="s-input" name="shop_tagline" value={s.shop_tagline || ''} /></div>
<div class="s-field"><label class="s-label">Kontakt-E-Mail</label><input class="s-input" name="shop_email" type="email" value={s.shop_email || ''} /></div>
</div>
<div class="s-card s-card-pad">
<div class="s-section-title" style="margin-bottom:14px;font-size:15px">Branding</div>
<div class="s-form-grid">
<div class="s-field"><label class="s-label">Akzentfarbe</label><input class="s-input" name="brand_accent" type="color" value={s.brand_accent || '#b8566a'} /></div>
<div class="s-field"><label class="s-label">Akzentfarbe (dunkel)</label><input class="s-input" name="brand_accent_dark" type="color" value={s.brand_accent_dark || '#8d3f50'} /></div>
</div>
<div class="s-help">Die Akzentfarbe wird im Storefront und im Admin als CSS-Variable injiziert.</div>
</div>
<div class="s-card s-card-pad">
<div class="s-section-title" style="margin-bottom:14px;font-size:15px">Verkauf</div>
<div class="s-form-grid">
<div class="s-field"><label class="s-label">Währung</label><select class="s-select" name="currency">{currencies.map((c) => (<option value={c} selected={s.currency === c}>{c}</option>))}</select></div>
<div class="s-field"><label class="s-label">Gratis-Versand ab (€)</label><input class="s-input" name="free_shipping" value={freeShipStr} /></div>
</div>
</div>
<button class="s-btn s-btn-primary" type="submit" style="align-self:flex-start">Alle Einstellungen speichern</button>
</div>
<div class="s-stack">
<div class="s-card s-card-pad">
<div class="s-section-title" style="margin-bottom:12px">Zahlung (Stripe)</div>
<p style="margin:0 0 8px"><span class={`s-badge ${stripeReal ? 'green' : 'amber'}`}>{stripeMode}</span></p>
<p class="s-help">{stripeReal ? 'Echter Stripe-Schlüssel erkannt — Checkout nutzt Stripe Hosted Checkout.' : 'Kein echter STRIPE_SECRET_KEY gesetzt. Der Checkout läuft im Demo-Fallback (Bestellung ohne Zahlung).'}</p>
<p class="s-help" style="margin-top:8px">Konfiguration über ENV: <b>STRIPE_SECRET_KEY</b>, <b>STRIPE_PUBLIC_KEY</b>.</p>
</div>
<div class="s-card s-card-pad">
<div class="s-section-title" style="margin-bottom:12px">Analytics</div>
<p class="s-help">hd-commerce nutzt eine eigene First-Party-Statistik (events-Tabelle). Kein externer Dienst, keine personenbezogenen Rohdaten — die Session-Kennung ist ein täglich rollender Hash.</p>
</div>
<div class="s-card s-card-pad">
<div class="s-section-title" style="margin-bottom:12px">System</div>
<p class="s-help">Datenbank: SQLite (<b>DB_PATH</b>). Admin-Zugang über <b>ADMIN_USER</b> / <b>ADMIN_PASS</b>.</p>
</div>
</div>
</form>
</div>
</Admin>