Overview
Text, title, and styling in standard markdown.
Supersaas is built with multi-tenancy in mind, using a "Team" or "Organization" structure as the primary way to group users and resources.
Concept
- Teams as Containers: Each Team acts as an isolated workspace. Users belong to teams, and data associated with your application (e.g., posts, projects, settings) should typically be scoped to a specific team.
- Ownership: Every team has a single
owner. The owner is usually the user who created the team and has the highest level of permissions within that team. - Unique Identification: Teams are identified by a unique
id(generated usingnanoid) and a user-definedslug. Theslugis used in URLs for easy access.
Creating a Team
- UI: New teams are created using the
App/NewTeamForm.vuecomponent. - Process:
- The user provides a Team Name.
- The user provides a Team URL Slug (e.g.,
my-awesome-company). This slug must be unique and follow the specified format (lowercase letters, numbers, single hyphens). The form provides real-time feedback on the final URL structure (e.g.,yourdomain.com/dashboard/my-awesome-company). - The user can optionally upload a Team Logo. If no logo is uploaded, a default avatar is generated based on the team name using DiceBear (
https://api.dicebear.com/). Uploaded logos are handled by the/api/upload-imageendpoint and stored (by default, the path assumes/images/, but you might adjust this based on your storage solution). - On submission, the
useTeam().createTeamfunction calls thePOST /api/teamsendpoint. - The backend (
server/api/teams/index.post.ts) validates the input usingcreateTeamSchemaand calls thecreateTeamdatabase query (server/database/queries/teams.ts). - The
createTeamquery inserts the team into theteamstable and automatically adds the creator to theteamMemberstable with theownerrole.
- State Management: Newly created teams are added to the
teamsstate managed byuseState<Team[]>('teams'). TheuseTeamPreferencescomposable (not fully shown, but referenced) is used to remember the last used team slug.
Team Context and Navigation
- Current Team: The
useTeam()composable provides acurrentTeamcomputed property. This determines the active team based on the:teamSlugroute parameter in the URL (e.g.,/dashboard/:teamSlug/...). - Team Switching: The
App/TeamDropdown.vuecomponent (referenced inApp/Sidebar/Team.vue) handles the UI for switching between teams the user belongs to. When a team is selected, the user is navigated to the corresponding team's dashboard (/dashboard/:teamSlug). - Sidebar: The
App/Sidebar/Team.vuecomponent dynamically updates navigation links based on thecurrentTeam.slug. It also conditionally shows settings links (Workspace Settings,Workspace Members,Billing) only if the current userisTeamOwner.
Managing Teams
- Updating:
- Team owners can update basic team settings (like name and logo). This functionality is within the
Workspace Settingspage linked from the sidebar. - The
useTeam().updateTeamfunction handles updates via thePATCH /api/teams/:idendpoint. - The backend uses the
updateTeamquery (server/database/queries/teams.ts). Access control happens within the API route or middleware to ensure only owners (or potentially admins) can update.
- Team owners can update basic team settings (like name and logo). This functionality is within the
- Deleting:
- Team deletion is a critical action restricted to the team owner.
- The
App/TeamDelete.vuecomponent provides the UI for this, requiring the owner to type the team name for confirmation. - It uses the
useTeam().deleteTeamfunction, which calls theDELETE /api/teams/:idendpoint. - The backend API route for deletion must use a mechanism like the
validateTeamOwnershiputility (server/utils/teamValidation.ts.ts) to ensure only the owner can perform this action. This utility verifies the user's session and checks their role within the target team usingfindUserTeams. - Deleting a team is irreversible and removes the team record and associated data (due to database cascade rules, e.g.,
teamMembers,teamInvites).
Relevant Files
- Composables:
app/composables/useTeam.ts - Components:
App/NewTeamForm.vue,App/TeamDelete.vue,App/Sidebar/Team.vue,App/TeamDropdown.vue(implied) - API Routes:
server/api/teams/index.post.ts,server/api/teams/[id]/index.patch.ts(implied),server/api/teams/[id]/index.delete.ts(implied) - Database:
server/database/schema/teams.ts(definesteams,teamMembers),server/database/queries/teams.ts(containscreateTeam,updateTeam,deleteTeam,findUserTeams) - Validation:
shared/validations/team.ts(definescreateTeamSchema),server/utils/teamValidation.ts - Middleware:
app/middleware/team-owner.ts(protects owner-specific routes)