Skip to content

Releases: EmberlyOSS/Emberly

[2.4.2] - API Keys and Bucket Partner

14 Apr 23:59
6c6ef76

Choose a tag to compare

Added

  • User API Key Management System — Users can now create, name, copy, and revoke multiple personal API keys from /me → API tab.
    • New UserApiKey Prisma model (ebk_ prefix, SHA-256 hashed, max 10 keys per user, lastUsedAt tracking).
    • packages/lib/api-keys/index.tsgenerateApiKey(), listUserApiKeys(), createUserApiKey() (enforces 10-key cap), revokeUserApiKey().
    • GET /api/profile/api-keys — lists keys (prefix and metadata only, never the hash). POST — creates a key and returns the full raw value exactly once.
    • DELETE /api/profile/api-keys/[keyId] — revokes a key; ownership enforced via userId filter.
    • packages/components/profile/api-keys-panel.tsx — combined panel: legacy upload token (show/hide/refresh) + named key CRUD (create with name, one-time copy flash, revoke per key).
    • ebk_ prefix recognized in getAuthenticatedUser — SHA-256 hash lookup with non-blocking lastUsedAt update; falls through cleanly if prefix matches but no record is found.
  • Admin System Key Auth Integration — The existing esk_ system key can now authenticate any admin or superadmin route.
    • requireAdmin(), requireSuperAdmin(), requireRole(), and requirePermission() all accept an optional req parameter; when provided, they fall back to system key auth if no session is present.
    • Synthetic SYSTEM_KEY_USER constant (SUPERADMIN role) returned by new getSystemKeyUser(req?) helper, which reuses the existing isSystemKeyAuth() check.
    • All existing callers (no req argument) are unaffected.
  • Partners Carousel — Marketing partner logos rebuilt from a static grid to an infinite auto-scrolling marquee.
    • Duplicated partner list drives a seamless CSS loop; pauses on hover.
    • Edge fade masks (mask-image gradient) soften the left/right boundaries.
    • animate-marquee keyframe added to tailwind.config.ts (translateX(-50%), 30 s linear infinite).
  • Vultr Object Storage Admin Panel — Admins can now provision, manage, and sync Vultr Object Storage instances directly from Admin → Storage.
    • New VultrObjectStorage Prisma model tracking vultrId, region, clusterId, s3Hostname, credentials, tier, status, cfHostname, and cfDnsRecordId.
    • packages/lib/vultr/index.ts — new typed Vultr API client: listObjectStorages(), createObjectStorage(), getObjectStorage(), deleteObjectStorage(), listClusters(), listTiers(), createObjectStorageBucket(), deleteObjectStorageBucket().
    • POST /api/admin/storage/vultr — provisions a new instance via Vultr API, saves to DB, auto-creates a Cloudflare CNAME record.
    • GET /api/admin/storage/vultr — lists all tracked instances; syncs stale status and credentials back to DB on every fetch.
    • GET/DELETE /api/admin/storage/vultr/[id] — fetches/deletes a single instance; DELETE removes the Vultr instance, CF DNS record, and DB row.
    • POST /api/admin/storage/vultr/sync — imports new Vultr instances not yet in the DB and updates status + credentials for existing stale records; response includes imported, updated, and skipped counts.
    • packages/components/admin/settings/vultr-instance-manager.tsx — full admin UI: cluster/tier picker, region selector with SVG country flags, instance list with live status badges, delete with toast confirmation.
    • Prisma migrations: 20260414071251_add_block_storage_management (VultrObjectStorage table, StorageBucket Vultr fields), 20260414084547_add_cf_hostname_to_vultr_storage (cfHostname, cfDnsRecordId columns).
  • Cloudflare DNS Auto-Provisioning — When an admin provisions a Vultr Object Storage instance, a DNS-only CNAME record is automatically created in Cloudflare pointing at the Vultr S3 hostname.
    • createDnsRecord() and deleteDnsRecord() added to packages/lib/cloudflare/client.ts; record ID stored as cfDnsRecordId on the instance for reliable cleanup on delete.
    • Record is DNS-only (proxied: false) since Vultr TLS certificates only cover *.vultrobjects.com; Cloudflare proxying would break S3 signature authentication.
  • Automatic Storage Bucket Provisioning — Purchasing a storage-bucket subscription now automatically creates a dedicated Vultr bucket for the user with no manual admin intervention required.
    • Stripe checkout.session.completed webhook reads metadata.type === 'storage-bucket' and metadata.location to select the correct Vultr pool and call createObjectStorageBucket().
    • On subscription cancellation, the webhook calls deleteObjectStorageBucket(), clears the StorageBucket record, and unlinks the user — keeping costs proportional to active subscribers.
    • Per-user bucket name is DNS-safe and scoped to the user ID (emberly-{userId slice}).
    • user.bucket-provisioned and user.bucket-deprovisioned events wired into email and Discord notification preference maps.
  • Tiered Storage Bucket Products — The S3/Object Storage tab on the pricing page now supports five tiers: Standard, Archival, Premium, Performance, and Accelerated.
    • Pricing page queries VultrObjectStorage for active instances, groups by tier keyword, and passes per-tier availableRegions to S3Section.
    • StorageTier type exported from S3Section.tsx carries slug, name, priceId, priceCents, and availableRegions.
    • EXCLUDED_ADDON_KEYS in AddOnsSection.tsx expanded to cover all storage-bucket-* slugs so tier products never appear in the generic add-ons list.
  • Vultr API Key in Integrations Configvultr.apiKey added to the config schema (packages/lib/config/index.ts) and DEFAULT_CONFIG; the Vultr client reads from this field with a fallback to the VULTR_API_KEY environment variable.

Changed

  • Profile API Tab — Upload token and API key management moved out of the Uploads tab into a new dedicated API tab in /me.
    • KeyRound icon; tab inserted between Uploads and Applications in the Content group.
    • Upload Host domain selector remains in the Uploads tab (upload config, not key management).
    • packages/components/profile/tools/upload-host.tsx extracted as a standalone component and restored to ProfileTools.
  • S3 Pricing Section RedesignedS3Section.tsx completely rebuilt around the new tier + region model.
    • Location picker shows SVG country flags (country-flag-icons/react/3x2) instead of emoji for consistent cross-platform rendering.
    • Tier cards replace the old single-product layout; each tier displays its price, region availability, and a "Choose location" picker that is only shown when the admin has provisioned that tier in at least one region.
    • Checkout flow passes metadata.type, metadata.location, and metadata.tier to the Stripe session so the webhook can auto-provision the correct bucket.
  • Dashboard Storage Tracker Reading Correct Source — The overview storage card now reads user.storageUsed (the denormalized MB field updated on every upload/delete) instead of aggregating raw bytes from the File table, which excluded S3-backed objects and caused near-zero or incorrect totals.

Fixed

  • Storage Usage Inconsistencies — Several parts of the site were displaying incorrect or near-zero storage values due to unit mismatches across the storage pipeline.
    • File.size and User.storageUsed are stored in MB; the analytics API was naming those values with bytes-suffixed fields, causing consumers to double-convert.
    • GET /api/analytics/storage — response fields renamed: totalBytestotalMB, daily[].bytesdaily[].mb, breakdown[].bytesbreakdown[].mb.
    • StorageMetrics.tsx — removed manual / 1024 / 1024 division; now reads data.totalMB and calls formatFileSize(mb) directly.
    • AnalyticsOverview.tsxstorageTotalBytesstorageTotalMB throughout; storageDay?.bytesstorageDay?.mb; item.bytesitem.mb in breakdown render.
    • POST /api/files — squad storageUsed was being incremented with raw bytes (uploadedFile.size) instead of MB; fixed to bytesToMB(uploadedFile.size).
  • Storage Quota Matching All Tiered Bucket SlugsgetPlanLimits() was only checking for an active storage-bucket subscription; users who purchased a tiered variant (e.g. storage-bucket-archival) still had quota enforced. Fixed to match any slug that startsWith('storage-bucket').
  • File Privacy: Direct URLs Never Expose Storage Hostnames — The /direct endpoint for raw file access and both GET/POST variants of the download route were returning pre-signed Vultr URLs or redirect responses that exposed *.vultrobjects.com hostnames to clients.
    • All paths now route through Emberly's own proxy (buildRawUrl()); storage provider URLs are never returned to the browser.
  • Admin Vultr Sync Updates Existing Instances — The "Sync Vultr" button previously only imported instances not yet tracked in the database; instances that had drifted (status changed from pendingactive, or credentials populated after instance activation) were skipped silently.
    • Sync endpoint now splits results into toImport (new) and toUpdate (existing with stale status or blank credentials) and runs both in parallel; toast shows imported, updated, and skipped counts.
  • Admin Vultr Status Not Synced Back to DB — Vultr instances provisioned while still pending were never updated to active in the database, causing the pricing page to show no available regions.
    • Both the list and single-instance GET handlers now detect drift (live status ≠ DB status, or blank credentials now populated) and write the updated values back to the database; list endpoint fires-and-forgets, single-instance endpoint awaits before responding.
  • Destructive Confirmations Use Toast Actions — All destructive confirmation dialogs across the admin and profile UI previously used the browser's native confirm(), which blocks t...
Read more

[2.4.1] - Files, Forms & Flexibility

10 Apr 09:27

Choose a tag to compare

