Commit Graph

9 Commits (main)

Author SHA1 Message Date
boban a3552970fe fix(bio): add missing url_format columns + update PublicUrl tests
- Migration: url_format_override on bio_pages, bio_url_format on workspaces,
  needs_migration + migration_required_at on bio_pages (model casts existed, columns did not)
- PublicUrl tests updated for effectiveFormat() logic:
  - Pro default = SystemPath (no subdomain without explicit bio_url_format=Subdomain)
  - url_format_override on bio overrides workspace format
  - needs_migration=true → publicUrl() returns ''

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 23:32:53 +02:00
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 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 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