FlightControl Documentation

website·Indexed 35 days ago

https://www.flightcontrol.dev/docs

A platform for deploying and managing applications, featuring guides on self-hosting Next.js, multi-tenant SaaS data modeling strategies, and optimizing GitHub Actions usage.

Tokens
3.5K
Snippets
11
Records
29
Agent score
73%

What's inside FlightControl

  1. Implement organization-level analytics with Segment

    To track analytics at the organization level, use the group concept to map users to an Organization. When tracking events, the groupId (Organization ID) must be included in the context to ensure the event is correctly attributed to the organization.
    import Analytics from "analytics-node"
    
    const orgId = '123'
    
    // link a user to an org, and set org level traits
    analytics.group({
      userId,
      groupId: orgId,
      traits: {type: "org", orgName: 'Acme', orgStatus: 'active'},
    })
    
    // submit the actual event
    analytics.track({
      userId,
      event: 'project-created',
      properties: {userId, projectId: '123'},
      // must include groupId in context or it won't work
      context: {groupId: orgId},
    })
  2. Manage organization context in URLs and API inputs

    To avoid expensive database lookups for user roles and organization context, explicitly include the organization_id in all critical paths:

    • URLs: Use patterns like /org/[orgId]/projects/[projectId].
    • Inputs: Require organization_id in all query and mutation inputs.
    • Database Calls: Pass organization_id into every database operation to ensure the correct tenant context is maintained.
  3. Set up a CDN for self-hosted Next.js with ISR support

    A CDN is recommended to reduce server load and improve static content delivery. If using ISR or stale-while-revalidate features, you must use a CDN that supports the stale-while-revalidate cache-control header (e.g., AWS CloudFront or Fastly). Cloudflare does not support this header.
  4. Handle new user sign-up in a multi-tenant model

    When a brand new user signs up, the system should simultaneously create a User model, an Organization model, and a Membership model to link the user to their new organization. For domain-based auto-joining, create the User and Membership during signup but link them to an existing Organization associated with that domain.
  5. Handle multi-tenant user sessions

    Depending on the required user experience, choose one of these session strategies:

    • Single Org Session: Store a single organization_id in the session. Users must 'switch' organizations to change context. This can lead to 404s if a user tries to access a URL for an organization not currently in their session.
    • Multi-Org Session: Store an array of accessible organizations and roles (e.g., accessibleOrgs: Array<{orgId: string, role: string}>) in the session. This allows users to access any organization they belong to without switching, provided the session is kept in sync with their actual permissions.
  6. Deploy Next.js to serverless functions using OpenNext

    Next.js does not officially support deployment to serverless functions outside of Vercel. OpenNext is a community-driven adapter that converts Next.js builds into assets compatible with serverless functions. Because it is unofficial, it may require updates following Next.js patch or minor releases.
  7. Deploy Next.js to servers (Self-Hosting)

    Deploying to servers is the only officially supported method for hosting Next.js outside of Vercel. While next start provides core functionality, production readiness requires additional configuration for image optimization, distributed caching, and CDN integration to avoid performance and consistency issues.
  8. Assign resources to memberships instead of users

    When assigning an item (e.g., a todo or task) to a user within an organization, assign the item to the user's membership record rather than the user record directly. This allows items to be assigned to a user who has been invited to the organization but has not yet accepted the invitation.
  9. Implement Role-Based Access Control (RBAC) for multi-tenancy

    Store the user's role within the session to avoid redundant database calls. Define a mapping of roles to permissions (either statically in code or dynamically in the database). Use an abstraction layer to check if the current session's role possesses the required permission for a specific query or mutation on both the client and server.
  10. Implement organization invitations using the Membership model

    Use the Membership model to handle invitations by creating a record where userId is null. Generate a unique invitationId (e.g., a CUID) and an invitationExpiresAt timestamp. Send the user a signup link containing the invitationId as a query parameter. Upon successful signup, the backend API should match the invitationId to the existing Membership record and attach the newly created User to it.
  11. Model personal accounts as single-user organizations

    To simplify implementation and ease the upgrade path from personal to business use, avoid creating separate 'Personal' and 'Business' account types. Instead, treat a personal account as an Organization that contains only one user. This eliminates friction when a user decides to upgrade to a paid business plan.
  12. Implement concurrent user sessions for multi-tenant access

    To allow users to be simultaneously logged into multiple organizations without logging out, store two distinct arrays in the user session. accessibleOrgs tracks all memberships and should be synced across all sessions to allow for role updates or access revocation. loggedInOrgsOnThisDevice tracks which organizations are currently active on a specific device and should not be synced, ensuring each device must independently authenticate into the organizations it accesses.
    // Session structure
    {
      accessibleOrgs: Array<{orgId: string, role: string}>,
      loggedInOrgsOnThisDevice: Array<{orgId: string}>
    }