From 6490b7d4e73ae8cb1a7fdb0acf2e59aac56f1906 Mon Sep 17 00:00:00 2001 From: zhaojisen <1301338853@qq.com> Date: Mon, 30 Dec 2024 18:40:57 +0800 Subject: [PATCH] Perf: Optimize code structure --- src/app/model.ts | 2 +- src/app/pages/pages.component.ts | 6 +- .../pam/gui.component/gui.component.html | 20 - .../pam/gui.component/gui.component.scss | 102 ---- src/app/pages/pam/pam.component.html | 140 ++++++ src/app/pages/pam/pam.component.scss | 453 ++++++++++++++++++ .../gui.component.ts => pam.component.ts} | 151 ++++-- src/app/pages/pam/pam.module.ts | 1 + .../terminal.component.html | 94 ---- .../terminal.component.scss | 264 ---------- .../terminal.component/terminal.component.ts | 232 --------- src/app/router.module.ts | 6 +- src/app/services/http.ts | 2 +- 13 files changed, 704 insertions(+), 769 deletions(-) delete mode 100644 src/app/pages/pam/gui.component/gui.component.html delete mode 100644 src/app/pages/pam/gui.component/gui.component.scss create mode 100644 src/app/pages/pam/pam.component.html create mode 100644 src/app/pages/pam/pam.component.scss rename src/app/pages/pam/{gui.component/gui.component.ts => pam.component.ts} (63%) create mode 100644 src/app/pages/pam/pam.module.ts delete mode 100644 src/app/pages/pam/terminal.component/terminal.component.html delete mode 100644 src/app/pages/pam/terminal.component/terminal.component.scss delete mode 100644 src/app/pages/pam/terminal.component/terminal.component.ts diff --git a/src/app/model.ts b/src/app/model.ts index 68d8e846..6bce1bae 100644 --- a/src/app/model.ts +++ b/src/app/model.ts @@ -376,7 +376,7 @@ export class ConnectOption { export class AdminConnectData { asset: Asset; account: Account; - protocol: Protocol; + protocol: string; input_username: string; method: string; } diff --git a/src/app/pages/pages.component.ts b/src/app/pages/pages.component.ts index 8cf61061..4e87a30b 100644 --- a/src/app/pages/pages.component.ts +++ b/src/app/pages/pages.component.ts @@ -4,9 +4,8 @@ import { PagesBlankComponent } from "./blank/blank.component"; import { PagesReplayComponent } from "./replay/replay.component"; import { PagesConnectComponent } from "./connect/connect.component"; import { PagesMonitorComponent } from "./monitor/monitor.component"; -import { PagePamGUIComponent } from "./pam/gui.component/gui.component"; import { PagesNotFoundComponent } from "./not-found/not-found.component"; -import { PagePamTerminalComponent } from "./pam/terminal.component/terminal.component"; +import { PagePamComponent } from "./pam/pam.component"; export const PagesComponents = [ PageMainComponent, @@ -16,6 +15,5 @@ export const PagesComponents = [ PagesNotFoundComponent, PageSftpComponent, PagesMonitorComponent, - PagePamTerminalComponent, - PagePamGUIComponent, + PagePamComponent ]; diff --git a/src/app/pages/pam/gui.component/gui.component.html b/src/app/pages/pam/gui.component/gui.component.html deleted file mode 100644 index f9547f54..00000000 --- a/src/app/pages/pam/gui.component/gui.component.html +++ /dev/null @@ -1,20 +0,0 @@ -
-
-
-
- {{ totalConnectTime }} -
-
- -
-
- close -
-
- - -
diff --git a/src/app/pages/pam/gui.component/gui.component.scss b/src/app/pages/pam/gui.component/gui.component.scss deleted file mode 100644 index 2493ca56..00000000 --- a/src/app/pages/pam/gui.component/gui.component.scss +++ /dev/null @@ -1,102 +0,0 @@ -.pam-gui { - position: relative; - width: 100%; - height: 100%; - - elements-iframe { - width: 100%; - height: 100%; - display: block; - } -} - -.timer-container { - position: fixed; - bottom: 70px; - right: 20px; - z-index: 999999; - - .timer { - display: flex; - align-items: center; - background: rgba(0, 0, 0, 0.8); - padding: 6px 12px; - border-radius: 100px; - color: white; - font-family: monospace; - font-size: 14px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); - - span { - margin-left: 4px; - } - - .status-dot { - width: 8px; - height: 8px; - border-radius: 50%; - background-color: #dc3545; - margin-right: 6px; - transition: opacity 0.3s ease; - - &.active { - animation: blink 1s infinite; - } - } - } -} - -.action-icons { - position: fixed; - top: 20px; - right: 20px; - z-index: 999999; - display: flex; - gap: 10px; - opacity: 0; - visibility: hidden; - transition: opacity 0.3s ease; - transition-delay: 0s, 0.3s; - - &.show { - opacity: 1; - visibility: visible; - transition-delay: 0s; - } - - .close-icon { - display: flex; - align-items: center; - justify-content: center; - width: 32px; - height: 32px; - border-radius: 50%; - background: rgba(0, 0, 0, 0.8); - cursor: pointer; - transition: all 0.3s ease; - - &:hover { - background: rgba(0, 0, 0, 0.9); - transform: scale(1.05); - } - - mat-icon { - color: white; - font-size: 20px; - width: 20px; - height: 20px; - } - } -} - -@keyframes blink { - 0% { - opacity: 1; - } - 50% { - opacity: 0.4; - } - 100% { - opacity: 1; - } -} diff --git a/src/app/pages/pam/pam.component.html b/src/app/pages/pam/pam.component.html new file mode 100644 index 00000000..c5854ab8 --- /dev/null +++ b/src/app/pages/pam/pam.component.html @@ -0,0 +1,140 @@ +
+
+ + +
+ close + +

文件管理

+
+ + + +
+
+ home +
/
+
+
+
+ + + + + SSH 会话 + + + +
+
+ dvr +
+
+ 资产信息: +
+ {{ assetName }} +
+
+ +
+ date_range + +
+
+ 连接时间: +
+ {{ startTime.toLocaleString() }} +
+
+
+ +
+
+
+
+ {{ totalConnectTime }} +
+
+ + + +
+
+ folder +
+ +
+ close +
+
+
+
+
+ + + + + + + +
+
+
+ +
+
+
+
+ {{ totalConnectTime }} +
+
+ +
+
+ close +
+
+ + +
+ +
+
+
+
+ {{ totalConnectTime }} +
+
+ +
+
+ close +
+
+ + +
+
diff --git a/src/app/pages/pam/pam.component.scss b/src/app/pages/pam/pam.component.scss new file mode 100644 index 00000000..f3d145bd --- /dev/null +++ b/src/app/pages/pam/pam.component.scss @@ -0,0 +1,453 @@ +@mixin time-container { + position: fixed; + bottom: 40px; + right: 40px; + z-index: 999999; + + .timer { + display: flex; + align-items: center; + background: rgba(0, 0, 0, 0.8); + padding: 6px 12px; + border-radius: 100px; + color: white; + font-family: monospace; + font-size: 14px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); + + span { + margin-left: 4px; + } + + .status-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background-color: #dc3545; + margin-right: 6px; + transition: opacity 0.3s ease; + + &.active { + animation: blink 1s infinite; + } + } + } + + @media screen and (max-width: 768px) { + bottom: calc(env(safe-area-inset-bottom, 0px) + 20px); + right: 50%; + transform: translateX(50%); + } +} + +@mixin time-icons { + position: fixed; + top: 20px; + right: 20px; + z-index: 999999; + display: flex; + gap: 10px; + opacity: 0; + visibility: hidden; + transition: opacity 0.3s ease; + transition-delay: 0s, 0.3s; + + &.show { + opacity: 1; + visibility: visible; + transition-delay: 0s; + } + + .close-icon { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border-radius: 50%; + background: rgba(0, 0, 0, 0.8); + cursor: pointer; + transition: all 0.3s ease; + + &:hover { + background: rgba(0, 0, 0, 0.9); + transform: scale(1.05); + } + + mat-icon { + color: white; + font-size: 20px; + width: 20px; + height: 20px; + } + } +} + +.terminal-connect { + display: flex; + justify-content: center; + height: 100vh; + width: 100vw; + + .file-manage-drawer { + position: absolute; + width: 800px; + z-index: 999999; + + @media screen and (max-width: 768px) { + width: 100% !important; + } + } + + .mat-drawer-container { + width: 100%; + + .mat-sidenav-content { + height: 100vh; + overflow: hidden; + + @media screen and (max-width: 768px) { + height: calc(100vh - env(safe-area-inset-bottom)); + padding-bottom: env(safe-area-inset-bottom); + + mat-card { + &:first-child { + margin: 0; + border-radius: 0; + + mat-card-header { + flex-direction: column; + height: auto !important; + padding: 15px; + + .mat-card-title { + font-size: 18px; + margin-bottom: 15px; + padding-bottom: 10px; + border-bottom: 1px solid #eee; + width: 100%; + text-align: center; + } + + .info-section { + width: 100%; + margin: 0; + padding: 10px 0; + + .top-part, + .bottom-part { + padding: 8px 0; + margin: 0; + gap: 10px; + + .mat-icon { + color: #666; + } + + .desc { + flex: 1; + display: flex; + flex-direction: column; + gap: 5px; + + div:first-child { + color: #666; + font-size: 13px; + } + + div:last-child { + color: #333; + font-weight: 500; + } + } + } + } + + .action-section { + width: 100%; + margin-top: 10px; + padding-top: 15px; + border-top: 1px solid #eee; + + .timer-container { + @include time-container; + } + + .mat-divider { + display: none; + } + + .actions { + width: 100%; + justify-content: space-around; + + .action-icon { + display: flex; + flex-direction: column; + align-items: center; + gap: 5px; + padding: 8px 15px; + + .mat-icon { + font-size: 24px; + width: 24px; + height: 24px; + } + + &::after { + content: attr(mattooltip); + font-size: 12px; + color: #666; + } + } + } + } + } + } + + &:last-child { + margin: 0; + height: calc(100% - 355px) !important; + background-color: #f7f8f9; + } + } + } + } + } + + ::ng-deep .mat-card-header-text { + display: flex; + align-items: center; + height: 100%; + + .mat-card-title { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + margin: 0 20px; + } + } + + .mat-divider { + height: 100%; + border-right-width: 2px; + border-right-color: #dcddde; + } + + .info-section { + display: flex; + flex: 2; + flex-direction: column; + justify-content: center; + align-items: flex-start; + height: 100%; + margin: 0 20px; + font-size: 14px; + + @media screen and (max-width: 768px) { + margin: 15px 0; + padding: 0 10px; + + flex: none; + width: 100%; + height: auto; + } + + .top-part, + .bottom-part { + display: flex; + align-items: center; + width: 100%; + height: 50%; + color: #313538; + font-size: 14px; + + @media screen and (max-width: 768px) { + height: auto; + margin: 8px 0; + + .mat-icon { + margin-right: 8px; + } + + .desc { + display: flex; + flex-wrap: wrap; + align-items: center; + margin-left: 0; + + div { + display: flex; + align-items: center; + + &:first-child { + margin-right: 8px; + } + } + } + } + + .mat-icon { + width: 20px; + height: 20px; + font-size: 20px; + cursor: pointer; + } + + .desc { + display: flex; + align-items: stretch; + margin-left: 5px; + } + } + } + + .action-section { + display: flex; + flex: 1; + justify-content: flex-end; + align-items: center; + height: 100%; + padding: 0 20px; + + .timer-container { + @include time-container; + } + + @media screen and (max-width: 768px) { + width: 100%; + margin-top: 15px; + padding: 15px 0 0; + border-top: 1px solid #eee; + + .mat-divider { + display: none; + } + + .actions { + width: 100%; + display: grid !important; + grid-template-columns: repeat(2, 1fr); + gap: 15px !important; + padding: 0 10px; + + .action-icon { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background: #f5f6f7; + border-radius: 8px; + padding: 12px; + gap: 8px; + height: auto; + transition: all 0.2s ease; + + &:hover { + background: #eef0f2; + transform: translateY(-1px); + } + + .mat-icon { + font-size: 24px !important; + width: 24px !important; + height: 24px !important; + color: #666; + } + + &::after { + content: attr(mattooltip); + font-size: 13px; + color: #666; + font-weight: 500; + } + } + } + } + + .mat-divider { + height: 40%; + margin: 0 20px; + } + + .actions { + display: flex; + align-items: center; + gap: 20px; + + .action-icon { + display: flex; + align-items: center; + height: 100%; + cursor: pointer; + transition: all 0.3s ease-in-out; + padding: 8px; + + &:hover { + color: #1ab394; + } + + .mat-icon { + font-size: 20px; + width: 20px; + height: 20px; + } + } + } + } +} + +.sftp-connect { + position: relative; + display: flex; + justify-content: center; + height: 100vh; + width: 100vw; + + .timer-container { + @include time-container; + } + + .action-icons { + @include time-icons; + } + + pages-sftp { + width: 100%; + height: 100%; + display: block; + } +} + +.pam-gui { + position: relative; + width: 100%; + height: 100%; + + .timer-container { + @include time-container; + } + + .action-icons { + @include time-icons; + } + + elements-iframe { + width: 100%; + height: 100%; + display: block; + } +} + +@keyframes blink { + 0% { + opacity: 1; + } + 50% { + opacity: 0.4; + } + 100% { + opacity: 1; + } +} diff --git a/src/app/pages/pam/gui.component/gui.component.ts b/src/app/pages/pam/pam.component.ts similarity index 63% rename from src/app/pages/pam/gui.component/gui.component.ts rename to src/app/pages/pam/pam.component.ts index b9f604ed..21a8a8a2 100644 --- a/src/app/pages/pam/gui.component/gui.component.ts +++ b/src/app/pages/pam/pam.component.ts @@ -1,54 +1,61 @@ +import { MatSidenav } from "@angular/material/sidenav"; import { ActivatedRoute, Params } from "@angular/router"; -import { Component, OnInit, OnDestroy, ViewChild } from "@angular/core"; import { Protocol, Account, Endpoint, Asset } from "@app/model"; +import { HttpService, I18nService, LogService } from "@app/services"; import { - HttpService, - DialogService, - I18nService, - LogService, -} from "@app/services"; -import { MatSidenav } from "@angular/material/sidenav"; + Component, + ViewChild, + OnInit, + OnDestroy, + ElementRef, +} from "@angular/core"; @Component({ - selector: "pages-pam-gui", - templateUrl: "./gui.component.html", - styleUrls: ["./gui.component.scss"], + selector: "pages-pam", + templateUrl: "./pam.component.html", + styleUrls: ["./pam.component.scss"], }) -export class PagePamGUIComponent implements OnInit, OnDestroy { +export class PagePamComponent implements OnInit, OnDestroy { @ViewChild("sidenav", { static: false }) sidenav: MatSidenav; + @ViewChild("iFrame", { static: false }) iframeRef: ElementRef; public startTime: Date; public endpoint: Endpoint; public userId: string = ""; - public username: string = ""; public assetId: string = ""; + public username: string = ""; public assetName: string = ""; - public iframeURL: string = ""; + public protocol: string = ""; + + public iframeRDPURL: string = ""; + public iframeVNCURL: string = ""; + public iframeSFTPURL: string = ""; + public iframeTerminalURL: string = ""; + public totalConnectTime: string = "00:00:00"; public isActive: boolean = true; + public showActionIcons: boolean = false; private timerInterval: any; private pausedElapsedTime: number = 0; - public showActionIcons: boolean = false; - constructor( private _http: HttpService, private _i18n: I18nService, private _logger: LogService, - private _dialogAlert: DialogService, - private route: ActivatedRoute + private _route: ActivatedRoute ) { this.startTime = new Date(); } - ngOnInit(): void { - this.route.params.subscribe(async (params: Params) => { + ngOnInit() { + this._route.params.subscribe(async (params: Params) => { this.userId = params["userId"]; this.username = params["username"]; this.assetId = params["assetId"]; this.assetName = params["assetName"]; + this.protocol = params["protocol"]; this._http.getAssetDetail(this.assetId).subscribe((asset: Asset) => { const currentUserInfo = asset.permed_accounts.find( @@ -56,24 +63,23 @@ export class PagePamGUIComponent implements OnInit, OnDestroy { ); let method: string = ""; - let protocol: Protocol = asset.permed_protocols[0]; - switch (protocol.name) { + switch (this.protocol) { case "ssh": case "telnet": - method = "ssh_client"; + method = "web_cli"; break; case "rdp": - method = "mstsc"; + method = "web_gui"; break; case "sftp": - method = "sftp_client"; + method = "web_sftp"; break; case "vnc": - method = "vnc_client"; + method = "web_gui"; break; default: - method = "db_client"; + method = "web_cli"; } const assetMessage = { @@ -89,7 +95,7 @@ export class PagePamGUIComponent implements OnInit, OnDestroy { }; const connectData = { method, - protocol, + protocol: this.protocol, asset: assetMessage, account: currentUserInfo, input_username: this.username, @@ -101,7 +107,31 @@ export class PagePamGUIComponent implements OnInit, OnDestroy { if (res) { const url = this.getUrl(); - this.iframeURL = `${url}/lion/connect?token=${res.id}`; + switch (this.protocol) { + case "ssh": { + this.iframeTerminalURL = `${url}/koko/connect?token=${res.id}`; + break; + } + case "sftp": { + this.iframeSFTPURL = `${url}/koko/elfinder/sftp/`; + break; + } + case "rdp": { + this.iframeRDPURL = `${url}/lion/connect?token=${res.id}`; + break; + } + case "vnc": { + this.iframeVNCURL = `${url}/lion/connect?token=${res.id}`; + break; + } + case "telnet": { + this.iframeTerminalURL = `${url}/koko/connect?token=${res.id}`; + break; + } + default: { + break; + } + } } }); }); @@ -127,12 +157,45 @@ export class PagePamGUIComponent implements OnInit, OnDestroy { document.addEventListener("mousemove", this.handleMouseMove.bind(this)); } + ngOnDestroy() { + this.stopTimer(); + document.removeEventListener("mousemove", this.handleMouseMove.bind(this)); + } + + /** + * 关闭当前连接 + */ public async handleCloseConnect() { - window.confirm("确定要关闭当前连接吗?"); + if (window.confirm("确定要关闭当前连接吗?")) { + window.close(); + } + } + + /** + * 打开文件管理器 + */ + public handleOpenFileManage() { + const iframeWindow = (this.iframeRef as unknown as { iframeWindow: Window }) + .iframeWindow; - window.close(); + if (iframeWindow) { + iframeWindow.postMessage({ name: "FILE" }, "*"); + this._logger.info(`[Luna] Send FILE`); + } + } + + /** + * windows 的关闭按钮 + * @param event + */ + private handleMouseMove(event: MouseEvent): void { + this.showActionIcons = event.clientY <= 65; } + /** + * @description 获取当前 host 的信息 + * @returns + */ private getUrl(): string { let host: string = ""; @@ -165,6 +228,15 @@ export class PagePamGUIComponent implements OnInit, OnDestroy { } } + /** + * 补零 + * @param value + * @returns + */ + private padZero(value: number): string { + return String(value).padStart(2, "0"); + } + /** * 更新连接时间 */ @@ -183,24 +255,9 @@ export class PagePamGUIComponent implements OnInit, OnDestroy { } /** - * 补零 - * @param value - * @returns + * 关闭抽屉 */ - private padZero(value: number): string { - return String(value).padStart(2, "0"); - } - - private handleMouseMove(event: MouseEvent): void { - this.showActionIcons = event.clientY <= 65; - } - - ngOnDestroy() { - this.stopTimer(); - document.removeEventListener("mousemove", this.handleMouseMove.bind(this)); - } - - public closeDrawer() { + private closeDrawer(): void { this.sidenav.close(); } } diff --git a/src/app/pages/pam/pam.module.ts b/src/app/pages/pam/pam.module.ts new file mode 100644 index 00000000..0519ecba --- /dev/null +++ b/src/app/pages/pam/pam.module.ts @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/app/pages/pam/terminal.component/terminal.component.html b/src/app/pages/pam/terminal.component/terminal.component.html deleted file mode 100644 index 20543c36..00000000 --- a/src/app/pages/pam/terminal.component/terminal.component.html +++ /dev/null @@ -1,94 +0,0 @@ -
- - -
- close - -

文件管理

-
- - - -
-
- home -
/
-
-
-
- - - - - SSH 会话 - - - -
-
- dvr -
-
资产信息:
- {{ assetName }} -
-
- -
- date_range - -
-
连接时间:
- {{ startTime.toLocaleString() }} -
-
-
- -
-
-
-
- {{ totalConnectTime }} -
-
- - - -
-
- folder -
- -
- close -
-
-
-
-
- - - - - - - -
-
-
diff --git a/src/app/pages/pam/terminal.component/terminal.component.scss b/src/app/pages/pam/terminal.component/terminal.component.scss deleted file mode 100644 index 160ea343..00000000 --- a/src/app/pages/pam/terminal.component/terminal.component.scss +++ /dev/null @@ -1,264 +0,0 @@ -.pam-connect { - display: flex; - justify-content: center; - height: 100vh; - width: 100vw; - - .file-manage-drawer { - position: absolute; - width: 800px; - z-index: 999999; - } - - .mat-drawer-container { - width: 100%; - - ::ng-deep .mat-drawer-inner-container { - //overflow: hidden; - - .drawer-header { - display: flex; - justify-content: flex-start; - align-items: center; - width: 100%; - padding: 0 20px; - - p { - font-size: 16px; - font-weight: 700; - margin-left: 10px; - } - } - - .mat-divider { - height: 10px; - } - - .drawer-manage { - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 20px; - height: 50px; - - .path { - display: flex; - align-items: center; - justify-content: flex-start; - flex: 2; - height: 100%; - - .delimiter { - margin-left: 5px; - font-size: 1.3rem; - } - } - - .upload-btn { - position: relative; - display: flex; - align-items: center; - justify-content: flex-end; - flex: 1; - - .mat-raised-button { - display: flex; - justify-content: center; - align-items: center; - height: 30px; - width: 100px; - } - - input[type="file"] { - opacity: 0; - position: absolute; - width: 100%; - height: 100%; - cursor: pointer; - } - } - } - - .drawer-table { - width: 100%; - height: calc(100% - 114px); - overflow: auto; - - .mat-table { - width: inherit; - height: 100%; - //box-shadow: unset; - } - - table { - width: 100%; - - ::ng-deep .mat-column-name { - width: 500px; - } - - ::ng-deep .mat-cell { - } - } - - &::-webkit-scrollbar { - width: 10px; - height: 10px; - } - - &::-webkit-scrollbar-track { - background: #f1f1f1; - } - - &::-webkit-scrollbar-thumb { - background: #888; - border-radius: 10px; - } - - &::-webkit-scrollbar-thumb:hover { - background: #555; - } - } - } - - ::ng-deep .mat-drawer-content { - overflow: hidden; - } - } - - ::ng-deep .mat-card-header-text { - display: flex; - align-items: center; - height: 100%; - - .mat-card-title { - display: flex; - align-items: center; - justify-content: center; - height: 100%; - margin: 0 20px; - } - } - - .mat-divider { - height: 100%; - border-right-width: 2px; - border-right-color: #dcddde; - } - - .info-section { - display: flex; - flex: 2; - flex-direction: column; - justify-content: center; - align-items: flex-start; - height: 100%; - margin: 0 20px; - font-size: 14px; - - .top-part, - .bottom-part { - display: flex; - align-items: center; - width: 100%; - height: 50%; - color: #313538; - font-size: 14px; - - .mat-icon { - width: 20px; - height: 20px; - font-size: 20px; - cursor: pointer; - } - - .desc { - display: flex; - align-items: stretch; - margin-left: 5px; - } - } - } - - .action-section { - display: flex; - flex: 1; - justify-content: flex-end; - align-items: center; - height: 100%; - padding: 0 20px; - - .mat-divider { - height: 40%; - margin: 0 20px; - } - - .actions { - display: flex; - align-items: center; - gap: 20px; - - .action-icon { - display: flex; - align-items: center; - height: 100%; - cursor: pointer; - transition: all 0.3s ease-in-out; - padding: 8px; - - &:hover { - color: #1ab394; - } - - .mat-icon { - font-size: 20px; - width: 20px; - height: 20px; - } - } - } - } -} - -.timer-container { - .timer { - display: flex; - align-items: center; - background: rgba(0, 0, 0, 0.8); - padding: 6px 12px; - border-radius: 100px; - color: white; - font-family: monospace; - font-size: 14px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); - - span { - margin-left: 4px; - } - - .status-dot { - width: 8px; - height: 8px; - border-radius: 50%; - background-color: #dc3545; - margin-right: 6px; - transition: opacity 0.3s ease; - - &.active { - animation: blink 1s infinite; - } - } - } -} - -@keyframes blink { - 0% { - opacity: 1; - } - 50% { - opacity: 0.4; - } - 100% { - opacity: 1; - } -} diff --git a/src/app/pages/pam/terminal.component/terminal.component.ts b/src/app/pages/pam/terminal.component/terminal.component.ts deleted file mode 100644 index 61f6ffa7..00000000 --- a/src/app/pages/pam/terminal.component/terminal.component.ts +++ /dev/null @@ -1,232 +0,0 @@ -import { ActivatedRoute } from "@angular/router"; -import { MatSidenav } from "@angular/material/sidenav"; -import { - Account, - Asset, - ConnectData, - ConnectionToken, - View, - Protocol, -} from "@app/model"; -import { - LogService, - AppService, - HttpService, - I18nService, - DialogService, - ConnectTokenService, -} from "@app/services"; -import { - OnInit, - Output, - Component, - ViewChild, - OnDestroy, - ElementRef, - EventEmitter, -} from "@angular/core"; - -export interface PeriodicElement { - name: string; - size: string; - modificationTime: string; - attributes: string; - action: object; -} - -@Component({ - selector: "pages-pam-terminal", - templateUrl: "./terminal.component.html", - styleUrls: ["./terminal.component.scss"], -}) -export class PagePamTerminalComponent implements OnInit, OnDestroy { - @ViewChild("sidenav", { static: false }) sidenav: MatSidenav; - @ViewChild("iFrame", { static: false }) iframeRef: ElementRef; - @Output() onNewView: EventEmitter = new EventEmitter(); - - constructor( - private _http: HttpService, - private _i18n: I18nService, - private _logger: LogService, - private route: ActivatedRoute - ) { - this.checkPageVisibility(); - this.startTime = new Date(); - } - - public isActive: boolean = true; - public iframeWindow: Window; - - public userId: string = ""; - public username: string = ""; - public assetId: string = ""; - public assetName: string = ""; - - public connectType: string = "SSH"; - public totalConnectTime: string; - - iframeURL: string; - - private pausedElapsedTime: number = 0; - private isTimerPaused: boolean = false; - - private startTime: Date; - private timerInterval: any; - - async ngOnInit(): Promise { - this.route.params.subscribe(async (params) => { - this.userId = params["userId"]; - this.username = params["username"]; - this.assetId = params["assetId"]; - this.assetName = params["assetName"]; - - this._http - .getAssetDetail(this.assetId) - .subscribe(async (asset: Asset) => { - const currentUserInfo = asset.permed_accounts.find( - (item: Account) => item.id === this.userId - ); - - let method: string = ""; - let protocol: Protocol = asset.permed_protocols[0]; - - switch (protocol.name) { - case "ssh": - case "telnet": - method = "ssh_client"; - break; - case "rdp": - method = "mstsc"; - break; - case "sftp": - method = "sftp_client"; - break; - case "vnc": - method = "vnc_client"; - break; - default: - method = "db_client"; - } - - const assetMessage = { - id: this.assetId, - name: this.assetName, - address: asset.address, - comment: asset.comment, - type: asset.type, - category: asset.category, - permed_protocols: asset.permed_protocols, - permed_accounts: asset.permed_accounts, - spec_info: asset.spec_info, - }; - const connectData = { - method, - protocol, - asset: assetMessage, - account: currentUserInfo, - input_username: this.username, - }; - - this._http - .adminConnectToken(assetMessage, connectData) - .subscribe((res) => { - if (res) { - const url = this.getUrl(); - - this.iframeURL = `${url}/koko/connect?token=${res.id}`; - } - }); - }); - }); - - this.startTimer(); - - document.addEventListener("visibilitychange", () => { - if (document.hidden) { - this.isActive = false; - this.stopTimer(); - const currentTime = new Date().getTime(); - this.pausedElapsedTime += currentTime - this.startTime.getTime(); - } else { - setTimeout(() => { - this.isActive = true; - this.startTime = new Date(); - this.startTimer(); - }, 0); - } - }); - } - - public handleCloseConnect() { - window.confirm("确定要关闭当前连接吗?"); - - window.close(); - } - - public closeDrawer() { - this.sidenav.close(); - } - - public handleOpenFileManage() { - const iframeWindow = (this.iframeRef as unknown as { iframeWindow: Window }) - .iframeWindow; - - if (iframeWindow) { - iframeWindow.postMessage({ name: "FILE" }, "*"); - this._logger.info(`[Luna] Send FILE`); - } - } - - private getUrl(): string { - let host: string = ""; - - const endpoint = window.location.host.split(":")[0]; - const protocole = window.location.protocol; - const port = "9530"; - - if (port) { - host = `${endpoint}:${port}`; - } - - this._logger.info(`Current host: ${protocole}//${host}`); - - return `${protocole}//${host}`; - } - - private checkPageVisibility() { - this.isActive = !document.hidden; - } - - private padZero(value: number): string { - return String(value).padStart(2, "0"); - } - - private updateConnectTime(): void { - const currentTime = new Date(); - const elapsed = - currentTime.getTime() - this.startTime.getTime() + this.pausedElapsedTime; - - const hours = Math.floor((elapsed / (1000 * 60 * 60)) % 24); - const minutes = Math.floor((elapsed / (1000 * 60)) % 60); - const seconds = Math.floor((elapsed / 1000) % 60); - - this.totalConnectTime = `${this.padZero(hours)}:${this.padZero( - minutes - )}:${this.padZero(seconds)}`; - } - - private startTimer(): void { - this.timerInterval = setInterval(() => this.updateConnectTime(), 1000); - } - - private stopTimer(): void { - clearInterval(this.timerInterval); - this.isTimerPaused = true; - } - - ngOnDestroy(): void { - if (this.timerInterval) { - clearInterval(this.timerInterval); - } - } -} diff --git a/src/app/router.module.ts b/src/app/router.module.ts index 4d57c8da..92702727 100644 --- a/src/app/router.module.ts +++ b/src/app/router.module.ts @@ -7,8 +7,7 @@ import { PagesReplayComponent } from "./pages/replay/replay.component"; import { PagesMonitorComponent } from "./pages/monitor/monitor.component"; import { PageMainComponent } from "./pages/main/main.component"; import { PageSftpComponent } from "./pages/sftp/sftp.component"; -import { PagePamGUIComponent } from "./pages/pam/gui.component/gui.component"; -import { PagePamTerminalComponent } from "./pages/pam/terminal.component/terminal.component"; +import { PagePamComponent } from "./pages/pam/pam.component"; const appRoutes: Routes = [ { path: "replay/:sid", component: PagesReplayComponent }, @@ -17,8 +16,7 @@ const appRoutes: Routes = [ { path: "sftp", component: PageSftpComponent }, { path: "undefined", component: PagesBlankComponent }, { path: "", component: PageMainComponent }, - { path: "pam_terminal_connect/:userId/:username/:assetId/:assetName", component: PagePamTerminalComponent }, - { path: "pam_gui_connect/:userId/:username/:assetId/:assetName", component: PagePamGUIComponent }, + { path: "pam_connect/:userId/:username/:assetId/:assetName/:protocol", component: PagePamComponent }, // { path: '**', component: PagesNotFoundComponent } ]; diff --git a/src/app/services/http.ts b/src/app/services/http.ts index b84b6ff6..9b726025 100644 --- a/src/app/services/http.ts +++ b/src/app/services/http.ts @@ -289,7 +289,7 @@ export class HttpService { const data = { asset: asset.id, account: account.alias, - protocol: protocol.name, + protocol: protocol, input_username: connectData.input_username, connect_method: connectData.method, };