{"id":15946537,"url":"https://github.com/inmanturbo/b2bsaas0","last_synced_at":"2025-06-18T03:39:42.239Z","repository":{"id":206294920,"uuid":"716297708","full_name":"inmanturbo/b2bsaas0","owner":"inmanturbo","description":"Multitenancy Template Based on Laravel Jetstream, Volt and Folio","archived":false,"fork":false,"pushed_at":"2024-01-26T19:00:36.000Z","size":822,"stargazers_count":4,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-01-31T14:59:31.389Z","etag":null,"topics":["b2b","b2bservices","folio","laravel","mariadb","multitenancy","mysql","saas-boilerplate","sqlite","starter-kit","starter-template","volt"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/inmanturbo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2023-11-08T20:59:08.000Z","updated_at":"2024-04-12T05:17:36.000Z","dependencies_parsed_at":"2024-01-17T00:37:11.153Z","dependency_job_id":"c1a23c38-1ce3-41cb-8e4a-8af5f78d421f","html_url":"https://github.com/inmanturbo/b2bsaas0","commit_stats":null,"previous_names":["inmanturbo/b2bsaas"],"tags_count":0,"template":true,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inmanturbo%2Fb2bsaas0","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inmanturbo%2Fb2bsaas0/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inmanturbo%2Fb2bsaas0/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inmanturbo%2Fb2bsaas0/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/inmanturbo","download_url":"https://codeload.github.com/inmanturbo/b2bsaas0/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":237073135,"owners_count":19251036,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["b2b","b2bservices","folio","laravel","mariadb","multitenancy","mysql","saas-boilerplate","sqlite","starter-kit","starter-template","volt"],"created_at":"2024-10-07T09:22:17.917Z","updated_at":"2025-02-04T06:31:24.296Z","avatar_url":"https://github.com/inmanturbo.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# B2bSaas\n\n- [B2bSaas](#b2bsaas)\n  - [Introduction](#introduction)\n  - [Multitenancy](#multitenancy)\n    - [Teams are tenants](#teams-are-tenants)\n    - [Users can have many databases](#users-can-have-many-databases)\n  - [Master Password](#master-password)\n      - [when using the master password to register a user](#when-using-the-master-password-to-register-a-user)\n  - [Impersonation](#impersonation)\n  - [Database Types](#database-types)\n  - [Configuration](#configuration)\n    - [Config file](#config-file)\n      - [APP\\_URL\\_SCHEME](#app_url_scheme)\n      - [DEFAULT\\_TEAM\\_DATABASE\\_CONNECTION\\_TEMPLATE](#default_team_database_connection_template)\n      - [B2BSAAS\\_DATABASE\\_CREATION\\_DISABLED](#b2bsaas_database_creation_disabled)\n      - [\\_\\_DB\\_DATABASE](#__db_database)\n      - [B2BSAAS\\_INVITATION\\_ONLY](#b2bsaas_invitation_only)\n  - [Installation](#installation)\n  - [Migrating tenant databases](#migrating-tenant-databases)\n\n## Introduction\n\nThis is about the simplest implementation possible, with minimal changes to the original jetstream skeleton, to to make it easier to keep it up to date with the latest changes in `laravel/jetstream`.\n\nIn order to avoid many changes to the skeleton, I've made most of my additions in the `b2bsaas/` directory, then bootstrapped them with a service provider and a Psr4 namespace of their own instead of adding them directly to the app directory.\n\n\u003e [!NOTE]    \n\u003e b2bsaas IS NOT a package under vendor/, or using any `modules` package.     \n\u003e It is just namespace, `B2bSaas` added to composer.json in the PSR4 Autoload section.      \n\u003e Feel free to modify it as needed!\n\nI've copied the blade files for the layout components directory from jetstream and added `wire:navigate` to the links to make the navigation more snappy. The simplest way to do this with as little change as possible to the original skeleton was to copy them into `inmanturbo/b2bsaas/resources/views` and ensure they are rendered by the Components in `app/View`. For example, for the `app/View/AppLayout.php` component (tagged `\u003cx-app-layout\u003e...\u003c/x-app-layout\u003e`), I've changed the render method to this:\n\n```php\n/**\n * Get the view / contents that represents the component.\n */\npublic function render(): View\n{\n    return view('b2bsaas::components.app');\n}\n```\n\nThe markup for the view `b2bsaas::components.app` lives in `inmanturbo/b2bsaas/resources/views/components/app.blade.php`.\n\n## Multitenancy\n\nYou may build your Laravel app as you normally would, and the default implementation for multitenancy will be handled for you automatically\n\nBy default tenancy is by authentication based on the user's team, but support for domain based tenancy is built in as well\n\nThe first user to login becomes a SuperAdmin\n\n- After that registration is by team invitation only except when registering using the `Master Password` (see Master Password section below)\n- Invitation only mode can be disabled by setting `B2BSAAS_INVITATION_ONLY=false` in `.env`\n\n\n\u003e[!IMPORTANT]   \n\u003eTeams, Metadata for the Tenant Databases and Authentication details are all stored in the `landlord database`\n\n\nAll of the migrations included in the template are for the landlord database, and are under `database/migrations/landlord`. These migration are not meant to be run on tenant databases!\n\nThe dynamic tenant connection is the default database connection. A typical development workflow using this template would include somethind like running `php artisan make migration create_posts_table`.    \nThis will create a migration in the default location, which would be run for all new team databases whenever a new team is created!    \n\nIf you have existing teams and would like to run your new migration for them right away, simply run `php artisan teams:migrate`. [More Info](#migrating-tenant-databases)\n\n### Teams are tenants\n\n- Setting the tenant can be done by calling `$team-\u003econfigure()-\u003euse();` on a team instance. This is done automatically when a user logs in, or when a request is for a domain registered to a team.\n  - Authentication Based tenancy \n    - [Team Auth Trait](https://github.com/inmanturbo/b2bsaas/blob/main/inmanturbo/b2bsaas/src/HandlesTeamAuth.php#L39C13-L39C13)\n    - [Team Middleware](https://github.com/inmanturbo/b2bsaas/blob/main/inmanturbo/b2bsaas/src/TeamMiddleware.php#L36)\n  - Domain Based Tenancy\n    - [Configure Requests](https://github.com/inmanturbo/b2bsaas/blob/main/b2bsaas/src/B2bSaasServiceProvider.php#L63) \n- A Team belongs to one Tenant Database, or `TeamDatabase`\n- More than one Team can be on a single database (optional)\n- Only SuperAdmins and UpgradedUsers can create Teams\n\n### Users can have many databases\n\n- Databases are created for SuperAdmins and UpgradedUsers when they create a team\n- A single Database and Team are created for each user when they register\n- Databases belong to one user\n- Databases can have many teams\n- SuperAdmins and UpgradedUsers may select an existing database that they already own when creating a new team, in the case that they want to share data across teams.\n\n## Master Password\n\u003e [!NOTE]    \n\u003e b2bsaas uses [laravel-Masterpass](https://github.com/imanghafoori1/laravel-MasterPass)\n\n#### when using the master password to register a user\n\n- `password_confirmation` field is not required\n- `password_confirmation` field can be used to set the user type (`User` is default).\n\nSimply enter one of the following into the `password_confirmation` field when registering a new user:\n\n- `UpgradedUser`\n  - Can Create Teams\n- `SuperAdmin`\n  - Can Create Teams and Impersonate\n- `User`\n  - Cannot Create Teams or Impersonate\n  - Can Invite others to join thier `personal_team`\n\n## Impersonation\n\nSuperAdmins have the ability to impersonate other users\n\n- Start by adding `start_impersonate={user_id}` to any request\n- End by adding `stop_impersonate` to any request\n- Only SuperAdmin users can Impersonate\n\n## Database Types\n\n\u003e [!NOTE]    \n\u003e Currently only `mysql`, `mariadb` and `sqlite`, support are currently implemented for tenant databases\n\nYou can easily support another database by extending `\\App\\Models\\TeamDatabase` and overriding some key methods found in `\\B2bSaas\\InteractsWithSystemDatabase`\n\nTeam Databases, like Users, SuperAdmins and UpgradedUsers use Single Table Inheritance based on the implementation found here: \u003chttps://github.com/tighten/parental\u003e, with a few small changes to support using an enum for the `type column`\n\nThe `team_databases` table has a `connection_templat`e column, the value of which should be the name of a `\\App\\TeamDatabaseType` that references a model.\n\nThis name must also be the name of a database connection which holds the configuration details for the database connection. Example:\n\nThe type:\n\n```php\n...\nenum TeamDatabaseType: string\n{\n    ...\n    case tenant_sqlite = Models\\SqliteTeamDatabase::class;\n    ...\n}\n```\n\nThe Model:\n\n```php\n\u003c?php\n\nnamespace App\\Models;\n\nuse Artisan;\nuse Inmanturbo\\B2bSaas\\HasParent;\nuse Illuminate\\Support\\Facades\\Storage;\n\nclass SqliteTeamDatabase extends TeamDatabase\n{\n    use HasParent;\n\n    protected function createTeamDatabase(bool $testing = false): self\n    {\n        $name = (string) str()-\u003eof($this-\u003ename)-\u003eslug('_');\n\n        if ($this-\u003eteamDatabaseExists(testing: $testing)) {\n            $name = $name.'_1';\n            $this-\u003ename = $name;\n            $this-\u003ecreateTeamDatabase(testing: $testing);\n        }\n\n        $userUuid = (string) $this-\u003euser-\u003euuid;\n\n        // create storage directory for user if it doesn't exist\n        if (! file_exists(storage_path('app/'.$userUuid))) {\n            mkdir(storage_path('app/'.$userUuid));\n        }\n\n        if (! file_exists(storage_path('app/'.$userUuid.'/'.$name.'.sqlite'))) {\n            Storage::disk('local')-\u003eput($userUuid.'/'.$name.'.sqlite', '');\n        }\n\n        return $this;\n    }\n\n...\n}\n```\n\nThe `connection_template`:\n\n```php\n// config/database.php\n...\n  'connections' =\u003e [\n\n...\n        'tenant_sqlite' =\u003e [\n            'driver' =\u003e 'sqlite',\n            'url' =\u003e env('DATABASE_URL'),\n            'database' =\u003e env('__DB_DATABASE'),\n            'prefix' =\u003e '',\n            'foreign_key_constraints' =\u003e env('DB_FOREIGN_KEYS', true),\n        ],\n...\n  ]\n```\n\nThe above \"connection_template\" (`tenant_sqlite`) will be merged along with the tenant specific details to create a working connection for the database at runtime.\n\nNote that the connection name `tenant_sqlite` is the same as the name for the enum case. `tenant_sqlite` is also the value that will be stored in the `connection_template` column for any `App\\Models\\SqliteTeamDatabase` instances.\n\n## Configuration\n\n### Config file\n\nThe path to the b2bsaas config file is `b2bsaas/src/config/b2bsaas.php`. However most of the options can be set using environment variables in your `.env` file.\n\n#### APP_URL_SCHEME\n\nThe url scheme for the application can be set by setting `APP_URL_SCHEME`. Default is `http`. Options are `http` and `https`.    \nSetting `APP_URL_SCHEME=https` in your `.env` will force all app urls to use `https` protocol.\n\n#### DEFAULT_TEAM_DATABASE_CONNECTION_TEMPLATE\n\nSetting the `DEFAULT_TEAM_DATABASE_CONNECTION_TEMPLATE` in your `.env` to a value corresponding to a database connection will cause the application to use that connection as a template for tenant database configuration by default.    \n\nThis value must also match a case name in `app/Models/TeamDatabaseType.php`, the value of which should be a model which extends `App\\Models\\TeamDatabase` and uses the `B2bSaas\\HasParent` trait.\nThe available values can be found by inspecting the `app/TeamDatabaseType.php` file. At the time of writing this, \"out of the box\" options include `tenant_mysql`, `tenant_mariadb` and `tenant_sqlite`.\n\n\u003e[!TIP]    \n\u003eSetting `DEFAULT_TEAM_DATABASE_CONNECTION_TEMPLATE=tenant_sqlite` in your `.env` file will cause the application to create and use sqlite databases for tenants by default\n\nFor example, setting `DEFAULT_TEAM_DATABASE_CONNECTION_TEMPLATE=tenant_sqlite` in your `.env` file will cause the application to create and use sqlite databases for tenants by default. `tenant_sqlite` corresponds to the following `TeamDatabaseType`: \n\n```php\ncase tenant_sqlite = Models\\SqliteTeamDatabase::class;\n```\n\nThis means that the names of these tenant databases will be stored in the `team_databases` table of the landlord database.     \n\nThe `connection_template` column on these instances will be set to `tenant_sqlite`, which is how the application knows to use the `SqliteTeamDatabase` model for these instances. Also, `tenant_sqlite` is a database connection in `config/database.php` which will be used to build the connection config for these `TeamDatabase` instances.    \n\nBy default sqlite databases live in the `storage/app` directory, under a folder by the uuid of the user who owns the database. The `SqliteTeamDatabase` class holds the logic for how these databases are created, in a method called `createTeamDatabase`, which is called by its parent class `TeamDatabase` during boot whenever a new instance is created.\n\n#### B2BSAAS_DATABASE_CREATION_DISABLED\n\nSetting `B2BSAAS_DATABASE_CREATION_DISABLED=true` in your `.env` file will disable the automatic creation of databases for teams. Additionally, setting the value of `__DB_DATABASE` (Note the double underscore) to the name of a database will cause all teams to use the same database. This can be useful during development or manual testing if you don't want your local or staging environment littered with databases automatically created during team creation. If you set `B2BSAAS_DATABASE_CREATION_DISABLED=true` but do not set `__DB_DATABASE`, you will likely get an error, as the application will be looking for a database which doesn't exist.\n\n#### __DB_DATABASE\n\nSetting `__DB_DATABASE` to a database name will force all teams to use this same database.\n\n#### B2BSAAS_INVITATION_ONLY\n\nBy default registation is by invitation only. This means that in order to register themselves a user must first be invited by a team owner\n\nSetting `B2BSAAS_INVITATION_ONLY=false` will allow new users to register without an invitation\n\n\n## Installation\n\n```bash\ncp .env.example .env\n```\n\n```bash\ntouch database/database.sqlite\n```\n\n```bash\ncomposer install\n```\n\n```bash\n# landlord_sqlite is the default landlord connection\nphp artisan migrate:fresh --path=database/migrations/landlord --database=landlord_sqlite\n```\n\n```bash\nnpm install \u0026\u0026 npm run build\n```\n\n## Migrating tenant databases\n\n```bash\nphp artisan teams:migrate --help\n```\n\n```bash\nDescription:\n  Migrate the database for the specified team database, or all team databases if none is specified.\n\nUsage:\n  teams:migrate [options] [--] [\u003cteamDatabaseName\u003e]\n\nArguments:\n  teamDatabaseName      \n\nOptions:\n      --fresh           Wipe the database(s)\n      --seed            Seed the database(s)\n      --force           Force the operation(s) to run when in production\n      --pretend         Dump the SQL queries that would be run\n      --path[=PATH]     The path of migrations files to be executed\n      --realpath        Indicate any provided migration file paths are pre-resolved absolute paths\n      --step            Force the migrations to be run so they can be rolled back individually\n      --rollback        Rollback the last database migration\n  -h, --help            Display help for the given command. When no command is given display help for the list command\n  -q, --quiet           Do not output any message\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finmanturbo%2Fb2bsaas0","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finmanturbo%2Fb2bsaas0","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finmanturbo%2Fb2bsaas0/lists"}