Setup to allow creating snipped from API
This commit is contained in:
4
.env
4
.env
@@ -27,3 +27,7 @@ APP_SECRET=
|
|||||||
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.13-MariaDB&charset=utf8mb4"
|
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.13-MariaDB&charset=utf8mb4"
|
||||||
# DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=15&charset=utf8"
|
# DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=15&charset=utf8"
|
||||||
###< doctrine/doctrine-bundle ###
|
###< doctrine/doctrine-bundle ###
|
||||||
|
|
||||||
|
###> nelmio/cors-bundle ###
|
||||||
|
CORS_ALLOW_ORIGIN='^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$'
|
||||||
|
###< nelmio/cors-bundle ###
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
"doctrine/orm": "^3.4",
|
"doctrine/orm": "^3.4",
|
||||||
"league/commonmark": "^2.6",
|
"league/commonmark": "^2.6",
|
||||||
"league/pipeline": "^1.0",
|
"league/pipeline": "^1.0",
|
||||||
|
"nelmio/cors-bundle": "^2.5",
|
||||||
"phpdocumentor/reflection-docblock": "^5.6",
|
"phpdocumentor/reflection-docblock": "^5.6",
|
||||||
"phpstan/phpdoc-parser": "^2.1",
|
"phpstan/phpdoc-parser": "^2.1",
|
||||||
"symfony/asset": "*",
|
"symfony/asset": "*",
|
||||||
|
64
composer.lock
generated
64
composer.lock
generated
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "405d2a2709cb499d92b64659d2f59b2e",
|
"content-hash": "b11b8d3b530bf74510557e53988cdf75",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "dflydev/dot-access-data",
|
"name": "dflydev/dot-access-data",
|
||||||
@@ -1548,6 +1548,68 @@
|
|||||||
],
|
],
|
||||||
"time": "2025-03-24T10:02:05+00:00"
|
"time": "2025-03-24T10:02:05+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "nelmio/cors-bundle",
|
||||||
|
"version": "2.5.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/nelmio/NelmioCorsBundle.git",
|
||||||
|
"reference": "3a526fe025cd20e04a6a11370cf5ab28dbb5a544"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/nelmio/NelmioCorsBundle/zipball/3a526fe025cd20e04a6a11370cf5ab28dbb5a544",
|
||||||
|
"reference": "3a526fe025cd20e04a6a11370cf5ab28dbb5a544",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"psr/log": "^1.0 || ^2.0 || ^3.0",
|
||||||
|
"symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"mockery/mockery": "^1.3.6",
|
||||||
|
"symfony/phpunit-bridge": "^5.4 || ^6.0 || ^7.0"
|
||||||
|
},
|
||||||
|
"type": "symfony-bundle",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "2.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Nelmio\\CorsBundle\\": ""
|
||||||
|
},
|
||||||
|
"exclude-from-classmap": [
|
||||||
|
"/Tests/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Nelmio",
|
||||||
|
"homepage": "http://nelm.io"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://github.com/nelmio/NelmioCorsBundle/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Adds CORS (Cross-Origin Resource Sharing) headers support in your Symfony application",
|
||||||
|
"keywords": [
|
||||||
|
"api",
|
||||||
|
"cors",
|
||||||
|
"crossdomain"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/nelmio/NelmioCorsBundle/issues",
|
||||||
|
"source": "https://github.com/nelmio/NelmioCorsBundle/tree/2.5.0"
|
||||||
|
},
|
||||||
|
"time": "2024-06-24T21:25:28+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "nette/schema",
|
"name": "nette/schema",
|
||||||
"version": "v1.3.2",
|
"version": "v1.3.2",
|
||||||
|
@@ -11,4 +11,5 @@ return [
|
|||||||
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
|
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
|
||||||
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
|
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
|
||||||
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true],
|
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true],
|
||||||
|
Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true],
|
||||||
];
|
];
|
||||||
|
15
config/packages/nelmio_cors.yaml
Normal file
15
config/packages/nelmio_cors.yaml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
nelmio_cors:
|
||||||
|
defaults:
|
||||||
|
origin_regex: true
|
||||||
|
allow_origin: ['%env(CORS_ALLOW_ORIGIN)%']
|
||||||
|
allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE']
|
||||||
|
allow_headers: ['Content-Type', 'Authorization']
|
||||||
|
expose_headers: ['Link']
|
||||||
|
max_age: 3600
|
||||||
|
paths:
|
||||||
|
'^/api/':
|
||||||
|
allow_origin: ['*']
|
||||||
|
allow_headers: ['*']
|
||||||
|
allow_methods: ['POST', 'PUT', 'GET', 'DELETE']
|
||||||
|
max_age: 3600
|
||||||
|
'^/': null
|
42
src/Controller/Api/ExtensionController.php
Normal file
42
src/Controller/Api/ExtensionController.php
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller\Api;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||||
|
use Symfony\Contracts\Cache\CacheInterface;
|
||||||
|
|
||||||
|
class ExtensionController extends AbstractApiController
|
||||||
|
{
|
||||||
|
#[Route('/content', methods: ['POST'])]
|
||||||
|
public function setContent(
|
||||||
|
Request $request,
|
||||||
|
CacheInterface $cache,
|
||||||
|
): Response {
|
||||||
|
// Parse JSON payload
|
||||||
|
$data = json_decode($request->getContent(), true);
|
||||||
|
if (!$data || !isset($data['html'])) {
|
||||||
|
return $this->errorResponse('Invalid JSON payload: Missing html parameter');
|
||||||
|
}
|
||||||
|
$html = $data['html'];
|
||||||
|
|
||||||
|
// Store HTML in cache with unique key
|
||||||
|
$userId = $this->getUser()?->getId();
|
||||||
|
$cacheKey = 'html_' . $userId . '_' . bin2hex(random_bytes(8));
|
||||||
|
$cache->get($cacheKey, function () use ($html) {
|
||||||
|
// value to cache
|
||||||
|
return $html;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Build URL to redirect user to a form page later
|
||||||
|
$formUrl = $this->generateUrl(
|
||||||
|
'snip_new',
|
||||||
|
['cacheKey' => $cacheKey],
|
||||||
|
UrlGeneratorInterface::ABSOLUTE_URL
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->successResponse(['url' => $formUrl]);
|
||||||
|
}
|
||||||
|
}
|
@@ -5,6 +5,8 @@ namespace App\Controller;
|
|||||||
use App\Controller\Attribute\MapQueryCached;
|
use App\Controller\Attribute\MapQueryCached;
|
||||||
use App\Dto\SnipFilterRequest;
|
use App\Dto\SnipFilterRequest;
|
||||||
use App\Entity\Snip;
|
use App\Entity\Snip;
|
||||||
|
use App\Entity\SnipContent;
|
||||||
|
use App\Entity\Tag;
|
||||||
use App\Form\ConfirmationType;
|
use App\Form\ConfirmationType;
|
||||||
use App\Form\SnipType;
|
use App\Form\SnipType;
|
||||||
use App\Repository\SnipRepository;
|
use App\Repository\SnipRepository;
|
||||||
@@ -16,6 +18,7 @@ use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
|||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Attribute\Route;
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
use Symfony\Contracts\Cache\CacheInterface;
|
||||||
|
|
||||||
#[Route('/snip', name: 'snip')]
|
#[Route('/snip', name: 'snip')]
|
||||||
class SnipController extends AbstractController
|
class SnipController extends AbstractController
|
||||||
@@ -121,8 +124,13 @@ class SnipController extends AbstractController
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Route('/new', name: '_new')]
|
#[Route('/new/{cacheKey}', name: '_new')]
|
||||||
public function new(Request $request, SnipContentService $contentService): Response
|
public function new(
|
||||||
|
Request $request,
|
||||||
|
SnipContentService $contentService,
|
||||||
|
CacheInterface $cache,
|
||||||
|
?string $cacheKey = null,
|
||||||
|
): Response
|
||||||
{
|
{
|
||||||
$snip = new Snip();
|
$snip = new Snip();
|
||||||
$snip->setCreatedAtNow();
|
$snip->setCreatedAtNow();
|
||||||
@@ -131,6 +139,12 @@ class SnipController extends AbstractController
|
|||||||
$form = $this->createForm(SnipType::class, $snip);
|
$form = $this->createForm(SnipType::class, $snip);
|
||||||
$form->add('Create', SubmitType::class);
|
$form->add('Create', SubmitType::class);
|
||||||
|
|
||||||
|
if ($cacheKey) {
|
||||||
|
$content = $cache->get($cacheKey, fn() => null);
|
||||||
|
$form->get('content')->setData($content);
|
||||||
|
$form->get('parser')->setData('unsafe');
|
||||||
|
}
|
||||||
|
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
if ($form->isSubmitted() && $form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
$this->repository->save($snip);
|
$this->repository->save($snip);
|
||||||
|
@@ -25,7 +25,7 @@ class Snip
|
|||||||
#[ORM\Column]
|
#[ORM\Column]
|
||||||
public bool $public = false;
|
public bool $public = false;
|
||||||
|
|
||||||
#[ORM\OneToMany(mappedBy: 'snip', targetEntity: SnipContent::class, orphanRemoval: true)]
|
#[ORM\OneToMany(targetEntity: SnipContent::class, mappedBy: 'snip', orphanRemoval: true)]
|
||||||
public Collection $snipContents;
|
public Collection $snipContents;
|
||||||
|
|
||||||
#[ORM\OneToOne]
|
#[ORM\OneToOne]
|
||||||
|
13
src/Service/SnipParser/Generic/UnsafeParser.php
Normal file
13
src/Service/SnipParser/Generic/UnsafeParser.php
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Service\SnipParser\Generic;
|
||||||
|
|
||||||
|
use App\Service\SnipParser\AbstractParser;
|
||||||
|
|
||||||
|
class UnsafeParser extends AbstractParser
|
||||||
|
{
|
||||||
|
public function safeParseView(string $content): string
|
||||||
|
{
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
}
|
12
symfony.lock
12
symfony.lock
@@ -35,6 +35,18 @@
|
|||||||
"migrations/.gitignore"
|
"migrations/.gitignore"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"nelmio/cors-bundle": {
|
||||||
|
"version": "2.5",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "main",
|
||||||
|
"version": "1.5",
|
||||||
|
"ref": "6bea22e6c564fba3a1391615cada1437d0bde39c"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"config/packages/nelmio_cors.yaml"
|
||||||
|
]
|
||||||
|
},
|
||||||
"symfony/console": {
|
"symfony/console": {
|
||||||
"version": "7.2",
|
"version": "7.2",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
|
Reference in New Issue
Block a user