Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions src/app/elements/asset-tree/asset-tree.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
import { ConnectEvt, InitTreeConfig, TreeNode } from '@app/model';
import { CookieService } from 'ngx-cookie-service';
import { HttpHeaders } from '@angular/common/http';
import { withSitePrefix } from '@app/utils/path';

declare var $: any;

Expand Down Expand Up @@ -260,8 +261,8 @@ export class ElementAssetTreeComponent implements OnInit {
const config = {
refresh,
showFavoriteAssets: true,
url: '/api/v1/perms/users/self/nodes/all-with-assets/tree/',
asyncUrl: '/api/v1/perms/users/self/nodes/children-with-assets/tree/?'
url: withSitePrefix('/api/v1/perms/users/self/nodes/all-with-assets/tree/'),
asyncUrl: withSitePrefix('/api/v1/perms/users/self/nodes/children-with-assets/tree/?')
};
const tree = new Tree('AssetTree', 'My assets', false, true, true, true, config);
if (!refresh) {
Expand All @@ -273,8 +274,8 @@ export class ElementAssetTreeComponent implements OnInit {
async initTypeTree(refresh = false) {
const config = {
refresh,
url: '/api/v1/perms/users/self/nodes/children-with-assets/category/tree/?sync=1',
asyncUrl: '/api/v1/perms/users/self/nodes/children-with-assets/category/tree/',
url: withSitePrefix('/api/v1/perms/users/self/nodes/children-with-assets/category/tree/?sync=1'),
asyncUrl: withSitePrefix('/api/v1/perms/users/self/nodes/children-with-assets/category/tree/'),
setting: {
async: {
autoParam: ['type', 'category']
Expand Down
6 changes: 6 additions & 0 deletions src/app/elements/chat/chat.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,12 +213,18 @@ export class ElementChatComponent implements OnInit, OnDestroy, AfterViewInit {
if (data && data.content) {
this.postCurrentTerminalContextToChatAI();
}
if (!this.iframeRef?.nativeElement?.contentWindow) {
return;
}
this.iframeRef.nativeElement.contentWindow.postMessage('show-chat-panel');
this.chatAIShown = true;
this.showBtn = false;
}

postCurrentTerminalContextToChatAI() {
if (!this.iframeRef?.nativeElement?.contentWindow) {
return;
}
const data = this.currentView.terminalContentData;
const content = {
viewId: this.currentView.id,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {Component, Inject, OnInit} from '@angular/core';
import {HttpService} from '@app/services';
import {NZ_MODAL_DATA, NzModalRef} from 'ng-zorro-antd/modal';
import { Component, Inject, OnInit } from '@angular/core';
import { HttpService } from '@app/services';
import { NZ_MODAL_DATA, NzModalRef } from 'ng-zorro-antd/modal';
import { withSitePrefix } from '@app/utils/path';

@Component({
standalone: false,
Expand All @@ -25,7 +26,7 @@

async getVariableFormMeta() {
const adhoc = this.command.id;
const url = `/api/v1/ops/variables/form-data/?t=${new Date().getTime()}&adhoc=${adhoc}`;
const url = withSitePrefix(`/api/v1/ops/variables/form-data/?t=${new Date().getTime()}&adhoc=${adhoc}`);

Check warning on line 29 in src/app/elements/content/content-footer/send-command-with-variable-dialog/send-command-with-variable-dialog.component.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `Date.now()` over `Date#getTime()`.

See more on https://sonarcloud.io/project/issues?id=jumpserver_luna&issues=AZ1RqmHGKQyapgKNihhA&open=AZ1RqmHGKQyapgKNihhA&pullRequest=1516
const res: any = await this._http.options(url).toPromise();
this.formConfig = res.actions.GET;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Component, ElementRef, Input, OnInit, ViewChild} from '@angular/core';
import {Asset, Endpoint, View} from '@app/model';
import {joinEndpointUrl} from '@app/utils/path';
import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
import { Asset, Endpoint, View } from '@app/model';
import { getSitePrefix, joinEndpointUrl, withSitePrefix } from '@app/utils/path';

@Component({
standalone: false,
Expand All @@ -11,18 +11,17 @@ import {joinEndpointUrl} from '@app/utils/path';
export class ElementConnectorDefaultComponent implements OnInit {
@Input() view: View;
@Input() connector: string;
@ViewChild('terminal', {static: false}) el: ElementRef;
@ViewChild('terminal', { static: false }) el: ElementRef;
iframeURL: string;
baseUrl: string;
asset: Asset;
protocol: string;
endpoint: Endpoint;

constructor() {
}
constructor() {}

ngOnInit() {
const {asset, protocol, smartEndpoint} = this.view;
const { asset, protocol, smartEndpoint } = this.view;
this.asset = asset;
this.protocol = protocol;
this.endpoint = smartEndpoint;
Expand All @@ -39,18 +38,29 @@ export class ElementConnectorDefaultComponent implements OnInit {
}
const endpointUrl = this.endpoint.getUrl();
const token = this.view.connectToken.id;

switch (this.connector) {
case 'chen':
const url = joinEndpointUrl(endpointUrl, `/chen/connect?token=${token}`);
case 'chen': {
const params = new URLSearchParams({ token });
const disableautohash = this.view.getConnectOption('disableautohash');
const sitePrefix = getSitePrefix();

if (disableautohash) {
return `${url}&disableautohash=true`;
params.set('disableautohash', 'true');
}
return url;

if (sitePrefix) {
params.set('site_prefix', sitePrefix);
}

return joinEndpointUrl(endpointUrl, withSitePrefix(`/chen/connect?${params.toString()}`));
}
case 'lion':
return joinEndpointUrl(endpointUrl, `/lion/connect?token=${token}`);
return joinEndpointUrl(endpointUrl, withSitePrefix(`/lion/connect?token=${token}`));
case 'default':
return joinEndpointUrl(endpointUrl, `/koko/connect?token=${token}`);
return joinEndpointUrl(endpointUrl, withSitePrefix(`/koko/connect?token=${token}`));
default:
return '';
}
}
}
53 changes: 33 additions & 20 deletions src/app/elements/content/content-window/koko/koko.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
} from '@app/services';
import { User } from '@app/globals';
import { Command, InfoItem } from '../guide/model';
import { joinEndpointUrl } from '@app/utils/path';
import { getSitePrefix, joinEndpointUrl, withSitePrefix } from '@app/utils/path';

@Component({
standalone: false,
Expand Down Expand Up @@ -152,31 +152,45 @@
}

generateNodeConnectUrl() {
const params = {};
params['disableautohash'] = this.view.getConnectOption('disableautohash');
params['token'] = this.view.connectToken.id;

params['_'] = Date.now().toString();
const query = Object.entries(params)
.map(([key, value]) => {
return `${key}=${value}`;
})
.reduce((a, b) => {
return `${a}&${b}`;
});
const params = new URLSearchParams({
token: this.view.connectToken.id,
_: Date.now().toString()
});
const disableautohash = this.view.getConnectOption('disableautohash');
const sitePrefix = getSitePrefix();

if (disableautohash !== undefined && disableautohash !== null) {
params.set('disableautohash', String(disableautohash));
}

if (sitePrefix) {
params.set('site_prefix', sitePrefix);
}

const query = params.toString();
const kokoPath = withSitePrefix(`/koko/connect/?${query}`);
const k8sPath = withSitePrefix(`/koko/k8s/?${query}`);

if (this.protocol === 'k8s') {
return (this.iframeURL = joinEndpointUrl(this.baseUrl, `/koko/k8s/?${query}`));
return (this.iframeURL = joinEndpointUrl(this.baseUrl, k8sPath));

Check warning on line 175 in src/app/elements/content/content-window/koko/koko.component.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract the assignment of "this.iframeURL" from this expression.

See more on https://sonarcloud.io/project/issues?id=jumpserver_luna&issues=AZ1RqmEXKQyapgKNihg_&open=AZ1RqmEXKQyapgKNihg_&pullRequest=1516
}

this.iframeURL = joinEndpointUrl(this.baseUrl, `/koko/connect/?${query}`);
this.iframeURL = joinEndpointUrl(this.baseUrl, kokoPath);
}

generateFileManagerURL() {
this.iframeURL = joinEndpointUrl(
this.baseUrl,
`/koko/elfinder/sftp/?token=${this.view.connectToken.id}&asset=${this.asset.id}`
);
const params = new URLSearchParams({
token: this.view.connectToken.id,
asset: this.asset.id
});
const sitePrefix = getSitePrefix();

if (sitePrefix) {
params.set('site_prefix', sitePrefix);
}

const path = `/koko/elfinder/sftp/?${params.toString()}`;
this.iframeURL = joinEndpointUrl(this.baseUrl, withSitePrefix(path));
}

async reconnect() {
Expand All @@ -186,7 +200,6 @@
if (!newConnectToken) {
return;
}
// 更新当前 view 的 connectToken
this.view.connectToken = newConnectToken;
await this.ngOnInit();
this.loading = false;
Expand Down
3 changes: 2 additions & 1 deletion src/app/elements/iframe/iframe.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
import { FaceService } from '@app/services/face';
import { DrawerStateService } from '@app/services/drawer';
import { Protocol } from '@app/model';
import { withSitePrefix } from '@app/utils/path';

@Component({
standalone: false,
Expand Down Expand Up @@ -74,7 +75,7 @@ export class ElementIframeComponent implements OnInit, AfterViewInit, OnDestroy
}

this.renewalTrigger.pipe(debounceTime(2000)).subscribe(() => {
this._http.get(`/api/v1/health/`).subscribe();
this._http.get(withSitePrefix('/api/v1/health/')).subscribe();
});

this.id = 'window-' + Math.random().toString(36).substr(2);
Expand Down
3 changes: 2 additions & 1 deletion src/app/elements/nav/setting/setting.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { NzMessageService } from 'ng-zorro-antd/message';
import { HttpService, IframeCommunicationService, SettingService } from '@app/services';
import { Component, Inject, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { NzSelectComponent } from 'ng-zorro-antd/select';
import { withSitePrefix } from '@app/utils/path';

interface terminalThemeMap {
label: string;
Expand Down Expand Up @@ -84,7 +85,7 @@ export class ElementSettingComponent implements OnInit, OnDestroy {
}

async getSettingOptions() {
const url = '/api/v1/users/preference/?category=luna';
const url = withSitePrefix('/api/v1/users/preference/?category=luna');
const res: any = await this._http.options(url).toPromise();
const graphics = res.actions.GET.graphics.children;
this.resolutionsOptions = graphics.rdp_resolution.choices;
Expand Down
23 changes: 16 additions & 7 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,22 @@
<meta content="no-cache" http-equiv="Cache-control">
<meta content="no-cache" http-equiv="Cache">
<title></title>
<base href="/luna/">

<script>
/**
* 核心逻辑:
* 1. 动态计算前缀(Site Prefix)。
* 2. 使用 document.write 同步写入 <base> 标签。
* 这能确保浏览器在解析后续的 link/script 标签前,base 路径已经生效,
* 从而避免预解析器(Pre-parser)发起不带前缀的错误请求。
*/
(function () {
var pathname = window.location.pathname || '/';
var marker = '/luna/';
var idx = pathname.indexOf(marker);
var prefix = '';

// 处理路径匹配
if (idx === -1 && /\/luna$/.test(pathname)) {
idx = pathname.length - '/luna'.length;
}
Expand All @@ -24,14 +32,14 @@
}

prefix = prefix.replace(/\/+$/, '');

// 设置全局变量供业务代码使用
window.__BASE_PATH__ = prefix;
window.__UI_BASE__ = prefix + '/ui/';
window.__LUNA_BASE__ = prefix + marker;

var baseElement = document.querySelector('base');
if (baseElement) {
baseElement.setAttribute('href', window.__LUNA_BASE__);
}
// 同步写入 base 标签,防止浏览器预加载“抢跑”
document.write('<base href="' + window.__LUNA_BASE__ + '">');

Check warning on line 42 in src/index.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `globalThis` over `window`.

See more on https://sonarcloud.io/project/issues?id=jumpserver_luna&issues=AZ1RqmJLKQyapgKNihhB&open=AZ1RqmJLKQyapgKNihhB&pullRequest=1516
})();
</script>

Expand Down Expand Up @@ -77,7 +85,7 @@
</div>
</app-root>
<span id="marker" style="display: none;font-size: 14px">marker</span>
</body>

<script>
if (navigator.keyboard && navigator.keyboard.lock) {
navigator.keyboard.lock(['Escape']);
Expand All @@ -95,7 +103,7 @@
clipboardData = await navigator.clipboard.readText()
}

// 写会内容
// 写回内容
if (navigator.clipboard && navigator.clipboard.writeText) {
await navigator.clipboard.writeText(clipboardData);
}
Expand All @@ -104,4 +112,5 @@
}
}
</script>
</body>
</html>