Skip to content

Commit 349cf27

Browse files
committed
feat: #273 allow to configure caddy endpoints via api
1 parent 4e1db11 commit 349cf27

File tree

12 files changed

+516
-360
lines changed

12 files changed

+516
-360
lines changed

Diff for: app/Actions/Nodes/RebuildCaddy.php

+2-3
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public function handle(Team $team, NodeTaskGroup $taskGroup)
5959
];
6060

6161
$pathRegexps = [];
62-
foreach ($process->rewriteRules as $rewriteRule) {
62+
foreach ($caddy->rewriteRules as $rewriteRule) {
6363
$pathRegexps[] = [
6464
'find' => $rewriteRule->pathFrom,
6565
'replace' => $rewriteRule->pathTo,
@@ -115,8 +115,7 @@ public function handle(Team $team, NodeTaskGroup $taskGroup)
115115
'handle' => $handlers,
116116
];
117117

118-
// FIXME: Here goes a big "OOPS": redirect rules are repeated for each caddy rule in the process
119-
foreach ($process->redirectRules as $redirectRule) {
118+
foreach ($caddy->redirectRules as $redirectRule) {
120119
$regexpName = dockerize_name($redirectRule->id);
121120

122121
$pathTo = preg_replace("/\\$(\d+)/", "{http.regexp.$regexpName.$1}", $redirectRule->pathTo);

Diff for: app/Api/Controllers/CaddyController.php

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php
2+
3+
namespace App\Api\Controllers;
4+
5+
use App\Actions\Nodes\RebuildCaddy;
6+
use App\Models\DeploymentData\Caddy;
7+
use App\Models\NodeTaskGroup;
8+
use App\Models\NodeTaskGroupType;
9+
use App\Models\Service;
10+
use Illuminate\Http\Request;
11+
use Illuminate\Http\Response;
12+
use Illuminate\Support\Facades\Gate;
13+
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
14+
15+
class CaddyController extends Controller
16+
{
17+
public function store(Service $service, string $process, Request $request): Response
18+
{
19+
Gate::authorize('deploy', $service);
20+
21+
$caddy = Caddy::from($request->all());
22+
23+
$deployment = $service->latestDeployment;
24+
25+
$process = $deployment->data->findProcessByName($process);
26+
if (! $process) {
27+
throw new NotFoundHttpException('Process not found');
28+
}
29+
30+
$process->addCaddy($caddy);
31+
32+
$deployment->save();
33+
34+
$taskGroup = NodeTaskGroup::createForUser($request->user(), $service->team, NodeTaskGroupType::RebuildCaddy);
35+
36+
RebuildCaddy::run($service->team, $taskGroup);
37+
38+
return response()->noContent();
39+
}
40+
41+
public function update(Service $service, string $process, string $id, Request $request): Response
42+
{
43+
Gate::authorize('deploy', $service);
44+
45+
$caddy = Caddy::from([
46+
...$request->all(),
47+
'id' => $id,
48+
]);
49+
50+
$deployment = $service->latestDeployment;
51+
52+
$process = $deployment->data->findProcessByName($process);
53+
if (! $process) {
54+
throw new NotFoundHttpException('Process not found');
55+
}
56+
57+
$process->putCaddyById($id, $caddy);
58+
59+
$deployment->save();
60+
61+
$taskGroup = NodeTaskGroup::createForUser($request->user(), $service->team, NodeTaskGroupType::RebuildCaddy);
62+
63+
RebuildCaddy::run($service->team, $taskGroup);
64+
65+
return response()->noContent();
66+
}
67+
68+
public function destroy(Service $service, string $process, string $id, Request $request): Response
69+
{
70+
Gate::authorize('deploy', $service);
71+
72+
$deployment = $service->latestDeployment;
73+
74+
$process = $deployment->data->findProcessByName($process);
75+
if (! $process) {
76+
throw new NotFoundHttpException('Process not found');
77+
}
78+
79+
$process->removeCaddyById($id);
80+
81+
$deployment->save();
82+
83+
$taskGroup = NodeTaskGroup::createForUser($request->user(), $service->team, NodeTaskGroupType::RebuildCaddy);
84+
85+
RebuildCaddy::run($service->team, $taskGroup);
86+
87+
return response()->noContent();
88+
}
89+
}

Diff for: app/Api/Controllers/ReviewAppsController.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public function store(Service $service, Request $request)
3636
'visit_url' => $reviewApp->visit_url,
3737
],
3838
'deployment' => [
39-
'id' => $reviewApp->deployment->id,
39+
'id' => $reviewApp->latestDeployment->id,
4040
],
4141
];
4242
});

