reconfigure-user.php 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. #!/usr/bin/env php
  2. <?php
  3. declare(strict_types=1);
  4. require __DIR__ . '/_cli.php';
  5. $cliOptions = new class extends CliOptionsParser {
  6. public string $user;
  7. public string $key;
  8. public string $value;
  9. public bool $list;
  10. public bool $set;
  11. public bool $unset;
  12. public bool $valueStdin;
  13. public bool $force;
  14. public bool $showSecrets;
  15. public function __construct() {
  16. $this->addRequiredOption('user', (new CliOption('user')));
  17. $this->addOption('key', (new CliOption('key')));
  18. $this->addOption('value', (new CliOption('value')));
  19. $this->addOption('list', (new CliOption('list'))->withValueNone());
  20. $this->addOption('set', (new CliOption('set'))->withValueNone());
  21. $this->addOption('unset', (new CliOption('unset'))->withValueNone());
  22. $this->addOption('valueStdin', (new CliOption('value-stdin'))->withValueNone());
  23. $this->addOption('force', (new CliOption('force'))->withValueNone());
  24. $this->addOption('showSecrets', (new CliOption('show-secrets'))->withValueNone());
  25. parent::__construct();
  26. }
  27. };
  28. if (!empty($cliOptions->errors)) {
  29. fail('FreshRSS error: ' . array_shift($cliOptions->errors) . "\n" . $cliOptions->usage);
  30. }
  31. $hasList = $cliOptions->list;
  32. $hasSet = $cliOptions->set;
  33. $hasUnset = $cliOptions->unset;
  34. $hasStdin = $cliOptions->valueStdin;
  35. $force = $cliOptions->force;
  36. $showSecrets = $cliOptions->showSecrets;
  37. $hasKey = isset($cliOptions->key);
  38. $hasValue = isset($cliOptions->value);
  39. if ($hasList && ($hasSet || $hasUnset || $hasKey)) {
  40. fail('FreshRSS error: --list cannot be combined with --key, --set, or --unset' . "\n" . $cliOptions->usage);
  41. }
  42. if (!$hasList && !$hasKey) {
  43. fail('FreshRSS error: --list or --key is required' . "\n" . $cliOptions->usage);
  44. }
  45. if ($hasSet && $hasUnset) {
  46. fail('FreshRSS error: --set and --unset are mutually exclusive' . "\n" . $cliOptions->usage);
  47. }
  48. if ($hasSet && $hasValue && $hasStdin) {
  49. fail('FreshRSS error: --value and --value-stdin are mutually exclusive' . "\n" . $cliOptions->usage);
  50. }
  51. if ($hasSet && !$hasValue && !$hasStdin) {
  52. fail('FreshRSS error: --set requires --value or --value-stdin' . "\n" . $cliOptions->usage);
  53. }
  54. $username = cliInitUser($cliOptions->user);
  55. $userConf = FreshRSS_Context::userConf();
  56. function isSecretKey(string $key): bool {
  57. return (bool) preg_match('/(hash|key|password|token|secret)$/i', $key);
  58. }
  59. function formatValue(mixed $v): string {
  60. if (is_array($v)) {
  61. return (string) json_encode($v, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
  62. }
  63. if (is_bool($v)) {
  64. return $v ? 'true' : 'false';
  65. }
  66. if (!is_scalar($v)) {
  67. return '';
  68. }
  69. return (string) $v;
  70. }
  71. if ($hasList) {
  72. $allData = $userConf->toArray();
  73. ksort($allData);
  74. foreach ($allData as $k => $v) {
  75. $display = !$showSecrets && isSecretKey($k) && $v !== '' ? '***' : formatValue($v);
  76. echo $k, '=', $display, "\n";
  77. }
  78. done();
  79. }
  80. $key = $cliOptions->key;
  81. if ($key === '') {
  82. fail('FreshRSS error: --key cannot be empty' . "\n" . $cliOptions->usage);
  83. }
  84. if (!$hasSet && !$hasUnset) {
  85. if (!$userConf->hasParam($key)) {
  86. fail('FreshRSS error: key not found: ' . $key, 2);
  87. }
  88. echo formatValue($userConf->toArray()[$key] ?? null), "\n";
  89. done();
  90. }
  91. if ($hasUnset) {
  92. if (!$userConf->hasParam($key)) {
  93. fail('FreshRSS error: key not found: ' . $key, 2);
  94. }
  95. $userConf->_attribute($key, null);
  96. done($userConf->save());
  97. }
  98. if (!$userConf->hasParam($key) && !$force) {
  99. fail('FreshRSS error: unknown key "' . $key . '". Use --force to set it anyway, or use the "extensions" sub-key for extension-specific config.');
  100. }
  101. $rawValue = $hasStdin
  102. ? rtrim((string) stream_get_contents(STDIN), "\n\r")
  103. : $cliOptions->value;
  104. if ($userConf->hasParam($key)) {
  105. $existing = $userConf->toArray()[$key] ?? null;
  106. if (is_array($existing)) {
  107. fail('FreshRSS error: key "' . $key . '" is an array type and cannot be set via CLI');
  108. } elseif (is_bool($existing)) {
  109. $typed = filter_var($rawValue, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
  110. if ($typed === null) {
  111. fail('FreshRSS error: key "' . $key . '" expects a boolean value (true/false/1/0)');
  112. }
  113. } elseif (is_int($existing)) {
  114. if (!ctype_digit(ltrim($rawValue, '-'))) {
  115. fail('FreshRSS error: key "' . $key . '" expects an integer value');
  116. }
  117. $typed = (int) $rawValue;
  118. } else {
  119. $typed = $rawValue;
  120. }
  121. } else {
  122. $lower = strtolower($rawValue);
  123. if (in_array($lower, ['true', 'false'], true)) {
  124. $typed = $lower === 'true';
  125. } elseif ($rawValue !== '' && ctype_digit(ltrim($rawValue, '-'))) {
  126. $typed = (int) $rawValue;
  127. } else {
  128. $typed = $rawValue;
  129. }
  130. }
  131. $userConf->_attribute($key, $typed);
  132. done($userConf->save());