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:
@@ -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 (?,?,?,?,?,?,?)",
|
||||
|
||||
Reference in New Issue
Block a user