MuseMVP Docs
Quick Build

Application Configuration

A deep dive into src/config/index.ts: what each config item does, where it takes effect, and suggested change patterns.

src/config/index.ts is MuseMVP's runtime configuration entry point. It centrally manages i18n, auth behavior, page switches, storage, payments, AI, and analytics.
If you want to "change product behavior," this file is usually your first stop.

What This Page Covers

This page only covers code-level configuration in src/config/index.ts. For secrets and environment variables (such as database, OAuth, and payment gateway API keys), see /docs/quick-build/environment-variables.


Configuration Guide

i18n: Internationalization

Key fields:

  • i18n.enabled
  • i18n.locales.en|zh (appName/summary/description/keywords/docsAlias/headerMenuMap/footerConfig)
  • i18n.defaultLocale
  • i18n.defaultCurrency
  • i18n.localePrefix
  • i18n.localeCookieName

Practical impact:

  • docsAlias is used as the docs page title suffix (for example, xxx | Docs).
  • headerMenuMap is reused by navigation and custom routes.
  • localePrefix affects next-intl routing behavior and the language-hiding strategy of docs sources.

Suggestion When Updating Copy

Update en and zh in sync to avoid mismatches between bilingual navigation and footer text.

users: New User Onboarding Redirect and Billing Master Switch

Key field:

  • users.enableSetup

Current behavior:

  • enableSetup=true: newly registered users (logged-in users who have not completed onboarding) are redirected to /setup.

auth: Registration Methods and Session Lifecycle

Key fields:

  • auth.gates.allowRegister
  • auth.gates.allowSocialSignIn
  • auth.gates.allowPasswordSignIn
  • auth.gates.allowTwoFactorAuth
  • auth.lifecycle.redirectAfterLogin
  • auth.lifecycle.redirectAfterSignOut
  • auth.lifecycle.redirectWhenSessionExpired
  • auth.lifecycle.sessionTtlSeconds

Behavior highlights:

  • When allowRegister=false, /auth/register redirects to /auth/login.
  • allowPasswordSignIn and allowSocialSignIn mainly control frontend entry visibility.
  • allowTwoFactorAuth mainly controls whether the 2FA block is shown on the settings page.
  • sessionTtlSeconds is written directly into Better-Auth session.expiresIn.

Easy-to-Misunderstand Point

allowTwoFactorAuth is not a backend plugin master switch. The 2FA plugin is still registered; this flag mainly controls whether the frontend exposes the entry point.

Key fields:

  • mails.from
  • mails.links.logo

Behavior highlights:

  • If mails.from is only an email address, it will be automatically assembled into the format "AppName" <email@...> at send time.
  • mails.links.logo is used for the logo in email templates. By default it uses public/icon.png under the project root, and you can also switch to a custom external image URL with faster access.

ui is the most frequently changed section, so it is best understood in blocks.

ui: {
  enabledThemes: ["light", "dark"],
  defaultTheme: "dark",
  saas: { enabled: true, useSidebarLayout: true, cookieInfo: { showBox: true } },
  features: { enabled: true },
  docs: { enabled: true },
  legal: {
    enabled: true,
    allowMap: [
      { type: "privacy", enabled: true },
      { type: "terms", enabled: true },
      { type: "behavior", enabled: false },
    ],
  },
  changelog: { enabled: true },
  blog: { enabled: true },
  contactForm: {
    enabled: true,
    to: "support@musemvp.com",
    subject: "MuseMVP contact form message",
  },
}

Behavior highlights:

  • ui.saas.enabled=false closes entries to both auth and saas areas (related layouts redirect directly to home).
  • ui.docs/blog/changelog/contactForm/features affects both nav visibility and corresponding page access.
  • ui.legal.enabled controls the legal pages master switch; allowMap mainly affects legal link display in footer/auth pages.
  • In customRoutes, items with enabled && !external are automatically added to sitemap.
  • For customRoutes, the final title prefers headerMenuMap[path], then falls back to label.
  • footerConfig.socialIcon.github.href is also used by docs pages to generate the GitHub source link.

Two Common Pitfalls

allowMap is not a legal route allowlist; it mainly controls link visibility.
Also, when ui.docs.enabled=false, the current sitemap implementation still generates docs URLs. If you need strict consistency, also update src/app/sitemap.ts.

storage: Cloudflare R2

Key fields:

  • storage.meta.S3_ACCESS_KEY_ID
  • storage.meta.S3_SECRET_ACCESS_KEY
  • storage.meta.S3_ENDPOINT
  • storage.meta.S3_REGION (default: auto)
  • storage.bucketNames.avatars (default: musemvp-com)
  • storage.proxyUrls.avatarsFile

Behavior highlights:

  • The upload API checks S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEY / S3_ENDPOINT; missing values return an error.
  • NEXT_PUBLIC_AVATARS_PROXY_URL removes a trailing / before URL composition.
  • If the avatars bucket name is not configured, it falls back to the default musemvp-com.

ai: Chat Model Selection

Key fields:

  • ai.enabled
  • ai.chatModel

Behavior highlights:

  • ai.enabled=false hides AI Chat items from account navigation, redirects /app/aichat to /app, and makes /api/aichat/** return 404.
  • The backend AI Chat route reads config.ai.chatModel directly as MODEL_ID; the default model is gemini-3-pro-preview.
  • In most cases, changing models only requires updating this one field (as long as the corresponding provider key is configured).

payments: Billing Master Switch and Product Catalog Mapping

Key fields:

  • payments.enableBilling
  • payments.enableFree
  • payments.enableEnterprise
  • payments.productCatalog

productCatalog productId resolution priority (using monthly Pro as an example):

NEXT_PUBLIC_MUSE_PRICE_PRO_MONTHLY_ID
  -> NEXT_PUBLIC_MUSE_CREEM_PRICE_PRO_MONTHLY_ID
  -> NEXT_PUBLIC_MUSE_STRIPE_PRICE_PRO_MONTHLY_ID
  -> ""

Behavior highlights:

  • productCatalog.*.gatewayProductIds is used when creating checkout sessions; missing values throw errors directly.
  • In pricing-desc-usage.ts, only price items with non-empty productId are included in plan display.
  • pricing-desc-usage.ts also supports an optional originalAmount field for compare-at pricing display. It affects only the pricing UI; checkout still uses amount.
  • enableBilling=false disables the pricing entry and related billing flow branches.

Important Boundary

The default gateway selection (MUSE_BILLING_DEFAULT_GATEWAY) and gateway API keys are in environment variables, not in config/index.ts.

analytics: Analytics Script Injection

Key fields:

  • analytics.googleAnalyticsId
  • analytics.baiduTongjiId

Behavior highlights:

  • Analytics scripts are injected only in non-development environments.
  • If an ID is empty, the corresponding script is not rendered.

Post-Change Verification Checklist

pnpm type-check
pnpm build

Confirm Header / Footer copy renders correctly in both en and zh.

Confirm visibility for docs/blog/legal/changelog/contact/features matches the switch settings.

If ai.enabled=false, confirm AI Chat no longer appears in the sidebar/profile menu, /app/aichat redirects, and /api/aichat/** is inaccessible.

Confirm login/register/sign-out redirects match auth.lifecycle config.

Confirm the pricing page only shows plans with configured productId.

If originalAmount is configured, confirm the pricing card shows the strikethrough original price while checkout still uses amount.

Confirm avatar upload and asset access match storage config (bucket name/proxy URL).