reencryptColumn($table, $column, fn (string $value) => SignRSA::EncryptFunc($this->decryptRsa($value))); } foreach (self::APP_KEY_COLUMNS as [$table, $column]) { $failed += $this->reencryptColumn($table, $column, fn (string $value) => Crypt::encryptString(Crypt::decryptString($value))); } $failed += $this->reencryptJsonSetting('smtp', 'password'); $failed += $this->reencryptJsonSetting('turnstile', 'secret_key'); return $failed === 0 ? self::SUCCESS : self::FAILURE; } private function reencryptColumn(string $table, string $column, callable $reencrypt): int { if (!Schema::hasColumn($table, $column)) { $this->warn("{$table}.{$column}: column not found, skipped"); return 0; } $updated = 0; $failed = 0; DB::table($table) ->select(['id', $column]) ->whereNotNull($column) ->where($column, '!=', '') ->chunkById(200, function ($rows) use ($table, $column, $reencrypt, &$updated, &$failed) { foreach ($rows as $row) { try { $value = $reencrypt((string) $row->{$column}); } catch (Throwable) { $failed++; $this->warn("{$table}#{$row->id}.{$column}: cannot decrypt with current or previous key"); continue; } if (!$this->option('dry-run')) { DB::table($table)->where('id', $row->id)->update([$column => $value]); } $updated++; } }); $this->info("{$table}.{$column}: {$updated} re-encrypted, {$failed} failed"); return $failed; } private function reencryptJsonSetting(string $key, string $field): int { $setting = DB::table('setting')->where('key', $key)->first(); $value = $setting ? json_decode((string) $setting->value, true) : null; if (empty($value[$field])) { return 0; } try { $value[$field] = SignRSA::EncryptFunc($this->decryptRsa($value[$field])); } catch (Throwable) { $this->warn("setting[{$key}].{$field}: cannot decrypt with current or previous key"); return 1; } if (!$this->option('dry-run')) { DB::table('setting')->where('key', $key)->update(['value' => json_encode($value)]); } $this->info("setting[{$key}].{$field}: re-encrypted"); return 0; } private function decryptRsa(string $value): string { $plain = SignRSA::DecryptFunc($value); if ($plain === '') { throw new RuntimeException('RSA decrypt failed'); } return $plain; } }