mirror of
https://github.com/archtechx/tenancy.git
synced 2025-12-13 00:14:04 +00:00
Merge branch 'master' into resource-syncing-refactor
This commit is contained in:
commit
728d2db321
12 changed files with 99 additions and 23 deletions
|
|
@ -48,6 +48,8 @@ return [
|
|||
* SECURITY NOTE: Keep in mind that autoincrement IDs come with potential enumeration issues (such as tenant storage URLs).
|
||||
*
|
||||
* @see \Stancl\Tenancy\UniqueIdentifierGenerators\UUIDGenerator
|
||||
* @see \Stancl\Tenancy\UniqueIdentifierGenerators\ULIDGenerator
|
||||
* @see \Stancl\Tenancy\UniqueIdentifierGenerators\UUIDv7Generator
|
||||
* @see \Stancl\Tenancy\UniqueIdentifierGenerators\RandomHexGenerator
|
||||
* @see \Stancl\Tenancy\UniqueIdentifierGenerators\RandomIntGenerator
|
||||
* @see \Stancl\Tenancy\UniqueIdentifierGenerators\RandomStringGenerator
|
||||
|
|
@ -311,7 +313,7 @@ return [
|
|||
*
|
||||
* Note: This will implicitly add your configured session store to the list of prefixed stores above.
|
||||
*/
|
||||
'scope_sessions' => true,
|
||||
'scope_sessions' => in_array(env('SESSION_DRIVER'), ['redis', 'memcached', 'dynamodb', 'apc'], true),
|
||||
|
||||
'tag_base' => 'tenant', // This tag_base, followed by the tenant_id, will form a tag that will be applied on each cache call.
|
||||
],
|
||||
|
|
|
|||
|
|
@ -102,14 +102,7 @@ class CacheTenancyBootstrapper implements TenancyBootstrapper
|
|||
if ($this->config->get('tenancy.cache.scope_sessions', true)) {
|
||||
// These are the only cache driven session backends (see Laravel's config/session.php)
|
||||
if (! in_array($this->config->get('session.driver'), ['redis', 'memcached', 'dynamodb', 'apc'], true)) {
|
||||
if (app()->environment('production')) {
|
||||
// We only throw this exception in prod to make configuration a little easier. Developers
|
||||
// may have scope_sessions set to true while using different session drivers e.g. in tests.
|
||||
// Previously we just silently ignored this, however since session scoping is of high importance
|
||||
// in production, we make sure to notify the developer, by throwing an exception, that session
|
||||
// scoping isn't happening as expected/configured due to an incompatible session driver.
|
||||
throw new Exception('Session driver [' . $this->config->get('session.driver') . '] cannot be scoped by tenancy.cache.scope_session');
|
||||
}
|
||||
throw new Exception('Session driver [' . $this->config->get('session.driver') . '] cannot be scoped by tenancy.cache.scope_sessions');
|
||||
} else {
|
||||
// Scoping sessions using this bootstrapper implicitly adds the session store to $names
|
||||
$names[] = $this->getSessionCacheStoreName();
|
||||
|
|
|
|||
|
|
@ -63,13 +63,17 @@ class DatabaseCacheBootstrapper implements TenancyBootstrapper
|
|||
$stores = $this->scopedStoreNames();
|
||||
|
||||
foreach ($stores as $storeName) {
|
||||
$this->originalConnections[$storeName] = $this->config->get("cache.stores.{$storeName}.connection");
|
||||
$this->originalLockConnections[$storeName] = $this->config->get("cache.stores.{$storeName}.lock_connection");
|
||||
$this->originalConnections[$storeName] = $this->config->get("cache.stores.{$storeName}.connection") ?? config('tenancy.database.central_connection');
|
||||
$this->originalLockConnections[$storeName] = $this->config->get("cache.stores.{$storeName}.lock_connection") ?? config('tenancy.database.central_connection');
|
||||
|
||||
$this->config->set("cache.stores.{$storeName}.connection", 'tenant');
|
||||
$this->config->set("cache.stores.{$storeName}.lock_connection", 'tenant');
|
||||
|
||||
$this->cache->purge($storeName);
|
||||
/** @var DatabaseStore $store */
|
||||
$store = $this->cache->store($storeName)->getStore();
|
||||
|
||||
$store->setConnection(DB::connection('tenant'));
|
||||
$store->setLockConnection(DB::connection('tenant'));
|
||||
}
|
||||
|
||||
if (static::$adjustGlobalCacheManager) {
|
||||
|
|
@ -78,8 +82,8 @@ class DatabaseCacheBootstrapper implements TenancyBootstrapper
|
|||
// *from here* being executed repeatedly in a loop on reinitialization. For that reason we do not do that
|
||||
// (this is our only use of $adjustCacheManagerUsing anyway) but ideally at some point we'd have a better solution.
|
||||
$originalConnections = array_combine($stores, array_map(fn (string $storeName) => [
|
||||
'connection' => $this->originalConnections[$storeName] ?? config('tenancy.database.central_connection'),
|
||||
'lockConnection' => $this->originalLockConnections[$storeName] ?? config('tenancy.database.central_connection'),
|
||||
'connection' => $this->originalConnections[$storeName],
|
||||
'lockConnection' => $this->originalLockConnections[$storeName],
|
||||
], $stores));
|
||||
|
||||
TenancyServiceProvider::$adjustCacheManagerUsing = static function (CacheManager $manager) use ($originalConnections) {
|
||||
|
|
@ -100,7 +104,11 @@ class DatabaseCacheBootstrapper implements TenancyBootstrapper
|
|||
$this->config->set("cache.stores.{$storeName}.connection", $originalConnection);
|
||||
$this->config->set("cache.stores.{$storeName}.lock_connection", $this->originalLockConnections[$storeName]);
|
||||
|
||||
$this->cache->purge($storeName);
|
||||
/** @var DatabaseStore $store */
|
||||
$store = $this->cache->store($storeName)->getStore();
|
||||
|
||||
$store->setConnection(DB::connection($this->originalConnections[$storeName]));
|
||||
$store->setLockConnection(DB::connection($this->originalLockConnections[$storeName]));
|
||||
}
|
||||
|
||||
TenancyServiceProvider::$adjustCacheManagerUsing = null;
|
||||
|
|
|
|||
|
|
@ -78,6 +78,15 @@ class FilesystemTenancyBootstrapper implements TenancyBootstrapper
|
|||
return;
|
||||
}
|
||||
|
||||
$path = $suffix
|
||||
? $this->tenantStoragePath($suffix) . '/framework/cache'
|
||||
: $this->originalStoragePath . '/framework/cache';
|
||||
|
||||
if (! is_dir($path)) {
|
||||
// Create tenant framework/cache directory if it does not exist
|
||||
mkdir($path, 0750, true);
|
||||
}
|
||||
|
||||
if ($suffix === false) {
|
||||
$this->app->useStoragePath($this->originalStoragePath);
|
||||
} else {
|
||||
|
|
@ -211,7 +220,7 @@ class FilesystemTenancyBootstrapper implements TenancyBootstrapper
|
|||
|
||||
if (! is_dir($path)) {
|
||||
// Create tenant framework/sessions directory if it does not exist
|
||||
mkdir($path, 0755, true);
|
||||
mkdir($path, 0750, true);
|
||||
}
|
||||
|
||||
$this->app['config']['session.files'] = $path;
|
||||
|
|
|
|||
|
|
@ -4,18 +4,25 @@ declare(strict_types=1);
|
|||
|
||||
namespace Stancl\Tenancy\Listeners;
|
||||
|
||||
use Stancl\Tenancy\Events\TenantCreated;
|
||||
use Stancl\Tenancy\Events\Contracts\TenantEvent;
|
||||
|
||||
/**
|
||||
* Can be used to manually create framework directories in the tenant storage when storage_path() is scoped.
|
||||
*
|
||||
* Useful when using real-time facades which use the framework/cache directory.
|
||||
*
|
||||
* Generally not needed anymore as the directory is also created by the FilesystemTenancyBootstrapper.
|
||||
*/
|
||||
class CreateTenantStorage
|
||||
{
|
||||
public function handle(TenantCreated $event): void
|
||||
public function handle(TenantEvent $event): void
|
||||
{
|
||||
$storage_path = tenancy()->run($event->tenant, fn () => storage_path());
|
||||
$cache_path = "$storage_path/framework/cache";
|
||||
|
||||
if (! is_dir($cache_path)) {
|
||||
// Create the tenant's storage directory and /framework/cache within (used for e.g. real-time facades)
|
||||
mkdir($cache_path, 0777, true);
|
||||
mkdir($cache_path, 0750, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@ declare(strict_types=1);
|
|||
namespace Stancl\Tenancy\Listeners;
|
||||
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Stancl\Tenancy\Events\DeletingTenant;
|
||||
use Stancl\Tenancy\Events\Contracts\TenantEvent;
|
||||
|
||||
class DeleteTenantStorage
|
||||
{
|
||||
public function handle(DeletingTenant $event): void
|
||||
public function handle(TenantEvent $event): void
|
||||
{
|
||||
$path = tenancy()->run($event->tenant, fn () => storage_path());
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use Illuminate\Support\Str;
|
|||
use Stancl\Tenancy\Contracts\UniqueIdentifierGenerator;
|
||||
|
||||
/**
|
||||
* Generates a UUID for the tenant key.
|
||||
* Generates a ULID for the tenant key.
|
||||
*/
|
||||
class ULIDGenerator implements UniqueIdentifierGenerator
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use Ramsey\Uuid\Uuid;
|
|||
use Stancl\Tenancy\Contracts\UniqueIdentifierGenerator;
|
||||
|
||||
/**
|
||||
* Generates a UUID for the tenant key.
|
||||
* Generates a UUIDv4 for the tenant key.
|
||||
*/
|
||||
class UUIDGenerator implements UniqueIdentifierGenerator
|
||||
{
|
||||
|
|
|
|||
20
src/UniqueIdentifierGenerators/UUIDv7Generator.php
Normal file
20
src/UniqueIdentifierGenerators/UUIDv7Generator.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Stancl\Tenancy\UniqueIdentifierGenerators;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Str;
|
||||
use Stancl\Tenancy\Contracts\UniqueIdentifierGenerator;
|
||||
|
||||
/**
|
||||
* Generates a UUIDv7 for the tenant key.
|
||||
*/
|
||||
class UUIDv7Generator implements UniqueIdentifierGenerator
|
||||
{
|
||||
public static function generate(Model $model): string|int
|
||||
{
|
||||
return Str::uuid7()->toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -200,3 +200,24 @@ test('tenant storage can get deleted after the tenant when DeletingTenant listen
|
|||
|
||||
expect(File::isDirectory($tenantStoragePath))->toBeFalse();
|
||||
});
|
||||
|
||||
test('the framework/cache directory is created when storage_path is scoped', function (bool $suffixStoragePath) {
|
||||
config([
|
||||
'tenancy.bootstrappers' => [
|
||||
FilesystemTenancyBootstrapper::class,
|
||||
],
|
||||
'tenancy.filesystem.suffix_storage_path' => $suffixStoragePath
|
||||
]);
|
||||
|
||||
$centralStoragePath = storage_path();
|
||||
|
||||
tenancy()->initialize($tenant = Tenant::create());
|
||||
|
||||
if ($suffixStoragePath) {
|
||||
expect(storage_path('framework/cache'))->toBe($centralStoragePath . "/tenant{$tenant->id}/framework/cache");
|
||||
expect(is_dir($centralStoragePath . "/tenant{$tenant->id}/framework/cache"))->toBeTrue();
|
||||
} else {
|
||||
expect(storage_path('framework/cache'))->toBe($centralStoragePath . '/framework/cache');
|
||||
expect(is_dir($centralStoragePath . "/tenant{$tenant->id}/framework/cache"))->toBeFalse();
|
||||
}
|
||||
})->with([true, false]);
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ test('file sessions are separated', function (bool $scopeSessions) {
|
|||
|
||||
if ($scopeSessions) {
|
||||
expect($sessionPath())->toBe(storage_path('tenant' . $tenant->getTenantKey() . '/framework/sessions'));
|
||||
expect(is_dir(storage_path('tenant' . $tenant->getTenantKey() . '/framework/sessions')))->toBeTrue();
|
||||
} else {
|
||||
expect($sessionPath())->toBe(storage_path('framework/sessions'));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ use Stancl\Tenancy\UniqueIdentifierGenerators\RandomHexGenerator;
|
|||
use Stancl\Tenancy\UniqueIdentifierGenerators\RandomIntGenerator;
|
||||
use Stancl\Tenancy\UniqueIdentifierGenerators\RandomStringGenerator;
|
||||
use Stancl\Tenancy\UniqueIdentifierGenerators\ULIDGenerator;
|
||||
use Stancl\Tenancy\UniqueIdentifierGenerators\UUIDv7Generator;
|
||||
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
|
|
@ -94,6 +95,20 @@ test('ulid ids are supported', function () {
|
|||
expect($tenant2->id > $tenant1->id)->toBeTrue();
|
||||
});
|
||||
|
||||
test('uuidv7 ids are supported', function () {
|
||||
app()->bind(UniqueIdentifierGenerator::class, UUIDv7Generator::class);
|
||||
|
||||
$tenant1 = Tenant::create();
|
||||
expect($tenant1->id)->toBeString();
|
||||
expect(strlen($tenant1->id))->toBe(36);
|
||||
|
||||
$tenant2 = Tenant::create();
|
||||
expect($tenant2->id)->toBeString();
|
||||
expect(strlen($tenant2->id))->toBe(36);
|
||||
|
||||
expect($tenant2->id > $tenant1->id)->toBeTrue();
|
||||
});
|
||||
|
||||
test('hex ids are supported', function () {
|
||||
app()->bind(UniqueIdentifierGenerator::class, RandomHexGenerator::class);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue