Skip to content

Commit d39c874

Browse files
authored
Merge pull request #1544 from jumpserver/dev
v4.10.17-lts
2 parents 08afac5 + abb9f74 commit d39c874

45 files changed

Lines changed: 2831 additions & 811 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.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 }}"

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,6 @@ luna.tar.gz
4747
luna/
4848
package-lock.json
4949
.angular
50+
51+
# generated at build time by scripts/generate-build-info.mjs
52+
/src/environments/build-info.ts

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:20250609_105214 AS stage-build
2+
FROM jumpserver/luna-base:20260701_071908 AS stage-build
33

44
ARG VERSION
55
ENV VERSION=$VERSION

angular.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"aot": false,
1919
"sourceMap": true,
2020
"namedChunks": true,
21+
"baseHref": "/luna/",
2122
"index": "src/index.html",
2223
"browser": "src/main.ts",
2324
"polyfills": ["src/polyfills.ts"],
@@ -101,7 +102,8 @@
101102
"builder": "@angular/build:dev-server",
102103
"options": {
103104
"buildTarget": "Luna:build",
104-
"proxyConfig": "proxy.conf.json"
105+
"proxyConfig": "proxy.conf.json",
106+
"servePath": "/luna"
105107
},
106108
"configurations": {
107109
"production": {

package.json

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
"license": "GPLv3",
55
"scripts": {
66
"ng": "ng",
7-
"dev": "ng serve --proxy-config proxy.conf.json --hmr --host 0.0.0.0",
8-
"start": "ng serve --proxy-config proxy.conf.json --host 0.0.0.0",
9-
"build": "ng build --configuration production",
7+
"dev": "node scripts/generate-build-info.mjs && ng serve --proxy-config proxy.conf.json --serve-path /luna --hmr --host 0.0.0.0",
8+
"start": "node scripts/generate-build-info.mjs && ng serve --proxy-config proxy.conf.json --serve-path /luna --host 0.0.0.0",
9+
"build": "node scripts/generate-build-info.mjs && ng build --configuration production",
1010
"test": "ng test",
1111
"lint": "ng lint",
1212
"e2e": "ng e2e",
@@ -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
}

scripts/generate-build-info.mjs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Generates src/environments/build-info.ts with the current build time.
2+
// Runs before `ng build` / `ng serve` (see package.json scripts). Because
3+
// `yarn build` runs inside the Docker build, the value equals the Docker build
4+
// moment in production, and the dev-server start time in development.
5+
// The container defaults to UTC, so the time is formatted as Asia/Shanghai.
6+
import { mkdirSync, writeFileSync } from 'node:fs';
7+
import { dirname } from 'node:path';
8+
9+
const buildTime = new Date().toLocaleString('zh-CN', {
10+
timeZone: 'Asia/Shanghai',
11+
hour12: false
12+
});
13+
14+
const target = 'src/environments/build-info.ts';
15+
mkdirSync(dirname(target), { recursive: true });
16+
writeFileSync(
17+
target,
18+
`// AUTO-GENERATED by scripts/generate-build-info.mjs. Do not edit or commit.\nexport const buildTime = '${buildTime}';\n`
19+
);
20+
21+
console.log(`[build-info] buildTime = ${buildTime}`);

src/app/app.module.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,19 @@ import { AppComponent } from './pages/app.component';
2020
import { ElementComponents } from './elements/elements.component';
2121
import { PluginModules } from './plugins/plugins';
2222
import { ClipboardService } from 'ngx-clipboard';
23-
import { environment, version } from '../environments/environment';
23+
import { version } from '../environments/environment';
2424
import { BehaviorSubject, forkJoin, Observable, of } from 'rxjs';
2525
import { FileInputAccessorModule } from 'file-input-accessor';
2626
import { catchError, mergeMap } from 'rxjs/operators';
2727
import { PagesComponents } from './pages/pages.component';
28+
import { getAppBasePath, withAppBase, withSitePrefix } from '@app/utils/path';
2829

2930
export class CustomLoader implements TranslateLoader {
3031
constructor(private http: HttpClient) {}
3132

3233
public getTranslation(lang: String): Observable<any> {
33-
const remote = '/api/v1/settings/i18n/luna/?lang=' + lang + '&v=' + version;
34-
let local = '/assets/i18n/' + lang + '.json';
35-
const isProd = environment.production;
36-
if (isProd) {
37-
local = '/luna' + local;
38-
}
34+
const remote = withSitePrefix('/api/v1/settings/i18n/luna/?lang=' + lang + '&v=' + version);
35+
const local = withAppBase('/assets/i18n/' + lang + '.json');
3936

4037
return forkJoin([
4138
this.http.get(remote).pipe(catchError(() => of({}))),
@@ -74,7 +71,7 @@ export class CustomLoader implements TranslateLoader {
7471
bootstrap: [AppComponent],
7572
providers: [
7673
...AllServices,
77-
{ provide: APP_BASE_HREF, useValue: '/luna/' },
74+
{ provide: APP_BASE_HREF, useFactory: getAppBasePath },
7875
CookieService,
7976
NGXLogger,
8077
ClipboardService,

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

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
</a>
2525
<i
2626
(click)="toggleTreeCheckable($event, tree)"
27+
*ngIf="tree.checkbox"
2728
[nzTooltipTitle]="'Batch actions(select and right click)' | translate"
2829
nz-tooltip
2930
[ngClass]="isTreeCheckEnabled(tree) ? 'fa-check-square' : 'fa-square-o'"
@@ -64,16 +65,48 @@
6465
>
6566
<table>
6667
<tbody>
67-
<tr *ngFor="let menu of rMenuList; let i = index" class="view-menu__item">
68+
<tr
69+
*ngFor="let menu of rMenuList; let i = index"
70+
[class.view-menu__item--has-submenu]="menu.children?.length"
71+
[ngStyle]="{ display: menu.hide ? 'none' : '' }"
72+
class="view-menu__item"
73+
>
6874
<td
69-
(click)="handleMenuClick(menu)"
75+
(click)="!menu.children?.length && handleMenuClick(menu, $event)"
7076
[attr.data-num]="i"
71-
[ngStyle]="{ display: menu.hide ? 'none' : '' }"
77+
[ngStyle]="{ opacity: menu.disabled ? 0.4 : 1 }"
7278
class="view-menu__data"
7379
>
74-
<span [ngClass]="menu.fa" class="view-menu__icon fa"></span> {{ menu.name | translate }}
80+
<span [ngClass]="menu.fa" class="view-menu__icon fa"></span>
81+
<span class="view-menu__label">{{ menu.name | translate }}</span>
82+
<i *ngIf="menu.children?.length" class="fa fa-angle-right view-menu__submenu-arrow"></i>
83+
<div *ngIf="menu.children?.length" class="view-menu__submenu">
84+
<table>
85+
<tbody>
86+
<tr *ngFor="let child of menu.children" class="view-menu__item">
87+
<td
88+
(click)="handleSubmenuClick($event, child)"
89+
class="view-menu__data view-menu__submenu-data"
90+
>
91+
<span class="view-menu__submenu-label">{{ child.name }}</span>
92+
<i *ngIf="child.checked" class="fa fa-check view-menu__submenu-check"></i>
93+
</td>
94+
</tr>
95+
</tbody>
96+
</table>
97+
</div>
7598
</td>
7699
</tr>
77100
</tbody>
78101
</table>
79102
</div>
103+
104+
<!-- New favorite folder dialog content template -->
105+
<ng-template #folderModalContent>
106+
<input
107+
nz-input
108+
[(ngModel)]="folderNameInput"
109+
[placeholder]="'Folder name' | translate"
110+
type="text"
111+
/>
112+
</ng-template>

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

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@
141141

142142
.tree-type-content {
143143
position: relative;
144-
padding: 5px 10px;
144+
padding: 1px;
145145
min-height: 40px;
146146
height: calc(100% - 31px);
147147
background-color: var(--el-asset-tree-bg-color);
@@ -286,9 +286,64 @@ tr:hover {
286286
}
287287

288288
.view-menu__data {
289+
position: relative;
289290
padding: 0 6px;
290291
}
291292

293+
.view-menu__label {
294+
flex: 1;
295+
}
296+
297+
.view-menu__submenu-arrow {
298+
margin-left: 12px;
299+
font-size: 12px;
300+
opacity: 0.6;
301+
}
302+
303+
.view-menu__item--has-submenu {
304+
.view-menu__data {
305+
display: flex;
306+
align-items: center;
307+
padding-right: 10px;
308+
}
309+
310+
&:hover > .view-menu__data > .view-menu__submenu {
311+
display: block;
312+
}
313+
}
314+
315+
.view-menu__submenu {
316+
display: none;
317+
position: absolute;
318+
top: -6px;
319+
left: 100%;
320+
margin: 0;
321+
padding: 6px 0;
322+
min-width: 120px;
323+
border-radius: 2px;
324+
background-color: var(--el-dropdown-bg-color);
325+
box-shadow: 0 3px 6px 0 rgba(0, 0, 0, 0.16);
326+
z-index: 1001;
327+
328+
.view-menu__submenu-data {
329+
display: flex;
330+
align-items: center;
331+
justify-content: space-between;
332+
min-width: 140px;
333+
padding-right: 10px;
334+
}
335+
336+
.view-menu__submenu-label {
337+
flex: 1;
338+
padding-right: 8px;
339+
}
340+
341+
.view-menu__submenu-check {
342+
font-size: 12px;
343+
color: #52c41a;
344+
}
345+
}
346+
292347
.open-tree {
293348
flex: 1;
294349
}

0 commit comments

Comments
 (0)