<?php12namespace Pterodactyl\Models;34use Pterodactyl\Contracts\Models\Identifiable;5use Illuminate\Database\Eloquent\Relations\HasMany;6use Pterodactyl\Models\Traits\HasRealtimeIdentifier;7use Illuminate\Database\Eloquent\Relations\BelongsTo;8use Illuminate\Database\Eloquent\Factories\HasFactory;910/**11* @property int $id12* @property string $uuid13* @property int $nest_id14* @property string $author15* @property string $name16* @property string|null $description17* @property array|null $features18* @property string $docker_image -- deprecated, use $docker_images19* @property array<string, string> $docker_images20* @property string $update_url21* @property bool $force_outgoing_ip22* @property array|null $file_denylist23* @property string|null $config_files24* @property string|null $config_startup25* @property string|null $config_logs26* @property string|null $config_stop27* @property int|null $config_from28* @property string|null $startup29* @property bool $script_is_privileged30* @property string|null $script_install31* @property string $script_entry32* @property string $script_container33* @property int|null $copy_script_from34* @property \Carbon\Carbon $created_at35* @property \Carbon\Carbon $updated_at36* @property string|null $copy_script_install37* @property string $copy_script_entry38* @property string $copy_script_container39* @property string|null $inherit_config_files40* @property string|null $inherit_config_startup41* @property string|null $inherit_config_logs42* @property string|null $inherit_config_stop43* @property string $inherit_file_denylist44* @property array|null $inherit_features45* @property Nest $nest46* @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\Server[] $servers47* @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\EggVariable[] $variables48* @property Egg|null $scriptFrom49* @property Egg|null $configFrom50*/51#[Attributes\Identifiable('eegg')]52class Egg extends Model implements Identifiable53{54/** @use HasFactory<\Database\Factories\EggFactory> */55use HasFactory;56use HasRealtimeIdentifier;5758/**59* The resource name for this model when it is transformed into an60* API representation using fractal.61*/62public const RESOURCE_NAME = 'egg';6364/**65* Defines the current egg export version.66*/67public const EXPORT_VERSION = 'PTDL_v2';6869/**70* Different features that can be enabled on any given egg. These are used internally71* to determine which types of frontend functionality should be shown to the user. Eggs72* will automatically inherit features from a parent egg if they are already configured73* to copy configuration values from said egg.74*75* To skip copying the features, an empty array value should be passed in ("[]") rather76* than leaving it null.77*/78public const FEATURE_EULA_POPUP = 'eula';79public const FEATURE_FASTDL = 'fastdl';8081/**82* The table associated with the model.83*/84protected $table = 'eggs';8586/**87* Fields that are not mass assignable.88*/89protected $fillable = [90'name',91'description',92'features',93'docker_images',94'force_outgoing_ip',95'file_denylist',96'config_files',97'config_startup',98'config_logs',99'config_stop',100'config_from',101'startup',102'script_is_privileged',103'script_install',104'script_entry',105'script_container',106'copy_script_from',107];108109/**110* Cast values to correct type.111*/112protected $casts = [113'nest_id' => 'integer',114'config_from' => 'integer',115'script_is_privileged' => 'boolean',116'force_outgoing_ip' => 'boolean',117'copy_script_from' => 'integer',118'features' => 'array',119'docker_images' => 'array',120'file_denylist' => 'array',121];122123public static array $validationRules = [124'nest_id' => 'required|bail|numeric|exists:nests,id',125'uuid' => 'required|string|size:36',126'name' => 'required|string|max:191',127'description' => 'string|nullable',128'features' => 'array|nullable',129'author' => 'required|string|email',130'file_denylist' => 'array|nullable',131'file_denylist.*' => 'string',132'docker_images' => 'required|array|min:1',133'docker_images.*' => ['required', 'string', 'max:191', 'regex:/^[\w#\.\/\- ]*\|?~?[\w\.\/\-:@ ]*$/'],134'startup' => 'required|nullable|string',135'config_from' => 'sometimes|bail|nullable|numeric|exists:eggs,id',136'config_stop' => 'required_without:config_from|nullable|string|max:191',137'config_startup' => 'required_without:config_from|nullable|json',138'config_logs' => 'required_without:config_from|nullable|json',139'config_files' => 'required_without:config_from|nullable|json',140'update_url' => 'sometimes|nullable|string',141'force_outgoing_ip' => 'sometimes|boolean',142];143144protected $attributes = [145'features' => null,146'file_denylist' => null,147'config_stop' => null,148'config_startup' => null,149'config_logs' => null,150'config_files' => null,151'update_url' => null,152];153154/**155* Returns the install script for the egg; if egg is copying from another156* it will return the copied script.157*/158public function getCopyScriptInstallAttribute(): ?string159{160if (!is_null($this->script_install) || is_null($this->copy_script_from)) {161return $this->script_install;162}163164return $this->scriptFrom->script_install;165}166167/**168* Returns the entry command for the egg; if egg is copying from another169* it will return the copied entry command.170*/171public function getCopyScriptEntryAttribute(): string172{173if (is_null($this->copy_script_from)) {174return $this->script_entry;175}176177return $this->scriptFrom->script_entry;178}179180/**181* Returns the install container for the egg; if egg is copying from another182* it will return the copied install container.183*/184public function getCopyScriptContainerAttribute(): string185{186if (is_null($this->copy_script_from)) {187return $this->script_container;188}189190return $this->scriptFrom->script_container;191}192193/**194* Return the file configuration for an egg.195*/196public function getInheritConfigFilesAttribute(): ?string197{198if (!is_null($this->config_files) || is_null($this->config_from)) {199return $this->config_files;200}201202return $this->configFrom->config_files;203}204205/**206* Return the startup configuration for an egg.207*/208public function getInheritConfigStartupAttribute(): ?string209{210if (!is_null($this->config_startup) || is_null($this->config_from)) {211return $this->config_startup;212}213214return $this->configFrom->config_startup;215}216217/**218* Return the log reading configuration for an egg.219*/220public function getInheritConfigLogsAttribute(): ?string221{222if (!is_null($this->config_logs) || is_null($this->config_from)) {223return $this->config_logs;224}225226return $this->configFrom->config_logs;227}228229/**230* Return the stop command configuration for an egg.231*/232public function getInheritConfigStopAttribute(): ?string233{234if (!is_null($this->config_stop) || is_null($this->config_from)) {235return $this->config_stop;236}237238return $this->configFrom->config_stop;239}240241/**242* Returns the features available to this egg from the parent configuration if there are243* no features defined for this egg specifically and there is a parent egg configured.244*/245public function getInheritFeaturesAttribute(): ?array246{247if (!is_null($this->features) || is_null($this->config_from)) {248return $this->features;249}250251return $this->configFrom->features;252}253254/**255* Returns the features available to this egg from the parent configuration if there are256* no features defined for this egg specifically and there is a parent egg configured.257*/258public function getInheritFileDenylistAttribute(): ?array259{260if (is_null($this->config_from)) {261return $this->file_denylist;262}263264return $this->configFrom->file_denylist;265}266267/**268* Gets nest associated with an egg.269*270* @return \Illuminate\Database\Eloquent\Relations\BelongsTo<\Pterodactyl\Models\Nest, $this>271*/272public function nest(): BelongsTo273{274return $this->belongsTo(Nest::class);275}276277/**278* Gets all servers associated with this egg.279*280* @return \Illuminate\Database\Eloquent\Relations\HasMany<\Pterodactyl\Models\Server, $this>281*/282public function servers(): HasMany283{284return $this->hasMany(Server::class, 'egg_id');285}286287/**288* Gets all variables associated with this egg.289*290* @return \Illuminate\Database\Eloquent\Relations\HasMany<\Pterodactyl\Models\EggVariable, $this>291*/292public function variables(): HasMany293{294return $this->hasMany(EggVariable::class, 'egg_id');295}296297/**298* Get the parent egg from which to copy scripts.299*300* @return \Illuminate\Database\Eloquent\Relations\BelongsTo<self, $this>301*/302public function scriptFrom(): BelongsTo303{304return $this->belongsTo(self::class, 'copy_script_from');305}306307/**308* Get the parent egg from which to copy configuration settings.309*310* @return \Illuminate\Database\Eloquent\Relations\BelongsTo<self, $this>311*/312public function configFrom(): BelongsTo313{314return $this->belongsTo(self::class, 'config_from');315}316}317318319