HarbourSOFT-LAB commited on
Commit
3661b15
·
verified ·
1 Parent(s): 920e9c2

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +167 -80
index.html CHANGED
@@ -1,101 +1,188 @@
1
  <!DOCTYPE html>
2
- <html lang="en">
3
  <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Phishing Email Detection</title>
7
  <style>
8
- body {
9
- font-family: Arial, sans-serif;
10
- padding: 20px;
11
- }
12
- textarea {
13
- width: 100%;
14
- height: 200px;
15
- margin-bottom: 20px;
16
- }
17
- button {
18
- padding: 10px 20px;
19
- background-color: #4CAF50;
20
- color: white;
21
- border: none;
22
- cursor: pointer;
23
- }
24
- #result {
25
- margin-top: 20px;
26
- padding: 10px;
27
- background-color: #f4f4f4;
28
- }
29
- #loading-message {
30
- margin-top: 20px;
31
- color: blue;
32
- }
33
  </style>
34
  </head>
35
  <body>
36
  <h1>Phishing Email Detection</h1>
37
- <p>Would you like to load the phishing detection model?</p>
38
- <button id="load-model-btn">Yes, Load Model</button>
39
- <button id="cancel-load-btn" style="display: none;">Cancel</button>
40
-
41
- <div id="loading-message" style="display: none;">Loading model, please wait...</div>
42
-
43
- <div id="email-input-section" style="display: none;">
44
- <textarea id="email-input" placeholder="Paste email content here..."></textarea><br>
45
- <button id="detect-btn">Detect Phishing</button>
 
 
 
 
 
 
 
 
 
46
  <div id="result"></div>
47
  </div>
48
 
 
 
 
49
  <script type="module">
50
- // Import transformers.js directly from the CDN
51
- import { pipeline } from "https://cdn.jsdelivr.net/npm/@huggingface/transformers@latest/dist/transformers.min.js";
52
-
53
- const modelPath = "https://huggingface.co/aaazKI/phishing-email-detection-distilbert_v2.4.1-ONNX/resolve/main/onnx/model.onnx";
54
- let model;
55
-
56
- const loadModelBtn = document.getElementById("load-model-btn");
57
- const cancelLoadBtn = document.getElementById("cancel-load-btn");
58
- const emailInputSection = document.getElementById("email-input-section");
59
- const loadingMessage = document.getElementById("loading-message");
60
- const resultDiv = document.getElementById("result");
61
- const detectBtn = document.getElementById("detect-btn");
62
-
63
- loadModelBtn.addEventListener("click", async () => {
64
- // Hide buttons and show loading message
65
- loadModelBtn.style.display = "none";
66
- cancelLoadBtn.style.display = "inline-block";
67
- loadingMessage.style.display = "block";
68
-
69
- // Load the model asynchronously
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  try {
71
- model = await pipeline("text-classification", modelPath);
72
- loadingMessage.style.display = "none";
73
- emailInputSection.style.display = "block"; // Show input section once model is loaded
74
- } catch (error) {
75
- loadingMessage.innerText = "Error loading the model. Please try again.";
 
76
  }
77
- });
78
 
79
- cancelLoadBtn.addEventListener("click", () => {
80
- // Reset the UI if the user cancels loading
81
- loadModelBtn.style.display = "inline-block";
82
- cancelLoadBtn.style.display = "none";
83
- loadingMessage.style.display = "none";
84
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
- detectBtn.addEventListener("click", async () => {
87
- const emailText = document.getElementById("email-input").value;
88
- if (!emailText.trim()) {
89
- resultDiv.innerText = "Please enter some text to classify.";
90
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  }
 
92
 
93
- // Run inference using the model
94
- const result = await model(emailText);
 
 
 
 
 
 
95
 
96
- // Display the result
97
- const prediction = result[0].label;
98
- resultDiv.innerText = `Prediction: ${prediction}`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  });
100
  </script>
101
  </body>
 
1
  <!DOCTYPE html>
2
+ <html lang="zh">
3
  <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width,initial-scale=1.0" />
6
+ <title>Phishing Email Detection (Transformers.js + ONNX)</title>
7
  <style>
8
+ body { font-family: Arial, sans-serif; padding: 20px; line-height: 1.6; }
9
+ textarea { width: 100%; height: 200px; margin: 12px 0; }
10
+ button { padding: 10px 16px; background: #4CAF50; color: #fff; border: 0; cursor: pointer; border-radius: 6px; }
11
+ button.secondary { background: #777; }
12
+ #result { margin-top: 16px; padding: 12px; background: #f4f4f4; border-radius: 6px; white-space: pre-wrap; }
13
+ #loading { margin-top: 16px; }
14
+ #email-input-section { margin-top: 16px; }
15
+ #log { background: #0b1021; color: #b8d0ff; padding: 12px; border-radius: 6px; height: 180px; overflow: auto; font-size: 12px; }
16
+ .row { display: flex; align-items: center; gap: 8px; margin: 8px 0; }
17
+ progress { width: 280px; height: 12px; }
18
+ .muted { color: #666; font-size: 12px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </style>
20
  </head>
21
  <body>
22
  <h1>Phishing Email Detection</h1>
23
+
24
+ <p>是否加载钓鱼检测模型?</p>
25
+ <div class="row">
26
+ <button id="load-model-btn">是,开始加载模型</button>
27
+ <button id="cancel-load-btn" class="secondary" style="display:none;">取消(仅重置UI)</button>
28
+ </div>
29
+
30
+ <div id="loading" style="display:none;">
31
+ <div class="row">
32
+ <progress id="progress" value="0" max="100"></progress>
33
+ <span id="progress-label">0%</span>
34
+ </div>
35
+ <div class="muted">首次运行会下载模型与分词器等文件,时间取决于网络与浏览器缓存。</div>
36
+ </div>
37
+
38
+ <div id="email-input-section" style="display:none;">
39
+ <textarea id="email-input" placeholder="把邮件内容粘贴到这里……"></textarea><br />
40
+ <button id="detect-btn">开始检测</button>
41
  <div id="result"></div>
42
  </div>
43
 
44
+ <h3>调试日志</h3>
45
+ <div id="log"></div>
46
+
47
  <script type="module">
48
+ // ===== 配置区 =====
49
+ // 确保使用“仓库ID”,不要填 .onnx 文件直链!
50
+ const MODEL_ID = 'onnx-community/phishing-email-detection-distilbert_v2.4.1-ONNX';
51
+ // 优先使用 v3(@huggingface/transformers),失败则回退到 v2(@xenova/transformers)
52
+ const CDN_PRIMARY = 'https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.0.0';
53
+ const CDN_FALLBACK = 'https://cdn.jsdelivr.net/npm/@xenova/transformers@2.17.2';
54
+
55
+ // ===== DOM =====
56
+ const loadBtn = document.getElementById('load-model-btn');
57
+ const cancelBtn = document.getElementById('cancel-load-btn');
58
+ const emailSec = document.getElementById('email-input-section');
59
+ const detectBtn = document.getElementById('detect-btn');
60
+ const resultDiv = document.getElementById('result');
61
+ const loading = document.getElementById('loading');
62
+ const pbar = document.getElementById('progress');
63
+ const plabel = document.getElementById('progress-label');
64
+ const logDiv = document.getElementById('log');
65
+
66
+ // ===== 状态 =====
67
+ let pipe = null;
68
+ let cancelled = false;
69
+
70
+ function log(...args) {
71
+ const line = args.map(a => (typeof a === 'string' ? a : JSON.stringify(a, null, 2))).join(' ');
72
+ logDiv.textContent += line + '\n';
73
+ logDiv.scrollTop = logDiv.scrollHeight;
74
+ console.log('[LOG]', ...args);
75
+ }
76
+ function setProgress(evt) {
77
+ if (evt?.status === 'progress') {
78
+ const pct = Math.max(0, Math.min(100, Math.round(evt.progress || 0)));
79
+ pbar.value = pct;
80
+ plabel.textContent = `${pct}% ${evt?.name || evt?.file || ''}`;
81
+ } else if (evt?.status) {
82
+ log(`status: ${evt.status} ${evt?.name || evt?.file || ''}`);
83
+ }
84
+ }
85
+ function showError(err) {
86
+ const msg = err?.stack || err?.message || String(err);
87
+ log('❌ ERROR:', msg);
88
+ }
89
+
90
+ // 捕获全局未处理错误,方便定位
91
+ window.addEventListener('error', e => log('window.error:', e.message, e.filename, e.lineno + ':' + e.colno));
92
+ window.addEventListener('unhandledrejection', e => log('unhandledrejection:', e.reason?.message || e.reason));
93
+
94
+ async function importTransformers() {
95
  try {
96
+ log('尝试加载库:', CDN_PRIMARY);
97
+ return await import(CDN_PRIMARY);
98
+ } catch (e1) {
99
+ showError(e1);
100
+ log('主库导入失败,改用回退版本:', CDN_FALLBACK);
101
+ return await import(CDN_FALLBACK);
102
  }
103
+ }
104
 
105
+ loadBtn.addEventListener('click', async () => {
106
+ try {
107
+ cancelled = false;
108
+ loadBtn.style.display = 'none';
109
+ cancelBtn.style.display = 'inline-block';
110
+ loading.style.display = 'block';
111
+ pbar.value = 0; plabel.textContent = '0%';
112
+
113
+ const mod = await importTransformers();
114
+ const { pipeline, env } = mod;
115
+ log('Transformers.js 版本已加载。');
116
+
117
+ // 可选:指定 WebGPU/wasm。默认自动选择;如想强制 WebGPU,可改为 'webgpu'
118
+ const device = 'auto';
119
+
120
+ // 量化权重(更小更快)。如果仓库提供 q4,就会加载它(该仓库提供多种量化权重)。:contentReference[oaicite:6]{index=6}
121
+ const options = {
122
+ dtype: 'q4',
123
+ device,
124
+ // 显示下载进度
125
+ progress_callback: setProgress,
126
+ };
127
 
128
+ log('开始通过仓库ID加载模型:', MODEL_ID);
129
+ pipe = await pipeline('text-classification', MODEL_ID, options);
130
+
131
+ if (cancelled) {
132
+ log('注意:取消只会重置UI,后台加载无法真正中断(库暂不支持)。'); // 说明限制
133
+ }
134
+
135
+ loading.style.display = 'none';
136
+ cancelBtn.style.display = 'none';
137
+ emailSec.style.display = 'block';
138
+ log('✅ 模型加载完成。');
139
+ } catch (err) {
140
+ loading.style.display = 'none';
141
+ cancelBtn.style.display = 'none';
142
+ loadBtn.style.display = 'inline-block';
143
+ showError(err);
144
+
145
+ // 常见定位:是否 404(多半是仓库ID写错或没权限)
146
+ if ((err?.message || '').includes('404')) {
147
+ log('可能的原因:仓库ID不存在/拼写错误/私有仓库无权限。当前使用:', MODEL_ID);
148
+ log('建议改用公开可用且结构完整的仓库:onnx-community/phishing-email-detection-distilbert_v2.4.1-ONNX');
149
+ }
150
+
151
+ // 文档:pipeline 要求模型在 Hub 上且 ONNX 权重在 onnx/ 子目录。:contentReference[oaicite:7]{index=7}
152
+ log('参考:Transformers.js pipeline 需仓库ID(含 onnx/ 权重与配置文件)。');
153
  }
154
+ });
155
 
156
+ cancelBtn.addEventListener('click', () => {
157
+ // 仅重置 UI;Transformers.js 暂不支持真正中断 pipeline() 的加载。:contentReference[oaicite:8]{index=8}
158
+ cancelled = true;
159
+ loadBtn.style.display = 'inline-block';
160
+ cancelBtn.style.display = 'none';
161
+ loading.style.display = 'none';
162
+ log('已取消(仅UI)。如果后台仍在下载,请稍等其失败或完成。');
163
+ });
164
 
165
+ detectBtn.addEventListener('click', async () => {
166
+ try {
167
+ const emailText = document.getElementById('email-input').value.trim();
168
+ if (!emailText) {
169
+ resultDiv.textContent = '请输入要分类的文本。';
170
+ return;
171
+ }
172
+ if (!pipe) {
173
+ resultDiv.textContent = '模型尚未加载成功。';
174
+ return;
175
+ }
176
+ resultDiv.textContent = '推理中…';
177
+ const out = await pipe(emailText); // 默认返回 top-1
178
+ // 同时把完整结构打印到日志,便于核对标签名称与分数
179
+ log('推理结果:', out);
180
+ const first = Array.isArray(out) ? out[0] : out;
181
+ resultDiv.textContent = `Prediction: ${first.label}\nScore: ${first.score?.toFixed?.(4) ?? first.score}`;
182
+ } catch (err) {
183
+ showError(err);
184
+ resultDiv.textContent = '检测失败,详见调试日志。';
185
+ }
186
  });
187
  </script>
188
  </body>