fpaste: add batch delete and --all with confirmation
All checks were successful
CI / Lint & Format (push) Successful in 17s
CI / Security Scan (push) Successful in 22s
CI / Tests (push) Successful in 1m4s

This commit is contained in:
Username
2025-12-21 22:06:53 +01:00
parent e8a99d5bdd
commit 916a09f595

87
fpaste
View File

@@ -635,26 +635,73 @@ def cmd_get(args: argparse.Namespace, config: dict[str, Any]) -> None:
def cmd_delete(args: argparse.Namespace, config: dict[str, Any]) -> None:
"""Delete a paste."""
"""Delete paste(s)."""
require_auth(config)
paste_id = args.id.split("/")[-1]
url = f"{config['server'].rstrip('/')}/{paste_id}"
delete_all = getattr(args, "all", False)
confirm_count = getattr(args, "confirm", None)
paste_ids = [pid.split("/")[-1] for pid in (args.ids or [])]
status, _, _ = request(
url, method="DELETE", headers=auth_headers(config), ssl_context=config.get("ssl_context")
)
# Validate arguments
if delete_all and paste_ids:
die("cannot specify both --all and paste IDs")
if not delete_all and not paste_ids:
die("specify paste ID(s) or use --all")
if status == 200:
print(f"deleted: {paste_id}")
elif status == 404:
die(f"not found: {paste_id}")
elif status == 403:
die("permission denied (not owner)")
elif status == 401:
die("authentication failed")
else:
die(f"delete failed ({status})")
if delete_all:
# Fetch all pastes to get count and IDs
url = f"{config['server'].rstrip('/')}/pastes?all=1&limit=1000"
status, body, _ = request(
url, headers=auth_headers(config), ssl_context=config.get("ssl_context")
)
if status == 401:
die("authentication failed")
if status != 200:
die(f"failed to list pastes ({status})")
data = json.loads(body)
pastes = data.get("pastes", [])
total = len(pastes)
if total == 0:
print("no pastes to delete")
return
# Require confirmation with expected count
if confirm_count is None:
die(f"--all requires --confirm {total} (found {total} pastes)")
if confirm_count != total:
die(f"confirmation mismatch: expected {confirm_count}, found {total}")
paste_ids = [p["id"] for p in pastes]
# Delete pastes
deleted = 0
failed = 0
for paste_id in paste_ids:
url = f"{config['server'].rstrip('/')}/{paste_id}"
status, _, _ = request(
url,
method="DELETE",
headers=auth_headers(config),
ssl_context=config.get("ssl_context"),
)
if status == 200:
print(f"deleted: {paste_id}")
deleted += 1
elif status == 404:
print(f"not found: {paste_id}", file=sys.stderr)
failed += 1
elif status == 403:
print(f"permission denied: {paste_id}", file=sys.stderr)
failed += 1
else:
print(f"failed ({status}): {paste_id}", file=sys.stderr)
failed += 1
# Summary for batch operations
if len(paste_ids) > 1:
print(f"\n{deleted} deleted, {failed} failed")
def cmd_info(args: argparse.Namespace, config: dict[str, Any]) -> None:
@@ -1285,8 +1332,12 @@ def build_parser() -> argparse.ArgumentParser:
p_get.add_argument("-m", "--meta", action="store_true", help="show metadata only")
# delete
p_delete = subparsers.add_parser("delete", aliases=["d", "rm"], help="delete paste")
p_delete.add_argument("id", help="paste ID or URL")
p_delete = subparsers.add_parser("delete", aliases=["d", "rm"], help="delete paste(s)")
p_delete.add_argument("ids", nargs="*", metavar="ID", help="paste ID(s) or URL(s)")
p_delete.add_argument("-a", "--all", action="store_true", help="delete all pastes (admin)")
p_delete.add_argument(
"-c", "--confirm", type=int, metavar="N", help="confirm expected delete count"
)
# info
subparsers.add_parser("info", aliases=["i"], help="show server info")