ContentPipelineController.php

Code Hygiene Score: 78

Keine Issues gefunden.

Dependencies 15

Klassen 1

Funktionen 18

Versionen 33

Code

<?php

declare(strict_types=1);

namespace Controller;

// @responsibility: HTTP-Endpunkte für Content-Pipeline-Verwaltung

use Application\PipelineStepService;
use Domain\Repository\PipelineRepositoryInterface;
use Framework\Controller;
use Infrastructure\AI\ModelRegistry;
use Infrastructure\Config\PipelineStepConfig;
use UseCases\Pipeline\PipelineRunStatusUseCase;
use UseCases\Pipeline\RunPipelineUseCase;
use UseCases\Pipeline\UpdatePipelineConfigUseCase;

class ContentPipelineController extends Controller
{
    public function __construct(
        private PipelineRepositoryInterface $repository,
        private PipelineStepService $stepService,
        private RunPipelineUseCase $runUseCase,
        private PipelineRunStatusUseCase $statusUseCase,
        private UpdatePipelineConfigUseCase $configUseCase,
        private ModelRegistry $modelRegistry
    ) {
    }

    /**
     * GET /content-pipeline
     */
    public function index(): void
    {
        $this->view('content-pipeline.index', [
            'title' => 'Content Pipeline',
            'pipelines' => $this->repository->findAll(),
            'stats' => $this->repository->getStatistics(),
        ]);
    }

    /**
     * GET /content-pipeline/import
     */
    public function import(): void
    {
        $pipeline = $this->repository->findDefault();

        if ($pipeline === null) {
            $pipelines = $this->repository->findAll(1);
            $pipeline = $pipelines[0] ?? null;
        }

        $this->view('content-pipeline.import', [
            'title' => 'Import Pipeline',
            'pipeline' => $pipeline,
            'latestRun' => $pipeline !== null
                ? $this->repository->findLatestRun((int) $pipeline['id'])
                : null,
        ]);
    }

    /**
     * GET /content-pipeline/new
     */
    public function pipelineNew(): void
    {
        $this->view('content-pipeline.form', [
            'title' => 'Neue Pipeline',
            'pipeline' => null,
            'stepTypes' => PipelineStepConfig::getStepTypes(),
        ]);
    }

    /**
     * GET /content-pipeline/{id}
     */
    public function show(string $id): void
    {
        $pipeline = $this->repository->findById((int) $id);

        if ($pipeline === null) {
            $this->notFound('Pipeline nicht gefunden');
        }

        $this->view('content-pipeline.show', [
            'title' => 'Pipeline: ' . $pipeline['name'],
            'pipeline' => $pipeline,
            'runs' => $this->repository->findRuns((int) $id, 10),
            'stepTypes' => PipelineStepConfig::getStepTypes(),
            'models' => $this->modelRegistry->getChatModels(),
            'defaultModel' => $this->modelRegistry->getDefaultChatModel(),
            'collections' => PipelineStepConfig::getCollections(),
        ]);
    }

    /**
     * GET /content-pipeline/{id}/edit
     */
    public function edit(string $id): void
    {
        $pipeline = $this->repository->findById((int) $id);

        if ($pipeline === null) {
            $this->notFound('Pipeline nicht gefunden');
        }

        $this->view('content-pipeline.form', [
            'title' => 'Pipeline bearbeiten: ' . $pipeline['name'],
            'pipeline' => $pipeline,
            'stepTypes' => PipelineStepConfig::getStepTypes(),
        ]);
    }

    /**
     * POST /content-pipeline
     */
    public function store(): void
    {
        $this->requireCsrf();

        $name = trim($_POST['name'] ?? '');

        if ($name === '') {
            $_SESSION['error'] = 'Name ist erforderlich.';
            $this->redirect('/content-pipeline/new');
        }

        $pipelineId = $this->repository->create([
            'name' => $name,
            'description' => trim($_POST['description'] ?? ''),
            'source_path' => trim($_POST['source_path'] ?? '/var/www/nextcloud/data/root/files/Documents'),
            'extensions' => PipelineStepConfig::parseExtensions($_POST['extensions'] ?? ''),
            'is_default' => isset($_POST['is_default']) ? 1 : 0,
        ]);

        $this->stepService->createDefaultSteps($pipelineId);

        $_SESSION['success'] = 'Pipeline erfolgreich erstellt.';
        $this->redirect('/content-pipeline/' . $pipelineId);
    }

    /**
     * POST /content-pipeline/{id}
     */
    public function update(string $id): void
    {
        $this->requireCsrf();

        $pipeline = $this->repository->findById((int) $id);

        if ($pipeline === null) {
            $this->notFound('Pipeline nicht gefunden');
        }

        $name = trim($_POST['name'] ?? '');

        if ($name === '') {
            $_SESSION['error'] = 'Name ist erforderlich.';
            $this->redirect('/content-pipeline/' . $id . '/edit');
        }

        $this->repository->update((int) $id, [
            'name' => $name,
            'description' => trim($_POST['description'] ?? ''),
            'source_path' => trim($_POST['source_path'] ?? ''),
            'extensions' => PipelineStepConfig::parseExtensions($_POST['extensions'] ?? ''),
            'is_default' => isset($_POST['is_default']) ? 1 : 0,
        ]);

        $_SESSION['success'] = 'Pipeline aktualisiert.';
        $this->redirect('/content-pipeline/' . $id);
    }

