mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-09 07:48:57 +00:00
Extend entry search filtering
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from typing import Optional, List
|
||||
import json
|
||||
|
||||
import typer
|
||||
@@ -135,10 +135,20 @@ def entry_list(
|
||||
|
||||
|
||||
@entry_app.command("search")
|
||||
def entry_search(ctx: typer.Context, query: str) -> None:
|
||||
def entry_search(
|
||||
ctx: typer.Context,
|
||||
query: str,
|
||||
kind: List[str] = typer.Option(
|
||||
None,
|
||||
"--kind",
|
||||
"-k",
|
||||
help="Filter by entry kinds (can be repeated)",
|
||||
),
|
||||
) -> None:
|
||||
"""Search entries."""
|
||||
service = _get_entry_service(ctx)
|
||||
results = service.search_entries(query)
|
||||
kinds = list(kind) if kind else None
|
||||
results = service.search_entries(query, kinds=kinds)
|
||||
if not results:
|
||||
typer.echo("No matching entries found")
|
||||
return
|
||||
|
@@ -220,9 +220,21 @@ class EntryService:
|
||||
include_archived=include_archived,
|
||||
)
|
||||
|
||||
def search_entries(self, query: str):
|
||||
def search_entries(
|
||||
self, query: str, kinds: list[str] | None = None
|
||||
) -> list[tuple[int, str, str | None, str | None, bool]]:
|
||||
"""Search entries optionally filtering by ``kinds``.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
query:
|
||||
Search string to match against entry metadata.
|
||||
kinds:
|
||||
Optional list of entry kinds to restrict the search.
|
||||
"""
|
||||
|
||||
with self._lock:
|
||||
return self._manager.entry_manager.search_entries(query)
|
||||
return self._manager.entry_manager.search_entries(query, kinds=kinds)
|
||||
|
||||
def retrieve_entry(self, entry_id: int):
|
||||
with self._lock:
|
||||
|
@@ -1045,9 +1045,10 @@ class EntryManager:
|
||||
return []
|
||||
|
||||
def search_entries(
|
||||
self, query: str
|
||||
self, query: str, kinds: List[str] | None = None
|
||||
) -> List[Tuple[int, str, Optional[str], Optional[str], bool]]:
|
||||
"""Return entries matching the query across common fields."""
|
||||
"""Return entries matching ``query`` across whitelisted metadata fields."""
|
||||
|
||||
data = self._load_index()
|
||||
entries_data = data.get("entries", {})
|
||||
|
||||
@@ -1059,74 +1060,33 @@ class EntryManager:
|
||||
|
||||
for idx, entry in sorted(entries_data.items(), key=lambda x: int(x[0])):
|
||||
etype = entry.get("type", entry.get("kind", EntryType.PASSWORD.value))
|
||||
|
||||
if kinds is not None and etype not in kinds:
|
||||
continue
|
||||
|
||||
label = entry.get("label", entry.get("website", ""))
|
||||
notes = entry.get("notes", "")
|
||||
username = (
|
||||
entry.get("username", "") if etype == EntryType.PASSWORD.value else None
|
||||
)
|
||||
url = entry.get("url", "") if etype == EntryType.PASSWORD.value else None
|
||||
tags = entry.get("tags", [])
|
||||
archived = entry.get("archived", entry.get("blacklisted", False))
|
||||
|
||||
label_match = query_lower in label.lower()
|
||||
notes_match = query_lower in notes.lower()
|
||||
username_match = bool(username) and query_lower in username.lower()
|
||||
url_match = bool(url) and query_lower in url.lower()
|
||||
tags_match = any(query_lower in str(t).lower() for t in tags)
|
||||
|
||||
if etype == EntryType.PASSWORD.value:
|
||||
username = entry.get("username", "")
|
||||
url = entry.get("url", "")
|
||||
custom_fields = entry.get("custom_fields", [])
|
||||
custom_match = any(
|
||||
query_lower in str(cf.get("label", "")).lower()
|
||||
or query_lower in str(cf.get("value", "")).lower()
|
||||
for cf in custom_fields
|
||||
if label_match or username_match or url_match or tags_match:
|
||||
results.append(
|
||||
(
|
||||
int(idx),
|
||||
label,
|
||||
username if username is not None else None,
|
||||
url if url is not None else None,
|
||||
archived,
|
||||
)
|
||||
)
|
||||
if (
|
||||
label_match
|
||||
or query_lower in username.lower()
|
||||
or query_lower in url.lower()
|
||||
or notes_match
|
||||
or custom_match
|
||||
or tags_match
|
||||
):
|
||||
results.append(
|
||||
(
|
||||
int(idx),
|
||||
label,
|
||||
username,
|
||||
url,
|
||||
entry.get("archived", entry.get("blacklisted", False)),
|
||||
)
|
||||
)
|
||||
elif etype in (EntryType.KEY_VALUE.value, EntryType.MANAGED_ACCOUNT.value):
|
||||
value_field = str(entry.get("value", ""))
|
||||
custom_fields = entry.get("custom_fields", [])
|
||||
custom_match = any(
|
||||
query_lower in str(cf.get("label", "")).lower()
|
||||
or query_lower in str(cf.get("value", "")).lower()
|
||||
for cf in custom_fields
|
||||
)
|
||||
if (
|
||||
label_match
|
||||
or query_lower in value_field.lower()
|
||||
or notes_match
|
||||
or custom_match
|
||||
or tags_match
|
||||
):
|
||||
results.append(
|
||||
(
|
||||
int(idx),
|
||||
label,
|
||||
None,
|
||||
None,
|
||||
entry.get("archived", entry.get("blacklisted", False)),
|
||||
)
|
||||
)
|
||||
else:
|
||||
if label_match or notes_match or tags_match:
|
||||
results.append(
|
||||
(
|
||||
int(idx),
|
||||
label,
|
||||
None,
|
||||
None,
|
||||
entry.get("archived", entry.get("blacklisted", False)),
|
||||
)
|
||||
)
|
||||
|
||||
return results
|
||||
|
||||
|
Reference in New Issue
Block a user