fixed migrations, changed settings to global settings, changed image list to have a "delete all" button instead of "create", fixed printing, added imagick for printing.
This commit is contained in:
@@ -124,6 +124,21 @@ class ComfyUi implements ApiPluginInterface
|
||||
$modelParams = $style->aiModel->parameters ?? [];
|
||||
$styleParams = $style->parameters ?? [];
|
||||
|
||||
// Ensure both parameters are arrays, decode JSON if needed
|
||||
if (is_string($modelParams)) {
|
||||
$modelParams = json_decode($modelParams, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
$modelParams = [];
|
||||
}
|
||||
}
|
||||
|
||||
if (is_string($styleParams)) {
|
||||
$styleParams = json_decode($styleParams, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
$styleParams = [];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($modelParams) && empty($styleParams)) {
|
||||
throw new \Exception('ComfyUI workflow (parameters) is missing.');
|
||||
}
|
||||
|
||||
37
app/Console/Commands/GetPrinterSetting.php
Normal file
37
app/Console/Commands/GetPrinterSetting.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class GetPrinterSetting extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'setting:get-printer';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Get the raw value of the \'selected_printer\' setting.';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$setting = \App\Models\Setting::where('key', 'selected_printer')->first();
|
||||
|
||||
if ($setting) {
|
||||
$this->info('Raw value of \'selected_printer\' setting:');
|
||||
$this->info(json_encode($setting->value, JSON_PRETTY_PRINT)); // Assuming \'value\' column holds the data
|
||||
} else {
|
||||
$this->info('\'selected_printer\' setting not found.');
|
||||
}
|
||||
}
|
||||
}
|
||||
96
app/Filament/Pages/GlobalSettings.php
Normal file
96
app/Filament/Pages/GlobalSettings.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Pages;
|
||||
|
||||
use App\Models\Setting;
|
||||
use App\Services\PrinterService;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Toggle;
|
||||
use Filament\Forms\Concerns\InteractsWithForms;
|
||||
use Filament\Forms\Contracts\HasForms;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Pages\Page;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class GlobalSettings extends Page implements HasForms
|
||||
{
|
||||
use InteractsWithForms;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-cog';
|
||||
protected static string $view = 'filament.pages.global-settings';
|
||||
protected static ?string $navigationGroup = 'Admin';
|
||||
|
||||
public ?array $data = [];
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$settings = Setting::all()->pluck('value', 'key')->toArray();
|
||||
$this->form->fill($settings);
|
||||
}
|
||||
|
||||
public function form(Form $form): Form
|
||||
{
|
||||
$printerService = new PrinterService();
|
||||
$printers = $printerService->getPrinters();
|
||||
$printerOptions = array_merge($printers, ['__custom__' => __('filament.resource.setting.form.custom_printer')]);
|
||||
|
||||
return $form
|
||||
->schema([
|
||||
TextInput::make('gallery_heading')
|
||||
->label(__('filament.resource.setting.form.gallery_heading'))
|
||||
->required(),
|
||||
TextInput::make('new_image_timespan_minutes')
|
||||
->label(__('filament.resource.setting.form.new_image_timespan_minutes'))
|
||||
->numeric()
|
||||
->required(),
|
||||
TextInput::make('image_refresh_interval')
|
||||
->label(__('filament.resource.setting.form.image_refresh_interval'))
|
||||
->numeric()
|
||||
->required(),
|
||||
TextInput::make('max_number_of_copies')
|
||||
->label(__('filament.resource.setting.form.max_number_of_copies'))
|
||||
->numeric()
|
||||
->required(),
|
||||
Toggle::make('show_print_button')
|
||||
->label(__('filament.resource.setting.form.show_print_button')),
|
||||
Select::make('selected_printer')
|
||||
->label(__('filament.resource.setting.form.printer'))
|
||||
->options($printerOptions)
|
||||
->reactive(),
|
||||
TextInput::make('custom_printer_address')
|
||||
->label(__('filament.resource.setting.form.custom_printer_address'))
|
||||
->visible(fn ($get) => $get('selected_printer') === '__custom__'),
|
||||
])
|
||||
->statePath('data');
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$data = $this->form->getState();
|
||||
|
||||
// if a non-custom printer is selected, clear the custom address
|
||||
if (Arr::get($data, 'selected_printer') !== '__custom__') {
|
||||
$data['custom_printer_address'] = '';
|
||||
}
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
Setting::where('key', $key)->update(['value' => $value]);
|
||||
}
|
||||
|
||||
Notification::make()
|
||||
->title(__('settings.saved_successfully'))
|
||||
->success()
|
||||
->send();
|
||||
}
|
||||
|
||||
protected function getFormActions(): array
|
||||
{
|
||||
return [
|
||||
\Filament\Actions\Action::make('save')
|
||||
->label(__('settings.save_button'))
|
||||
->submit('save'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -3,21 +3,13 @@
|
||||
namespace App\Filament\Pages;
|
||||
|
||||
use Filament\Pages\Page;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Filament\Forms\Contracts\HasForms;
|
||||
use Filament\Forms\Concerns\InteractsWithForms;
|
||||
use App\Models\Plugin;
|
||||
|
||||
class Plugins extends Page implements Tables\Contracts\HasTable, HasForms
|
||||
class Plugins extends Page
|
||||
{
|
||||
use Tables\Concerns\InteractsWithTable;
|
||||
use InteractsWithForms;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-puzzle-piece';
|
||||
|
||||
protected static ?string $navigationGroup = 'Plugins';
|
||||
protected static ?string $navigationGroup = 'Plugins';
|
||||
|
||||
protected static ?string $navigationLabel = 'Plugin List';
|
||||
|
||||
@@ -27,31 +19,7 @@ class Plugins extends Page implements Tables\Contracts\HasTable, HasForms
|
||||
|
||||
protected static ?string $slug = 'list-plugins';
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->query(Plugin::query()) // Use a dummy query
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('name')
|
||||
->label('Name')
|
||||
->searchable()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('identifier')
|
||||
->label('Identifier'),
|
||||
Tables\Columns\IconColumn::make('enabled')
|
||||
->label('Enabled')
|
||||
->boolean(),
|
||||
Tables\Columns\IconColumn::make('configured')
|
||||
->label('Configured')
|
||||
->boolean()
|
||||
->tooltip(fn ($record) => $record->configured ? 'Has ApiProvider record' : 'No ApiProvider record'),
|
||||
Tables\Columns\TextColumn::make('file_path')
|
||||
->label('File Path')
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
]);
|
||||
}
|
||||
|
||||
public $plugins;
|
||||
public $plugins;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
@@ -67,9 +35,4 @@ class Plugins extends Page implements Tables\Contracts\HasTable, HasForms
|
||||
{
|
||||
return __('filament.navigation.plugin_list');
|
||||
}
|
||||
|
||||
public function currentlyValidatingForm(\Filament\Forms\ComponentContainer|null $form): void
|
||||
{
|
||||
// No form validation needed for this page
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,11 +32,11 @@ class AiModelResource extends Resource
|
||||
->schema([
|
||||
Forms\Components\Section::make()
|
||||
->schema([
|
||||
Select::make('api_provider_id')
|
||||
->label(__('filament.resource.ai_model.form.api_provider'))
|
||||
Select::make('apiProviders')
|
||||
->label(__('filament.resource.ai_model.form.api_providers'))
|
||||
->relationship('apiProviders', 'name')
|
||||
->multiple()
|
||||
->live()
|
||||
->nullable()
|
||||
->afterStateUpdated(function (callable $set) {
|
||||
$set('model_search_result', null);
|
||||
}),
|
||||
@@ -44,20 +44,24 @@ class AiModelResource extends Resource
|
||||
->label(__('filament.resource.ai_model.form.search_model'))
|
||||
->searchable()
|
||||
->live()
|
||||
->hidden(fn (callable $get) => !static::canSearchModels($get('api_provider_id')))
|
||||
->hidden(fn (callable $get) => !static::canSearchModelsWithAnyProvider($get('apiProviders')))
|
||||
->getSearchResultsUsing(function (string $search, callable $get) {
|
||||
$apiProviderId = $get('api_provider_id');
|
||||
if (!$apiProviderId) {
|
||||
$apiProviderIds = $get('apiProviders');
|
||||
if (empty($apiProviderIds)) {
|
||||
return [];
|
||||
}
|
||||
$pluginInstance = static::getPluginInstance($apiProviderId);
|
||||
if ($pluginInstance && method_exists($pluginInstance, 'searchModels')) {
|
||||
$models = $pluginInstance->searchModels($search);
|
||||
$options = [];
|
||||
foreach ($models as $model) {
|
||||
$options[json_encode(['name' => $model['name'], 'id' => $model['id'], 'type' => $model['type'] ?? null])] = $model['name'] . ' (' . $model['id'] . ')';
|
||||
|
||||
// Try each API provider until we find one that works
|
||||
foreach ($apiProviderIds as $apiProviderId) {
|
||||
$pluginInstance = static::getPluginInstance($apiProviderId);
|
||||
if ($pluginInstance && method_exists($pluginInstance, 'searchModels')) {
|
||||
$models = $pluginInstance->searchModels($search);
|
||||
$options = [];
|
||||
foreach ($models as $model) {
|
||||
$options[json_encode(['name' => $model['name'], 'id' => $model['id'], 'type' => $model['type'] ?? null, 'api_provider_id' => $apiProviderId])] = $model['name'] . ' (' . $model['id'] . ')';
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
return [];
|
||||
})
|
||||
@@ -103,22 +107,28 @@ class AiModelResource extends Resource
|
||||
]);
|
||||
}
|
||||
|
||||
protected static function canSearchModels(?int $apiProviderId): bool
|
||||
protected static function canSearchModelsWithAnyProvider(?array $apiProviderIds): bool
|
||||
{
|
||||
if (!$apiProviderId) {
|
||||
if (empty($apiProviderIds)) {
|
||||
return false;
|
||||
}
|
||||
$apiProvider = ApiProvider::find($apiProviderId);
|
||||
if (!$apiProvider || !$apiProvider->plugin) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
$pluginInstance = PluginLoader::getPlugin($apiProvider->plugin, $apiProvider);
|
||||
return method_exists($pluginInstance, 'searchModels');
|
||||
} catch (\Exception $e) {
|
||||
// Log the exception if needed
|
||||
return false;
|
||||
|
||||
foreach ($apiProviderIds as $apiProviderId) {
|
||||
$apiProvider = ApiProvider::find($apiProviderId);
|
||||
if (!$apiProvider || !$apiProvider->plugin) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
$pluginInstance = PluginLoader::getPlugin($apiProvider->plugin, $apiProvider);
|
||||
if (method_exists($pluginInstance, 'searchModels')) {
|
||||
return true;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// Log the exception if needed
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static function getPluginInstance(?int $apiProviderId): ?ApiPluginInterface
|
||||
@@ -138,6 +148,24 @@ class AiModelResource extends Resource
|
||||
}
|
||||
}
|
||||
|
||||
protected static function canSearchModels(?int $apiProviderId): bool
|
||||
{
|
||||
if (!$apiProviderId) {
|
||||
return false;
|
||||
}
|
||||
$apiProvider = ApiProvider::find($apiProviderId);
|
||||
if (!$apiProvider || !$apiProvider->plugin) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
$pluginInstance = PluginLoader::getPlugin($apiProvider->plugin, $apiProvider);
|
||||
return method_exists($pluginInstance, 'searchModels');
|
||||
} catch (\Exception $e) {
|
||||
// Log the exception if needed
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
@@ -148,7 +176,7 @@ class AiModelResource extends Resource
|
||||
Tables\Columns\IconColumn::make('enabled')
|
||||
->label(__('filament.resource.ai_model.table.enabled'))
|
||||
->boolean(),
|
||||
TextColumn::make('apiProviders.name')->label(__('filament.resource.ai_model.table.api_providers'))->searchable()->sortable(),
|
||||
TextColumn::make('apiProviders.name')->label(__('filament.resource.ai_model.table.api_providers'))->searchable()->sortable()->limit(50),
|
||||
])
|
||||
->filters([
|
||||
//
|
||||
|
||||
@@ -54,9 +54,6 @@ class ImageResource extends Resource
|
||||
Tables\Actions\BulkActionGroup::make([
|
||||
Tables\Actions\DeleteBulkAction::make(),
|
||||
]),
|
||||
])
|
||||
->emptyStateActions([
|
||||
Tables\Actions\CreateAction::make(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -71,8 +68,7 @@ class ImageResource extends Resource
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListImages::route('/'),
|
||||
'create' => Pages\CreateImage::route('/create'),
|
||||
'edit' => Pages\EditImage::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\ImageResource\Pages;
|
||||
|
||||
use App\Filament\Resources\ImageResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateImage extends CreateRecord
|
||||
{
|
||||
protected static string $resource = ImageResource::class;
|
||||
|
||||
protected function getRedirectUrl(): string
|
||||
{
|
||||
return $this->getResource()::getUrl('index');
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,8 @@ use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
use Illuminate\Contracts\Pagination\Paginator;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Filament\Notifications\Notification;
|
||||
|
||||
class ListImages extends ListRecords
|
||||
{
|
||||
@@ -15,7 +17,25 @@ class ListImages extends ListRecords
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make(),
|
||||
Actions\Action::make('delete_all')
|
||||
->label('Delete All Images')
|
||||
->icon('heroicon-o-trash')
|
||||
->color('danger')
|
||||
->requiresConfirmation()
|
||||
->action(function () {
|
||||
// Delete all images from storage
|
||||
Storage::disk('public')->deleteDirectory('images');
|
||||
Storage::disk('public')->makeDirectory('images');
|
||||
|
||||
// Show success notification
|
||||
Notification::make()
|
||||
->title('All images deleted successfully')
|
||||
->success()
|
||||
->send();
|
||||
|
||||
// Refresh the page
|
||||
$this->redirect(static::getUrl());
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources;
|
||||
|
||||
use App\Filament\Resources\SettingResource\Pages;
|
||||
use App\Filament\Resources\SettingResource\RelationManagers;
|
||||
use App\Models\Setting;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Fieldset;
|
||||
|
||||
class SettingResource extends Resource
|
||||
{
|
||||
protected static ?string $model = Setting::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-cog';
|
||||
|
||||
protected static ?string $navigationGroup = 'Einstellungen';
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Forms\Components\TextInput::make('key')
|
||||
->label(__('Key'))
|
||||
->required()
|
||||
->maxLength(255)
|
||||
->hiddenOn('edit'),
|
||||
Forms\Components\Fieldset::make()
|
||||
->label(fn (?Setting $record) => $record ? __('filament.resource.setting.form.' . $record->key) : __('New Setting'))
|
||||
->schema([
|
||||
TextInput::make('value')
|
||||
->label(__('Bitte Wert eingeben'))
|
||||
->disableLabel(fn (?Setting $record) => $record && ($record->key !== 'image_refresh_interval' && $record->key !== 'new_image_timespan_minutes' && $record->key !== 'gallery_heading'))
|
||||
->formatStateUsing(function (?string $state, ?Setting $record) {
|
||||
if ($record && $record->key === 'image_refresh_interval') {
|
||||
return (int)$state / 1000;
|
||||
} else if ($record && $record->key === 'new_image_timespan_minutes') {
|
||||
return (int)$state;
|
||||
}
|
||||
return $state;
|
||||
})
|
||||
->dehydrateStateUsing(function (?string $state, ?Setting $record) {
|
||||
if ($record && $record->key === 'image_refresh_interval') {
|
||||
return (int)$state * 1000;
|
||||
} else if ($record && $record->key === 'new_image_timespan_minutes') {
|
||||
return (int)$state;
|
||||
}
|
||||
return $state;
|
||||
})
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('key')->label(__('Key'))->searchable()->sortable(),
|
||||
Tables\Columns\TextColumn::make('value')->label(__('Value'))->searchable()->sortable(),
|
||||
])
|
||||
->filters([
|
||||
//
|
||||
])
|
||||
->actions([
|
||||
Tables\Actions\EditAction::make(),
|
||||
])
|
||||
|
||||
->bulkActions([
|
||||
Tables\Actions\BulkActionGroup::make([
|
||||
Tables\Actions\DeleteBulkAction::make(),
|
||||
]),
|
||||
])
|
||||
->emptyStateActions([
|
||||
Tables\Actions\CreateAction::make(),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListSettings::route('/'),
|
||||
'create' => Pages\CreateSetting::route('/create'),
|
||||
'edit' => Pages\EditSetting::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
|
||||
public static function getNavigationGroup(): ?string
|
||||
{
|
||||
return __('filament.navigation.groups.settings');
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\SettingResource\Pages;
|
||||
|
||||
use App\Filament\Resources\SettingResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateSetting extends CreateRecord
|
||||
{
|
||||
protected static string $resource = SettingResource::class;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\SettingResource\Pages;
|
||||
|
||||
use App\Filament\Resources\SettingResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditSetting extends EditRecord
|
||||
{
|
||||
protected static string $resource = SettingResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\DeleteAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\SettingResource\Pages;
|
||||
|
||||
use App\Filament\Resources\SettingResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListSettings extends ListRecords
|
||||
{
|
||||
protected static string $resource = SettingResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -109,11 +109,22 @@ class ImageController extends Controller
|
||||
|
||||
public function styleChangeRequest(Request $request)
|
||||
{
|
||||
// Log the incoming request for debugging
|
||||
\Illuminate\Support\Facades\Log::info('styleChangeRequest called', [
|
||||
'image_id' => $request->image_id,
|
||||
'style_id' => $request->style_id,
|
||||
'all_params' => $request->all()
|
||||
]);
|
||||
|
||||
// Same-origin check
|
||||
$appUrl = config('app.url');
|
||||
$referer = $request->headers->get('referer');
|
||||
|
||||
if ($referer && parse_url($referer, PHP_URL_HOST) !== parse_url($appUrl, PHP_URL_HOST)) {
|
||||
\Illuminate\Support\Facades\Log::warning('Unauthorized styleChangeRequest', [
|
||||
'referer' => $referer,
|
||||
'app_url' => $appUrl
|
||||
]);
|
||||
return response()->json(['error' => 'Unauthorized: Request must originate from the same domain.'], 403);
|
||||
}
|
||||
|
||||
@@ -127,7 +138,7 @@ class ImageController extends Controller
|
||||
|
||||
if ($request->style_id) {
|
||||
$style = Style::with(['aiModel' => function ($query) {
|
||||
$query->where('enabled', true)->with(['apiProviders' => function ($query) {
|
||||
$query->where('enabled', true)->with(['primaryApiProvider', 'apiProviders' => function ($query) {
|
||||
$query->where('enabled', true);
|
||||
}]);
|
||||
}])->find($request->style_id);
|
||||
@@ -136,7 +147,7 @@ class ImageController extends Controller
|
||||
$defaultStyleSetting = \App\Models\Setting::where('key', 'default_style_id')->first();
|
||||
if ($defaultStyleSetting && $defaultStyleSetting->value) {
|
||||
$style = Style::with(['aiModel' => function ($query) {
|
||||
$query->where('enabled', true)->with(['apiProviders' => function ($query) {
|
||||
$query->where('enabled', true)->with(['primaryApiProvider', 'apiProviders' => function ($query) {
|
||||
$query->where('enabled', true);
|
||||
}]);
|
||||
}])->find($defaultStyleSetting->value);
|
||||
@@ -144,14 +155,38 @@ class ImageController extends Controller
|
||||
}
|
||||
|
||||
if (!$style || !$style->aiModel || $style->aiModel->apiProviders->isEmpty()) {
|
||||
\Illuminate\Support\Facades\Log::warning('Style or provider not found', [
|
||||
'style' => $style ? $style->toArray() : null,
|
||||
'ai_model' => $style && $style->aiModel ? $style->aiModel->toArray() : null
|
||||
]);
|
||||
return response()->json(['error' => __('api.style_or_provider_not_found')], 404);
|
||||
}
|
||||
|
||||
try {
|
||||
$apiProvider = $style->aiModel->apiProviders->first(); // Get the first enabled API provider
|
||||
// Use the primary API provider for this AI model if available
|
||||
$apiProvider = $style->aiModel->primaryApiProvider;
|
||||
if (!$apiProvider) {
|
||||
// Fallback to the first enabled API provider from the many-to-many relationship
|
||||
$apiProvider = $style->aiModel->apiProviders->where('enabled', true)->first();
|
||||
}
|
||||
if (!$apiProvider) {
|
||||
// If no enabled provider found, try any provider
|
||||
$apiProvider = $style->aiModel->apiProviders->first();
|
||||
}
|
||||
if (!$apiProvider) {
|
||||
\Illuminate\Support\Facades\Log::error('No API provider found for style', [
|
||||
'style_id' => $style->id,
|
||||
'ai_model_id' => $style->aiModel->id
|
||||
]);
|
||||
return response()->json(['error' => __('api.style_or_provider_not_found')], 404);
|
||||
}
|
||||
|
||||
\Illuminate\Support\Facades\Log::info('Selected API provider for style change', [
|
||||
'api_provider_id' => $apiProvider->id,
|
||||
'api_provider_name' => $apiProvider->name,
|
||||
'plugin' => $apiProvider->plugin
|
||||
]);
|
||||
|
||||
$plugin = PluginLoader::getPlugin($apiProvider->plugin, $apiProvider);
|
||||
|
||||
$result = $plugin->processImageStyleChange($image, $style);
|
||||
@@ -162,12 +197,22 @@ class ImageController extends Controller
|
||||
$image->save();
|
||||
|
||||
// Return the prompt_id for WebSocket tracking
|
||||
\Illuminate\Support\Facades\Log::info('Style change request completed', [
|
||||
'prompt_id' => $result['prompt_id'],
|
||||
'image_uuid' => $image->uuid
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Style change request sent.',
|
||||
'prompt_id' => $result['prompt_id'],
|
||||
'image_uuid' => $image->uuid, // Pass image UUID for frontend tracking
|
||||
'plugin' => $apiProvider->plugin, // Pass plugin name for frontend handling
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
\Illuminate\Support\Facades\Log::error('Error in styleChangeRequest', [
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
return response()->json(['error' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
@@ -230,8 +275,10 @@ class ImageController extends Controller
|
||||
Log::info('fetchStyledImage called.', ['prompt_id' => $promptId]);
|
||||
try {
|
||||
// Find the image associated with the prompt_id, eagerly loading relationships
|
||||
$image = Image::with(['style.aiModel.apiProviders' => function ($query) {
|
||||
$query->where('enabled', true);
|
||||
$image = Image::with(['style.aiModel' => function ($query) {
|
||||
$query->with(['primaryApiProvider', 'apiProviders' => function ($query) {
|
||||
$query->where('enabled', true);
|
||||
}]);
|
||||
}])->where('comfyui_prompt_id', $promptId)->first();
|
||||
|
||||
if (!$image) {
|
||||
@@ -259,7 +306,16 @@ class ImageController extends Controller
|
||||
Log::warning('fetchStyledImage: No enabled API Providers found for AI Model.', ['ai_model_id' => $style->aiModel->id]);
|
||||
return response()->json(['error' => __('api.style_or_provider_not_found')], 404);
|
||||
}
|
||||
$apiProvider = $style->aiModel->apiProviders->first();
|
||||
// Use the primary API provider for this AI model if available
|
||||
$apiProvider = $style->aiModel->primaryApiProvider;
|
||||
if (!$apiProvider) {
|
||||
// Fallback to the first enabled API provider from the many-to-many relationship
|
||||
$apiProvider = $style->aiModel->apiProviders->where('enabled', true)->first();
|
||||
}
|
||||
if (!$apiProvider) {
|
||||
// If no enabled provider found, try any provider
|
||||
$apiProvider = $style->aiModel->apiProviders->first();
|
||||
}
|
||||
Log::info('fetchStyledImage: API Provider found.', ['api_provider_id' => $apiProvider->id, 'api_provider_name' => $apiProvider->name]);
|
||||
|
||||
Log::info('Fetching base64 image from plugin.', ['prompt_id' => $promptId, 'api_provider' => $apiProvider->name]);
|
||||
@@ -310,9 +366,85 @@ class ImageController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
public function getComfyUiUrl()
|
||||
public function getComfyUiUrl(Request $request)
|
||||
{
|
||||
$apiProvider = ApiProvider::where('plugin', 'comfyui')->where('enabled', true)->first();
|
||||
$styleId = $request->query('style_id');
|
||||
$imageUuid = $request->query('image_uuid');
|
||||
|
||||
$apiProvider = null;
|
||||
|
||||
// If style_id is provided, get the API provider for that style
|
||||
if ($styleId) {
|
||||
$style = Style::with(['aiModel' => function ($query) {
|
||||
$query->where('enabled', true)->with(['primaryApiProvider', 'apiProviders' => function ($query) {
|
||||
$query->where('enabled', true);
|
||||
}]);
|
||||
}])->find($styleId);
|
||||
|
||||
if ($style && $style->aiModel) {
|
||||
// Use the primary API provider for this AI model if available
|
||||
$apiProvider = $style->aiModel->primaryApiProvider;
|
||||
if (!$apiProvider) {
|
||||
// Fallback to the first enabled API provider from the many-to-many relationship
|
||||
$apiProvider = $style->aiModel->apiProviders->where('enabled', true)->first();
|
||||
}
|
||||
if (!$apiProvider) {
|
||||
// If no enabled provider found, try any provider
|
||||
$apiProvider = $style->aiModel->apiProviders->first();
|
||||
}
|
||||
}
|
||||
}
|
||||
// If image_uuid is provided, get the API provider for that image's style
|
||||
elseif ($imageUuid) {
|
||||
$image = Image::with(['style.aiModel' => function ($query) {
|
||||
$query->with(['primaryApiProvider', 'apiProviders' => function ($query) {
|
||||
$query->where('enabled', true);
|
||||
}]);
|
||||
}])->where('uuid', $imageUuid)->first();
|
||||
|
||||
if ($image && $image->style && $image->style->aiModel) {
|
||||
// Use the primary API provider for this AI model if available
|
||||
$apiProvider = $image->style->aiModel->primaryApiProvider;
|
||||
if (!$apiProvider) {
|
||||
// Fallback to the first enabled API provider from the many-to-many relationship
|
||||
$apiProvider = $image->style->aiModel->apiProviders->where('enabled', true)->first();
|
||||
}
|
||||
if (!$apiProvider) {
|
||||
// If no enabled provider found, try any provider
|
||||
$apiProvider = $image->style->aiModel->apiProviders->first();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fallback to the old behavior if no style_id or image_uuid is provided
|
||||
else {
|
||||
// Try to get a default style if none is provided
|
||||
$defaultStyleSetting = \App\Models\Setting::where('key', 'default_style_id')->first();
|
||||
if ($defaultStyleSetting && $defaultStyleSetting->value) {
|
||||
$style = Style::with(['aiModel' => function ($query) {
|
||||
$query->where('enabled', true)->with(['primaryApiProvider', 'apiProviders' => function ($query) {
|
||||
$query->where('enabled', true);
|
||||
}]);
|
||||
}])->find($defaultStyleSetting->value);
|
||||
|
||||
if ($style && $style->aiModel) {
|
||||
// Use the primary API provider for this AI model if available
|
||||
$apiProvider = $style->aiModel->primaryApiProvider;
|
||||
if (!$apiProvider) {
|
||||
// Fallback to the first enabled API provider from the many-to-many relationship
|
||||
$apiProvider = $style->aiModel->apiProviders->where('enabled', true)->first();
|
||||
}
|
||||
if (!$apiProvider) {
|
||||
// If no enabled provider found, try any provider
|
||||
$apiProvider = $style->aiModel->apiProviders->first();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If still no API provider, use the first available ComfyUI provider
|
||||
if (!$apiProvider) {
|
||||
$apiProvider = ApiProvider::where('plugin', 'ComfyUi')->where('enabled', true)->first();
|
||||
}
|
||||
}
|
||||
|
||||
if (!$apiProvider) {
|
||||
return response()->json(['error' => 'No enabled ComfyUI API provider found.'], 404);
|
||||
|
||||
@@ -4,12 +4,11 @@ namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\Process\Exception\ProcessFailedException;
|
||||
use Symfony\Component\Process\Process;
|
||||
use App\Services\PrinterService;
|
||||
|
||||
class PrintController extends Controller
|
||||
{
|
||||
public function printImage(Request $request)
|
||||
public function printImage(Request $request, PrinterService $printerService)
|
||||
{
|
||||
$request->validate([
|
||||
'image_path' => 'required|string',
|
||||
@@ -18,36 +17,32 @@ class PrintController extends Controller
|
||||
|
||||
$imagePath = public_path(str_replace(url('/'), '', $request->input('image_path')));
|
||||
$quantity = $request->input('quantity');
|
||||
// Retrieve printer name from global settings using standard Eloquent
|
||||
$printerName = \App\Models\Setting::where('key', 'selected_printer')->value('value');
|
||||
|
||||
if (!$printerName) {
|
||||
Log::error("PrintController: Default printer name not found in settings.");
|
||||
return response()->json(['error' => 'Default printer not configured.'], 500);
|
||||
}
|
||||
|
||||
if (!$printerName) {
|
||||
Log::error("PrintController: Default printer name not found in settings.");
|
||||
return response()->json(['error' => 'Default printer not configured.'], 500);
|
||||
}
|
||||
|
||||
if (!file_exists($imagePath)) {
|
||||
Log::error("PrintController: Image file not found at {$imagePath}");
|
||||
return response()->json(['error' => 'Image file not found.'], 404);
|
||||
}
|
||||
|
||||
// IMPORTANT: Replace this command with one that works in your environment.
|
||||
// Examples:
|
||||
// Linux/macOS: $command = ['lpr', '-#', $quantity, $imagePath];
|
||||
// Windows (assuming a shared printer named 'MyNetworkPrinter'):
|
||||
// $command = ['print', '/d:\\MyNetworkPrinter', $imagePath];
|
||||
// You might need to install additional software or configure your system
|
||||
// to enable command-line printing.
|
||||
// For a more robust solution, consider a dedicated print server application
|
||||
// or a commercial print API.
|
||||
$command = ['echo', "Simulating print of {$quantity} copies of {$imagePath}"]; // Placeholder
|
||||
$printSuccess = $printerService->printImage($imagePath, $printerName, $quantity);
|
||||
|
||||
try {
|
||||
$process = new Process($command);
|
||||
$process->run();
|
||||
|
||||
if (!$process->isSuccessful()) {
|
||||
throw new ProcessFailedException($process);
|
||||
}
|
||||
|
||||
Log::info("PrintController: Successfully sent print command for {$imagePath} (x{$quantity})");
|
||||
if ($printSuccess) {
|
||||
Log::info("PrintController: Successfully sent print command for {$imagePath} (x{$quantity}) to {$printerName}");
|
||||
return response()->json(['message' => 'Print command sent successfully.']);
|
||||
} catch (ProcessFailedException $exception) {
|
||||
Log::error("PrintController: Print command failed. Error: " . $exception->getMessage());
|
||||
return response()->json(['error' => 'Failed to send print command.', 'details' => $exception->getMessage()], 500);
|
||||
} else {
|
||||
Log::error("PrintController: Failed to send print command for {$imagePath} (x{$quantity}) to {$printerName}");
|
||||
return response()->json(['error' => 'Failed to send print command.'], 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Middleware;
|
||||
use App\Models\Setting;
|
||||
|
||||
class HandleInertiaRequests extends Middleware
|
||||
{
|
||||
@@ -35,6 +36,7 @@ class HandleInertiaRequests extends Middleware
|
||||
'user' => $request->user(),
|
||||
],
|
||||
'locale' => app()->getLocale(),
|
||||
'settings' => Setting::all()->pluck('value', 'key'),
|
||||
'translations' => function () use ($request) {
|
||||
$currentLocale = app()->getLocale(); // Store current locale
|
||||
$requestedLocale = $request->input('locale', $currentLocale);
|
||||
@@ -47,8 +49,6 @@ class HandleInertiaRequests extends Middleware
|
||||
// Add other translation files as needed
|
||||
];
|
||||
|
||||
dd($lang); // <-- ADDED FOR DEBUGGING
|
||||
|
||||
app()->setLocale($currentLocale); // Revert to original locale
|
||||
return $lang;
|
||||
},
|
||||
|
||||
@@ -20,6 +20,11 @@ class AiModel extends Model
|
||||
'parameters' => 'array',
|
||||
];
|
||||
|
||||
public function primaryApiProvider()
|
||||
{
|
||||
return $this->belongsTo(ApiProvider::class, 'api_provider_id');
|
||||
}
|
||||
|
||||
public function apiProviders()
|
||||
{
|
||||
return $this->belongsToMany(ApiProvider::class, 'ai_model_api_provider');
|
||||
|
||||
119
app/Services/PrinterService.php
Normal file
119
app/Services/PrinterService.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class PrinterService
|
||||
{
|
||||
public function getPrinters(): array
|
||||
{
|
||||
$printers = [];
|
||||
$os = strtoupper(substr(PHP_OS, 0, 3));
|
||||
|
||||
if ($os === 'WIN') {
|
||||
// Windows implementation
|
||||
$output = shell_exec('wmic printer get name');
|
||||
if ($output != "") {
|
||||
$lines = explode(PHP_EOL, trim($output));
|
||||
$start = 1; // Skip header on Windows
|
||||
for ($i = $start; $i < count($lines); $i++) {
|
||||
// Remove all control characters and non-printable characters, then trim
|
||||
$printerName = trim(preg_replace('/[[:cntrl:]]/', '', $lines[$i]));
|
||||
if (!empty($printerName)) {
|
||||
$printers[$printerName] = $printerName;
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($os === 'DAR' || $os === 'LIN') {
|
||||
// macOS and Linux implementation
|
||||
$output = shell_exec('lpstat -p 2>/dev/null');
|
||||
if ($output !== null) {
|
||||
$lines = explode(PHP_EOL, trim($output));
|
||||
foreach ($lines as $line) {
|
||||
if (preg_match('/printer\s+(.*?)\s+is/', $line, $matches)) {
|
||||
$printerName = trim($matches[1]);
|
||||
if (!empty($printerName)) {
|
||||
$printers[$printerName] = $printerName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $printers;
|
||||
}
|
||||
|
||||
public function printImage(string $imagePath, string $printerName, int $copies = 1): bool
|
||||
{
|
||||
$os = strtoupper(substr(PHP_OS, 0, 3));
|
||||
|
||||
if ($os === 'WIN') {
|
||||
// Windows implementation
|
||||
$magickPath = base_path('bin/imagick/win64/magick.exe');
|
||||
|
||||
if (!file_exists($magickPath)) {
|
||||
Log::error('PrinterService: ImageMagick executable not found.', ['path' => $magickPath]);
|
||||
return false;
|
||||
}
|
||||
|
||||
$success = true;
|
||||
for ($i = 0; $i < $copies; $i++) {
|
||||
// Ensure imagePath is absolute and properly quoted
|
||||
$quotedImagePath = escapeshellarg($imagePath);
|
||||
$quotedPrinterName = escapeshellarg("printer:$printerName"); // "printer:printerName" needs to be one argument
|
||||
|
||||
$command = "{$magickPath} {$quotedImagePath} {$quotedPrinterName}";
|
||||
|
||||
Log::debug('PrinterService: Executing print command:', ['command' => $command]);
|
||||
$output = shell_exec($command);
|
||||
|
||||
// Log::debug('PrinterService: Executing print command:', ['command' => $command]);
|
||||
$output = shell_exec($command);
|
||||
Log::debug('PrinterService: Print command output:', ['output' => $output]);
|
||||
|
||||
// Check for errors. shell_exec returns NULL on command failure.
|
||||
if ($output === null) {
|
||||
Log::error('PrinterService: Print command failed.', ['command' => $command, 'output' => $output]);
|
||||
$success = false;
|
||||
break; // Stop if one copy fails
|
||||
}
|
||||
}
|
||||
|
||||
return $success;
|
||||
} elseif ($os === 'DAR' || $os === 'LIN') {
|
||||
// macOS and Linux implementation
|
||||
if (!file_exists($imagePath)) {
|
||||
Log::error('PrinterService: Image file not found.', ['path' => $imagePath]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use the lp command to print the image
|
||||
$success = true;
|
||||
for ($i = 0; $i < $copies; $i++) {
|
||||
$quotedImagePath = escapeshellarg($imagePath);
|
||||
$quotedPrinterName = escapeshellarg($printerName);
|
||||
|
||||
// For image printing on Linux/macOS, we might need to convert to a printable format first
|
||||
// Using lp command directly with the image file
|
||||
$command = "lp -d {$quotedPrinterName} {$quotedImagePath} 2>&1";
|
||||
|
||||
Log::debug('PrinterService: Executing print command:', ['command' => $command]);
|
||||
$output = shell_exec($command);
|
||||
Log::debug('PrinterService: Print command output:', ['output' => $output]);
|
||||
|
||||
// Check for errors
|
||||
if ($output === null || (strpos($output, 'Error') !== false)) {
|
||||
Log::error('PrinterService: Print command failed.', ['command' => $command, 'output' => $output]);
|
||||
$success = false;
|
||||
break; // Stop if one copy fails
|
||||
}
|
||||
}
|
||||
|
||||
return $success;
|
||||
} else {
|
||||
Log::error('PrinterService: Unsupported operating system.');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user