Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions app/Data/Api/ApiResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

namespace App\Data\Api;

use Carbon\CarbonImmutable;
use Carbon\CarbonInterface;
use Spatie\LaravelData\Data;

/**
* Base class for the API wire format objects that replaced the Fractal transformers.
* Concrete classes are plain property bags hydrated by a static fromModel method,
* which the envelope layer invokes through the container so extra typed parameters
* receive dependency injection. Includes are declared as closures so the envelope
* can resolve them lazily with the request context.
*/
abstract class ApiResource extends Data
{
public const RESPONSE_TIMEZONE = 'UTC';

/**
* Include names a request may ask for on this resource. Only names listed here
* are honored from the query string; unknown names are silently ignored.
*
* @var string[]
*/
public static array $availableIncludes = [];

/**
* Includes that always run, whether requested or not.
*
* @var string[]
*/
public static array $defaultIncludes = [];

/**
* The value of the envelope's object key for this resource.
*/
abstract public static function getResourceName(): string;

/**
* Map of include name to resolver. Each resolver receives the model being
* transformed and an IncludeContext already descended to that include's path,
* and returns an envelope fragment (item, list, or null_resource).
*
* @return array<string, \Closure(mixed, IncludeContext): array<mixed>>
*/
public static function includes(): array
{
return [];
}

/**
* Return an ISO-8601 formatted timestamp to use in the API response.
*/
protected static function formatTimestamp(string $timestamp): string
{
return CarbonImmutable::createFromFormat(CarbonInterface::DEFAULT_TO_STRING_FORMAT, $timestamp)
->setTimezone(self::RESPONSE_TIMEZONE)
->toAtomString();
}
}
61 changes: 61 additions & 0 deletions app/Data/Api/Application/AllocationData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

namespace App\Data\Api\Application;

use App\Data\Api\ApiResource;
use App\Data\Api\IncludeContext;
use App\Models\Allocation;
use App\Models\Node;
use App\Models\Server;

final class AllocationData extends ApiResource
{
/** @var string[] */
public static array $availableIncludes = ['node', 'server'];

public function __construct(
public int $id,
public string $ip,
public ?string $alias,
public int $port,
public ?string $notes,
public bool $assigned,
) {}

public static function getResourceName(): string
{
return Allocation::RESOURCE_NAME;
}

public static function fromModel(Allocation $model): static
{
return new self(
id: $model->id,
ip: $model->ip,
alias: $model->ip_alias,
port: $model->port,
notes: $model->notes,
assigned: !is_null($model->server_id),
);
}

public static function includes(): array
{
return [
'node' => function (Allocation $allocation, IncludeContext $context): array {
if (!$context->allowsAdmin(Node::RESOURCE_NAME)) {
return $context->null();
}

return $context->item($allocation->node, NodeData::class);
},
'server' => function (Allocation $allocation, IncludeContext $context): array {
if (!$context->allowsAdmin(Server::RESOURCE_NAME) || !$allocation->server) {
return $context->null();
}

return $context->item($allocation->server, ServerData::class);
},
];
}
}
70 changes: 70 additions & 0 deletions app/Data/Api/Application/DatabaseHostData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

namespace App\Data\Api\Application;

use App\Data\Api\ApiResource;
use App\Data\Api\IncludeContext;
use App\Models\Database;
use App\Models\DatabaseHost;
use App\Models\Node;

final class DatabaseHostData extends ApiResource
{
/** @var string[] */
public static array $availableIncludes = [
'databases',
'nodes',
];

public function __construct(
public int $id,
public string $name,
public string $host,
public int $port,
public string $username,
public string $created_at,
public string $updated_at,
) {}

public static function getResourceName(): string
{
return DatabaseHost::RESOURCE_NAME;
}

public static function fromModel(DatabaseHost $model): static
{
return new self(
id: $model->id,
name: $model->name,
host: $model->host,
port: $model->port,
username: $model->username,
created_at: $model->created_at->toAtomString(),
updated_at: $model->updated_at->toAtomString(),
);
}

public static function includes(): array
{
return [
'databases' => function (DatabaseHost $host, IncludeContext $context): array {
if (!$context->allowsAdmin(Database::RESOURCE_NAME)) {
return $context->null();
}

$host->loadMissing('databases');

return $context->collection($host->getRelation('databases'), ServerDatabaseData::class);
},
'nodes' => function (DatabaseHost $host, IncludeContext $context): array {
if (!$context->allowsAdmin(Node::RESOURCE_NAME)) {
return $context->null();
}

$host->loadMissing('nodes');

return $context->collection($host->getRelation('nodes'), NodeData::class);
},
];
}
}
112 changes: 112 additions & 0 deletions app/Data/Api/Application/EggData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?php