    /**
     * POST /content-pipeline/{id}/run
     */
    public function run(string $id): void
    {
        $this->requireCsrf();

        $result = $this->runUseCase->start((int) $id);

        if (!$result['success']) {
            $this->notFound($result['error'] ?? 'Pipeline nicht gefunden');
        }

        $this->redirect('/content-pipeline/' . $id . '/run/' . $result['run_id'] . '/status');
    }

    /**
     * GET /content-pipeline/{id}/status (AJAX)
     */
    public function status(string $id): void
    {
        $result = $this->runUseCase->getStatus((int) $id);

        if (!$result['success']) {
            $this->json(['error' => $result['error']], 404);

            return;
        }

        $this->json($result);
    }

    /**
     * GET /content-pipeline/{id}/run/{runId}/status
     */
    public function runStatus(string $id, string $runId): void
    {
        $pipeline = $this->repository->findById((int) $id);

        if ($pipeline === null) {
            $this->notFound('Pipeline nicht gefunden');
        }

        $run = $this->repository->findRunById((int) $runId);

        if ($run === null || (int) $run['pipeline_id'] !== (int) $id) {
            $this->notFound('Run nicht gefunden');
        }

        $this->view('content-pipeline.run-status', [
            'title' => 'Pipeline Status: ' . $pipeline['name'],
            'pipeline' => $pipeline,
            'run' => $run,
        ]);
    }

    /**
     * GET /content-pipeline/{id}/run/{runId}/poll (AJAX/HTMX)
     */
    public function runStatusPoll(string $id, string $runId): void
    {
        $result = $this->statusUseCase->execute((int) $id, (int) $runId);

        if (!$result['success']) {
            $this->json(['error' => $result['error']], 404);

            return;
        }

        $this->json($result['data']);
    }

    /**
     * POST /content-pipeline/{id}/run/{runId}/cancel
     */
    public function runCancel(string $id, string $runId): void
    {
        $this->requireCsrf();

        $result = $this->runUseCase->cancel((int) $id, (int) $runId);

        if (!$result['success']) {
            if ($result['error'] === 'Run nicht gefunden') {
                $this->notFound($result['error']);
            }

            if ($this->isHtmxRequest()) {
                $this->htmxError($result['error']);

                return;
            }
            $_SESSION['error'] = $result['error'];
        } else {
            // HTMX: Return disabled button
            if ($this->isHtmxRequest()) {
                $this->partial('content-pipeline.partials.cancel-button', [
                    'disabled' => true,
                    'label' => 'Abgebrochen',
                ]);

                return;
            }
            $_SESSION['success'] = 'Pipeline-Run wurde abgebrochen.';
        }

        $this->redirect('/content-pipeline/' . $id . '/run/' . $runId . '/status');
    }

    /**
     * POST /content-pipeline/{id}/steps/{stepId}/toggle
     */
    public function toggleStep(string $id, string $stepId): void
    {
        $this->requireCsrf();

        if (!$this->stepService->toggleStep((int) $id, (int) $stepId)) {
            $this->notFound('Pipeline oder Schritt nicht gefunden');
        }

        $this->redirect('/content-pipeline/' . $id);
    }

    /**
     * POST /content-pipeline/{id}/steps/{stepId}/model (HTMX)
     */
    public function updateStepModel(string $id, string $stepId): void
    {
        $this->requireCsrf();

        $result = $this->stepService->updateModel(
            (int) $id,
            (int) $stepId,
            $_POST['model'] ?? ''
        );

        if (!$result['success']) {
            $this->htmxError($result['error']);

            return;
        }

        // hx-swap="none" erwartet nur Status 200
        $this->text('OK');
    }

    /**
     * POST /content-pipeline/{id}/steps/{stepId}/collection (HTMX)
     */
    public function updateStepCollection(string $id, string $stepId): void
    {
        $this->requireCsrf();

        $result = $this->stepService->updateCollection(
            (int) $id,
            (int) $stepId,
            $_POST['collection'] ?? ''
        );

        if (!$result['success']) {
            $this->htmxError($result['error']);

            return;
        }

        // hx-swap="none" erwartet nur Status 200
        $this->text('OK');
    }

    /**
     * POST /content-pipeline/{id}/config (HTMX)
     */
    public function updateConfig(string $id): void
    {
        $this->requireCsrf();

        $result = $this->configUseCase->execute((int) $id, $_POST);

        if (!$result['success']) {
            $this->htmxError($result['error']);

            return;
        }

        // hx-swap="none" erwartet nur Status 200
        $this->text('OK');
    }

    /**
     * POST /content-pipeline/{id}/delete
     */
    public function delete(string $id): void
    {
        $this->requireCsrf();

        $pipeline = $this->repository->findById((int) $id);

        if ($pipeline === null) {
            $this->notFound('Pipeline nicht gefunden');
        }

        $this->repository->delete((int) $id);

        $_SESSION['success'] = 'Pipeline geloescht.';
        $this->redirect('/content-pipeline');
    }
}
← Übersicht Graph