BrianChuan010393 commited on
Commit
db1f0da
·
verified ·
1 Parent(s): 97e5e2a

Update API for connect

Browse files
Files changed (2) hide show
  1. app.py +141 -6
  2. baseline_logs.csv +60 -0
app.py CHANGED
@@ -2,7 +2,7 @@ import gradio as gr
2
  import datetime
3
  import pandas as pd
4
  import joblib
5
- from sklearn.cluster import HDBSCAN
6
  import numpy as np
7
  import logger_service
8
  import os
@@ -11,6 +11,8 @@ import matplotlib.pyplot as plt
11
  import seaborn as sns
12
  import matplotlib.font_manager as fm
13
  import shutil
 
 
14
 
15
  # --- 🌟 解決 Matplotlib 中文顯示問題 ---
16
  font_path = 'SimHei.ttf'
@@ -200,6 +202,15 @@ def api_get_chart_data(action_name):
200
  "action_code": action_code,
201
  "data": {"normal": normal_data, "abnormal": abnormal_data}
202
  }
 
 
 
 
 
 
 
 
 
203
  # =====================================================================
204
 
205
 
@@ -244,7 +255,111 @@ def get_dashboard_html():
244
  </div>
245
  """
246
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  def get_plot_data(selected_key):
 
248
  if not selected_key or not AI_READY: return None
249
  action_code = ACTION_MAP.get(selected_key)
250
  try:
@@ -419,7 +534,7 @@ def check_anomaly(ip, account, action, log_time):
419
  fit_X = np.vstack((other_history, relevant_history, new_data_point))
420
  strictness = max(2, int(2 + (ow / 100.0) * 4))
421
 
422
- model = HDBSCAN(min_cluster_size=strictness, min_samples=2)
423
  clusters = model.fit_predict(fit_X)
424
 
425
  if clusters[-1] == -1: return "⚠️異常_作息或行為不符"
@@ -668,13 +783,12 @@ with gr.Blocks(title="模擬校園入口與資安防護系統") as demo:
668
 
669
  demo.load(fn=timer_update, inputs=[action_dropdown], outputs=[stat_dashboard_display, action_plot])
670
  dashboard_timer.tick(fn=timer_update, inputs=[action_dropdown], outputs=[stat_dashboard_display, action_plot])
671
- action_dropdown.change(fn=get_plot_data, inputs=[action_dropdown], outputs=[action_plot])
672
  time_mode.change(fn=toggle_time_input, inputs=time_mode, outputs=custom_time_input)
673
  btn_explain_log.click(fn=explain_abnormal_log, inputs=[abnormal_log_input], outputs=[ai_explanation_output])
674
 
675
- weight_inputs = [overall_weight, time_weight, ip_weight, device_weight, geo_weight, freq_weight]
676
- for w_input in weight_inputs:
677
- w_input.change(fn=update_system_weights, inputs=weight_inputs, outputs=[weights_status])
678
 
679
  update_outputs = [main_router, role_tabs, login_msg, welcome_text, store_df_all, store_df_abn, abnormal_log_input]
680
 
@@ -735,6 +849,27 @@ with gr.Blocks(title="模擬校園入口與資安防護系統") as demo:
735
  api_chart_in = gr.Textbox()
736
  api_chart_out = gr.JSON()
737
  api_chart_btn.click(fn=api_get_chart_data, inputs=[api_chart_in], outputs=[api_chart_out], api_name="get_chart_data")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
738
  # =====================================================================
739
 
740
  if __name__ == "__main__":
 
2
  import datetime
3
  import pandas as pd
4
  import joblib
5
+ import hdbscan
6
  import numpy as np
7
  import logger_service
8
  import os
 
11
  import seaborn as sns
12
  import matplotlib.font_manager as fm
13
  import shutil
14
+ import io
15
+ import base64
16
 
17
  # --- 🌟 解決 Matplotlib 中文顯示問題 ---
18
  font_path = 'SimHei.ttf'
 
202
  "action_code": action_code,
203
  "data": {"normal": normal_data, "abnormal": abnormal_data}
204
  }
205
+
206
+ def api_get_abnormal_logs():
207
+ """取得所有異常 Log"""
208
+ abnormal_logs = [log for log in log_history if "異常" in str(log.get("status", "")) or "錯誤" in str(log.get("status", "")) or "受限" in str(log.get("status", ""))]
209
+ return {
210
+ "status": "success",
211
+ "count": len(abnormal_logs),
212
+ "data": abnormal_logs
213
+ }
214
  # =====================================================================
215
 
216
 
 
255
  </div>
256
  """
257
 
258
+ def get_plot_data_for_api(selected_key):
259
+ """API專用版本,返回Base64圖片字符串"""
260
+ if not selected_key or not AI_READY: return None
261
+ action_code = ACTION_MAP.get(selected_key)
262
+ try:
263
+ plt.close('all')
264
+ df_hist = baseline_df[baseline_df['action'] == action_code].copy()
265
+
266
+ df_live = pd.DataFrame(log_history)
267
+ if not df_live.empty:
268
+ df_live['timestamp'] = pd.to_datetime(df_live['timestamp'], errors='coerce')
269
+ df_live = df_live.dropna(subset=['timestamp'])
270
+ df_live = df_live[df_live['action'] == action_code].copy()
271
+
272
+ if not df_live.empty:
273
+ df_combined = pd.concat([df_hist, df_live], ignore_index=True)
274
+ df_combined = df_combined.drop_duplicates(subset=['timestamp', 'account', 'action'])
275
+ else:
276
+ df_combined = df_hist.copy()
277
+
278
+ if df_combined.empty:
279
+ fig, ax = plt.subplots(figsize=(12, 6))
280
+ if os.path.exists(font_path): plt.rc('font', family='SimHei')
281
+ ax.text(0.5, 0.5, f"【{selected_key}】目前尚無資料", ha='center', va='center', fontsize=15)
282
+ # 將圖片轉換為 Base64
283
+ buf = io.BytesIO()
284
+ fig.savefig(buf, format='png', dpi=100, bbox_inches='tight')
285
+ buf.seek(0)
286
+ img_base64 = base64.b64encode(buf.getvalue()).decode('utf-8')
287
+ buf.close()
288
+ plt.close(fig)
289
+ return f"data:image/png;base64,{img_base64}"
290
+
291
+ df_combined['month'] = df_combined['timestamp'].dt.month
292
+ df_combined['time_of_day'] = df_combined['timestamp'].dt.hour + df_combined['timestamp'].dt.minute / 60.0
293
+
294
+ is_abnormal = df_combined['status'].astype(str).str.contains("異常", na=False)
295
+ df_normal = df_combined[~is_abnormal]
296
+ df_abnormal = df_combined[is_abnormal]
297
+
298
+ fig, ax = plt.subplots(figsize=(12, 6))
299
+ sns.set_theme(style="whitegrid")
300
+ if os.path.exists(font_path): plt.rc('font', family='SimHei')
301
+ plt.rcParams['axes.unicode_minus'] = False
302
+
303
+ has_data = False
304
+ months_order = list(range(1, 13))
305
+
306
+ if not df_normal.empty:
307
+ has_data = True
308
+ sns.stripplot(data=df_normal, x='month', y='time_of_day', jitter=0.3, alpha=0.4, size=5, color='#10b981', order=months_order, ax=ax, label='正常資料')
309
+
310
+ if not df_abnormal.empty:
311
+ has_data = True
312
+ sns.stripplot(data=df_abnormal, x='month', y='time_of_day', jitter=0.3, alpha=0.9, size=9, color='#ef4444', marker='X', order=months_order, ax=ax, label='異常資料')
313
+
314
+ if not has_data:
315
+ ax.text(0.5, 0.5, f"【{selected_key}】目前尚無資料", ha='center', va='center', fontsize=15)
316
+ # 將圖片轉換為 Base64
317
+ buf = io.BytesIO()
318
+ fig.savefig(buf, format='png', dpi=100, bbox_inches='tight')
319
+ buf.seek(0)
320
+ img_base64 = base64.b64encode(buf.getvalue()).decode('utf-8')
321
+ buf.close()
322
+ plt.close(fig)
323
+ return f"data:image/png;base64,{img_base64}"
324
+
325
+ ax.set_xlabel('月份 (1 ~ 12月)', fontsize=12)
326
+ ax.set_ylabel('時間 (24小時制)', fontsize=12)
327
+ ax.set_yticks(np.arange(0, 25, 2))
328
+ ax.set_ylim(24.5, -0.5)
329
+
330
+ handles, labels = ax.get_legend_handles_labels()
331
+ by_label = dict(zip(labels, handles))
332
+ if by_label: ax.legend(by_label.values(), by_label.keys(), loc='upper right', bbox_to_anchor=(1.15, 1))
333
+
334
+ plt.tight_layout()
335
+
336
+ # 將圖片轉換為 Base64
337
+ buf = io.BytesIO()
338
+ fig.savefig(buf, format='png', dpi=100, bbox_inches='tight')
339
+ buf.seek(0)
340
+ img_base64 = base64.b64encode(buf.getvalue()).decode('utf-8')
341
+ buf.close()
342
+ plt.close(fig)
343
+ return f"data:image/png;base64,{img_base64}"
344
+ except Exception as e:
345
+ print(f"繪圖失敗: {e}")
346
+ # 返回一個簡單的錯誤圖片
347
+ fig, ax = plt.subplots(figsize=(8, 4))
348
+ ax.text(0.5, 0.5, f"圖表生成失敗: {str(e)}", ha='center', va='center', fontsize=12, color='red')
349
+ ax.set_xlim(0, 1)
350
+ ax.set_ylim(0, 1)
351
+ ax.axis('off')
352
+
353
+ buf = io.BytesIO()
354
+ fig.savefig(buf, format='png', dpi=100, bbox_inches='tight')
355
+ buf.seek(0)
356
+ img_base64 = base64.b64encode(buf.getvalue()).decode('utf-8')
357
+ buf.close()
358
+ plt.close(fig)
359
+ return f"data:image/png;base64,{img_base64}"
360
+
361
  def get_plot_data(selected_key):
362
+ """給Gradio界面使用,返回Figure對象"""
363
  if not selected_key or not AI_READY: return None
364
  action_code = ACTION_MAP.get(selected_key)
365
  try:
 
534
  fit_X = np.vstack((other_history, relevant_history, new_data_point))
535
  strictness = max(2, int(2 + (ow / 100.0) * 4))
536
 
537
+ model = hdbscan.HDBSCAN(min_cluster_size=strictness, min_samples=2)
538
  clusters = model.fit_predict(fit_X)
539
 
540
  if clusters[-1] == -1: return "⚠️異常_作息或行為不符"
 
783
 
784
  demo.load(fn=timer_update, inputs=[action_dropdown], outputs=[stat_dashboard_display, action_plot])
785
  dashboard_timer.tick(fn=timer_update, inputs=[action_dropdown], outputs=[stat_dashboard_display, action_plot])
786
+ action_dropdown.change(fn=get_plot_data, inputs=[action_dropdown], outputs=[action_plot], api_name="getPlotData")
787
  time_mode.change(fn=toggle_time_input, inputs=time_mode, outputs=custom_time_input)
788
  btn_explain_log.click(fn=explain_abnormal_log, inputs=[abnormal_log_input], outputs=[ai_explanation_output])
789
 
790
+ # weight_inputs = [overall_weight, time_weight, ip_weight, device_weight, geo_weight, freq_weight]
791
+ # 移除所有 UI 欄位的 update_system_weights 綁定,僅保留 API 專用端點
 
792
 
793
  update_outputs = [main_router, role_tabs, login_msg, welcome_text, store_df_all, store_df_abn, abnormal_log_input]
794
 
 
849
  api_chart_in = gr.Textbox()
850
  api_chart_out = gr.JSON()
851
  api_chart_btn.click(fn=api_get_chart_data, inputs=[api_chart_in], outputs=[api_chart_out], api_name="get_chart_data")
852
+
853
+ api_abnormal_logs_btn = gr.Button("API_Abnormal_Logs")
854
+ api_abnormal_logs_out = gr.JSON()
855
+ api_abnormal_logs_btn.click(fn=api_get_abnormal_logs, inputs=[], outputs=[api_abnormal_logs_out], api_name="get_abnormal_logs")
856
+
857
+ api_plot_btn = gr.Button("API_Plot")
858
+ api_plot_in = gr.Textbox()
859
+ api_plot_out = gr.Textbox()
860
+ api_plot_btn.click(fn=get_plot_data_for_api, inputs=[api_plot_in], outputs=[api_plot_out], api_name="get_plot_data_api")
861
+
862
+ # System weights update API endpoint
863
+ api_update_weights_btn = gr.Button("API_Update_Weights")
864
+ api_update_weights_ow = gr.Number(label="Overall Weight")
865
+ api_update_weights_tw = gr.Number(label="Time Weight")
866
+ api_update_weights_iw = gr.Number(label="IP Weight")
867
+ api_update_weights_dw = gr.Number(label="Device Weight")
868
+ api_update_weights_gw = gr.Number(label="Geo Weight")
869
+ api_update_weights_fw = gr.Number(label="Frequency Weight")
870
+ api_update_weights_in = [api_update_weights_ow, api_update_weights_tw, api_update_weights_iw, api_update_weights_dw, api_update_weights_gw, api_update_weights_fw]
871
+ api_update_weights_out = gr.Textbox()
872
+ api_update_weights_btn.click(fn=update_system_weights, inputs=api_update_weights_in, outputs=[api_update_weights_out], api_name="update_system_weights")
873
  # =====================================================================
874
 
875
  if __name__ == "__main__":
baseline_logs.csv CHANGED
@@ -49999,3 +49999,63 @@ timestamp,ip_address,cookie_id,account,role,action,status
49999
  2025-12-21 22:29:10,140.124.18.22,baseline_session,student,學生,click_eval_system,success
50000
  2025-02-18 22:19:13,61.228.45.112,baseline_session,student,學生,click_course_system,success
50001
  2025-06-11 08:40:08,61.228.45.112,baseline_session,student,學生,click_leave_system,success
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49999
  2025-12-21 22:29:10,140.124.18.22,baseline_session,student,學生,click_eval_system,success
50000
  2025-02-18 22:19:13,61.228.45.112,baseline_session,student,學生,click_course_system,success
50001
  2025-06-11 08:40:08,61.228.45.112,baseline_session,student,學生,click_leave_system,success
