mirror of
https://github.com/archtechx/tenancy.git
synced 2026-02-05 10:14:04 +00:00
Merge branch 'db-users' into dbusers
This commit is contained in:
commit
d0423cfd29
18 changed files with 337 additions and 119 deletions
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: stancl
|
||||||
|
patreon: samuelstancl
|
||||||
|
open_collective: # Replace with a single Open Collective username
|
||||||
|
ko_fi: samuelstancl
|
||||||
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
|
liberapay: # Replace with a single Liberapay username
|
||||||
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
|
otechie: # Replace with a single Otechie username
|
||||||
|
custom: ['https://www.paypal.me/samuelstancl', 'https://gumroad.com/l/tenancy']
|
||||||
31
.github/workflows/ci.yml
vendored
Normal file
31
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
env:
|
||||||
|
COMPOSE_INTERACTIVE_NO_CLI: 1
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ 2.x ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ 2.x ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
tests:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
laravel: ["^6.0", "^7.0"]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Start docker containers
|
||||||
|
run: docker-compose up -d
|
||||||
|
- name: Install dependencies
|
||||||
|
run: docker-compose exec -T test composer require --no-interaction "laravel/framework:${{ matrix.laravel }}"
|
||||||
|
- name: Run tests
|
||||||
|
run: ./fulltest
|
||||||
|
- name: Send code coverage to codecov
|
||||||
|
env:
|
||||||
|
CODECOV_TOKEN: 24382d15-84e7-4a55-bea4-c4df96a24a9b
|
||||||
|
run: bash <(curl -s https://codecov.io/bash)
|
||||||
26
.travis.yml
26
.travis.yml
|
|
@ -1,26 +0,0 @@
|
||||||
env:
|
|
||||||
- LARAVEL_VERSION="^7.0"
|
|
||||||
- LARAVEL_VERSION="^6.0"
|
|
||||||
|
|
||||||
# language: php
|
|
||||||
# php:
|
|
||||||
# - '7.4'
|
|
||||||
|
|
||||||
services:
|
|
||||||
- docker
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- docker-compose up -d
|
|
||||||
|
|
||||||
install:
|
|
||||||
- travis_retry docker-compose exec test composer require --no-interaction "laravel/framework:$LARAVEL_VERSION"
|
|
||||||
- travis_retry docker-compose exec test composer install --no-interaction
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- export DB_USERNAME=root DB_PASSWORD="" DB_DATABASE=tenancy CODECOV_TOKEN="24382d15-84e7-4a55-bea4-c4df96a24a9b"
|
|
||||||
- cat vendor/laravel/framework/src/Illuminate/Foundation/Application.php| grep 'const VERSION'
|
|
||||||
|
|
||||||
script: ./fulltest
|
|
||||||
|
|
||||||
after_success:
|
|
||||||
- bash <(curl -s https://codecov.io/bash)
|
|
||||||
16
DONATIONS.md
16
DONATIONS.md
|
|
@ -4,9 +4,15 @@ Any donations will be greatly appreciated and help ensure that the package is de
|
||||||
|
|
||||||
If you're a company and this package is helping you make money, please consider donating.
|
If you're a company and this package is helping you make money, please consider donating.
|
||||||
|
|
||||||
|
**If you'd like to donate in your local currency, see the Bank transfer section.**
|
||||||
|
|
||||||
|
### Patreon
|
||||||
|
|
||||||
|
If you would like to support me on a monthly basis, you can use Patreon: https://patreon.com/samuelstancl
|
||||||
|
|
||||||
### PayPal
|
### PayPal
|
||||||
|
|
||||||
PayPal is the preferable donation method as it comes with the lowest fees.
|
PayPal is the preferable donation method for one-time donations as it comes with the lowest fees.
|
||||||
|
|
||||||
You can donate here: [https://paypal.me/samuelstancl](https://paypal.me/samuelstancl)
|
You can donate here: [https://paypal.me/samuelstancl](https://paypal.me/samuelstancl)
|
||||||
|
|
||||||
|
|
@ -16,11 +22,17 @@ If you can't use PayPal, you may use my Gumroad link. This comes with higher fee
|
||||||
|
|
||||||
You can donate here: [https://gumroad.com/l/tenancy](https://gumroad.com/l/tenancy)
|
You can donate here: [https://gumroad.com/l/tenancy](https://gumroad.com/l/tenancy)
|
||||||
|
|
||||||
|
### Bank transfer
|
||||||
|
|
||||||
|
If you'd like to donate money from your bank account, you can can do that too. I use [TransferWise](https://transferwise.com/invite/u/samuels1719) (affiliate link 🙂), so I can accept bank transfers in virtually any currency.
|
||||||
|
|
||||||
|
Contact me on [samuel.stancl@gmail.com](mailto:samuel.stancl@gmail.com?subject=Donation) and I'll give you bank details for your local currency.
|
||||||
|
|
||||||
### Legal
|
### Legal
|
||||||
|
|
||||||
If you're a business making a donation, you may want an invoice.
|
If you're a business making a donation, you may want an invoice.
|
||||||
|
|
||||||
Contact me on [samuel.stancl@gmail.com](mailto:samuel.stancl@gmail.com) and let me know what you need to have on the invoice and I will make it happen.
|
Contact me on [samuel.stancl@gmail.com](mailto:samuel.stancl@gmail.com?subject=Donation%20with%20invoice) and let me know what you need to have on the invoice and I will make it happen.
|
||||||
|
|
||||||
### Thank you!
|
### Thank you!
|
||||||
|
|
||||||
|
|
|
||||||
14
README.md
14
README.md
|
|
@ -1,16 +1,16 @@
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://tenancy.samuelstancl.me"><img width="400" height="335" src="/art/logo.png" alt="Tenancy logo" /></a>
|
<a href="https://tenancyforlaravel.com"><img width="800" src="/art/logo.png" alt="Tenancy for Laravel logo" /></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://laravel.com"><img alt="Laravel 6.x" src="https://img.shields.io/badge/laravel-6.x-red.svg"></a>
|
<a href="https://laravel.com"><img alt="Laravel 6.x" src="https://img.shields.io/badge/laravel-6.x-red.svg"></a>
|
||||||
<a href="https://packagist.org/packages/stancl/tenancy"><img alt="Latest Stable Version" src="https://poser.pugx.org/stancl/tenancy/version"></a>
|
<a href="https://packagist.org/packages/stancl/tenancy"><img alt="Latest Stable Version" src="https://poser.pugx.org/stancl/tenancy/version"></a>
|
||||||
<a href="https://travis-ci.com/stancl/tenancy"><img alt="Travis CI build" src="https://travis-ci.com/stancl/tenancy.svg?branch=2.x"></a>
|
<a href="https://github.com/stancl/tenancy/actions"><img alt="GitHub Actions CI status" src="https://github.com/stancl/tenancy/workflows/CI/badge.svg"></a>
|
||||||
<a href="https://codecov.io/gh/stancl/tenancy"><img alt="codecov" src="https://codecov.io/gh/stancl/tenancy/branch/2.x/graph/badge.svg"></a>
|
<a href="https://codecov.io/gh/stancl/tenancy"><img alt="codecov" src="https://codecov.io/gh/stancl/tenancy/branch/2.x/graph/badge.svg"></a>
|
||||||
<a href="https://github.com/stancl/tenancy/blob/2.x/DONATIONS.md"><img alt="Donate" src="https://img.shields.io/badge/Donate-%3C3-red"></a>
|
<a href="https://github.com/stancl/tenancy/blob/2.x/DONATIONS.md"><img alt="Donate" src="https://img.shields.io/badge/Donate-%3C3-red"></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h1><a href="https://tenancy.samuelstancl.me">stancl/tenancy</a></h1>
|
<h1><a href="https://tenancyforlaravel.com">Tenancy for Laravel — stancl/tenancy</a></h1>
|
||||||
|
|
||||||
### *Automatic multi-tenancy for your Laravel app.*
|
### *Automatic multi-tenancy for your Laravel app.*
|
||||||
|
|
||||||
|
|
@ -20,9 +20,9 @@ You won't have to change a thing in your application's code.
|
||||||
- :heavy_check_mark: No replacing of Laravel classes (`Cache`, `Storage`, ...) with tenancy-aware classes
|
- :heavy_check_mark: No replacing of Laravel classes (`Cache`, `Storage`, ...) with tenancy-aware classes
|
||||||
- :heavy_check_mark: Built-in tenant identification based on hostname (including second level domains)
|
- :heavy_check_mark: Built-in tenant identification based on hostname (including second level domains)
|
||||||
|
|
||||||
### [Documentation](https://tenancy.samuelstancl.me/docs/v2/)
|
### [Documentation](https://tenancyforlaravel.com/docs/v2/)
|
||||||
|
|
||||||
Documentation can be found here: https://tenancy.samuelstancl.me/docs/v2/
|
Documentation can be found here: https://tenancyforlaravel.com/docs/v2/
|
||||||
|
|
||||||
The repository with the documentation source code can be found here: [stancl/tenancy-docs](https://github.com/stancl/tenancy-docs).
|
The repository with the documentation source code can be found here: [stancl/tenancy-docs](https://github.com/stancl/tenancy-docs).
|
||||||
|
|
||||||
|
|
@ -30,5 +30,5 @@ The repository with the documentation source code can be found here: [stancl/ten
|
||||||
|
|
||||||
### Credits
|
### Credits
|
||||||
|
|
||||||
- Created by [Samuel Štancl](https://github.com/stancl)
|
- Created by [Samuel Štancl](https://twitter.com/samuelstancl)
|
||||||
- Logo by [Caneco](https://twitter.com/caneco)
|
- Logo by [Brian Dillingham](https://twitter.com/dillinghammm)
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
If you need help with implementing the package, you can:
|
If you need help with implementing the package, you can:
|
||||||
- Open an [issue here on GitHub](https://github.com/stancl/tenancy/issues/new?assignees=stancl&labels=support&template=support-question.md&title=)
|
- Open an [issue here on GitHub](https://github.com/stancl/tenancy/issues/new?assignees=stancl&labels=support&template=support-question.md&title=)
|
||||||
- Message me (`@stancl`) on the [Unofficial Laravel Discord](https://discord.gg/zGVGFAd), in the `#stancl_tenancy` channel
|
- Join our new [Discord server](https://discord.gg/7cpgPxv) and ask in `#help`
|
||||||
|
|
||||||
|
The methods above are preferred, but you may also
|
||||||
- Contact me on Telegram: [@samuelstancl](https://t.me/samuelstancl)
|
- Contact me on Telegram: [@samuelstancl](https://t.me/samuelstancl)
|
||||||
- Send me an email: [samuel.stancl@gmail.com](mailto:samuel.stancl@gmail.com)
|
- Send me an email: [samuel.stancl@gmail.com](mailto:samuel.stancl@gmail.com)
|
||||||
|
|
|
||||||
BIN
art/logo.png
BIN
art/logo.png
Binary file not shown.
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 495 KiB |
BIN
art/old_logo.png
Normal file
BIN
art/old_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
|
|
@ -3,112 +3,286 @@
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
/**
|
||||||
|
* Storage drivers are used to store information about your tenants.
|
||||||
|
* They hold the Tenant Storage data and keeps track of domains.
|
||||||
|
*/
|
||||||
'storage_driver' => 'db',
|
'storage_driver' => 'db',
|
||||||
'storage_drivers' => [
|
'storage_drivers' => [
|
||||||
|
/**
|
||||||
|
* The majority of applications will want to use this storage driver.
|
||||||
|
* The information about tenants is persisted in a relational DB
|
||||||
|
* like MySQL or PostgreSQL. The only downside is performance.
|
||||||
|
*
|
||||||
|
* A database connection to the central database has to be established on each
|
||||||
|
* request, to identify the tenant based on the domain. This takes three DB
|
||||||
|
* queries. Then, the connection to the tenant database is established.
|
||||||
|
*
|
||||||
|
* Note: From v2.3.0, the performance of the DB storage driver can be improved
|
||||||
|
* by a lot by using Cached Tenant Lookup. Be sure to enable that if you're
|
||||||
|
* using this storage driver. Enabling that feature can completely avoid
|
||||||
|
* querying the central database to identify build the Tenant object.
|
||||||
|
*/
|
||||||
'db' => [
|
'db' => [
|
||||||
'driver' => Stancl\Tenancy\StorageDrivers\Database\DatabaseStorageDriver::class,
|
'driver' => Stancl\Tenancy\StorageDrivers\Database\DatabaseStorageDriver::class,
|
||||||
'data_column' => 'data',
|
'data_column' => 'data',
|
||||||
'custom_columns' => [
|
'custom_columns' => [
|
||||||
// 'plan',
|
// 'plan',
|
||||||
],
|
],
|
||||||
'connection' => null, // Your central database connection. Set to null to use the default connection.
|
|
||||||
|
/**
|
||||||
|
* Your central database connection. Set to null to use the default one.
|
||||||
|
*
|
||||||
|
* Note: It's recommended to create a designated central connection,
|
||||||
|
* to let you easily use it in your app, e.g. via the DB facade.
|
||||||
|
*/
|
||||||
|
'connection' => null,
|
||||||
|
|
||||||
'table_names' => [
|
'table_names' => [
|
||||||
'tenants' => 'tenants',
|
'tenants' => 'tenants',
|
||||||
'domains' => 'domains',
|
'domains' => 'domains',
|
||||||
],
|
],
|
||||||
'cache_store' => null, // What store should be used to cache tenant resolution. Set to null to disable cache or a string with a specific cache store name.
|
|
||||||
|
/**
|
||||||
|
* Here you can enable the Cached Tenant Lookup.
|
||||||
|
*
|
||||||
|
* You can specify what cache store should be used to cache the tenant resolution.
|
||||||
|
* Set to string with a specific cache store name, or to null to disable cache.
|
||||||
|
*/
|
||||||
|
'cache_store' => null,
|
||||||
'cache_ttl' => 3600, // seconds
|
'cache_ttl' => 3600, // seconds
|
||||||
],
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Redis storage driver is much more performant than the database driver.
|
||||||
|
* However, by default, Redis is a not a durable data storage. It works well for ephemeral data
|
||||||
|
* like cache, but to hold critical data, it needs to be configured in a way that guarantees
|
||||||
|
* that data will be persisted permanently. Specifically, you want to enable both AOF and
|
||||||
|
* RDB. Read this here: https://tenancy.samuelstancl.me/docs/v2/storage-drivers/#redis.
|
||||||
|
*/
|
||||||
'redis' => [
|
'redis' => [
|
||||||
'driver' => Stancl\Tenancy\StorageDrivers\RedisStorageDriver::class,
|
'driver' => Stancl\Tenancy\StorageDrivers\RedisStorageDriver::class,
|
||||||
'connection' => 'tenancy',
|
'connection' => 'tenancy',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller namespace used by routes in routes/tenant.php.
|
||||||
|
*/
|
||||||
'tenant_route_namespace' => 'App\Http\Controllers',
|
'tenant_route_namespace' => 'App\Http\Controllers',
|
||||||
'exempt_domains' => [ // e.g. domains which host landing pages, sign up pages, etc
|
|
||||||
|
/**
|
||||||
|
* Central domains (hostnames), e.g. domains which host landing pages, sign up pages, etc.
|
||||||
|
*/
|
||||||
|
'exempt_domains' => [
|
||||||
// 'localhost',
|
// 'localhost',
|
||||||
],
|
],
|
||||||
'database' => [
|
|
||||||
'based_on' => null, // The connection that will be used as a base for the dynamically created tenant connection. Set to null to use the default connection.
|
/**
|
||||||
'prefix' => 'tenant',
|
* Tenancy bootstrappers are executed when tenancy is initialized.
|
||||||
'suffix' => ''
|
* Their responsibility is making Laravel features tenant-aware.
|
||||||
],
|
*
|
||||||
'redis' => [
|
* To configure their behavior, see the config keys below.
|
||||||
'prefix_base' => 'tenant',
|
*/
|
||||||
'prefixed_connections' => [
|
|
||||||
// 'default',
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'cache' => [
|
|
||||||
'tag_base' => 'tenant',
|
|
||||||
],
|
|
||||||
'filesystem' => [ // https://tenancy.samuelstancl.me/docs/v2/filesystem-tenancy/
|
|
||||||
'suffix_base' => 'tenant',
|
|
||||||
'suffix_storage_path' => true, // Note: Disabling this will likely break local disk tenancy. Only disable this if you're using an external file storage service like S3.
|
|
||||||
'asset_helper_tenancy' => true, // should asset() be automatically tenant-aware. You may want to disable this if you use tools like Horizon.
|
|
||||||
// Disks which should be suffixed with the suffix_base + tenant id.
|
|
||||||
'disks' => [
|
|
||||||
'local',
|
|
||||||
'public',
|
|
||||||
// 's3',
|
|
||||||
],
|
|
||||||
'root_override' => [
|
|
||||||
// Disks whose roots should be overriden after storage_path() is suffixed.
|
|
||||||
'local' => '%storage_path%/app/',
|
|
||||||
'public' => '%storage_path%/app/public/',
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'database_managers' => [
|
|
||||||
// Tenant database managers handle the creation & deletion of tenant databases.
|
|
||||||
'sqlite' => Stancl\Tenancy\TenantDatabaseManagers\SQLiteDatabaseManager::class,
|
|
||||||
'mysql' => Stancl\Tenancy\TenantDatabaseManagers\MySQLDatabaseManager::class,
|
|
||||||
'pgsql' => Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLDatabaseManager::class,
|
|
||||||
// 'pgsql' => Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLSchemaManager::class, // Separate by schema instead of database
|
|
||||||
],
|
|
||||||
'database_manager_connections' => [
|
|
||||||
// Connections used by TenantDatabaseManagers. This tells, for example, the
|
|
||||||
// MySQLDatabaseManager to use the mysql connection to create databases.
|
|
||||||
'sqlite' => 'sqlite',
|
|
||||||
'mysql' => 'mysql',
|
|
||||||
'pgsql' => 'pgsql',
|
|
||||||
],
|
|
||||||
'bootstrappers' => [
|
'bootstrappers' => [
|
||||||
// Tenancy bootstrappers are executed when tenancy is initialized.
|
|
||||||
// Their responsibility is making Laravel features tenant-aware.
|
|
||||||
'database' => Stancl\Tenancy\TenancyBootstrappers\DatabaseTenancyBootstrapper::class,
|
'database' => Stancl\Tenancy\TenancyBootstrappers\DatabaseTenancyBootstrapper::class,
|
||||||
'cache' => Stancl\Tenancy\TenancyBootstrappers\CacheTenancyBootstrapper::class,
|
'cache' => Stancl\Tenancy\TenancyBootstrappers\CacheTenancyBootstrapper::class,
|
||||||
'filesystem' => Stancl\Tenancy\TenancyBootstrappers\FilesystemTenancyBootstrapper::class,
|
'filesystem' => Stancl\Tenancy\TenancyBootstrappers\FilesystemTenancyBootstrapper::class,
|
||||||
'queue' => Stancl\Tenancy\TenancyBootstrappers\QueueTenancyBootstrapper::class,
|
'queue' => Stancl\Tenancy\TenancyBootstrappers\QueueTenancyBootstrapper::class,
|
||||||
// 'redis' => Stancl\Tenancy\TenancyBootstrappers\RedisTenancyBootstrapper::class, // Note: phpredis is needed
|
// 'redis' => Stancl\Tenancy\TenancyBootstrappers\RedisTenancyBootstrapper::class, // Note: phpredis is needed
|
||||||
],
|
],
|
||||||
'features' => [
|
|
||||||
// Features are classes that provide additional functionality
|
|
||||||
// not needed for tenancy to be bootstrapped. They are run
|
|
||||||
// regardless of whether tenancy has been initialized.
|
|
||||||
|
|
||||||
// Stancl\Tenancy\Features\Timestamps::class,
|
/**
|
||||||
// Stancl\Tenancy\Features\TenantConfig::class,
|
* Database tenancy config. Used by DatabaseTenancyBootstrapper.
|
||||||
// Stancl\Tenancy\Features\TelescopeTags::class,
|
*/
|
||||||
// Stancl\Tenancy\Features\TenantRedirect::class,
|
'database' => [
|
||||||
|
/**
|
||||||
|
* The connection that will be used as a template for the dynamically created tenant connection.
|
||||||
|
* Set to null to use the default connection.
|
||||||
|
*/
|
||||||
|
'based_on' => null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tenant database names are created like this:
|
||||||
|
* prefix + tenant_id + suffix.
|
||||||
|
*/
|
||||||
|
'prefix' => 'tenant',
|
||||||
|
'suffix' => '',
|
||||||
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redis tenancy config. Used by RedisTenancyBoostrapper.
|
||||||
|
*
|
||||||
|
* Note: You need phpredis to use Redis tenancy.
|
||||||
|
*
|
||||||
|
* Note: You don't need to use this if you're using Redis only for cache.
|
||||||
|
* Redis tenancy is only relevant if you're making direct Redis calls,
|
||||||
|
* either using the Redis facade or by injecting it as a dependency.
|
||||||
|
*/
|
||||||
|
'redis' => [
|
||||||
|
'prefix_base' => 'tenant', // Each key in Redis will be prepended by this prefix_base, followed by the tenant id.
|
||||||
|
'prefixed_connections' => [ // Redis connections whose keys are prefixed, to separate one tenant's keys from another.
|
||||||
|
// 'default',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache tenancy config. Used by CacheTenancyBootstrapper.
|
||||||
|
*
|
||||||
|
* This works for all Cache facade calls, cache() helper
|
||||||
|
* calls and direct calls to injected cache stores.
|
||||||
|
*
|
||||||
|
* Each key in cache will have a tag applied on it. This tag is used to
|
||||||
|
* scope the cache both when writing to it and when reading from it.
|
||||||
|
*/
|
||||||
|
'cache' => [
|
||||||
|
'tag_base' => 'tenant', // This tag_base, followed by the tenant_id, will form a tag that will be applied on each cache call.
|
||||||
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filesystem tenancy config. Used by FilesystemTenancyBootstrapper.
|
||||||
|
* https://tenancy.samuelstancl.me/docs/v2/filesystem-tenancy/.
|
||||||
|
*/
|
||||||
|
'filesystem' => [
|
||||||
|
/**
|
||||||
|
* Each disk listed in the 'disks' array will be suffixed by the suffix_base, followed by the tenant_id.
|
||||||
|
*/
|
||||||
|
'suffix_base' => 'tenant',
|
||||||
|
'disks' => [
|
||||||
|
'local',
|
||||||
|
'public',
|
||||||
|
// 's3',
|
||||||
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this for local disks.
|
||||||
|
*
|
||||||
|
* See https://tenancy.samuelstancl.me/docs/v2/filesystem-tenancy/
|
||||||
|
*/
|
||||||
|
'root_override' => [
|
||||||
|
// Disks whose roots should be overriden after storage_path() is suffixed.
|
||||||
|
'local' => '%storage_path%/app/',
|
||||||
|
'public' => '%storage_path%/app/public/',
|
||||||
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should storage_path() be suffixed.
|
||||||
|
*
|
||||||
|
* Note: Disabling this will likely break local disk tenancy. Only disable this if you're using an external file storage service like S3.
|
||||||
|
*
|
||||||
|
* For the vast majority of applications, this feature should be enabled. But in some
|
||||||
|
* edge cases, it can cause issues (like using Passport with Vapor - see #196), so
|
||||||
|
* you may want to disable this if you are experiencing these edge case issues.
|
||||||
|
*/
|
||||||
|
'suffix_storage_path' => true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By default, asset() calls are made multi-tenant too. You can use global_asset() and mix()
|
||||||
|
* for global, non-tenant-specific assets. However, you might have some issues when using
|
||||||
|
* packages that use asset() calls inside the tenant app. To avoid such issues, you can
|
||||||
|
* disable asset() helper tenancy and explicitly use tenant_asset() calls in places
|
||||||
|
* where you want to use tenant-specific assets (product images, avatars, etc).
|
||||||
|
*/
|
||||||
|
'asset_helper_tenancy' => true,
|
||||||
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TenantDatabaseManagers are classes that handle the creation & deletion of tenant databases.
|
||||||
|
*/
|
||||||
|
'database_managers' => [
|
||||||
|
'sqlite' => Stancl\Tenancy\TenantDatabaseManagers\SQLiteDatabaseManager::class,
|
||||||
|
'mysql' => Stancl\Tenancy\TenantDatabaseManagers\MySQLDatabaseManager::class,
|
||||||
|
'pgsql' => Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLDatabaseManager::class,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable the pgsql manager above, enable the one below, and set the
|
||||||
|
* tenancy.database.separate_by config key to 'schema' if you would
|
||||||
|
* like to separate tenant DBs by schemas rather than databases.
|
||||||
|
*/
|
||||||
|
// 'pgsql' => Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLSchemaManager::class, // Separate by schema instead of database
|
||||||
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connections used by TenantDatabaseManagers. This tells, for example, the
|
||||||
|
* MySQLDatabaseManager to use the mysql connection to create databases.
|
||||||
|
*/
|
||||||
|
'database_manager_connections' => [
|
||||||
|
'sqlite' => 'sqlite',
|
||||||
|
'mysql' => 'mysql',
|
||||||
|
'pgsql' => 'pgsql',
|
||||||
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Features are classes that provide additional functionality
|
||||||
|
* not needed for tenancy to be bootstrapped. They are run
|
||||||
|
* regardless of whether tenancy has been initialized.
|
||||||
|
*
|
||||||
|
* See the documentation page for each class to
|
||||||
|
* understand which ones you want to enable.
|
||||||
|
*/
|
||||||
|
'features' => [
|
||||||
|
// Stancl\Tenancy\Features\Timestamps::class, // https://tenancy.samuelstancl.me/docs/v2/features/timestamps/
|
||||||
|
// Stancl\Tenancy\Features\TenantConfig::class, // https://tenancy.samuelstancl.me/docs/v2/features/tenant-config/
|
||||||
|
// Stancl\Tenancy\Features\TelescopeTags::class, // https://tenancy.samuelstancl.me/docs/v2/telescope/
|
||||||
|
// Stancl\Tenancy\Features\TenantRedirect::class, // https://tenancy.samuelstancl.me/docs/v2/features/tenant-redirect/
|
||||||
],
|
],
|
||||||
'storage_to_config_map' => [ // Used by the TenantConfig feature
|
'storage_to_config_map' => [ // Used by the TenantConfig feature
|
||||||
// 'paypal_api_key' => 'services.paypal.api_key',
|
// 'paypal_api_key' => 'services.paypal.api_key',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The URL to which users will be redirected when they try to acceess a central route on a tenant domain.
|
||||||
|
*/
|
||||||
'home_url' => '/app',
|
'home_url' => '/app',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automatically create a database when creating a tenant.
|
||||||
|
*/
|
||||||
'create_database' => true,
|
'create_database' => true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should tenant databases be created asynchronously in a queued job.
|
||||||
|
*/
|
||||||
'queue_database_creation' => false,
|
'queue_database_creation' => false,
|
||||||
'migrate_after_creation' => false, // run migrations after creating a tenant
|
|
||||||
|
/**
|
||||||
|
* Should tenant migrations be ran after the tenant's database is created.
|
||||||
|
*/
|
||||||
|
'migrate_after_creation' => false,
|
||||||
'migration_parameters' => [
|
'migration_parameters' => [
|
||||||
// '--force' => true, // force database migrations
|
// '--force' => true, // Set this to true to be able to run migrations in production
|
||||||
|
// '--path' => [], // If you need to customize paths to tenant migrations
|
||||||
],
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should tenant databases be automatically seeded after they're created & migrated.
|
||||||
|
*/
|
||||||
'seed_after_migration' => false, // should the seeder run after automatic migration
|
'seed_after_migration' => false, // should the seeder run after automatic migration
|
||||||
'seeder_parameters' => [
|
'seeder_parameters' => [
|
||||||
'--class' => 'DatabaseSeeder', // root seeder class, e.g.: 'DatabaseSeeder'
|
'--class' => 'DatabaseSeeder', // root seeder class, e.g.: 'DatabaseSeeder'
|
||||||
// '--force' => true, // force database seeder
|
// '--force' => true,
|
||||||
],
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should tenant databases be deleted asynchronously in a queued job.
|
||||||
|
*/
|
||||||
'queue_database_deletion' => false,
|
'queue_database_deletion' => false,
|
||||||
'delete_database_after_tenant_deletion' => false, // delete the tenant's database after deleting the tenant
|
|
||||||
|
/**
|
||||||
|
* Automatically delete the tenant's database after the tenant is deleted.
|
||||||
|
*
|
||||||
|
* This will save space but permanently delete data which you might want to keep.
|
||||||
|
*/
|
||||||
|
'delete_database_after_tenant_deletion' => false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If you don't supply an id when creating a tenant, this class will be used to generate a random ID.
|
||||||
|
*/
|
||||||
'unique_id_generator' => Stancl\Tenancy\UniqueIDGenerators\UUIDGenerator::class,
|
'unique_id_generator' => Stancl\Tenancy\UniqueIDGenerators\UUIDGenerator::class,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Middleware pushed to the global middleware stack.
|
||||||
|
*/
|
||||||
'global_middleware' => [
|
'global_middleware' => [
|
||||||
Stancl\Tenancy\Middleware\InitializeTenancy::class,
|
Stancl\Tenancy\Middleware\InitializeTenancy::class,
|
||||||
],
|
],
|
||||||
|
|
|
||||||
2
fulltest
2
fulltest
|
|
@ -4,4 +4,4 @@ set -e
|
||||||
# for development
|
# for development
|
||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
./test "$@"
|
./test "$@"
|
||||||
docker-compose exec test vendor/bin/phpcov merge --clover clover.xml coverage/
|
docker-compose exec -T test vendor/bin/phpcov merge --clover clover.xml coverage/
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,12 @@ class Seed extends SeedCommand
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
foreach (config('tenancy.seeder_parameters') as $parameter => $value) {
|
||||||
|
if (! $this->input->hasParameterOption($parameter)) {
|
||||||
|
$this->input->setOption(ltrim($parameter, '-'), $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (! $this->confirmToProceed()) {
|
if (! $this->confirmToProceed()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -52,12 +58,6 @@ class Seed extends SeedCommand
|
||||||
|
|
||||||
$this->input->setOption('database', $tenant->getConnectionName());
|
$this->input->setOption('database', $tenant->getConnectionName());
|
||||||
|
|
||||||
foreach (config('tenancy.seeder_parameters') as $parameter => $value) {
|
|
||||||
if (! $this->input->hasParameterOption($parameter)) {
|
|
||||||
$this->input->setOption(ltrim($parameter, '-'), $value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$tenant->run(function () {
|
$tenant->run(function () {
|
||||||
// Seed
|
// Seed
|
||||||
parent::handle();
|
parent::handle();
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ namespace Stancl\Tenancy\Features;
|
||||||
use Laravel\Telescope\IncomingEntry;
|
use Laravel\Telescope\IncomingEntry;
|
||||||
use Laravel\Telescope\Telescope;
|
use Laravel\Telescope\Telescope;
|
||||||
use Stancl\Tenancy\Contracts\Feature;
|
use Stancl\Tenancy\Contracts\Feature;
|
||||||
|
use Stancl\Tenancy\Middleware\PreventAccessFromTenantDomains;
|
||||||
use Stancl\Tenancy\TenantManager;
|
use Stancl\Tenancy\TenantManager;
|
||||||
|
|
||||||
class TelescopeTags implements Feature
|
class TelescopeTags implements Feature
|
||||||
|
|
@ -30,7 +31,15 @@ class TelescopeTags implements Feature
|
||||||
Telescope::tag(function (IncomingEntry $entry) {
|
Telescope::tag(function (IncomingEntry $entry) {
|
||||||
$tags = $this->getTags($entry);
|
$tags = $this->getTags($entry);
|
||||||
|
|
||||||
if (in_array('tenancy', optional(request()->route())->middleware() ?? [])) {
|
if (! request()->route()) {
|
||||||
|
return $tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tenantRoute = PreventAccessFromTenantDomains::routeHasMiddleware(request()->route(), 'tenancy')
|
||||||
|
|| PreventAccessFromTenantDomains::routeHasMiddleware(request()->route(), 'universal');
|
||||||
|
|
||||||
|
// Don't do anything if we're visiting a universal route on a central domain
|
||||||
|
if ($tenantRoute && tenancy()->initialized) {
|
||||||
$tags = array_merge($tags, [
|
$tags = array_merge($tags, [
|
||||||
'tenant:' . tenant('id'),
|
'tenant:' . tenant('id'),
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ class InitializeTenancy
|
||||||
try {
|
try {
|
||||||
tenancy()->init($request->getHost());
|
tenancy()->init($request->getHost());
|
||||||
} catch (TenantCouldNotBeIdentifiedException $e) {
|
} catch (TenantCouldNotBeIdentifiedException $e) {
|
||||||
($this->onFail)($e);
|
return ($this->onFail)($e, $request, $next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ class InitializeTenancyByRequestData
|
||||||
try {
|
try {
|
||||||
$this->initializeTenancy($request);
|
$this->initializeTenancy($request);
|
||||||
} catch (TenantCouldNotBeIdentifiedException $e) {
|
} catch (TenantCouldNotBeIdentifiedException $e) {
|
||||||
($this->onFail)($e);
|
return ($this->onFail)($e, $request, $next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,16 @@ use Illuminate\Support\Facades\Route as Router;
|
||||||
*/
|
*/
|
||||||
class PreventAccessFromTenantDomains
|
class PreventAccessFromTenantDomains
|
||||||
{
|
{
|
||||||
|
/** @var callable */
|
||||||
|
protected $central404;
|
||||||
|
|
||||||
|
public function __construct(callable $central404 = null)
|
||||||
|
{
|
||||||
|
$this->central404 = $central404 ?? function () {
|
||||||
|
return 404;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle an incoming request.
|
* Handle an incoming request.
|
||||||
*
|
*
|
||||||
|
|
@ -39,13 +49,13 @@ class PreventAccessFromTenantDomains
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($isExemptDomain && $isTenantRoute) { // accessing tenant routes on web domains
|
if ($isExemptDomain && $isTenantRoute) { // accessing tenant routes on web domains
|
||||||
abort(404);
|
return ($this->central404)($request, $next);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function routeHasMiddleware(Route $route, $middleware): bool
|
public static function routeHasMiddleware(Route $route, $middleware): bool
|
||||||
{
|
{
|
||||||
if (in_array($middleware, $route->middleware(), true)) {
|
if (in_array($middleware, $route->middleware(), true)) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ class CachedTenantResolver
|
||||||
return $this->config->get('tenancy.storage_drivers.db.cache_ttl');
|
return $this->config->get('tenancy.storage_drivers.db.cache_ttl');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTenantIdByDomain(string $domain, Closure $query): string
|
public function getTenantIdByDomain(string $domain, Closure $query): ?string
|
||||||
{
|
{
|
||||||
return $this->cache->remember('_tenancy_domain_to_id:' . $domain, $this->ttl(), $query);
|
return $this->cache->remember('_tenancy_domain_to_id:' . $domain, $this->ttl(), $query);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,8 @@ trait TenantAwareCommand
|
||||||
protected function execute(InputInterface $input, OutputInterface $output)
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
$tenants = $this->getTenants();
|
$tenants = $this->getTenants();
|
||||||
|
|
||||||
if (count($tenants) === 1) {
|
|
||||||
return $tenants[0]->run(function () {
|
|
||||||
return $this->laravel->call([$this, 'handle']);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$exitCode = 0;
|
$exitCode = 0;
|
||||||
|
|
||||||
foreach ($tenants as $tenant) {
|
foreach ($tenants as $tenant) {
|
||||||
$result = (int) $tenant->run(function () {
|
$result = (int) $tenant->run(function () {
|
||||||
return $this->laravel->call([$this, 'handle']);
|
return $this->laravel->call([$this, 'handle']);
|
||||||
|
|
|
||||||
4
test
4
test
|
|
@ -2,6 +2,6 @@
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
printf "Variant 1 (DB)\n\n"
|
printf "Variant 1 (DB)\n\n"
|
||||||
docker-compose exec test env TENANCY_TEST_STORAGE_DRIVER=db vendor/bin/phpunit --coverage-php coverage/1.cov "$@"
|
docker-compose exec -T test env TENANCY_TEST_STORAGE_DRIVER=db vendor/bin/phpunit --coverage-php coverage/1.cov "$@"
|
||||||
printf "Variant 2 (Redis)\n\n"
|
printf "Variant 2 (Redis)\n\n"
|
||||||
docker-compose exec test env TENANCY_TEST_STORAGE_DRIVER=redis vendor/bin/phpunit --coverage-php coverage/2.cov "$@"
|
docker-compose exec -T test env TENANCY_TEST_STORAGE_DRIVER=redis vendor/bin/phpunit --coverage-php coverage/2.cov "$@"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue