Skip to content

Commit 4585cb3

Browse files
ZhaoJiSenw940853815fit2botgithub-actions[bot]ibuler
authored
Pr@dev@merge osm (#1534)
* feat: configurable url prefix (#1509) (#1512) * feat: configurable url prefix * perf: Update Dockerfile with new base image tag --------- Co-authored-by: fit2bot <68588906+fit2bot@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * perf: update build base image * perf: update base image * feat:support proxy site prefix (#1516) Co-authored-by: Crane.z <1481445951@qq.com> * fix: share session page not load favicon * style:make web terminal logo smaller * feat: Added a feature for important message notifications * fix: fullscreen issue * fix: modify remember password through config * fix:Modify the split-screen logic for asset connection * fix:Make the guide function work properly * chore: update github action * deps: update some dep pkgs * fix: client detection * fix: version display * fix: strip build suffix and trailing content from version display * perf: Update Dockerfile with new base image tag --------- Co-authored-by: wrd <w940853815@gmail.com> Co-authored-by: fit2bot <68588906+fit2bot@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: ibuler <ibuler@qq.com> Co-authored-by: Crane.z <1481445951@qq.com> Co-authored-by: Bai <baijiangjie@gmail.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 1427a36 commit 4585cb3

23 files changed

Lines changed: 1291 additions & 652 deletions

File tree

.github/workflows/build-base-image.yml

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,29 @@ on:
99
- 'package-lock.json'
1010
- 'yarn.lock'
1111
- 'Dockerfile-base'
12+
workflow_dispatch:
13+
inputs:
14+
branch:
15+
description: '构建基础镜像所用的分支'
16+
required: true
17+
type: string
18+
default: 'main'
1219

1320
jobs:
1421
build-and-push:
1522
runs-on: ubuntu-latest
1623

1724
steps:
25+
- name: Lock Pull Request
26+
if: github.event_name == 'push'
27+
run: |
28+
curl -X POST -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
29+
-d '{"state":"pending", "description":"Action running, merge disabled", "context":"Lock PR"}' \
30+
"https://api.github.com/repos/${{ github.repository }}/statuses/${{ github.sha }}"
1831
- name: Checkout repository
1932
uses: actions/checkout@v4
33+
with:
34+
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.branch || github.ref }}
2035

2136
- name: Set up QEMU
2237
uses: docker/setup-qemu-action@v3
@@ -43,8 +58,8 @@ jobs:
4358
with:
4459
platforms: linux/amd64,linux/arm64
4560
push: true
46-
tags: jumpserver/${{ env.REPO }}-base:${{ env.IMAGE_TAG }}
4761
file: Dockerfile-base
62+
tags: jumpserver/${{ env.REPO }}-base:${{ env.IMAGE_TAG }}
4863

4964
- name: Update Dockerfile
5065
run: |
@@ -59,3 +74,10 @@ jobs:
5974
git push
6075
env:
6176
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
77+
78+
- name: Unlock Pull Request
79+
if: github.event_name == 'push'
80+
run: |
81+
curl -X POST -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
82+
-d '{"state":"success", "description":"Action running, merge disabled", "context":"Lock PR"}' \
83+
"https://api.github.com/repos/${{ github.repository }}/statuses/${{ github.sha }}"

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Use base image to build the project avoid npm install every time
2-
FROM jumpserver/luna-base:20260320_114803 AS stage-build
2+
FROM jumpserver/luna-base:20260701_071908 AS stage-build
33

44
ARG VERSION
55
ENV VERSION=$VERSION

package.json

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,16 @@
1515
"private": true,
1616
"dependencies": {
1717
"@acrodata/code-editor": "^0.5.1",
18-
"@angular/animations": "^19.0.0",
18+
"@angular/animations": "^19.2.20",
1919
"@angular/cdk": "^19.0.0",
20-
"@angular/common": "^19.0.0",
21-
"@angular/compiler": "^19.0.0",
22-
"@angular/core": "^19.0.0",
20+
"@angular/common": "^19.2.20",
21+
"@angular/compiler": "^19.2.20",
22+
"@angular/core": "^19.2.20",
2323
"@angular/flex-layout": "15.0.0-beta.42",
24-
"@angular/forms": "^19.0.0",
25-
"@angular/platform-browser": "^19.0.0",
26-
"@angular/platform-browser-dynamic": "^19.0.0",
27-
"@angular/router": "^19.0.0",
24+
"@angular/forms": "^19.2.20",
25+
"@angular/platform-browser": "^19.2.20",
26+
"@angular/platform-browser-dynamic": "^19.2.20",
27+
"@angular/router": "^19.2.20",
2828
"@ant-design/icons-angular": "^19.0.0",
2929
"@codemirror/language-data": "^6.5.1",
3030
"@ngx-translate/core": "^16.0.4",
@@ -45,7 +45,7 @@
4545
"guacamole-common-js": "^1.5.0",
4646
"jquery": "^3.7.1",
4747
"jsencrypt": "^3.3.2",
48-
"lodash-es": "^4.17.21",
48+
"lodash-es": "^4.18.0",
4949
"metismenu": "^3.0.7",
5050
"ng-zorro-antd": "^19.3.0",
5151
"ngx-clipboard": "^16.0.0",
@@ -55,21 +55,22 @@
5555
"popper.js": "^1.16.1",
5656
"requirejs": "^2.3.6",
5757
"rxjs": "^7.8.1",
58+
"sm-crypto": "^0.4.0",
5859
"tslib": "^2.6.2",
5960
"xterm-theme": "^1.1.0",
6061
"zone.js": "~0.15.0"
6162
},
6263
"devDependencies": {
63-
"@angular-devkit/build-angular": "^19.0.0",
64+
"@angular-devkit/build-angular": "^19.2.20",
6465
"@angular-eslint/builder": "^19.5.0",
6566
"@angular-eslint/eslint-plugin": "^19.5.0",
6667
"@angular-eslint/eslint-plugin-template": "^19.5.0",
6768
"@angular-eslint/schematics": "^19.5.0",
6869
"@angular-eslint/template-parser": "^19.5.0",
69-
"@angular/build": "^19.0.0",
70-
"@angular/cli": "^19.0.0",
71-
"@angular/compiler-cli": "^19.0.0",
72-
"@angular/language-service": "^19.0.0",
70+
"@angular/build": "^19.2.20",
71+
"@angular/cli": "^19.2.20",
72+
"@angular/compiler-cli": "^19.2.20",
73+
"@angular/language-service": "^19.2.20",
7374
"@types/jasmine": "^5.1.4",
7475
"@types/jasminewd2": "^2.0.13",
7576
"@types/node": "^20.11.19",
@@ -88,5 +89,9 @@
8889
"ts-node": "^10.9.2",
8990
"typescript": "~5.5.0",
9091
"vite": "^5.0.0"
92+
},
93+
"resolutions": {
94+
"seroval": "1.4.1",
95+
"fast-uri": "3.1.2"
9196
}
9297
}

src/app/elements/asset-tree/asset-tree.component.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
import { ConnectEvt, InitTreeConfig, TreeNode } from '@app/model';
2323
import { CookieService } from 'ngx-cookie-service';
2424
import { HttpHeaders } from '@angular/common/http';
25+
import { withSitePrefix } from '@app/utils/path';
2526

2627
declare var $: any;
2728

@@ -397,8 +398,13 @@ export class ElementAssetTreeComponent implements OnInit {
397398
async initAssetTree(refresh = false) {
398399
const config = {
399400
refresh,
401+
// dev 方案(当前生效):直接使用原始接口地址
400402
url: '/api/v1/perms/users/self/nodes/all-with-assets/tree/',
401403
asyncUrl: '/api/v1/perms/users/self/nodes/children-with-assets/tree/?'
404+
// 多子目录方案(保留备用):site prefix 包裹 + showFavoriteAssets
405+
// showFavoriteAssets: true,
406+
// url: withSitePrefix('/api/v1/perms/users/self/nodes/all-with-assets/tree/'),
407+
// asyncUrl: withSitePrefix('/api/v1/perms/users/self/nodes/children-with-assets/tree/?')
402408
};
403409
const tree = new Tree('AssetTree', 'My assets', false, true, true, true, config);
404410
if (!refresh) {
@@ -410,8 +416,8 @@ export class ElementAssetTreeComponent implements OnInit {
410416
async initTypeTree(refresh = false) {
411417
const config = {
412418
refresh,
413-
url: '/api/v1/perms/users/self/nodes/children-with-assets/category/tree/?sync=1',
414-
asyncUrl: '/api/v1/perms/users/self/nodes/children-with-assets/category/tree/',
419+
url: withSitePrefix('/api/v1/perms/users/self/nodes/children-with-assets/category/tree/?sync=1'),
420+
asyncUrl: withSitePrefix('/api/v1/perms/users/self/nodes/children-with-assets/category/tree/'),
415421
setting: {
416422
async: {
417423
autoParam: ['type', 'category']

src/app/elements/chat/chat.component.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,12 +213,18 @@ export class ElementChatComponent implements OnInit, OnDestroy, AfterViewInit {
213213
if (data && data.content) {
214214
this.postCurrentTerminalContextToChatAI();
215215
}
216+
if (!this.iframeRef?.nativeElement?.contentWindow) {
217+
return;
218+
}
216219
this.iframeRef.nativeElement.contentWindow.postMessage('show-chat-panel');
217220
this.chatAIShown = true;
218221
this.showBtn = false;
219222
}
220223

221224
postCurrentTerminalContextToChatAI() {
225+
if (!this.iframeRef?.nativeElement?.contentWindow) {
226+
return;
227+
}
222228
const data = this.currentView.terminalContentData;
223229
const content = {
224230
viewId: this.currentView.id,

src/app/elements/connect/connect.component.ts

Lines changed: 38 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
2+
import { ElementConnectDialogComponent } from '@app/elements/connect/connect-dialog/connect-dialog.component';
23
import { connectEvt } from '@app/globals';
4+
import { Account, Asset, ConnectData, ConnectionToken, View } from '@app/model';
35
import {
46
AlertService,
57
AppService,
@@ -10,12 +12,10 @@ import {
1012
SettingService,
1113
ViewService
1214
} from '@app/services';
13-
import { Account, Asset, ConnectData, ConnectionToken, View } from '@app/model';
14-
import { launchLocalApp } from '@app/utils/common';
15-
import { ElementConnectDialogComponent } from '@app/elements/connect/connect-dialog/connect-dialog.component';
15+
import { launchLocalApp, LOCAL_CLIENT_REMINDER_STORAGE_KEY } from '@app/utils/common';
1616
import { NzModalService } from 'ng-zorro-antd/modal';
17-
import { ElementDownloadDialogComponent } from './download-dialog/download-dialog.component';
1817
import { firstValueFrom } from 'rxjs';
18+
import { ElementDownloadDialogComponent } from './download-dialog/download-dialog.component';
1919

2020
@Component({
2121
standalone: false,
@@ -153,17 +153,6 @@ export class ElementConnectComponent implements OnInit, OnDestroy {
153153
this._logger.info('Create connection token failed');
154154
return;
155155
}
156-
157-
// 分屏连接
158-
if (splitConnect) {
159-
return this.currentWebSubView(asset, connectInfo, connToken);
160-
}
161-
162-
// 特殊处理
163-
if (connectMethod.value.endsWith('_guide')) {
164-
return this.createWebView(asset, connectInfo, connToken);
165-
}
166-
167156
let appletConnectMethod = connectOption ? connectOption['appletConnectMethod'] : 'web';
168157

169158
if (!this._settingSvc.hasXPack()) {
@@ -174,21 +163,30 @@ export class ElementConnectComponent implements OnInit, OnDestroy {
174163
virtualappConnectMethod = 'web';
175164
}
176165

166+
const isGuide = connectMethod.value.endsWith('_guide');
167+
const isClientConnect =
168+
connectMethod.type === 'native' ||
169+
(connectMethod.type === 'applet' && appletConnectMethod === 'client') ||
170+
(connectMethod.type === 'virtual_app' && virtualappConnectMethod === 'client');
171+
const openWebView = () =>
172+
splitConnect
173+
? this.currentWebSubView(asset, connectInfo, connToken)
174+
: this.createWebView(asset, connectInfo, connToken);
175+
177176
if (connectInfo.downloadRDP) {
178177
return this._http.downloadRDPFile(
179178
connToken,
180179
this._settingSvc.setting,
181180
connectInfo.connectOption
182181
);
183-
} else if (connectMethod.type === 'native') {
184-
this.callLocalClient(connToken).then();
185-
} else if (connectMethod.type === 'applet' && appletConnectMethod === 'client') {
186-
this.callLocalClient(connToken).then();
187-
} else if (connectMethod.type === 'virtual_app' && virtualappConnectMethod === 'client') {
182+
}
183+
184+
if (isClientConnect && !isGuide) {
188185
this.callLocalClient(connToken).then();
189-
} else {
190-
this.createWebView(asset, connectInfo, connToken);
186+
return;
191187
}
188+
189+
return openWebView();
192190
}
193191

194192
async callLocalClient(connToken: ConnectionToken) {
@@ -204,20 +202,24 @@ export class ElementConnectComponent implements OnInit, OnDestroy {
204202

205203
const url = response['url'];
206204

207-
launchLocalApp(url, () => {
208-
const downLoadStatus = localStorage.getItem('hasDownLoadApp');
209-
210-
if (downLoadStatus !== '1') {
211-
this._dialog.create({
212-
nzTitle: this._i18n.instant('DownloadClient'),
213-
nzContent: ElementDownloadDialogComponent,
214-
nzOnOk: cmp => cmp.onConfirm(),
215-
nzOnCancel: cmp => cmp.onCancel(),
216-
nzCancelText: this._i18n.instant('Cancel'),
217-
nzOkText: this._i18n.instant('Confirm')
218-
});
219-
}
220-
});
205+
const localClientLaunched = await launchLocalApp(url);
206+
207+
if (localClientLaunched) {
208+
return;
209+
}
210+
211+
const ignoreDownloadReminder = localStorage.getItem(LOCAL_CLIENT_REMINDER_STORAGE_KEY);
212+
213+
if (ignoreDownloadReminder !== '1') {
214+
this._dialog.create({
215+
nzTitle: this._i18n.instant('DownloadClient'),
216+
nzContent: ElementDownloadDialogComponent,
217+
nzOnOk: cmp => cmp.onConfirm(),
218+
nzOnCancel: cmp => cmp.onCancel(),
219+
nzCancelText: this._i18n.instant('Cancel'),
220+
nzOkText: this._i18n.instant('Confirm')
221+
});
222+
}
221223
}
222224

223225
createWebView(asset: Asset, connectInfo: any, connToken: ConnectionToken) {
Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import {Component, Inject, OnInit} from '@angular/core';
2-
import {NZ_MODAL_DATA} from "ng-zorro-antd/modal";
3-
import {withSitePrefix} from '@app/utils/path';
1+
import { Component, Inject, OnInit } from '@angular/core';
2+
import { LOCAL_CLIENT_REMINDER_STORAGE_KEY } from '@app/utils/common';
3+
import { withSitePrefix } from '@app/utils/path';
4+
import { NZ_MODAL_DATA } from "ng-zorro-antd/modal";
45

56
@Component({
67
standalone: false,
@@ -10,6 +11,7 @@ import {withSitePrefix} from '@app/utils/path';
1011
})
1112
export class ElementDownloadDialogComponent implements OnInit {
1213
public ignoreRemind = false;
14+
private readonly legacyReminderStorageKey = 'hasDownLoadApp';
1315

1416
constructor(
1517
@Inject(NZ_MODAL_DATA) public data: any,
@@ -18,21 +20,34 @@ export class ElementDownloadDialogComponent implements OnInit {
1820
}
1921

2022
ngOnInit() {
21-
if (localStorage.getItem('hasDownLoadApp') === '1') {
23+
const ignoreDownloadReminder = localStorage.getItem(LOCAL_CLIENT_REMINDER_STORAGE_KEY);
24+
const legacyIgnoreDownloadReminder = localStorage.getItem(this.legacyReminderStorageKey);
25+
26+
if (ignoreDownloadReminder === '1' || legacyIgnoreDownloadReminder === '1') {
2227
this.ignoreRemind = true;
2328
}
29+
30+
if (legacyIgnoreDownloadReminder === '1' && ignoreDownloadReminder !== '1') {
31+
localStorage.setItem(LOCAL_CLIENT_REMINDER_STORAGE_KEY, '1');
32+
}
33+
localStorage.removeItem(this.legacyReminderStorageKey);
2434
}
2535

26-
onCancel(): void {
36+
private persistIgnoreRemind(): void {
2737
if (this.ignoreRemind) {
28-
localStorage.setItem('hasDownLoadApp', '1');
38+
localStorage.setItem(LOCAL_CLIENT_REMINDER_STORAGE_KEY, '1');
39+
return;
2940
}
41+
42+
localStorage.removeItem(LOCAL_CLIENT_REMINDER_STORAGE_KEY);
43+
}
44+
45+
onCancel(): void {
46+
this.persistIgnoreRemind();
3047
}
3148

3249
onConfirm(): void {
33-
if (this.ignoreRemind) {
34-
localStorage.setItem('hasDownLoadApp', '1');
35-
}
50+
this.persistIgnoreRemind();
3651
window.open(withSitePrefix('/core/download/'), '_blank');
3752
}
3853
}

src/app/elements/content/content-footer/send-command-with-variable-dialog/send-command-with-variable-dialog.component.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import {Component, Inject, OnInit} from '@angular/core';
2-
import {HttpService} from '@app/services';
3-
import {NZ_MODAL_DATA, NzModalRef} from 'ng-zorro-antd/modal';
1+
import { Component, Inject, OnInit } from '@angular/core';
2+
import { HttpService } from '@app/services';
3+
import { NZ_MODAL_DATA, NzModalRef } from 'ng-zorro-antd/modal';
4+
import { withSitePrefix } from '@app/utils/path';
45

56
@Component({
67
standalone: false,
@@ -25,7 +26,7 @@ export class ElementSendCommandWithVariableDialogComponent implements OnInit {
2526

2627
async getVariableFormMeta() {
2728
const adhoc = this.command.id;
28-
const url = `/api/v1/ops/variables/form-data/?t=${new Date().getTime()}&adhoc=${adhoc}`;
29+
const url = withSitePrefix(`/api/v1/ops/variables/form-data/?t=${new Date().getTime()}&adhoc=${adhoc}`);
2930
const res: any = await this._http.options(url).toPromise();
3031
this.formConfig = res.actions.GET;
3132
}

0 commit comments

Comments
 (0)