console 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. #!/usr/bin/env php
  2. <?php
  3. declare(strict_types=1);
  4. use App\App\Container;
  5. use App\Domain\Auth\Role;
  6. use App\Domain\Auth\TokenHasher;
  7. use App\Domain\Auth\TokenIssuer;
  8. use App\Domain\Auth\TokenKind;
  9. use App\Infrastructure\Auth\ServiceTokenBootstrap;
  10. use App\Infrastructure\Auth\TokenRecord;
  11. use App\Infrastructure\Auth\TokenRepository;
  12. require __DIR__ . '/../vendor/autoload.php';
  13. $argv = $_SERVER['argv'] ?? [];
  14. $command = $argv[1] ?? null;
  15. $phinxBin = __DIR__ . '/../vendor/bin/phinx';
  16. $phinxConfig = __DIR__ . '/../config/phinx.php';
  17. $run = static function (string $phinxCommand) use ($phinxBin, $phinxConfig): never {
  18. $cmd = sprintf(
  19. '%s %s --configuration=%s',
  20. escapeshellarg($phinxBin),
  21. $phinxCommand,
  22. escapeshellarg($phinxConfig)
  23. );
  24. passthru($cmd, $exitCode);
  25. exit($exitCode);
  26. };
  27. /**
  28. * @param array<int, string> $argv
  29. */
  30. $flag = static function (array $argv, string $name): ?string {
  31. $prefix = '--' . $name . '=';
  32. foreach ($argv as $arg) {
  33. if (str_starts_with($arg, $prefix)) {
  34. return substr($arg, strlen($prefix));
  35. }
  36. }
  37. return null;
  38. };
  39. /**
  40. * @param array<int, string> $argv
  41. */
  42. $hasFlag = static function (array $argv, string $name): bool {
  43. return in_array('--' . $name, $argv, true);
  44. };
  45. switch ($command) {
  46. case 'db:migrate':
  47. $run('migrate');
  48. // no break
  49. case 'db:rollback':
  50. $run('rollback');
  51. // no break
  52. case 'db:seed':
  53. $run('seed:run');
  54. // no break
  55. case 'auth:bootstrap-service-token':
  56. $container = Container::build();
  57. /** @var ServiceTokenBootstrap $boot */
  58. $boot = $container->get(ServiceTokenBootstrap::class);
  59. /** @var string $rawToken */
  60. $rawToken = $container->get('settings.ui_service_token');
  61. $boot->bootstrap($rawToken);
  62. exit(0);
  63. case 'auth:create-token':
  64. $kindArg = $flag($argv, 'kind') ?? '';
  65. $roleArg = $flag($argv, 'role');
  66. $quiet = $hasFlag($argv, 'quiet');
  67. $kind = TokenKind::tryFrom($kindArg);
  68. if ($kind === null) {
  69. fwrite(STDERR, "Unknown --kind: {$kindArg}. Admin tokens are the only kind supported here.\n");
  70. exit(1);
  71. }
  72. if ($kind === TokenKind::Service) {
  73. fwrite(STDERR, "Refusing to create a service token via this command. Use UI_SERVICE_TOKEN + auth:bootstrap-service-token.\n");
  74. exit(1);
  75. }
  76. if ($kind === TokenKind::Reporter || $kind === TokenKind::Consumer) {
  77. fwrite(STDERR, "Reporter/consumer tokens are bound to records and are issued via M04 endpoints, not this CLI.\n");
  78. exit(1);
  79. }
  80. // kind=admin from here on.
  81. $role = $roleArg !== null ? Role::tryFrom(strtolower($roleArg)) : null;
  82. if ($role === null) {
  83. fwrite(STDERR, "Admin tokens require --role=viewer|operator|admin.\n");
  84. exit(1);
  85. }
  86. $container = Container::build();
  87. /** @var TokenIssuer $issuer */
  88. $issuer = $container->get(TokenIssuer::class);
  89. /** @var TokenHasher $hasher */
  90. $hasher = $container->get(TokenHasher::class);
  91. /** @var TokenRepository $repo */
  92. $repo = $container->get(TokenRepository::class);
  93. $raw = $issuer->issue(TokenKind::Admin);
  94. $hash = $hasher->hash($raw);
  95. $repo->create(new TokenRecord(
  96. id: null,
  97. kind: TokenKind::Admin,
  98. hash: $hash,
  99. prefix: substr($raw, 0, 8),
  100. reporterId: null,
  101. consumerId: null,
  102. role: $role,
  103. expiresAt: null,
  104. revokedAt: null,
  105. lastUsedAt: null,
  106. ));
  107. if ($quiet) {
  108. fwrite(STDOUT, $raw);
  109. } else {
  110. fwrite(STDOUT, $raw . "\n");
  111. fwrite(STDERR, "Created admin token (role={$role->value}). The token is only shown once.\n");
  112. }
  113. exit(0);
  114. case null:
  115. case '--help':
  116. case '-h':
  117. fwrite(STDOUT, <<<TXT
  118. Usage: console <command>
  119. Commands:
  120. db:migrate Run Phinx migrations
  121. db:rollback Roll back the most recent migration
  122. db:seed Run all seeders idempotently
  123. auth:bootstrap-service-token Provision UI_SERVICE_TOKEN row in api_tokens
  124. auth:create-token --kind=admin --role=admin|operator|viewer [--quiet]
  125. Create an admin token; raw token printed to stdout
  126. TXT);
  127. exit(0);
  128. default:
  129. fwrite(STDERR, "Unknown command: {$command}\n");
  130. exit(1);
  131. }