50002
+ 2026-03-29 11:25:30,140.124.71.55 (校內預設),sess_baseline,admin,管理,login_attempt,success
50003
+ 2026-06-13 11:00:00,140.124.71.55 (校內預設),sess_baseline,admin,管理,click_pre_select_system,success
50004
+ 2026-10-15 13:00:00,140.124.71.55 (校內預設),sess_baseline,admin,管理,click_pre_select_system,⚠️異常_作息或行為不符
50005
+ 2026-03-29 11:25:43,140.124.71.55 (校內預設),sess_baseline,student,學生,login_attempt,success
50006
+ 2026-03-29 11:25:45,140.124.71.55 (校內預設),sess_baseline,student,學生,click_istudy,⚠️異常_作息或行為不符
50007
+ 2026-03-29 11:25:49,140.124.71.55 (校內預設),sess_baseline,student,學生,click_istudy,⚠️異常_作息或行為不符
50008
+ 2026-03-29 11:25:51,140.124.71.55 (校內預設),sess_baseline,student,學生,click_card_loss,success
50009
+ 2026-03-29 11:25:53,140.124.71.55 (校內預設),sess_baseline,student,學生,click_course_system,⚠️異常_作息或行為不符
50010
+ 2026-03-29 11:25:54,140.124.71.55 (校內預設),sess_baseline,student,學生,click_score_query,⚠️異常_作息或行為不符
50011
+ 2026-03-29 11:25:56,140.124.71.55 (校內預設),sess_baseline,student,學生,click_dorm_system,⚠️異常_作息或行為不符
50012
+ 2026-03-29 11:25:57,140.124.71.55 (校內預設),sess_baseline,student,學生,click_leave_system,success
50013
+ 2026-03-29 11:25:59,140.124.71.55 (校內預設),sess_baseline,student,學生,click_webmail,⚠️異常_作息或行為不符
50014
+ 2026-03-29 11:26:00,140.124.71.55 (校內預設),sess_baseline,student,學生,click_vdesk,success
50015
+ 2026-03-29 11:26:01,140.124.71.55 (校內預設),sess_baseline,student,學生,click_webmail,⚠️異常_作息或行為不符
50016
+ 2026-03-29 11:26:04,140.124.71.55 (校內預設),sess_baseline,student,學生,click_scholarship,success
50017
+ 2026-03-29 11:26:06,140.124.71.55 (校內預設),sess_baseline,student,學生,click_scholarship,success
50018
+ 2026-03-29 11:26:08,140.124.71.55 (校內預設),sess_baseline,student,學生,click_leave_system,success
50019
+ 2026-03-29 11:26:10,140.124.71.55 (校內預設),sess_baseline,student,學生,click_dorm_system,⚠️異常_作息或行為不符
50020
+ 2026-03-29 11:26:11,140.124.71.55 (校內預設),sess_baseline,student,學生,click_webmail,⚠️異常_作息或行為不符
50021
+ 2026-03-29 11:26:14,140.124.71.55 (校內預設),sess_baseline,admin,管理,login_attempt,success
50022
+ 2026-06-13 11:00:00,140.124.71.55 (校內預設),sess_baseline,admin,管理,click_pre_select_system,success
50023
+ 2026-03-29 11:27:35,140.124.71.55 (校內預設),sess_baseline,admin,管理,login_attempt,success
50024
+ 2026-03-29 19:59:38,114.32.1.45,sess_9a2b4c6d8e0f,admin,管理,login_attempt,success
50025
+ 2026-03-29 20:10:17,114.32.1.45,sess_9a2b4c6d8e0f,admin,管理,login_attempt,success
50026
+ 2026-03-29 20:10:47,114.32.1.45,sess_9a2b4c6d8e0f,admin,管理,login_attempt,success
50027
+ 2026-03-29 20:11:07,140.124.71.55 (校內預設),sess_baseline,admin,管理,login_attempt,success
50028
+ 2026-03-29 20:15:19,114.32.1.45,sess_9a2b4c6d8e0f,admin,管理,login_attempt,success
50029
+ 2026-03-29 20:15:31,114.32.1.45,sess_9a2b4c6d8e0f,admin,管理,login_attempt,success
50030
+ 2026-03-29 20:18:07,114.32.1.45,sess_9a2b4c6d8e0f,admin,管理,login_attempt,success
50031
+ 2026-03-29 20:29:45,192.168.1.1,test_cookie,admin,管理,login_attempt,failed_captcha
50032
+ 2026-03-29 21:32:17,140.124.71.55 (校內預設),sess_baseline,admin,管理,login_attempt,success
50033
+ 2026-03-29 21:33:13,140.124.71.55 (校內預設),sess_baseline,admin,管理,login_attempt,success
50034
+ 2026-03-29 21:35:14,140.124.71.55 (校內預設),sess_baseline,admin,管理,login_attempt,success
50035
+ 2026-03-29 21:35:31,114.32.1.45,sess_9a2b4c6d8e0f,admin,管理,login_attempt,success
50036
+ 2026-03-29 21:53:57,114.32.1.45,sess_9a2b4c6d8e0f,admin,管理,login_attempt,success
50037
+ 2026-03-29 21:54:17,140.124.71.55 (校內預設),sess_baseline,admin,管理,login_attempt,success
50038
+ 2026-03-29 22:01:26,114.32.1.45,sess_9a2b4c6d8e0f,admin,管理,login_attempt,success
50039
+ 2026-03-29 22:01:39,140.124.71.55 (校內預設),sess_baseline,admin,管理,login_attempt,success
50040
+ 2026-03-29 22:07:04,114.32.1.45,sess_9a2b4c6d8e0f,admin,管理,login_attempt,success
50041
+ 2026-03-29 22:15:21,114.32.1.45,sess_9a2b4c6d8e0f,admin,管理,login_attempt,success
50042
+ 2026-03-29 22:15:29,140.124.71.55 (校內預設),sess_baseline,admin,管理,login_attempt,success
50043
+ 2026-03-29 22:23:16,114.32.1.45,sess_9a2b4c6d8e0f,admin,管理,click_add_drop_system,⚠️異常_地理位置受限 (預設防護: 禁止海外異常 IP)
50044
+ 2026-03-29 22:23:30,114.32.1.45,sess_9a2b4c6d8e0f,admin,管理,login_attempt,success
50045
+ 2026-03-29 22:28:32,140.124.71.55 (校內預設),sess_baseline,student,學生,login_attempt,success
50046
+ 2026-03-29 22:28:34,140.124.71.55 (校內預設),sess_baseline,student,學生,click_eval_system,⚠️異常_作息或行為不符
50047
+ 2026-03-29 22:28:35,140.124.71.55 (校內預設),sess_baseline,student,學生,click_istudy,success
50048
+ 2026-03-29 22:28:38,140.124.71.55 (校內預設),sess_baseline,admin,管理,login_attempt,success
50049
+ 2026-02-25 07:50:00,61.228.45.112 (家裡),sess_baseline,admin,管理,click_course_system,success
50050
+ 2026-03-20 03:00:00,61.228.45.112 (家裡),sess_baseline,student,學生,login_attempt,success
50051
+ 2026-03-20 03:00:00,61.228.45.112 (家裡),sess_baseline,student,學生,logout,success
50052
+ 2026-03-20 03:00:00,61.228.45.112 (家裡),sess_baseline,admin,管理,login_attempt,success
50053
+ 2026-03-20 03:00:00,61.228.45.112 (家裡),sess_baseline,admin,管理,logout,success
50054
+ 2026-03-20 03:00:00,61.228.45.112 (家裡),sess_baseline,adfa ,Unknown,login_attempt,failed_no_account
50055
+ 2026-03-29 22:29:31,114.32.1.45,sess_9a2b4c6d8e0f,admin,管理,logout,success
50056
+ 2026-03-29 22:29:34,114.32.1.45,sess_9a2b4c6d8e0f,ad fa,Unknown,login_attempt,failed_no_account
50057
+ 2026-03-29 22:29:40,114.32.1.45,sess_9a2b4c6d8e0f,admin,管理,login_attempt,failed_captcha
50058
+ 2026-03-29 22:29:43,114.32.1.45,sess_9a2b4c6d8e0f,admin,管理,login_attempt,failed_captcha
50059
+ 2026-03-29 22:29:48,114.32.1.45,sess_9a2b4c6d8e0f,admin,管理,login_attempt,failed_captcha
50060
+ 2026-03-29 22:29:52,114.32.1.45,sess_9a2b4c6d8e0f,admin,管理,login_attempt,success
50061
+ 2026-03-29 22:29:55,114.32.1.45,sess_9a2b4c6d8e0f,admin,管理,logout,success