Added

  • Static Web Standards Filesrobots.txt and manifest.webmanifest moved to public/ for faster serving and simpler deployment.
    • public/robots.txt — Static robot exclusion file for search engines and web crawlers; disallows /api/, /dashboard/, /admin/ paths globally and blocks AI crawlers from user content (/u/).
    • public/manifest.webmanifest — Static PWA manifest with standalone display, dark theme (#09090b background, #F97316 accent), app shortcuts (Upload, Shorten URL, Pricing), and maskable SVG icon support.
    • No route handlers (app/robots.ts, app/manifest.ts removed); Next.js serves from public/ directly and links manifest from metadata.
  • CORS Headers for File Embedding — Emberly-hosted files and images can now be embedded and used externally on other websites.
    • New packages/lib/api/cors.ts module provides addCORSHeaders(), addSecurityHeaders(), handleCORSPreflight(), and getCORSHeaders() utilities.
    • File serving routes now respond with Access-Control-Allow-Origin: * on both requests and preflight OPTIONS, enabling cross-origin file access from any domain.
    • Response headers include X-Content-Type-Options: nosniff (prevents MIME-type sniffing) and Referrer-Policy: no-referrer-when-downgrade (enables safe cross-origin embedding).
    • Content-Disposition: inline (not attachment) allows browsers to display files/images inline instead of forcing downloads, enabling embedding in <img>, <video>, <canvas>, <iframe>, and XMLHttpRequest contexts.
    • Files remain correctly permission-checked server-side; CORS headers simply allow the browser to fetch them cross-origin once access is confirmed.
    • Updated routes: /api/files/[...path]/ (raw file serving), /api/files/[id]/download/, /api/files/[id]/thumbnail/, /api/avatars/[filename]/.
  • SMTP Email Provider Support — The email system now supports SMTP as an alternative to Resend, selectable via Admin → Settings → Integrations.
    • emailProvider ('resend' | 'smtp', default 'resend') and smtp (host, port, secure, user, password, from) added to the config schema and DEFAULT_CONFIG in packages/lib/config/index.ts.
    • packages/lib/emails/index.ts — added getSmtpTransport() backed by nodemailer with connection caching; sendEmail() now branches on integrations.emailProvider: SMTP path renders React templates to HTML via @react-email/render and sends via nodemailer; Resend path unchanged.
    • Admin settings UI (packages/components/admin/settings/settings-manager.tsx) — Email Provider selector (Resend / SMTP) added to the Resend section; new SMTP section with Host, Port, Secure (TLS) toggle, Username, Password, and From Address fields plus a Test Connection button.
    • app/api/admin/integrations/test/route.ts — added testSmtp() using nodemailer.createTransport().verify() and wired 'smtp' into the IntegrationKey union and switch.
  • File Sharing & Collaborators — Files can now be shared with other users directly from the file manager.
    • app/api/files/[id]/collaborators/route.ts — new GET / POST / PATCH / DELETE endpoints for managing per-file collaborators with role support (viewer / editor).
    • Share button added to every FileCard; opens a dialog showing current collaborators (with role badges and remove controls) and an invite form (email + role).
    • FileSharedEmail template (packages/lib/emails/templates/file-shared.tsx) — notifies the recipient with file name, sharer name, and a direct link.
    • Squad files are now correctly scoped: GET /api/files filters by squadId when a query param is provided, with membership verification.
  • Dashboard Tabbed NavigationDashboardShell (packages/components/dashboard/dashboard-shell.tsx) rewritten from a sidebar layout to a horizontal pill tab strip.
    • Tabs: Overview, Files, Upload, Paste, Links, Domains, Analytics, Buckets, Discovery.
    • Active tab highlighted with bg-primary/10 text-primary border border-primary/20; strip is horizontally scrollable on mobile inside a ScrollIndicator.
    • header prop still accepted and rendered full-width above the tab strip.
  • Squads Dashboard Migrated to /dashboard/squads — The squads section previously embedded in the Discovery dashboard is now a standalone route at /dashboard/squads.
  • File Grid Owned HeroFileGrid (packages/components/dashboard/file-grid/index.tsx) now owns the full glass-card hero — title, description, and view switcher dropdown — eliminating the need for a separate header prop on the files page.
    • ViewMode is a discriminated union: { type: 'my-files' } | { type: 'shared' } | { type: 'squad'; squadId: string; squadName: string }.
    • Squad selector dropdown lets users switch between My Files, Shared with Me, and per-squad views.
  • Upload & Paste Forms in Glass Cardsupload-form.tsx and paste-form.tsx content sections wrapped in glass-card panels for visual consistency with the rest of the dashboard.
  • Private Promo Codes — Admins can now mark a Stripe promotion code as private so it is hidden from the public Discounts tab on the pricing page while still being redeemable at checkout.
    • app/api/admin/promo-codes/route.tsPOST accepts isPrivate: boolean; when true, sets metadata: { private: 'true' } on the Stripe promotion code. GET returns isPrivate derived from the same metadata field.
    • app/api/payments/promo-codes/route.ts — public endpoint now filters out any code whose Stripe metadata.private equals 'true' before returning results.
    • packages/components/admin/payments/promo-codes-manager.tsx — "Private code" toggle (Switch + description) added to the create dialog; private codes display an EyeOff · Private pill badge inline with their code in the table. Create form default state extended with isPrivate: false.

Fixed

  • Admin Broadcast Email Markdown Not Rendering — The compose UI advertised markdown support but the AdminBroadcastEmail template rendered the body as a plain text node, causing **bold**, # headings, - lists, etc. to appear literally in the email.
    • Added a marked renderer with email-safe inline styles for all elements (paragraphs, headings h1–h3, bold, italic, lists, links, blockquotes, code blocks, inline code, horizontal rules) since email clients strip <style> tags and don't honour Tailwind class names on dynamically injected HTML.
    • Replaced <Text>{body}</Text> with <div dangerouslySetInnerHTML={{ __html: marked.parse(body) }} /> in packages/lib/emails/templates/admin-broadcast.tsx.
  • Admin Product List Redesignpackages/components/admin/products/ProductManager.tsx product list rebuilt to match the v2 glass aesthetic.
    • New TypeBadge component with colour-coded variants per type: plan → blue, addon → violet, nexium-plan → orange, one-time → slate.
    • Product rows use glass-subtle border-divided list (consistent with blog-list and partner-list) replacing the old glass-card wrapper.
    • Action buttons (Stripe, Sync, Edit seed, Delete) are always visible instead of appearing only on hover.
    • Removed scale-75 Switch hack; switches render at full size with proper aria-label.
    • Inline skeleton loading rows replace the old full-page spinner, maintaining layout stability during load.
    • Stats row shows price as $X.XX / interval with the Stripe product ID in dim 10px mono beneath the feature badge row.
  • Legal Article Page Redesignpackages/components/legal/LegalArticle.tsx rebuilt for a more open, professional reading experience.
    • Removed inner max-w-5xl constraint that was double-constraining content inside DashboardWrapper's max-w-7xl.
    • Three-card layout replacing the old heavy border stack: glass-subtle meta strip (type + date), glass-subtle article body with generous sm:p-10 padding, and glass-subtle footer CTA — all spaced via space-y-4.
    • Meta strip is a compact inline row replacing the large icon header card.
    • Sidebar cards switched from manual relative rounded-xl bg-background/80 backdrop-blur-lg border + gradient overlay to glass-subtle rounded-xl, consistent with the rest of the admin/dashboard UI. Sidebar column narrowed from 280 px to 260 px.
  • Collaborators Route req Reference BugPOST and PATCH handlers in the collaborators route incorrectly referenced req instead of request, causing runtime errors on every invocation. All handlers rewritten with consistent request naming and duplicate exports removed.
  • File Filters Accidentally Triggered During Mobile Scroll — On touch devices, scrolling past the filter buttons opened their dropdowns because Radix DropdownMenu/Popover fire on pointerup, which coincides with a finger lift ending a scroll gesture.
    • useScrollSafeOpen hook added to file-filters.tsx — touch-triggered opens are deferred by one requestAnimationFrame; if the browser fires pointercancel (scroll took over) before the frame runs, the pending open is cancelled. Mouse and keyboard interactions open immediately with no change in behaviour.

Policy

  • SLA Uptime Commitment Adjusted — Monthly uptime guarantee updated from 99.9% to 99.6% to better reflect a sustainable foundation for Emberly's current stage of growth.
  • Infrastructure Timezone Alignment — Monitoring, maintenance windows, and status reporting moved from UTC to Mountain Standard Time (MST / GMT-7) to align with Emberly's Canada-based operations.

[2.4.0] - Refined Vistas

10 Apr 04:23

Choose a tag to compare

Added

  • Profile Route Moved to /me — User profile settings previously lived at /dashboard/profile; now a standalone route at /me with its own layout and sidebar.
    • app/(main)/me/layout.tsx — new layout using DashboardWrapper (dashboard nav, no footer).
    • app/(main)/me/page.tsx — profile page powered by ProfileClient which has its own Account / Content / Engagement / Billing tab sidebar.
    • app/(main)/me/logout-button.tsxLogoutButton moved from dashboard/profile/.
    • /dashboard/profile route removed; /profile redirect page updated to point at /me.
    • OAuth callback routes (GitHub and Discord link/unlink) updated to redirect to /me after completion.
  • Full-Width Hero Cards on All Dashboard & Admin Pages — Every dashboard and admin page now renders a full-width glass hero/title card above the [sidebar | content] flex row, rather than inside it.
    • DashboardShell gains an optional header?: React.ReactNode prop; when provided it renders full-width above the sidebar+content flex row.
    • AdminShell gains the same header prop with identical behaviour.
    • All dashboard pages (/dashboard, /dashboard/files, /dashboard/analytics, /dashboard/upload, /dashboard/urls, /dashboard/domains, /dashboard/paste, /dashboard/verification-codes, /dashboard/bucket, /dashboard/discovery) updated to pass their hero as header.
    • All admin pages (/admin, /admin/users, /admin/blog, /admin/legal, /admin/settings, /admin/logs, /admin/email, /admin/testimonials, /admin/partners, /admin/products, /admin/reports, /admin/applications) updated with the same pattern.
    • Hero glass-cards extracted from client components (DashboardIndex, AnalyticsOverview, AdminOverviewContent) — server pages own the hero, client components render content only.
    • DashboardShell removed from dashboard/layout.tsx; each page wraps individually so the shell appears exactly once per page.
  • New Sidebar Components — Dedicated sidebar components for dashboard and admin panels.
    • packages/components/dashboard/dashboard-sidebar.tsx — collapsible nav with all dashboard routes; Discovery section expands to show Talent Profile / Squads / talent sub-links; mobile horizontal scroll strip.
    • packages/components/admin/admin-sidebar.tsx — admin panel nav with all admin routes and mobile scroll strip.
  • Files Page — New dedicated files page at /dashboard/files.
    • app/(main)/dashboard/files/page.tsx + client.tsx — full file browser with filtering, sorting, and bulk actions.
    • packages/components/dashboard/file-grid/index.tsx — significant improvements to the file grid component.
  • Discovery Mobile Tab Navigation — The Discovery page previously relied on the sidebar for section navigation, leaving mobile users with no way to switch between Talent and Squads or navigate talent sub-sections.
    • NexiumDashboardClient renders two horizontally-scrollable tab strips on mobile (lg:hidden) when not in squad-detail view.
    • Top strip: Talent Profile / Squads — controls the selectedTab state.
    • Sub-strip (Talent only): Profile / Skills / Signals / Opportunities / Applications — controls talentSection.
    • Both strips match the existing dashboard sidebar mobile style (glass-subtle rounded-xl p-1.5, active state bg-primary/10 text-primary border border-primary/20).
  • Discovery Navigation in Dashboard Sidebar — Discovery sub-links integrated directly into DashboardSidebar as a collapsible expandable section, replacing the old discovery-specific internal sidebar.
    • Parent row with ChevronDown toggle; children are Talent Profile and Squads.
    • Talent sub-links (Profile, Skills, Signals, Opportunities, Applications) render as indented secondary items when on a talent sub-page.
    • Expand state initialised to open when already under /dashboard/discovery.

Fixed

  • Double Navbar on /me RoutesConditionalBaseNav excluded /dashboard and /admin from the base nav but not /me, causing two navbars to render on the profile page (<BaseNav /> from the main layout + DashboardWrapper's fixed glass navbar).
    • Added /me to the exclusion list in conditional-base-nav.tsx.
  • LogoutButton Import Broken After Profile Movediscovery/page.tsx imported LogoutButton from ../../profile/logout-button which no longer exists after the profile route moved to /me. Fixed to ../../me/logout-button.
  • NexiumSquad.urlId / avatarUrl Invalid Field References — Prisma threw PrismaClientValidationError on the squad invite routes because urlId and avatarUrl are not fields on NexiumSquad (the correct fields are slug and logo).
    • GET /api/discovery/invites — squad select updated: urlIdslug, avatarUrllogo.
    • GET /api/discovery/invites/[token]/accept — same correction.
    • SquadIncomingInvite type in dashboard/discovery/client.tsx updated to match.
  • Squad Member Invite Flow — Various issues in the invite accept/decline redirect flow corrected.
  • Build Errors and GitHub Links — Miscellaneous build failures and broken GitHub repository links resolved.

[2.3.0] - The Discovery Update

09 Apr 06:40
eccb724

Choose a tag to compare

Added

  • Discovery Dashboard Redesign — Full sidebar-controlled layout replacing the previous flat tab strip.
    • Three sidebar modes: talent (sub-tabs), squad detail (squad tabs), and top-level (squads list).
    • Talent sub-sections — Profile, Skills, Signals, Opportunities, Applications — rendered as indented sidebar items below "Talent Profile" on desktop; horizontal strip on mobile.
    • NexiumDashboard now supports optional controlled activeSection / onSectionChange props so the outer sidebar can drive section navigation without internal state conflicts.
    • Squad detail view rendered inline via SquadDashboardClient in embedded mode; tab state owned by the parent discovery client.
  • Discovery Page Hero Header — Glass-card header on /dashboard/discovery showing the page title, a short description, quick-action links (View Public Profile, Profile Settings, Applications), and a LogoutButton.
  • GitHub Repo Picker Improvements — Signal creation flow is now picker-first when GitHub is linked.
    • Repo picker shown immediately on "Add signal"; manual form is an explicit secondary option.
    • Org switcher replaced with a compact native <select> dropdown (user shown first, then orgs alphabetically).
    • Multi-select checkboxes on every repo row; selection highlighted with border-primary/40 bg-primary/5.
    • Footer "Add X signals" bulk-submit button appears when at least one repo is selected; POSTs each repo as a separate signal and shows a count toast on completion.
    • Info callout explains why repos are shown and lists other signal types (deployed apps, OSS contributions, shipped products, etc.) with a link to the manual form.
    • Manual form redesigned with "Browse repos" back button and "Cancel" header; no longer shows an inline GitHub shortcut inside the form body.
  • GitHub read:org OAuth Scope — GitHub account linking now requests read:org in addition to public_repo,repo, so repositories from organizations the user belongs to are accessible in the picker.
    • Organization repos included via affiliation=owner,collaborator,organization_member on the GitHub repos API call.
    • "Grant org access" re-auth link in the picker footer and error state for users needing to re-authorize.
  • Squad Branding FieldsNexiumSquad schema extended with tagline (≤120 chars), logo (URL), banner (URL), website, twitter, github, and discord social/web presence fields.
  • Squad File RelationFile model gains a nullable squadId foreign key and NexiumSquad relation (onDelete: SetNull), enabling squad-owned file uploads alongside per-user files.
  • Discovery Dashboard Nav Link — Dashboard navigation updated to surface /dashboard/discovery directly under the Dashboard menu.
  • Email Template Expansion — 13 new transactional email templates covering the full account lifecycle.
    • EmailChangedOldEmail / EmailChangedNewEmail — dual notifications sent to both old and new address when email is changed, including timestamp and change source.
    • ExportRequestedEmail / ExportCompletedEmail — confirmations for data export request and download-ready notifications.
    • DeletionRequestedEmail / DeletionCancelledEmail / AccountDeletedEmail — full account deletion flow emails with cancellation window and final confirmation.
    • SubscriptionCreatedEmail / SubscriptionUpdatedEmail / SubscriptionCancelledEmail — Stripe subscription lifecycle notifications with plan details.
    • PaymentSucceededEmail / PaymentFailedEmail — payment confirmation and failure alerts with invoice context.
    • RefundIssuedEmail — refund confirmation with amount and reason.
    • All templates barrel-exported from packages/lib/emails/index.ts and wired into the event handler in packages/lib/events/handlers/email.ts.
  • Email Event Handler Overhaulpackages/lib/events/handlers/email.ts completely rewritten.
    • Each template now has a dedicated dispatch branch instead of falling through to the generic BasicEmail renderer.
    • Existing templates (welcome, verify-email, magic-link, password-reset, account-change, perk-gained, quota-reached, storage-assigned, new-login, admin-broadcast, Nexium invite/welcome/opportunity, application status/reply, bucket credentials) all converted to typed sendTemplateEmail calls.
    • Eliminates the previous "body string split" fallback that caused inconsistent rendering across templates.
  • Squad Create Dialog — Full-featured squad creation dialog replacing the previous single-field inline form.
    • packages/components/discovery/squad-create-dialog.tsx — React Hook Form + Zod validated dialog with name, description, max size, public toggle, and comma-separated skills fields.
    • Accepts any trigger element as children so it can be opened from any context.
  • Add Member Dialog — Searchable user picker for squad owner to invite members.
    • packages/components/discovery/add-member-dialog.tsx — live search via /api/users/search with debounced input, avatar display, and one-click add.
    • Shows user name, handle, and email; highlights already-added members with a checkmark badge.
  • Squad Members Manager Component — Standalone member list with role and kick controls.
    • packages/components/discovery/squad-members-manager.tsx — renders member rows with inline role <select> (MEMBER / OBSERVER) and kick button; owner row is non-editable.
    • Calls POST /api/discovery/squads/[id]/members for both role changes and kicks.
  • Squad Settings Form — Full settings and danger-zone panel for squad owners.
    • packages/components/discovery/squad-settings-form.tsx — editable name, description, max size, public toggle, and skills; separate danger zone card with AlertDialog-gated disband action.
    • Validates with Zod, shows inline field errors, and navigates away on successful disband.
  • User Search API EndpointGET /api/users/search?q=query&limit=10.
    • Returns id, name, email, image, urlId for matching users; searches name and email case-insensitively.
    • Used by AddMemberDialog for live member lookups.
  • Squad Members GET EndpointGET /api/discovery/squads/[id]/members now returns the full member list for authenticated squad members (was previously missing entirely).
  • GitHub-Style Signal Cards — Nexium proof-of-skill signals now render as rich GitHub embed widgets for GITHUB_REPO type signals, and branded cards for all other types.
    • New packages/components/profile/signal-card.tsxGitHubRepoCard renders with GitHub's dark #0d1117 surface, blue repo name link, muted description, topic chips, language color dot (30+ language colors from linguist palette), and live ⭐/🍴 counts pulled from metadata.
    • Generic signals show the user-supplied logo/banner image as a card header, falling back to a colored type abbreviation chip when no image is provided.
    • SignalCard dispatches to the correct renderer based on type; exported SignalCardData type consumed by all three display locations.
  • Signal Logo / Banner URL — Non-GitHub signals now accept an optional imageUrl field for custom logos or banners displayed in the signal card header.
    • imageUrl added to SignalInputSchema in packages/types/dto/nexium.ts and exposed as a form field in the Nexium dashboard panel.
    • Field is hidden for GITHUB_REPO signals since the owner avatar is sourced automatically from GitHub API metadata.
  • Automatic GitHub Repo Metadata Fetch — When a GITHUB_REPO signal is created or updated with a github.com URL, the API now automatically fetches live repository metadata and stores it in the signal's metadata field.
    • POST /api/discovery/signals — calls parseGitHubUrl() then getRepo(owner, repo) before writing to the database; stores full_name, description, stargazers_count, forks_count, language, topics, and owner.avatar_url.
    • PUT /api/discovery/signals/[id] — re-fetches metadata when the URL field changes on an existing GITHUB_REPO signal, keeping star/fork counts fresh on edit.

Fixed

  • Squad Dashboard Blank Screen — Squad detail page rendered nothing after a previous session changed the GET route to return { squad, isOwner } while the client still read the response as the squad object directly.
    • Reverted GET /api/discovery/squads/[id] to return the squad directly via apiResponse(squad); the isOwner flag is redundant since page.tsx passes role as a prop from the server.
  • Kick Member Failures — Kick action was broken in two independent ways.
    • Wrong HTTP method: client called DELETE /members which invokes leaveSquad() on the caller (owners can't leave, so always errored). Fixed to POST /members with { userId, kick: true }.
    • Wrong ID field: client passed m.user.urlId (human slug) instead of the UUID. Fixed by adding id: true to SQUAD_INCLUDE's user selects in packages/lib/nexium/squads.ts.
  • ShareX Squad Uploads Returning 401POST /api/files only checked session and personal upload tokens; squad-issued upload tokens and nsk_ API keys were never validated.
    • getSquadFromBearerToken() is now called first; on match, the squad owner's session is used as the acting user.
    • Squad uploads check storageQuotaMB separately from user quota and increment nexiumSquad.storageUsed (in bytes) in the same DB transaction.
  • Overview Stat Counts Always — API keys and domain counts on the squad overview tab showed dashes because those resources were only fetched when the user switched to their respective tabs.
    • Both are now pre-fetched on initial mount alongside the squad load so overview cards populate immediately.
  • Members Route Now Handles Four POST CasesPOST /api/discovery/squads/[id]/members was previously only handling the join-self flow.
    • Rewritten to dispatch on...
Read more

[2.2.0] - Infrastructure & Access Control

04 Apr 11:00
bf0baed

Choose a tag to compare

Added

  • Stripe API Clover Compatibility — Updated promo code creation to use new Stripe v2025-11-17.clover API structure.
    • promotionCodes.create now uses promotion: { type: 'coupon', coupon: string } wrapper instead of direct coupon: string parameter.
    • Applied to three endpoints: POST /api/admin/promo-codes, GET /api/admin/promo-codes, and POST /api/payments/promo-codes.
    • Promo code responses properly expand nested coupon data via expand: ['data.promotion.coupon'] for complete pricing details.
  • Promo Code Orphan Prevention — Coupon deletion rollback mechanism when promotion code creation fails.
    • After creating a coupon, if promotionCodes.create fails, the orphaned coupon is automatically deleted to prevent dangling resources.
    • Reduces Stripe admin cleanup burden and improves data consistency.
  • Kener Status Page Integration — Real time service status monitoring from Kener instance.
    • Maps Kener v4 monitor state field format to aggregated health statuses: ACTIVEUP, INACTIVEDOWN.
    • GET /api/status returns aggregated system health from all visible monitors with 60 second cache TTL.
    • Properly differentiates between Kener workflow state (ACTIVE/INACTIVE) and actual health status (UP/DOWN/DEGRADED).
  • Graceful Status Fallback — Status endpoint returns UNKNOWN status instead of 503 when Kener is unreachable.
    • Improves UX for development environments without configured status page.
    • Prevents production status page from failing hard when external monitoring is unavailable.
  • User Buckets Storage System — Granular file storage organization per user.
    • New UserBucket model with storage quota tracking and access control.
    • Prisma migration: 20260404075543_add_user_buckets — Database schema for bucket management.
    • Enables team-based storage organization without requiring squads.
  • User Grants & Permissions System — Role-based access control and permission management.
    • New UserGrant model and permission system for granular authorization.
    • Prisma migration: 20260404085324_add_user_grants — Permission tracking and enforcement.
    • Supports delegation of admin functions without full superadmin access.
  • Sentry Error Tracking Integration — Client and server error reporting with @sentry/nextjs.
    • Automatic error capture from both browser and server environments.
    • Environment-specific configuration via sentry.client.config.ts, sentry.server.config.ts, and sentry.edge.config.ts.
    • Sentry source map upload and release tracking for production deployments.
  • Admin Applications List Redesign — Comprehensive card based redesign replacing plain table layout.
    • Individual glass card rows with user avatars (initials fallback), role badges, and action buttons.
    • Header card with live stats pills showing PENDING and REVIEWING application counts via Prisma groupBy aggregation.
    • Proper empty state with centered icon when no applications exist.
  • Admin Applications Detail Redesign — Improved detail page layout with icon headers and organized sections.
    • Header card with accent gradient top bar, ClipboardCheck icon, and back navigation.
    • Applicant card uses next/image with initial fallback instead of bare <img> tag for proper image optimization.
    • Section headers use small caps muted styling for visual hierarchy.
    • Metadata sidebar displays icon prefixed rows instead of plain key/value pairs for better readability.
  • Pricing Discounts Section Redesign — Flat card design removing layered nesting.
    • Changed from outer wrapper with inner nested glass-subtle cards to flat glass-card per discount.
    • Matches the design pattern of AddOnSelector component.

Changed

  • Documentation Files Comprehensive Update — Production-ready documentation for open source project.
    • README.md expanded from minimal placeholder to complete project overview including:
      • Feature breakdown (file storage, domains, verification, teams, applications, admin tools)
      • Tech stack reference (Next.js 15, React 19, TypeScript, Tailwind, Stripe, Prisma, PostgreSQL, Kener)
      • Quick start setup guide with prerequisites and step-by-step instructions
      • Directory structure overview and contribution guidelines
      • Support channels (Discord, GitHub Discussions, email)
      • License and acknowledgments sections
    • CONTRIBUTING.md normalized for consistency:
      • Removed hyphens in compound words (real-timerealtime, longer-formlonger form)
      • Removed emoji formatting from template text
      • Maintained comprehensive developer setup, PR process, and coding standards documentation
  • Kener Status API Response Mapping — Updated aggregation logic to handle Kener v4 format correctly.
    • Recognizes ACTIVE as enabled monitor state (treated as UP health status).
    • Differentiates between workflow state and actual health status properly.
    • Fallback to UNKNOWN status prevents status page crashes during Kener outages.
  • GitHub Module Export Structure — Fixed module export for better client bundling.
    • Removed unused github object export from packages/lib/github/index.ts.
    • Clients now import only required functions (getGitHubUser, getOrgRepos, etc.) instead of entire module.
  • Grant Constants Client Access — Separated client-safe constants from server code.
    • public-profile.tsx now imports GRANT_META and ALL_GRANTS directly from packages/lib/grants/constants.
    • Prevents Prisma (server-only database code) from being bundled into browser JavaScript.

Fixed

  • TypeScript Nullish Coalescing Operator Syntax — Resolved Turbopack build error in GitHub utility.
    • Fixed operator precedence in packages/lib/github/index.ts:181 by adding parentheses.
    • Changed: org ?? integrations.github?.org || process.env.GITHUB_ORG ?? 'EmberlyOSS'
    • To: org ?? (integrations.github?.org || process.env.GITHUB_ORG) ?? 'EmberlyOSS'
  • Admin Applications Pages File Truncation — Resolved file truncation issues from concurrent editing.
    • app/(main)/admin/applications/page.tsx — truncated old table code block removed, file normalized to 224 lines.
    • app/(main)/admin/applications/[id]/page.tsx — stray old code after return removed, missing closing brace added, file normalized to 243 lines.
  • Review Form Hydration Error — Fixed React hydration mismatch in application review form.
    • packages/components/admin/applications/review-form.tsx — replaced invalid <Badge> render inside <p> tag with <div> flex container.
    • Text content wrapped in <span> elements to maintain layout structure without hydration mismatches.
  • Stripe Promo Code Expansion Fields — Public promo code endpoint now correctly expands promotion coupon data.
    • Added expand: ['data.promotion.coupon'] to promotionCodes.list calls for complete coupon details in responses.
    • Clients can now access coupon discount amounts directly from promo code API responses.
  • Prisma Bundle in Client Components — Removed Node.js database library from browser bundles.
    • public-profile.tsx client component no longer imports server-only database utilities.
    • Reduced bundle size and prevented runtime errors from missing Node.js modules (dns, fs, net, tls) in browser context.
  • Debug Console Output Cleanup — Removed temporary logging statements from Kener integration.
    • Cleaned up [KENER] prefixed console logs used during debugging phase.

Technical

  • Prisma Migrations — Three migrations for storage and permissions infrastructure.
    • 20260404075543_add_user_buckets — User bucket management schema with quota tracking.
    • 20260404085324_add_user_grants — User grants and permission system models.
  • Status Endpoint Architecture — Kener integration in packages/lib/kener/index.ts.
    • getKenerStatus() fetches from /api/v4/monitors with Bearer token authorization.
    • aggregateStatus() intelligently maps monitor states to health statuses with proper fallback logic.
    • 60 second cache TTL reduces API load while maintaining reasonable freshness.
  • Sentry Configuration — Multi-environment error tracking setup.
    • sentry.client.config.ts — Browser client configuration with sourcemap upload and release tracking.
    • sentry.server.config.ts — Backend API server error capture.
    • sentry.edge.config.ts — Edge runtime (middleware) error handling.
    • Environment-aware initialization with development/production specific settings.

[2.1.0] - Quality of Life Improvements

03 Apr 21:19
22efc00

Choose a tag to compare

Added

  • Brand Icon System (skill-icons.tsx) — New shared utility in packages/components/profile/ mapping 100+ skill name patterns to brand icons.
    • Uses react-icons/si (Simple Icons SVG) for modern tech: React, Next.js, TypeScript, Docker, Kubernetes, Terraform, GraphQL, Tailwind CSS, Svelte, Angular, Kotlin, Flutter, Rust, Go, and more.
    • Uses devicons CSS font for supplemental coverage: Python, Node.js, PHP, Ruby, Java, Swift, Dart, PostgreSQL, MySQL, MongoDB, Redis, GitHub, Firebase, AWS, HTML5, CSS3, Sass, Linux, Ubuntu, Debian, jQuery, npm, Laravel, Django, Meteor, Heroku, Jenkins, Travis CI, and more.
    • getSkillIcon(name) returns a type-discriminated si | di entry; SkillIcon component renders SVG or devicons glyph with correct brand color.
    • Consumed by both public-profile.tsx and nexium-dashboard.tsx.
  • Skill Level Bar — New SkillLevelBar component replacing verbose badge text with 4 colored dots (blue/green/orange/purple by level). Used in public profile Talent tab and Nexium dashboard Skills panel.
  • Skills Category Grouping — Skills are now grouped by category with a subtle category header in both the public profile and Nexium dashboard panels.
  • Signal Type Icon Chips — Each signal type renders a colored icon chip for quick visual identification. GitHub repo signals use the SiGithub brand icon. All signal cards redesigned with bolder title, small uppercase type label, and cleaner layout.
  • Per-Tier Contributor & Booster Badges — Perk role badges completely redesigned with custom gradient pill styling per tier.
    • Contributor: Bronze (amber), Silver (slate/zinc), Gold (yellow), Platinum (cyan/teal), Diamond (sky→violet gradient).
    • Booster: Bronze (amber), Silver (slate/zinc), Gold (yellow), Platinum (fuchsia), Diamond (purple→pink gradient).
    • Each badge includes a tier icon and a gradient border, replacing the generic flat <Badge> component.
  • Discord Social Link Icon — Profile Discord social link now renders the SiDiscord brand icon (#5865F2) instead of the generic MessageCircle lucide icon.
  • Admin User Verify — New admin action to manually verify a user's account.
    • POST /api/admin/users/[id]/verify — sets isVerified: true (or toggles it) on the target user.
    • isVerified and storageQuotaMB fields added to USER_ADMIN_SELECT; active subscription with product details also included.
    • Verify button in the admin user list shows BadgeCheck icon; verified users display a blue checkmark next to their name.
  • Admin User List — Plan & Storage columns — Each user row now shows:
    • A plan badge (with Zap icon and plan name, or "Free" fallback).
    • A storage usage bar with color thresholds (orange at 75%, red at 90%).
  • Subscription Sync Endpoint — New POST /api/payments/sync-subscription re-syncs the authenticated user's active Stripe subscriptions into the database. Useful when the original checkout webhook was missed or not yet configured.
  • Storage Quota Auto-HealinggetPlanLimits() now automatically attempts a one-time Stripe sync when no active subscription is found in the database. A per-user 5-minute TTL cache prevents excessive Stripe calls.
  • Analytics Plan & Usage CardAnalyticsOverview now includes a Plan & Usage section showing:
    • Current plan name and badge.
    • Storage progress bar with used/total and percentage.
    • Upload size cap and custom domain limit (from plan).
    • GET /api/analytics/overview now returns quotaInfo and planInfo fields.
  • Billing History — Payment Methods & Subscriptions — The billing settings tab now shows:
    • All saved payment methods (card brand, last 4 digits, expiry, default indicator).
    • All Stripe subscriptions with status badges (Active/Trial/Past Due/Cancelled), renewal date, billing interval, and amount.
    • Fetches all payment method types via stripe.customers.listPaymentMethods (captures Link-attached cards that type:'card' misses).

Changed

  • Public Profile Redesignpublic-profile.tsx fully rewritten with a tabbed layout (Overview, Files, URLs, Contributions, Talent). Contributions tab lazy-loads on first click.
  • Contributions API PerformanceGET /api/users/[id]/contributions parallelized with Promise.allSettled — all repo and commit detail fetches now run concurrently, eliminating sequential O(repos × commits) round-trips.
  • Analytics GatingGET /api/analytics/overview now enforces plan-based gating on topFiles and topUrls fields (returns empty arrays for free tier) instead of unconditionally sending them.
  • Domain Slot CountinggetPurchasedDomainSlots() now counts yearly subscription domain slots alongside legacy one-off purchases using a parallel Promise.all query.
  • package.json Cleanup — Removed bun as a runtime dependency; added react-icons and devicons as explicit dependencies; tsx added for script execution.

Fixed

  • /u/[shortCode] Username Lookup — Short URL redirect now correctly resolves by username/vanity ID instead of raw user ID.
  • Proxy Custom Domain Root Rewrite — Fixed incorrect rewrite target when a visitor hits / on a verified custom domain.
  • Private Profile Handling — Profile page now correctly shows a private state instead of partially rendering when profileVisibility is private.
  • Duplicate Declaration Crash — Removed stale duplicate code block appended after public-profile.tsx rewrite that caused five symbols to be declared twice.
  • Analytics formatBytes Unit BugAnalyticsOverview was treating MB values as raw bytes; now correctly multiplies by 1 024² before formatting.
  • Stripe Credit Balance Sign — Billing history was displaying Stripe's negative customer balance (credit) as a negative number; now correctly inverted to show credit as a positive stripeBalance.

[v2.0.0] - Talent Discovery

03 Apr 02:50
5dabf1b

Choose a tag to compare

Added

  • Royal Purple Theme - Signature preset Emberly theme with rich purple color palette.
    • Royal Purple (💜) now the default theme for all new installations.
    • Comprehensive color configuration with custom hue/saturation/lightness controls.
    • Theme preset system expanded with additional base and animated themes.
    • Persistent theme selection in user profile persisted to database.
  • Discord Webhook Notification System - Complete Discord integration for account and event notifications.
    • New /api/profile/discord-webhook/test endpoint to validate webhook URLs and send test notifications.
    • Discord notification handler integrated into event system with automatic preference gating.
    • User model extended with discordWebhookUrl, discordNotificationsEnabled, and discordPreferences fields.
    • Notification preferences UI in profile settings with category toggles: Security, Account, Billing, Marketing, Product Updates.
    • Event-driven delivery: billing events, security alerts, and account changes routed to Discord webhooks.
    • Prisma migration: 20260330011308_add_discord_webhook_notification_preferences.
  • Custom Domain Routing via Proxy - Visitors on custom domains see owner's public profile on root path.
    • New /api/internal/domain-lookup endpoint for secure hostname → profile mapping.
    • Middleware-level custom domain detection with fallback to normal routing for non-root paths.
    • Dynamic profile lookup using vanityId, urlId, and name with public visibility checks.
    • Enables white-label-style domains pointing to individual creator profiles.
  • Nexium Talent Discovery Platform - New /nexium page introducing talent discovery features coming to Emberly.
    • Landing page showcasing Nexium's core features: unified profiles, proof-of-skill signals, smart opportunity routing, squad collaboration.
    • "How It Works" journey: Show Your Best Work → Prove with Signals → Match with Opportunities → Collaborate Fast.
    • Feature cards with animated hover effects and gradient icons.
    • FAQ section with 5 key questions about platform scope, audience, and timeline.
    • Audience badges: Developers, Creators, Community Managers, Studios.
    • Call-to-action buttons linking to registration and GitHub/Discord community channels.
  • Nexium Backend Infrastructure - Complete talent platform backend with profiles, skills, signals, opportunities, applications, and squads.
    • 8 new Prisma models: NexiumProfile, NexiumSkill, NexiumSignal, NexiumOpportunity, NexiumApplication, NexiumSquad, NexiumSquadMember, NexiumSquadSubscription, NexiumSquadApiKey.
    • 8 new enums: NexiumAvailability, NexiumSkillLevel, NexiumSignalType, NexiumOpportunityType, NexiumOpportunityStatus, NexiumApplicationStatus, NexiumSquadStatus, NexiumSquadRole.
    • NexiumProfile extends User with unique @handle (lowercase, 3–32 chars), title, headline, availability, lookingFor tags, timezone, and location.
    • NexiumSkill supports level (Beginner → Expert), category (13 categories including Frontend, Backend, Game Dev, Data/ML), and sort ordering (max 30 per profile).
    • NexiumSignal tracks proof-of-work artifacts: GitHub repos, deployed apps, open-source contributions, shipped products, certifications, and more (max 20 per profile, with optional verification).
    • NexiumOpportunity supports Full-Time, Part-Time, Contract, Collab, and Bounty types with budget ranges, deadlines, required skills, and team size.
    • NexiumApplication workflow: PENDING → VIEWED → SHORTLISTED → ACCEPTED/REJECTED/WITHDRAWN with one-app-per-opportunity constraint.
    • Prisma migration: 20260330091056_add_nexium_tables.
  • Nexium Library Modules - Full business logic layer across 7 modules in packages/lib/nexium/.
    • constants.ts: Platform-wide limits (30 skills, 20 signals, 20 squad members, 10 API keys), handle regex, and enum-to-label mappings for all 8 enums.
    • profiles.ts: CRUD + discovery with paginated listProfiles() filtering by availability, skill, and lookingFor tags. Handle availability check with case-insensitive validation.
    • skills.ts: Add, update, remove, reorder (atomic transaction), and bulk replace for profile skills.
    • signals.ts: Add, update, remove, reorder, and admin verifySignal() for proof-of-work verification.
    • opportunities.ts: CRUD with poster ownership enforcement, paginated listing with type/skill/remote filters.
    • applications.ts: Apply/withdraw for applicants, list/status-update for opportunity posters.
    • squads.ts: Full squad lifecycle (create/update/disband), membership management (join/leave/kick/role), upload tokens, API keys, quota, and custom domains.
    • Zod DTOs in packages/types/dto/nexium.ts for all request/response validation (15+ schemas).
    • Barrel export via packages/lib/nexium/index.ts.
  • Nexium API Routes - 20+ REST endpoints for the complete Nexium platform.
    • Profile: GET/POST/PUT/DELETE /api/nexium/profile, GET /api/nexium/profile/[handle] (public lookup).
    • Skills: GET/POST /api/nexium/skills, PUT/DELETE /api/nexium/skills/[id] — supports add, bulk replace, and reorder via POST.
    • Signals: GET/POST /api/nexium/signals, PUT/DELETE /api/nexium/signals/[id] — supports add and reorder via POST.
    • Opportunities: GET/POST /api/nexium/opportunities, GET/PUT/DELETE /api/nexium/opportunities/[id], GET/POST/DELETE /api/nexium/opportunities/[id]/apply.
    • Squads: GET/POST /api/nexium/squads (with ?mine=true filter), GET/PUT/DELETE /api/nexium/squads/[id], POST/DELETE /api/nexium/squads/[id]/members.
    • All routes use requireAuth() with appropriate ownership checks; public endpoints available for discovery.
  • Nexium Squad Infrastructure - Teams/squads with quotas, billing, API access, upload tokens, and plans.
    • NexiumSquad model with storageUsed, storageQuotaMB, uploadToken (unique UUID), and stripeCustomerId for Stripe billing integration.
    • NexiumSquadSubscription model mirrors user Subscription, reuses existing Product table for plan management.
    • NexiumSquadApiKey model with nsk_ prefixed keys, SHA-256 hashed storage, prefix display (first 12 chars), and lastUsedAt tracking. Max 10 keys per squad.
    • Upload token management: GET/POST /api/nexium/squads/[id]/token — generate and rotate Bearer tokens for squad file uploads.
    • API key management: GET/POST /api/nexium/squads/[id]/keys, DELETE /api/nexium/squads/[id]/keys/[keyId] — full key shown once on creation, only prefix visible after.
    • Quota endpoint: GET /api/nexium/squads/[id]/quota — returns plan name, storage used/quota, upload size cap, percent usage.
    • Free tier defaults: 5 GB storage, 500 MB upload size cap, 3 custom domains, 10 API keys.
    • Prisma migration: 20260330135926_add_nexium_squad_billing_and_api_keys.
  • Nexium Squad Custom Domains - Squads can own and manage custom domains independently from users.
    • CustomDomain model updated: userId made nullable, new squadId foreign key and NexiumSquad relation added.
    • Domain management API: GET/POST /api/nexium/squads/[id]/domains, DELETE /api/nexium/squads/[id]/domains/[domainId].
    • Domain limits enforced per plan (3 for free tier, unlimited for paid plans via subscription lookup).
    • Cloudflare integration for domain registration and removal via existing registerCustomDomain/removeCustomDomain utilities.
    • Upload validation updated: validateSquadCustomDomain() validates domain ownership for squad-authenticated uploads.
  • Nexium Squad Authentication - Squad-level Bearer token and API key authentication integrated into existing auth system.
    • New AuthenticatedSquad type in api-auth.ts with squadId, slug, ownerUserId, storageUsed, storageQuotaMB, and authMethod (upload_token | api_key).
    • getSquadFromBearerToken(req): Authenticates squads via Bearer header — tries squad upload token first, then SHA-256 hashes nsk_ prefixed keys against NexiumSquadApiKey.keyHash.
    • requireSquadAuth(req): Wrapper returning { squad, response } for use in API route handlers.
    • User domain validation extended: validateCustomDomain() now accepts domains owned by any squad the user is a member of.
  • Nexium Squad ShareX Config - Pre-configured ShareX .sxcu download for squad file uploads.
    • New endpoint: GET /api/nexium/squads/[id]/sharex — generates and downloads a ShareX config file.
    • Config uses squad's upload token as Bearer auth, prefers squad's primary custom domain if available.
    • Version 15.0.0 format with MultipartFormData body, {json:data.url} response URL parsing.
    • Only accessible to squad members with an active upload token.
  • Nexium Dashboard - Standalone squad management dashboard at /dashboard/nexium with full glass-card styling.
    • Dashboard list page: View all squads the user belongs to, create new squads inline, status badges (Forming/Active/Completed/Disbanded), member counts.
    • Squad detail page at /dashboard/nexium/squads/[id] with membership verification and role-based access.
    • Tabbed squad management interface with 6 tabs:
      • Overview: Squad info cards (members, domains, API keys), slug, max size, visibility, skills tags.
      • Members: Member list with avatars, role badges (Owner/Member/Observer), kick functionality for owners.
      • Uploads: Upload token display/hide/copy/rotate, ShareX .sxcu config download button.
      • API Keys: Create named keys, one-time full key display with copy, revoke keys, prefix-only listing with last-used dates.
      • Domains: Add/remove custom domains, verified/pending status badges, primary domain indicator.
      • Storage: Plan name, storage usage progress bar with color thresholds (green/yellow/red), max upload size display.
    • Lazy-loaded tab data fe...
Read more

[1.4.0] - Status Page & Quality of Life

04 Jan 11:08
3abb802

Choose a tag to compare

Added

  • Public User API Access - Added /api/users to public paths for contribution stats visibility.
    • Public profiles can now display GitHub contribution statistics without authentication.
    • Ensures contribution data is accessible for public profile pages.
  • Custom Status Page System - Comprehensive system status page powered by Instatus API integration.
    • New /status page displaying real-time service health, incidents, and maintenance windows.
    • TypeScript types for full Instatus API coverage (packages/types/instatus.ts): StatusSummary, StatusComponent, Incident, Maintenance, and related interfaces.
    • Instatus client library (packages/lib/instatus/index.ts) with public and authenticated API support.
    • Supports both public API (/summary.json, /v2/components.json) and authenticated API (/v2/:page_id/...) with Bearer token.
    • Environment variables: INSTATUS_API_KEY, INSTATUS_PAGE_ID, INSTATUS_STATUS_URL for configuration.
    • API routes: /api/status, /api/status/components, /api/status/incidents, /api/status/maintenances.
    • Status components: StatusBadge, StatusIcon, StatusHeader, ComponentsList, ActiveIncidentsPanel, ActiveMaintenancesPanel, IncidentHistory, MaintenanceHistory, UptimeDisplay, StatusPageSkeleton.
    • Tabbed interface organizing Components, Incidents, and Maintenances with count badges.
    • Glass-morphism styling consistent with rest of site using GlassCard components.
    • Expandable incident/maintenance cards showing update timelines with HTML message support.
    • Parent-child component hierarchy built dynamically from flat API responses using group.id references.
    • Auto-refresh capability with manual refresh button and last-updated timestamps.
    • Responsive design with mobile-optimized tab navigation.
  • Media Kit Generator Script - Automated media kit generation for press and promotional use.
    • New scripts/generate-media-kit.ts script generates complete media kit zip file.
    • Run with bun run media-kit to generate /public/emberly-media-kit.zip.
    • Includes: logos (SVG, PNG), brand guidelines, color palette documentation, typography guide, promotional videos.
    • Auto-generates README, BRAND_GUIDELINES.md, COLOR_PALETTE.md, and TYPOGRAPHY.md documentation.
    • Copies all video assets from /public/videos/ with technical specs and usage guidelines.
    • Creates downloadable zip using PowerShell (Windows) or native zip (Unix).
  • Promotional Videos in Media Kit - Added video showcase section to press media kit page.
    • New Promotional Videos section in /press/media-kit page between Logo Assets and Color Palette.
    • Client-side VideoPlayer component with hydration-safe rendering to avoid SSR mismatches.
    • Videos display with native HTML5 controls, download buttons, and duration indicators.
    • Videos: site-preview-ad.mp4 (interface overview), uploading-ad.mp4 (upload flow demo).
  • Community Documentation - Added open source contribution and conduct guidelines.
    • CONTRIBUTING.md with development setup, coding standards, commit conventions, and PR guidelines.
    • CODE_OF_CONDUCT.md based on Contributor Covenant 2.1 with enforcement guidelines.
    • Contact information updated to use correct domain (hey@embrly.ca, Discord invite link).

Changed

  • Profile Dashboard Tab Navigation - Migrated from select menu to proper icon-based tabs.
    • Replaced Select dropdown with horizontal TabsList component for better UX.
    • Added icons to all 10 profile tabs: Profile (User), Billing (CreditCard), Uploads (Upload), Security (Shield), Perks (Gift), Referrals (Users), Notifications (Bell), Appearance (Palette), Testimonials (MessageSquare), Data (Database).
    • Gradient glow effect behind tab container for visual depth.
    • Responsive design: icons only on mobile (hidden sm:inline for labels), full labels on larger screens.
    • Active tab styling with primary color highlight, subtle border, and shadow.
    • Glassmorphism tab container with backdrop blur and semi-transparent background.
  • Referrals Section Responsive Design - Improved mobile experience for referral code creation.
    • Form input and "Create Code" button now stack vertically on mobile (flex-col sm:flex-row).
    • Button stretches to full width on mobile for better tap targets.
    • Code requirements text uses break-words to prevent overflow on narrow screens.
    • "How Billing Credits Work" sections have responsive padding (p-3 sm:p-4) and list margins (ml-3 sm:ml-4).
    • List items use break-words class for long text handling.
  • Markdown Table Rendering - Added horizontal scrolling and improved table styling.
    • Tables wrapped in scrollable container with overflow-x-auto for mobile responsiveness.
    • Custom table header styling with subtle background color (bg-white/5 dark:bg-black/10).
    • Table headers use whitespace-nowrap to prevent awkward text breaking.
    • Consistent padding and border styling on all table cells.
    • Hover effect on table rows for better interactivity (hover:bg-white/5).
    • Negative margin with padding (-mx-2 px-2) allows tables to use full width while maintaining scroll container.
  • Environment Variable Consolidation - Unified domain configuration to use existing NEXT_PUBLIC_BASE_URL.
    • Updated OAuth routes (GitHub and Discord) to use NEXT_PUBLIC_BASE_URL instead of deprecated NEXT_PUBLIC_APP_URL.
    • Changed fallback domain from https://emberly.site to https://emberly.ca across all authentication flows.
    • Updated root layout metadata base URL to ensure proper Open Graph and Twitter card generation.
    • Ensures consistent redirect URI configuration between OAuth initiation and callback handling.
  • GitHub Actions Build Workflow Simplified - Removed unnecessary PostgreSQL service from CI.
    • Build workflow no longer spins up PostgreSQL container since prisma generate doesn't require a database connection.
    • Reduced CI complexity and build times by eliminating unused service dependency.
    • Workflow now: checkout → setup Node/Bun → install dependencies → prisma generate → build.
  • Security Section Responsive Design - Enhanced mobile experience for login history and session management.
    • Login history header now stacks vertically on mobile (flex-col sm:flex-row) with proper gap spacing.
    • Action buttons (Refresh, Sign Out Everywhere) now stretch to full width on mobile (flex-1 sm:flex-none).
    • Current session card padding reduced on mobile (p-3 sm:p-4) with responsive text sizing (text-xs sm:text-sm).
    • Login entry cards feature smaller gaps on mobile (gap-2 sm:gap-3) and responsive padding (p-1.5 sm:p-2).
    • Device icons and metadata icons use flex-shrink-0 to prevent squishing on narrow screens.
    • Added break-all for IP addresses and break-words for location text to prevent overflow.
  • Recovery Codes UI Overhaul - Converted to modal-based display with improved accessibility.
    • Removed Card wrapper in favor of cleaner inline section layout matching other security components.
    • Recovery codes now display in a Dialog modal instead of inline expansion for better focus and security.
    • Modal features scrollable code list with max-h-[50vh] preventing viewport overflow on long lists.
    • Statistics grid improved with responsive layout: 2 columns on mobile, 4 columns on desktop (grid-cols-2 sm:grid-cols-4).
    • Text sizes scale appropriately: text-xs sm:text-sm for labels, text-xl sm:text-2xl for stats.
    • Action buttons (View Codes, Regenerate Codes) stack vertically on mobile with full width.
    • Modal action buttons (Download as File, Copy All Codes) use default size instead of small for better tap targets.
    • Code display cards feature break-all on code text to handle long strings gracefully.
    • Warning banners and important notices use responsive text sizing for readability.
  • GitHub Contributions API Optimization - Improved reliability and performance of contribution statistics.
    • Reduced commits fetched per repository from 10 to 5 to avoid GitHub API rate limiting.
    • Added individual try-catch blocks around commit detail fetches to prevent single failures from breaking entire stats.
    • Improved error handling with specific error logging per commit and repository.
    • Stats calculation (additions, deletions, files changed) now properly accumulates even with partial failures.
  • Press Pages Theme Compatibility - Fixed hardcoded colors to respect active theme.
    • Press page hero section now uses theme variables (text-foreground, text-primary) instead of hardcoded colors.
    • Media kit color palette dynamically pulls from CSS variables to display actual theme colors.
    • Color swatches show live theme values with proper hex code extraction from computed styles.

Fixed

  • Rich Embeds Metadata System - Fixed inconsistent behavior where enableRichEmbeds setting was not respected for all file types.
    • Images now respect enableRichEmbeds=false: Previously images always showed preview regardless of setting; now returns minimal metadata with no image preview.
    • Videos now respect enableRichEmbeds=false: Previously videos still generated video metadata; now returns minimal metadata with no media.
    • Middleware now checks user settings: Updated bot-handler.ts to query user's enableRichEmbeds setting from database before redirecting bot requests.
    • No embed metadata when disabled: When rich embeds are disabled, buildMinimalMetadata() returns pure metadata with NO openGraph or twitter card data, ensuring Discord/Twitter show plain links without any embed cards.
    • Videos work properly when enabled: When enableRichEmbeds=true, videos use raw URL directly so Discord/Twitter can extract their own thumbnail and play the video inlin...
Read more

[1.3.0] - Public Profiles and More.

29 Dec 17:53

Choose a tag to compare

Added

  • Enhanced Public Profile System - Comprehensive user profile pages with GitHub integration and milestone-based perk displays.
    • Tab-based interface with Overview, Contributions, and Files sections for organized content presentation.
    • Milestone tier system display: Bronze (🥉), Silver (🥈), Gold (🥇), Platinum (💎), and Diamond (💠) badges for contributors and Discord boosters.
    • Real-time contribution stats fetched from GitHub API showing lines of code, repository activity, and commit history.
    • Public files showcase displaying user's shared files with view counts, download stats, and upload dates.
    • Social account badges: Discord username badge with themed styling, GitHub profile link with external indicator.
    • Perk benefits breakdown showing tier-specific storage bonuses and custom domain slot allocations.
    • "How to Earn Perks" section guiding users on becoming contributors, Discord boosters, or affiliates.
  • GitHub Contributions API (/api/users/[id]/contributions) - Detailed contribution metrics and activity tracking.
    • Fetches total lines of code contributed across EmberlyOSS repositories.
    • Lists contributed repositories with name, description, programming language, and star count.
    • Retrieves up to 10 most recent commits with SHA, message, date, and repository name.
    • Collects commit statistics: files changed, lines added (green), lines deleted (red).
    • Aggregates total contribution stats: cumulative files changed, additions, deletions, and repository count.
    • Uses GitHub PAT for public profile viewing to avoid rate limiting and token exposure.
  • Public Files API (/api/users/[id]/public-files) - Public file listing endpoint.
    • Queries user's public files (visibility: 'PUBLIC') with comprehensive metadata.
    • Returns file details: name, URL path, MIME type, size, view count, download count, upload date.
    • Limits results to 20 most recent files ordered by upload date descending.
    • Generates full URLs for direct file access.
  • Dashboard Profile Query Parameters - URL-based tab navigation support.
    • Added ?tab= query parameter support to profile settings page (e.g., /dashboard/profile?tab=security).
    • Tab state syncs with URL using window.history.pushState for shareable links.
    • Initial tab selection reads from URL on page load with validation against available tabs.
    • Works with both tabs component and select menu for consistent navigation experience.

Changed

  • Public Profile Architecture Refactored - Switched from API-based to direct database access.
    • Removed intermediate API route calls in favor of server-side Prisma queries for better performance.
    • Updated to use Next.js 15 async params pattern (await params) throughout dynamic routes.
    • Implemented GlassCard component pattern for visual consistency across profile sections.
    • Consolidated perk calculation logic in server components to reduce client-side processing.
  • Perk Display System Enhanced - Accurate milestone-based benefit visualization.
    • Updated to display actual tier names (Bronze/Silver/Gold/Platinum/Diamond) instead of generic labels.
    • Contribution statistics now show precise lines of code count with locale formatting.
    • Discord booster duration displayed in months with proper pluralization.
    • Storage and domain bonuses calculated from milestone constants with tier-specific values.
    • Added visual tier icons for quick recognition of achievement levels.
  • API Route Organization - Consolidated dynamic routes under consistent parameter naming.
    • Moved contribution and file endpoints from /api/users/[username]/ to /api/users/[id]/ to avoid route conflicts.
    • Renamed files endpoint to public-files to prevent collision with existing /api/users/[id]/files/[fileId]/ route.
    • Updated routes to accept id, urlId, vanityId, or username for flexible user lookup.
    • Frontend updated to use user.id for API calls ensuring consistent identifier usage.
  • GitHub Integration Display - Rich commit history and repository contribution visualization.
    • Contributions tab now shows detailed commit cards with truncated SHA hashes and clickable links.
    • Added color-coded statistics: green for additions, red for deletions, blue for files changed.
    • Repository cards display with hover effects and external link indicators.
    • Commit metadata includes repository name, file count, line changes, and formatted dates.

Fixed

  • Next.js 15 Compatibility Issues - Resolved dynamic route parameter handling errors.
    • Fixed "params is a Promise" errors by properly awaiting params in all dynamic route handlers.
    • Removed invalid fetch cache options causing build-time errors.
    • Updated API route type definitions to use Promise<{ id: string }> parameter types.
  • Prisma Schema Field Errors - Corrected field name mismatches across queries.
    • Fixed accounts field references to use correct linkedAccounts relation name.
    • Updated username field access to use providerUsername from LinkedAccount model.
    • Corrected provider account data structure queries for GitHub and Discord integrations.
  • Dynamic Route Naming Conflicts - Resolved slug parameter conflicts in API routes.
    • Eliminated "You cannot use different slug names for the same dynamic path" error.
    • Standardized on [id] parameter naming throughout /api/users/ route hierarchy.
    • Prevented route collision between user files endpoint and existing file management routes.
  • Public Profile 404 Errors - Fixed routing and data fetching issues.
    • Resolved profile not found errors by ensuring proper user lookup across urlId, vanityId, and name fields.
    • Added isProfilePublic: true filter to all public profile queries for privacy enforcement.
    • Implemented proper null handling and not-found redirects for non-existent or private profiles.

[1.2.0] - User Referrals and More

29 Dec 13:21

Choose a tag to compare

Added

  • 2FA Recovery Codes System - One-time backup codes for account recovery if authenticator is lost.
    • Generate 10 recovery codes when enabling 2FA, displayed only once to the user.
    • Codes stored in database with used/unused tracking and timestamps.
    • Users can regenerate codes anytime to invalidate old ones and create new ones.
    • Recovery codes work as valid 2FA authentication alongside TOTP codes.
    • Enhanced Recovery Codes UI - Beautiful glass-morphism design with individual copy buttons, copy-all functionality, and download as text file.
    • View Codes Anytime - "View Codes" button allows users to fetch and display all unused recovery codes on demand (not just after generation).
    • Individual code copy buttons with visual feedback (checkmark animation on copy).
    • Numbered code list with monospace font, hover effects, and responsive layout.
    • Prominent download and copy-all action buttons side-by-side.
    • Session storage persistence for recently generated codes (cleared on browser close).
    • Visual status dashboard showing total codes, used codes, and remaining codes with count badges.
    • Warning indicators when only 3 or fewer codes remain.
    • Automatic invalidation of all codes when 2FA is disabled.
    • New TwoFactorRecoveryCode model with batchId for tracking code generations.
    • /api/profile/2fa/recovery-codes endpoints for status, regeneration, and download with optional ?includeCodes=true parameter to fetch unused codes.
    • RecoveryCodesManager component in security settings to manage codes.
    • getUnusedRecoveryCodes() utility function to retrieve all unused recovery codes for a user.
  • Environment configuration template (.env.template) with clear placeholder values, helpful comments, and format examples for all required environment variables.
  • Complete two-factor authentication (2FA) system using TOTP (Time-based One-Time Password) with authenticator app support.
    • New TwoFactorForm component for secure 6-digit authenticator code entry with real-time validation.
    • 2FA enforcement in NextAuth credentials provider: users with twoFactorEnabled must enter a valid authenticator code before login succeeds.
    • Added twoFactorEnabled and twoFactorSecret fields to JWT and session interfaces for persistent 2FA state tracking.
    • Proper error-based 2FA flow: throws TwoFactorRequired error when 2FA is enabled but code not provided, forcing frontend to show 2FA form.
    • Support for both password and magic link authentication paths with identical 2FA enforcement.
  • Comprehensive metadata coverage across all pages:
    • Added metadata exports to home page using buildSiteMetadata() for dynamic OG/Twitter image generation.
    • Added metadata to all admin pages: User Management, Platform Settings, Blog Management, Products, Docs Management, Legal Pages, Partner Management, Testimonial Management, Audit Logs, and Email Broadcasts.
    • All metadata uses consistent buildPageMetadata() helper with title and description for SEO and social previews.
  • GitHub/Discord account linking system for social authentication and perk system integration.
    • OAuth endpoints for GitHub (/api/auth/link/github) and Discord (/api/auth/link/discord) account linking.
    • Automatic contributor detection: users with 1000+ lines of code across EmberlyOSS repos get +1GB storage per 1000 LOC.
    • Automatic Discord booster detection: server boosters get +5GB storage + 1 custom domain slot.
    • LinkedAccount model to store OAuth connections with access tokens and provider metadata.
    • Perk utilities for calculating storage/domain bonuses and managing contributor levels.
  • Cross-domain session sharing via Redis for seamless login across multiple domains.
    • Switched from JWT to database session strategy using Redis adapter.
    • Sessions now shared between emberly.site and embrly.ca domains automatically.
    • Users log in on one domain and are authenticated on both.
  • Enhanced login history tracking with complete device and IP information.
    • Captures client IP address with support for Vercel, Cloudflare, and standard x-forwarded-for headers.
    • Stores user agent, device fingerprint, country, and city for each login.
    • Displays parsed device type (Desktop/Mobile/Tablet), browser, and OS in security dashboard.
    • Device icons and detailed geographic information shown in login history.
    • New device detection triggers automatic email alerts for suspicious logins.
  • Billing Credits & Transaction Logging System - Comprehensive credit tracking for audit trail and financial transparency.
    • New CreditTransaction Prisma model with userId, type, amount, description, and related metadata fields.
    • Transaction logging for all credit operations: referral earnings, credit applications, manual adjustments, and refunds.
    • /api/profile/billing-history endpoint for retrieving credit transaction history with graceful error handling.
    • BillingCreditsSection component in user profile displaying current balance, Stripe customer balance, and recent transaction activity.
    • Transaction metadata includes relatedUserId for referral tracking and relatedOrderId for purchase attribution.
    • Automatic transaction logging when credits are applied to Stripe customer balance during checkout.
  • Custom Referral Codes System - User-friendly referral code creation replacing auto-generated codes.
    • Users can create custom, memorable referral codes (3-30 characters, alphanumeric with hyphens/underscores).
    • Custom code form in profile with real-time validation feedback.
    • Validation prevents reserved words (admin, api, auth, dashboard, settings, profile, billing, null).
    • Brand name protection blocks referral codes containing trademarked terms (emberly, pixelated, codemeapixel).
    • Case-insensitive substring matching ensures brand variations cannot bypass restrictions.
    • /api/profile/referrals POST endpoint supports creating custom codes with comprehensive validation.
    • Two-state referral component: creation form when no code exists, full statistics and sharing options when code is active.
  • Enhanced Payment Routes with Centralized Stripe Utilities - Consolidated payment processing infrastructure.
    • All payment routes (/api/payments/portal, /api/payments/webhook, /api/payments/checkout, /api/payments/purchase) refactored to use centralized utilities.
    • ensureStripeCustomer() utility validates and creates Stripe customer IDs consistently across all payment flows.
    • applyReferralCreditsToStripe() utility manages credit application and transaction logging with metadata tracking.
    • Webhook handler enhanced with credit transaction logging for purchase completion events.
    • Checkout and purchase routes now include order metadata for credit transaction attribution.

Changed

  • Metadata system significantly refactored and simplified:
    • Consolidated metadata building from ~400+ lines across multiple helper functions into streamlined buildRichMetadata(), buildSiteMetadata(), and buildPageMetadata() functions.
    • Removed 150+ lines of unnecessary helper functions (buildOpenGraphImages, buildOpenGraphAudio, buildTwitterMetadata, buildOtherMetadata).
    • Removed hardcoded image references in favor of Next.js auto-detection of opengraph-image.tsx and twitter-image.tsx.
    • Improved URL handling using URL constructor instead of manual string manipulation.
    • enableRichEmbeds setting now properly enforced: disables rich metadata for both images and videos, returning minimal metadata instead.
    • Password-protected files now return minimal metadata to prevent embedding sensitive content in social previews.
    • Added strict fileId validation requirement for rich metadata generation to ensure thumbnail URLs are available.
  • Login form updated to detect 2FA requirement and display TwoFactorForm component:
    • Stores pending credentials during 2FA verification flow.
    • Clear error messaging for invalid authenticator codes.
    • "Back to login" button allows users to restart authentication flow.
  • Dashboard metadata conflicts resolved:
    • Removed redundant metadata definitions from dashboard/layout.tsx and pricing/layout.tsx.
    • Each dashboard page now defines its own specific metadata with unique titles and descriptions.
    • Applied buildPageMetadata() consistently across all dashboard pages.
  • Short URL layout metadata cleaned up:
    • Removed 30+ lines of verbose metadata with explicit null/empty field definitions.
    • Simplified to only include essential robots: { index: false, follow: false } to prevent search engine indexing.
  • OG/Twitter image generation updated to use actual Emberly logo instead of placeholder.
    • Logo now renders with dynamic colors based on selected theme.
    • Proper two-color flame icon design applied to social media previews.
  • Storage quota system now includes perk bonuses.
    • Domain slot calculation includes Discord booster +1 domain bonus.
    • Storage quota calculation includes contributor and booster storage bonuses.
    • Perk bonuses stack additively for users with multiple perk roles.
  • README updated to clearly establish this as the Emberly Cloud instance:
    • Added tech stack documentation (Next.js 14, TypeScript, PostgreSQL, Prisma, NextAuth.js, Tailwind CSS, shadcn/ui).
    • Clarified distinction between this cloud-hosted repository and the upcoming self-hosted open-source distribution.
    • Included cloud-specific features (Stripe billing, Resend email, analytics, 2FA).
    • Added development setup instructions.

Fixed

  • Critical 2FA enforcement vulnerability: users with 2FA enabled were able to bypass authentication without entering an authenticator code.
    • Root cause: NextAuth's authorize function was returning user objects on password validation, which NextAuth int...
Read more