- Replace `except Exception` with specific exception types in
google_wallet_service.py (requests.RequestException, ValueError, etc.)
and apple_wallet_service.py (httpx.HTTPError, OSError, ssl.SSLError)
- Rename loyalty_onboarding.py -> loyalty_onboarding_service.py to
match NAM-002 naming convention (+ test file + imports)
- Add PasswordChangeResponse Pydantic model to user_account API,
removing raw dict return and noqa suppression
Resolves 12 EXC-003 + 1 NAM-002 architecture warnings in loyalty module.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixes deployment test failures where get_store_usage() and get_merchant_usage()
were called with db=None but attempted to run queries.
Also adds noqa suppressions for pre-existing security validator findings
in dev-toolbar (innerHTML with trusted content) and test fixtures
(hardcoded test passwords).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Logo URL is required by Google Wallet API for LoyaltyClass creation.
Added validation across all three program edit screens (admin, merchant, store)
with a helpful hint explaining the requirement.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix Google Wallet class creation: add required issuerName field (merchant name),
programLogo with default logo fallback, hexBackgroundColor default
- Add default loyalty logo assets (200px + 512px) for programs without custom logos
- Smart retry: skip retries on 400/401/403/404 client errors (not transient)
- Fix enrollment success page: use sessionStorage for wallet URLs instead of
authenticated API call (self-enrolled customers have no session)
- Hide wallet section on success page when no wallet URLs available
- Wire up T&C modal on enrollment page with program.terms_text
- Add startup validation for Google/Apple Wallet configs in lifespan
- Add admin wallet status dashboard endpoint and UI (moved to service layer)
- Fix Apple Wallet push notifications with real APNs HTTP/2 implementation
- Fix docs: correct enrollment URLs (port, path segments, /v1 prefix)
- Fix test assertion: !loyalty-enroll! → !enrollment!
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The toolbar was only included in the store base template. Add it to all
frontends so developers can use Ctrl+Alt+D everywhere in dev.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Evolve the platform-debug page into a diagnostics hub with sidebar
explorer layout. Add permissions audit API that introspects all
registered page routes and reports auth/permission enforcement status.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add merchant stats API tests (GET /merchants/loyalty/stats) with 7 test cases
- Add merchant page route tests (program, program-edit, analytics) with 6 test cases
- Add store page route tests (terminal, cards, card-detail, program, program-edit, analytics, enroll) with 16 test cases
- Add unit tests for get_merchant_stats() enhanced fields (new_this_month, estimated_liability_cents, location breakdown) with 6 test cases
- Add unit tests for get_platform_stats() enhanced fields (total_points_issued/redeemed, total_points_balance, new_this_month, estimated_liability_cents) with 4 test cases
- Total: 38 new tests (174 -> 212 passing)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract analytics stat cards, points activity, and location breakdown
into a shared partial used by admin, merchant, and store dashboards.
Add merchant stats API endpoint and client-side merchant filter on admin
analytics page. Extend stats schema with new_this_month and
estimated_liability_cents fields.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add Loyalty and Billing SQL query presets to dev tools
- Extract shared program-form.html partial and loyalty-program-form.js mixin
- Refactor admin program-edit to use shared form partial
- Add store loyalty API endpoints for program management
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Standardize naming (Program for view/edit, Analytics for stats), create shared
read-only program-view partial, fix admin edit field population bug (14 missing
fields), add store Program menu item, and rename merchant Overview→Program,
Settings→Analytics.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New admin page to browse, search, compare, and inline-edit translation
keys across all modules and languages from the browser.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add admin SQL query tool with saved queries, schema explorer presets,
and collapsible category sections (dev_tools module)
- Add platform debug tool for admin diagnostics
- Add loyalty settings page with owner-only access control
- Fix loyalty settings owner check (use currentUser instead of window.__userData)
- Replace HTTPException with AuthorizationException in loyalty routes
- Expand loyalty module with PIN service, Apple Wallet, program management
- Improve store login with platform detection and multi-platform support
- Update billing feature gates and subscription services
- Add store platform sync improvements and remove is_primary column
- Add unit tests for loyalty (PIN, points, stamps, program services)
- Update i18n translations across dev_tools locales
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix platform-grouped merchant sidebar menu with core items at root level
- Add merchant store management (detail page, create store, team page)
- Fix store settings 500 error by removing dead stripe/API tab
- Move onboarding translations to module-owned locale files
- Fix onboarding banner i18n with server-side rendering + context inheritance
- Refactor login language selectors to use languageSelector() function (LANG-002)
- Move HTTPException handling to global exception handler in merchant routes (API-003)
- Add language selector to all login pages and portal headers
- Fix customer module: drop order stats from customer model, add to orders module
- Fix admin menu config visibility for super admin platform context
- Fix storefront auth and layout issues
- Add missing i18n translations for onboarding steps (en/fr/de/lb)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move 39 documentation files from top-level docs/ into each module's
docs/ folder, accessible via symlinks from docs/modules/. Create
data-model.md files for 10 modules with full schema documentation.
Replace originals with redirect stubs. Remove empty guide stubs.
Modules migrated: tenancy, billing, loyalty, marketplace, orders,
messaging, cms, catalog, inventory, hosting, prospecting.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add 55 unit tests for hosting module (hosted site service, client
service service, stats service) with full fixture setup
- Fix table_empty_state macro: add x_message param for dynamic Alpine.js
expressions rendered via x-text instead of server-side Jinja
- Fix hosting templates (sites, clients) using message= with Alpine
expressions that rendered as literal text
- Fix prospecting templates (leads, scan-jobs, prospects) using
nonexistent subtitle= param, migrated to x_message=
- Align hosting and prospecting admin templates with shared design system
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
celery-beat: check that the schedule file was modified within the last
120 seconds (confirms beat is ticking).
node-exporter: check /metrics endpoint for build_info metric.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 16 missing translations for: subscription_welcome, payment_failed,
subscription_cancelled, trial_ending, team_invite (fr/de/lb each) and
team_invitation (lb). All 11 email templates now have full coverage
across all 4 supported languages.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The pagination() macro expects startIndex, endIndex, pageNumbers, totalPages,
nextPage(), and previousPage() to be defined in the Alpine.js component.
Added these to scan-jobs.js, prospects.js, and leads.js.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The API router was missing prefix="/prospecting", causing all endpoints to
register at /api/v1/admin/stats instead of /api/v1/admin/prospecting/stats.
Also fix 500 errors on prospects, leads, and scan-jobs pages caused by
importing non-existent macro names (table_empty → table_empty_state,
pagination_controls → pagination).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Flower: use /healthcheck endpoint (auth-exempt) instead of root URL.
Redis-exporter: switch to alpine image (has wget) and verify redis_up
in /metrics instead of non-existent /health endpoint.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove duplicate Tom Select dark mode CSS (~280 lines) from customers,
orders, store-product-create, marketplace-letzshop, and marketplace-products
pages. Replace inline store select elements and selected badges with
entity_selector() and entity_selected_badge() macros.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Move platforms menu from CMS to Platform Admin section with create/edit
- Add platform create page, API endpoint, and service method
- Remove CMS-specific content from platform list and detail pages
- Create shared entity_selector + entity_selected_badge Jinja macros
- Create entity-selector.js generalizing store-selector.js for any entity
- Add Tom Select merchant filter to stores page with localStorage persistence
- Migrate store-products page to use shared macros (remove 53 lines of duped CSS)
- Fix broken icons: puzzle→puzzle-piece, building-storefront→store, language→translate, server→cube
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The footer and meta description tag used platform.description (raw column)
instead of platform.get_translated_description(), so DE and LB translations
were never displayed. FR/EN appeared to work because the base description
field was synced from the default language on admin save.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The store dashboard endpoint uses require_platform which needs the
PlatformContextMiddleware to resolve a platform. TestClient uses
'testserver' as Host, which the middleware maps to the 'main' platform.
Added _ensure_main_platform fixture to create it in the test DB.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The reset_all_data() function used `ContentPage.store_id is not None` which
is a Python identity check (always True), causing it to delete ALL content
pages including platform defaults. Same bug in print_summary() caused the
count to always show 0 platform pages. Fixed both to use proper SQLAlchemy
.is_(None) / .is_not(None) syntax.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ASCII diagram showing all services, Docker networks, port bindings,
and traffic flow from Cloudflare through Caddy to each container.
Clearly marks which ports are internet-exposed, localhost-only,
or Docker-internal-only.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add description_translations JSON column to Platform model + migration
- Add language tabs to platform admin edit form for multilingual descriptions
- Update API schemas to include description_translations in request/response
- Translate pricing section UI labels via _t() macro (monthly/annual/CTA/etc.)
- Add Luxembourgish (lb) support to all platforms (OMS, Main, Loyalty, Hosting)
- Seed description_translations, contact emails, and social links for all platforms
- Add LuxWeb Agency demo merchant with hosting stores, team, and content pages
- Fix language code typo: lu → lb in platform-edit.js availableLanguages
- Fix store content pages to use correct primary platform instead of hardcoded OMS
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix Gitea docker-compose in Step 7 to bind port 3000 to 127.0.0.1
- Add REDIS_PASSWORD to Step 10 critical production values
- Replace misleading UFW rules with Docker port binding instructions
- Add warning about Docker bypassing UFW in Step 14
- Add initial setup note for temporary Gitea port exposure
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add Redis password via REDIS_PASSWORD env var (--requirepass flag)
- Update all REDIS_URL and REDIS_ADDR references to include password
- Restrict /metrics endpoint to localhost and Docker internal networks (403 for external requests)
- Document Gitea port 3000 localhost binding fix (must be applied manually on server)
- Add REDIS_PASSWORD to .env.example
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Docker bypasses UFW iptables, so bare port mappings like "5432:5432"
exposed the database to the public internet. Removed port mappings for
PostgreSQL and Redis (they only need Docker-internal networking), and
bound the API port to 127.0.0.1 since only Caddy needs to reach it.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add name_translations JSON column to SubscriptionTier for multi-language
tier names. Pre-resolve tier names and build dynamic feature lists from
module providers in route handlers. Fix Jinja2 macro scoping by importing
pricing partial with context. Backfill content_translations for all 43
content pages across 4 platforms (en/fr/de/lb).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add sections covering CMS locale file structure, translated template
inventory, TranslatableText pattern for sections, and the new
title_translations/content_translations model API with migration cms_002.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Phase 5: Translate homepage-modern.html (~90 new locale keys, all
hardcoded strings replaced with _() calls for dashboard mock,
features, pricing tiers, testimonial sections)
- Phase 6: Translate homepage-minimal.html (17 new locale keys for
fallback content, features, and CTA sections)
- Phase 7: Add multi-language page.title/content support with
title_translations and content_translations JSON columns, Alembic
migration cms_002, translated title/content resolution in templates,
and seed script updates with tt() helper
- Phase 8: Complete lb.json audit — fill 6 missing keys (messages,
confirmations), also backfill same keys in fr.json and de.json
All 4 locale files now have 340 keys with full parity.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Document the complete nuclear reset sequence (tested end-to-end):
stop → build → infra up → schema reset → migrations → seeds → start.
Update seeded data counts to match current output (30 CMS pages,
12 tiers, 3 admins, 28 email templates). Switch from exec to run --rm
for seed commands so they work before services are started.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
celery-beat was missing env_file and DATABASE_URL, so it had no access
to app config (Stripe keys, etc.). Also downgrade "Stripe API key not
configured" from warning to debug to stop log spam when Stripe is not
yet set up.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Celery worker was OOM-killed (41 restarts) at 512MB with 4 concurrent
workers. Reduce concurrency to 2, increase worker limit to 768MB, and
reclaim memory from over-provisioned services (db 512→256, beat 256→128,
flower 256→192). Total allocation stays within 4GB server budget.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Redis had no maxmemory set, causing the Prometheus alert expression
(used/max) to evaluate to +Inf. Set maxmemory to 100mb with allkeys-lru
eviction policy, and guard the alert expression against division by zero.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove all hardcoded OMS-specific content from platform base template:
nav links, contact info, brand name, and footer columns. Everything is
now dynamic via platform model and CMS page queries. Wire up legal_pages
context (privacy/terms) from database instead of hardcoded fallback.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
_get_tiers_data() was querying all active tiers across all platforms.
Now accepts platform_id parameter to scope tiers to the current platform.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add ProductCard/ProductsSection schema and _products.html section macro
- Rewrite seed script with 3-platform homepage sections (wizard, OMS, loyalty),
platform marketing pages, and store defaults with {{store_name}} placeholders
- Add resolve_placeholders() to ContentPageService for store default pages
- Fix SQLAlchemy filter bugs: replace Python `is None` with `.is_(None)` across
all ContentPageService query methods (was silently breaking all platform page lookups)
- Remove hardcoded orion fallback and delete homepage-orion.html
- Add placeholder hint box with click-to-copy in admin content page editor
- Export ProductCard/ProductsSection from cms schemas __init__
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add OnboardingProviderProtocol so modules declare their own post-signup
onboarding steps. The core OnboardingAggregator discovers enabled
providers and exposes a dashboard API (GET /dashboard/onboarding).
A session-scoped banner on the store dashboard shows a checklist that
guides merchants through setup without blocking signup.
Signup is simplified from 4 steps to 3 (Plan → Account → Payment):
store creation is merged into account creation, store language is
captured from the user's browsing language, and platform-specific
template branching is removed.
Includes 47 unit and integration tests covering all new providers,
the aggregator, the API endpoint, and the signup service changes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move core signup service from marketplace to billing module, add
automatic Stripe product/price sync for tiers, create loyalty-specific
signup wizard, and enforce that platform is always explicitly known
(no silent defaulting to primary/hardcoded ID).
Key changes:
- New billing SignupService with separated account/store creation steps
- Stripe auto-sync on tier create/update (new prices, archive old)
- Loyalty signup template (Plan → Account → Store → Payment)
- platform_code is now required throughout the signup flow
- Pricing/signup pages return 404 if platform not detected
- OMS-specific logic (Letzshop claiming) stays in marketplace module
- Bootstrap script: scripts/seed/sync_stripe_products.py
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>