seawolf2357 commited on
Commit
f7e8a67
ยท
verified ยท
1 Parent(s): 6bd9991

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +305 -743
app.py CHANGED
@@ -3,802 +3,364 @@ import requests
3
  import os
4
  import json
5
  from datetime import timedelta
 
 
 
 
 
 
6
 
7
  app = Flask(__name__)
8
  app.secret_key = os.urandom(24) # ์„ธ์…˜ ์•”ํ˜ธํ™”๋ฅผ ์œ„ํ•œ ๋น„๋ฐ€ ํ‚ค
9
  app.permanent_session_lifetime = timedelta(days=7) # ์„ธ์…˜ ์œ ์ง€ ๊ธฐ๊ฐ„ ์„ค์ •
10
 
11
- # ํ—ˆ๊น…ํŽ˜์ด์Šค URL ๋ชฉ๋ก
12
- HUGGINGFACE_URLS = [
13
- "https://huggingface.co/spaces/ginipick/Tech_Hangman_Game",
14
- "https://huggingface.co/spaces/openfree/deepseek_r1_API",
15
- "https://huggingface.co/spaces/ginipick/open_Deep-Research",
16
- "https://huggingface.co/spaces/aiqmaster/open-deep-research",
17
- "https://huggingface.co/spaces/seawolf2357/DeepSeek-R1-32b-search",
18
- "https://huggingface.co/spaces/ginigen/LLaDA",
19
- "https://huggingface.co/spaces/VIDraft/PHI4-Multimodal",
20
- "https://huggingface.co/spaces/ginigen/Ovis2-8B",
21
- "https://huggingface.co/spaces/ginigen/Graph-Mind",
22
- "https://huggingface.co/spaces/ginigen/Workflow-Canvas",
23
- "https://huggingface.co/spaces/ginigen/Design",
24
- "https://huggingface.co/spaces/ginigen/Diagram",
25
- "https://huggingface.co/spaces/ginigen/Mockup",
26
- "https://huggingface.co/spaces/ginigen/Infographic",
27
- "https://huggingface.co/spaces/ginigen/Flowchart",
28
- "https://huggingface.co/spaces/aiqcamp/FLUX-Vision",
29
- "https://huggingface.co/spaces/ginigen/VoiceClone-TTS",
30
- "https://huggingface.co/spaces/openfree/Perceptron-Network",
31
- "https://huggingface.co/spaces/openfree/Article-Generator",
32
- ]
33
-
34
- # URL์—์„œ ๋ชจ๋ธ/์ŠคํŽ˜์ด์Šค ์ •๋ณด ์ถ”์ถœ
35
- def extract_model_info(url):
36
- parts = url.split('/')
37
- if len(parts) < 6:
38
- return None
39
-
40
- if parts[3] == 'spaces' or parts[3] == 'models':
41
- return {
42
- 'type': parts[3],
43
- 'owner': parts[4],
44
- 'repo': parts[5],
45
- 'full_id': f"{parts[4]}/{parts[5]}"
46
- }
47
- elif len(parts) >= 5:
48
- # ๋‹ค๋ฅธ ํ˜•์‹์˜ URL
49
- return {
50
- 'type': 'models', # ๊ธฐ๋ณธ๊ฐ’
51
- 'owner': parts[3],
52
- 'repo': parts[4],
53
- 'full_id': f"{parts[3]}/{parts[4]}"
54
- }
55
-
56
- return None
57
-
58
- # URL์˜ ๋งˆ์ง€๋ง‰ ๋ถ€๋ถ„์„ ์ œ๋ชฉ์œผ๋กœ ์ถ”์ถœ
59
- def extract_title(url):
60
- parts = url.split("/")
61
- title = parts[-1] if parts else ""
62
- return title.replace("_", " ").replace("-", " ")
63
-
64
- # ํ—ˆ๊น…ํŽ˜์ด์Šค ์ธ์ฆ ํ™•์ธ - ์ˆ˜์ •๋œ ๋ฒ„์ „
65
- def validate_token(token):
66
- headers = {"Authorization": f"Bearer {token}"}
67
-
68
- # ์ˆ˜์ •: ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ whoami-v2 ์—”๋“œํฌ์ธํŠธ ๋จผ์ € ์‹œ๋„
69
- try:
70
- response = requests.get("https://huggingface.co/api/whoami-v2", headers=headers)
71
- if response.ok:
72
- return True, response.json()
73
- except Exception as e:
74
- print(f"whoami-v2 ํ† ํฐ ๊ฒ€์ฆ ์˜ค๋ฅ˜: {e}")
75
-
76
- # ๊ธฐ์กด whoami ์—”๋“œํฌ์ธํŠธ๋„ ์‹œ๋„
77
- try:
78
- response = requests.get("https://huggingface.co/api/whoami", headers=headers)
79
- if response.ok:
80
- return True, response.json()
81
- except Exception as e:
82
- print(f"whoami ํ† ํฐ ๊ฒ€์ฆ ์˜ค๋ฅ˜: {e}")
83
-
84
- return False, None
85
-
86
- # ์ข‹์•„์š” ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ
87
  def get_liked_repos(token):
88
  headers = {"Authorization": f"Bearer {token}"}
 
 
 
89
  endpoints = [
90
  "/api/me/likes",
91
  "/api/me/liked-repos",
92
  "/api/me/favorites"
93
  ]
94
 
95
- liked_models = {}
96
 
97
  for endpoint in endpoints:
 
 
98
  try:
99
- response = requests.get(f"https://huggingface.co{endpoint}", headers=headers)
 
 
 
 
 
100
  if response.ok:
101
  data = response.json()
 
 
 
102
  if isinstance(data, list):
103
- for model in data:
 
 
 
 
104
  if isinstance(model, dict):
 
105
  if 'owner' in model and 'name' in model:
106
- liked_models[f"{model['owner']}/{model['name']}"] = True
 
 
107
  elif 'id' in model:
108
  liked_models[model['id']] = True
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  elif isinstance(data, dict):
110
- for key in data:
111
- liked_models[key] = True
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
- print(f"์ข‹์•„์š” ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ ์„ฑ๊ณต (์—”๋“œํฌ์ธํŠธ: {endpoint})")
114
- return liked_models
 
 
 
115
  except Exception as e:
116
- print(f"์ข‹์•„์š” ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ ์˜ค๋ฅ˜ (์—”๋“œํฌ์ธํŠธ: {endpoint}): {e}")
117
 
118
- return liked_models
119
-
120
- # ์ข‹์•„์š” ํ† ๊ธ€ API ํ˜ธ์ถœ
121
- def toggle_like(token, type_name, owner, repo, is_liked):
122
- headers = {"Authorization": f"Bearer {token}"}
123
- normalized_type = "spaces" if type_name == "spaces" else "models"
124
- method = "DELETE" if is_liked else "POST"
125
- url = f"https://huggingface.co/api/{normalized_type}/{owner}/{repo}/like"
126
 
127
  try:
128
- if method == "DELETE":
129
- response = requests.delete(url, headers=headers)
130
- else:
131
- response = requests.post(url, headers=headers)
132
 
133
- return response.ok, response.status_code
 
 
134
  except Exception as e:
135
- print(f"์ข‹์•„์š” ํ† ๊ธ€ ์˜ค๋ฅ˜: {e}")
136
- return False, 500
137
-
138
- # ํ™ˆํŽ˜์ด์ง€ ๋ผ์šฐํŠธ
139
- @app.route('/')
140
- def home():
141
- return render_template('index.html')
142
-
143
- # ๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌ API
144
- @app.route('/api/login', methods=['POST'])
145
- def login():
146
- token = request.form.get('token', '')
147
-
148
- if not token:
149
- return jsonify({'success': False, 'message': 'ํ† ํฐ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.'})
150
-
151
- is_valid, user_info = validate_token(token)
152
-
153
- if not is_valid or not user_info:
154
- return jsonify({'success': False, 'message': '์œ ํšจํ•˜์ง€ ์•Š์€ ํ† ํฐ์ž…๋‹ˆ๋‹ค.'})
155
-
156
- # ์‚ฌ์šฉ์ž ์ด๋ฆ„ ์ฐพ๊ธฐ
157
- username = None
158
- if 'name' in user_info:
159
- username = user_info['name']
160
- elif 'user' in user_info and 'username' in user_info['user']:
161
- username = user_info['user']['username']
162
- elif 'username' in user_info:
163
- username = user_info['username']
164
- else:
165
- username = '์ธ์ฆ๋œ ์‚ฌ์šฉ์ž'
166
-
167
- # ์ข‹์•„์š” ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ
168
- liked_models = get_liked_repos(token)
169
-
170
- # ์„ธ์…˜์— ์ €์žฅ
171
- session['token'] = token
172
- session['username'] = username
173
- session['liked_models'] = liked_models
174
-
175
- return jsonify({
176
- 'success': True,
177
- 'username': username,
178
- 'liked_models': liked_models
179
- })
180
-
181
- # ๋กœ๊ทธ์•„์›ƒ ์ฒ˜๋ฆฌ API
182
- @app.route('/api/logout', methods=['POST'])
183
- def logout():
184
- session.pop('token', None)
185
- session.pop('username', None)
186
- session.pop('liked_models', None)
187
- return jsonify({'success': True})
188
-
189
- # URL ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ API
190
- @app.route('/api/urls', methods=['GET'])
191
- def get_urls():
192
- search_query = request.args.get('search', '').lower()
193
- show_only_liked = request.args.get('liked', 'false').lower() == 'true'
194
 
195
- liked_models = session.get('liked_models', {})
196
-
197
- results = []
198
- for url in HUGGINGFACE_URLS:
199
- title = extract_title(url)
200
- model_info = extract_model_info(url)
201
-
202
- if not model_info:
203
- continue
204
 
205
- is_liked = model_info['full_id'] in liked_models
 
206
 
207
- # ํ•„ํ„ฐ๋ง ์ ์šฉ
208
- if show_only_liked and not is_liked:
209
- continue
 
 
 
 
 
 
 
210
 
211
- if search_query and search_query not in url.lower() and search_query not in title.lower():
212
- continue
213
-
214
- results.append({
215
- 'url': url,
216
- 'title': title,
217
- 'model_info': model_info,
218
- 'is_liked': is_liked
219
- })
220
 
221
- return jsonify(results)
 
 
222
 
223
- # ์ข‹์•„์š” ํ† ๊ธ€ API
224
- @app.route('/api/toggle-like', methods=['POST'])
225
- def api_toggle_like():
226
- if 'token' not in session:
227
- return jsonify({'success': False, 'message': '๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.'})
228
-
229
- data = request.json
230
- url = data.get('url')
231
- current_liked = data.get('currentLiked', False)
232
-
233
- if not url:
234
- return jsonify({'success': False, 'message': 'URL์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.'})
235
-
236
- model_info = extract_model_info(url)
237
- if not model_info:
238
- return jsonify({'success': False, 'message': '์œ ํšจํ•˜์ง€ ์•Š์€ URL์ž…๋‹ˆ๋‹ค.'})
239
-
240
- success, status_code = toggle_like(
241
- session['token'],
242
- model_info['type'],
243
- model_info['owner'],
244
- model_info['repo'],
245
- current_liked
246
- )
247
-
248
- liked_models = session.get('liked_models', {})
249
-
250
- if success:
251
- # ์„ธ์…˜์˜ ์ข‹์•„์š” ์ƒํƒœ ์—…๋ฐ์ดํŠธ
252
- if current_liked:
253
- if model_info['full_id'] in liked_models:
254
- del liked_models[model_info['full_id']]
255
- else:
256
- liked_models[model_info['full_id']] = True
257
 
258
- session['liked_models'] = liked_models
 
259
 
260
- return jsonify({
261
- 'success': True,
262
- 'new_state': not current_liked,
263
- 'message': f"{'์ข‹์•„์š” ์ทจ์†Œ' if current_liked else '์ข‹์•„์š”'} ์„ฑ๊ณต"
264
- })
265
- else:
266
- return jsonify({
267
- 'success': False,
268
- 'message': f"API ์˜ค๋ฅ˜ (์ƒํƒœ ์ฝ”๋“œ: {status_code})"
269
- })
270
-
271
- # ์„ธ์…˜ ์ƒํƒœ ํ™•์ธ API
272
- @app.route('/api/session-status', methods=['GET'])
273
- def session_status():
274
- return jsonify({
275
- 'logged_in': 'token' in session,
276
- 'username': session.get('username'),
277
- 'liked_count': len(session.get('liked_models', {}))
278
- })
279
-
280
- if __name__ == '__main__':
281
- # templates ํด๋” ์ƒ์„ฑ
282
- os.makedirs('templates', exist_ok=True)
283
-
284
- # index.html ํŒŒ์ผ ์ƒ์„ฑ
285
- with open('templates/index.html', 'w', encoding='utf-8') as f:
286
- f.write('''
287
- <!DOCTYPE html>
288
- <html lang="ko">
289
- <head>
290
- <meta charset="UTF-8">
291
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
292
- <title>Hugging Face URL ์นด๋“œ ๋ฆฌ์ŠคํŠธ</title>
293
- <style>
294
- body {
295
- font-family: Arial, sans-serif;
296
- line-height: 1.6;
297
- margin: 0;
298
- padding: 0;
299
- color: #333;
300
- }
301
-
302
- .container {
303
- max-width: 1200px;
304
- margin: 0 auto;
305
- padding: 1rem;
306
- }
307
-
308
- .header {
309
- background-color: #f0f0f0;
310
- padding: 1rem;
311
- border-radius: 5px;
312
- margin-bottom: 1rem;
313
- }
314
-
315
- .user-controls {
316
- display: flex;
317
- justify-content: space-between;
318
- align-items: center;
319
- flex-wrap: wrap;
320
- }
321
-
322
- .filter-controls {
323
- background-color: #f8f9fa;
324
- padding: 1rem;
325
- border-radius: 5px;
326
- margin-bottom: 1rem;
327
- }
328
-
329
- input[type="password"],
330
- input[type="text"] {
331
- padding: 0.5rem;
332
- border: 1px solid #ccc;
333
- border-radius: 4px;
334
- margin-right: 5px;
335
- }
336
-
337
- button {
338
- padding: 0.5rem 1rem;
339
- background-color: #4CAF50;
340
- color: white;
341
- border: none;
342
- border-radius: 4px;
343
- cursor: pointer;
344
- }
345
-
346
- button:hover {
347
- background-color: #45a049;
348
- }
349
-
350
- button.logout {
351
- background-color: #f44336;
352
- }
353
-
354
- button.logout:hover {
355
- background-color: #d32f2f;
356
- }
357
-
358
- .token-help {
359
- margin-top: 0.5rem;
360
- font-size: 0.8rem;
361
- color: #666;
362
- }
363
-
364
- .token-help a {
365
- color: #4CAF50;
366
- text-decoration: none;
367
- }
368
-
369
- .token-help a:hover {
370
- text-decoration: underline;
371
- }
372
-
373
- .cards-container {
374
- display: flex;
375
- flex-wrap: wrap;
376
- gap: 1rem;
377
- }
378
-
379
- .card {
380
- border: 1px solid #ccc;
381
- border-radius: 5px;
382
- padding: 1rem;
383
- width: 300px;
384
- box-shadow: 2px 2px 8px rgba(0,0,0,0.1);
385
- position: relative;
386
- background-color: #f9f9f9;
387
- }
388
-
389
- .card a {
390
- text-decoration: none;
391
- color: #333;
392
- word-break: break-all;
393
- }
394
-
395
- .like-button {
396
- position: absolute;
397
- top: 1rem;
398
- right: 1rem;
399
- border: none;
400
- background: transparent;
401
- font-size: 1.5rem;
402
- cursor: pointer;
403
- transition: color 0.2s;
404
- }
405
-
406
- .like-button.liked {
407
- color: red;
408
- }
409
-
410
- .like-button.not-liked {
411
- color: white;
412
- -webkit-text-stroke: 1px #333;
413
- }
414
-
415
- .status-message {
416
- padding: 1rem;
417
- border-radius: 4px;
418
- margin-bottom: 1rem;
419
- display: none;
420
- }
421
-
422
- .success {
423
- background-color: #dff0d8;
424
- color: #3c763d;
425
- }
426
-
427
- .error {
428
- background-color: #f2dede;
429
- color: #a94442;
430
- }
431
-
432
- .loading {
433
- position: fixed;
434
- top: 0;
435
- left: 0;
436
- right: 0;
437
- bottom: 0;
438
- background-color: rgba(255, 255, 255, 0.7);
439
- display: none;
440
- justify-content: center;
441
- align-items: center;
442
- z-index: 1000;
443
- font-size: 1.5rem;
444
- }
445
-
446
- .login-section {
447
- margin-top: 1rem;
448
- }
449
-
450
- .logged-in-section {
451
- display: none;
452
- margin-top: 1rem;
453
- }
454
-
455
- @media (max-width: 768px) {
456
- .user-controls {
457
- flex-direction: column;
458
- align-items: flex-start;
459
- }
460
-
461
- .user-controls > div {
462
- margin-bottom: 1rem;
463
- }
464
-
465
- .card {
466
- width: 100%;
467
- }
468
- }
469
- </style>
470
- </head>
471
- <body>
472
- <div class="container">
473
- <div class="header">
474
- <div class="user-controls">
475
- <div>
476
- <span>ํ—ˆ๊น…ํŽ˜์ด์Šค ๊ณ„์ •: </span>
477
- <span id="currentUser">๋กœ๊ทธ์ธ๋˜์ง€ ์•Š์Œ</span>
478
- </div>
479
-
480
- <div id="loginSection" class="login-section">
481
- <input type="password" id="tokenInput" placeholder="ํ—ˆ๊น…ํŽ˜์ด์Šค API ํ† ํฐ ์ž…๋ ฅ" />
482
- <button id="loginButton">์ธ์ฆํ•˜๊ธฐ</button>
483
- <div class="token-help">
484
- API ํ† ํฐ์€ <a href="https://huggingface.co/settings/tokens" target="_blank">ํ—ˆ๊น…ํŽ˜์ด์Šค ํ† ํฐ ํŽ˜์ด์ง€</a>์—์„œ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
485
- </div>
486
- </div>
487
-
488
- <div id="loggedInSection" class="logged-in-section">
489
- <button id="logoutButton" class="logout">๋กœ๊ทธ์•„์›ƒ</button>
490
- </div>
491
- </div>
492
- </div>
493
-
494
- <div class="filter-controls">
495
- <label>
496
- <input type="checkbox" id="showOnlyLiked" />
497
- ๋‚ด๊ฐ€ ์ข‹์•„์š”ํ•œ URL๋งŒ ๋ณด๊ธฐ
498
- </label>
499
- <label style="margin-left: 1rem;">
500
- <input type="text" id="searchInput" placeholder="URL ๋˜๋Š” ์ œ๋ชฉ์œผ๋กœ ๊ฒ€์ƒ‰" style="width: 250px;" />
501
- </label>
502
- </div>
503
-
504
- <div id="statusMessage" class="status-message"></div>
505
-
506
- <div id="loadingIndicator" class="loading">์ฒ˜๋ฆฌ ์ค‘...</div>
507
-
508
- <div id="cardsContainer" class="cards-container"></div>
509
- </div>
510
-
511
- <script>
512
- // DOM ์š”์†Œ ์ฐธ์กฐ
513
- const elements = {
514
- tokenInput: document.getElementById('tokenInput'),
515
- loginButton: document.getElementById('loginButton'),
516
- logoutButton: document.getElementById('logoutButton'),
517
- currentUser: document.getElementById('currentUser'),
518
- cardsContainer: document.getElementById('cardsContainer'),
519
- loadingIndicator: document.getElementById('loadingIndicator'),
520
- statusMessage: document.getElementById('statusMessage'),
521
- showOnlyLiked: document.getElementById('showOnlyLiked'),
522
- searchInput: document.getElementById('searchInput'),
523
- loginSection: document.getElementById('loginSection'),
524
- loggedInSection: document.getElementById('loggedInSection')
525
- };
526
-
527
- // ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ƒํƒœ
528
- const state = {
529
- username: null,
530
- likedModels: {},
531
- isLoading: false
532
- };
533
-
534
- // ๋กœ๋”ฉ ์ƒํƒœ ํ‘œ์‹œ ํ•จ์ˆ˜
535
- function setLoading(isLoading) {
536
- state.isLoading = isLoading;
537
- elements.loadingIndicator.style.display = isLoading ? 'flex' : 'none';
538
- }
539
-
540
- // ์ƒํƒœ ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ ํ•จ์ˆ˜
541
- function showMessage(message, isError = false) {
542
- elements.statusMessage.textContent = message;
543
- elements.statusMessage.className = `status-message ${isError ? 'error' : 'success'}`;
544
- elements.statusMessage.style.display = 'block';
545
-
546
- // 3์ดˆ ํ›„ ๋ฉ”์‹œ์ง€ ์‚ฌ๋ผ์ง
547
- setTimeout(() => {
548
- elements.statusMessage.style.display = 'none';
549
- }, 3000);
550
- }
551
-
552
- // API ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜
553
- async function handleApiResponse(response) {
554
- if (!response.ok) {
555
- const errorText = await response.text();
556
- throw new Error(`API ์˜ค๋ฅ˜ (${response.status}): ${errorText}`);
557
- }
558
- return response.json();
559
- }
560
-
561
- // ์„ธ์…˜ ์ƒํƒœ ํ™•์ธ
562
- async function checkSessionStatus() {
563
- try {
564
- const response = await fetch('/api/session-status');
565
- const data = await handleApiResponse(response);
566
 
567
- if (data.logged_in) {
568
- state.username = data.username;
569
- elements.currentUser.textContent = data.username;
570
- elements.loginSection.style.display = 'none';
571
- elements.loggedInSection.style.display = 'block';
572
-
573
- // URL ๋ชฉ๋ก ๋กœ๋“œ
574
- loadUrls();
575
  }
576
- } catch (error) {
577
- console.error('์„ธ์…˜ ์ƒํƒœ ํ™•์ธ ์˜ค๋ฅ˜:', error);
578
- }
579
- }
580
-
581
- // ๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌ
582
- async function login(token) {
583
- if (!token.trim()) {
584
- showMessage('ํ† ํฐ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.', true);
585
- return;
586
- }
587
-
588
- setLoading(true);
589
-
590
- try {
591
- const formData = new FormData();
592
- formData.append('token', token);
593
-
594
- const response = await fetch('/api/login', {
595
- method: 'POST',
596
- body: formData
597
- });
598
 
599
- const data = await handleApiResponse(response);
600
-
601
- if (data.success) {
602
- state.username = data.username;
603
- state.likedModels = data.liked_models || {};
604
-
605
- elements.currentUser.textContent = state.username;
606
- elements.loginSection.style.display = 'none';
607
- elements.loggedInSection.style.display = 'block';
608
-
609
- showMessage(`${state.username}๋‹˜์œผ๋กœ ๋กœ๊ทธ์ธ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.`);
610
-
611
- // URL ๋ชฉ๋ก ๋กœ๋“œ
612
- loadUrls();
613
- } else {
614
- showMessage(data.message || '๋กœ๊ทธ์ธ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.', true);
615
- }
616
- } catch (error) {
617
- console.error('๋กœ๊ทธ์ธ ์˜ค๋ฅ˜:', error);
618
- showMessage(`๋กœ๊ทธ์ธ ์˜ค๋ฅ˜: ${error.message}`, true);
619
- } finally {
620
- setLoading(false);
621
- }
622
- }
623
 
624
- // ๋กœ๊ทธ์•„์›ƒ ์ฒ˜๋ฆฌ
625
- async function logout() {
626
- setLoading(true);
627
-
628
- try {
629
- const response = await fetch('/api/logout', {
630
- method: 'POST'
631
- });
632
 
633
- const data = await handleApiResponse(response);
 
 
 
 
634
 
635
- if (data.success) {
636
- state.username = null;
637
- state.likedModels = {};
638
-
639
- elements.currentUser.textContent = '๋กœ๊ทธ์ธ๋˜์ง€ ์•Š์Œ';
640
- elements.tokenInput.value = '';
641
- elements.loginSection.style.display = 'block';
642
- elements.loggedInSection.style.display = 'none';
643
-
644
- showMessage('๋กœ๊ทธ์•„์›ƒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.');
645
-
646
- // ์นด๋“œ ์ดˆ๊ธฐํ™”
647
- elements.cardsContainer.innerHTML = '';
648
- }
649
- } catch (error) {
650
- console.error('๋กœ๊ทธ์•„์›ƒ ์˜ค๋ฅ˜:', error);
651
- showMessage(`๋กœ๊ทธ์•„์›ƒ ์˜ค๋ฅ˜: ${error.message}`, true);
652
- } finally {
653
- setLoading(false);
654
- }
655
- }
656
-
657
- // URL ๋ชฉ๋ก ๋กœ๋“œ
658
- async function loadUrls() {
659
- setLoading(true);
660
-
661
- try {
662
- const showOnlyLiked = elements.showOnlyLiked.checked;
663
- const searchText = elements.searchInput.value;
664
 
665
- const response = await fetch(`/api/urls?liked=${showOnlyLiked}&search=${encodeURIComponent(searchText)}`);
666
- const urls = await handleApiResponse(response);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
667
 
668
- renderCards(urls);
669
- } catch (error) {
670
- console.error('URL ๋ชฉ๋ก ๋กœ๋“œ ์˜ค๋ฅ˜:', error);
671
- showMessage(`URL ๋กœ๋“œ ์˜ค๋ฅ˜: ${error.message}`, true);
672
- } finally {
673
- setLoading(false);
674
- }
675
- }
676
-
677
- // ์ข‹์•„์š” ํ† ๊ธ€
678
- async function toggleLike(url, button, currentLiked) {
679
- if (!state.username) {
680
- showMessage('์ข‹์•„์š”๋ฅผ ํ•˜๋ ค๋ฉด ํ—ˆ๊น…ํŽ˜์ด์Šค API ํ† ํฐ์œผ๋กœ ์ธ์ฆ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.', true);
681
- return;
682
- }
683
-
684
- setLoading(true);
685
-
686
- try {
687
- const response = await fetch('/api/toggle-like', {
688
- method: 'POST',
689
- headers: {
690
- 'Content-Type': 'application/json'
691
- },
692
- body: JSON.stringify({
693
- url: url,
694
- currentLiked: currentLiked
695
- })
696
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
697
 
698
- const data = await handleApiResponse(response);
 
699
 
700
- if (data.success) {
701
- if (currentLiked) {
702
- button.classList.remove('liked');
703
- button.classList.add('not-liked');
704
- } else {
705
- button.classList.add('liked');
706
- button.classList.remove('not-liked');
707
- }
708
-
709
- showMessage(data.message);
710
-
711
- // ํ•„ํ„ฐ๋ง๋œ ๊ฒฝ์šฐ ๋ชฉ๋ก ๋‹ค์‹œ ๋กœ๋“œ
712
- if (elements.showOnlyLiked.checked) {
713
- loadUrls();
714
- }
715
- } else {
716
- showMessage(data.message, true);
717
- }
718
- } catch (error) {
719
- console.error('์ข‹์•„์š” ํ† ๊ธ€ ์˜ค๋ฅ˜:', error);
720
- showMessage(`์ข‹์•„์š” ์ฒ˜๋ฆฌ ์˜ค๋ฅ˜: ${error.message}`, true);
721
- } finally {
722
- setLoading(false);
723
- }
724
- }
725
-
726
- // ์นด๋“œ ๋ Œ๋”๋ง
727
- function renderCards(urls) {
728
- elements.cardsContainer.innerHTML = '';
729
-
730
- if (!urls || urls.length === 0) {
731
- const noResultsMsg = document.createElement('p');
732
- noResultsMsg.textContent = 'ํ‘œ์‹œํ•  URL์ด ์—†์Šต๋‹ˆ๋‹ค.';
733
- noResultsMsg.style.padding = '1rem';
734
- noResultsMsg.style.fontStyle = 'italic';
735
- elements.cardsContainer.appendChild(noResultsMsg);
736
- return;
737
- }
738
-
739
- urls.forEach(item => {
740
- const { url, title, is_liked } = item;
741
 
742
- // ์นด๋“œ ์ƒ์„ฑ
743
- const card = document.createElement('div');
744
- card.className = 'card';
 
 
 
 
 
 
 
 
 
745
 
746
- // ์ œ๋ชฉ
747
- const titleEl = document.createElement('h3');
748
- titleEl.textContent = title;
749
- card.appendChild(titleEl);
750
 
751
- // URL ๋งํฌ
752
- const linkEl = document.createElement('a');
753
- linkEl.href = url;
754
- linkEl.textContent = url;
755
- linkEl.target = '_blank';
756
- card.appendChild(linkEl);
 
 
 
 
757
 
758
- // ์ข‹์•„์š” ๋ฒ„ํŠผ (โ™ฅ ์•„์ด์ฝ˜)
759
- const likeBtn = document.createElement('button');
760
- likeBtn.className = `like-button ${is_liked ? 'liked' : 'not-liked'}`;
761
- likeBtn.textContent = 'โ™ฅ';
762
 
763
- likeBtn.addEventListener('click', function(e) {
764
- e.preventDefault();
765
- toggleLike(url, likeBtn, is_liked);
766
- });
767
- card.appendChild(likeBtn);
768
 
769
- // ์นด๋“œ ์ถ”๊ฐ€
770
- elements.cardsContainer.appendChild(card);
771
- });
772
- }
773
-
774
- // ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ์„ค์ •
775
- elements.loginButton.addEventListener('click', () => {
776
- login(elements.tokenInput.value);
777
- });
778
-
779
- elements.logoutButton.addEventListener('click', logout);
780
-
781
- // ์—”ํ„ฐ ํ‚ค๋กœ ๋กœ๊ทธ์ธ ๊ฐ€๋Šฅํ•˜๊ฒŒ
782
- elements.tokenInput.addEventListener('keypress', (event) => {
783
- if (event.key === 'Enter') {
784
- login(elements.tokenInput.value);
785
- }
786
- });
787
-
788
- // ํ•„ํ„ฐ๋ง ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ
789
- elements.showOnlyLiked.addEventListener('change', loadUrls);
790
- elements.searchInput.addEventListener('input', () => {
791
- // ์ž…๋ ฅ ์ง€์—ฐ ์ฒ˜๋ฆฌ (ํƒ€์ดํ•‘ํ•  ๋•Œ๋งˆ๋‹ค API ํ˜ธ์ถœ ๋ฐฉ์ง€)
792
- clearTimeout(state.searchTimeout);
793
- state.searchTimeout = setTimeout(loadUrls, 300);
794
- });
795
-
796
- // ์ดˆ๊ธฐํ™”
797
- checkSessionStatus();
798
- </script>
799
- </body>
800
- </html>
801
- ''')
802
 
803
- # ํ—ˆ๊น…ํŽ˜์ด์Šค ์ŠคํŽ˜์ด์Šค์—์„œ๋Š” 7860 ํฌํŠธ ์‚ฌ์šฉ
804
- app.run(host='0.0.0.0', port=7860)
 
 
3
  import os
4
  import json
5
  from datetime import timedelta
6
+ import logging
7
+
8
+ # ๋กœ๊น… ์„ค์ •
9
+ logging.basicConfig(level=logging.DEBUG,
10
+ format='%(asctime)s - %(levelname)s - %(message)s')
11
+ logger = logging.getLogger(__name__)
12
 
13
  app = Flask(__name__)
14
  app.secret_key = os.urandom(24) # ์„ธ์…˜ ์•”ํ˜ธํ™”๋ฅผ ์œ„ํ•œ ๋น„๋ฐ€ ํ‚ค
15
  app.permanent_session_lifetime = timedelta(days=7) # ์„ธ์…˜ ์œ ์ง€ ๊ธฐ๊ฐ„ ์„ค์ •
16
 
17
+ # ์ข‹์•„์š” ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ ํ•จ์ˆ˜ - ๋””๋ฒ„๊น… ๊ฐœ์„ 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  def get_liked_repos(token):
19
  headers = {"Authorization": f"Bearer {token}"}
