[core] replace quarter-arsed CLI interface with Symfony Console

This commit is contained in:
Shish 2024-01-11 00:55:05 +00:00
parent f3b292f2ea
commit ec35cace6a
21 changed files with 983 additions and 911 deletions

View file

@ -51,7 +51,8 @@
"psr/simple-cache": "^1.0", "psr/simple-cache": "^1.0",
"sabre/cache": "^2.0.1", "sabre/cache": "^2.0.1",
"naroga/redis-cache": "dev-master", "naroga/redis-cache": "dev-master",
"aws/aws-sdk-php": "^3.294" "aws/aws-sdk-php": "^3.294",
"symfony/console": "6.4.x-dev"
}, },
"require-dev" : { "require-dev" : {

1136
composer.lock generated

File diff suppressed because it is too large Load diff

64
core/cli_app.php Normal file
View file

@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
namespace Shimmie2;
use Symfony\Component\Console\Input\{ArgvInput,InputOption,InputDefinition,InputInterface};
use Symfony\Component\Console\Output\{OutputInterface,ConsoleOutput};
class CliApp extends \Symfony\Component\Console\Application
{
public function __construct()
{
parent::__construct('Shimmie', VERSION);
}
protected function getDefaultInputDefinition(): InputDefinition
{
$definition = parent::getDefaultInputDefinition();
$definition->addOption(new InputOption(
'--user',
'-u',
InputOption::VALUE_NONE,
'Log in as the given user'
));
return $definition;
}
public function run(InputInterface $input = null, OutputInterface $output = null): int
{
global $user;
$input ??= new ArgvInput();
$output ??= new ConsoleOutput();
if ($input->hasParameterOption(['--user', '-u'])) {
$user = User::by_name($input->getOption('user'));
if (is_null($user)) {
die("Unknown user");
} else {
send_event(new UserLoginEvent($user));
}
}
$log_level = SCORE_LOG_WARNING;
if (true === $input->hasParameterOption(['--quiet', '-q'], true)) {
$log_level = SCORE_LOG_ERROR;
} else {
if ($input->hasParameterOption('-vvv', true) || $input->hasParameterOption('--verbose=3', true) || 3 === $input->getParameterOption('--verbose', false, true)) {
$log_level = SCORE_LOG_DEBUG;
} elseif ($input->hasParameterOption('-vv', true) || $input->hasParameterOption('--verbose=2', true) || 2 === $input->getParameterOption('--verbose', false, true)) {
$log_level = SCORE_LOG_DEBUG;
} elseif ($input->hasParameterOption('-v', true) || $input->hasParameterOption('--verbose=1', true) || $input->hasParameterOption('--verbose', true) || $input->getParameterOption('--verbose', false, true)) {
$log_level = SCORE_LOG_INFO;
}
}
if (!defined("CLI_LOG_LEVEL")) {
define("CLI_LOG_LEVEL", $log_level);
}
return parent::run($input, $output);
}
}

View file

@ -200,73 +200,12 @@ class PageRequestEvent extends Event
} }
/** class CliGenEvent extends Event
* Sent when index.php is called from the command line
*/
class CommandEvent extends Event
{ {
public string $cmd = "help"; public function __construct(
public \Symfony\Component\Console\Application $app
/** ) {
* @var string[]
*/
public array $args = [];
/**
* @param string[] $args
*/
public function __construct(array $args)
{
parent::__construct(); parent::__construct();
global $user;
$opts = [];
$log_level = SCORE_LOG_WARNING;
$arg_count = count($args);
for ($i = 1; $i < $arg_count; $i++) {
switch ($args[$i]) {
case '-u':
$user = User::by_name($args[++$i]);
if (is_null($user)) {
die("Unknown user");
} else {
send_event(new UserLoginEvent($user));
}
break;
case '-q':
$log_level += 10;
break;
case '-v':
$log_level -= 10;
break;
default:
$opts[] = $args[$i];
break;
}
}
if (!defined("CLI_LOG_LEVEL")) {
define("CLI_LOG_LEVEL", $log_level);
}
if (count($opts) > 0) {
$this->cmd = $opts[0];
$this->args = array_slice($opts, 1);
} else {
print "\n";
print "Usage: php {$args[0]} [flags] [command]\n";
print "\n";
print "Flags:\n";
print "\t-u [username]\n";
print "\t\tLog in as the specified user\n";
print "\t-q / -v\n";
print "\t\tBe quieter / more verbose\n";
print "\t\tScale is debug - info - warning - error - critical\n";
print "\t\tDefault is to show warnings and above\n";
print "\n";
print "Currently known commands:\n";
}
} }
} }

