feat: ADB network access, prompt editor, resize persistence, and multiple fixes

- Add Update Network Access endpoint and UI for ADB ACL management
- Add inline prompt editor with auto-save in Terraform agent menu
- Persist resize bar positions across re-renders (Explorer + Terraform)
- Restore full Terraform session state from history (files, plan, outputs, OCI config)
- Parse free-form Resource Plan format (model often skips ```plan blocks)
- Auto-refresh resource state after Start/Stop actions
- Clear previous apply/destroy outputs on Re-plan
- Deduplicate CIS Compliance Scanner MCP server on startup
- Show ACL IPs in Explorer for Autonomous Databases
- Detect user IP via /api/oci/my-ip endpoint
This commit is contained in:
nogueiraguh
2026-03-09 15:01:26 -03:00
parent 01fe037cd0
commit c4a9f8d6d5
2 changed files with 188 additions and 28 deletions

View File

@@ -874,7 +874,7 @@ async def explore_databases(cid: str, compartment_id: str = Query(None), region:
adbs = db_client.list_autonomous_databases(comp).data
return [{"id":a.id,"display_name":a.display_name,"db_name":a.db_name,"lifecycle_state":a.lifecycle_state,
"cpu_core_count":a.cpu_core_count,"data_storage_size_in_tbs":a.data_storage_size_in_tbs,
"is_free_tier":a.is_free_tier} for a in adbs]
"is_free_tier":a.is_free_tier,"whitelisted_ips":a.whitelisted_ips or []} for a in adbs]
except Exception as e:
return {"error": str(e)[:500]}
@@ -3646,6 +3646,43 @@ async def oci_adb_action(adb_id: str, req: dict, u=Depends(current_user)):
raise HTTPException(500, str(e)[:500])
@app.post("/api/oci/autonomous-databases/{adb_id}/update-network-access")
async def oci_adb_update_network(adb_id: str, req: dict, u=Depends(current_user)):
oci_config_id = req.get("oci_config_id", "")
ip = req.get("ip", "")
region = req.get("region")
if not oci_config_id:
raise HTTPException(400, "oci_config_id obrigatório")
if not ip:
raise HTTPException(400, "ip obrigatório")
try:
import oci
config = _get_oci_config(oci_config_id)
if region:
config["region"] = region
db_client = oci.database.DatabaseClient(config)
adb = db_client.get_autonomous_database(adb_id).data
current_acl = adb.whitelisted_ips or []
# Build new ACL: keep existing entries, add new IP if not present
ip_cidr = ip if "/" in ip else ip + "/32"
new_acl = list(set(current_acl) | {ip_cidr})
db_client.update_autonomous_database(
adb_id,
oci.database.models.UpdateAutonomousDatabaseDetails(whitelisted_ips=new_acl))
_audit(u["id"], u["username"], "adb_update_network", adb_id, f"ip={ip_cidr}")
return {"ok": True, "adb_id": adb_id, "ip_added": ip_cidr, "acl": new_acl}
except Exception as e:
raise HTTPException(500, str(e)[:500])
@app.get("/api/oci/my-ip")
async def get_my_ip(request: Request):
"""Return the caller's public IP address."""
forwarded = request.headers.get("x-forwarded-for", "")
ip = forwarded.split(",")[0].strip() if forwarded else (request.client.host if request.client else "")
return {"ip": ip}
@app.get("/api/terraform/resources")
async def tf_list_resources(oci_config_id: str = Query(...), compartment_id: str = Query(...), region: str = Query(None), u=Depends(current_user)):
"""List existing OCI resources in a compartment for terraform context."""
@@ -3720,7 +3757,7 @@ async def tf_run_plan(wid: str, bg: BackgroundTasks, u=Depends(current_user)):
if ws["status"] in ("planning", "applying", "destroying"):
raise HTTPException(400, f"Workspace is {ws['status']}")
with db() as c:
c.execute("UPDATE terraform_workspaces SET status='planning', plan_output='', error=NULL, updated_at=datetime('now') WHERE id=?", (wid,))
c.execute("UPDATE terraform_workspaces SET status='planning', plan_output='', apply_output='', destroy_output='', error=NULL, updated_at=datetime('now') WHERE id=?", (wid,))
bg.add_task(_terraform_exec, wid, "plan", dict(u))
return {"status": "planning"}
@@ -4178,16 +4215,18 @@ provider "oci" {{
_running_terraform.pop(wid, None)
if proc.returncode == 0:
_update_output(f"\n✅ terraform {action} completed successfully")
output_lines.append(f"\n✅ terraform {action} completed successfully")
full_output = "\n".join(output_lines)
with db() as c:
c.execute("UPDATE terraform_workspaces SET status=?, updated_at=datetime('now') WHERE id=?",
(final_ok, wid))
c.execute(f"UPDATE terraform_workspaces SET {status_col}=?, status=?, updated_at=datetime('now') WHERE id=?",
(full_output, final_ok, wid))
_audit(user["id"], user["username"], f"terraform_{action}", wid, f"status={final_ok}")
else:
_update_output(f"\n❌ terraform {action} failed (exit {proc.returncode})")
output_lines.append(f"\n❌ terraform {action} failed (exit {proc.returncode})")
full_output = "\n".join(output_lines)
with db() as c:
c.execute("UPDATE terraform_workspaces SET status='failed', error=?, updated_at=datetime('now') WHERE id=?",
(f"terraform {action} failed (exit {proc.returncode})", wid))
c.execute(f"UPDATE terraform_workspaces SET {status_col}=?, status='failed', error=?, updated_at=datetime('now') WHERE id=?",
(full_output, f"terraform {action} failed (exit {proc.returncode})", wid))
except Exception as e:
log.error(f"Terraform exec error: {e}")
@@ -4376,10 +4415,15 @@ async def startup():
orphaned = c.execute("UPDATE reports SET status='failed', error_msg='Interrompido: container reiniciado' WHERE status='running'").rowcount
if orphaned:
log.warning(f"Marked {orphaned} orphaned running report(s) as failed")
# Auto-register CIS Compliance Scanner MCP server if not present
# Auto-register CIS Compliance Scanner MCP server if not present (deduplicate on startup)
with db() as c:
existing = c.execute("SELECT id FROM mcp_servers WHERE name='CIS Compliance Scanner'").fetchone()
if not existing:
dupes = c.execute("SELECT id FROM mcp_servers WHERE name='CIS Compliance Scanner' ORDER BY created_at ASC").fetchall()
if len(dupes) > 1:
keep = dupes[0]["id"]
for d in dupes[1:]:
c.execute("DELETE FROM mcp_servers WHERE id=?", (d["id"],))
log.info(f"Removed {len(dupes)-1} duplicate CIS Compliance Scanner entries, kept {keep}")
if not dupes:
mid = str(uuid.uuid4())
c.execute(
"INSERT INTO mcp_servers (id, user_id, name, description, server_type, command, args) VALUES (?,?,?,?,?,?,?)",