mirror of
https://github.com/pterodactyl/panel.git
synced 2026-04-13 02:25:38 -05:00
This is a partial implementation to begin moving towards stripe-style identifiers for resources in the system. Any models with an existing `uuid` column can easily be updated to return an identifier in the format of `prfx_xyz` where `prfx` is a four character prefix, and `xyz` is the UUID, encoded using base-32. These are quite easy to use within the API layer because we just need to do one quick transformation to extract the UUID for those models. This PR implements that logic for servers in the `SubstituteClientBindings` logic. A future PR will need to come through and handle identifiers for models that _don't_ currently use UUIDs for reference that we want to expose to clients. In those cases it is easier to just generate base-32 encoded UUID7s that get stored in the database and indexed. They follow the same base approach, but you don't need to do any transformations in the code (other than stripping the prefix, unless we decide to store the prefix). There is also now a `PTERODACTYL_USE_SERVER_IDENTIFIERS` environment variable, that when set to true, updates the front-end and API response to use this new identifier in place of the `uuidShort` value.
74 lines
2.2 KiB
PHP
74 lines
2.2 KiB
PHP
<?php
|
|
|
|
namespace Pterodactyl\Models\Traits;
|
|
|
|
use Ramsey\Uuid\Uuid;
|
|
use Illuminate\Support\Str;
|
|
use Webmozart\Assert\Assert;
|
|
use ParagonIE\ConstantTime\Base32;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Pterodactyl\Models\Attributes\Identifiable;
|
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
|
|
|
/**
|
|
* Support realtime identifiers on models that do not track an "identifier" column in
|
|
* the database. This allows us to make use of the existing data reliant on UUID columns
|
|
* while still allowing for output and querying against a more human readable identifier
|
|
* value.
|
|
*
|
|
* @property-read string $identifier
|
|
*
|
|
* @method static Builder whereIdentifier(string $identifier)
|
|
*
|
|
* @mixin \Illuminate\Database\Eloquent\Model
|
|
*/
|
|
trait HasRealtimeIdentifier
|
|
{
|
|
private static string $identifierPrefix;
|
|
|
|
private static string $identifierDataColumn;
|
|
|
|
protected function identifier(): Attribute
|
|
{
|
|
return Attribute::get(function () {
|
|
$bytes = Uuid::fromString($this->getRawOriginal(static::$identifierDataColumn))->getBytes();
|
|
|
|
return sprintf('%s_%s', static::$identifierPrefix, Base32::encodeUnpadded($bytes));
|
|
});
|
|
}
|
|
|
|
public function scopeWhereIdentifier(Builder $builder, string $identifier): void
|
|
{
|
|
if (!str_starts_with($identifier, $prefix = self::$identifierPrefix . '_')) {
|
|
$builder->whereRaw('0 = 1');
|
|
|
|
return;
|
|
}
|
|
|
|
$bytes = rescue(fn () => Base32::decode(Str::replaceFirst($prefix, '', $identifier)), report: false);
|
|
if (empty($bytes)) {
|
|
$builder->whereRaw('0 = 1');
|
|
|
|
return;
|
|
}
|
|
|
|
$builder->where(self::$identifierDataColumn, Uuid::fromBytes($bytes)->toString());
|
|
}
|
|
|
|
protected static function bootHasRealtimeIdentifier(): void
|
|
{
|
|
$attrs = (new \ReflectionClass(static::class))->getAttributes(Identifiable::class);
|
|
|
|
Assert::count(
|
|
$attrs,
|
|
1,
|
|
'The #[' . Identifiable::class . '] attribute must be set on ' . static::class . ' to use realtime identifiers.'
|
|
);
|
|
|
|
$instance = $attrs[0]->newInstance();
|
|
|
|
self::$identifierPrefix = $instance->prefix;
|
|
self::$identifierDataColumn = $instance->column;
|
|
}
|
|
}
|