Skip to content

Commit c304d9d

Browse files
rakduttacrivetimihai
authored andcommitted
dom
Signed-off-by: rakdutta <[email protected]>
1 parent d561c16 commit c304d9d

File tree

2 files changed

+270
-16
lines changed

2 files changed

+270
-16
lines changed

mcpgateway/admin.py

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,14 +1040,36 @@ async def admin_add_server(request: Request, db: Session = Depends(get_db), user
10401040
except json.JSONDecodeError:
10411041
LOGGER.warning("Failed to parse allToolIds JSON, falling back to checked tools")
10421042

1043+
# Handle "Select All" for resources
1044+
associated_resources_list = form.getlist("associatedResources")
1045+
if form.get("selectAllResources") == "true":
1046+
all_resource_ids_json = str(form.get("allResourceIds", "[]"))
1047+
try:
1048+
all_resource_ids = json.loads(all_resource_ids_json)
1049+
associated_resources_list = all_resource_ids
1050+
LOGGER.info(f"Select All resources enabled: {len(all_resource_ids)} resources selected")
1051+
except json.JSONDecodeError:
1052+
LOGGER.warning("Failed to parse allResourceIds JSON, falling back to checked resources")
1053+
1054+
# Handle "Select All" for prompts
1055+
associated_prompts_list = form.getlist("associatedPrompts")
1056+
if form.get("selectAllPrompts") == "true":
1057+
all_prompt_ids_json = str(form.get("allPromptIds", "[]"))
1058+
try:
1059+
all_prompt_ids = json.loads(all_prompt_ids_json)
1060+
associated_prompts_list = all_prompt_ids
1061+
LOGGER.info(f"Select All prompts enabled: {len(all_prompt_ids)} prompts selected")
1062+
except json.JSONDecodeError:
1063+
LOGGER.warning("Failed to parse allPromptIds JSON, falling back to checked prompts")
1064+
10431065
server = ServerCreate(
10441066
id=form.get("id") or None,
10451067
name=form.get("name"),
10461068
description=form.get("description"),
10471069
icon=form.get("icon"),
10481070
associated_tools=",".join(str(x) for x in associated_tools_list),
1049-
associated_resources=",".join(str(x) for x in form.getlist("associatedResources")),
1050-
associated_prompts=",".join(str(x) for x in form.getlist("associatedPrompts")),
1071+
associated_resources=",".join(str(x) for x in associated_resources_list),
1072+
associated_prompts=",".join(str(x) for x in associated_prompts_list),
10511073
tags=tags,
10521074
visibility=visibility,
10531075
)
@@ -1244,14 +1266,36 @@ async def admin_edit_server(
12441266
except json.JSONDecodeError:
12451267
LOGGER.warning("Failed to parse allToolIds JSON, falling back to checked tools")
12461268

1269+
# Handle "Select All" for resources
1270+
associated_resources_list = form.getlist("associatedResources")
1271+
if form.get("selectAllResources") == "true":
1272+
all_resource_ids_json = str(form.get("allResourceIds", "[]"))
1273+
try:
1274+
all_resource_ids = json.loads(all_resource_ids_json)
1275+
associated_resources_list = all_resource_ids
1276+
LOGGER.info(f"Select All resources enabled for edit: {len(all_resource_ids)} resources selected")
1277+
except json.JSONDecodeError:
1278+
LOGGER.warning("Failed to parse allResourceIds JSON, falling back to checked resources")
1279+
1280+
# Handle "Select All" for prompts
1281+
associated_prompts_list = form.getlist("associatedPrompts")
1282+
if form.get("selectAllPrompts") == "true":
1283+
all_prompt_ids_json = str(form.get("allPromptIds", "[]"))
1284+
try:
1285+
all_prompt_ids = json.loads(all_prompt_ids_json)
1286+
associated_prompts_list = all_prompt_ids
1287+
LOGGER.info(f"Select All prompts enabled for edit: {len(all_prompt_ids)} prompts selected")
1288+
except json.JSONDecodeError:
1289+
LOGGER.warning("Failed to parse allPromptIds JSON, falling back to checked prompts")
1290+
12471291
server = ServerUpdate(
12481292
id=form.get("id"),
12491293
name=form.get("name"),
12501294
description=form.get("description"),
12511295
icon=form.get("icon"),
12521296
associated_tools=",".join(str(x) for x in associated_tools_list),
1253-
associated_resources=",".join(str(x) for x in form.getlist("associatedResources")),
1254-
associated_prompts=",".join(str(x) for x in form.getlist("associatedPrompts")),
1297+
associated_resources=",".join(str(x) for x in associated_resources_list),
1298+
associated_prompts=",".join(str(x) for x in associated_prompts_list),
12551299
tags=tags,
12561300
visibility=visibility,
12571301
team_id=team_id,
@@ -5470,6 +5514,35 @@ async def admin_get_all_prompt_ids(
54705514
return {"prompt_ids": prompt_ids, "count": len(prompt_ids)}
54715515

54725516

5517+
@admin_router.get("/resources/ids", response_class=JSONResponse)
5518+
async def admin_get_all_resource_ids(
5519+
include_inactive: bool = False,
5520+
db: Session = Depends(get_db),
5521+
user=Depends(get_current_user_with_permissions),
5522+
):
5523+
"""Return all resource IDs accessible to the current user (select-all helper).
5524+
5525+
This endpoint is used by UI "Select All" helpers to fetch only the IDs
5526+
of resources the requesting user can access (owner, team, or public).
5527+
"""
5528+
user_email = get_user_email(user)
5529+
team_service = TeamManagementService(db)
5530+
user_teams = await team_service.get_user_teams(user_email)
5531+
team_ids = [t.id for t in user_teams]
5532+
5533+
query = select(DbResource.id)
5534+
if not include_inactive:
5535+
query = query.where(DbResource.is_active.is_(True))
5536+
5537+
access_conditions = [DbResource.owner_email == user_email, DbResource.visibility == "public"]
5538+
if team_ids:
5539+
access_conditions.append(and_(DbResource.team_id.in_(team_ids), DbResource.visibility.in_(["team", "public"])))
5540+
5541+
query = query.where(or_(*access_conditions))
5542+
resource_ids = [row[0] for row in db.execute(query).all()]
5543+
return {"resource_ids": resource_ids, "count": len(resource_ids)}
5544+
5545+
54735546
@admin_router.get("/prompts/search", response_class=JSONResponse)
54745547
async def admin_search_prompts(
54755548
q: str = Query("", description="Search query"),

mcpgateway/static/admin.js

Lines changed: 193 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6687,6 +6687,21 @@ function initResourceSelect(
66876687
'input[type="checkbox"]',
66886688
);
66896689
checkboxes.forEach((cb) => (cb.checked = false));
6690+
6691+
// Remove any select-all hidden inputs
6692+
const selectAllInput = container.querySelector(
6693+
'input[name="selectAllResources"]',
6694+
);
6695+
if (selectAllInput) {
6696+
selectAllInput.remove();
6697+
}
6698+
const allIdsInput = container.querySelector(
6699+
'input[name="allResourceIds"]',
6700+
);
6701+
if (allIdsInput) {
6702+
allIdsInput.remove();
6703+
}
6704+
66906705
update();
66916706
});
66926707
}
@@ -6697,12 +6712,61 @@ function initResourceSelect(
66976712
newSelectBtn.dataset.listenerAttached = "true";
66986713
selectBtn.parentNode.replaceChild(newSelectBtn, selectBtn);
66996714

6700-
newSelectBtn.addEventListener("click", () => {
6701-
const checkboxes = container.querySelectorAll(
6702-
'input[type="checkbox"]',
6703-
);
6704-
checkboxes.forEach((cb) => (cb.checked = true));
6705-
update();
6715+
newSelectBtn.addEventListener("click", async () => {
6716+
const originalText = newSelectBtn.textContent;
6717+
newSelectBtn.disabled = true;
6718+
newSelectBtn.textContent = "Selecting all resources...";
6719+
6720+
try {
6721+
const resp = await fetch(`${window.ROOT_PATH}/admin/resources/ids`);
6722+
if (!resp.ok) {
6723+
throw new Error("Failed to fetch resource IDs");
6724+
}
6725+
const data = await resp.json();
6726+
const allIds = data.resource_ids || [];
6727+
6728+
// Check all currently loaded checkboxes
6729+
const loadedCheckboxes = container.querySelectorAll(
6730+
'input[type="checkbox"]',
6731+
);
6732+
loadedCheckboxes.forEach((cb) => (cb.checked = true));
6733+
6734+
// Add hidden select-all flag
6735+
let selectAllInput = container.querySelector(
6736+
'input[name="selectAllResources"]',
6737+
);
6738+
if (!selectAllInput) {
6739+
selectAllInput = document.createElement("input");
6740+
selectAllInput.type = "hidden";
6741+
selectAllInput.name = "selectAllResources";
6742+
container.appendChild(selectAllInput);
6743+
}
6744+
selectAllInput.value = "true";
6745+
6746+
// Store IDs as JSON for backend handling
6747+
let allIdsInput = container.querySelector(
6748+
'input[name="allResourceIds"]',
6749+
);
6750+
if (!allIdsInput) {
6751+
allIdsInput = document.createElement("input");
6752+
allIdsInput.type = "hidden";
6753+
allIdsInput.name = "allResourceIds";
6754+
container.appendChild(allIdsInput);
6755+
}
6756+
allIdsInput.value = JSON.stringify(allIds);
6757+
6758+
update();
6759+
6760+
newSelectBtn.textContent = `✓ All ${allIds.length} resources selected`;
6761+
setTimeout(() => {
6762+
newSelectBtn.textContent = originalText;
6763+
}, 2000);
6764+
} catch (error) {
6765+
console.error("Error selecting all resources:", error);
6766+
alert("Failed to select all resources. Please try again.");
6767+
} finally {
6768+
newSelectBtn.disabled = false;
6769+
}
67066770
});
67076771
}
67086772

@@ -6713,6 +6777,33 @@ function initResourceSelect(
67136777
container.dataset.changeListenerAttached = "true";
67146778
container.addEventListener("change", (e) => {
67156779
if (e.target.type === "checkbox") {
6780+
// If Select All mode is active, update the stored IDs array
6781+
const selectAllInput = container.querySelector(
6782+
'input[name="selectAllResources"]',
6783+
);
6784+
const allIdsInput = container.querySelector(
6785+
'input[name="allResourceIds"]',
6786+
);
6787+
6788+
if (
6789+
selectAllInput &&
6790+
selectAllInput.value === "true" &&
6791+
allIdsInput
6792+
) {
6793+
try {
6794+
let allIds = JSON.parse(allIdsInput.value);
6795+
const id = e.target.value;
6796+
if (e.target.checked) {
6797+
if (!allIds.includes(id)) allIds.push(id);
6798+
} else {
6799+
allIds = allIds.filter((x) => x !== id);
6800+
}
6801+
allIdsInput.value = JSON.stringify(allIds);
6802+
} catch (err) {
6803+
console.error("Error updating allResourceIds:", err);
6804+
}
6805+
}
6806+
67166807
update();
67176808
}
67186809
});
@@ -6796,6 +6887,21 @@ function initPromptSelect(
67966887
'input[type="checkbox"]',
67976888
);
67986889
checkboxes.forEach((cb) => (cb.checked = false));
6890+
6891+
// Remove any select-all hidden inputs
6892+
const selectAllInput = container.querySelector(
6893+
'input[name="selectAllPrompts"]',
6894+
);
6895+
if (selectAllInput) {
6896+
selectAllInput.remove();
6897+
}
6898+
const allIdsInput = container.querySelector(
6899+
'input[name="allPromptIds"]',
6900+
);
6901+
if (allIdsInput) {
6902+
allIdsInput.remove();
6903+
}
6904+
67996905
update();
68006906
});
68016907
}
@@ -6805,13 +6911,61 @@ function initPromptSelect(
68056911
const newSelectBtn = selectBtn.cloneNode(true);
68066912
newSelectBtn.dataset.listenerAttached = "true";
68076913
selectBtn.parentNode.replaceChild(newSelectBtn, selectBtn);
6914+
newSelectBtn.addEventListener("click", async () => {
6915+
const originalText = newSelectBtn.textContent;
6916+
newSelectBtn.disabled = true;
6917+
newSelectBtn.textContent = "Selecting all prompts...";
68086918

6809-
newSelectBtn.addEventListener("click", () => {
6810-
const checkboxes = container.querySelectorAll(
6811-
'input[type="checkbox"]',
6812-
);
6813-
checkboxes.forEach((cb) => (cb.checked = true));
6814-
update();
6919+
try {
6920+
const resp = await fetch(`${window.ROOT_PATH}/admin/prompts/ids`);
6921+
if (!resp.ok) {
6922+
throw new Error("Failed to fetch prompt IDs");
6923+
}
6924+
const data = await resp.json();
6925+
const allIds = data.prompt_ids || [];
6926+
6927+
// Check all currently loaded checkboxes
6928+
const loadedCheckboxes = container.querySelectorAll(
6929+
'input[type="checkbox"]',
6930+
);
6931+
loadedCheckboxes.forEach((cb) => (cb.checked = true));
6932+
6933+
// Add hidden select-all flag
6934+
let selectAllInput = container.querySelector(
6935+
'input[name="selectAllPrompts"]',
6936+
);
6937+
if (!selectAllInput) {
6938+
selectAllInput = document.createElement("input");
6939+
selectAllInput.type = "hidden";
6940+
selectAllInput.name = "selectAllPrompts";
6941+
container.appendChild(selectAllInput);
6942+
}
6943+
selectAllInput.value = "true";
6944+
6945+
// Store IDs as JSON for backend handling
6946+
let allIdsInput = container.querySelector(
6947+
'input[name="allPromptIds"]',
6948+
);
6949+
if (!allIdsInput) {
6950+
allIdsInput = document.createElement("input");
6951+
allIdsInput.type = "hidden";
6952+
allIdsInput.name = "allPromptIds";
6953+
container.appendChild(allIdsInput);
6954+
}
6955+
allIdsInput.value = JSON.stringify(allIds);
6956+
6957+
update();
6958+
6959+
newSelectBtn.textContent = `✓ All ${allIds.length} prompts selected`;
6960+
setTimeout(() => {
6961+
newSelectBtn.textContent = originalText;
6962+
}, 2000);
6963+
} catch (error) {
6964+
console.error("Error selecting all prompts:", error);
6965+
alert("Failed to select all prompts. Please try again.");
6966+
} finally {
6967+
newSelectBtn.disabled = false;
6968+
}
68156969
});
68166970
}
68176971

@@ -6822,6 +6976,33 @@ function initPromptSelect(
68226976
container.dataset.changeListenerAttached = "true";
68236977
container.addEventListener("change", (e) => {
68246978
if (e.target.type === "checkbox") {
6979+
// If Select All mode is active, update the stored IDs array
6980+
const selectAllInput = container.querySelector(
6981+
'input[name="selectAllPrompts"]',
6982+
);
6983+
const allIdsInput = container.querySelector(
6984+
'input[name="allPromptIds"]',
6985+
);
6986+
6987+
if (
6988+
selectAllInput &&
6989+
selectAllInput.value === "true" &&
6990+
allIdsInput
6991+
) {
6992+
try {
6993+
let allIds = JSON.parse(allIdsInput.value);
6994+
const id = e.target.value;
6995+
if (e.target.checked) {
6996+
if (!allIds.includes(id)) allIds.push(id);
6997+
} else {
6998+
allIds = allIds.filter((x) => x !== id);
6999+
}
7000+
allIdsInput.value = JSON.stringify(allIds);
7001+
} catch (err) {
7002+
console.error("Error updating allPromptIds:", err);
7003+
}
7004+
}
7005+
68257006
update();
68267007
}
68277008
});

0 commit comments

Comments
 (0)