Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -10,7 +10,7 @@ import shodan
|
|
10 |
import time
|
11 |
from datasets import load_dataset, Dataset
|
12 |
from huggingface_hub import HfApi, login
|
13 |
-
|
14 |
from typing import List, Dict, Any, Optional, Tuple
|
15 |
import pandas as pd
|
16 |
|
@@ -48,7 +48,7 @@ def get_dataset() -> Dataset:
|
|
48 |
"""Get the dataset with Ollama instances and models."""
|
49 |
try:
|
50 |
# Try to load the existing dataset
|
51 |
-
hf_token =
|
52 |
dataset = load_dataset(DATASET_NAME, use_auth_token=hf_token)
|
53 |
logger.info(f"Loaded existing dataset: {DATASET_NAME}")
|
54 |
return dataset["train"]
|
@@ -70,7 +70,7 @@ def get_dataset() -> Dataset:
|
|
70 |
def push_dataset(dataset: Dataset) -> None:
|
71 |
"""Push the dataset to the Hub."""
|
72 |
try:
|
73 |
-
hf_token =
|
74 |
dataset.push_to_hub(DATASET_NAME, token=hf_token)
|
75 |
logger.info(f"Pushed dataset to Hub: {DATASET_NAME}")
|
76 |
except Exception as e:
|
@@ -203,8 +203,13 @@ async def scan_ollama_instances(progress=None) -> Tuple[str, Dataset]:
|
|
203 |
|
204 |
# Get secrets
|
205 |
try:
|
206 |
-
shodan_api_key =
|
207 |
-
shodan_query =
|
|
|
|
|
|
|
|
|
|
|
208 |
except Exception as e:
|
209 |
logger.error(f"Failed to get secrets: {e}")
|
210 |
return "Error: Failed to retrieve secrets", None
|
@@ -370,10 +375,195 @@ def get_unique_values(dataset: Dataset) -> Tuple[List[str], List[str]]:
|
|
370 |
def login_submit(password: str) -> Tuple[bool, str, str]:
|
371 |
"""Handle admin login."""
|
372 |
try:
|
373 |
-
stored_password =
|
|
|
|
|
|
|
|
|
374 |
|
375 |
# Check if stored password is already hashed
|
376 |
-
if stored_password.startswith('$2b
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
377 |
is_valid = verify_password(password, stored_password)
|
378 |
else:
|
379 |
# Compare directly for first-time setup
|
@@ -383,6 +573,8 @@ def login_submit(password: str) -> Tuple[bool, str, str]:
|
|
383 |
if is_valid:
|
384 |
hashed_password = hash_password(stored_password)
|
385 |
logger.info("Hashed admin password for future use")
|
|
|
|
|
386 |
|
387 |
if is_valid:
|
388 |
return True, "success", "Login successful! You now have admin access."
|
|
|
10 |
import time
|
11 |
from datasets import load_dataset, Dataset
|
12 |
from huggingface_hub import HfApi, login
|
13 |
+
import os
|
14 |
from typing import List, Dict, Any, Optional, Tuple
|
15 |
import pandas as pd
|
16 |
|
|
|
48 |
"""Get the dataset with Ollama instances and models."""
|
49 |
try:
|
50 |
# Try to load the existing dataset
|
51 |
+
hf_token = os.getenv("HF_TOKEN")
|
52 |
dataset = load_dataset(DATASET_NAME, use_auth_token=hf_token)
|
53 |
logger.info(f"Loaded existing dataset: {DATASET_NAME}")
|
54 |
return dataset["train"]
|
|
|
70 |
def push_dataset(dataset: Dataset) -> None:
|
71 |
"""Push the dataset to the Hub."""
|
72 |
try:
|
73 |
+
hf_token = os.getenv("HF_TOKEN")
|
74 |
dataset.push_to_hub(DATASET_NAME, token=hf_token)
|
75 |
logger.info(f"Pushed dataset to Hub: {DATASET_NAME}")
|
76 |
except Exception as e:
|
|
|
203 |
|
204 |
# Get secrets
|
205 |
try:
|
206 |
+
shodan_api_key = os.getenv("SHODAN_API_KEY")
|
207 |
+
shodan_query = os.getenv("SHODAN_QUERY")
|
208 |
+
|
209 |
+
if not shodan_api_key:
|
210 |
+
raise ValueError("SHODAN_API_KEY environment variable is not set")
|
211 |
+
if not shodan_query:
|
212 |
+
raise ValueError("SHODAN_QUERY environment variable is not set")
|
213 |
except Exception as e:
|
214 |
logger.error(f"Failed to get secrets: {e}")
|
215 |
return "Error: Failed to retrieve secrets", None
|
|
|
375 |
def login_submit(password: str) -> Tuple[bool, str, str]:
|
376 |
"""Handle admin login."""
|
377 |
try:
|
378 |
+
stored_password = os.getenv("ADMIN_PASSWORD")
|
379 |
+
|
380 |
+
if not stored_password:
|
381 |
+
logger.error("ADMIN_PASSWORD environment variable is not set")
|
382 |
+
return False, "error", "Admin password is not configured. Please contact the administrator."
|
383 |
|
384 |
# Check if stored password is already hashed
|
385 |
+
if stored_password.startswith('$2b
|
386 |
+
|
387 |
+
def search_models(
|
388 |
+
family: str,
|
389 |
+
parameter_size: str,
|
390 |
+
name_search: str,
|
391 |
+
is_admin: bool,
|
392 |
+
dataset: Dataset
|
393 |
+
) -> Tuple[pd.DataFrame, List[Dict[str, Any]]]:
|
394 |
+
"""Search and filter models in the dataset."""
|
395 |
+
df, details = filter_and_search_models(
|
396 |
+
dataset,
|
397 |
+
family=None if family == "All" else family,
|
398 |
+
parameter_size=None if parameter_size == "All" else parameter_size,
|
399 |
+
name_search=name_search,
|
400 |
+
is_admin=is_admin
|
401 |
+
)
|
402 |
+
return df, details
|
403 |
+
|
404 |
+
def show_model_details(evt: gr.SelectData, models: List[Dict[str, Any]]) -> Dict[str, Any]:
|
405 |
+
"""Show details for a selected model."""
|
406 |
+
if not models or evt.index[0] >= len(models):
|
407 |
+
return {}
|
408 |
+
|
409 |
+
model = models[evt.index[0]]
|
410 |
+
return model
|
411 |
+
|
412 |
+
# Main Gradio application
|
413 |
+
def create_app():
|
414 |
+
with gr.Blocks(title="Ollama Scanner", theme=gr.themes.Soft()) as app:
|
415 |
+
# State variables
|
416 |
+
admin_logged_in = gr.State(False)
|
417 |
+
dataset_state = gr.State(get_dataset())
|
418 |
+
model_details_state = gr.State([])
|
419 |
+
|
420 |
+
# Header
|
421 |
+
gr.Markdown("# 🔍 Ollama Scanner")
|
422 |
+
gr.Markdown("Browse publicly accessible Ollama instances and their models")
|
423 |
+
|
424 |
+
# Login tab
|
425 |
+
with gr.Tab("Admin Login") as login_tab:
|
426 |
+
with gr.Group():
|
427 |
+
gr.Markdown("### Admin Login")
|
428 |
+
gr.Markdown("Enter the admin password to access administrative features.")
|
429 |
+
|
430 |
+
admin_password = gr.Textbox(
|
431 |
+
type="password",
|
432 |
+
label="Admin Password",
|
433 |
+
placeholder="Enter admin password"
|
434 |
+
)
|
435 |
+
login_btn = gr.Button("Login", variant="primary")
|
436 |
+
login_status = gr.Markdown("")
|
437 |
+
|
438 |
+
# Browse Models tab
|
439 |
+
with gr.Tab("Browse Models") as browse_tab:
|
440 |
+
with gr.Row():
|
441 |
+
# Filters column
|
442 |
+
with gr.Column(scale=1):
|
443 |
+
gr.Markdown("### Filters")
|
444 |
+
|
445 |
+
family_dropdown = gr.Dropdown(
|
446 |
+
choices=["All"],
|
447 |
+
value="All",
|
448 |
+
label="Model Family"
|
449 |
+
)
|
450 |
+
|
451 |
+
parameter_size_dropdown = gr.Dropdown(
|
452 |
+
choices=["All"],
|
453 |
+
value="All",
|
454 |
+
label="Parameter Size"
|
455 |
+
)
|
456 |
+
|
457 |
+
name_search = gr.Textbox(
|
458 |
+
label="Search by Name",
|
459 |
+
placeholder="Search model names..."
|
460 |
+
)
|
461 |
+
|
462 |
+
search_btn = gr.Button("Search", variant="primary")
|
463 |
+
|
464 |
+
stats_box = gr.Markdown("Loading stats...")
|
465 |
+
|
466 |
+
# Results column
|
467 |
+
with gr.Column(scale=2):
|
468 |
+
gr.Markdown("### Results")
|
469 |
+
|
470 |
+
results_table = gr.DataFrame(
|
471 |
+
label="Models",
|
472 |
+
interactive=False
|
473 |
+
)
|
474 |
+
|
475 |
+
model_json_display = gr.JSON(
|
476 |
+
label="Model Details",
|
477 |
+
visible=True
|
478 |
+
)
|
479 |
+
|
480 |
+
# Shodan Scan tab (admin only)
|
481 |
+
with gr.Tab("Shodan Scan", visible=False) as scan_tab:
|
482 |
+
with gr.Group():
|
483 |
+
gr.Markdown("### Shodan Scan")
|
484 |
+
gr.Markdown("Scan for publicly accessible Ollama instances using Shodan.")
|
485 |
+
|
486 |
+
scan_btn = gr.Button("Start Scan", variant="primary")
|
487 |
+
scan_progress = gr.Textbox(
|
488 |
+
label="Scan Status",
|
489 |
+
placeholder="Click 'Start Scan' to begin scanning...",
|
490 |
+
interactive=False
|
491 |
+
)
|
492 |
+
|
493 |
+
# Login logic
|
494 |
+
def handle_login(password):
|
495 |
+
is_admin, status, message = login_submit(password)
|
496 |
+
if is_admin:
|
497 |
+
return is_admin, message, gr.update(visible=True)
|
498 |
+
else:
|
499 |
+
return is_admin, message, gr.update(visible=False)
|
500 |
+
|
501 |
+
login_btn.click(
|
502 |
+
handle_login,
|
503 |
+
inputs=[admin_password],
|
504 |
+
outputs=[admin_logged_in, login_status, scan_tab]
|
505 |
+
)
|
506 |
+
|
507 |
+
# Search logic
|
508 |
+
def update_stats(dataset):
|
509 |
+
total_instances = len(dataset)
|
510 |
+
models_count = sum(len(instance.get('models', [])) for instance in dataset)
|
511 |
+
|
512 |
+
families, parameter_sizes = get_unique_values(dataset)
|
513 |
+
family_count = len(families) - 1 # Subtract "All"
|
514 |
+
|
515 |
+
return f"**Stats:** {total_instances} instances, {models_count} models, {family_count} families"
|
516 |
+
|
517 |
+
search_btn.click(
|
518 |
+
search_models,
|
519 |
+
inputs=[family_dropdown, parameter_size_dropdown, name_search, admin_logged_in, dataset_state],
|
520 |
+
outputs=[results_table, model_details_state]
|
521 |
+
)
|
522 |
+
|
523 |
+
# Model selection logic
|
524 |
+
results_table.select(
|
525 |
+
show_model_details,
|
526 |
+
inputs=[model_details_state],
|
527 |
+
outputs=[model_json_display]
|
528 |
+
)
|
529 |
+
|
530 |
+
# Scan logic
|
531 |
+
async def run_scan():
|
532 |
+
result, updated_dataset = await scan_ollama_instances()
|
533 |
+
if updated_dataset is not None:
|
534 |
+
return result, updated_dataset, *get_unique_values(updated_dataset), update_stats(updated_dataset)
|
535 |
+
else:
|
536 |
+
return result, None, [], [], ""
|
537 |
+
|
538 |
+
scan_btn.click(
|
539 |
+
run_scan,
|
540 |
+
inputs=[],
|
541 |
+
outputs=[scan_progress, dataset_state, family_dropdown, parameter_size_dropdown, stats_box]
|
542 |
+
)
|
543 |
+
|
544 |
+
# Initial data load
|
545 |
+
def init_ui(dataset):
|
546 |
+
families, parameter_sizes = get_unique_values(dataset)
|
547 |
+
stats = update_stats(dataset)
|
548 |
+
|
549 |
+
# Run initial search
|
550 |
+
df, details = search_models("All", "All", "", False, dataset)
|
551 |
+
|
552 |
+
return families, parameter_sizes, stats, df, details
|
553 |
+
|
554 |
+
app.load(
|
555 |
+
init_ui,
|
556 |
+
inputs=[dataset_state],
|
557 |
+
outputs=[family_dropdown, parameter_size_dropdown, stats_box, results_table, model_details_state]
|
558 |
+
)
|
559 |
+
|
560 |
+
return app
|
561 |
+
|
562 |
+
# Start the application
|
563 |
+
if __name__ == "__main__":
|
564 |
+
app = create_app()
|
565 |
+
app.launch()
|
566 |
+
):
|
567 |
is_valid = verify_password(password, stored_password)
|
568 |
else:
|
569 |
# Compare directly for first-time setup
|
|
|
573 |
if is_valid:
|
574 |
hashed_password = hash_password(stored_password)
|
575 |
logger.info("Hashed admin password for future use")
|
576 |
+
# Note: We can't store the hashed password back to environment variables
|
577 |
+
# in a Hugging Face Space environment. This would require a different approach.
|
578 |
|
579 |
if is_valid:
|
580 |
return True, "success", "Login successful! You now have admin access."
|