Spaces:
Sleeping
Sleeping
dynamic history data
Browse files
app.py
CHANGED
|
@@ -52,23 +52,33 @@ ACTION_MAP = {
|
|
| 52 |
log_history = []
|
| 53 |
initial_df_abn = pd.DataFrame(columns=["timestamp", "ip_address", "cookie_id", "account", "role", "action", "status"])
|
| 54 |
|
| 55 |
-
# --- 1. 載入模型與歷史資料 ---
|
| 56 |
try:
|
| 57 |
le_action = joblib.load('le_action.pkl')
|
| 58 |
le_ip = joblib.load('le_ip.pkl')
|
| 59 |
|
| 60 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
baseline_df['timestamp'] = pd.to_datetime(baseline_df['timestamp'])
|
| 62 |
|
|
|
|
| 63 |
global_stats["total_count"] = len(baseline_df)
|
| 64 |
global_stats["abnormal_count"] = baseline_df[baseline_df['status'].str.contains("異常", na=False)].shape[0]
|
| 65 |
print(f"📊 統計初始化:總監測量 {global_stats['total_count']}, 異常數 {global_stats['abnormal_count']}")
|
| 66 |
|
| 67 |
-
#
|
| 68 |
display_df = baseline_df.sort_values(by='timestamp', ascending=False).head(20).copy()
|
| 69 |
display_df['timestamp'] = display_df['timestamp'].dt.strftime('%Y-%m-%d %H:%M:%S')
|
| 70 |
log_history = display_df.to_dict('records')
|
| 71 |
|
|
|
|
| 72 |
initial_abnormal_logs = [log for log in log_history if "異常" in str(log.get("status", ""))]
|
| 73 |
if initial_abnormal_logs:
|
| 74 |
initial_df_abn = pd.DataFrame(initial_abnormal_logs)
|
|
@@ -86,7 +96,7 @@ try:
|
|
| 86 |
))
|
| 87 |
|
| 88 |
AI_READY = True
|
| 89 |
-
print("✅ AI 模組與歷史基準資料載入成功!")
|
| 90 |
except Exception as e:
|
| 91 |
AI_READY = False
|
| 92 |
global_stats["total_count"] = 0
|
|
@@ -116,7 +126,7 @@ def get_dashboard_html():
|
|
| 116 |
<div class="stat-card card-total">
|
| 117 |
<div class="card-icon">📊</div>
|
| 118 |
<div class="card-content">
|
| 119 |
-
<div class="card-label">總監測量 (
|
| 120 |
<div class="card-value">{total:,} <span class="card-unit">筆 Log</span></div>
|
| 121 |
<div class="card-sub-text">自系統啟動起統計 (+今日即時)</div>
|
| 122 |
</div>
|
|
@@ -456,7 +466,7 @@ custom_css = """
|
|
| 456 |
|
| 457 |
with gr.Blocks(title="模擬校園入口與資安防護系統") as demo:
|
| 458 |
|
| 459 |
-
#
|
| 460 |
store_df_all = gr.State(value=pd.DataFrame(log_history))
|
| 461 |
store_df_abn = gr.State(value=initial_df_abn)
|
| 462 |
|
|
@@ -472,7 +482,6 @@ with gr.Blocks(title="模擬校園入口與資安防護系統") as demo:
|
|
| 472 |
custom_time_input = gr.Textbox(label="輸入自訂時間", placeholder="格式: YYYY-MM-DD HH:MM:SS", value="2026-03-17 03:00:00", visible=False)
|
| 473 |
gr.Markdown("---")
|
| 474 |
gr.Markdown("### 📊 即時 Log 與 AI 判定區")
|
| 475 |
-
# 🌟 指定吃 Pandas Dataframe 以維持最穩定的序列化
|
| 476 |
log_display = gr.Dataframe(value=pd.DataFrame(log_history), headers=["timestamp", "ip_address", "cookie_id", "account", "role", "action", "status"], datatype=["str", "str", "str", "str", "str", "str", "str"], interactive=False, wrap=True)
|
| 477 |
|
| 478 |
with gr.Column(scale=2):
|
|
@@ -585,7 +594,6 @@ with gr.Blocks(title="模擬校園入口與資安防護系統") as demo:
|
|
| 585 |
with gr.Row():
|
| 586 |
t_vdesk_norm = gr.Button("✅ 正常: 軟體雲/WebMail (03-22 18:00)", size="sm"); t_vdesk_anom = gr.Button("⚠️ 異常: 軟體雲/WebMail (08-10 00:00)", size="sm")
|
| 587 |
|
| 588 |
-
# --- 🌟 修正點 3:綁定 State 更新到實體的 Dataframe (確保 DOM 渲染完畢後才餵入資料) ---
|
| 589 |
def update_dataframes(df_all, df_abn):
|
| 590 |
return gr.update(value=df_all), gr.update(value=df_abn)
|
| 591 |
|
|
@@ -600,7 +608,6 @@ with gr.Blocks(title="模擬校園入口與資安防護系統") as demo:
|
|
| 600 |
|
| 601 |
weight_inputs = [overall_weight, time_weight, ip_weight, device_weight]
|
| 602 |
|
| 603 |
-
# 🎯 把輸出目標從實體 Dataframe 改為背後的 State
|
| 604 |
update_outputs = [main_router, role_tabs, login_msg, welcome_text, store_df_all, store_df_abn, abnormal_log_input]
|
| 605 |
|
| 606 |
login_btn.click(
|
|
@@ -636,7 +643,7 @@ with gr.Blocks(title="模擬校園入口與資安防護系統") as demo:
|
|
| 636 |
btn.click(
|
| 637 |
fn=lambda ip, c, acc, t_mode, t_custom, ow, tw, iw, dw, n=action_name: simulate_click(ip, c, acc, n, t_mode, t_custom, ow, tw, iw, dw),
|
| 638 |
inputs=[mock_ip, mock_cookie, acc_input, time_mode, custom_time_input] + weight_inputs,
|
| 639 |
-
outputs=[store_df_all, store_df_abn, abnormal_log_input]
|
| 640 |
)
|
| 641 |
|
| 642 |
test_mapping = [
|
|
@@ -657,7 +664,7 @@ with gr.Blocks(title="模擬校園入口與資安防護系統") as demo:
|
|
| 657 |
btn.click(
|
| 658 |
fn=lambda ip, c, acc, ow, tw, iw, dw, n=action_name, t_str=test_time_str: test_anomaly(ip, c, acc, n, t_str, ow, tw, iw, dw),
|
| 659 |
inputs=[mock_ip, mock_cookie, acc_input, overall_weight, time_weight, ip_weight, device_weight],
|
| 660 |
-
outputs=[store_df_all, store_df_abn, abnormal_log_input]
|
| 661 |
)
|
| 662 |
|
| 663 |
if __name__ == "__main__":
|
|
|
|
| 52 |
log_history = []
|
| 53 |
initial_df_abn = pd.DataFrame(columns=["timestamp", "ip_address", "cookie_id", "account", "role", "action", "status"])
|
| 54 |
|
| 55 |
+
# --- 1. 載入模型與歷史資料 (結合 logger_service 雲端同步) ---
|
| 56 |
try:
|
| 57 |
le_action = joblib.load('le_action.pkl')
|
| 58 |
le_ip = joblib.load('le_ip.pkl')
|
| 59 |
|
| 60 |
+
# 🌟 核心升級:啟動時直接從 Hugging Face 雲端拉取最新動態歷史資料庫
|
| 61 |
+
print("🔄 正在從 Hugging Face 雲端同步最新動態歷史資料...")
|
| 62 |
+
baseline_df = logger_service.sync_from_hf()
|
| 63 |
+
|
| 64 |
+
# 防呆機制:如果雲端資料庫為空或下載失敗,退回使用本地端檔案
|
| 65 |
+
if baseline_df.empty and os.path.exists('baseline_logs.csv'):
|
| 66 |
+
print("⚠️ 雲端資料庫為空或讀取失敗,改用本地端 baseline_logs.csv")
|
| 67 |
+
baseline_df = pd.read_csv('baseline_logs.csv')
|
| 68 |
+
|
| 69 |
baseline_df['timestamp'] = pd.to_datetime(baseline_df['timestamp'])
|
| 70 |
|
| 71 |
+
# 這裡的統計數據現在會包含雲端的最新總數與異常數
|
| 72 |
global_stats["total_count"] = len(baseline_df)
|
| 73 |
global_stats["abnormal_count"] = baseline_df[baseline_df['status'].str.contains("異常", na=False)].shape[0]
|
| 74 |
print(f"📊 統計初始化:總監測量 {global_stats['total_count']}, 異常數 {global_stats['abnormal_count']}")
|
| 75 |
|
| 76 |
+
# 確保前端表格吃的是正確的字串格式,並且直接抓取雲端最新的 20 筆作為預設顯示
|
| 77 |
display_df = baseline_df.sort_values(by='timestamp', ascending=False).head(20).copy()
|
| 78 |
display_df['timestamp'] = display_df['timestamp'].dt.strftime('%Y-%m-%d %H:%M:%S')
|
| 79 |
log_history = display_df.to_dict('records')
|
| 80 |
|
| 81 |
+
# 異常清單也會直接從雲端最新的資料中篩選出來
|
| 82 |
initial_abnormal_logs = [log for log in log_history if "異常" in str(log.get("status", ""))]
|
| 83 |
if initial_abnormal_logs:
|
| 84 |
initial_df_abn = pd.DataFrame(initial_abnormal_logs)
|
|
|
|
| 96 |
))
|
| 97 |
|
| 98 |
AI_READY = True
|
| 99 |
+
print("✅ AI 模組與動態歷史基準資料載入成功!")
|
| 100 |
except Exception as e:
|
| 101 |
AI_READY = False
|
| 102 |
global_stats["total_count"] = 0
|
|
|
|
| 126 |
<div class="stat-card card-total">
|
| 127 |
<div class="card-icon">📊</div>
|
| 128 |
<div class="card-content">
|
| 129 |
+
<div class="card-label">總監測量 (包含雲端資料庫)</div>
|
| 130 |
<div class="card-value">{total:,} <span class="card-unit">筆 Log</span></div>
|
| 131 |
<div class="card-sub-text">自系統啟動起統計 (+今日即時)</div>
|
| 132 |
</div>
|
|
|
|
| 466 |
|
| 467 |
with gr.Blocks(title="模擬校園入口與資安防護系統") as demo:
|
| 468 |
|
| 469 |
+
# 確保 State 初始化時能抓到剛從雲端下載的最新資料
|
| 470 |
store_df_all = gr.State(value=pd.DataFrame(log_history))
|
| 471 |
store_df_abn = gr.State(value=initial_df_abn)
|
| 472 |
|
|
|
|
| 482 |
custom_time_input = gr.Textbox(label="輸入自訂時間", placeholder="格式: YYYY-MM-DD HH:MM:SS", value="2026-03-17 03:00:00", visible=False)
|
| 483 |
gr.Markdown("---")
|
| 484 |
gr.Markdown("### 📊 即時 Log 與 AI 判定區")
|
|
|
|
| 485 |
log_display = gr.Dataframe(value=pd.DataFrame(log_history), headers=["timestamp", "ip_address", "cookie_id", "account", "role", "action", "status"], datatype=["str", "str", "str", "str", "str", "str", "str"], interactive=False, wrap=True)
|
| 486 |
|
| 487 |
with gr.Column(scale=2):
|
|
|
|
| 594 |
with gr.Row():
|
| 595 |
t_vdesk_norm = gr.Button("✅ 正常: 軟體雲/WebMail (03-22 18:00)", size="sm"); t_vdesk_anom = gr.Button("⚠️ 異常: 軟體雲/WebMail (08-10 00:00)", size="sm")
|
| 596 |
|
|
|
|
| 597 |
def update_dataframes(df_all, df_abn):
|
| 598 |
return gr.update(value=df_all), gr.update(value=df_abn)
|
| 599 |
|
|
|
|
| 608 |
|
| 609 |
weight_inputs = [overall_weight, time_weight, ip_weight, device_weight]
|
| 610 |
|
|
|
|
| 611 |
update_outputs = [main_router, role_tabs, login_msg, welcome_text, store_df_all, store_df_abn, abnormal_log_input]
|
| 612 |
|
| 613 |
login_btn.click(
|
|
|
|
| 643 |
btn.click(
|
| 644 |
fn=lambda ip, c, acc, t_mode, t_custom, ow, tw, iw, dw, n=action_name: simulate_click(ip, c, acc, n, t_mode, t_custom, ow, tw, iw, dw),
|
| 645 |
inputs=[mock_ip, mock_cookie, acc_input, time_mode, custom_time_input] + weight_inputs,
|
| 646 |
+
outputs=[store_df_all, store_df_abn, abnormal_log_input]
|
| 647 |
)
|
| 648 |
|
| 649 |
test_mapping = [
|
|
|
|
| 664 |
btn.click(
|
| 665 |
fn=lambda ip, c, acc, ow, tw, iw, dw, n=action_name, t_str=test_time_str: test_anomaly(ip, c, acc, n, t_str, ow, tw, iw, dw),
|
| 666 |
inputs=[mock_ip, mock_cookie, acc_input, overall_weight, time_weight, ip_weight, device_weight],
|
| 667 |
+
outputs=[store_df_all, store_df_abn, abnormal_log_input]
|
| 668 |
)
|
| 669 |
|
| 670 |
if __name__ == "__main__":
|