Diff for: app/Models/DeploymentData/Caddy.php

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace App\Models\DeploymentData;
44

55
use App\Util\ResourceId;
6+
use Spatie\LaravelData\Attributes\DataCollectionOf;
67
use Spatie\LaravelData\Attributes\Validation\Between;
78
use Spatie\LaravelData\Attributes\Validation\In;
89
use Spatie\LaravelData\Data;
@@ -19,6 +20,10 @@ public function __construct(
1920
public int $publishedPort,
2021
public string $domain,
2122
public string $path,
23+
#[DataCollectionOf(RewriteRule::class)]
24+
public array $rewriteRules = [],
25+
#[DataCollectionOf(RedirectRule::class)]
26+
public array $redirectRules = [],
2227
) {
2328
$this->id = $id ?? ResourceId::make('caddy');
2429
}

Diff for: app/Models/DeploymentData/Process.php

+39-9
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Exception;
1515
use Illuminate\Validation\ValidationException;
1616
use Spatie\LaravelData\Attributes\DataCollectionOf;
17+
use Spatie\LaravelData\Attributes\Validation\Distinct;
1718
use Spatie\LaravelData\Attributes\Validation\Exists;
1819
use Spatie\LaravelData\Attributes\Validation\RequiredWith;
1920
use Spatie\LaravelData\Attributes\Validation\Rule;
@@ -56,17 +57,11 @@ public function __construct(
5657
#[Rule(new UniqueInArray('targetPort'))]
5758
/* @var NodePort[] */
5859
public array $ports,
59-
#[DataCollectionOf(Caddy::class)]
60+
#[DataCollectionOf(Caddy::class), Distinct('id')]
6061
/* @var Caddy[] */
6162
public array $caddy,
6263
#[Rule(new RequiredIfArrayHas('caddy.*.targetProtocol', 'fastcgi'))]
6364
public ?FastCgi $fastCgi,
64-
#[DataCollectionOf(RedirectRule::class)]
65-
/* @var RedirectRule[] */
66-
public array $redirectRules,
67-
#[DataCollectionOf(RewriteRule::class)]
68-
/* @var RewriteRule[] */
69-
public array $rewriteRules
7065
) {}
7166

7267
public function findVolume(string $id): ?Volume
@@ -89,6 +84,43 @@ public function findSecretVar(string $name): ?SecretVar
8984
return collect($this->secretVars)->first(fn (SecretVar $var) => $var->name === $name);
9085
}
9186

87+
public function findCaddyById(string $id): ?Caddy
88+
{
89+
return collect($this->caddy)->first(fn (Caddy $caddy) => $caddy->id === $id);
90+
}
91+
92+
public function addCaddy(Caddy $caddy): void
93+
{
94+
if ($this->findCaddyById($caddy->id)) {
95+
throw ValidationException::withMessages([
96+
'caddy' => 'Caddy with id '.$caddy->id.' already exists',
97+
]);
98+
}
99+
100+
array_push($this->caddy, $caddy);
101+
}
102+
103+
public function putCaddyById(string $id, Caddy $caddy): void
104+
{
105+
$this->removeCaddyById($id, safe: true);
106+
$this->addCaddy($caddy);
107+
}
108+
109+
public function removeCaddyById(string $id, bool $safe = false): void
110+
{
111+
if (! $this->findCaddyById($id)) {
112+
if ($safe) {
113+
return;
114+
}
115+
116+
throw ValidationException::withMessages([
117+
'caddy' => 'Caddy with id '.$id.' does not exist',
118+
]);
119+
}
120+
121+
$this->caddy = array_filter($this->caddy, fn ($caddy) => $caddy->id !== $id);
122+
}
123+
92124
/**
93125
* @throws Exception
94126
*/
@@ -282,8 +314,6 @@ public static function make(array $attributes): static
282314
'replicas' => 1,
283315
'caddy' => [],
284316
'fastCgi' => null,
285-
'redirectRules' => [],
286-
'rewriteRules' => [],
287317
];
288318