View file

@ -4,6 +4,10 @@ declare(strict_types=1);
namespace Shimmie2; namespace Shimmie2;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\{InputInterface,InputArgument};
use Symfony\Component\Console\Output\OutputInterface;
/** /**
* Sent when the admin page is ready to be added to * Sent when the admin page is ready to be added to
*/ */
@ -65,67 +69,90 @@ class AdminPage extends Extension
} }
} }
public function onCommand(CommandEvent $event) public function onCliGen(CliGenEvent $event)
{ {
if ($event->cmd == "help") { $event->app->register('page:get')
print "\tget-page <query string>\n"; ->addArgument('query', InputArgument::REQUIRED)
print "\t\teg 'get-page post/list'\n\n"; ->addArgument('args', InputArgument::OPTIONAL)
print "\tpost-page <query string> <urlencoded params>\n"; ->setDescription('Get a page, eg /post/list')
print "\t\teg 'post-page ip_ban/delete id=1'\n\n"; ->setCode(function (InputInterface $input, OutputInterface $output): int {
print "\tget-token\n"; global $page;
print "\t\tget a CSRF auth token\n\n"; $query = $input->getArgument('query');
print "\tregen-thumb <id / hash>\n"; $args = $input->getArgument('args');
print "\t\tregenerate a thumbnail\n\n"; $_SERVER['REQUEST_URI'] = $query;
print "\tcache [get|set|del] [key] <value>\n"; if (!is_null($args)) {
print "\t\teg 'cache get config'\n\n"; parse_str($args, $_GET);
} $_SERVER['REQUEST_URI'] .= "?" . $args;
if ($event->cmd == "get-page") { }
global $page; send_event(new PageRequestEvent("GET", $query));
$_SERVER['REQUEST_URI'] = $event->args[0]; $page->display();
if (isset($event->args[1])) { return Command::SUCCESS;
parse_str($event->args[1], $_GET); });
$_SERVER['REQUEST_URI'] .= "?" . $event->args[1]; $event->app->register('page:post')
} ->addArgument('query', InputArgument::REQUIRED)
send_event(new PageRequestEvent("GET", $event->args[0])); ->addArgument('args', InputArgument::OPTIONAL)
$page->display(); ->setDescription('Post a page, eg ip_ban/delete id=1')
} ->setCode(function (InputInterface $input, OutputInterface $output): int {
if ($event->cmd == "post-page") { global $page;
global $page; $query = $input->getArgument('query');
if (isset($event->args[1])) { $args = $input->getArgument('args');
parse_str($event->args[1], $_POST); global $page;
} if (!is_null($args)) {
send_event(new PageRequestEvent("POST", $event->args[0])); parse_str($args, $_POST);
$page->display(); }
} send_event(new PageRequestEvent("POST", $query));
if ($event->cmd == "get-token") { $page->display();
global $user; return Command::SUCCESS;
print($user->get_auth_token()); });
} $event->app->register('get-token')
if ($event->cmd == "regen-thumb") { ->setDescription('Get a CSRF token')
$uid = $event->args[0]; ->setCode(function (InputInterface $input, OutputInterface $output): int {
$image = Image::by_id_or_hash($uid); global $user;
if ($image) { $output->writeln($user->get_auth_token());
send_event(new ThumbnailGenerationEvent($image, true)); return Command::SUCCESS;
} else { });
print("No post with ID '$uid'\n"); $event->app->register('regen-thumb')
} ->addArgument('id_or_hash', InputArgument::REQUIRED)
} ->setDescription("Regenerate a post's thumbnail")
if ($event->cmd == "cache") { ->setCode(function (InputInterface $input, OutputInterface $output): int {
global $cache; $uid = $input->getArgument('id_or_hash');
$cmd = $event->args[0]; $image = Image::by_id_or_hash($uid);
$key = $event->args[1]; if ($image) {
switch ($cmd) { send_event(new ThumbnailGenerationEvent($image, true));
case "get": } else {
var_export($cache->get($key)); $output->writeln("No post with ID '$uid'\n");
break; }
case "set": return Command::SUCCESS;
$cache->set($key, $event->args[2], 60); });
break; $event->app->register('cache:get')
case "del": ->addArgument('key', InputArgument::REQUIRED)
$cache->delete($key); ->setDescription("Get a cache value")
break; ->setCode(function (InputInterface $input, OutputInterface $output): int {
} global $cache;
} $key = $input->getArgument('key');
$output->writeln(var_export($cache->get($key), true));
return Command::SUCCESS;
});
$event->app->register('cache:set')
->addArgument('key', InputArgument::REQUIRED)
->addArgument('value', InputArgument::REQUIRED)
->setDescription("Set a cache value")
->setCode(function (InputInterface $input, OutputInterface $output): int {
global $cache;
$key = $input->getArgument('key');
$value = $input->getArgument('value');
$cache->set($key, $value, 60);
return Command::SUCCESS;
});
$event->app->register('cache:del')
->addArgument('key', InputArgument::REQUIRED)
->setDescription("Delete a cache value")
->setCode(function (InputInterface $input, OutputInterface $output): int {
global $cache;
$key = $input->getArgument('key');
$cache->delete($key);
return Command::SUCCESS;
});
} }
public function onAdminBuilding(AdminBuildingEvent $event) public function onAdminBuilding(AdminBuildingEvent $event)

View file

@ -23,19 +23,4 @@ class AdminPageTest extends ShimmiePHPUnitTestCase
$this->assertEquals(200, $page->code); $this->assertEquals(200, $page->code);
$this->assertEquals("Admin Tools", $page->title); $this->assertEquals("Admin Tools", $page->title);
} }
public function testCommands()
{
send_event(new UserLoginEvent(User::by_name(self::$admin_name)));
ob_start();
send_event(new CommandEvent(["index.php", "help"]));
send_event(new CommandEvent(["index.php", "get-page", "post/list"]));
send_event(new CommandEvent(["index.php", "post-page", "post/list", "foo=bar"]));
send_event(new CommandEvent(["index.php", "get-token"]));
send_event(new CommandEvent(["index.php", "regen-thumb", "42"]));
ob_end_clean();
// don't crash
$this->assertTrue(true);
}
} }

View file

@ -4,6 +4,10 @@ declare(strict_types=1);
namespace Shimmie2; namespace Shimmie2;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\{InputInterface,InputArgument};
use Symfony\Component\Console\Output\OutputInterface;
class BulkActionException extends SCoreException class BulkActionException extends SCoreException
{ {
} }
@ -97,22 +101,20 @@ class BulkActions extends Extension
} }
} }
public function onCommand(CommandEvent $event) public function onCliGen(CliGenEvent $event)
{ {
if ($event->cmd == "help") { $event->app->register('bulk-action')
print "\tbulk-action <action> <query>\n"; ->addArgument('action', InputArgument::REQUIRED)
print "\t\tperform an action on all query results\n\n"; ->addArgument('query', InputArgument::REQUIRED)
} ->setDescription('Perform a bulk action on a given query')
if ($event->cmd == "bulk-action") { ->setCode(function (InputInterface $input, OutputInterface $output): int {
if (count($event->args) < 2) { $action = $input->getArgument('action');
return; $query = $input->getArgument('query');
} $items = $this->yield_search_results($query);
$action = $event->args[0]; log_info("bulk_actions", "Performing $action on $query");
$query = $event->args[1]; send_event(new BulkActionEvent($action, $items));
$items = $this->yield_search_results($query); return Command::SUCCESS;
log_info("bulk_actions", "Performing $action on {$event->args[1]}"); });
send_event(new BulkActionEvent($event->args[0], $items));
}
} }
public function onBulkAction(BulkActionEvent $event) public function onBulkAction(BulkActionEvent $event)

View file

@ -4,6 +4,10 @@ declare(strict_types=1);
namespace Shimmie2; namespace Shimmie2;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\{InputInterface,InputArgument};
use Symfony\Component\Console\Output\OutputInterface;
class BulkAddEvent extends Event class BulkAddEvent extends Event
{ {
public string $dir; public string $dir;
@ -35,25 +39,23 @@ class BulkAdd extends Extension
} }
} }
public function onCommand(CommandEvent $event) public function onCliGen(CliGenEvent $event)
{ {
if ($event->cmd == "help") { $event->app->register('bulk-add')
print "\tbulk-add [directory]\n"; ->addArgument('directory', InputArgument::REQUIRED)
print "\t\tImport this directory\n\n"; ->setDescription('Import a directory of images')
} ->setCode(function (InputInterface $input, OutputInterface $output): int {
if ($event->cmd == "bulk-add") { $dir = $input->getArgument('directory');
if (count($event->args) == 1) { $bae = send_event(new BulkAddEvent($dir));
$bae = send_event(new BulkAddEvent($event->args[0]));
foreach ($bae->results as $r) { foreach ($bae->results as $r) {
if(is_a($r, UploadError::class)) { if(is_a($r, UploadError::class)) {
print($r->name." failed: ".$r->error."\n"); $output->writeln($r->name." failed: ".$r->error);
} else { } else {
print($r->name." ok\n"); $output->writeln($r->name." ok");
} }
} }
print(implode("\n", $bae->results)); return Command::SUCCESS;
} });
}
} }
public function onAdminBuilding(AdminBuildingEvent $event) public function onAdminBuilding(AdminBuildingEvent $event)

View file

@ -4,6 +4,10 @@ declare(strict_types=1);
namespace Shimmie2; namespace Shimmie2;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\{InputInterface,InputArgument};
use Symfony\Component\Console\Output\OutputInterface;
class BulkAddCSV extends Extension class BulkAddCSV extends Extension
{ {
/** @var BulkAddCSVTheme */ /** @var BulkAddCSVTheme */
@ -21,23 +25,22 @@ class BulkAddCSV extends Extension
} }
} }
public function onCommand(CommandEvent $event) public function onCliGen(CliGenEvent $event)
{ {
if ($event->cmd == "help") { $event->app->register('bulk-add-csv')
print "\tbulk-add-csv [/path/to.csv]\n"; ->addArgument('path-to-csv', InputArgument::REQUIRED)
print "\t\tImport this .csv file (refer to documentation)\n\n"; ->setDescription('Import posts from a given CSV file')
} ->setCode(function (InputInterface $input, OutputInterface $output): int {
if ($event->cmd == "bulk-add-csv") { global $user;
global $user; if (!$user->can(Permissions::BULK_ADD)) {
$output->writeln("Not running as an admin, which can cause problems.");
$output->writeln("Please add the parameter: -u admin_username");
return Command::FAILURE;
}
//Nag until CLI is admin by default $this->add_csv($input->getArgument('path-to-csv'));
if (!$user->can(Permissions::BULK_ADD)) { return Command::SUCCESS;
print "Not running as an admin, which can cause problems.\n"; });
print "Please add the parameter: -u admin_username";
} elseif (count($event->args) == 1) {
$this->add_csv($event->args[0]);
}
}
} }
public function onAdminBuilding(AdminBuildingEvent $event) public function onAdminBuilding(AdminBuildingEvent $event)

View file

@ -4,6 +4,10 @@ declare(strict_types=1);
namespace Shimmie2; namespace Shimmie2;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\{InputInterface,InputArgument};
use Symfony\Component\Console\Output\OutputInterface;
class ET extends Extension class ET extends Extension
{ {
/** @var ETTheme */ /** @var ETTheme */
@ -37,15 +41,14 @@ class ET extends Extension
} }
} }
public function onCommand(CommandEvent $event) public function onCliGen(CliGenEvent $event)
{ {
if ($event->cmd == "help") { $event->app->register('info')
print "\tinfo\n"; ->setDescription('List a bunch of info')
print "\t\tList a bunch of info\n\n"; ->setCode(function (InputInterface $input, OutputInterface $output): int {
} print($this->to_yaml($this->get_info()));
if ($event->cmd == "info") { return Command::SUCCESS;
print($this->to_yaml($this->get_info())); });
}
} }
/** /**

View file

@ -4,6 +4,10 @@ declare(strict_types=1);
namespace Shimmie2; namespace Shimmie2;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\{InputInterface,InputArgument};
use Symfony\Component\Console\Output\OutputInterface;
class ExtensionAuthor class ExtensionAuthor
{ {
public string $name; public string $name;
@ -60,15 +64,14 @@ class ExtManager extends Extension
} }
} }
public function onCommand(CommandEvent $event) public function onCliGen(CliGenEvent $event)
{ {
if ($event->cmd == "help") { $event->app->register('disable-all-ext')
print "\tdisable-all-ext\n"; ->setDescription('Disable all extensions')
print "\t\tdisable all extensions\n\n"; ->setCode(function (InputInterface $input, OutputInterface $output): int {
} $this->write_config([]);
if ($event->cmd == "disable-all-ext") { return Command::SUCCESS;
$this->write_config([]); });
}
} }
public function onPageSubNavBuilding(PageSubNavBuildingEvent $event) public function onPageSubNavBuilding(PageSubNavBuildingEvent $event)

View file

@ -4,6 +4,10 @@ declare(strict_types=1);
namespace Shimmie2; namespace Shimmie2;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\{InputInterface,InputArgument};
use Symfony\Component\Console\Output\OutputInterface;
use GraphQL\GraphQL as GQL; use GraphQL\GraphQL as GQL;
use GraphQL\Server\StandardServer; use GraphQL\Server\StandardServer;
use GraphQL\Error\DebugFlag; use GraphQL\Error\DebugFlag;
@ -173,29 +177,32 @@ class GraphQL extends Extension
return ["image_ids" => array_map(fn ($im) => $im->id, $event->images)]; return ["image_ids" => array_map(fn ($im) => $im->id, $event->images)];
} }
public function onCommand(CommandEvent $event) public function onCliGen(CliGenEvent $event)
{ {
if ($event->cmd == "help") { $event->app->register('graphql:query')
print "\tgraphql <query string>\n"; ->addArgument('query', InputArgument::REQUIRED)
print "\t\teg 'graphql \"{ post(id: 18) { id, hash } }\"'\n\n"; ->setDescription('Run a GraphQL query')
print "\tgraphql-schema\n"; ->setCode(function (InputInterface $input, OutputInterface $output): int {
print "\t\tdump the schema\n\n"; $query = $input->getArgument('query');
} $t1 = ftime();
if ($event->cmd == "graphql") { $schema = $this->get_schema();
$t1 = ftime(); $t2 = ftime();
$schema = $this->get_schema(); $debug = DebugFlag::INCLUDE_DEBUG_MESSAGE | DebugFlag::RETHROW_INTERNAL_EXCEPTIONS;
$t2 = ftime(); $body = GQL::executeQuery($schema, $query)->toArray($debug);
$debug = DebugFlag::INCLUDE_DEBUG_MESSAGE | DebugFlag::RETHROW_INTERNAL_EXCEPTIONS; $t3 = ftime();
$body = GQL::executeQuery($schema, $event->args[0])->toArray($debug); $body['stats'] = get_debug_info_arr();
$t3 = ftime(); $body['stats']['graphql_schema_time'] = round($t2 - $t1, 2);
$body['stats'] = get_debug_info_arr(); $body['stats']['graphql_execute_time'] = round($t3 - $t2, 2);
$body['stats']['graphql_schema_time'] = round($t2 - $t1, 2); echo \json_encode($body, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
$body['stats']['graphql_execute_time'] = round($t3 - $t2, 2); return Command::SUCCESS;
echo \json_encode($body, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); });
} $event->app->register('graphql:schema')
if ($event->cmd == "graphql-schema") { ->addArgument('query', InputArgument::REQUIRED)
$schema = $this->get_schema(); ->setDescription('Print out the GraphQL schema')
echo(SchemaPrinter::doPrint($schema)); ->setCode(function (InputInterface $input, OutputInterface $output): int {
} $schema = $this->get_schema();
echo(SchemaPrinter::doPrint($schema));
return Command::SUCCESS;
});
} }
} }

View file

@ -4,6 +4,10 @@ declare(strict_types=1);
namespace Shimmie2; namespace Shimmie2;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\{InputInterface,InputArgument};
use Symfony\Component\Console\Output\OutputInterface;
require_once "config.php"; require_once "config.php";
/** /**
@ -118,17 +122,17 @@ class ImageIO extends Extension
} }
} }
public function onCommand(CommandEvent $event) public function onCliGen(CliGenEvent $event)
{ {
if ($event->cmd == "help") { $event->app->register('delete')
print "\tdelete <post id>\n"; ->addArgument('id', InputArgument::REQUIRED)
print "\t\tdelete a specific post\n\n"; ->setDescription('Delete a specific post')
} ->setCode(function (InputInterface $input, OutputInterface $output): int {
if ($event->cmd == "delete") { $post_id = (int)$input->getArgument('id');
$post_id = (int)$event->args[0]; $image = Image::by_id($post_id);
$image = Image::by_id($post_id); send_event(new ImageDeletionEvent($image));
send_event(new ImageDeletionEvent($image)); return Command::SUCCESS;
} });
} }
public function onImageAddition(ImageAdditionEvent $event) public function onImageAddition(ImageAdditionEvent $event)

View file

@ -4,6 +4,10 @@ declare(strict_types=1);
namespace Shimmie2; namespace Shimmie2;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\{InputInterface,InputArgument};
use Symfony\Component\Console\Output\OutputInterface;
require_once "config.php"; require_once "config.php";
require_once "events.php"; require_once "events.php";
@ -147,20 +151,19 @@ class Index extends Extension
} }
} }
public function onCommand(CommandEvent $event) public function onCliGen(CliGenEvent $event)
{ {
if ($event->cmd == "help") { $event->app->register('search')
# TODO: --fields a,b,c ->addArgument('query', InputArgument::REQUIRED)
print "\tsearch <query>\n"; ->setDescription('Search the database and print results')
print "\t\tsearch the database and print results\n\n"; ->setCode(function (InputInterface $input, OutputInterface $output): int {
} $query = Tag::explode($input->getArgument('query'));
if ($event->cmd == "search") { $items = Search::find_images(limit: 1000, tags: $query);
$query = count($event->args) > 0 ? Tag::explode($event->args[0]) : []; foreach ($items as $item) {
$items = Search::find_images(limit: 1000, tags: $query); $output->writeln($item->hash);
foreach ($items as $item) { }
print("{$item->hash}\n"); return Command::SUCCESS;
} });
}
} }
public function onSearchTermParse(SearchTermParseEvent $event) public function onSearchTermParse(SearchTermParseEvent $event)

View file

@ -4,6 +4,10 @@ declare(strict_types=1);
namespace Shimmie2; namespace Shimmie2;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\{InputInterface,InputArgument};
use Symfony\Component\Console\Output\OutputInterface;
require_once "config.php"; require_once "config.php";
require_once "events.php"; require_once "events.php";
require_once "media_engine.php"; require_once "media_engine.php";
@ -147,22 +151,22 @@ class Media extends Extension
} }
} }
public function onCommand(CommandEvent $event) public function onCliGen(CliGenEvent $event)
{ {
if ($event->cmd == "help") { $event->app->register('media-rescan')
print "\tmedia-rescan <id / hash>\n"; ->addArgument('id_or_hash', InputArgument::REQUIRED)
print "\t\trefresh metadata for a given post\n\n"; ->setDescription('Refresh metadata for a given post')
} ->setCode(function (InputInterface $input, OutputInterface $output): int {
if ($event->cmd == "media-rescan") { $uid = $input->getArgument('id_or_hash');
$uid = $event->args[0]; $image = Image::by_id_or_hash($uid);
$image = Image::by_id_or_hash($uid); if ($image) {
if ($image) { send_event(new MediaCheckPropertiesEvent($image));
send_event(new MediaCheckPropertiesEvent($image)); $image->save_to_db();
$image->save_to_db(); } else {
} else { $output->writeln("No post with ID '$uid'");
print("No post with ID '$uid'\n"); }
} return Command::SUCCESS;
} });
} }
/** /**

View file

@ -4,6 +4,11 @@ declare(strict_types=1);
namespace Shimmie2; namespace Shimmie2;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\{InputInterface,InputArgument};
use Symfony\Component\Console\Output\OutputInterface;
use function MicroHTML\{emptyHTML, A}; use function MicroHTML\{emptyHTML, A};
if ( if (
@ -71,15 +76,20 @@ class Rule34 extends Extension
} }
} }
public function onCommand(CommandEvent $event) public function onCliGen(CliGenEvent $event)
{ {
global $cache; $event->app->register('wipe-thumb-cache')
if ($event->cmd == "wipe-thumb-cache") { ->addArgument('tags', InputArgument::REQUIRED)
foreach (Search::find_images_iterable(0, null, Tag::explode($event->args[0])) as $image) { ->setDescription('Delete cached thumbnails for images matching the given tags')
print($image->id . "\n"); ->setCode(function (InputInterface $input, OutputInterface $output): int {
$cache->delete("thumb-block:{$image->id}"); global $cache;
} $tags = Tag::explode($input->getArgument('tags'));
} foreach (Search::find_images_iterable(0, null, $tags) as $image) {
$output->writeln($image->id);
$cache->delete("thumb-block:{$image->id}");
}
return Command::SUCCESS;
});
} }
public function onSourceSet(SourceSetEvent $event) public function onSourceSet(SourceSetEvent $event)

View file

@ -4,6 +4,10 @@ declare(strict_types=1);
namespace Shimmie2; namespace Shimmie2;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\{InputInterface,InputArgument};
use Symfony\Component\Console\Output\OutputInterface;
use function MicroHTML\INPUT; use function MicroHTML\INPUT;
require_once "config.php"; require_once "config.php";
@ -37,56 +41,54 @@ class S3 extends Extension
} }
} }
public function onCommand(CommandEvent $event) public function onCliGen(CliGenEvent $event)
{ {
global $database; $event->app->register('s3:process')
if ($event->cmd == "help") { ->setDescription('Process the S3 queue')
print "\ts3-sync <post id>\n"; ->setCode(function (InputInterface $input, OutputInterface $output): int {
print "\t\tsync a post to s3\n\n"; global $database;
print "\ts3-rm <hash>\n";
print "\t\tdelete a leftover file from s3\n\n";
}
if ($event->cmd == "s3-sync") {
if(count($event->args) == 0) {
$count = $database->get_one("SELECT COUNT(*) FROM s3_sync_queue"); $count = $database->get_one("SELECT COUNT(*) FROM s3_sync_queue");
print("{$count} items in queue\n"); $output->writeln("{$count} items in queue");
foreach($database->get_all("SELECT * FROM s3_sync_queue ORDER BY time ASC") as $row) { foreach($database->get_all("SELECT * FROM s3_sync_queue ORDER BY time ASC") as $row) {
if($row['action'] == "S") { if($row['action'] == "S") {
$image = Image::by_hash($row['hash']); $image = Image::by_hash($row['hash']);
print("SYN {$row['hash']} ($image->id)\n"); $output->writeln("SYN {$row['hash']} ($image->id)");
$this->sync_post($image); $this->sync_post($image);
} elseif($row['action'] == "D") { } elseif($row['action'] == "D") {
print("DEL {$row['hash']}\n"); $output->writeln("DEL {$row['hash']}");
$this->remove_file($row['hash']); $this->remove_file($row['hash']);
} else { } else {
print("??? {$row['hash']} ({$row['action']})\n"); $output->writeln("??? {$row['hash']} ({$row['action']})");
} }
} }
} else { return Command::SUCCESS;
if (preg_match('/^(\d+)-(\d+)$/', $event->args[0], $matches)) { });
$start = (int)$matches[1]; $event->app->register('s3:sync')
$end = (int)$matches[2]; ->addArgument('start', InputArgument::REQUIRED)
} else { ->addArgument('end', InputArgument::REQUIRED)
$start = (int)$event->args[0]; ->setDescription('Sync a range of images to S3')
$end = $start; ->setCode(function (InputInterface $input, OutputInterface $output): int {
} $start = (int)$input->getArgument('start');
$end = (int)$input->getArgument('end');
$output->writeln("Syncing range: $start - $end");
foreach(Search::find_images_iterable(tags: ["order=id", "id>=$start", "id<=$end"]) as $image) { foreach(Search::find_images_iterable(tags: ["order=id", "id>=$start", "id<=$end"]) as $image) {
if($this->sync_post($image)) { if($this->sync_post($image)) {
print("{$image->id}: {$image->hash}\n"); print("{$image->id}: {$image->hash}\n");
} else { } else {
print("{$image->id}: {$image->hash} (skipped)\n"); print("{$image->id}: {$image->hash} (skipped)\n");
} }
ob_flush();
} }
} return Command::SUCCESS;
} });
if ($event->cmd == "s3-rm") { $event->app->register('s3:rm')
foreach($event->args as $hash) { ->addArgument('hash', InputArgument::REQUIRED)
print("{$hash}\n"); ->setDescription('Delete a leftover file from S3')
ob_flush(); ->setCode(function (InputInterface $input, OutputInterface $output): int {
$hash = $input->getArgument('hash');
$output->writeln("Deleting file: '$hash'");
$this->remove_file($hash); $this->remove_file($hash);
} return Command::SUCCESS;
} });
} }
public function onPageRequest(PageRequestEvent $event) public function onPageRequest(PageRequestEvent $event)

View file

@ -4,6 +4,10 @@ declare(strict_types=1);
namespace Shimmie2; namespace Shimmie2;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\{InputInterface,InputArgument};
use Symfony\Component\Console\Output\OutputInterface;
require_once "config.php"; require_once "config.php";
/* /*
@ -420,26 +424,26 @@ class Setup extends Extension
log_warning("setup", "Cache cleared"); log_warning("setup", "Cache cleared");
} }
public function onCommand(CommandEvent $event) public function onCliGen(CliGenEvent $event)
{ {
if ($event->cmd == "help") { $event->app->register('config:get')
print "\tconfig [get|set] <args>\n"; ->addArgument('key', InputArgument::REQUIRED)
print "\t\teg 'config get db_version'\n\n"; ->setDescription('Get a config value')
} ->setCode(function (InputInterface $input, OutputInterface $output): int {
if ($event->cmd == "config") { global $config;
global $cache, $config; $output->writeln($config->get_string($input->getArgument('key')));
$cmd = $event->args[0]; return Command::SUCCESS;
$key = $event->args[1]; });
switch ($cmd) { $event->app->register('config:set')
case "get": ->addArgument('key', InputArgument::REQUIRED)
print($config->get_string($key) . "\n"); ->addArgument('value', InputArgument::REQUIRED)
break; ->setDescription('Set a config value')
case "set": ->setCode(function (InputInterface $input, OutputInterface $output): int {
$config->set_string($key, $event->args[2]); global $cache, $config;
break; $config->set_string($input->getArgument('key'), $input->getArgument('value'));
} $cache->delete("config");
$cache->delete("config"); return Command::SUCCESS;
} });
} }
public function onPageSubNavBuilding(PageSubNavBuildingEvent $event) public function onPageSubNavBuilding(PageSubNavBuildingEvent $event)

View file

@ -4,6 +4,10 @@ declare(strict_types=1);
namespace Shimmie2; namespace Shimmie2;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\{InputInterface,InputArgument};
use Symfony\Component\Console\Output\OutputInterface;
/* /*
* OwnerSetEvent: * OwnerSetEvent:
* $image_id * $image_id
@ -159,19 +163,19 @@ class TagEdit extends Extension
} }
} }
public function onCommand(CommandEvent $event) public function onCliGen(CliGenEvent $event)
{ {
global $database; $event->app->register('tag-replace')
if ($event->cmd == "help") { ->addArgument('old_tag', InputArgument::REQUIRED)
print "\ttag-replace <tag> <tag>\n"; ->addArgument('new_tag', InputArgument::REQUIRED)
print "\t\tmass replace tags\n\n"; ->setDescription('Mass edit tags')
} ->setCode(function (InputInterface $input, OutputInterface $output): int {
if ($event->cmd == "tag-replace") { $old_tag = $input->getArgument('old_tag');
print("Mass editing tags: '{$event->args[0]}' -> '{$event->args[1]}'\n"); $new_tag = $input->getArgument('new_tag');
if(count($event->args) == 2) { $output->writeln("Mass editing tags: '$old_tag' -> '$new_tag'");
$this->mass_tag_edit($event->args[0], $event->args[1], true); $this->mass_tag_edit($old_tag, $new_tag, true);
} return Command::SUCCESS;
} });
} }
// public function onPostListBuilding(PostListBuildingEvent $event) // public function onPostListBuilding(PostListBuildingEvent $event)

View file

@ -4,20 +4,23 @@ declare(strict_types=1);
namespace Shimmie2; namespace Shimmie2;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\{InputInterface,InputArgument};
use Symfony\Component\Console\Output\OutputInterface;
class Upgrade extends Extension class Upgrade extends Extension
{ {
public function onCommand(CommandEvent $event) public function onCliGen(CliGenEvent $event)
{ {
if ($event->cmd == "help") { $event->app->register('db-upgrade')
print "\tdb-upgrade\n"; ->setDescription('Run DB schema updates, if automatic updates are disabled')
print "\t\tRun DB schema updates, if automatic updates are disabled\n\n"; ->setCode(function (InputInterface $input, OutputInterface $output): int {
} $output->writeln("Running DB Upgrade");
if ($event->cmd == "db-upgrade") { global $database;
print("Running DB Upgrade\n"); $database->set_timeout(null); // These updates can take a little bit
global $database; send_event(new DatabaseUpgradeEvent());
$database->set_timeout(null); // These updates can take a little bit return Command::SUCCESS;
send_event(new DatabaseUpgradeEvent()); });
}
} }
public function onDatabaseUpgrade(DatabaseUpgradeEvent $event) public function onDatabaseUpgrade(DatabaseUpgradeEvent $event)

View file

@ -82,7 +82,9 @@ try {
if (PHP_SAPI === 'cli' || PHP_SAPI == 'phpdbg') { if (PHP_SAPI === 'cli' || PHP_SAPI == 'phpdbg') {
ob_end_flush(); ob_end_flush();
ob_implicit_flush(true); ob_implicit_flush(true);
send_event(new CommandEvent($argv)); $app = new CliApp();
send_event(new CliGenEvent($app));
$app->run();
} else { } else {
send_event(new PageRequestEvent($_SERVER['REQUEST_METHOD'], _get_query())); send_event(new PageRequestEvent($_SERVER['REQUEST_METHOD'], _get_query()));
$page->display(); $page->display();