diff --git a/ui/angular.json b/ui/angular.json
index d435038..7774b5c 100644
--- a/ui/angular.json
+++ b/ui/angular.json
@@ -76,6 +76,9 @@
"browserTarget": "ui:build:development"
}
},
+ "options": {
+ "proxyConfig": "src/proxy.conf.json"
+ },
"defaultConfiguration": "development"
},
"extract-i18n": {
diff --git a/ui/src/app/app-routing.module.ts b/ui/src/app/app-routing.module.ts
index 0297262..06a8d29 100644
--- a/ui/src/app/app-routing.module.ts
+++ b/ui/src/app/app-routing.module.ts
@@ -1,7 +1,12 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
+import {DirectNodesComponent} from "./components/nodes/DirectNodes.component";
+import {BotCommandsComponent} from "./components/botCommands/BotCommands.component";
-const routes: Routes = [];
+const routes: Routes = [
+ {path: "nodes/direct", component: DirectNodesComponent},
+ {path: "", component: BotCommandsComponent}
+];
@NgModule({
imports: [RouterModule.forRoot(routes)],
diff --git a/ui/src/app/app.component.ts b/ui/src/app/app.component.ts
index 0741c53..f8336b6 100644
--- a/ui/src/app/app.component.ts
+++ b/ui/src/app/app.component.ts
@@ -9,7 +9,7 @@ export class AppComponent {
routes: {name: string, url: string}[] = [
{name: "Команды бота", url:""},
{name: "История сообщений", url:""},
- {name: "Статистика по нодам", url:""},
- {name: "Связь", url:""},
+ {name: "Прямые ноды", url:""},
+ {name: "Все ноды", url:""},
]
}
diff --git a/ui/src/app/app.module.ts b/ui/src/app/app.module.ts
index 81f9a69..5204118 100644
--- a/ui/src/app/app.module.ts
+++ b/ui/src/app/app.module.ts
@@ -8,11 +8,29 @@ import {MatToolbarModule} from "@angular/material/toolbar";
import {MatIconModule} from "@angular/material/icon";
import {MatSidenavModule} from "@angular/material/sidenav";
import {MatButtonModule} from "@angular/material/button";
-import {HttpClientModule} from "@angular/common/http";
+import {HttpClientModule, HTTP_INTERCEPTORS} from "@angular/common/http";
+import {BotCommandsComponent} from "./components/botCommands/BotCommands.component";
+import {MessageHistoryComponent} from "./components/messages/MessageHistory.component";
+import {DirectNodesComponent} from "./components/nodes/DirectNodes.component";
+import {AllNodesComponent} from "./components/nodes/AllNodes.component";
+import {AuthDialog, AuthInterceptor} from "./auth/AuthInterceptor";
+import {MatDialogModule} from "@angular/material/dialog";
+import {MatTabsModule} from "@angular/material/tabs";
+import {MatInputModule} from "@angular/material/input";
+import {MatAutocompleteModule} from "@angular/material/autocomplete";
+import {FormsModule, ReactiveFormsModule} from "@angular/forms";
+import {MatSnackBarModule} from "@angular/material/snack-bar";
+import {MatDividerModule} from "@angular/material/divider";
+import {MatCardModule} from "@angular/material/card";
@NgModule({
declarations: [
- AppComponent
+ AppComponent,
+ BotCommandsComponent,
+ MessageHistoryComponent,
+ DirectNodesComponent,
+ AllNodesComponent,
+ AuthDialog
],
imports: [
BrowserModule,
@@ -22,9 +40,22 @@ import {HttpClientModule} from "@angular/common/http";
MatIconModule,
MatSidenavModule,
MatButtonModule,
- HttpClientModule
+ HttpClientModule,
+ MatDialogModule,
+ MatTabsModule,
+ MatInputModule,
+ MatAutocompleteModule,
+ ReactiveFormsModule,
+ FormsModule,
+ MatSnackBarModule,
+ MatDividerModule,
+ MatCardModule
],
- providers: [],
+ providers: [{
+ provide: HTTP_INTERCEPTORS,
+ useClass: AuthInterceptor,
+ multi: true
+ }],
bootstrap: [AppComponent]
})
export class AppModule { }
diff --git a/ui/src/app/auth/AuthInterceptor.ts b/ui/src/app/auth/AuthInterceptor.ts
new file mode 100644
index 0000000..5986a88
--- /dev/null
+++ b/ui/src/app/auth/AuthInterceptor.ts
@@ -0,0 +1,120 @@
+import {
+ HttpClient,
+ HttpErrorResponse,
+ HttpEvent,
+ HttpHandler,
+ HttpInterceptor,
+ HttpRequest
+} from "@angular/common/http";
+import {Component, Injectable, OnInit, ViewContainerRef} from "@angular/core";
+import {catchError, Observable, startWith, throwError} from "rxjs";
+import {MatDialog, MatDialogRef} from "@angular/material/dialog";
+import {FormControl} from "@angular/forms";
+import {NodeMiniDTO} from "../entities/NodeMiniDTO";
+import {MatSnackBar} from "@angular/material/snack-bar";
+
+
+@Component({
+ selector: "app-auth-dialog",
+ template: `
+
Авторизация
+
+
+
+
+
+
+ Имя ноды
+
+
+
+ {{ node.long_name }} ({{node.num}})
+
+
+
+
+
+
+
+
+
+ Код из сообщения
+
+
+
+
+
+
+
+
+
+
+
+ `
+})
+export class AuthDialog implements OnInit {
+ constructor(private http: HttpClient,
+ private dialogRef: MatDialogRef,
+ private snack: MatSnackBar) {
+ }
+
+ ngOnInit(): void {
+ this.myControl.valueChanges.subscribe(
+ (v) => {
+ if (typeof v == "string")
+ this.foundedNodes = this.http.get(`api/nodes/search?name=${v}`) as Observable
+ }
+ )
+ }
+ myControl = new FormControl();
+ foundedNodes: Observable = new Observable()
+ code!: number
+
+ acceptCode() {
+ this.http.get(`api/auth/code/check?code=${this.code}`)
+ // @ts-ignore
+ .subscribe((res:{status:boolean}) => {
+ if (res.status) {
+ this.dialogRef.close()
+ } else {
+ this.snack.open("Неправильный код")
+ }
+ })
+ }
+
+ displayNode(node: NodeMiniDTO) {
+ if (!node) return ''
+ return `${node.long_name} (${node.num})`
+ }
+
+ sendCodeToNode() {
+ let node:NodeMiniDTO = this.myControl.value as NodeMiniDTO
+ this.http.get(`api/auth/code?num=${node.num}`).subscribe(
+ (res) => this.snack.open(`Код отправлен на ноду ${node.long_name}`)
+ )
+ }
+}
+
+@Injectable()
+export class AuthInterceptor implements HttpInterceptor {
+ constructor(private dialog: MatDialog) {}
+ dialogOpened: boolean = false;
+
+ intercept(req: HttpRequest, next: HttpHandler): Observable> {
+ return next.handle(req).pipe(
+ catchError((error: HttpErrorResponse) => {
+ if (error.status === 401 && !this.dialogOpened) {
+ const ref = this.dialog.open(AuthDialog, {disableClose: true, width:"50%"})
+ ref.afterClosed().subscribe((res:any) => this.dialogOpened = false)
+ }
+ return throwError(() => error);
+ })
+ )
+ }
+
+}
diff --git a/ui/src/app/components/botCommands/BotCommands.component.ts b/ui/src/app/components/botCommands/BotCommands.component.ts
new file mode 100644
index 0000000..f597f0d
--- /dev/null
+++ b/ui/src/app/components/botCommands/BotCommands.component.ts
@@ -0,0 +1,9 @@
+import {Component} from "@angular/core";
+
+@Component({
+ selector: "app-bot-commands",
+ template: ``
+})
+export class BotCommandsComponent {
+
+}
diff --git a/ui/src/app/components/messages/MessageHistory.component.ts b/ui/src/app/components/messages/MessageHistory.component.ts
new file mode 100644
index 0000000..df572d7
--- /dev/null
+++ b/ui/src/app/components/messages/MessageHistory.component.ts
@@ -0,0 +1,9 @@
+import {Component} from "@angular/core";
+
+@Component({
+ selector: "app-message-history",
+ template: ``
+})
+export class MessageHistoryComponent {
+
+}
diff --git a/ui/src/app/components/nodes/AllNodes.component.ts b/ui/src/app/components/nodes/AllNodes.component.ts
new file mode 100644
index 0000000..5eac8c3
--- /dev/null
+++ b/ui/src/app/components/nodes/AllNodes.component.ts
@@ -0,0 +1,9 @@
+import {Component} from "@angular/core";
+
+@Component({
+ selector: "app-all-nodes",
+ template: ``
+})
+export class AllNodesComponent {
+
+}
diff --git a/ui/src/app/components/nodes/DirectNodes.component.ts b/ui/src/app/components/nodes/DirectNodes.component.ts
new file mode 100644
index 0000000..bffd966
--- /dev/null
+++ b/ui/src/app/components/nodes/DirectNodes.component.ts
@@ -0,0 +1,39 @@
+import {Component, OnInit} from "@angular/core";
+import {HttpClient} from "@angular/common/http";
+import {NodeDTO} from "../../entities/NodeDTO";
+
+@Component({
+ selector: 'app-direct-nodes',
+ styleUrls: ['nodes.styles.scss'],
+ template: `
+
+
+
+
+ {{node.long_name}}
+ {{node.short_name}} ({{node.num}})
+
+
+ график
+
+
+
+
+
+
+
+
+ `
+})
+export class DirectNodesComponent implements OnInit {
+ constructor(private http: HttpClient) {
+ }
+
+ nodes: NodeDTO[] = [];
+
+ ngOnInit(): void {
+ this.http.get(`api/nodes/direct`).subscribe(
+ (res) => this.nodes = res as NodeDTO[]
+ )
+ }
+}
diff --git a/ui/src/app/components/nodes/nodes.styles.scss b/ui/src/app/components/nodes/nodes.styles.scss
new file mode 100644
index 0000000..8b71b74
--- /dev/null
+++ b/ui/src/app/components/nodes/nodes.styles.scss
@@ -0,0 +1,11 @@
+.card-wrapper {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
+ gap: 8px;
+}
+
+.grid-card {
+ width: 100%;
+ height: 100%;
+ /* Additional styling as needed */
+}
diff --git a/ui/src/app/entities/NodeDTO.ts b/ui/src/app/entities/NodeDTO.ts
new file mode 100644
index 0000000..406a78e
--- /dev/null
+++ b/ui/src/app/entities/NodeDTO.ts
@@ -0,0 +1,7 @@
+import {NodeMiniDTO} from "./NodeMiniDTO";
+
+export interface NodeDTO extends NodeMiniDTO {
+ snr: number,
+ hops_away: number,
+ ts: number
+}
diff --git a/ui/src/app/entities/NodeMiniDTO.ts b/ui/src/app/entities/NodeMiniDTO.ts
new file mode 100644
index 0000000..a802fe1
--- /dev/null
+++ b/ui/src/app/entities/NodeMiniDTO.ts
@@ -0,0 +1,5 @@
+export interface NodeMiniDTO {
+ num: number,
+ long_name: string,
+ short_name: string
+}
diff --git a/ui/src/proxy.conf.json b/ui/src/proxy.conf.json
new file mode 100644
index 0000000..5aa24d7
--- /dev/null
+++ b/ui/src/proxy.conf.json
@@ -0,0 +1,6 @@
+{
+ "/api": {
+ "target": "http://192.168.3.2:8680/",
+ "secure": false
+ }
+}
diff --git a/webExtensions/messageList.py b/webExtensions/messageList.py
index 99290f5..348d42a 100644
--- a/webExtensions/messageList.py
+++ b/webExtensions/messageList.py
@@ -17,8 +17,12 @@ class WebExtension:
self.dbStore = core.dbStore
@self.app.get(f"{self.core.context}/messages")
+ @self.core.authManager.authRequest()
async def listOfMessages(limit: int = Query(10), offset: int = Query(0)):
collection = self.dbStore['packet']
- c = collection.find({"to": int(self.core.PUB_CH), "portnum":self.MESSAGE_PORTNUM}).sort("ts", DESCENDING).skip(offset).limit(limit)
+ c = collection.find({
+ "to": int(self.core.PUB_CH),
+ "portnum":self.MESSAGE_PORTNUM
+ }).sort("ts", DESCENDING).skip(offset).limit(limit)
l = await c.to_list()
return [MessageDTO(msg) for msg in l]
\ No newline at end of file