WACATW commited on
Commit
4edbaaa
·
verified ·
1 Parent(s): d380e1c

Upload static/deepseek_html_20260921_60b93d.html

Browse files
static/deepseek_html_20260921_60b93d.html ADDED
@@ -0,0 +1,307 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>HF 存储桶极速上传下载</title>
7
+ <style>
8
+ * { box-sizing: border-box; margin: 0; padding: 0; }
9
+ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0d1117; color: #c9d1d9; min-height: 100vh; display: flex; justify-content: center; padding: 30px 20px; }
10
+ .container { max-width: 680px; width: 100%; }
11
+ h1 { font-size: 1.4rem; margin-bottom: 6px; color: #f0f6fc; }
12
+ .subtitle { font-size: 0.85rem; color: #8b949e; margin-bottom: 20px; }
13
+ .card { background: #161b22; border: 1px solid #30363d; border-radius: 10px; padding: 20px; margin-bottom: 16px; }
14
+ .card h2 { font-size: 0.95rem; margin-bottom: 14px; color: #f0f6fc; border-bottom: 1px solid #21262d; padding-bottom: 8px; }
15
+ .field { margin-bottom: 12px; }
16
+ label { display: block; font-size: 0.78rem; color: #8b949e; margin-bottom: 4px; }
17
+ input[type="text"], input[type="password"] { width: 100%; padding: 8px 12px; background: #0d1117; border: 1px solid #30363d; border-radius: 6px; color: #c9d1d9; font-size: 0.85rem; outline: none; }
18
+ input:focus { border-color: #58a6ff; }
19
+ button { padding: 8px 16px; background: #238636; color: #fff; border: none; border-radius: 6px; font-size: 0.85rem; cursor: pointer; transition: background 0.2s; }
20
+ button:hover { background: #2ea043; }
21
+ button:disabled { background: #21262d; color: #484f58; cursor: not-allowed; }
22
+ button.secondary { background: #21262d; border: 1px solid #30363d; }
23
+ button.secondary:hover { background: #30363d; }
24
+ .row { display: flex; gap: 10px; align-items: flex-end; margin-bottom: 10px; }
25
+ .row .field { flex: 1; margin-bottom: 0; }
26
+ .status { margin-top: 10px; padding: 8px 12px; border-radius: 6px; font-size: 0.8rem; display: none; word-break: break-all; }
27
+ .status.success { display: block; background: #0d2818; border: 1px solid #238636; color: #3fb950; }
28
+ .status.error { display: block; background: #2d0f0f; border: 1px solid #f85149; color: #f85149; }
29
+ .status.info { display: block; background: #0d1f2d; border: 1px solid #1f6feb; color: #58a6ff; }
30
+ .file-list { list-style: none; margin-top: 10px; max-height: 250px; overflow-y: auto; }
31
+ .file-list li { display: flex; justify-content: space-between; align-items: center; padding: 6px 8px; border-bottom: 1px solid #21262d; font-size: 0.8rem; }
32
+ .file-list li:last-child { border-bottom: none; }
33
+ .file-list button { padding: 3px 10px; font-size: 0.75rem; }
34
+ .warning { background: #2d1f00; border: 1px solid #9e6a03; border-radius: 6px; padding: 10px 14px; font-size: 0.78rem; color: #d29922; margin-bottom: 16px; line-height: 1.4; }
35
+ </style>
36
+ </head>
37
+ <body>
38
+ <div class="container">
39
+ <h1>🚀 Hugging Face 存储桶极速工具</h1>
40
+ <p class="subtitle">本地双击直接运行 · 无需 HTTP 服务器</p>
41
+
42
+ <div class="warning">
43
+ ⚠️ <strong>安全提示:</strong>你的密钥已预填在页面中,<strong>绝对不要将此 HTML 文件发给别人或上传到公网!</strong>仅供本地个人使用。
44
+ </div>
45
+
46
+ <!-- 配置卡片 -->
47
+ <div class="card">
48
+ <h2>🔑 连接配置</h2>
49
+ <button id="loadBtn" class="secondary" style="margin-bottom: 14px; width: 100%;">⚡ 一键加载你的密钥(已预填)</button>
50
+ <div class="field">
51
+ <label>AWS_ACCESS_KEY_ID</label>
52
+ <input type="text" id="accessKey" />
53
+ </div>
54
+ <div class="field">
55
+ <label>AWS_SECRET_ACCESS_KEY</label>
56
+ <input type="password" id="secretKey" />
57
+ </div>
58
+ <div class="field">
59
+ <label>端点 URL(Endpoint)</label>
60
+ <input type="text" id="endpoint" />
61
+ </div>
62
+ <div class="field">
63
+ <label>存储桶名称(Bucket,包含命名空间)</label>
64
+ <input type="text" id="bucketName" />
65
+ </div>
66
+ <button id="connectBtn" class="secondary" style="width: 100%;">🔗 初始化连接</button>
67
+ <div id="connectStatus" class="status"></div>
68
+ </div>
69
+
70
+ <!-- 上传卡片 -->
71
+ <div class="card">
72
+ <h2>📤 上传文件</h2>
73
+ <div class="field">
74
+ <input type="file" id="fileInput" multiple />
75
+ </div>
76
+ <button id="uploadBtn" disabled style="width: 100%;">上传到存储桶</button>
77
+ <div id="uploadStatus" class="status"></div>
78
+ </div>
79
+
80
+ <!-- 下载与列表卡片 -->
81
+ <div class="card">
82
+ <h2>📥 下载文件 / 文件列表</h2>
83
+ <div class="row">
84
+ <div class="field">
85
+ <label>文件路径(如 test.txt)</label>
86
+ <input type="text" id="downloadPath" placeholder="test.txt" />
87
+ </div>
88
+ <button id="downloadBtn" disabled>下载</button>
89
+ </div>
90
+ <div id="downloadStatus" class="status"></div>
91
+ <button id="listBtn" class="secondary" style="margin-top: 10px; width: 100%;" disabled>📋 列出存储桶中的所有文件</button>
92
+ <ul class="file-list" id="fileList"></ul>
93
+ </div>
94
+ </div>
95
+
96
+ <!-- 引入轻量级 AWS S3 签名库(更换了国内更稳定的 CDN) -->
97
+ <script src="https://unpkg.com/aws4fetch@1.0.20/dist/aws4fetch.min.js"></script>
98
+
99
+ <script>
100
+ // ── DOM 引用 ──
101
+ const loadBtn = document.getElementById('loadBtn');
102
+ const accessKeyInput = document.getElementById('accessKey');
103
+ const secretKeyInput = document.getElementById('secretKey');
104
+ const endpointInput = document.getElementById('endpoint');
105
+ const bucketInput = document.getElementById('bucketName');
106
+ const connectBtn = document.getElementById('connectBtn');
107
+ const connectStatus = document.getElementById('connectStatus');
108
+
109
+ const fileInput = document.getElementById('fileInput');
110
+ const uploadBtn = document.getElementById('uploadBtn');
111
+ const uploadStatus = document.getElementById('uploadStatus');
112
+
113
+ const downloadPath = document.getElementById('downloadPath');
114
+ const downloadBtn = document.getElementById('downloadBtn');
115
+ const downloadStatus = document.getElementById('downloadStatus');
116
+
117
+ const listBtn = document.getElementById('listBtn');
118
+ const fileList = document.getElementById('fileList');
119
+
120
+ let awsClient = null;
121
+ let currentBucket = '';
122
+ let currentEndpoint = '';
123
+
124
+ // ── 工具函数 ──
125
+ function showStatus(el, msg, type = 'info') {
126
+ el.textContent = msg;
127
+ el.className = `status ${type}`;
128
+ }
129
+ function setButtonsEnabled(enabled) {
130
+ uploadBtn.disabled = !enabled;
131
+ downloadBtn.disabled = !enabled;
132
+ listBtn.disabled = !enabled;
133
+ }
134
+
135
+ // ── 一键加载预填密钥 ──
136
+ loadBtn.addEventListener('click', () => {
137
+ accessKeyInput.value = 'HFAK0z5hUE3yQ7h0lPHMMF4gITYxkMP';
138
+ secretKeyInput.value = '5a4d39a13b8fff06128fb4fa64c24258b1081b1437075ae16311de9f884b8271';
139
+ endpointInput.value = 'https://s3.hf.co';
140
+ bucketInput.value = 'WACATW/pan-storage';
141
+ showStatus(connectStatus, '✅ 密钥已全部加载!请点击“初始化连接”。', 'success');
142
+ });
143
+
144
+ // ── 初始化 S3 客户端 ──
145
+ connectBtn.addEventListener('click', () => {
146
+ const accessKey = accessKeyInput.value.trim();
147
+ const secretKey = secretKeyInput.value.trim();
148
+ let endpoint = endpointInput.value.trim();
149
+ const bucket = bucketInput.value.trim();
150
+
151
+ if (!accessKey || !secretKey || !endpoint || !bucket) {
152
+ showStatus(connectStatus, '请确保所有字段都已填写', 'error');
153
+ return;
154
+ }
155
+
156
+ // 检查 CDN 是否加载成功
157
+ if (typeof AwsClient === 'undefined') {
158
+ showStatus(connectStatus, '❌ 依赖库加载失败!请检查网络是否能访问 unpkg.com,或按下方说明下载本地库。', 'error');
159
+ return;
160
+ }
161
+
162
+ // 标准化端点 URL(去掉末尾斜杠)
163
+ endpoint = endpoint.replace(/\/+$/, '');
164
+ currentEndpoint = endpoint;
165
+
166
+ try {
167
+ awsClient = new AwsClient({
168
+ accessKeyId: accessKey,
169
+ secretAccessKey: secretKey,
170
+ service: 's3',
171
+ region: 'us-east-1'
172
+ });
173
+ currentBucket = bucket;
174
+
175
+ showStatus(connectStatus, `✅ 客户端初始化成功!存储桶: ${bucket}`, 'success');
176
+ setButtonsEnabled(true);
177
+ } catch (err) {
178
+ console.error(err);
179
+ showStatus(connectStatus, '❌ 初始化失败:' + err.message, 'error');
180
+ setButtonsEnabled(false);
181
+ }
182
+ });
183
+
184
+ // 组装完整的 URL
185
+ function buildUrl(path = '') {
186
+ let url = `${currentEndpoint}/${currentBucket}`;
187
+ if (path) url += `/${path}`;
188
+ return url;
189
+ }
190
+
191
+ // ── 上传文件 ──
192
+ uploadBtn.addEventListener('click', async () => {
193
+ const files = fileInput.files;
194
+ if (!files || files.length === 0) {
195
+ showStatus(uploadStatus, '请先选择文件', 'error');
196
+ return;
197
+ }
198
+ if (!awsClient) return;
199
+
200
+ uploadBtn.disabled = true;
201
+ let successCount = 0;
202
+
203
+ for (let i = 0; i < files.length; i++) {
204
+ const file = files[i];
205
+ showStatus(uploadStatus, `正在上传 (${i+1}/${files.length}): ${file.name}...`, 'info');
206
+ try {
207
+ const response = await awsClient.fetch(buildUrl(file.name), {
208
+ method: 'PUT',
209
+ body: file
210
+ });
211
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
212
+ successCount++;
213
+ } catch (err) {
214
+ console.error(err);
215
+ showStatus(uploadStatus, `❌ 上传 ${file.name} 失败: ${err.message}`, 'error');
216
+ uploadBtn.disabled = false;
217
+ return;
218
+ }
219
+ }
220
+
221
+ showStatus(uploadStatus, `✅ 成功上传 ${successCount} 个文件`, 'success');
222
+ fileInput.value = '';
223
+ uploadBtn.disabled = false;
224
+ });
225
+
226
+ // ── 下载文件 ──
227
+ downloadBtn.addEventListener('click', async () => {
228
+ const path = downloadPath.value.trim();
229
+ if (!path) {
230
+ showStatus(downloadStatus, '请输入文件路径', 'error');
231
+ return;
232
+ }
233
+ if (!awsClient) return;
234
+
235
+ downloadBtn.disabled = true;
236
+ showStatus(downloadStatus, `正在下载 ${path}...`, 'info');
237
+
238
+ try {
239
+ const response = await awsClient.fetch(buildUrl(path));
240
+
241
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
242
+
243
+ const blob = await response.blob();
244
+ const urlObj = URL.createObjectURL(blob);
245
+ const a = document.createElement('a');
246
+ a.href = urlObj;
247
+ a.download = path.split('/').pop();
248
+ document.body.appendChild(a);
249
+ a.click();
250
+ document.body.removeChild(a);
251
+ URL.revokeObjectURL(urlObj);
252
+
253
+ showStatus(downloadStatus, `✅ 已下载 ${path}`, 'success');
254
+ } catch (err) {
255
+ console.error(err);
256
+ showStatus(downloadStatus, `❌ 下载失败: ${err.message}`, 'error');
257
+ } finally {
258
+ downloadBtn.disabled = false;
259
+ }
260
+ });
261
+
262
+ // ── 列出文件 ──
263
+ listBtn.addEventListener('click', async () => {
264
+ if (!awsClient) return;
265
+ listBtn.disabled = true;
266
+ fileList.innerHTML = '';
267
+ showStatus(downloadStatus, '正在获取文件列表...', 'info');
268
+
269
+ try {
270
+ const response = await awsClient.fetch(buildUrl() + '?list-type=2');
271
+
272
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
273
+
274
+ const text = await response.text();
275
+ const parser = new DOMParser();
276
+ const xmlDoc = parser.parseFromString(text, 'text/xml');
277
+ const contents = xmlDoc.getElementsByTagName('Contents');
278
+
279
+ if (contents.length === 0) {
280
+ fileList.innerHTML = '<li style="color:#8b949e;">存储桶为空</li>';
281
+ } else {
282
+ for (let i = 0; i < contents.length; i++) {
283
+ const key = contents[i].getElementsByTagName('Key')[0]?.textContent || '未知文件';
284
+ const li = document.createElement('li');
285
+ li.innerHTML = `<span>${key}</span>`;
286
+ const btn = document.createElement('button');
287
+ btn.textContent = '⬇️';
288
+ btn.title = '下载此文件';
289
+ btn.addEventListener('click', () => {
290
+ downloadPath.value = key;
291
+ downloadBtn.click();
292
+ });
293
+ li.appendChild(btn);
294
+ fileList.appendChild(li);
295
+ }
296
+ }
297
+ showStatus(downloadStatus, `✅ 共 ${contents.length} 个文件`, 'success');
298
+ } catch (err) {
299
+ console.error(err);
300
+ showStatus(downloadStatus, `❌ 获取列表失败: ${err.message}`, 'error');
301
+ } finally {
302
+ listBtn.disabled = false;
303
+ }
304
+ });
305
+ </script>
306
+ </body>
307
+ </html>