Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -1,78 +1,41 @@
|
|
1 |
from flask import Flask, render_template, request, jsonify, session
|
2 |
-
import requests
|
3 |
import os
|
4 |
from datetime import timedelta
|
5 |
-
import json
|
6 |
|
7 |
app = Flask(__name__)
|
8 |
app.secret_key = os.urandom(24)
|
9 |
app.permanent_session_lifetime = timedelta(days=7)
|
10 |
|
11 |
-
# Hugging Face 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 |
-
return {
|
49 |
-
'type': 'models',
|
50 |
-
'owner': parts[3],
|
51 |
-
'repo': parts[4],
|
52 |
-
'full_id': f"{parts[3]}/{parts[4]}"
|
53 |
-
}
|
54 |
-
|
55 |
-
return None
|
56 |
-
|
57 |
# URL์ ๋ง์ง๋ง ๋ถ๋ถ์ ์ ๋ชฉ์ผ๋ก ์ถ์ถ
|
58 |
def extract_title(url):
|
59 |
parts = url.split("/")
|
60 |
title = parts[-1] if parts else ""
|
61 |
return title.replace("_", " ").replace("-", " ")
|
62 |
|
63 |
-
# ํ๊น
ํ์ด์ค ์ฌ์ฉ์ ์ธ์ฆ
|
64 |
-
def validate_token(token):
|
65 |
-
headers = {"Authorization": f"Bearer {token}"}
|
66 |
-
|
67 |
-
try:
|
68 |
-
response = requests.get("https://huggingface.co/api/whoami-v2", headers=headers)
|
69 |
-
if response.ok:
|
70 |
-
return True, response.json()
|
71 |
-
except Exception as e:
|
72 |
-
print(f"ํ ํฐ ๊ฒ์ฆ ์ค๋ฅ: {e}")
|
73 |
-
|
74 |
-
return False, None
|
75 |
-
|
76 |
@app.route('/')
|
77 |
def home():
|
78 |
return render_template('index.html')
|
@@ -84,30 +47,16 @@ def login():
|
|
84 |
if not token:
|
85 |
return jsonify({'success': False, 'message': 'ํ ํฐ์ ์
๋ ฅํด์ฃผ์ธ์.'})
|
86 |
|
87 |
-
|
88 |
-
|
89 |
-
if not is_valid or not user_info:
|
90 |
return jsonify({'success': False, 'message': '์ ํจํ์ง ์์ ํ ํฐ์
๋๋ค.'})
|
91 |
|
92 |
-
# ์ฌ์ฉ์
|
93 |
-
username =
|
94 |
-
if 'name' in user_info:
|
95 |
-
username = user_info['name']
|
96 |
-
elif 'user' in user_info and 'username' in user_info['user']:
|
97 |
-
username = user_info['user']['username']
|
98 |
-
elif 'username' in user_info:
|
99 |
-
username = user_info['username']
|
100 |
-
else:
|
101 |
-
username = '์ธ์ฆ๋ ์ฌ์ฉ์'
|
102 |
|
103 |
# ์ธ์
์ ์ ์ฅ
|
104 |
-
session['token'] = token
|
105 |
session['username'] = username
|
106 |
|
107 |
-
# ์ฌ์ฉ์๊ฐ ์ข์์ํ URL ์ํ๋ฅผ ์ธ์
์ ์ด๊ธฐํ
|
108 |
-
if 'liked_urls' not in session:
|
109 |
-
session['liked_urls'] = {}
|
110 |
-
|
111 |
return jsonify({
|
112 |
'success': True,
|
113 |
'username': username
|
@@ -115,30 +64,20 @@ def login():
|
|
115 |
|
116 |
@app.route('/api/logout', methods=['POST'])
|
117 |
def logout():
|
118 |
-
session.pop('token', None)
|
119 |
session.pop('username', None)
|
120 |
-
session.pop('liked_urls', None)
|
121 |
return jsonify({'success': True})
|
122 |
|
123 |
@app.route('/api/urls', methods=['GET'])
|
124 |
def get_urls():
|
125 |
results = []
|
126 |
-
for
|
|
|
|
|
127 |
title = extract_title(url)
|
128 |
-
model_info = extract_model_info(url)
|
129 |
-
|
130 |
-
if not model_info:
|
131 |
-
continue
|
132 |
-
|
133 |
-
# ์ฌ์ฉ์์ ์ข์์ ์ํ ํ์ธ
|
134 |
-
is_liked = False
|
135 |
-
if 'liked_urls' in session and url in session['liked_urls']:
|
136 |
-
is_liked = True
|
137 |
|
138 |
results.append({
|
139 |
'url': url,
|
140 |
'title': title,
|
141 |
-
'model_info': model_info,
|
142 |
'is_liked': is_liked
|
143 |
})
|
144 |
|
@@ -155,38 +94,19 @@ def toggle_like():
|
|
155 |
if not url:
|
156 |
return jsonify({'success': False, 'message': 'URL์ด ํ์ํฉ๋๋ค.'})
|
157 |
|
158 |
-
#
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
# ์ธ์
์ ์ข์์ ์ ๋ณด ์
๋ฐ์ดํธ
|
172 |
-
session['liked_urls'] = liked_urls
|
173 |
-
|
174 |
-
return jsonify({
|
175 |
-
'success': True,
|
176 |
-
'is_liked': is_liked,
|
177 |
-
'message': message
|
178 |
-
})
|
179 |
-
|
180 |
-
@app.route('/api/get-likes', methods=['GET'])
|
181 |
-
def get_likes():
|
182 |
-
if 'username' not in session:
|
183 |
-
return jsonify({'success': False, 'message': '๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค.'})
|
184 |
-
|
185 |
-
liked_urls = session.get('liked_urls', {})
|
186 |
-
return jsonify({
|
187 |
-
'success': True,
|
188 |
-
'liked_urls': liked_urls
|
189 |
-
})
|
190 |
|
191 |
@app.route('/api/session-status', methods=['GET'])
|
192 |
def session_status():
|
@@ -560,7 +480,6 @@ if __name__ == '__main__':
|
|
560 |
// ์ ํ๋ฆฌ์ผ์ด์
์ํ
|
561 |
const state = {
|
562 |
username: null,
|
563 |
-
likedURLs: {},
|
564 |
allURLs: [],
|
565 |
isLoading: false,
|
566 |
viewMode: 'all' // 'all' ๋๋ 'liked'
|
@@ -596,7 +515,7 @@ if __name__ == '__main__':
|
|
596 |
// ์ข์์ ํต๊ณ ์
๋ฐ์ดํธ
|
597 |
function updateLikeStats() {
|
598 |
const totalCount = state.allURLs.length;
|
599 |
-
const likedCount =
|
600 |
|
601 |
elements.totalUrlCount.textContent = totalCount;
|
602 |
elements.likedUrlCount.textContent = likedCount;
|
@@ -615,9 +534,6 @@ if __name__ == '__main__':
|
|
615 |
elements.loggedInSection.style.display = 'block';
|
616 |
elements.likeStatus.style.display = 'block';
|
617 |
|
618 |
-
// ์ข์์ ๋ชฉ๋ก ๊ฐ์ ธ์ค๊ธฐ
|
619 |
-
await getLikedUrls();
|
620 |
-
|
621 |
// URL ๋ชฉ๋ก ๋ก๋
|
622 |
loadUrls();
|
623 |
}
|
@@ -626,23 +542,6 @@ if __name__ == '__main__':
|
|
626 |
}
|
627 |
}
|
628 |
|
629 |
-
// ์ข์์ URL ๊ฐ์ ธ์ค๊ธฐ
|
630 |
-
async function getLikedUrls() {
|
631 |
-
try {
|
632 |
-
const response = await fetch('/api/get-likes');
|
633 |
-
const data = await handleApiResponse(response);
|
634 |
-
|
635 |
-
if (data.success) {
|
636 |
-
state.likedURLs = data.liked_urls || {};
|
637 |
-
return true;
|
638 |
-
}
|
639 |
-
} catch (error) {
|
640 |
-
console.error('์ข์์ ๋ชฉ๋ก ๊ฐ์ ธ์ค๊ธฐ ์ค๋ฅ:', error);
|
641 |
-
}
|
642 |
-
|
643 |
-
return false;
|
644 |
-
}
|
645 |
-
|
646 |
// ๋ก๊ทธ์ธ ์ฒ๋ฆฌ
|
647 |
async function login(token) {
|
648 |
if (!token.trim()) {
|
@@ -673,9 +572,6 @@ if __name__ == '__main__':
|
|
673 |
|
674 |
showMessage(`${state.username}๋์ผ๋ก ๋ก๊ทธ์ธ๋์์ต๋๋ค.`);
|
675 |
|
676 |
-
// ์ข์์ ๋ชฉ๋ก ๊ฐ์ ธ์ค๊ธฐ
|
677 |
-
await getLikedUrls();
|
678 |
-
|
679 |
// URL ๋ชฉ๋ก ๋ก๋
|
680 |
loadUrls();
|
681 |
} else {
|
@@ -702,7 +598,6 @@ if __name__ == '__main__':
|
|
702 |
|
703 |
if (data.success) {
|
704 |
state.username = null;
|
705 |
-
state.likedURLs = {};
|
706 |
state.allURLs = [];
|
707 |
|
708 |
elements.currentUser.textContent = '๋ก๊ทธ์ธ๋์ง ์์';
|
@@ -735,13 +630,6 @@ if __name__ == '__main__':
|
|
735 |
// URL ๋ฐ ์ข์์ ์ํ ์ ์ฅ
|
736 |
state.allURLs = urls;
|
737 |
|
738 |
-
// ์๋ฒ์์ ๋ฐ์ ์ข์์ ์ํ ์
๋ฐ์ดํธ
|
739 |
-
urls.forEach(item => {
|
740 |
-
if (item.is_liked) {
|
741 |
-
state.likedURLs[item.url] = true;
|
742 |
-
}
|
743 |
-
});
|
744 |
-
|
745 |
// ํํฐ๋ง ๋ฐ ๋ ๋๋ง
|
746 |
filterAndRenderCards();
|
747 |
|
@@ -761,10 +649,10 @@ if __name__ == '__main__':
|
|
761 |
|
762 |
// ํํฐ๋ง ์ ์ฉ
|
763 |
const filteredUrls = state.allURLs.filter(item => {
|
764 |
-
const { url, title } = item;
|
765 |
|
766 |
// ์ข์์ ํํฐ๋ง (์ข์์๋ง ๋ณด๊ธฐ ๋ชจ๋)
|
767 |
-
if (state.viewMode === 'liked' && !
|
768 |
return false;
|
769 |
}
|
770 |
|
@@ -800,23 +688,34 @@ if __name__ == '__main__':
|
|
800 |
const data = await handleApiResponse(response);
|
801 |
|
802 |
if (data.success) {
|
803 |
-
//
|
804 |
-
|
805 |
-
|
806 |
-
|
807 |
-
|
808 |
-
|
809 |
-
|
810 |
-
|
811 |
-
|
812 |
-
|
813 |
-
|
814 |
-
|
815 |
-
|
816 |
-
|
817 |
-
|
818 |
-
|
819 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
820 |
}
|
821 |
|
822 |
showMessage(data.message);
|
@@ -853,12 +752,11 @@ if __name__ == '__main__':
|
|
853 |
}
|
854 |
|
855 |
urls.forEach(item => {
|
856 |
-
const { url, title } = item;
|
857 |
-
const isLiked = state.likedURLs[url] || false;
|
858 |
|
859 |
// ์นด๋ ์์ฑ
|
860 |
const card = document.createElement('div');
|
861 |
-
card.className = `card ${
|
862 |
|
863 |
// ์นด๋ ํค๋
|
864 |
const cardHeader = document.createElement('div');
|
@@ -874,18 +772,16 @@ if __name__ == '__main__':
|
|
874 |
|
875 |
// URL ๋งํฌ
|
876 |
const linkEl = document.createElement('a');
|
877 |
-
|
878 |
-
|
879 |
-
linkEl.href = url;
|
880 |
linkEl.textContent = url;
|
881 |
linkEl.target = '_blank';
|
882 |
card.appendChild(linkEl);
|
883 |
|
884 |
// ์ข์์ ๋ฒํผ
|
885 |
const likeBtn = document.createElement('button');
|
886 |
-
likeBtn.className = `like-button ${
|
887 |
likeBtn.innerHTML = 'โฅ';
|
888 |
-
likeBtn.title =
|
889 |
|
890 |
likeBtn.addEventListener('click', (e) => {
|
891 |
e.preventDefault();
|
@@ -895,7 +791,7 @@ linkEl.href = url;
|
|
895 |
card.appendChild(likeBtn);
|
896 |
|
897 |
// ์ข์์ ๋ฐฐ์ง (์ข์์ ์ํ์ผ ๋๋ง)
|
898 |
-
if (
|
899 |
const likeBadge = document.createElement('div');
|
900 |
likeBadge.className = 'like-badge';
|
901 |
likeBadge.textContent = '์ข์์';
|
@@ -919,7 +815,7 @@ linkEl.href = url;
|
|
919 |
filterAndRenderCards();
|
920 |
}
|
921 |
|
922 |
-
|
923 |
elements.loginButton.addEventListener('click', () => {
|
924 |
login(elements.tokenInput.value);
|
925 |
});
|
@@ -952,4 +848,4 @@ linkEl.href = url;
|
|
952 |
''')
|
953 |
|
954 |
# ํ๊น
ํ์ด์ค ์คํ์ด์ค์์๋ 7860 ํฌํธ ์ฌ์ฉ
|
955 |
-
app.run(host='0.0.0.0', port=7860)
|
|
|
1 |
from flask import Flask, render_template, request, jsonify, session
|
|
|
2 |
import os
|
3 |
from datetime import timedelta
|
|
|
4 |
|
5 |
app = Flask(__name__)
|
6 |
app.secret_key = os.urandom(24)
|
7 |
app.permanent_session_lifetime = timedelta(days=7)
|
8 |
|
9 |
+
# Hugging Face URL ๋ชฉ๋ก - ์ผ๋ถ URL์ ๋ฏธ๋ฆฌ ์ข์์ ์ํ๋ก ์ค์
|
10 |
+
# 'is_liked'๋ฅผ true๋ก ์ค์ ํ URL์ ํญ์ ์ข์์ ์ํ๋ก ํ์๋จ
|
11 |
HUGGINGFACE_URLS = [
|
12 |
+
{"url": "https://huggingface.co/spaces/ginipick/Tech_Hangman_Game", "is_liked": True},
|
13 |
+
{"url": "https://huggingface.co/spaces/openfree/deepseek_r1_API", "is_liked": False},
|
14 |
+
{"url": "https://huggingface.co/spaces/ginipick/open_Deep-Research", "is_liked": True},
|
15 |
+
{"url": "https://huggingface.co/spaces/aiqmaster/open-deep-research", "is_liked": False},
|
16 |
+
{"url": "https://huggingface.co/spaces/seawolf2357/DeepSeek-R1-32b-search", "is_liked": True},
|
17 |
+
{"url": "https://huggingface.co/spaces/ginigen/LLaDA", "is_liked": False},
|
18 |
+
{"url": "https://huggingface.co/spaces/VIDraft/PHI4-Multimodal", "is_liked": True},
|
19 |
+
{"url": "https://huggingface.co/spaces/ginigen/Ovis2-8B", "is_liked": False},
|
20 |
+
{"url": "https://huggingface.co/spaces/ginigen/Graph-Mind", "is_liked": True},
|
21 |
+
{"url": "https://huggingface.co/spaces/ginigen/Workflow-Canvas", "is_liked": False},
|
22 |
+
{"url": "https://huggingface.co/spaces/ginigen/Design", "is_liked": True},
|
23 |
+
{"url": "https://huggingface.co/spaces/ginigen/Diagram", "is_liked": False},
|
24 |
+
{"url": "https://huggingface.co/spaces/ginigen/Mockup", "is_liked": True},
|
25 |
+
{"url": "https://huggingface.co/spaces/ginigen/Infographic", "is_liked": False},
|
26 |
+
{"url": "https://huggingface.co/spaces/ginigen/Flowchart", "is_liked": True},
|
27 |
+
{"url": "https://huggingface.co/spaces/aiqcamp/FLUX-Vision", "is_liked": False},
|
28 |
+
{"url": "https://huggingface.co/spaces/ginigen/VoiceClone-TTS", "is_liked": True},
|
29 |
+
{"url": "https://huggingface.co/spaces/openfree/Perceptron-Network", "is_liked": False},
|
30 |
+
{"url": "https://huggingface.co/spaces/openfree/Article-Generator", "is_liked": True},
|
31 |
]
|
32 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
# URL์ ๋ง์ง๋ง ๋ถ๋ถ์ ์ ๋ชฉ์ผ๋ก ์ถ์ถ
|
34 |
def extract_title(url):
|
35 |
parts = url.split("/")
|
36 |
title = parts[-1] if parts else ""
|
37 |
return title.replace("_", " ").replace("-", " ")
|
38 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
@app.route('/')
|
40 |
def home():
|
41 |
return render_template('index.html')
|
|
|
47 |
if not token:
|
48 |
return jsonify({'success': False, 'message': 'ํ ํฐ์ ์
๋ ฅํด์ฃผ์ธ์.'})
|
49 |
|
50 |
+
# ๊ฐ๋จํ ํ ํฐ ๊ธธ์ด๋ง ๊ฒ์ฌ (์ค์ ๋ก๋ ๋ ๋ณต์กํ ๊ฒ์ฆ ํ์)
|
51 |
+
if len(token) < 5:
|
|
|
52 |
return jsonify({'success': False, 'message': '์ ํจํ์ง ์์ ํ ํฐ์
๋๋ค.'})
|
53 |
|
54 |
+
# ํ ํฐ์ ์ฒซ ๊ธ์๋ฅผ ์ฌ์ฉ์ ์ด๋ฆ์ผ๋ก ์ค์ (ํ
์คํธ์ฉ)
|
55 |
+
username = f"์ฌ์ฉ์_{token[:3]}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
|
57 |
# ์ธ์
์ ์ ์ฅ
|
|
|
58 |
session['username'] = username
|
59 |
|
|
|
|
|
|
|
|
|
60 |
return jsonify({
|
61 |
'success': True,
|
62 |
'username': username
|
|
|
64 |
|
65 |
@app.route('/api/logout', methods=['POST'])
|
66 |
def logout():
|
|
|
67 |
session.pop('username', None)
|
|
|
68 |
return jsonify({'success': True})
|
69 |
|
70 |
@app.route('/api/urls', methods=['GET'])
|
71 |
def get_urls():
|
72 |
results = []
|
73 |
+
for url_item in HUGGINGFACE_URLS:
|
74 |
+
url = url_item["url"]
|
75 |
+
is_liked = url_item["is_liked"]
|
76 |
title = extract_title(url)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
77 |
|
78 |
results.append({
|
79 |
'url': url,
|
80 |
'title': title,
|
|
|
81 |
'is_liked': is_liked
|
82 |
})
|
83 |
|
|
|
94 |
if not url:
|
95 |
return jsonify({'success': False, 'message': 'URL์ด ํ์ํฉ๋๋ค.'})
|
96 |
|
97 |
+
# URL ๋ชฉ๋ก์์ ํด๋น URL ์ฐพ๊ธฐ
|
98 |
+
for url_item in HUGGINGFACE_URLS:
|
99 |
+
if url_item["url"] == url:
|
100 |
+
# ์ข์์ ์ํ ํ ๊ธ
|
101 |
+
url_item["is_liked"] = not url_item["is_liked"]
|
102 |
+
|
103 |
+
return jsonify({
|
104 |
+
'success': True,
|
105 |
+
'is_liked': url_item["is_liked"],
|
106 |
+
'message': '์ข์์๋ฅผ ์ถ๊ฐํ์ต๋๋ค.' if url_item["is_liked"] else '์ข์์๋ฅผ ์ทจ์ํ์ต๋๋ค.'
|
107 |
+
})
|
108 |
+
|
109 |
+
return jsonify({'success': False, 'message': 'ํด๋น URL์ ์ฐพ์ ์ ์์ต๋๋ค.'})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
110 |
|
111 |
@app.route('/api/session-status', methods=['GET'])
|
112 |
def session_status():
|
|
|
480 |
// ์ ํ๋ฆฌ์ผ์ด์
์ํ
|
481 |
const state = {
|
482 |
username: null,
|
|
|
483 |
allURLs: [],
|
484 |
isLoading: false,
|
485 |
viewMode: 'all' // 'all' ๋๋ 'liked'
|
|
|
515 |
// ์ข์์ ํต๊ณ ์
๋ฐ์ดํธ
|
516 |
function updateLikeStats() {
|
517 |
const totalCount = state.allURLs.length;
|
518 |
+
const likedCount = state.allURLs.filter(item => item.is_liked).length;
|
519 |
|
520 |
elements.totalUrlCount.textContent = totalCount;
|
521 |
elements.likedUrlCount.textContent = likedCount;
|
|
|
534 |
elements.loggedInSection.style.display = 'block';
|
535 |
elements.likeStatus.style.display = 'block';
|
536 |
|
|
|
|
|
|
|
537 |
// URL ๋ชฉ๋ก ๋ก๋
|
538 |
loadUrls();
|
539 |
}
|
|
|
542 |
}
|
543 |
}
|
544 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
545 |
// ๋ก๊ทธ์ธ ์ฒ๋ฆฌ
|
546 |
async function login(token) {
|
547 |
if (!token.trim()) {
|
|
|
572 |
|
573 |
showMessage(`${state.username}๋์ผ๋ก ๋ก๊ทธ์ธ๋์์ต๋๋ค.`);
|
574 |
|
|
|
|
|
|
|
575 |
// URL ๋ชฉ๋ก ๋ก๋
|
576 |
loadUrls();
|
577 |
} else {
|
|
|
598 |
|
599 |
if (data.success) {
|
600 |
state.username = null;
|
|
|
601 |
state.allURLs = [];
|
602 |
|
603 |
elements.currentUser.textContent = '๋ก๊ทธ์ธ๋์ง ์์';
|
|
|
630 |
// URL ๋ฐ ์ข์์ ์ํ ์ ์ฅ
|
631 |
state.allURLs = urls;
|
632 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
633 |
// ํํฐ๋ง ๋ฐ ๋ ๋๋ง
|
634 |
filterAndRenderCards();
|
635 |
|
|
|
649 |
|
650 |
// ํํฐ๋ง ์ ์ฉ
|
651 |
const filteredUrls = state.allURLs.filter(item => {
|
652 |
+
const { url, title, is_liked } = item;
|
653 |
|
654 |
// ์ข์์ ํํฐ๋ง (์ข์์๋ง ๋ณด๊ธฐ ๋ชจ๋)
|
655 |
+
if (state.viewMode === 'liked' && !is_liked) {
|
656 |
return false;
|
657 |
}
|
658 |
|
|
|
688 |
const data = await handleApiResponse(response);
|
689 |
|
690 |
if (data.success) {
|
691 |
+
// ์ํ ๊ฐ์ฒด์์ URL ์ฐพ๊ธฐ
|
692 |
+
const urlItem = state.allURLs.find(item => item.url === url);
|
693 |
+
if (urlItem) {
|
694 |
+
// ์ข์์ ์ํ ์
๋ฐ์ดํธ
|
695 |
+
urlItem.is_liked = data.is_liked;
|
696 |
+
|
697 |
+
// ์นด๋ UI ์
๋ฐ์ดํธ
|
698 |
+
if (data.is_liked) {
|
699 |
+
card.classList.add('liked');
|
700 |
+
const likeBtn = card.querySelector('.like-button');
|
701 |
+
if (likeBtn) likeBtn.classList.add('liked');
|
702 |
+
|
703 |
+
// ์ข์์ ๋ฐฐ์ง ์ถ๊ฐ
|
704 |
+
if (!card.querySelector('.like-badge')) {
|
705 |
+
const likeBadge = document.createElement('div');
|
706 |
+
likeBadge.className = 'like-badge';
|
707 |
+
likeBadge.textContent = '์ข์์';
|
708 |
+
card.appendChild(likeBadge);
|
709 |
+
}
|
710 |
+
} else {
|
711 |
+
card.classList.remove('liked');
|
712 |
+
const likeBtn = card.querySelector('.like-button');
|
713 |
+
if (likeBtn) likeBtn.classList.remove('liked');
|
714 |
+
|
715 |
+
// ์ข์์ ๋ฐฐ์ง ์ ๊ฑฐ
|
716 |
+
const likeBadge = card.querySelector('.like-badge');
|
717 |
+
if (likeBadge) card.removeChild(likeBadge);
|
718 |
+
}
|
719 |
}
|
720 |
|
721 |
showMessage(data.message);
|
|
|
752 |
}
|
753 |
|
754 |
urls.forEach(item => {
|
755 |
+
const { url, title, is_liked } = item;
|
|
|
756 |
|
757 |
// ์นด๋ ์์ฑ
|
758 |
const card = document.createElement('div');
|
759 |
+
card.className = `card ${is_liked ? 'liked' : ''}`;
|
760 |
|
761 |
// ์นด๋ ํค๋
|
762 |
const cardHeader = document.createElement('div');
|
|
|
772 |
|
773 |
// URL ๋งํฌ
|
774 |
const linkEl = document.createElement('a');
|
775 |
+
linkEl.href = url;
|
|
|
|
|
776 |
linkEl.textContent = url;
|
777 |
linkEl.target = '_blank';
|
778 |
card.appendChild(linkEl);
|
779 |
|
780 |
// ์ข์์ ๋ฒํผ
|
781 |
const likeBtn = document.createElement('button');
|
782 |
+
likeBtn.className = `like-button ${is_liked ? 'liked' : ''}`;
|
783 |
likeBtn.innerHTML = 'โฅ';
|
784 |
+
likeBtn.title = is_liked ? '์ข์์ ์ทจ์' : '์ข์์';
|
785 |
|
786 |
likeBtn.addEventListener('click', (e) => {
|
787 |
e.preventDefault();
|
|
|
791 |
card.appendChild(likeBtn);
|
792 |
|
793 |
// ์ข์์ ๋ฐฐ์ง (์ข์์ ์ํ์ผ ๋๋ง)
|
794 |
+
if (is_liked) {
|
795 |
const likeBadge = document.createElement('div');
|
796 |
likeBadge.className = 'like-badge';
|
797 |
likeBadge.textContent = '์ข์์';
|
|
|
815 |
filterAndRenderCards();
|
816 |
}
|
817 |
|
818 |
+
// ์ด๋ฒคํธ ๋ฆฌ์ค๋ ์ค์
|
819 |
elements.loginButton.addEventListener('click', () => {
|
820 |
login(elements.tokenInput.value);
|
821 |
});
|
|
|
848 |
''')
|
849 |
|
850 |
# ํ๊น
ํ์ด์ค ์คํ์ด์ค์์๋ 7860 ํฌํธ ์ฌ์ฉ
|
851 |
+
app.run(host='0.0.0.0', port=7860)
|