289319
return self::from([

Diff for: app/Models/DeploymentData/Worker.php

+27-15
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
use App\Models\Backup;
66
use App\Models\Deployment;
77
use App\Models\DeploymentData\AppSource\AppSourceType;
8+
use App\Models\DeploymentData\AppSource\DockerSource;
9+
use App\Models\DeploymentData\AppSource\GitWithNixpacksSource;
810
use App\Models\NodeTasks\BuildImageWithDockerfile\BuildImageWithDockerfileMeta;
911
use App\Models\NodeTasks\BuildImageWithNixpacks\BuildImageWithNixpacksMeta;
1012
use App\Models\NodeTasks\DownloadS3File\DownloadS3FileMeta;
@@ -624,29 +626,39 @@ private function makeResourceName(Process $process, string $name): string
624626

625627
public static function make(array $attributes): static
626628
{
627-
$defaults = [
628-
'name' => 'main',
629-
'source' => [
630-
'type' => 'docker_image',
631-
'docker' => [
632-
'registryId' => null,
633-
'image' => '',
634-
],
629+
$source = isset($attributes['source']) ? ($attributes['source']) : ([
630+
'type' => 'docker_image',
631+
'docker' => [
632+
'registryId' => null,
633+
'image' => '',
635634
],
635+
]);
636+
637+
if (isset($source['nixpacks'])) {
638+
if (! isset($source['nixpacks']['nixpacksFilePath'])) {
639+
$source['nixpacks']['nixpacksFilePath'] = 'nixpacks.toml';
640+
}
641+
642+
$source['nixpacks'] = GitWithNixpacksSource::from($source['nixpacks']);
643+
}
644+
645+
if (isset($source['docker'])) {
646+
$source['docker'] = DockerSource::from($source['docker']);
647+
}
648+
649+
return self::from([
650+
...$attributes,
651+
'name' => 'main',
652+
'source' => AppSource::from($source),
636653
'launchMode' => LaunchMode::Daemon->value,
637654
'replicas' => 1,
638655
'command' => null,
639-
'releaseCommand' => ReleaseCommand::from([
656+
'releaseCommand' => ([
640657
'command' => null,
641658
]),
642-
'healthcheck' => Healthcheck::from([
659+
'healthcheck' => ([
643660
'command' => null,
644661
]),
645-
];
646-
647-
return self::from([
648-
...$defaults,
649-
...$attributes,
650662
]);
651663
}
652664

Diff for: app/Models/NodeTaskGroupType.php

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ enum NodeTaskGroupType: int
2525
case LaunchReviewApp = 15;
2626
case DestroyReviewApp = 16;
2727
case InitPtahSh = 17;
28+
case RebuildCaddy = 18;
2829

2930
public function completed(): ?string
3031
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
use Illuminate\Database\Eloquent\Casts\Json;
4+
use Illuminate\Database\Migrations\Migration;
5+
use Illuminate\Support\Facades\DB;
6+
7+
return new class extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*/
12+
public function up(): void
13+
{
14+
DB::table('deployments')->select('id', 'data')->orderBy('id')->chunk(100, function ($deployments) {
15+
foreach ($deployments as $deployment) {
16+
$data = Json::decode($deployment->data);
17+
foreach ($data['processes'] as &$process) {
18+
foreach ($process['caddy'] as &$caddy) {
19+
$caddy['rewriteRules'] = $process['rewriteRules'];
20+
$caddy['redirectRules'] = $process['redirectRules'];
21+
}
22+
}
23+
24+
DB::table('deployments')->where('id', $deployment->id)->update(['data' => $data]);
25+
}
26+
});
27+
}
28+
29+
/**
30+
* Reverse the migrations.
31+
*/
32+
public function down(): void
33+
{
34+
//
35+
}
36+
};

0 commit comments

Comments
 (0)