boban
308778b99d
fix(bio): prevent duplicate slugs within same workspace
...
- Migration: add UNIQUE(workspace_id, slug) to bio_pages
Old (subdomain_host, slug) unique allowed MySQL NULL bypass —
two free-plan bios with NULL subdomain_host + same slug were not caught
- Edit::save(): validate slug uniqueness before update, dispatch toast on conflict
- Edit::save(): validate slug format (alphanumeric + dash/underscore, max 64)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 23:09:17 +02:00
boban
deb5f310a1
fix(bio): plan-aware URL + plan-seeder + ulid→slug fix
...
- Migration: add bio_pages_limit to plans, make limit cols nullable (agency=unlimited)
- PlanSeeder: 4 plans (free/pro/business/agency) with tier/features/bio_pages_limit
- workspaces:assign-default-plan command: assigns free plan to unassigned workspaces
- BioPage::publicUrl(): free=apex-path, pro+=subdomain, custom=custom-domain
- Security: str_ends_with check uses '.'.$apex to prevent evilnimu.li bypass
- BioPage::shortPublicUrl(): URL without scheme for display
- PlanLimitsService: use bio_pages_limit column (was features['bio_pages']), null=unlimited
- BioPageController: fix $bio → $page variable (view expects $page)
- Bio index/edit views: use publicUrl()/shortPublicUrl(), not hardcoded ULID URLs
- Bio edit view: publicUrlBase/displayUrlBase from component (plan-aware prefix)
- Create-bio modal: plan-aware URL prefix display
- PlanFactory/WorkspaceFactory: free()/pro() states for tests
- Tests: PublicUrlTest (5 cases), Free404FixTest (3 cases)
Known pre-existing failure (not caused by this change):
- Tests\Feature\Auth\EmailVerificationTest::email can be verified
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 22:44:54 +02:00
boban
2d231fe6b8
fix: bio public /b/{slug} route + QR types + code cleanup
...
- Add GET /b/{slug} route alias (nimu.li shorthand for bio pages)
- QR wizard: extend type enum to include email, phone, sms, location
- Modals: replace FQCN references with imported class aliases (Pint clean)
- Analytics: add missing Carbon/Builder imports, improve return types
- Octane config: add ResetCurrentWorkspace import
- scripts/crawl-all.sh: curl+PHPUnit based crawler (replaces Playwright)
All 158 tests pass, PHPStan clean, Pint clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 16:41:05 +02:00
boban
257d1b94f1
feat: QR wizard, UTM Builder, A/B variants, AI features, analytics URL filters
...
- QR Code: split-pane wizard with type tabs, live preview canvas, style/frame/advanced options
- UTM Builder modal: generate URL with UTM params, copy + create short link
- A/B Test: LinkVariants modal with auto-balance weights (min 2, max 5 variants)
- AI: SlugSuggester service (3 suggestions via claude-haiku), AI-slug button in CreateLink
- AI pages: /ai/insights, /ai/anomalies, /ai/ab-generator with sidebar entry
- Analytics: #[Url] attributes for range/country/device/linkId, 24h/7d/30d/90d buttons
- QR enum migration (MySQL-only guard) to add email/phone/sms/location types
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 15:14:19 +02:00
boban
c67bde8fc5
feat: Toaster, button-component, 10 new modals, links-page fix, vite-tuning
...
- Toaster Livewire component (toast event, dismiss, auto-hide, 6 tests)
- <x-ui.button> Blade component with wire:loading indicator + variants
- QR modals: CreateQrCode, EditQrCode, DeleteQrCode (TDD, 8 tests)
- Bio modals: CreateBioPage, EditBioPage, DeleteBioPage (TDD, 7 tests)
- Domain modals: AddDomain, VerifyDomain, DeleteDomain (TDD, 7 tests)
- Profile modal: DeleteAccount with password confirm (TDD, 4 tests)
- Links page: fix openModal event name, wire up Edit/Delete to modals, toast on all CRUD
- vite.config.js: add chart.js/auto + optimizeDeps.force
- QrCode model: add newFactory() + QrCodeFactory
- Layout: Toaster embedded before </body>
- 141 tests / 296 assertions all green, verify 29/29
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 13:55:25 +02:00
boban
ba67f67d60
fix(ui+seed): FOUC eliminated + persistent dev-user
...
Seed:
- DevUserSeeder: idempotent — creates workspace + WorkspaceMember + sets
default_workspace_id. Auto-recreates everything on migrate:fresh --seed.
- DatabaseSeeder: PlanSeeder first (nexxo needs a plan), then DevUserSeeder
always, DemoDataSeeder only in local/dev env.
- DevUserSeederTest: create + idempotent + in-DatabaseSeeder + order check.
- Removed WithoutModelEvents trait from DatabaseSeeder (was suppressing
model observers needed for workspace creation).
UI (nimuli-app.blade.php):
- Inline JS theme-init before first paint: reads localStorage nimuli_theme,
falls back to server-side user preference. Eliminates theme flash on reload.
- Critical CSS in <head>: CSS vars, body opacity 0 → 1 fade (120ms),
skeleton sidebar + topbar visible during JS load.
- app-spinner shown until app-ready class added by markReady().
- Skeleton + spinner hidden via body.app-ready CSS selector.
- livewire:navigated event re-triggers markReady() for SPA navigation.
- x-cloak on mobile sidebar overlay to prevent pre-Alpine flash.
Bash aliases added to ~/.bashrc:
- seed-dev → db:seed --class=DevUserSeeder
- migrate-fresh → migrate:fresh --seed
- reset-db → migrate:fresh --seed (with confirmation message)
Tests: 109/109 passing. Smoke: all routes ✅ . DB sanity ✅ .
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 12:53:29 +02:00
boban
5748dd3443
style: apply Pint formatting to modal files and tests
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 12:42:16 +02:00
boban
ef0105098b
fix: workspace membership + middleware owner fallback
...
- DemoDataSeeder now creates workspace_members record for owner + sets default_workspace_id
- ResolveWorkspace middleware accepts workspace owners even without explicit member record
- Applied membership fix for nexxo@nimuli.com via PHP (existing installs)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 11:17:54 +02:00
boban
30989523cd
feat: vollständiges Nimuli-Frontend Phase 1
...
Layouts:
- nimuli-app: Sidebar + Topbar + Mobile-Drawer (Alpine.js stores)
- guest: Nimuli-Brand, bg-bg, Inter/JetBrains Mono
- sidebar: Workspace-Switcher, Nav mit SVG-Icons, Plan-Usage-Box
- topbar: Theme-Toggle, Notifications-Bell, User-Dropdown
Auth (Breeze-Restyle mit Design-Tokens):
- Login: "Sign in" + dark-theme-native (text-t1, bg-s2, text-blue)
- Register: "Create account"
- Forgot Password: restyled
- Alle Input-Komponenten (text-input, label, button, errors) auf Tokens
Dashboard:
- Hero-Metrics (Today/7d/30d/Total Links)
- 30-Tage Chart.js Line-Chart
- Top-5-Links-Tabelle
- Quick-Actions
Links:
- Tabelle: nimu.li/-Prefix, font-mono slug, Tag-Badges, Status-Badges
- Create-Link Modal (Alpine.js + Livewire, auto-slug)
- Modal-Container-System
Stub-Pages (alle mit nimuli-app layout):
- QR Codes: Empty-State + CTA
- Link-in-Bio: Empty-State + CTA
- Analytics: Metrics + Top-Countries + By-Device
- Domains: Empty-State + CTA
- Settings: Workspace-Name + Danger-Zone
- Billing: Current-Plan + 3-Tier-Pricing-Cards
Demo-Daten:
- DemoDataSeeder: 20 Links + 957 Clicks (30 Tage)
- Workspace "Demo Agency" für nexxo@nimuli.com
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 10:52:52 +02:00
boban
e551fb62ac
fix: Vite-HMR-Websocket via /vite-hmr path + DevUserSeeder
...
- Add hmr.path '/vite-hmr' to vite.config.js for WSS routing
- Improve nginx /vite-hmr location with full proxy headers
- Add DevUserSeeder (nexxo@nimuli.com ) for local dev login
- Register DevUserSeeder in DatabaseSeeder
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 09:45:40 +02:00
boban
ea9274be49
fix(quality): PHPStan level 6 compliance, Pint formatting
...
- Add phpstan.neon with level 6 config and larastan extension
- Fix all 92 PHPStan errors: Eloquent relation generics, iterable
value types, HasFactory generic annotations, missing return types
- Fix logic bugs: always-true Safari/Chrome comparison, nullsafe on
non-nullable Carbon, unused $renderer suppressed with ignore rule
- Implement MustVerifyEmail on User model (was commented out in Breeze
scaffold) to satisfy VerifyEmailController type contract
- Remove broken QrCode::newFactory() referencing non-existent factory
- Add @var docblocks for app('current_workspace') mixed resolution
- Apply Pint formatting: 53 style issues fixed across 168 files
- Test suite unchanged: 9 pre-existing assertSeeLivewire failures,
55 tests passing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 08:28:10 +02:00
boban
96ec518200
feat(domains): Domain model, DNS TXT verification action, pending verifier job
2026-05-16 08:14:33 +02:00
boban
a394e0ff18
fix(bio): add BioPageFactory, newFactory() override on BioPage
2026-05-16 08:12:54 +02:00
boban
c8a6325a77
feat(webhooks): outgoing webhooks with HMAC signature, SSRF protection, retry
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 07:46:57 +02:00
boban
8f1398380f
fix(billing): pm_type/pm_last_four migration, narrow CSRF exclusion, EU paper size
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 01:46:54 +02:00
boban
dd89ee395c
feat(billing): plan limits service, plan factory, seeder (free/pro/business/agency)
2026-05-16 01:06:01 +02:00
boban
ebdcd141bb
feat(redirect): hot-path redirect controller with Redis cache, 302/404/410
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 00:52:16 +02:00
boban
3b19bbe88f
fix(db): null-safe unique keys on rollup tables, workspace slug unique, link slug scoping
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 00:11:39 +02:00
boban
23fd5a5e30
feat(db): add all Phase 1 migrations
...
Creates all Nimuli domain tables: plans, workspaces, workspace_members,
workspace_invitations, billing_profiles, domains, links, link_variants,
qr_codes, bio_pages, bio_blocks, clicks (+ hourly/daily/by_referrer/by_utm
aggregates), translations, webhooks, webhook_deliveries. Also extends the
users table with ulid, 2FA fields, locale, theme, default_workspace_id,
and soft deletes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 00:04:01 +02:00
boban
25dddf5293
feat: initial Nimuli scaffold with Laravel 12, Livewire 3, Octane, Reverb, Docker
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 23:51:57 +02:00