Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
SynchronizationResource.cs
1using System;
2using System.Collections.Generic;
3using System.Threading.Tasks;
4using Waher.Content;
11using Waher.Script;
12using Waher.Security;
13
15{
20 {
21 private readonly NeuroLedgerClient client;
22 private readonly string userVariable;
23 private readonly string[] userPrivileges;
24
32 public SynchronizationResource(string ResourceName, NeuroLedgerClient NLClient, string UserVariable, params string[] UserPrivileges)
33 : base(ResourceName)
34 {
35 this.client = NLClient;
36 this.userVariable = UserVariable;
37 this.userPrivileges = UserPrivileges;
38 }
39
43 public override bool HandlesSubPaths => false;
44
48 public override bool UserSessions => true;
49
53 public bool AllowsPOST => true;
54
61 public async Task POST(HttpRequest Request, HttpResponse Response)
62 {
63 if (Request.Session is null ||
64 !Request.Session.TryGetVariable(this.userVariable, out Variable v) ||
65 !(v.ValueObject is IUser User))
66 {
67 throw new ForbiddenException("User not authenticated.");
68 }
69
70 foreach (string Privilege in this.userPrivileges)
71 {
72 if (!User.HasPrivilege(Privilege))
73 throw new ForbiddenException("User not authorized.");
74 }
75
76 string TabID = Request.Header["X-TabID"];
77 if (string.IsNullOrEmpty(TabID))
78 throw new BadRequestException("Expected X-TabID header.");
79
80 string Command = Request.Header["X-Command"];
81 if (string.IsNullOrEmpty(Command))
82 throw new BadRequestException("Expected command.");
83
84 if (Gateway.XmppClient.State != XmppState.Connected)
85 throw new ConflictException("The Neuro-Ledger is not currently connected to the network. Synchronization cannot start.");
86
87 switch (Command)
88 {
89 case "START":
90 this.Synchronize(TabID);
91 break;
92
93 case "STOP":
94 await this.StopSynchronization(TabID);
95 break;
96
97 default:
98 throw new BadRequestException("Invalid command.");
99 }
100 }
101
102 private async void Synchronize(string TabID)
103 {
104 try
105 {
106 await ClientEvents.PushEvent(new string[] { TabID }, "AddContact", JSON.Encode(new KeyValuePair<string, object>[]
107 {
108 new KeyValuePair<string, object>("jid", this.client.Provider.ExternalIdentity),
109 new KeyValuePair<string, object>("name", this.client.Provider.ExternalIdentity)
110 }, false), true, this.userVariable, this.userPrivileges);
111
112 await ClientEvents.PushEvent(new string[] { TabID }, "ReportAvailability", JSON.Encode(new KeyValuePair<string, object>[]
113 {
114 new KeyValuePair<string, object>("jid", this.client.Provider.ExternalIdentity),
115 new KeyValuePair<string, object>("type", "Neuro-Ledger"),
116 new KeyValuePair<string, object>("availability", "Local")
117 }, false), true, this.userVariable, this.userPrivileges);
118
119 LocalStatus LocalStatus = new LocalStatus()
120 {
121 TabID = TabID
122 };
123
124 await this.client.Provider.RepairRegistry(
125 (Sender, e) =>
126 {
127 LocalStatus.NrBlocks++;
128 return this.Report(LocalStatus, e);
129 },
130 (Sender, e) =>
131 {
132 LocalStatus.NrAdded++;
133 LocalStatus.NrBlocks++;
134 return this.Report(LocalStatus, e);
135 },
136 (Sender, e) =>
137 {
138 LocalStatus.NrUpdated++;
139 LocalStatus.NrBlocks++;
140 return this.Report(LocalStatus, e);
141 },
142 (Sender, e) =>
143 {
144 LocalStatus.NrDeleted++;
145 return this.Report(LocalStatus, e);
146 });
147
148 LocalStatus.Last = DateTime.MinValue;
149 await this.Report(LocalStatus, true);
150
151 LinkedList<Task> Tasks = new LinkedList<Task>();
152
153 foreach (RosterItem Item in Gateway.XmppClient.Roster)
154 {
155 await ClientEvents.PushEvent(new string[] { TabID }, "AddContact", JSON.Encode(new KeyValuePair<string, object>[]
156 {
157 new KeyValuePair<string, object>("jid", Item.BareJid),
158 new KeyValuePair<string, object>("name", Item.NameOrBareJid)
159 }, false), true, this.userVariable, this.userPrivileges);
160
161 PeerStatus Status = null;
162 PresenceEventArgs Peer = null;
163 bool HasPresence = false;
164
165 foreach (PresenceEventArgs e in Item.Resources)
166 {
167 HasPresence = true;
168
169 Status = await this.client.GetPeerStatus(e);
170 if (Status is null)
171 continue;
172
173 if (Status.IsNeuroLedger)
174 {
175 Peer = e;
176 break;
177 }
178 }
179
180 string Availability;
181 string Type;
182
183 if (!HasPresence)
184 {
185 Type = "Unknown";
186 Availability = "Offline";
187 }
188 else if (!(Peer is null))
189 {
190 Type = "Neuro-Ledger";
191 Availability = Peer.Availability.ToString();
192 }
193 else
194 {
195 Type = "Unknown";
196 Availability = Item.LastPresence?.Availability.ToString() ?? "Offline";
197 }
198
199 await ClientEvents.PushEvent(new string[] { TabID }, "ReportAvailability", JSON.Encode(new KeyValuePair<string, object>[]
200 {
201 new KeyValuePair<string, object>("jid", Item.BareJid),
202 new KeyValuePair<string, object>("type", Type),
203 new KeyValuePair<string, object>("availability", Availability)
204 }, false), true, this.userVariable, this.userPrivileges);
205
206 if (Peer is null || !Peer.IsOnline)
207 continue;
208
209 Tasks.AddLast(Task.Run(() => this.SynchronizePeer(TabID, Status, Item)));
210 }
211
212 if (Tasks.Count > 0)
213 await Task.WhenAll(Tasks);
214 }
215 catch (Exception ex)
216 {
217 await ClientEvents.PushEvent(new string[] { TabID }, "ShowError", ex.Message, false, this.userVariable, this.userPrivileges);
218 }
219 }
220
221 private class LocalStatus
222 {
223 public string TabID;
224 public int NrBlocks = 0;
225 public int NrAdded = 0;
226 public int NrUpdated = 0;
227 public int NrDeleted = 0;
228 public DateTime Last = DateTime.Now;
229 public DateTime FirstBlock = DateTime.MaxValue;
230 public DateTime LastBlock = DateTime.MinValue;
231 public ulong TotalBytes = 0;
232 }
233
234 private Task Report(LocalStatus LocalStatus, BlockReferenceEventArgs e)
235 {
236 if (e.Block.Created < LocalStatus.FirstBlock)
237 LocalStatus.FirstBlock = e.Block.Created;
238
239 if (e.Block.Created > LocalStatus.LastBlock)
240 LocalStatus.LastBlock = e.Block.Created;
241
242 LocalStatus.TotalBytes += e.Block.Bytes;
243
244 return this.Report(LocalStatus, false);
245 }
246
247 private async Task Report(LocalStatus LocalStatus, bool Done)
248 {
249 DateTime Now = DateTime.Now;
250 if ((Now - LocalStatus.Last).TotalSeconds >= 1)
251 {
252 LocalStatus.Last = Now;
253
254 try
255 {
256 await ClientEvents.PushEvent(new string[] { LocalStatus.TabID }, "StatusUpdate", JSON.Encode(new KeyValuePair<string, object>[]
257 {
258 new KeyValuePair<string, object>("jid", this.client.Provider.ExternalIdentity),
259 new KeyValuePair<string, object>("first", LocalStatus.FirstBlock == DateTime.MaxValue ? string.Empty : LocalStatus.FirstBlock.ToShortDateString()),
260 new KeyValuePair<string, object>("last", LocalStatus.LastBlock == DateTime.MinValue ? string.Empty : LocalStatus.LastBlock.ToShortDateString()),
261 new KeyValuePair<string, object>("nrBlocks", LocalStatus.NrBlocks),
262 new KeyValuePair<string, object>("nrNew", LocalStatus.NrAdded),
263 new KeyValuePair<string, object>("nrUpdated", LocalStatus.NrUpdated),
264 new KeyValuePair<string, object>("blockErrors", LocalStatus.NrDeleted),
265 new KeyValuePair<string, object>("totalBytes", LocalStatus.TotalBytes),
266 new KeyValuePair<string, object>("done", Done),
267 new KeyValuePair<string, object>("nrLoaded", string.Empty),
268 new KeyValuePair<string, object>("nrDenied", string.Empty),
269 new KeyValuePair<string, object>("loadedBytes", string.Empty),
270 new KeyValuePair<string, object>("events", string.Empty),
271 new KeyValuePair<string, object>("objectsAdded", string.Empty),
272 new KeyValuePair<string, object>("objectsUpdated", string.Empty),
273 new KeyValuePair<string, object>("objectsDeleted", string.Empty),
274 new KeyValuePair<string, object>("objectErrors", string.Empty)
275
276 }, false), true, this.userVariable, this.userPrivileges);
277 }
278 catch (Exception)
279 {
280 // Ignore
281 }
282 }
283 }
284
285 private async Task StopSynchronization(string TabID)
286 {
287 try
288 {
289 foreach (RosterItem Item in Gateway.XmppClient.Roster)
290 {
291 PeerStatus Status = null;
292 PresenceEventArgs Peer = null;
293
294 foreach (PresenceEventArgs e in Item.Resources)
295 {
296 Status = await this.client.GetPeerStatus(e);
297 if (Status is null)
298 continue;
299
300 if (Status.IsNeuroLedger)
301 {
302 Peer = e;
303 break;
304 }
305 }
306
307 if (Peer is null)
308 continue;
309
310 this.client.StopSynchronization(Status);
311 }
312 }
313 catch (Exception ex)
314 {
315 await ClientEvents.PushEvent(new string[] { TabID }, "ShowError", ex.Message, false, this.userVariable, this.userPrivileges);
316 }
317 }
318
319 private async Task SynchronizePeer(string TabID, PeerStatus Status, RosterItem Item)
320 {
321 try
322 {
323 string Msg = await this.client.SynchronizePeer(Status, true, async (Progress) =>
324 {
325 await ClientEvents.PushEvent(new string[] { TabID }, "StatusUpdate", JSON.Encode(new KeyValuePair<string, object>[]
326 {
327 new KeyValuePair<string, object>("jid", Item.BareJid),
328 new KeyValuePair<string, object>("first", Progress.First == DateTime.MaxValue ? string.Empty : Progress.First.ToShortDateString()),
329 new KeyValuePair<string, object>("last", Progress.Last == DateTime.MinValue ? string.Empty : Progress.Last.ToShortDateString()),
330 new KeyValuePair<string, object>("nrBlocks", Progress.NrBlocks),
331 new KeyValuePair<string, object>("nrNew", Progress.NrNew),
332 new KeyValuePair<string, object>("nrUpdated", Progress.NrUpdated),
333 new KeyValuePair<string, object>("nrLoaded", Progress.NrLoaded),
334 new KeyValuePair<string, object>("nrDenied", Progress.NrDenied),
335 new KeyValuePair<string, object>("blockErrors", Progress.BlockErrors),
336 new KeyValuePair<string, object>("totalBytes", Progress.TotalBytes),
337 new KeyValuePair<string, object>("loadedBytes", Progress.LoadedBytes),
338 new KeyValuePair<string, object>("events", Progress.Events),
339 new KeyValuePair<string, object>("objectsAdded", Progress.ObjectsAdded),
340 new KeyValuePair<string, object>("objectsUpdated", Progress.ObjectsUpdated),
341 new KeyValuePair<string, object>("objectsDeleted", Progress.ObjectsDeleted),
342 new KeyValuePair<string, object>("objectErrors", Progress.ObjectErrors),
343 new KeyValuePair<string, object>("done", Progress.Done)
344 }, false), true, this.userVariable, this.userPrivileges);
345
346 KeyValuePair<string, string>[] Errors = Progress.PopBlockErrorMessages();
347 if (!(Errors is null))
348 {
349 foreach (KeyValuePair<string, string> P in Errors)
350 {
351 await ClientEvents.PushEvent(new string[] { TabID }, "BlockError", JSON.Encode(new KeyValuePair<string, object>[]
352 {
353 new KeyValuePair<string, object>("time", DateTime.Now.ToLongTimeString()),
354 new KeyValuePair<string, object>("peer", P.Key),
355 new KeyValuePair<string, object>("message", P.Value)
356 }, false), true, this.userVariable, this.userPrivileges);
357 }
358 }
359 });
360
361 if (!string.IsNullOrEmpty(Msg))
362 {
363 await ClientEvents.PushEvent(new string[] { TabID }, "UnableToSynchronize", JSON.Encode(new KeyValuePair<string, object>[]
364 {
365 new KeyValuePair<string, object>("jid", Item.BareJid),
366 new KeyValuePair<string, object>("message", Msg)
367 }, false), true, this.userVariable, this.userPrivileges);
368 }
369 }
370 catch (Exception ex)
371 {
372 await ClientEvents.PushEvent(new string[] { TabID }, "ShowError", ex.Message, false, this.userVariable, this.userPrivileges);
373 }
374 }
375
376 }
377}
Helps with common JSON-related tasks.
Definition: JSON.cs:14
static string Encode(string s)
Encodes a string for inclusion in JSON.
Definition: JSON.cs:507
The ClientEvents class allows applications to push information asynchronously to web clients connecte...
Definition: ClientEvents.cs:51
static Task< int > PushEvent(string[] TabIDs, string Type, object Data)
Puses an event to a set of Tabs, given their Tab IDs.
Static class managing the runtime environment of the IoT Gateway.
Definition: Gateway.cs:126
static XmppClient XmppClient
XMPP Client connection of gateway.
Definition: Gateway.cs:3187
The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repe...
The request could not be completed due to a conflict with the current state of the resource....
The server understood the request, but is refusing to fulfill it. Authorization will not help and the...
Represents an HTTP request.
Definition: HttpRequest.cs:18
Variables Session
Contains session states, if the resource requires sessions, or null otherwise.
Definition: HttpRequest.cs:164
string ResourceName
Name of resource.
Represets a response of an HTTP client request.
Definition: HttpResponse.cs:21
Base class for all synchronous HTTP resources. A synchronous resource responds within the method hand...
Event arguments for presence events.
Availability Availability
Resource availability.
Contains information about current synchronization status for a peer in the network.
Definition: PeerStatus.cs:13
bool IsNeuroLedger
If the peer is a Neuro-Ledger node.
Definition: PeerStatus.cs:119
Maintains information about an item in the roster.
Definition: RosterItem.cs:75
string NameOrBareJid
Returns the name of the contact, or the Bare JID, if there's no name provided.
Definition: RosterItem.cs:436
string BareJid
Bare JID of the roster item.
Definition: RosterItem.cs:276
PresenceEventArgs LastPresence
Last presence received from a resource having this bare JID.
Definition: RosterItem.cs:356
PresenceEventArgs[] Resources
Active resources utilized by contact.
Definition: RosterItem.cs:300
XmppState State
Current state of connection.
Definition: XmppClient.cs:985
RosterItem[] Roster
Items in the roster.
Definition: XmppClient.cs:4671
Event arguments for block reference events.
Contains information about a variable.
Definition: Variable.cs:10
virtual bool TryGetVariable(string Name, out Variable Variable)
Tries to get a variable object, given its name.
Definition: Variables.cs:52
Allows operators to manually start network synchronization.
override bool HandlesSubPaths
If the resource handles sub-paths.
override bool UserSessions
If the resource uses user sessions.
SynchronizationResource(string ResourceName, NeuroLedgerClient NLClient, string UserVariable, params string[] UserPrivileges)
Allows operators to manually start network synchronization.
async Task POST(HttpRequest Request, HttpResponse Response)
Executes the POST method on the resource.
POST Interface for HTTP resources.
Basic interface for a user.
Definition: IUser.cs:7
Availability
Resource availability.
Definition: Availability.cs:7
XmppState
State of XMPP connection.
Definition: XmppState.cs:7