Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,14 +1,34 @@
|
|
1 |
import json
|
2 |
import requests
|
3 |
-
from flask import Flask,
|
|
|
4 |
|
5 |
app = Flask(__name__)
|
6 |
-
|
|
|
|
|
|
|
7 |
|
8 |
@app.route("/", methods=["GET", "POST"])
|
9 |
def index():
|
10 |
if request.method == "POST":
|
11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
space_name = request.form["space_name"]
|
13 |
github_url = request.form["github_url"]
|
14 |
github_token = request.form.get("github_token")
|
@@ -21,11 +41,9 @@ def index():
|
|
21 |
emoji = request.form["emoji"]
|
22 |
pinned = request.form.get("pinned") == "on"
|
23 |
|
24 |
-
# GitHubのIDだけの入力にも対応
|
25 |
if not github_url.startswith("https://"):
|
26 |
github_url = f"https://github.com/{github_url}"
|
27 |
|
28 |
-
# Hugging Face ユーザー名を取得(または入力フォームから取得してもOK)
|
29 |
headers = {"Authorization": f"Bearer {token}"}
|
30 |
user_info = requests.get("https://huggingface.co/api/whoami-v2", headers=headers).json()
|
31 |
username = user_info["name"]
|
@@ -63,7 +81,211 @@ def index():
|
|
63 |
except requests.exceptions.JSONDecodeError:
|
64 |
return f"エラー内容(JSON形式でない可能性): {response.text}", response.status_code
|
65 |
|
66 |
-
return
|
67 |
-
if __name__ == "__main__":
|
68 |
-
app.run(host="0.0.0.0", port=7860)
|
69 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import json
|
2 |
import requests
|
3 |
+
from flask import Flask, render_template_string, request, jsonify, redirect, url_for
|
4 |
+
import secrets
|
5 |
|
6 |
app = Flask(__name__)
|
7 |
+
app.secret_key = secrets.token_hex(16)
|
8 |
+
|
9 |
+
# 簡易的なOAuth状態管理
|
10 |
+
oauth_state = {}
|
11 |
|
12 |
@app.route("/", methods=["GET", "POST"])
|
13 |
def index():
|
14 |
if request.method == "POST":
|
15 |
+
# OAuthコールバック処理
|
16 |
+
if "code" in request.args and "state" in request.args:
|
17 |
+
state = request.args["state"]
|
18 |
+
if state not in oauth_state:
|
19 |
+
return "無効な状態トークン", 400
|
20 |
+
|
21 |
+
code = request.args["code"]
|
22 |
+
# 実際にはここでHugging FaceのOAuthトークンエンドポイントにリクエストを送信
|
23 |
+
# デモ用に簡略化
|
24 |
+
token = f"oauth-token-{code}" # 実際には取得したトークンを使用
|
25 |
+
return render_index(token=token)
|
26 |
+
|
27 |
+
# スペース作成処理
|
28 |
+
token = request.form.get("token")
|
29 |
+
if not token:
|
30 |
+
return "トークンが提供されていません", 400
|
31 |
+
|
32 |
space_name = request.form["space_name"]
|
33 |
github_url = request.form["github_url"]
|
34 |
github_token = request.form.get("github_token")
|
|
|
41 |
emoji = request.form["emoji"]
|
42 |
pinned = request.form.get("pinned") == "on"
|
43 |
|
|
|
44 |
if not github_url.startswith("https://"):
|
45 |
github_url = f"https://github.com/{github_url}"
|
46 |
|
|
|
47 |
headers = {"Authorization": f"Bearer {token}"}
|
48 |
user_info = requests.get("https://huggingface.co/api/whoami-v2", headers=headers).json()
|
49 |
username = user_info["name"]
|
|
|
81 |
except requests.exceptions.JSONDecodeError:
|
82 |
return f"エラー内容(JSON形式でない可能性): {response.text}", response.status_code
|
83 |
|
84 |
+
return render_index()
|
|
|
|
|
85 |
|
86 |
+
def render_index(token=None):
|
87 |
+
return render_template_string('''
|
88 |
+
<!DOCTYPE html>
|
89 |
+
<html lang="ja">
|
90 |
+
<head>
|
91 |
+
<meta charset="UTF-8" />
|
92 |
+
<title>HuggingFace Space 作成フォーム</title>
|
93 |
+
<style>
|
94 |
+
.tab {
|
95 |
+
overflow: hidden;
|
96 |
+
border: 1px solid #ccc;
|
97 |
+
background-color: #f1f1f1;
|
98 |
+
border-radius: 4px 4px 0 0;
|
99 |
+
}
|
100 |
+
.tab button {
|
101 |
+
background-color: inherit;
|
102 |
+
float: left;
|
103 |
+
border: none;
|
104 |
+
outline: none;
|
105 |
+
cursor: pointer;
|
106 |
+
padding: 10px 16px;
|
107 |
+
transition: 0.3s;
|
108 |
+
}
|
109 |
+
.tab button:hover {
|
110 |
+
background-color: #ddd;
|
111 |
+
}
|
112 |
+
.tab button.active {
|
113 |
+
background-color: #fff;
|
114 |
+
border-bottom: 2px solid #4CAF50;
|
115 |
+
}
|
116 |
+
.tabcontent {
|
117 |
+
display: none;
|
118 |
+
padding: 20px;
|
119 |
+
border: 1px solid #ccc;
|
120 |
+
border-top: none;
|
121 |
+
border-radius: 0 0 4px 4px;
|
122 |
+
}
|
123 |
+
.tabcontent.active {
|
124 |
+
display: block;
|
125 |
+
}
|
126 |
+
.form-group {
|
127 |
+
margin-bottom: 15px;
|
128 |
+
}
|
129 |
+
label {
|
130 |
+
display: block;
|
131 |
+
margin-bottom: 5px;
|
132 |
+
font-weight: bold;
|
133 |
+
}
|
134 |
+
input, select {
|
135 |
+
width: 100%;
|
136 |
+
padding: 8px;
|
137 |
+
box-sizing: border-box;
|
138 |
+
border: 1px solid #ddd;
|
139 |
+
border-radius: 4px;
|
140 |
+
}
|
141 |
+
button[type="submit"] {
|
142 |
+
background-color: #4CAF50;
|
143 |
+
color: white;
|
144 |
+
padding: 10px 15px;
|
145 |
+
border: none;
|
146 |
+
border-radius: 4px;
|
147 |
+
cursor: pointer;
|
148 |
+
}
|
149 |
+
button[type="submit"]:hover {
|
150 |
+
background-color: #45a049;
|
151 |
+
}
|
152 |
+
</style>
|
153 |
+
</head>
|
154 |
+
<body>
|
155 |
+
<h1>Hugging Face Space 作成</h1>
|
156 |
+
|
157 |
+
<div class="tab">
|
158 |
+
<button class="tablinks active" onclick="openTab(event, 'tokenTab')">トークン入力</button>
|
159 |
+
<button class="tablinks" onclick="openTab(event, 'oauthTab')">Hugging Faceでログイン</button>
|
160 |
+
</div>
|
161 |
+
|
162 |
+
<form method="POST">
|
163 |
+
<div id="tokenTab" class="tabcontent active">
|
164 |
+
<div class="form-group">
|
165 |
+
<label for="token">Hugging Face 書き込みトークン:</label>
|
166 |
+
<input type="password" name="token" id="token" required>
|
167 |
+
</div>
|
168 |
+
</div>
|
169 |
+
|
170 |
+
<div id="oauthTab" class="tabcontent">
|
171 |
+
<div class="form-group">
|
172 |
+
<p>Hugging FaceのOAuthを使用してログインします。</p>
|
173 |
+
<button type="button" id="oauthLogin" onclick="startOAuth()">Hugging Faceでログイン</button>
|
174 |
+
</div>
|
175 |
+
</div>
|
176 |
+
|
177 |
+
<div class="form-group">
|
178 |
+
<label for="space_name">スペース名:</label>
|
179 |
+
<input type="text" name="space_name" id="space_name" required>
|
180 |
+
</div>
|
181 |
+
|
182 |
+
<div class="form-group">
|
183 |
+
<label for="github_url">GitHub リポジトリURL または ID:</label>
|
184 |
+
<input type="text" name="github_url" id="github_url" required>
|
185 |
+
</div>
|
186 |
+
|
187 |
+
<div class="form-group">
|
188 |
+
<label for="github_token">GitHub トークン (必要な場合):</label>
|
189 |
+
<input type="password" name="github_token" id="github_token">
|
190 |
+
</div>
|
191 |
+
|
192 |
+
<div class="form-group">
|
193 |
+
<label>
|
194 |
+
<input type="checkbox" name="private" id="private">
|
195 |
+
非公開にする
|
196 |
+
</label>
|
197 |
+
</div>
|
198 |
+
|
199 |
+
<div class="form-group">
|
200 |
+
<label for="sdk">SDK:</label>
|
201 |
+
<select name="sdk" id="sdk">
|
202 |
+
<option value="gradio">Gradio</option>
|
203 |
+
<option value="streamlit">Streamlit</option>
|
204 |
+
<option value="static">Static</option>
|
205 |
+
<option value="docker">Docker</option>
|
206 |
+
</select>
|
207 |
+
</div>
|
208 |
+
|
209 |
+
<div class="form-group">
|
210 |
+
<label for="description">説明:</label>
|
211 |
+
<input type="text" name="description" id="description">
|
212 |
+
</div>
|
213 |
+
|
214 |
+
<div class="form-group">
|
215 |
+
<label for="license">ライセンス:</label>
|
216 |
+
<input type="text" name="license" id="license" placeholder="e.g. apache-2.0">
|
217 |
+
</div>
|
218 |
+
|
219 |
+
<div class="form-group">
|
220 |
+
<label for="port">アプリのポート:</label>
|
221 |
+
<input type="text" name="port" id="port" placeholder="例: 7860">
|
222 |
+
</div>
|
223 |
+
|
224 |
+
<div class="form-group">
|
225 |
+
<label for="title">スペースの名前 (表示用):</label>
|
226 |
+
<input type="text" name="title" id="title">
|
227 |
+
</div>
|
228 |
+
|
229 |
+
<div class="form-group">
|
230 |
+
<label for="emoji">絵文字 (表示アイコン):</label>
|
231 |
+
<input type="text" name="emoji" id="emoji" placeholder="例: 🚀">
|
232 |
+
</div>
|
233 |
+
|
234 |
+
<div class="form-group">
|
235 |
+
<label>
|
236 |
+
<input type="checkbox" name="pinned" id="pinned">
|
237 |
+
固定 (ピン止め)
|
238 |
+
</label>
|
239 |
+
</div>
|
240 |
+
|
241 |
+
<button type="submit">スペースを作成</button>
|
242 |
+
</form>
|
243 |
+
|
244 |
+
<script>
|
245 |
+
function openTab(evt, tabName) {
|
246 |
+
const tabcontent = document.getElementsByClassName("tabcontent");
|
247 |
+
for (let i = 0; i < tabcontent.length; i++) {
|
248 |
+
tabcontent[i].classList.remove("active");
|
249 |
+
}
|
250 |
+
|
251 |
+
const tablinks = document.getElementsByClassName("tablinks");
|
252 |
+
for (let i = 0; i < tablinks.length; i++) {
|
253 |
+
tablinks[i].classList.remove("active");
|
254 |
+
}
|
255 |
+
|
256 |
+
document.getElementById(tabName).classList.add("active");
|
257 |
+
evt.currentTarget.classList.add("active");
|
258 |
+
}
|
259 |
+
|
260 |
+
function startOAuth() {
|
261 |
+
// 実際のアプリではHugging FaceのOAuthエンドポイントを使用
|
262 |
+
// デモ用に簡略化
|
263 |
+
const state = Math.random().toString(36).substring(2);
|
264 |
+
window.location.href = `https://huggingface.co/oauth/authorize?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=${encodeURIComponent(window.location.origin)}&scope=read-repos,write-repos&state=${state}`;
|
265 |
+
}
|
266 |
+
|
267 |
+
document.addEventListener("DOMContentLoaded", () => {
|
268 |
+
const tokenInput = document.getElementById("token");
|
269 |
+
|
270 |
+
// URLからOAuthトークンを取得 (デモ用)
|
271 |
+
const urlParams = new URLSearchParams(window.location.search);
|
272 |
+
const oauthToken = urlParams.get('oauth_token');
|
273 |
+
|
274 |
+
if (oauthToken) {
|
275 |
+
tokenInput.value = oauthToken;
|
276 |
+
} else {
|
277 |
+
// 保存されたトークンを自動で読み込み
|
278 |
+
tokenInput.value = localStorage.getItem("hf_token") || "";
|
279 |
+
}
|
280 |
+
|
281 |
+
tokenInput.addEventListener("input", () => {
|
282 |
+
localStorage.setItem("hf_token", tokenInput.value);
|
283 |
+
});
|
284 |
+
});
|
285 |
+
</script>
|
286 |
+
</body>
|
287 |
+
</html>
|
288 |
+
''', token=token)
|
289 |
+
|
290 |
+
if __name__ == "__main__":
|
291 |
+
app.run(host="0.0.0.0", port=7860)
|