20
+ liked_models = {}
21
+
22
+ # 1. API์— ์ง์ ‘ ์งˆ์˜
23
  endpoints = [
24
  "/api/me/likes",
25
  "/api/me/liked-repos",
26
  "/api/me/favorites"
27
  ]
28
 
29
+ logger.debug(f"ํ† ํฐ์œผ๋กœ ์ข‹์•„์š” ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ ์‹œ๋„ (ํ† ํฐ ์ผ๋ถ€: {token[:4]}...)")
30
 
31
  for endpoint in endpoints:
32
+ logger.debug(f"์—”๋“œํฌ์ธํŠธ ์‹œ๋„: {endpoint}")
33
+
34
  try:
35
+ full_url = f"https://huggingface.co{endpoint}"
36
+ logger.debug(f"API ํ˜ธ์ถœ: {full_url}")
37
+
38
+ response = requests.get(full_url, headers=headers)
39
+ logger.debug(f"์‘๋‹ต ์ƒํƒœ: {response.status_code}")
40
+
41
  if response.ok:
42
  data = response.json()
43
+ logger.debug(f"์‘๋‹ต ๋ฐ์ดํ„ฐ ํƒ€์ž…: {type(data)}")
44
+ logger.debug(f"๋ฐ์ดํ„ฐ ์ƒ˜ํ”Œ: {str(data)[:200]}")
45
+
46
  if isinstance(data, list):
