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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Use base image to build the project avoid npm install every time
FROM jumpserver/luna-base:20260701_071908 AS stage-build
FROM jumpserver/luna-base:20260806_071716 AS stage-build

ARG VERSION
ENV VERSION=$VERSION
Expand Down
111 changes: 81 additions & 30 deletions src/app/elements/asset-tree/asset-tree.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,13 @@
name: options.name,
fa: options.fa,
hide: false,
disabled: true,
click: () => {}
children: [
{
id: 'create-folder-and-favorite',
name: this._i18n.instant('Create folder and favorite'),
click: this.onCreateFolderAndFavorite.bind(this)
}
]
}
];
}
Expand Down Expand Up @@ -469,6 +474,24 @@
return String(folderOrId);
}

private resolveCreatedFavoriteFolderId(folder: any, name: string, parentId: string = null): Promise<string> {
const createdFolderId = this.resolveFolderId(folder);
if (createdFolderId) {
return Promise.resolve(createdFolderId);
}
const normalizedParentId = this.resolveFolderId(parentId);
return this._http
.getFavoriteFolders()
.toPromise()
.then(folders => {
this.favoriteFolders = folders || [];
const createdFolder = this.favoriteFolders.find(
f => f.name === name && this.resolveFolderId(f.parent) === normalizedParentId
);
return this.resolveFolderId(createdFolder);
});
}

/**
* Resolve the real asset id from tree/favorite nodes.
*/
Expand Down Expand Up @@ -1064,11 +1087,15 @@
connectOnNewPage(node, 'auto');
}

onCreateFolderAndFavorite() {
this.onCreateFolder(null, this.rightClickSelectNode);
}

/**
* Create folder: pop an input, validate empty/duplicate name, then call backend
* @param parentId parent folder id, null means top-level
*/
onCreateFolder(parentId: string = null) {
onCreateFolder(parentId: string = null, favoriteNode: any = null) {
this.folderNameInput = '';
this._modal.create({
nzTitle: this._i18n.instant(parentId ? 'Create subfolder' : 'Create folder'),
Expand All @@ -1089,13 +1116,34 @@
return this._http
.createFavoriteFolder(name, parentId)
.toPromise()
.then(() => {
.then(folder => {
const msg = this._i18n.instant('Create folder') + ' ' + this._i18n.instant('success');
if (favoriteNode) {
return this.resolveCreatedFavoriteFolderId(folder, name, parentId)
.then(createdFolderId => {
if (!createdFolderId) {
this.refreshFavoriteTree();
return;
}
return this.favoriteAssetToFolder(createdFolderId, favoriteNode, 'Favorite', {
refreshTreeOnSuccess: true
}).catch(() => {
this.refreshFavoriteTree();
});
})
.then(() => {
this._toastr.success(msg, '', { nzClass: 'custom-success-notification' });
});
}
this._toastr.success(msg, '', { nzClass: 'custom-success-notification' });
this.refreshFavoriteTree();
}, error => {
if (error && error.status === 400) {

Check warning on line 1141 in src/app/elements/asset-tree/asset-tree.component.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer using an optional chain expression instead, as it's more concise and easier to read.

See more on https://sonarcloud.io/project/issues?id=jumpserver_luna&issues=AZ_F8lhM7cXMlFMtiVZN&open=AZ_F8lhM7cXMlFMtiVZN&pullRequest=1561
this._message.error(this._i18n.instant('Folder already exists'));
}
return false;
})
.catch(() => {
this._message.error(this._i18n.instant('Folder already exists'));
return false;
});
}
Expand Down Expand Up @@ -1197,54 +1245,57 @@
* @param folderId target folder id
*/
onFavoriteTo(folderId: string) {
this.assignFavoriteToFolder(folderId, 'Favorite');
this.favoriteAssetToFolder(folderId, this.rightClickSelectNode, 'Favorite').catch(() => {});
}

/**
* Move a favorite-tree asset leaf into another folder.
* Same backend as favorite-to; only the success toast wording differs.
*/
/** Move a favorite-tree asset leaf into another folder. */
onMoveTo(folderId: string) {
this.assignFavoriteToFolder(folderId, 'Move');
this.favoriteAssetToFolder(folderId, this.rightClickSelectNode, 'Move').catch(() => {});
}

/**
* Assign the right-clicked asset to a favorite folder (create or relocate).
* @param folderId target folder id
* @param actionKey i18n key for success toast ("Favorite" / "Move")
*/
private assignFavoriteToFolder(folderId: string, actionKey: string) {
const srcNode = this.rightClickSelectNode;
private favoriteAssetToFolder(
folderId: string,
srcNode: any,
actionKey: string = 'Favorite',
options: { refreshTreeOnSuccess?: boolean } = {}
): Promise<void> {
const normalizedFolderId = this.resolveFolderId(folderId);
const assetId = this.resolveAssetId(srcNode);
if (!normalizedFolderId || !assetId) {
return;
return Promise.resolve();
}
const currentFolderId = this.resolveFolderId(srcNode.favoriteFolderId);
if (currentFolderId && currentFolderId === normalizedFolderId) {
this._message.warning(this._i18n.instant('Already in this folder'));
return;
return Promise.resolve();
}
const favoritingKey = `${assetId}-${normalizedFolderId}`;
if (this.favoritingInFlight.has(favoritingKey)) {
return;
return Promise.resolve();
}
this.favoritingInFlight.add(favoritingKey);
this._http.favoriteAssetToFolder(assetId, normalizedFolderId).subscribe(
() => {
return this._http
.favoriteAssetToFolder(assetId, normalizedFolderId)
.toPromise()
.then(() => {
const msg = this._i18n.instant(actionKey) + ' ' + this._i18n.instant('success');
this._toastr.success(msg, '', { nzClass: 'custom-success-notification' });
this.setFavoriteAssetRecord(normalizedFolderId, assetId, srcNode);
this.moveFavoriteLeaf(normalizedFolderId, srcNode, assetId);
this.favoritingInFlight.delete(favoritingKey);
},
error => {
this.favoritingInFlight.delete(favoritingKey);
if (options.refreshTreeOnSuccess) {
this.refreshFavoriteTree();
} else {
this.moveFavoriteLeaf(normalizedFolderId, srcNode, assetId);
}
})
.catch(error => {
if (error && error.status === 400) {
this._message.warning(this._i18n.instant('Already in this folder'));
}
}
);
return Promise.reject(error);

Check failure on line 1294 in src/app/elements/asset-tree/asset-tree.component.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `throw error` over `return Promise.reject(error)`.

See more on https://sonarcloud.io/project/issues?id=jumpserver_luna&issues=AZ_F8lhM7cXMlFMtiVZO&open=AZ_F8lhM7cXMlFMtiVZO&pullRequest=1561
})
.finally(() => {
this.favoritingInFlight.delete(favoritingKey);
});
}

/**
Expand Down