diff options
-rw-r--r-- | backend/api/api/Controllers/DatasetController.cs | 14 | ||||
-rw-r--r-- | backend/api/api/Controllers/WebSocketController.cs | 63 | ||||
-rw-r--r-- | backend/api/api/Program.cs | 51 | ||||
-rw-r--r-- | backend/api/api/Services/ChatHub.cs | 49 | ||||
-rw-r--r-- | backend/api/api/Services/IChat.cs | 8 | ||||
-rw-r--r-- | backend/api/api/Services/IMlConnectionService.cs | 2 | ||||
-rw-r--r-- | backend/api/api/Services/MLWebSocketService.cs | 68 | ||||
-rw-r--r-- | backend/api/api/Services/MlConnectionService.cs | 9 | ||||
-rw-r--r-- | frontend/package-lock.json | 298 | ||||
-rw-r--r-- | frontend/package.json | 1 | ||||
-rw-r--r-- | frontend/src/app/_elements/notifications/notifications.component.ts | 4 | ||||
-rw-r--r-- | frontend/src/app/_services/signal-r.service.spec.ts (renamed from frontend/src/app/_services/web-socket.service.spec.ts) | 8 | ||||
-rw-r--r-- | frontend/src/app/_services/signal-r.service.ts | 30 | ||||
-rw-r--r-- | frontend/src/app/_services/web-socket.service.ts | 39 | ||||
-rw-r--r-- | frontend/src/app/app-routing.module.ts | 2 | ||||
-rw-r--r-- | frontend/src/app/app.component.ts | 20 | ||||
-rw-r--r-- | frontend/src/app/app.module.ts | 2 |
17 files changed, 455 insertions, 213 deletions
diff --git a/backend/api/api/Controllers/DatasetController.cs b/backend/api/api/Controllers/DatasetController.cs index e9c824f9..375b5bfd 100644 --- a/backend/api/api/Controllers/DatasetController.cs +++ b/backend/api/api/Controllers/DatasetController.cs @@ -162,6 +162,18 @@ namespace api.Controllers [Authorize(Roles = "User,Guest")] public async Task<ActionResult<Dataset>> Post([FromBody] Dataset dataset) { + string uploaderId; + var header = Request.Headers[HeaderNames.Authorization]; + if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) + { + var scheme = headerValue.Scheme; + var parameter = headerValue.Parameter; + uploaderId = jwtToken.TokenToId(parameter); + if (uploaderId == null) + return null; + } + else + return BadRequest(); //da li ce preko tokena da se ubaci username ili front salje //dataset.username = usernameToken; //username = "" ako je GUEST DODAO @@ -175,7 +187,7 @@ namespace api.Controllers FileModel fileModel = _fileService.getFile(dataset.fileId); dataset.isPreProcess = false; _datasetService.Create(dataset); - _mlConnectionService.PreProcess(dataset,fileModel.path); + _mlConnectionService.PreProcess(dataset,fileModel.path,uploaderId); return Ok(); } } diff --git a/backend/api/api/Controllers/WebSocketController.cs b/backend/api/api/Controllers/WebSocketController.cs deleted file mode 100644 index 184b47e7..00000000 --- a/backend/api/api/Controllers/WebSocketController.cs +++ /dev/null @@ -1,63 +0,0 @@ -using api.Services; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using System.Net.WebSockets; - -namespace api.Controllers -{ - [Route("api")] - [ApiController] - public class WebSocketController : ControllerBase - { - private IMLWebSocketService mlWS; - public WebSocketController(IMLWebSocketService mlWS) - { - this.mlWS = mlWS; - } - - [HttpGet("wstest")] - public string Test() - { - this.mlWS.Send("ABC123"); - return "MESSAGE SENT!"; - } - - [HttpGet("ws")] - public async Task Get() - { - if (HttpContext.WebSockets.IsWebSocketRequest) - { - using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync(); - await Echo(webSocket); - } - else - { - HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest; - } - } - - private static async Task Echo(WebSocket webSocket) - { - var buffer = new byte[1024 * 4]; - var receiveResult = await webSocket.ReceiveAsync( - new ArraySegment<byte>(buffer), CancellationToken.None); - - while (!receiveResult.CloseStatus.HasValue) - { - await webSocket.SendAsync( - new ArraySegment<byte>(buffer, 0, receiveResult.Count), - receiveResult.MessageType, - receiveResult.EndOfMessage, - CancellationToken.None); - - receiveResult = await webSocket.ReceiveAsync( - new ArraySegment<byte>(buffer), CancellationToken.None); - } - - await webSocket.CloseAsync( - receiveResult.CloseStatus.Value, - receiveResult.CloseStatusDescription, - CancellationToken.None); - } - } -} diff --git a/backend/api/api/Program.cs b/backend/api/api/Program.cs index 2d6dcfbb..6f1116d9 100644 --- a/backend/api/api/Program.cs +++ b/backend/api/api/Program.cs @@ -36,14 +36,13 @@ builder.Services.AddScoped<IPredictorService, PredictorService>(); builder.Services.AddScoped<IFileService, FileService>(); builder.Services.AddScoped<IJwtToken, JwtToken>(); builder.Services.AddScoped<IExperimentService, ExperimentService>(); +builder.Services.AddScoped<IChat,ChatHub>(); +builder.Services.AddHostedService<TempFileService>(); +builder.Services.AddHostedService<FillAnEmptyDb>(); + -var mlwss = new MLWebSocketService(); -builder.Services.AddSingleton<IMLWebSocketService>(mlwss); -builder.Services.AddHostedService(_ => mlwss); -builder.Services.AddHostedService<TempFileService>(); -builder.Services.AddHostedService<FillAnEmptyDb>(); //Add Authentication builder.Services.AddAuthentication( @@ -63,32 +62,54 @@ builder.Services.Configure<FormOptions>(x => x.MultipartBodyLengthLimit = int.MaxValue; }); +builder.Services.AddSignalR(); builder.Services.AddControllers(); +builder.Services.AddCors(options => +{ + options.AddPolicy("CorsPolicy", builder => builder + .WithOrigins("http://localhost:4200") + .AllowAnyMethod() + .AllowAnyHeader() + .AllowCredentials()); +}); + + + var app = builder.Build(); -var webSocketOptions = new WebSocketOptions + +app.UseWebSockets(new WebSocketOptions { - KeepAliveInterval = TimeSpan.FromMinutes(2) -}; + KeepAliveInterval = TimeSpan.FromSeconds(120) +}); + + + -app.UseWebSockets(webSocketOptions); //Add Cors -app.UseCors( - x=>x.AllowAnyOrigin() - .AllowAnyMethod() - .AllowAnyHeader() - ); +app.UseCors("CorsPolicy"); // Configure the HTTP request pipeline. //Add Authentication app.UseAuthentication(); + + +app.UseRouting(); app.UseAuthorization(); +app.UseEndpoints(endpoints => +{ + endpoints.MapControllers(); + endpoints.MapHub<ChatHub>("/chatHub"); +}); + + + + -app.MapControllers(); app.Run(); diff --git a/backend/api/api/Services/ChatHub.cs b/backend/api/api/Services/ChatHub.cs new file mode 100644 index 00000000..49466273 --- /dev/null +++ b/backend/api/api/Services/ChatHub.cs @@ -0,0 +1,49 @@ +using api.Models; +using Microsoft.AspNetCore.SignalR; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Threading.Tasks; + + + +namespace api.Services +{ + public class ChatHub :Hub,IChat + { + static readonly Dictionary<string,string> Users=new Dictionary<string,string>(); + private readonly IJwtToken _tokenService; + public ChatHub(IJwtToken tokenService) + { + _tokenService=tokenService; + } + + public override async Task OnConnectedAsync() + { + string token=Context.GetHttpContext().Request.Query["access_token"]; + string id=_tokenService.TokenToId(token); + Users.Add(id,Context.ConnectionId); + //await Send(id,Context.ConnectionId); + await SendDirect(id, "poruka"); + await base.OnConnectedAsync(); + + } + public override async Task OnDisconnectedAsync(Exception? exception) + { + string id = Users.FirstOrDefault(u => u.Key == Context.ConnectionId).Value; + Users.Remove(id); + } + public async Task SendDirect(string id,string message) + { + if (Users[id]==null) + return; + + await Clients.Client(Users[id]).SendAsync("Notify",message); + } + public async Task Send(string message) + { + await Clients.All.SendAsync("Notify",message); + } + } + + +} diff --git a/backend/api/api/Services/IChat.cs b/backend/api/api/Services/IChat.cs new file mode 100644 index 00000000..8a905868 --- /dev/null +++ b/backend/api/api/Services/IChat.cs @@ -0,0 +1,8 @@ +namespace api.Services +{ + public interface IChat + { + Task SendDirect(string id, string message); + Task Send(string message); + } +}
\ No newline at end of file diff --git a/backend/api/api/Services/IMlConnectionService.cs b/backend/api/api/Services/IMlConnectionService.cs index 5edc5554..9a9a81f4 100644 --- a/backend/api/api/Services/IMlConnectionService.cs +++ b/backend/api/api/Services/IMlConnectionService.cs @@ -6,7 +6,7 @@ namespace api.Services public interface IMlConnectionService { Task<string> SendModelAsync(object model, object dataset); - Task PreProcess(Dataset dataset, string filePath); + Task PreProcess(Dataset dataset, string filePath,string id); Task TrainModel(Model model, Experiment experiment, string filePath); //Task<Dataset> PreProcess(Dataset dataset, byte[] file, string filename); } diff --git a/backend/api/api/Services/MLWebSocketService.cs b/backend/api/api/Services/MLWebSocketService.cs deleted file mode 100644 index ca6919bf..00000000 --- a/backend/api/api/Services/MLWebSocketService.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System.Net.WebSockets; -using System.Text; - -namespace api.Services -{ - public class MLWebSocketService: BackgroundService, IMLWebSocketService - { - private static readonly string Connection = "ws://localhost:5027"; - - private Queue<string> dataQueue = new Queue<string>(); - - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - while (!stoppingToken.IsCancellationRequested) - using (var socket = new ClientWebSocket()) - try - { - await socket.ConnectAsync(new Uri(Connection), stoppingToken); - - - while(dataQueue.Count > 0) - { - await Send(socket, dataQueue.Dequeue(), stoppingToken); - } - - Receive(socket, stoppingToken); - - await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", stoppingToken); - } - catch (Exception ex) - { - Console.WriteLine($"ERROR - {ex.Message}"); - } - } - - private async Task Send(ClientWebSocket socket, string data, CancellationToken stoppingToken) => - await socket.SendAsync(Encoding.UTF8.GetBytes(data), WebSocketMessageType.Text, true, stoppingToken); - - private async Task Receive(ClientWebSocket socket, CancellationToken stoppingToken) - { - var buffer = new ArraySegment<byte>(new byte[2048]); - while (!stoppingToken.IsCancellationRequested) - { - WebSocketReceiveResult result; - using (var ms = new MemoryStream()) - { - do - { - result = await socket.ReceiveAsync(buffer, stoppingToken); - ms.Write(buffer.Array, buffer.Offset, result.Count); - } while (!result.EndOfMessage); - - if (result.MessageType == WebSocketMessageType.Close) - break; - - ms.Seek(0, SeekOrigin.Begin); - using (var reader = new StreamReader(ms, Encoding.UTF8)) - Console.WriteLine(await reader.ReadToEndAsync()); - } - }; - } - - public void Send(string data) - { - dataQueue.Enqueue(data); - } - } -} diff --git a/backend/api/api/Services/MlConnectionService.cs b/backend/api/api/Services/MlConnectionService.cs index fb42965b..8523a3ac 100644 --- a/backend/api/api/Services/MlConnectionService.cs +++ b/backend/api/api/Services/MlConnectionService.cs @@ -3,6 +3,7 @@ using RestSharp; using System.Net.WebSockets; using System.Text; using Newtonsoft.Json; +using Microsoft.AspNetCore.SignalR; namespace api.Services { @@ -11,11 +12,13 @@ namespace api.Services private RestClient client; private readonly IDatasetService _datasetService; private readonly IModelService _modelService; + private readonly IChat _chat; - public MlConnectionService(IDatasetService datasetService) + public MlConnectionService(IDatasetService datasetService,IChat chat) { this.client = new RestClient("http://127.0.0.1:5543"); _datasetService=datasetService; + _chat=chat; } public async Task<string> SendModelAsync(object model, object dataset)//Don't Use @@ -42,7 +45,7 @@ namespace api.Services return; } - public async Task PreProcess(Dataset dataset,string filePath)//(Dataset dataset,byte[] file,string filename) + public async Task PreProcess(Dataset dataset,string filePath,string id)//(Dataset dataset,byte[] file,string filename) { var request=new RestRequest("preprocess", Method.Post); request.AddParameter("dataset", JsonConvert.SerializeObject(dataset)); @@ -54,7 +57,7 @@ namespace api.Services Dataset newDataset = JsonConvert.DeserializeObject<Dataset>(result.Content); newDataset.isPreProcess = true; _datasetService.Update(newDataset); - + await _chat.SendDirect(id, "Procesed dataset with name "+newDataset.name); return; } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c79f4ea9..a82131d5 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -21,6 +21,7 @@ "@angular/platform-browser-dynamic": "~13.2.0", "@angular/router": "~13.2.0", "@auth0/angular-jwt": "^5.0.2", + "@microsoft/signalr": "^6.0.4", "@ng-bootstrap/ng-bootstrap": "^12.0.0", "@popperjs/core": "^2.10.2", "bootstrap": "^5.1.3", @@ -2479,6 +2480,38 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@microsoft/signalr": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-6.0.4.tgz", + "integrity": "sha512-YeWRh4LxfYnq4I5CKw17/HOq8rY+ouTv6Bq+s55122StE3pK29j8j2OpP+1PA3D1ksHPfy7dFIgC33yr/E+01A==", + "dependencies": { + "abort-controller": "^3.0.0", + "eventsource": "^1.0.7", + "fetch-cookie": "^0.11.0", + "node-fetch": "^2.6.7", + "ws": "^7.4.5" + } + }, + "node_modules/@microsoft/signalr/node_modules/ws": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", + "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/@ng-bootstrap/ng-bootstrap": { "version": "12.0.0", "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-12.0.0.tgz", @@ -3118,6 +3151,17 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -5050,7 +5094,6 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "dev": true, "optional": true, "dependencies": { "iconv-lite": "^0.6.2" @@ -5060,7 +5103,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, "optional": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -5620,6 +5662,14 @@ "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, "node_modules/eventemitter-asyncresource": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", @@ -5641,6 +5691,17 @@ "node": ">=0.8.x" } }, + "node_modules/eventsource": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz", + "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==", + "dependencies": { + "original": "^1.0.0" + }, + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -5815,6 +5876,17 @@ "node": ">=0.8.0" } }, + "node_modules/fetch-cookie": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.11.0.tgz", + "integrity": "sha512-BQm7iZLFhMWFy5CZ/162sAGjBfdNWb7a8LEqqnzsHFhxT/X/SVj/z2t2nu3aJvjlbQkrAlTUApplPRjWyH4mhA==", + "dependencies": { + "tough-cookie": "^2.3.3 || ^3.0.1 || ^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -8212,6 +8284,25 @@ "dev": true, "optional": true }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -8724,6 +8815,14 @@ "node": ">=8" } }, + "node_modules/original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "dependencies": { + "url-parse": "^1.4.3" + } + }, "node_modules/os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -9665,11 +9764,15 @@ "dev": true, "optional": true }, + "node_modules/psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, "engines": { "node": ">=6" } @@ -9695,6 +9798,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -9919,8 +10027,7 @@ "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" }, "node_modules/resolve": { "version": "1.22.0", @@ -10089,7 +10196,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "devOptional": true }, "node_modules/sass": { "version": "1.49.0", @@ -11020,6 +11127,32 @@ "node": ">=0.6" } }, + "node_modules/tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -11181,6 +11314,15 @@ "punycode": "^2.1.0" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -11269,6 +11411,11 @@ "defaults": "^1.0.3" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, "node_modules/webpack": { "version": "5.67.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.67.0.tgz", @@ -11575,6 +11722,15 @@ "resolved": "https://registry.npmjs.org/websocket-ts/-/websocket-ts-1.1.1.tgz", "integrity": "sha512-rm+S60J74Ckw5iizzgID12ju+OfaHAa6dhXhULIOrXkl0e05RzxfY42/vMStpz5jWL3iz9mkyjPcFUY1IgI0fw==" }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -13417,6 +13573,26 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "@microsoft/signalr": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-6.0.4.tgz", + "integrity": "sha512-YeWRh4LxfYnq4I5CKw17/HOq8rY+ouTv6Bq+s55122StE3pK29j8j2OpP+1PA3D1ksHPfy7dFIgC33yr/E+01A==", + "requires": { + "abort-controller": "^3.0.0", + "eventsource": "^1.0.7", + "fetch-cookie": "^0.11.0", + "node-fetch": "^2.6.7", + "ws": "^7.4.5" + }, + "dependencies": { + "ws": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", + "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "requires": {} + } + } + }, "@ng-bootstrap/ng-bootstrap": { "version": "12.0.0", "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-12.0.0.tgz", @@ -14002,6 +14178,14 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -15466,7 +15650,6 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "dev": true, "optional": true, "requires": { "iconv-lite": "^0.6.2" @@ -15476,7 +15659,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, "optional": true, "requires": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -15804,6 +15986,11 @@ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", "dev": true }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, "eventemitter-asyncresource": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", @@ -15822,6 +16009,14 @@ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true }, + "eventsource": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz", + "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==", + "requires": { + "original": "^1.0.0" + } + }, "execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -15966,6 +16161,14 @@ "websocket-driver": ">=0.5.1" } }, + "fetch-cookie": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.11.0.tgz", + "integrity": "sha512-BQm7iZLFhMWFy5CZ/162sAGjBfdNWb7a8LEqqnzsHFhxT/X/SVj/z2t2nu3aJvjlbQkrAlTUApplPRjWyH4mhA==", + "requires": { + "tough-cookie": "^2.3.3 || ^3.0.1 || ^4.0.0" + } + }, "figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -17754,6 +17957,14 @@ "dev": true, "optional": true }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, "node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -18138,6 +18349,14 @@ } } }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "requires": { + "url-parse": "^1.4.3" + } + }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -18800,11 +19019,15 @@ "dev": true, "optional": true }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "qjobs": { "version": "1.2.0", @@ -18818,6 +19041,11 @@ "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", "dev": true }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -18991,8 +19219,7 @@ "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" }, "resolve": { "version": "1.22.0", @@ -19112,7 +19339,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "devOptional": true }, "sass": { "version": "1.49.0", @@ -19806,6 +20033,28 @@ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true }, + "tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + }, + "dependencies": { + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + } + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, "tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -19917,6 +20166,15 @@ "punycode": "^2.1.0" } }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -19989,6 +20247,11 @@ "defaults": "^1.0.3" } }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, "webpack": { "version": "5.67.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.67.0.tgz", @@ -20199,6 +20462,15 @@ "resolved": "https://registry.npmjs.org/websocket-ts/-/websocket-ts-1.1.1.tgz", "integrity": "sha512-rm+S60J74Ckw5iizzgID12ju+OfaHAa6dhXhULIOrXkl0e05RzxfY42/vMStpz5jWL3iz9mkyjPcFUY1IgI0fw==" }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index ad68e176..04fc5df3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -24,6 +24,7 @@ "@angular/platform-browser-dynamic": "~13.2.0", "@angular/router": "~13.2.0", "@auth0/angular-jwt": "^5.0.2", + "@microsoft/signalr": "^6.0.4", "@ng-bootstrap/ng-bootstrap": "^12.0.0", "@popperjs/core": "^2.10.2", "bootstrap": "^5.1.3", diff --git a/frontend/src/app/_elements/notifications/notifications.component.ts b/frontend/src/app/_elements/notifications/notifications.component.ts index 82613448..5863f669 100644 --- a/frontend/src/app/_elements/notifications/notifications.component.ts +++ b/frontend/src/app/_elements/notifications/notifications.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { WebSocketService } from 'src/app/_services/web-socket.service'; +import { SignalRService } from 'src/app/_services/signal-r.service'; import Notification from 'src/app/_data/Notification'; @Component({ @@ -12,7 +12,7 @@ export class NotificationsComponent implements OnInit { notifications?: Notification[]; closed: boolean = false; - constructor(private wsService: WebSocketService) { + constructor(private signalRService:SignalRService) { this.notifications = [ new Notification("Titanik (Preziveli)", "79768456867", 0.2), new Notification("Test Prediktor 1", "56758768678", 0.4), diff --git a/frontend/src/app/_services/web-socket.service.spec.ts b/frontend/src/app/_services/signal-r.service.spec.ts index a86aeca7..f737fa50 100644 --- a/frontend/src/app/_services/web-socket.service.spec.ts +++ b/frontend/src/app/_services/signal-r.service.spec.ts @@ -1,13 +1,13 @@ import { TestBed } from '@angular/core/testing'; -import { WebSocketService } from './web-socket.service'; +import { SignalRService } from './signal-r.service'; -describe('WebSocketService', () => { - let service: WebSocketService; +describe('SignalRService', () => { + let service: SignalRService; beforeEach(() => { TestBed.configureTestingModule({}); - service = TestBed.inject(WebSocketService); + service = TestBed.inject(SignalRService); }); it('should be created', () => { diff --git a/frontend/src/app/_services/signal-r.service.ts b/frontend/src/app/_services/signal-r.service.ts new file mode 100644 index 00000000..5eca48d3 --- /dev/null +++ b/frontend/src/app/_services/signal-r.service.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@angular/core'; +import * as signalR from "@microsoft/signalr"; +import Shared from '../Shared'; +import { CookieService } from 'ngx-cookie-service'; +@Injectable({ + providedIn: 'root' +}) +export class SignalRService { +private hubConnection?:signalR.HubConnection; +public startConnection=()=>{ + + this.hubConnection= new signalR.HubConnectionBuilder() + .withUrl('http://localhost:5283/chatHub', { + accessTokenFactory: () => this.cookie.get("token"), + withCredentials: false + }).build(); + + this.hubConnection.on("Notify",(message:string) =>{ + console.log(" "+message); + }); + + + + this.hubConnection + .start() + .then(()=>console.log("con Started")) + .catch(err=>console.log("Error"+err)) +} + constructor(private cookie:CookieService) { } +} diff --git a/frontend/src/app/_services/web-socket.service.ts b/frontend/src/app/_services/web-socket.service.ts deleted file mode 100644 index 1a7efa87..00000000 --- a/frontend/src/app/_services/web-socket.service.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Injectable } from '@angular/core'; -import { ConstantBackoff, Websocket, WebsocketBuilder } from 'websocket-ts'; -import { API_SETTINGS } from 'src/config'; - -@Injectable({ - providedIn: 'root' -}) -export class WebSocketService { - - ws?: Websocket; - - private handlers: Function[] = []; - - constructor() { - this.ws = new WebsocketBuilder(API_SETTINGS.apiWSUrl) - .withBackoff(new ConstantBackoff(120000)) - .onOpen((i, e) => { /*console.log('WS: Connected to ' + API_SETTINGS.apiWSUrl)*/ }) - .onMessage((i, e) => { - console.log('WS MESSAGE: ', e.data); - this.handlers.forEach(handler => { - handler(e.data); - }) - }) - .onClose((i, e) => { /*console.log('WS: Connection closed!')*/ }) - .build(); - } - - send(msg: string) { - this.ws?.send(msg); - } - - addHandler(handler: Function) { - this.handlers.push(handler); - } - - removeHandler(handler: Function) { - this.handlers.splice(this.handlers.indexOf(handler), 1); - } -} diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 54c29531..e22f7a88 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -27,7 +27,7 @@ const routes: Routes = [ { path: 'profile', component: ProfileComponent, canActivate: [AuthGuardService], data: { title: 'Profil' } }, { path: 'browse-datasets', component: FilterDatasetsComponent, data: { title: 'Javni izvori podataka' } }, { path: 'browse-predictors', component: BrowsePredictorsComponent, data: { title: 'Javni trenirani modeli' } }, - { path: 'predict/:id', component: PredictComponent, data: { title: 'Predvidi vrednosti' } } + { path: 'predict/:id', component: PredictComponent, data: { title: 'Predvidi vrednosti' } }, ]; @NgModule({ diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts index 8c6f8452..59f247ed 100644 --- a/frontend/src/app/app.component.ts +++ b/frontend/src/app/app.component.ts @@ -3,7 +3,9 @@ import { Title } from '@angular/platform-browser'; import { Router, NavigationEnd, ActivatedRoute } from '@angular/router'; import { filter, map } from 'rxjs'; import { AuthService } from './_services/auth.service'; - +import { SignalRService } from './_services/signal-r.service'; +import { HttpClient } from '@angular/common/http'; +import Shared from './Shared'; @Component({ selector: 'app-root', templateUrl: './app.component.html', @@ -11,7 +13,7 @@ import { AuthService } from './_services/auth.service'; }) export class AppComponent implements OnInit { - constructor(private router: Router, private titleService: Title,private authService:AuthService) { } + constructor(private router: Router, private titleService: Title,private authService:AuthService,private signalRService:SignalRService,private http:HttpClient) { } ngOnInit() { this.router.events @@ -38,5 +40,19 @@ export class AppComponent implements OnInit { { this.authService.addGuestToken(); } + this.signalRService.startConnection(); + //this.startHttpRequest(); + + + + + } + private startHttpRequest = () => { + this.http.get('http://localhost:5283/chatHub') + .subscribe(res => { + console.log(res); + }) } + + } diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 3909c680..1b91a1ac 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -79,7 +79,7 @@ import { TrainingComponent } from './training/training.component'; AlertDialogComponent, AddNewDatasetComponent, GraphComponent, - TrainingComponent + TrainingComponent, ], imports: [ BrowserModule, |