namespace App\Data\Api\Application;

use App\Data\Api\ApiResource;
use App\Data\Api\IncludeContext;
use App\Models\Egg;
use App\Models\EggVariable;
use App\Models\Server;
use Illuminate\Support\Arr;

final class EggData extends ApiResource
{
/** @var string[] */
public static array $availableIncludes = [
'servers',
'variables',
];

/**
* @param array<string, mixed> $config
* @param array<string, mixed> $script
*/
public function __construct(
public int $id,
public string $uuid,
public string $name,
public ?string $author,
public ?string $description,
public ?string $icon,
public mixed $features,
public mixed $tags,
public string $docker_image,
public mixed $docker_images,
public array $config,
public string $startup,
public mixed $startup_commands,
public array $script,
public string $created_at,
public string $updated_at,
) {}

public static function getResourceName(): string
{
return Egg::RESOURCE_NAME;
}

public static function fromModel(Egg $model): static
{
$model->loadMissing('configFrom');

$files = json_decode($model->inherit_config_files ?: '{}', true, 512, JSON_THROW_ON_ERROR);

$model->loadMissing('scriptFrom');

return new self(
id: $model->id,
uuid: $model->uuid,
name: $model->name,
author: $model->author,
description: $model->description,
icon: $model->icon,
features: $model->features,
tags: $model->tags,
docker_image: Arr::first($model->docker_images, default: ''), // deprecated, use docker_images
docker_images: $model->docker_images,
config: [
'files' => $files,
'startup' => json_decode($model->inherit_config_startup ?: '{}', true),
'stop' => $model->inherit_config_stop,
'logs' => json_decode($model->inherit_config_logs ?: '{}', true),
'file_denylist' => $model->inherit_file_denylist,
'extends' => $model->config_from,
],
startup: Arr::first($model->startup_commands, default: ''), // deprecated, use startup_commands
startup_commands: $model->startup_commands,
script: [
'privileged' => $model->script_is_privileged,
'install' => $model->copy_script_install,
'entry' => $model->copy_script_entry,
'container' => $model->copy_script_container,
'extends' => $model->copy_script_from,
],
created_at: self::formatTimestamp($model->created_at),
updated_at: self::formatTimestamp($model->updated_at),
);
}

public static function includes(): array
{
return [
'servers' => function (Egg $egg, IncludeContext $context): array {
if (!$context->allowsAdmin(Server::RESOURCE_NAME)) {
return $context->null();
}

$egg->loadMissing('servers');

return $context->collection($egg->getRelation('servers'), ServerData::class);
},
'variables' => function (Egg $egg, IncludeContext $context): array {
if (!$context->allowsAdmin(Egg::RESOURCE_NAME)) {
return $context->null();
}

$egg->loadMissing('variables');

return $context->collection($egg->getRelation('variables'), EggVariableData::class, EggVariable::RESOURCE_NAME);
},
];
}
}
33 changes: 33 additions & 0 deletions app/Data/Api/Application/EggVariableData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace App\Data\Api\Application;

use App\Data\Api\ApiResource;
use App\Models\Egg;
use App\Models\EggVariable;

final class EggVariableData extends ApiResource
{
/**
* @param array<string, mixed> $attributes
*/
public function __construct(public array $attributes) {}

public static function getResourceName(): string
{
return Egg::RESOURCE_NAME;
}

public static function fromModel(EggVariable $model): static
{
return new self($model->toArray());
}

/**
* @return array<string, mixed>
*/
public function toArray(): array
{
return $this->attributes;
}
}
Loading
Loading