4
0

SshExport.razor 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. @page "/ssh/export"
  2. @using RackPeek.Domain.UseCases.SSH
  3. @inject SshConfigExportUseCase SshUseCase
  4. <div class="border border-zinc-800 rounded p-4 bg-zinc-900 max-w-5xl mx-auto"
  5. data-testid="ssh-export-page">
  6. <div class="flex justify-between items-center mb-4">
  7. <div class="text-zinc-100 text-lg">
  8. SSH Config Export
  9. </div>
  10. <button class="text-sm bg-emerald-600 hover:bg-emerald-500 px-3 py-1 rounded text-white transition"
  11. data-testid="generate-ssh-button"
  12. @onclick="GenerateExport">
  13. Generate
  14. </button>
  15. </div>
  16. <!-- Options -->
  17. <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
  18. <!-- Include Tags -->
  19. <div>
  20. <div class="text-zinc-400 mb-1">Include Tags</div>
  21. <input class="w-full bg-zinc-800 text-zinc-200 p-2 rounded border border-zinc-700"
  22. placeholder="prod, linux"
  23. data-testid="ssh-include-tags-input"
  24. @bind="_includeTagsRaw"/>
  25. <div class="text-xs text-zinc-500 mt-1">
  26. Only include resources with these tags (optional)
  27. </div>
  28. </div>
  29. <!-- Default SSH User -->
  30. <div>
  31. <div class="text-zinc-400 mb-1">Default SSH User</div>
  32. <input class="w-full bg-zinc-800 text-zinc-200 p-2 rounded border border-zinc-700"
  33. placeholder="ubuntu"
  34. data-testid="ssh-default-user-input"
  35. @bind="_defaultUser"/>
  36. <div class="text-xs text-zinc-500 mt-1">
  37. Used if ssh_user or ansible_user label is not defined
  38. </div>
  39. </div>
  40. <!-- Default SSH Port -->
  41. <div>
  42. <div class="text-zinc-400 mb-1">Default SSH Port</div>
  43. <input type="number"
  44. class="w-full bg-zinc-800 text-zinc-200 p-2 rounded border border-zinc-700"
  45. data-testid="ssh-default-port-input"
  46. @bind="_defaultPort"/>
  47. <div class="text-xs text-zinc-500 mt-1">
  48. Used if ssh_port or ansible_port label is not defined
  49. </div>
  50. </div>
  51. <!-- Default Identity File -->
  52. <div>
  53. <div class="text-zinc-400 mb-1">Default Identity File</div>
  54. <input class="w-full bg-zinc-800 text-zinc-200 p-2 rounded border border-zinc-700"
  55. placeholder="~/.ssh/id_rsa"
  56. data-testid="ssh-default-identity-input"
  57. @bind="_defaultIdentityFile"/>
  58. <div class="text-xs text-zinc-500 mt-1">
  59. Used if ssh_identity_file label is not defined
  60. </div>
  61. </div>
  62. </div>
  63. <!-- Warnings -->
  64. @if (_warnings.Any())
  65. {
  66. <div class="border border-red-700 bg-red-900/40 text-red-300 p-3 rounded mb-4"
  67. data-testid="ssh-warnings">
  68. <div class="font-semibold mb-1">Warnings</div>
  69. <ul class="list-disc ml-5 text-sm">
  70. @foreach (var warning in _warnings)
  71. {
  72. <li>@warning</li>
  73. }
  74. </ul>
  75. </div>
  76. }
  77. <!-- Output -->
  78. <div>
  79. <div class="text-zinc-400 mb-1">Generated SSH Config</div>
  80. <textarea class="w-full bg-black text-emerald-400 p-3 rounded border border-zinc-800 font-mono text-sm"
  81. rows="18"
  82. readonly
  83. data-testid="ssh-output">
  84. @_sshText
  85. </textarea>
  86. </div>
  87. </div>
  88. @code {
  89. private string _includeTagsRaw = string.Empty;
  90. private string? _defaultUser = "ubuntu";
  91. private int _defaultPort = 22;
  92. private string? _defaultIdentityFile = "~/.ssh/id_rsa";
  93. private string _sshText = string.Empty;
  94. private List<string> _warnings = new();
  95. private async Task GenerateExport()
  96. {
  97. try
  98. {
  99. _warnings.Clear();
  100. _sshText = string.Empty;
  101. var options = new SshExportOptions
  102. {
  103. IncludeTags = ParseCsv(_includeTagsRaw),
  104. DefaultUser = string.IsNullOrWhiteSpace(_defaultUser) ? null : _defaultUser,
  105. DefaultPort = _defaultPort,
  106. DefaultIdentityFile = string.IsNullOrWhiteSpace(_defaultIdentityFile)
  107. ? null
  108. : _defaultIdentityFile
  109. };
  110. var result = await SshUseCase.ExecuteAsync(options);
  111. if (result is null)
  112. {
  113. _warnings.Add("SSH export returned null.");
  114. return;
  115. }
  116. _sshText = result.ConfigText;
  117. _warnings = result.Warnings.ToList();
  118. }
  119. catch (Exception ex)
  120. {
  121. _warnings.Clear();
  122. _warnings.Add($"Unexpected error: {ex.Message}");
  123. }
  124. }
  125. private static IReadOnlyList<string> ParseCsv(string raw)
  126. {
  127. if (string.IsNullOrWhiteSpace(raw))
  128. return [];
  129. return raw.Split(',', StringSplitOptions.RemoveEmptyEntries)
  130. .Select(x => x.Trim())
  131. .Where(x => !string.IsNullOrWhiteSpace(x))
  132. .ToArray();
  133. }
  134. }