feat: implement Change resource with CRUD functionality and migration
update image tags to v0.0.8 in build scripts and docker-compose files
This commit is contained in:
parent
64a7d1d2f4
commit
5b0e55c4b2
20 changed files with 408 additions and 88 deletions
|
|
@ -14,10 +14,10 @@ steps:
|
||||||
- docker pull quay.io/marshyon/share-lt:latest || true
|
- docker pull quay.io/marshyon/share-lt:latest || true
|
||||||
- echo "Building image for testing (amd64 only for CI compatibility)..."
|
- echo "Building image for testing (amd64 only for CI compatibility)..."
|
||||||
- docker build --platform linux/amd64 --cache-from=quay.io/marshyon/share-lt:latest -t share-lt:test .
|
- docker build --platform linux/amd64 --cache-from=quay.io/marshyon/share-lt:latest -t share-lt:test .
|
||||||
- echo "Tagging test image as quay.io/marshyon/share-lt:v0.0.7..."
|
- echo "Tagging test image as quay.io/marshyon/share-lt:v0.0.8..."
|
||||||
- docker tag share-lt:test quay.io/marshyon/share-lt:v0.0.7
|
- docker tag share-lt:test quay.io/marshyon/share-lt:v0.0.8
|
||||||
- echo "Generating SBOM..."
|
- echo "Generating SBOM..."
|
||||||
- docker run --rm -v /var/run/docker.sock:/var/run/docker.sock anchore/syft:latest scan quay.io/marshyon/share-lt:v0.0.7 -o cyclonedx-json > sbom.json
|
- docker run --rm -v /var/run/docker.sock:/var/run/docker.sock anchore/syft:latest scan quay.io/marshyon/share-lt:v0.0.8 -o cyclonedx-json > sbom.json
|
||||||
scan-vulnerabilities:
|
scan-vulnerabilities:
|
||||||
image: aquasec/trivy:0.67.2
|
image: aquasec/trivy:0.67.2
|
||||||
volumes:
|
volumes:
|
||||||
|
|
@ -43,7 +43,7 @@ steps:
|
||||||
repo: quay.io/marshyon/share-lt
|
repo: quay.io/marshyon/share-lt
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
tags:
|
tags:
|
||||||
- v0.0.7
|
- v0.0.8
|
||||||
- latest
|
- latest
|
||||||
username:
|
username:
|
||||||
from_secret: QUAY_USERNAME
|
from_secret: QUAY_USERNAME
|
||||||
|
|
@ -59,6 +59,6 @@ steps:
|
||||||
COSIGN_REGISTRY_PASSWORD:
|
COSIGN_REGISTRY_PASSWORD:
|
||||||
from_secret: QUAY_PASSWORD
|
from_secret: QUAY_PASSWORD
|
||||||
commands:
|
commands:
|
||||||
- cosign attach sbom --sbom sbom.json quay.io/marshyon/share-lt:v0.0.7 || echo "SBOM attach failed"
|
- cosign attach sbom --sbom sbom.json quay.io/marshyon/share-lt:v0.0.8 || echo "SBOM attach failed"
|
||||||
- echo "Done - trivy report saved to workspace for manual review"
|
- echo "Done - trivy report saved to workspace for manual review"
|
||||||
|
|
||||||
|
|
|
||||||
53
app/Filament/Resources/Changes/ChangeResource.php
Normal file
53
app/Filament/Resources/Changes/ChangeResource.php
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Changes;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Changes\Pages\CreateChange;
|
||||||
|
use App\Filament\Resources\Changes\Pages\EditChange;
|
||||||
|
use App\Filament\Resources\Changes\Pages\ListChanges;
|
||||||
|
use App\Filament\Resources\Changes\Schemas\ChangeForm;
|
||||||
|
use App\Filament\Resources\Changes\Tables\ChangesTable;
|
||||||
|
use App\Models\Change as ModelsChange;
|
||||||
|
use BackedEnum;
|
||||||
|
use Filament\Resources\Resource;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
use Filament\Support\Icons\Heroicon;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
|
||||||
|
class ChangeResource extends Resource
|
||||||
|
{
|
||||||
|
protected static ?string $model = ModelsChange::class;
|
||||||
|
|
||||||
|
protected static string|BackedEnum|null $navigationIcon = Heroicon::ArrowUpOnSquareStack;
|
||||||
|
|
||||||
|
public static function getNavigationGroup(): ?string
|
||||||
|
{
|
||||||
|
return 'Settings';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function form(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return ChangeForm::configure($schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function table(Table $table): Table
|
||||||
|
{
|
||||||
|
return ChangesTable::configure($table);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRelations(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getPages(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'index' => ListChanges::route('/'),
|
||||||
|
'create' => CreateChange::route('/create'),
|
||||||
|
'edit' => EditChange::route('/{record}/edit'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
18
app/Filament/Resources/Changes/Pages/CreateChange.php
Normal file
18
app/Filament/Resources/Changes/Pages/CreateChange.php
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Changes\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Changes\ChangeResource;
|
||||||
|
use Filament\Resources\Pages\CreateRecord;
|
||||||
|
|
||||||
|
class CreateChange extends CreateRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = ChangeResource::class;
|
||||||
|
|
||||||
|
protected function mutateFormDataBeforeCreate(array $data): array
|
||||||
|
{
|
||||||
|
$data['user_id'] = auth()->id();
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
16
app/Filament/Resources/Changes/Pages/EditChange.php
Normal file
16
app/Filament/Resources/Changes/Pages/EditChange.php
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Changes\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Changes\ChangeResource;
|
||||||
|
use Filament\Resources\Pages\EditRecord;
|
||||||
|
|
||||||
|
class EditChange extends EditRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = ChangeResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
19
app/Filament/Resources/Changes/Pages/ListChanges.php
Normal file
19
app/Filament/Resources/Changes/Pages/ListChanges.php
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Changes\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Changes\ChangeResource;
|
||||||
|
use Filament\Actions\CreateAction;
|
||||||
|
use Filament\Resources\Pages\ListRecords;
|
||||||
|
|
||||||
|
class ListChanges extends ListRecords
|
||||||
|
{
|
||||||
|
protected static string $resource = ChangeResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
CreateAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
28
app/Filament/Resources/Changes/Schemas/ChangeForm.php
Normal file
28
app/Filament/Resources/Changes/Schemas/ChangeForm.php
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Changes\Schemas;
|
||||||
|
|
||||||
|
use Filament\Forms\Components\Select as FormSelect;
|
||||||
|
use Filament\Forms\Components\Textarea;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
|
||||||
|
class ChangeForm
|
||||||
|
{
|
||||||
|
public static function configure(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
Textarea::make('note')
|
||||||
|
->label('Note')
|
||||||
|
->required(),
|
||||||
|
FormSelect::make('type')
|
||||||
|
->label('Type')
|
||||||
|
->options([
|
||||||
|
'go-live' => 'Go Live',
|
||||||
|
'general' => 'General',
|
||||||
|
])
|
||||||
|
->default('go-live')
|
||||||
|
->required(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
34
app/Filament/Resources/Changes/Tables/ChangesTable.php
Normal file
34
app/Filament/Resources/Changes/Tables/ChangesTable.php
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Changes\Tables;
|
||||||
|
|
||||||
|
use Filament\Actions\BulkActionGroup;
|
||||||
|
use Filament\Actions\DeleteBulkAction;
|
||||||
|
use Filament\Tables\Columns\TextColumn;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
|
||||||
|
class ChangesTable
|
||||||
|
{
|
||||||
|
public static function configure(Table $table): Table
|
||||||
|
{
|
||||||
|
return $table
|
||||||
|
->columns([
|
||||||
|
TextColumn::make('note')
|
||||||
|
->label('Note')
|
||||||
|
->wrap(),
|
||||||
|
TextColumn::make('type')
|
||||||
|
->label('Type'),
|
||||||
|
TextColumn::make('user.name')
|
||||||
|
->label('User'),
|
||||||
|
])
|
||||||
|
->filters([
|
||||||
|
//
|
||||||
|
])
|
||||||
|
->recordActions([])
|
||||||
|
->toolbarActions([
|
||||||
|
BulkActionGroup::make([
|
||||||
|
DeleteBulkAction::make(),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
36
app/Jobs/ProcessChangeUpdate.php
Normal file
36
app/Jobs/ProcessChangeUpdate.php
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Services\ProcessUpdateService;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Foundation\Queue\Queueable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class ProcessChangeUpdate implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
public int $changeId,
|
||||||
|
public string $action,
|
||||||
|
public string $type = 'change_update_'
|
||||||
|
) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*/
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
Log::info("Processing change update: changeId={$this->changeId}, action={$this->action}");
|
||||||
|
(new ProcessUpdateService)->processUpdate($this->changeId, $this->action, $this->type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,16 +2,12 @@
|
||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\Models\Entry;
|
use App\Services\ProcessUpdateService;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Queue\Queueable;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Foundation\Queue\Queueable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Facades\Process;
|
|
||||||
use Symfony\Component\Console\Exception\RuntimeException;
|
|
||||||
|
|
||||||
class ProcessEntryUpdate implements ShouldQueue
|
class ProcessEntryUpdate implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
|
@ -20,11 +16,10 @@ class ProcessEntryUpdate implements ShouldQueue
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public int $entryId,
|
public int $entryId,
|
||||||
public string $action
|
public string $action,
|
||||||
|
public string $type = 'entry_update_'
|
||||||
) {
|
) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
@ -34,70 +29,6 @@ class ProcessEntryUpdate implements ShouldQueue
|
||||||
*/
|
*/
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
|
(new ProcessUpdateService)->processUpdate($this->entryId, $this->action, $this->type);
|
||||||
$subject = env('NATS_SUBJECT', 'entry_update');
|
|
||||||
$appUrl = env('APP_URL', 'http://localhost');
|
|
||||||
|
|
||||||
$incoming = [
|
|
||||||
'id' => $this->entryId,
|
|
||||||
'action' => $this->action,
|
|
||||||
'subject' => $subject,
|
|
||||||
'app_url' => $appUrl,
|
|
||||||
];
|
|
||||||
|
|
||||||
$jsonData = json_encode($incoming, JSON_THROW_ON_ERROR);
|
|
||||||
$tempFile = tempnam(sys_get_temp_dir(), 'entry_update_');
|
|
||||||
file_put_contents($tempFile, $jsonData);
|
|
||||||
|
|
||||||
$scriptPath = env('HANDLE_ENTRY_UPDATES_SCRIPT', base_path('cmd/handle_cms_updates.sh'));
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Log what we're about to execute
|
|
||||||
Log::info("Executing script: {$scriptPath} with action: {$this->action}", [
|
|
||||||
'entry_id' => $this->entryId,
|
|
||||||
'temp_file' => $tempFile,
|
|
||||||
'script_exists' => file_exists($scriptPath),
|
|
||||||
'script_executable' => is_executable($scriptPath),
|
|
||||||
]);
|
|
||||||
|
|
||||||
$result = Process::run([
|
|
||||||
'bash',
|
|
||||||
$scriptPath,
|
|
||||||
$this->action,
|
|
||||||
$tempFile
|
|
||||||
]);
|
|
||||||
|
|
||||||
if ($result->failed()) {
|
|
||||||
$errorDetails = [
|
|
||||||
'exit_code' => $result->exitCode(),
|
|
||||||
'stdout' => $result->output(),
|
|
||||||
'stderr' => $result->errorOutput(),
|
|
||||||
'command' => ['bash', $scriptPath, $this->action, $tempFile],
|
|
||||||
'script_path' => $scriptPath,
|
|
||||||
'script_exists' => file_exists($scriptPath),
|
|
||||||
'temp_file_exists' => file_exists($tempFile),
|
|
||||||
'temp_file_contents' => file_exists($tempFile) ? file_get_contents($tempFile) : 'N/A',
|
|
||||||
];
|
|
||||||
|
|
||||||
Log::error("Script execution failed", $errorDetails);
|
|
||||||
|
|
||||||
throw new RuntimeException(
|
|
||||||
"Script execution failed with exit code {$result->exitCode()}. " .
|
|
||||||
"STDOUT: " . ($result->output() ?: 'empty') . " " .
|
|
||||||
"STDERR: " . ($result->errorOutput() ?: 'empty')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::info("Script executed successfully", [
|
|
||||||
'stdout' => $result->output(),
|
|
||||||
'entry_id' => $this->entryId,
|
|
||||||
'action' => $this->action,
|
|
||||||
]);
|
|
||||||
} finally {
|
|
||||||
// Clean up temp file
|
|
||||||
if (file_exists($tempFile)) {
|
|
||||||
unlink($tempFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
19
app/Models/Change.php
Normal file
19
app/Models/Change.php
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class Change extends Model
|
||||||
|
{
|
||||||
|
protected $fillable = [
|
||||||
|
'note',
|
||||||
|
'user_id',
|
||||||
|
'type',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function user()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,11 +6,9 @@ namespace App\Models;
|
||||||
|
|
||||||
use Filament\Models\Contracts\FilamentUser;
|
use Filament\Models\Contracts\FilamentUser;
|
||||||
use Filament\Panel;
|
use Filament\Panel;
|
||||||
use Illuminate\Container\Attributes\Log;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
use Illuminate\Support\Facades\Log as FacadesLog;
|
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Laravel\Fortify\TwoFactorAuthenticatable;
|
use Laravel\Fortify\TwoFactorAuthenticatable;
|
||||||
use Laravel\Sanctum\HasApiTokens;
|
use Laravel\Sanctum\HasApiTokens;
|
||||||
|
|
@ -18,7 +16,7 @@ use Laravel\Sanctum\HasApiTokens;
|
||||||
class User extends Authenticatable implements FilamentUser
|
class User extends Authenticatable implements FilamentUser
|
||||||
{
|
{
|
||||||
/** @use HasFactory<\Database\Factories\UserFactory> */
|
/** @use HasFactory<\Database\Factories\UserFactory> */
|
||||||
use HasFactory, Notifiable, TwoFactorAuthenticatable, HasApiTokens;
|
use HasApiTokens, HasFactory, Notifiable, TwoFactorAuthenticatable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The attributes that are mass assignable.
|
* The attributes that are mass assignable.
|
||||||
|
|
@ -75,4 +73,9 @@ class User extends Authenticatable implements FilamentUser
|
||||||
{
|
{
|
||||||
return $this->email === config('app.admin_email') || $this->role === 'admin';
|
return $this->email === config('app.admin_email') || $this->role === 'admin';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function changes()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Change::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
49
app/Observers/ChangeObserver.php
Normal file
49
app/Observers/ChangeObserver.php
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Observers;
|
||||||
|
|
||||||
|
use App\Jobs\ProcessChangeUpdate;
|
||||||
|
use App\Models\Change;
|
||||||
|
|
||||||
|
class ChangeObserver
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle the Change "created" event.
|
||||||
|
*/
|
||||||
|
public function created(Change $change): void
|
||||||
|
{
|
||||||
|
ProcessChangeUpdate::dispatch($change->id, 'created');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the Change "updated" event.
|
||||||
|
*/
|
||||||
|
public function updated(Change $change): void
|
||||||
|
{
|
||||||
|
ProcessChangeUpdate::dispatch($change->id, 'updated');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the Change "deleted" event.
|
||||||
|
*/
|
||||||
|
public function deleted(Change $change): void
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the Change "restored" event.
|
||||||
|
*/
|
||||||
|
public function restored(Change $change): void
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the Change "force deletecURLd" event.
|
||||||
|
*/
|
||||||
|
public function forceDeleted(Change $change): void
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,8 @@ namespace App\Providers;
|
||||||
|
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
use App\Observers\EntryObserver;
|
use App\Observers\EntryObserver;
|
||||||
|
use App\Models\Change;
|
||||||
|
use App\Observers\ChangeObserver;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
use Illuminate\Support\Facades\File;
|
use Illuminate\Support\Facades\File;
|
||||||
|
|
||||||
|
|
@ -29,5 +31,6 @@ class AppServiceProvider extends ServiceProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry::observe(EntryObserver::class);
|
Entry::observe(EntryObserver::class);
|
||||||
|
Change::observe(ChangeObserver::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
79
app/Services/ProcessUpdateService.php
Normal file
79
app/Services/ProcessUpdateService.php
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Process;
|
||||||
|
use Symfony\Component\Console\Exception\RuntimeException;
|
||||||
|
|
||||||
|
class ProcessUpdateService
|
||||||
|
{
|
||||||
|
public function processUpdate(int $entryId, string $action, string $type = 'entry_update_'): void
|
||||||
|
{
|
||||||
|
$subject = env('NATS_SUBJECT', 'entry_update');
|
||||||
|
$appUrl = env('APP_URL', 'http://localhost');
|
||||||
|
|
||||||
|
$incoming = [
|
||||||
|
'id' => $entryId,
|
||||||
|
'action' => $action,
|
||||||
|
'subject' => $subject,
|
||||||
|
'app_url' => $appUrl,
|
||||||
|
'type' => $type,
|
||||||
|
];
|
||||||
|
|
||||||
|
$jsonData = json_encode($incoming, JSON_THROW_ON_ERROR);
|
||||||
|
$tempFile = tempnam(sys_get_temp_dir(), $type);
|
||||||
|
file_put_contents($tempFile, $jsonData);
|
||||||
|
|
||||||
|
$scriptPath = env('HANDLE_ENTRY_UPDATES_SCRIPT', base_path('cmd/handle_cms_updates.sh'));
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Log what we're about to execute
|
||||||
|
Log::info("Executing script: {$scriptPath} with action: {$action}", [
|
||||||
|
'entry_id' => $entryId,
|
||||||
|
'temp_file' => $tempFile,
|
||||||
|
'script_exists' => file_exists($scriptPath),
|
||||||
|
'script_executable' => is_executable($scriptPath),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$result = Process::run([
|
||||||
|
'bash',
|
||||||
|
$scriptPath,
|
||||||
|
$action,
|
||||||
|
$tempFile,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($result->failed()) {
|
||||||
|
$errorDetails = [
|
||||||
|
'exit_code' => $result->exitCode(),
|
||||||
|
'stdout' => $result->output(),
|
||||||
|
'stderr' => $result->errorOutput(),
|
||||||
|
'command' => ['bash', $scriptPath, $action, $tempFile],
|
||||||
|
'script_path' => $scriptPath,
|
||||||
|
'script_exists' => file_exists($scriptPath),
|
||||||
|
'temp_file_exists' => file_exists($tempFile),
|
||||||
|
'temp_file_contents' => file_exists($tempFile) ? file_get_contents($tempFile) : 'N/A',
|
||||||
|
];
|
||||||
|
|
||||||
|
Log::error('Script execution failed', $errorDetails);
|
||||||
|
|
||||||
|
throw new RuntimeException(
|
||||||
|
"Script execution failed with exit code {$result->exitCode()}. " .
|
||||||
|
'STDOUT: ' . ($result->output() ?: 'empty') . ' ' .
|
||||||
|
'STDERR: ' . ($result->errorOutput() ?: 'empty')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::info('Script executed successfully', [
|
||||||
|
'stdout' => $result->output(),
|
||||||
|
'entry_id' => $entryId,
|
||||||
|
'action' => $action,
|
||||||
|
]);
|
||||||
|
} finally {
|
||||||
|
// Clean up temp file
|
||||||
|
if (file_exists($tempFile)) {
|
||||||
|
unlink($tempFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
LARAVEL_CONTAINER_NAME="quay.io/marshyon/share-lt"
|
LARAVEL_CONTAINER_NAME="quay.io/marshyon/share-lt"
|
||||||
CONTAINER_LABEL="v0.0.7"
|
CONTAINER_LABEL="v0.0.8"
|
||||||
CACHE="--no-cache"
|
CACHE="--no-cache"
|
||||||
# CACHE=""
|
# CACHE=""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('changes', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('note');
|
||||||
|
$table->integer('user_id');
|
||||||
|
$table->string('type')->default('general');
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('changes');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -30,7 +30,9 @@ fi
|
||||||
nats \
|
nats \
|
||||||
stream add "$NATS_STREAM" \
|
stream add "$NATS_STREAM" \
|
||||||
--user ruser --password $NATS_PASSWORD \
|
--user ruser --password $NATS_PASSWORD \
|
||||||
--subjects "$NATS_SUBJECT" \
|
--subjects NATS_SUBJECT1 \
|
||||||
|
--subjects NATS_SUBJECT2 \
|
||||||
|
--subjects NATS_SUBJECT3 \
|
||||||
--storage file \
|
--storage file \
|
||||||
--dupe-window="2m" \
|
--dupe-window="2m" \
|
||||||
--replicas=1 \
|
--replicas=1 \
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ services:
|
||||||
# - nats
|
# - nats
|
||||||
|
|
||||||
app:
|
app:
|
||||||
image: quay.io/marshyon/share-lt:v0.0.7
|
image: quay.io/marshyon/share-lt:v0.0.8
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
tty: false
|
tty: false
|
||||||
working_dir: /var/www
|
working_dir: /var/www
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
image: quay.io/marshyon/share-lt:v0.0.7
|
image: quay.io/marshyon/share-lt:v0.0.8
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
tty: false
|
tty: false
|
||||||
working_dir: /var/www
|
working_dir: /var/www
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue