diff --git a/docker-compose.example.yaml b/docker-compose.example.yaml
index 3fdd123..55cc810 100644
--- a/docker-compose.example.yaml
+++ b/docker-compose.example.yaml
@@ -39,4 +39,18 @@ services:
resources:
limits:
cpus: "1"
- memory: 1024M
\ No newline at end of file
+ memory: 1024M
+
+ meshcenterfrontend:
+ build:
+ context: ./ui
+ dockerfile: Dockerfile
+ ports:
+ - 8686:80
+ depends_on:
+ - meshcenterbackend
+ deploy:
+ resources:
+ limits:
+ cpus: "1"
+ memory: 1024M
\ No newline at end of file
diff --git a/ui/Dockerfile b/ui/Dockerfile
new file mode 100644
index 0000000..a6c8290
--- /dev/null
+++ b/ui/Dockerfile
@@ -0,0 +1,10 @@
+FROM node:18-alpine as builder
+WORKDIR /build
+COPY . .
+RUN npm install --reg https://nexus.pblr-nyk.pro/repository/npm/ && \
+ npm run ng build
+FROM nginx:stable-alpine
+COPY --from=builder /build/dist/ui /usr/share/nginx/html
+RUN rm /etc/nginx/conf.d/default.conf
+COPY srv.conf /etc/nginx/conf.d
+CMD ["nginx", "-g", "daemon off;"]
diff --git a/ui/src/app/auth/AuthInterceptor.ts b/ui/src/app/auth/AuthInterceptor.ts
index c3b104b..1e4e14c 100644
--- a/ui/src/app/auth/AuthInterceptor.ts
+++ b/ui/src/app/auth/AuthInterceptor.ts
@@ -123,6 +123,7 @@ export class AuthInterceptor implements HttpInterceptor {
return next.handle(req).pipe(
catchError((error: HttpErrorResponse) => {
if (error.status === 401 && !this.dialogOpened) {
+ this.dialogOpened = true;
const ref = this.dialog.open(AuthDialog, {disableClose: true, width:"30%"})
ref.afterClosed().subscribe((res:any) => this.dialogOpened = false)
}
diff --git a/ui/src/app/components/messages/MessageHistory.component.ts b/ui/src/app/components/messages/MessageHistory.component.ts
index bf54acc..299825d 100644
--- a/ui/src/app/components/messages/MessageHistory.component.ts
+++ b/ui/src/app/components/messages/MessageHistory.component.ts
@@ -1,4 +1,4 @@
-import {Component, OnInit} from "@angular/core";
+import {Component, OnDestroy, OnInit} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import {MessageDTO} from "../../entities/MessageDTO";
import {NodeDTO} from "../../entities/NodeDTO";
@@ -11,7 +11,7 @@ import {Subscription} from "rxjs";
-
+
{{knownNodes[msg.from.toString()] == null ? msg.from : knownNodes[msg.from.toString()].long_name}} | {{msg.ts * 1000 | date: 'HH:mm:ss dd.MM.yyyy'}} | {{msg.hop_start - msg.hop_limit == 0 ? 'напрямую' : (msg.hop_start - msg.hop_limit) + ' хопов'}} | rssi: {{msg.rx_rssi}} snr: {{msg.rx_snr}}
{{msg.decoded_payload}}
@@ -19,7 +19,7 @@ import {Subscription} from "rxjs";
`
})
-export class MessageHistoryComponent implements OnInit {
+export class MessageHistoryComponent implements OnInit, OnDestroy {
constructor(private http: HttpClient) {}
loading: boolean = false;
@@ -28,20 +28,26 @@ export class MessageHistoryComponent implements OnInit {
offset = 0;
limit = 10;
lastMsgTs = 0;
-
+ olderMsgTs = new Date().getTime()/1000;
+ interval:any = null;
knownNodes:KeyValueMap
= {}
ngOnInit() {
this.getMessages();
- setInterval(() => {
- //this.newMessagePooler()
- }, 1000)
+ this.interval = setInterval(() => {
+ this.newMessagePooler()
+ }, 5000)
+ }
+
+ ngOnDestroy(): void {
+ if (this.interval)
+ clearInterval(this.interval)
}
tryKnownNodes(nums: number[]) {
let notKnownNodes = nums.filter(num => Object.keys(this.knownNodes).indexOf(`${num}`) == -1)
if (notKnownNodes.length == 0) return Subscription.EMPTY;
- let params = notKnownNodes.length > 1 ? notKnownNodes.join("&nums=") : `$nums=${notKnownNodes.pop()}`;
+ let params = notKnownNodes.length > 1 ? "&nums=" + notKnownNodes.join("&nums=") : `?nums=${notKnownNodes.pop()}`;
return this.http.get(`api/nodes?s=1${params}`).subscribe(
(res) => {
(res as NodeDTO[]).forEach(
@@ -53,7 +59,7 @@ export class MessageHistoryComponent implements OnInit {
getMessages() {
this.loading = true;
- this.http.get(`api/messages?offset=${this.offset}&limit=${this.limit}&after=${this.lastMsgTs}`)
+ this.http.get(`api/messages?limit=${this.limit}&before=${this.olderMsgTs}`)
.subscribe((res) => {
let new_msgs = res as MessageDTO[]
this.tryKnownNodes(new_msgs.map(msg => msg.from)).add(
@@ -62,6 +68,9 @@ export class MessageHistoryComponent implements OnInit {
.sort((m1, m2) => m1.ts - m2.ts);
if (this.messages.length>0) {
+ if (this.messages[0].ts < this.olderMsgTs)
+ this.olderMsgTs = this.messages[0].ts;
+
if (this.messages[this.messages.length-1].ts > this.lastMsgTs)
this.lastMsgTs = this.messages[this.messages.length-1].ts;
}
@@ -77,7 +86,7 @@ export class MessageHistoryComponent implements OnInit {
//ЭХ ВОТ БЫ ВЕБСОКЕТ НО МНЕ ЛЕНЬ
newMessagePooler() {
- this.http.get(`api/messages?offset=0&limit=10&after=${this.lastMsgTs}`)
+ this.http.get(`api/messages?limit=10&after=${this.lastMsgTs}&before=2147483647`)//todo 37 year moment
.subscribe((res) => {
let new_msgs = res as MessageDTO[]
this.tryKnownNodes(new_msgs.map(msg => msg.from)).add(
@@ -92,4 +101,21 @@ export class MessageHistoryComponent implements OnInit {
)
})
}
+
+ numToColor(num:number) {
+ // Приводим к беззнаковому 32-битному и перемешиваем биты
+ let n = num >>> 0;
+ // Мультипликативная хеш-функция с простыми числами
+ n = (n * 2654435761) >>> 0; // константа из Knuth
+ n ^= (n >> 16);
+ n = (n * 0x85EBCA6B) >>> 0;
+ n ^= (n >> 13);
+ n = (n * 0xC2B2AE35) >>> 0;
+ n ^= (n >> 16);
+
+ const r = (n >> 16) & 0xFF;
+ const g = (n >> 8) & 0xFF;
+ const b = n & 0xFF;
+ return `rgba(${r}, ${g}, ${b}, 0.2)`
+ }
}
diff --git a/ui/srv.conf b/ui/srv.conf
new file mode 100644
index 0000000..35781a5
--- /dev/null
+++ b/ui/srv.conf
@@ -0,0 +1,19 @@
+server {
+ listen 80;
+ listen [::]:80;
+ server_name localhost;
+
+ root /usr/share/nginx/html;
+ index index.html index.htm;
+
+ location / {
+ try_files $uri /index.html;
+ }
+
+ location /api/ {
+ proxy_pass http://meshcenterbackend:8680;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ }
+}