|
| 1 | +@page "/" |
| 2 | +@rendermode InteractiveServer |
| 3 | +@inject ServerState State |
| 4 | +@implements IDisposable |
| 5 | + |
1 | 6 | <div class="container"> |
2 | 7 | <div class="header"> |
3 | 8 | <h1>ApacheMinaSSHD.NET</h1> |
|
13 | 18 | <label>Port:</label> |
14 | 19 | <input @bind="Port" type="number" /> |
15 | 20 | </div> |
16 | | - <button class="btn start" @onclick="StartServer" disabled="@(_server != null)"> |
| 21 | + <button class="btn start" @onclick="StartServer" disabled="@(State.Server is not null)"> |
17 | 22 | Start |
18 | 23 | </button> |
19 | | - <button class="btn stop" @onclick="StopServer" disabled="@(_server is null)"> |
| 24 | + <button class="btn stop" @onclick="StopServer" disabled="@(State.Server is null)"> |
20 | 25 | Stop |
21 | 26 | </button> |
22 | 27 | <span class="status @StatusClass">@StatusText</span> |
|
33 | 38 | </div> |
34 | 39 | <div class="panel"> |
35 | 40 | <h3>Active Sessions</h3> |
36 | | - @if (_sessions.Count == 0) |
| 41 | + @if (State.Sessions.Count == 0) |
37 | 42 | { |
38 | 43 | <p class="muted">No active sessions</p> |
39 | 44 | } |
|
42 | 47 | <table> |
43 | 48 | <thead><tr><th>ID</th><th>Remote</th><th>Since</th></tr></thead> |
44 | 49 | <tbody> |
45 | | - @foreach (var s in _sessions) |
| 50 | + @foreach (var s in State.Sessions) |
46 | 51 | { |
47 | 52 | <tr><td>@s.Id.ToString("N")[..8]</td><td>@s.RemoteAddress</td><td>@s.StartedAt</td></tr> |
48 | 53 | } |
|
53 | 58 | <div class="panel log-panel"> |
54 | 59 | <h3>Log</h3> |
55 | 60 | <div class="log-box"> |
56 | | - @foreach (var entry in _log) |
| 61 | + @foreach (var entry in State.Log) |
57 | 62 | { |
58 | 63 | <div class="log-entry">[@entry.Timestamp] @entry.Text</div> |
59 | 64 | } |
|
63 | 68 |
|
64 | 69 | <div class="footer"> |
65 | 70 | <span>ApacheMinaSSHD.NET by SERALYNX LLC</span> |
66 | | - <button class="btn" @onclick="ClearLog">Clear Log</button> |
| 71 | + <button class="btn clear-log" @onclick="ClearLog">Clear Log</button> |
67 | 72 | </div> |
68 | 73 | </div> |
69 | 74 |
|
|
86 | 91 | .btn.start:hover:not(:disabled) { background: #388e3c; } |
87 | 92 | .btn.stop { background: #c62828; color: #fff; } |
88 | 93 | .btn.stop:hover:not(:disabled) { background: #d32f2f; } |
| 94 | + .clear-log { font-size: .8rem; background: #333; color: #ccc; } |
| 95 | + .clear-log:hover { background: #444; } |
89 | 96 | .status { font-weight: 600; font-size: .85rem; margin-left: auto; } |
90 | 97 | .status.running { color: #66bb6a; } |
91 | 98 | .status.stopped { color: #aaa; } |
|
103 | 110 | .log-entry { padding: 1px 4px; } |
104 | 111 | .log-entry:nth-child(odd) { background: #1e1e32; } |
105 | 112 | .footer { display: flex; justify-content: space-between; align-items: center; color: #888; font-size: .8rem; } |
106 | | - .footer .btn { font-size: .8rem; background: #333; color: #ccc; } |
107 | 113 | </style> |
108 | 114 |
|
109 | 115 | @code { |
110 | | - private ApacheMinaSSHD.NET.Wrapper.AMNetSshServer? _server; |
111 | | - private readonly List<LogEntry> _log = []; |
112 | | - private readonly List<SessionInfo> _sessions = []; |
113 | 116 | private string _statusText = "Stopped"; |
114 | | - private string _statusClass = "stopped"; |
| 117 | + private bool _statusRunning; |
115 | 118 |
|
116 | 119 | private string Host { get; set; } = "127.0.0.1"; |
117 | 120 | private string Port { get; set; } = "2222"; |
|
122 | 125 | private bool UseModernAlgos { get; set; } = true; |
123 | 126 |
|
124 | 127 | private string StatusText => _statusText; |
125 | | - private string StatusClass => _statusClass; |
| 128 | + private string StatusClass => _statusRunning ? "running" : "stopped"; |
| 129 | + |
| 130 | + protected override void OnInitialized() |
| 131 | + { |
| 132 | + State.Changed += OnStateChanged; |
| 133 | + } |
| 134 | + |
| 135 | + private void OnStateChanged() => InvokeAsync(StateHasChanged); |
126 | 136 |
|
127 | 137 | private void StartServer() |
128 | 138 | { |
129 | 139 | try |
130 | 140 | { |
131 | | - _server = ApacheMinaSSHD.NET.Wrapper.AMNetSshServer.SetUpDefaultServer(); |
132 | | - _server.Host = Host; |
133 | | - _server.Port = int.Parse(Port); |
| 141 | + State.Server = ApacheMinaSSHD.NET.Wrapper.AMNetSshServer.SetUpDefaultServer(); |
| 142 | + State.Server.Host = Host; |
| 143 | + State.Server.Port = int.Parse(Port); |
134 | 144 |
|
135 | | - if (UseProductionDefaults) _server.Config.ApplyProductionDefaults(); |
136 | | - if (UseModernAlgos) _server.Config.ApplyModernAlgorithmDefaults(); |
137 | | - _server.Config.WELCOME_BANNER = "ApacheMinaSSHD.NET — Photino Manager"; |
| 145 | + if (UseProductionDefaults) State.Server.Config.ApplyProductionDefaults(); |
| 146 | + if (UseModernAlgos) State.Server.Config.ApplyModernAlgorithmDefaults(); |
| 147 | + State.Server.Config.WELCOME_BANNER = "ApacheMinaSSHD.NET — Photino Manager"; |
138 | 148 |
|
139 | | - _server.SetFixedPasswordAuthenticator("admin", Password); |
140 | | - _server.setKeyPairProvider( |
| 149 | + State.Server.SetFixedPasswordAuthenticator("admin", Password); |
| 150 | + State.Server.setKeyPairProvider( |
141 | 151 | new ApacheMinaSSHD.NET.Wrapper.Factories.AMNetSimpleGeneratorHostKeyProvider(HostKey)); |
142 | 152 |
|
143 | | - EnsureDir(StorageRoot); |
144 | | - _server.setFileSystemFactory( |
| 153 | + if (!Directory.Exists(StorageRoot)) Directory.CreateDirectory(StorageRoot); |
| 154 | + State.Server.setFileSystemFactory( |
145 | 155 | new ApacheMinaSSHD.NET.Wrapper.Factories.AMNetVirtualFileSystemFactory(StorageRoot)); |
146 | 156 |
|
147 | | - _server.addSessionListener(new PhotinoSessionListener(this)); |
148 | | - _server.Start(); |
| 157 | + State.Server.setSubsystemFactories(new ApacheMinaSSHD.NET.Wrapper.Factories.AMNetSftpSubsystemFactory()); |
| 158 | + State.Server.addSessionListener(new PhotinoSessionListener(State)); |
| 159 | + State.Server.Start(); |
149 | 160 |
|
150 | 161 | _statusText = "Running"; |
151 | | - _statusClass = "running"; |
152 | | - Log("Server started"); |
153 | | - StateHasChanged(); |
| 162 | + _statusRunning = true; |
| 163 | + State.LogMessage("Server started"); |
154 | 164 | } |
155 | 165 | catch (Exception ex) |
156 | 166 | { |
157 | | - Log($"Start failed: {ex.Message}"); |
158 | | - StateHasChanged(); |
| 167 | + State.LogMessage($"Start failed: {ex.Message}"); |
159 | 168 | } |
160 | 169 | } |
161 | 170 |
|
162 | 171 | private void StopServer() |
163 | 172 | { |
164 | 173 | try |
165 | 174 | { |
166 | | - _server?.Stop(); |
167 | | - _server = null; |
168 | | - _sessions.Clear(); |
| 175 | + State.Server?.Stop(); |
| 176 | + State.Server = null; |
| 177 | + State.Sessions.Clear(); |
169 | 178 | _statusText = "Stopped"; |
170 | | - _statusClass = "stopped"; |
171 | | - Log("Server stopped"); |
172 | | - StateHasChanged(); |
| 179 | + _statusRunning = false; |
| 180 | + State.LogMessage("Server stopped"); |
173 | 181 | } |
174 | 182 | catch (Exception ex) |
175 | 183 | { |
176 | | - Log($"Stop error: {ex.Message}"); |
177 | | - StateHasChanged(); |
| 184 | + State.LogMessage($"Stop error: {ex.Message}"); |
178 | 185 | } |
179 | 186 | } |
180 | 187 |
|
181 | | - private void ClearLog() |
182 | | - { |
183 | | - _log.Clear(); |
184 | | - StateHasChanged(); |
185 | | - } |
186 | | - |
187 | | - internal void Log(string message) |
188 | | - { |
189 | | - _log.Add(new LogEntry(message)); |
190 | | - if (_log.Count > 500) _log.RemoveAt(0); |
191 | | - InvokeAsync(StateHasChanged); |
192 | | - } |
193 | | - |
194 | | - internal void AddSession(ApacheMinaSSHD.NET.Wrapper.Abstractions.Models.ISshSession session) |
195 | | - { |
196 | | - _sessions.Add(new SessionInfo(session)); |
197 | | - InvokeAsync(StateHasChanged); |
198 | | - } |
199 | | - |
200 | | - internal void RemoveSession(ApacheMinaSSHD.NET.Wrapper.Abstractions.Models.ISshSession session) |
201 | | - { |
202 | | - _sessions.RemoveAll(s => s.Id == session.SessionId); |
203 | | - InvokeAsync(StateHasChanged); |
204 | | - } |
205 | | - |
206 | | - private static void EnsureDir(string path) |
207 | | - { |
208 | | - if (!Directory.Exists(path)) Directory.CreateDirectory(path); |
209 | | - } |
210 | | - |
211 | | - public class LogEntry(string text) |
212 | | - { |
213 | | - public string Timestamp { get; } = DateTime.Now.ToString("HH:mm:ss"); |
214 | | - public string Text { get; } = text; |
215 | | - } |
| 188 | + private void ClearLog() => State.ClearLog(); |
216 | 189 |
|
217 | | - public class SessionInfo(ApacheMinaSSHD.NET.Wrapper.Abstractions.Models.ISshSession session) |
218 | | - { |
219 | | - public Guid Id => session.SessionId; |
220 | | - public string RemoteAddress => session.RemoteAddress ?? "unknown"; |
221 | | - public string StartedAt => DateTime.Now.ToString("HH:mm:ss"); |
222 | | - } |
| 190 | + public void Dispose() => State.Changed -= OnStateChanged; |
223 | 191 | } |
0 commit comments