2023-02-01 12:50:00 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
namespace Shimmie2;
|
|
|
|
|
2024-01-11 00:55:05 +00:00
|
|
|
use Symfony\Component\Console\Command\Command;
|
|
|
|
use Symfony\Component\Console\Input\{InputInterface,InputArgument};
|
|
|
|
use Symfony\Component\Console\Output\OutputInterface;
|
|
|
|
|
2023-02-01 12:50:00 +00:00
|
|
|
use GraphQL\GraphQL as GQL;
|
|
|
|
use GraphQL\Server\StandardServer;
|
|
|
|
use GraphQL\Error\DebugFlag;
|
2023-02-04 18:24:34 +00:00
|
|
|
use GraphQL\Type\Schema;
|
2023-02-01 12:50:00 +00:00
|
|
|
use GraphQL\Utils\SchemaPrinter;
|
|
|
|
|
2023-03-08 22:27:00 +00:00
|
|
|
#[\GQLA\InputObjectType]
|
|
|
|
class MetadataInput
|
|
|
|
{
|
|
|
|
public function __construct(
|
|
|
|
#[\GQLA\Field]
|
|
|
|
public string $tags,
|
|
|
|
#[\GQLA\Field]
|
|
|
|
public string $source,
|
|
|
|
) {
|
|
|
|
}
|
|
|
|
|
|
|
|
#[\GQLA\Mutation]
|
|
|
|
public static function update_post_metadata(int $post_id, MetadataInput $metadata): Image
|
|
|
|
{
|
|
|
|
global $user;
|
|
|
|
$_POST['tag_edit__tags'] = $metadata->tags;
|
|
|
|
$_POST['tag_edit__source'] = $metadata->source;
|
|
|
|
$image = Image::by_id($post_id);
|
|
|
|
if (!$image->is_locked() || $user->can(Permissions::EDIT_IMAGE_LOCK)) {
|
|
|
|
send_event(new ImageInfoSetEvent($image));
|
|
|
|
}
|
|
|
|
return Image::by_id($post_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-01 12:50:00 +00:00
|
|
|
class GraphQL extends Extension
|
|
|
|
{
|
2023-02-04 18:24:34 +00:00
|
|
|
public static function get_schema(): Schema
|
|
|
|
{
|
|
|
|
global $_tracer;
|
|
|
|
$_tracer->begin("Create Schema");
|
2023-02-05 15:56:06 +00:00
|
|
|
$schema = new \GQLA\Schema();
|
2023-02-04 18:24:34 +00:00
|
|
|
$_tracer->end(null);
|
|
|
|
return $schema;
|
|
|
|
}
|
|
|
|
|
2023-02-07 14:18:21 +00:00
|
|
|
private function cors(): void
|
|
|
|
{
|
|
|
|
global $config;
|
|
|
|
$pat = $config->get_string("graphql_cors_pattern");
|
|
|
|
|
|
|
|
if ($pat && isset($_SERVER['HTTP_ORIGIN'])) {
|
|
|
|
if (preg_match("#$pat#", $_SERVER['HTTP_ORIGIN'])) {
|
|
|
|
header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
|
|
|
|
header('Access-Control-Allow-Credentials: true');
|
|
|
|
header('Access-Control-Max-Age: 86400');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
|
|
|
|
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) {
|
|
|
|
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
|
|
|
|
}
|
|
|
|
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) {
|
|
|
|
header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
|
|
|
|
}
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-15 11:52:35 +00:00
|
|
|
public function onInitExt(InitExtEvent $event): void
|
2023-02-07 14:18:21 +00:00
|
|
|
{
|
|
|
|
global $config;
|
|
|
|
$config->set_default_string('graphql_cors_pattern', "");
|
|
|
|
$config->set_default_bool('graphql_debug', false);
|
|
|
|
}
|
|
|
|
|
2024-01-15 11:52:35 +00:00
|
|
|
public function onPageRequest(PageRequestEvent $event): void
|
2023-02-01 12:50:00 +00:00
|
|
|
{
|
2023-02-07 14:18:21 +00:00
|
|
|
global $config, $page;
|
2023-02-02 16:50:09 +00:00
|
|
|
if ($event->page_matches("graphql")) {
|
2023-02-07 14:18:21 +00:00
|
|
|
$this->cors();
|
2023-02-01 12:50:00 +00:00
|
|
|
$t1 = ftime();
|
|
|
|
$server = new StandardServer([
|
2023-02-04 18:24:34 +00:00
|
|
|
'schema' => $this->get_schema(),
|
2023-02-01 12:50:00 +00:00
|
|
|
]);
|
|
|
|
$t2 = ftime();
|
|
|
|
$resp = $server->executeRequest();
|
2023-02-07 14:18:21 +00:00
|
|
|
if ($config->get_bool("graphql_debug")) {
|
|
|
|
$debug = DebugFlag::INCLUDE_DEBUG_MESSAGE | DebugFlag::RETHROW_INTERNAL_EXCEPTIONS;
|
|
|
|
$body = $resp->toArray($debug);
|
|
|
|
} else {
|
|
|
|
$body = $resp->toArray();
|
|
|
|
}
|
2023-02-01 12:50:00 +00:00
|
|
|
$t3 = ftime();
|
|
|
|
$body['stats'] = get_debug_info_arr();
|
|
|
|
$body['stats']['graphql_schema_time'] = round($t2 - $t1, 2);
|
|
|
|
$body['stats']['graphql_execute_time'] = round($t3 - $t2, 2);
|
2023-03-08 22:27:00 +00:00
|
|
|
// sleep(1);
|
2023-02-01 12:50:00 +00:00
|
|
|
$page->set_mode(PageMode::DATA);
|
|
|
|
$page->set_mime("application/json");
|
|
|
|
$page->set_data(\json_encode($body, JSON_UNESCAPED_UNICODE));
|
|
|
|
}
|
2023-02-24 05:32:52 +00:00
|
|
|
if ($event->page_matches("graphql_upload")) {
|
|
|
|
$this->cors();
|
|
|
|
$page->set_mode(PageMode::DATA);
|
|
|
|
$page->set_mime("application/json");
|
|
|
|
$page->set_data(\json_encode(self::handle_uploads()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static function handle_uploads(): array
|
|
|
|
{
|
|
|
|
global $user;
|
|
|
|
|
|
|
|
if (!$user->can(Permissions::CREATE_IMAGE)) {
|
|
|
|
return ["error" => "User cannot create posts"];
|
|
|
|
}
|
|
|
|
|
|
|
|
$common_tags = $_POST['common_tags'];
|
|
|
|
$common_source = $_POST['common_source'];
|
|
|
|
|
|
|
|
$results = [];
|
2023-11-11 21:49:12 +00:00
|
|
|
for ($n = 0; $n < 100; $n++) {
|
2023-02-24 05:32:52 +00:00
|
|
|
if (empty($_POST["url$n"]) && empty($_FILES["data$n"])) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (isset($_FILES["data$n"]) && ($_FILES["data$n"]["size"] == 0 || $_FILES["data$n"]["error"] == UPLOAD_ERR_NO_FILE)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
$results[] = self::handle_upload($n, $common_tags, $common_source);
|
|
|
|
} catch(\Exception $e) {
|
|
|
|
$results[] = ["error" => $e->getMessage()];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ["results" => $results];
|
|
|
|
}
|
|
|
|
|
|
|
|
private static function handle_upload(int $n, string $common_tags, string $common_source): array
|
|
|
|
{
|
|
|
|
if (!empty($_POST["url$n"])) {
|
2024-01-15 13:40:18 +00:00
|
|
|
throw new \Exception("URLs not handled yet");
|
2023-02-24 05:32:52 +00:00
|
|
|
} else {
|
|
|
|
$ec = $_FILES["data$n"]["error"];
|
|
|
|
switch($ec) {
|
|
|
|
case UPLOAD_ERR_OK:
|
|
|
|
$tmpname = $_FILES["data$n"]["tmp_name"];
|
|
|
|
$filename = $_FILES["data$n"]["name"];
|
|
|
|
break;
|
|
|
|
case UPLOAD_ERR_INI_SIZE:
|
|
|
|
return ["error" => "File larger than PHP can handle"];
|
|
|
|
default:
|
|
|
|
return ["error" => "Mystery error: $ec"];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$tags = trim($common_tags . " " . $_POST["tags$n"]);
|
|
|
|
$source = $common_source;
|
|
|
|
if (!empty($_POST["source$n"])) {
|
|
|
|
$source = $_POST["source$n"];
|
|
|
|
}
|
|
|
|
$event = send_event(new DataUploadEvent($tmpname, [
|
|
|
|
'filename' => $filename,
|
|
|
|
'tags' => Tag::explode($tags),
|
|
|
|
'source' => $source,
|
|
|
|
]));
|
|
|
|
|
2024-01-05 15:32:07 +00:00
|
|
|
return ["image_ids" => array_map(fn ($im) => $im->id, $event->images)];
|
2023-02-01 12:50:00 +00:00
|
|
|
}
|
|
|
|
|
2024-01-15 11:52:35 +00:00
|
|
|
public function onCliGen(CliGenEvent $event): void
|
2023-02-01 12:50:00 +00:00
|
|
|
{
|
2024-01-11 00:55:05 +00:00
|
|
|
$event->app->register('graphql:query')
|
|
|
|
->addArgument('query', InputArgument::REQUIRED)
|
|
|
|
->setDescription('Run a GraphQL query')
|
|
|
|
->setCode(function (InputInterface $input, OutputInterface $output): int {
|
|
|
|
$query = $input->getArgument('query');
|
|
|
|
$t1 = ftime();
|
|
|
|
$schema = $this->get_schema();
|
|
|
|
$t2 = ftime();
|
|
|
|
$debug = DebugFlag::INCLUDE_DEBUG_MESSAGE | DebugFlag::RETHROW_INTERNAL_EXCEPTIONS;
|
|
|
|
$body = GQL::executeQuery($schema, $query)->toArray($debug);
|
|
|
|
$t3 = ftime();
|
|
|
|
$body['stats'] = get_debug_info_arr();
|
|
|
|
$body['stats']['graphql_schema_time'] = round($t2 - $t1, 2);
|
|
|
|
$body['stats']['graphql_execute_time'] = round($t3 - $t2, 2);
|
|
|
|
echo \json_encode($body, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
|
|
|
return Command::SUCCESS;
|
|
|
|
});
|
|
|
|
$event->app->register('graphql:schema')
|
|
|
|
->setDescription('Print out the GraphQL schema')
|
|
|
|
->setCode(function (InputInterface $input, OutputInterface $output): int {
|
|
|
|
$schema = $this->get_schema();
|
|
|
|
echo(SchemaPrinter::doPrint($schema));
|
|
|
|
return Command::SUCCESS;
|
|
|
|
});
|
2023-02-01 12:50:00 +00:00
|
|
|
}
|
|
|
|
}
|