[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",
"sabre/cache": "^2.0.1",
"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" : {

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

View file

@ -23,19 +23,4 @@ class AdminPageTest extends ShimmiePHPUnitTestCase
$this->assertEquals(200, $page->code);
$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;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\{InputInterface,InputArgument};
use Symfony\Component\Console\Output\OutputInterface;
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") {
print "\tbulk-action <action> <query>\n";
print "\t\tperform an action on all query results\n\n";
}
if ($event->cmd == "bulk-action") {
if (count($event->args) < 2) {
return;
}
$action = $event->args[0];
$query = $event->args[1];
$event->app->register('bulk-action')
->addArgument('action', InputArgument::REQUIRED)
->addArgument('query', InputArgument::REQUIRED)
->setDescription('Perform a bulk action on a given query')
->setCode(function (InputInterface $input, OutputInterface $output): int {
$action = $input->getArgument('action');
$query = $input->getArgument('query');
$items = $this->yield_search_results($query);
log_info("bulk_actions", "Performing $action on {$event->args[1]}");
send_event(new BulkActionEvent($event->args[0], $items));
}
log_info("bulk_actions", "Performing $action on $query");
send_event(new BulkActionEvent($action, $items));
return Command::SUCCESS;
});
}
public function onBulkAction(BulkActionEvent $event)

View file

@ -4,6 +4,10 @@ declare(strict_types=1);
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
{
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") {
print "\tbulk-add [directory]\n";
print "\t\tImport this directory\n\n";
}
if ($event->cmd == "bulk-add") {
if (count($event->args) == 1) {
$bae = send_event(new BulkAddEvent($event->args[0]));
$event->app->register('bulk-add')
->addArgument('directory', InputArgument::REQUIRED)
->setDescription('Import a directory of images')
->setCode(function (InputInterface $input, OutputInterface $output): int {
$dir = $input->getArgument('directory');
$bae = send_event(new BulkAddEvent($dir));
foreach ($bae->results as $r) {
if(is_a($r, UploadError::class)) {
print($r->name." failed: ".$r->error."\n");
$output->writeln($r->name." failed: ".$r->error);
} else {
print($r->name." ok\n");
}
}
print(implode("\n", $bae->results));
$output->writeln($r->name." ok");
}
}
return Command::SUCCESS;
});
}
public function onAdminBuilding(AdminBuildingEvent $event)

View file

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

View file

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

View file

@ -4,6 +4,10 @@ declare(strict_types=1);
namespace Shimmie2;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\{InputInterface,InputArgument};
use Symfony\Component\Console\Output\OutputInterface;
class ExtensionAuthor
{
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") {
print "\tdisable-all-ext\n";
print "\t\tdisable all extensions\n\n";
}
if ($event->cmd == "disable-all-ext") {
$event->app->register('disable-all-ext')
->setDescription('Disable all extensions')
->setCode(function (InputInterface $input, OutputInterface $output): int {
$this->write_config([]);
}
return Command::SUCCESS;
});
}
public function onPageSubNavBuilding(PageSubNavBuildingEvent $event)

View file

@ -4,6 +4,10 @@ declare(strict_types=1);
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\Server\StandardServer;
use GraphQL\Error\DebugFlag;
@ -173,29 +177,32 @@ class GraphQL extends Extension
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") {
print "\tgraphql <query string>\n";
print "\t\teg 'graphql \"{ post(id: 18) { id, hash } }\"'\n\n";
print "\tgraphql-schema\n";
print "\t\tdump the schema\n\n";
}
if ($event->cmd == "graphql") {
$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, $event->args[0])->toArray($debug);
$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);
}
if ($event->cmd == "graphql-schema") {
return Command::SUCCESS;
});
$event->app->register('graphql:schema')
->addArgument('query', InputArgument::REQUIRED)
->setDescription('Print out the GraphQL 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;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\{InputInterface,InputArgument};
use Symfony\Component\Console\Output\OutputInterface;
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") {
print "\tdelete <post id>\n";
print "\t\tdelete a specific post\n\n";
}
if ($event->cmd == "delete") {
$post_id = (int)$event->args[0];
$event->app->register('delete')
->addArgument('id', InputArgument::REQUIRED)
->setDescription('Delete a specific post')
->setCode(function (InputInterface $input, OutputInterface $output): int {
$post_id = (int)$input->getArgument('id');
$image = Image::by_id($post_id);
send_event(new ImageDeletionEvent($image));
}
return Command::SUCCESS;
});
}
public function onImageAddition(ImageAdditionEvent $event)

View file

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

View file

@ -4,6 +4,10 @@ declare(strict_types=1);
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 "events.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") {
print "\tmedia-rescan <id / hash>\n";
print "\t\trefresh metadata for a given post\n\n";
}
if ($event->cmd == "media-rescan") {
$uid = $event->args[0];
$event->app->register('media-rescan')
->addArgument('id_or_hash', InputArgument::REQUIRED)
->setDescription('Refresh metadata for a given post')
->setCode(function (InputInterface $input, OutputInterface $output): int {
$uid = $input->getArgument('id_or_hash');
$image = Image::by_id_or_hash($uid);
if ($image) {
send_event(new MediaCheckPropertiesEvent($image));
$image->save_to_db();
} else {
print("No post with ID '$uid'\n");
}
$output->writeln("No post with ID '$uid'");
}
return Command::SUCCESS;
});
}
/**

View file

@ -4,6 +4,11 @@ declare(strict_types=1);
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};
if (
@ -71,15 +76,20 @@ class Rule34 extends Extension
}
}
public function onCommand(CommandEvent $event)
public function onCliGen(CliGenEvent $event)
{
$event->app->register('wipe-thumb-cache')
->addArgument('tags', InputArgument::REQUIRED)
->setDescription('Delete cached thumbnails for images matching the given tags')
->setCode(function (InputInterface $input, OutputInterface $output): int {
global $cache;
if ($event->cmd == "wipe-thumb-cache") {
foreach (Search::find_images_iterable(0, null, Tag::explode($event->args[0])) as $image) {
print($image->id . "\n");
$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)

View file

@ -4,6 +4,10 @@ declare(strict_types=1);
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;
require_once "config.php";
@ -37,56 +41,54 @@ class S3 extends Extension
}
}
public function onCommand(CommandEvent $event)
public function onCliGen(CliGenEvent $event)
{
$event->app->register('s3:process')
->setDescription('Process the S3 queue')
->setCode(function (InputInterface $input, OutputInterface $output): int {
global $database;
if ($event->cmd == "help") {
print "\ts3-sync <post id>\n";
print "\t\tsync a post to s3\n\n";
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");
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) {
if($row['action'] == "S") {
$image = Image::by_hash($row['hash']);
print("SYN {$row['hash']} ($image->id)\n");
$output->writeln("SYN {$row['hash']} ($image->id)");
$this->sync_post($image);
} elseif($row['action'] == "D") {
print("DEL {$row['hash']}\n");
$output->writeln("DEL {$row['hash']}");
$this->remove_file($row['hash']);
} else {
print("??? {$row['hash']} ({$row['action']})\n");
$output->writeln("??? {$row['hash']} ({$row['action']})");
}
}
} else {
if (preg_match('/^(\d+)-(\d+)$/', $event->args[0], $matches)) {
$start = (int)$matches[1];
$end = (int)$matches[2];
} else {
$start = (int)$event->args[0];
$end = $start;
}
return Command::SUCCESS;
});
$event->app->register('s3:sync')
->addArgument('start', InputArgument::REQUIRED)
->addArgument('end', InputArgument::REQUIRED)
->setDescription('Sync a range of images to S3')
->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) {
if($this->sync_post($image)) {
print("{$image->id}: {$image->hash}\n");
} else {
print("{$image->id}: {$image->hash} (skipped)\n");
}
ob_flush();
}
}
}
if ($event->cmd == "s3-rm") {
foreach($event->args as $hash) {
print("{$hash}\n");
ob_flush();
return Command::SUCCESS;
});
$event->app->register('s3:rm')
->addArgument('hash', InputArgument::REQUIRED)
->setDescription('Delete a leftover file from S3')
->setCode(function (InputInterface $input, OutputInterface $output): int {
$hash = $input->getArgument('hash');
$output->writeln("Deleting file: '$hash'");
$this->remove_file($hash);
}
}
return Command::SUCCESS;
});
}
public function onPageRequest(PageRequestEvent $event)

View file

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

View file

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

View file

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

View file

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