47
+ logger.debug(f"๋ชฉ๋ก ๊ธธ์ด: {len(data)}")
48
+
49
+ for i, model in enumerate(data[:5]): # ์ฒ˜์Œ 5๊ฐœ๋งŒ ๋กœ๊น…
50
+ logger.debug(f"๋ชจ๋ธ {i}: {model}")
51
+
52
  if isinstance(model, dict):
53
+ # ๋‹ค์–‘ํ•œ API ์‘๋‹ต ๊ตฌ์กฐ ์ฒ˜๋ฆฌ
54
  if 'owner' in model and 'name' in model:
55
+ model_id = f"{model['owner']}/{model['name']}"
56
+ liked_models[model_id] = True
57
+ logger.debug(f"์ถ”๊ฐ€๋œ ๋ชจ๋ธ ID: {model_id}")
58
  elif 'id' in model:
59
  liked_models[model['id']] = True
60
+ logger.debug(f"์ถ”๊ฐ€๋œ ๋ชจ๋ธ ID: {model['id']}")
61
+ elif 'modelId' in model:
62
+ liked_models[model['modelId']] = True
63
+ logger.debug(f"์ถ”๊ฐ€๋œ ๋ชจ๋ธ ID: {model['modelId']}")
64
+ else:
65
+ # ๋‹ค๋ฅธ ํ•„๋“œ ํ™•์ธ
66
+ logger.debug(f"๋ชจ๋ธ ํ‚ค: {list(model.keys())}")
67
+
68
+ # ์ง์ ‘ owner/repo ํ˜•์‹ ์ถ”์ถœ ์‹œ๋„
69
+ if 'repo' in model and 'rowner' in model:
70
+ model_id = f"{model['rowner']}/{model['repo']}"
71
+ liked_models[model_id] = True
72
+ logger.debug(f"์ถ”๊ฐ€๋œ ๋ชจ๋ธ ID: {model_id}")
73
  elif isinstance(data, dict):
74
+ logger.debug(f"๊ฐ์ฒด ํ‚ค: {list(data.keys())}")
75
+
76
+ # ๊ฐ์ฒด ๊ตฌ์กฐ์— ๋”ฐ๋ผ ์ฒ˜๋ฆฌ
77
+ if 'models' in data and isinstance(data['models'], list):
78
+ for model in data['models']:
79
+ if isinstance(model, dict) and 'id' in model:
80
+ liked_models[model['id']] = True
81
+ logger.debug(f"์ถ”๊ฐ€๋œ ๋ชจ๋ธ ID: {model['id']}")
82
+ else:
83
+ # ๋‹จ์ˆœ ํ‚ค-๊ฐ’ ๊ตฌ์กฐ๋ผ๋ฉด
84
+ for key in data:
85
+ liked_models[key] = True
86
+ logger.debug(f"์ถ”๊ฐ€๋œ ํ‚ค: {key}")
87
+
88
+ logger.debug(f"์ด ์ข‹์•„์š” ํ•ญ๋ชฉ ์ˆ˜: {len(liked_models)}")
89
 
90
+ if len(liked_models) > 0:
91
+ logger.debug("์ข‹์•„์š” ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ ์„ฑ๊ณต!")
92
+ return liked_models
93
+ else:
94
+ logger.debug(f"API ์˜ค๋ฅ˜: {response.text}")
95
  except Exception as e:
96
+ logger.error(f"์—”๋“œํฌ์ธํŠธ {endpoint} ์˜ค๋ฅ˜: {str(e)}")
97
 
98
+ # 2. ์ˆ˜๋™ ์ŠคํŽ˜์ด์Šค/๋ชจ๋ธ ํ™•์ธ (API๊ฐ€ ์‹คํŒจํ•œ ๊ฒฝ์šฐ)
99
+ logger.debug("API ์กฐํšŒ ์‹คํŒจ, ์ˆ˜๋™ ์ŠคํŽ˜์ด์Šค/๋ชจ๋ธ ํ™•์ธ ์‹œ๋„")
 
 
 
 
 
 
100
 
101
  try:
102
+ # "me" ํŽ˜์ด์ง€ ์ ‘๊ทผํ•ด์„œ HTML์—์„œ ์ข‹์•„์š” ์ •๋ณด ์ฐพ๊ธฐ
103
+ response = requests.get("https://huggingface.co/me/likes", headers=headers)
 
 
104
 
105
+ if response.ok:
106
+ logger.debug("์ข‹์•„์š” ํŽ˜์ด์ง€ ๊ฐ€์ ธ์˜ค๊ธฐ ์„ฑ๊ณต")
107
+ # ์—ฌ๊ธฐ์„œ HTML ํŒŒ์‹ฑํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ ๋ณต์žกํ•˜๋ฏ€๋กœ ์ƒ๋žต
108
  except Exception as e:
109
+ logger.error(f"์ˆ˜๋™ ํ™•์ธ ์˜ค๋ฅ˜: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
 
111
+ # 3. API ๊ฒฐ๊ณผ ํ™•์ธ ํŽ˜์ด์ง€ ์ถ”๊ฐ€ (๋””๋ฒ„๊น…์šฉ)
112
+ @app.route('/api/debug-likes', methods=['GET'])
113
+ def debug_likes():
114
+ if 'token' not in session:
115
+ return jsonify({'success': False, 'message': '๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.'})
 
 
 
 
116
 
117
+ headers = {"Authorization": f"Bearer {session['token']}"}
118
+ results = {}
119
 
120
+ for endpoint in ["/api/me/likes", "/api/me/liked-repos", "/api/me/favorites"]:
121
+ try:
122
+ response = requests.get(f"https://huggingface.co{endpoint}", headers=headers)
123
+ results[endpoint] = {
124
+ 'status': response.status_code,
125
+ 'ok': response.ok,
126
+ 'data': response.json() if response.ok else None
127
+ }
128
+ except Exception as e:
129
+ results[endpoint] = {'error': str(e)}
130
 
131
+ return jsonify(results)
 
 
 
 
 
 
 
 
132
 
133
+ # ์ข‹์•„์š” ์ƒํƒœ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ ๋นˆ ๊ฐ์ฒด ๋ฐ˜ํ™˜
134
+ logger.debug("์ข‹์•„์š” ๋ชฉ๋ก ์—†์Œ, ๋นˆ ๊ฐ์ฒด ๋ฐ˜ํ™˜")
135
+ return liked_models
136
 
137
+ # ๋ฉ”์ธ ์•ฑ์šฉ ํ”Œ๋ž˜๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•ด ๋””๋ฒ„๊น… ์—”๋“œํฌ์ธํŠธ ์ถ”๊ฐ€
138
+ def add_debug_endpoints(app):
139
+ @app.route('/api/debug-likes', methods=['GET'])
140
+ def debug_likes():
141
+ if 'token' not in session:
142
+ return jsonify({'success': False, 'message': '๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.'})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
 
144
+ headers = {"Authorization": f"Bearer {session['token']}"}
145
+ results = {}
146
 
147
+ for endpoint in ["/api/me/likes", "/api/me/liked-repos", "/api/me/favorites"]:
148
+ try:
149
+ response = requests.get(f"https://huggingface.co{endpoint}", headers=headers)
150
+ results[endpoint] = {
151
+ 'status': response.status_code,
152
+ 'ok': response.ok
153
+ }
154
+
155
+ # ์„ฑ๊ณตํ•œ ๊ฒฝ์šฐ์—๋งŒ ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€
156
+ if response.ok:
157
+ try:
158
+ results[endpoint]['data'] = response.json()
159
+ except Exception as e:
160
+ results[endpoint]['parse_error'] = str(e)
161
+ except Exception as e:
162
+ results[endpoint] = {'error': str(e)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
 
164
+ # ํ˜„์žฌ ์„ธ์…˜์— ์ €์žฅ๋œ ์ข‹์•„์š” ๋ชฉ๋ก ๋ฐ˜ํ™˜
165
+ results['current_session_likes'] = {
166
+ 'count': len(session.get('liked_models', {})),
167
+ 'items': list(session.get('liked_models', {}).keys())
 
 
 
 
168
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
 
170
+ return jsonify(results)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
 
172
+ @app.route('/api/debug-urls', methods=['GET'])
173
+ def debug_urls():
174
+ # ๋ชจ๋ธ ์ •๋ณด ์ถ”์ถœ ๋””๋ฒ„๊น…
175
+ from urllib.parse import urlparse
 
 
 
 
176
 
177
+ test_urls = [
178
+ "https://huggingface.co/spaces/ginipick/Tech_Hangman_Game",
179
+ "https://huggingface.co/models/meta/llama-2",
180
+ "https://huggingface.co/meta/llama-2"
181
+ ]
182
 
183
+ results = {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
 
185
+ for url in test_urls:
186
+ try:
187
+ parsed = urlparse(url)
188
+ path_parts = parsed.path.split('/')
189
+ path_parts = [p for p in path_parts if p] # ๋นˆ ๋ฌธ์ž์—ด ์ œ๊ฑฐ
190
+
191
+ result = {
192
+ 'parsed_url': {
193
+ 'scheme': parsed.scheme,
194
+ 'netloc': parsed.netloc,
195
+ 'path': parsed.path,
196
+ 'path_parts': path_parts
197
+ }
198
+ }
199
+
200
+ # ๊ฒฝ๋กœ ํŒŒ์‹ฑ
201
+ if len(path_parts) >= 2:
202
+ if path_parts[0] == 'spaces' or path_parts[0] == 'models':
203
+ result['extract'] = {
204
+ 'type': path_parts[0],
205
+ 'owner': path_parts[1],
206
+ 'repo': path_parts[2] if len(path_parts) > 2 else None,
207
+ 'full_id': f"{path_parts[1]}/{path_parts[2]}" if len(path_parts) > 2 else f"{path_parts[1]}"
208
+ }
209
+ else:
210
+ result['extract'] = {
211
+ 'type': 'models', # ๊ธฐ๋ณธ๊ฐ’
212
+ 'owner': path_parts[0],
213
+ 'repo': path_parts[1] if len(path_parts) > 1 else None,
214
+ 'full_id': f"{path_parts[0]}/{path_parts[1]}" if len(path_parts) > 1 else f"{path_parts[0]}"
215
+ }
216
+
217
+ results[url] = result
218
+ except Exception as e:
219
+ results[url] = {'error': str(e)}
220
 
221
+ return jsonify(results)
222
+
223
+ # ์ด ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ง์ ‘ ์‹คํ–‰๋˜๋ฉด ํ…Œ์ŠคํŠธ ์„œ๋ฒ„๋ฅผ ์‹œ์ž‘
224
+ if __name__ == '__main__':
225
+ test_app = Flask(__name__)
226
+ test_app.secret_key = os.urandom(24)
227
+
228
+ # ์„ธ์…˜ ์ฟ ํ‚ค ์„ค์ •
229
+ test_app.config['SESSION_COOKIE_SECURE'] = False # HTTPS ์—†์ด๋„ ์ž‘๋™
230
+ test_app.config['SESSION_COOKIE_HTTPONLY'] = True
231
+
232
+ add_debug_endpoints(test_app)
233
+
234
+ @test_app.route('/')
235
+ def home():
236
+ return '''
237
+ <h1>ํ—ˆ๊น…ํŽ˜์ด์Šค ์ข‹์•„์š” ๋””๋ฒ„๊ฑฐ</h1>
238
+ <p>๋‹ค์Œ ์—”๋“œํฌ์ธํŠธ๋กœ ์ ‘์†ํ•˜์„ธ์š”:</p>
239
+ <ul>
240
+ <li><a href="/login-form">๋กœ๊ทธ์ธ ํŽ˜์ด์ง€</a></li>
241
+ <li><a href="/api/debug-likes">์ข‹์•„์š” API ๋””๋ฒ„๊ทธ (๋กœ๊ทธ์ธ ํ•„์š”)</a></li>
242
+ <li><a href="/api/debug-urls">URL ํŒŒ์‹ฑ ๋””๋ฒ„๊ทธ</a></li>
243
+ </ul>
244
+ '''
245
+
246
+ @test_app.route('/login-form')
247
+ def login_form():
248
+ return '''
249
+ <!DOCTYPE html>
250
+ <html>
251
+ <head>
252
+ <title>ํ† ํฐ ๋กœ๊ทธ์ธ</title>
253
+ <style>
254
+ body { font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; }
255
+ input { width: 100%; padding: 8px; margin: 10px 0; }
256
+ button { padding: 10px 15px; background: #4CAF50; color: white; border: none; cursor: pointer; }
257
+ .result { margin-top: 20px; padding: 10px; border: 1px solid #ddd; display: none; }
258
+ pre { background: #f5f5f5; padding: 10px; overflow-x: auto; }
259
+ </style>
260
+ </head>
261
+ <body>
262
+ <h1>ํ—ˆ๊น…ํŽ˜์ด์Šค ํ† ํฐ ํ…Œ์ŠคํŠธ</h1>
263
+ <p>ํ—ˆ๊น…ํŽ˜์ด์Šค API ํ† ํฐ์„ ์ž…๋ ฅํ•˜์„ธ์š”:</p>
264
+
265
+ <input type="password" id="tokenInput" placeholder="API ํ† ํฐ" />
266
+ <button id="loginBtn">๋กœ๊ทธ์ธ</button>
267
+
268
+ <div id="result" class="result">
269
+ <h3>๊ฒฐ๊ณผ</h3>
270
+ <pre id="resultContent"></pre>
271
+ </div>
272
+
273
+ <script>
274
+ const loginBtn = document.getElementById('loginBtn');
275
+ const tokenInput = document.getElementById('tokenInput');
276
+ const result = document.getElementById('result');
277
+ const resultContent = document.getElementById('resultContent');
278
+
279
+ loginBtn.addEventListener('click', async () => {
280
+ const token = tokenInput.value.trim();
281
+ if (!token) {
282
+ alert('ํ† ํฐ์„ ์ž…๋ ฅํ•˜์„ธ์š”');
283
+ return;
284
+ }
285
+
286
+ try {
287
+ const formData = new FormData();
288
+ formData.append('token', token);
289
+
290
+ const response = await fetch('/api/login-test', {
291
+ method: 'POST',
292
+ body: formData
293
+ });
294
+
295
+ const data = await response.json();
296
+ result.style.display = 'block';
297
+ resultContent.textContent = JSON.stringify(data, null, 2);
298
+
299
+ if (data.success) {
300
+ // ์„ฑ๊ณต ์‹œ ์ข‹์•„์š” ๋ชฉ๋ก ํŽ˜์ด์ง€๋กœ ์ด๋™
301
+ setTimeout(() => {
302
+ window.location.href = '/api/debug-likes';
303
+ }, 2000);
304
+ }
305
+ } catch (error) {
306
+ result.style.display = 'block';
307
+ resultContent.textContent = `์˜ค๋ฅ˜: ${error.message}`;
308
+ }
309
+ });
310
+ </script>
311
+ </body>
312
+ </html>
313
+ '''
314
+
315
+ @test_app.route('/api/login-test', methods=['POST'])
316
+ def login_test():
317
+ token = request.form.get('token', '')
318
 
319
+ if not token:
320
+ return jsonify({'success': False, 'message': 'ํ† ํฐ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.'})
321
 
322
+ session['token'] = token
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
323
 
324
+ # ์‚ฌ์šฉ์ž ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ
325
+ user_info = None
326
+ for endpoint in ["https://huggingface.co/api/whoami-v2", "https://huggingface.co/api/whoami"]:
327
+ try:
328
+ headers = {"Authorization": f"Bearer {token}"}
329
+ response = requests.get(endpoint, headers=headers)
330
+
331
+ if response.ok:
332
+ user_info = response.json()
333
+ break
334
+ except Exception as e:
335
+ logger.error(f"์‚ฌ์šฉ์ž ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ ์˜ค๋ฅ˜: {str(e)}")
336
 
337
+ if not user_info:
338
+ return jsonify({'success': False, 'message': '์œ ํšจํ•˜์ง€ ์•Š์€ ํ† ํฐ์ž…๋‹ˆ๋‹ค.'})
 
 
339
 
340
+ # ์‚ฌ์šฉ์ž ์ด๋ฆ„ ์ฐพ๊ธฐ
341
+ username = None
342
+ if 'name' in user_info:
343
+ username = user_info['name']
344
+ elif 'user' in user_info and 'username' in user_info['user']:
345
+ username = user_info['user']['username']
346
+ elif 'username' in user_info:
347
+ username = user_info['username']
348
+ else:
349
+ username = '์ธ์ฆ๋œ ์‚ฌ์šฉ์ž'
350
 
351
+ session['username'] = username
 
 
 
352
 
353
+ # ์ข‹์•„์š” ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ
354
+ liked_models = get_liked_repos(token)
355
+ session['liked_models'] = liked_models
 
 
356
 
357
+ return jsonify({
358
+ 'success': True,
359
+ 'username': username,
360
+ 'liked_models_count': len(liked_models),
361
+ 'liked_models_sample': list(liked_models.keys())[:5] if liked_models else []
362
+ })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
363
 
364
+ # ๋””๋ฒ„๊น… ์„œ๋ฒ„ ์‹คํ–‰
365
+ logger.info("๋””๋ฒ„๊น… ์„œ๋ฒ„ ์‹œ์ž‘ (ํฌํŠธ 7860)")
366
+ test_app.run(host='0.0.0.0', port=7860, debug=True)