Spaces:
Sleeping
Sleeping
| import json | |
| import requests | |
| from flask import Flask, render_template_string, request, jsonify, redirect, url_for | |
| import secrets | |
| app = Flask(__name__) | |
| app.secret_key = secrets.token_hex(16) | |
| # 簡易的なOAuth状態管理 | |
| oauth_state = {} | |
| def index(): | |
| if request.method == "POST": | |
| # OAuthコールバック処理 | |
| if "code" in request.args and "state" in request.args: | |
| state = request.args["state"] | |
| if state not in oauth_state: | |
| return "無効な状態トークン", 400 | |
| code = request.args["code"] | |
| # 実際にはここでHugging FaceのOAuthトークンエンドポイントにリクエストを送信 | |
| # デモ用に簡略化 | |
| token = f"oauth-token-{code}" # 実際には取得したトークンを使用 | |
| return render_index(token=token) | |
| # スペース作成処理 | |
| token = request.form.get("token") | |
| if not token: | |
| return "トークンが提供されていません", 400 | |
| space_name = request.form["space_name"] | |
| github_url = request.form["github_url"] | |
| github_token = request.form.get("github_token") | |
| private = request.form.get("private") == "on" | |
| sdk = request.form["sdk"] | |
| description = request.form["description"] | |
| license_ = request.form["license"] | |
| port = request.form["port"] | |
| title = request.form["title"] | |
| emoji = request.form["emoji"] | |
| pinned = request.form.get("pinned") == "on" | |
| if not github_url.startswith("https://"): | |
| github_url = f"https://github.com/{github_url}" | |
| headers = {"Authorization": f"Bearer {token}"} | |
| user_info = requests.get("https://huggingface.co/api/whoami-v2", headers=headers).json() | |
| username = user_info["name"] | |
| api_url = f"https://huggingface.co/api/spaces/{username}/{space_name}" | |
| payload = { | |
| "sdk": sdk, | |
| "private": private, | |
| "title": title, | |
| "emoji": emoji, | |
| "pinned": pinned, | |
| "license": license_, | |
| "app_port": port, | |
| "description": description, | |
| "git": github_url | |
| } | |
| if github_token: | |
| payload["secrets"] = {"GITHUB_TOKEN": github_token} | |
| json_data = json.dumps(payload, ensure_ascii=False).encode("utf-8") | |
| response = requests.put( | |
| api_url, | |
| headers={ | |
| "Authorization": f"Bearer {token}", | |
| "Content-Type": "application/json" | |
| }, | |
| data=json_data | |
| ) | |
| try: | |
| return jsonify(response.json()) | |
| except requests.exceptions.JSONDecodeError: | |
| return f"エラー内容(JSON形式でない可能性): {response.text}", response.status_code | |
| return render_index() | |
| def render_index(token=None): | |
| return render_template_string(''' | |
| <!DOCTYPE html> | |
| <html lang="ja"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <title>HuggingFace Space 作成フォーム</title> | |
| <style> | |
| .tab { | |
| overflow: hidden; | |
| border: 1px solid #ccc; | |
| background-color: #f1f1f1; | |
| border-radius: 4px 4px 0 0; | |
| } | |
| .tab button { | |
| background-color: inherit; | |
| float: left; | |
| border: none; | |
| outline: none; | |
| cursor: pointer; | |
| padding: 10px 16px; | |
| transition: 0.3s; | |
| } | |
| .tab button:hover { | |
| background-color: #ddd; | |
| } | |
| .tab button.active { | |
| background-color: #fff; | |
| border-bottom: 2px solid #4CAF50; | |
| } | |
| .tabcontent { | |
| display: none; | |
| padding: 20px; | |
| border: 1px solid #ccc; | |
| border-top: none; | |
| border-radius: 0 0 4px 4px; | |
| } | |
| .tabcontent.active { | |
| display: block; | |
| } | |
| .form-group { | |
| margin-bottom: 15px; | |
| } | |
| label { | |
| display: block; | |
| margin-bottom: 5px; | |
| font-weight: bold; | |
| } | |
| input, select { | |
| width: 100%; | |
| padding: 8px; | |
| box-sizing: border-box; | |
| border: 1px solid #ddd; | |
| border-radius: 4px; | |
| } | |
| button[type="submit"] { | |
| background-color: #4CAF50; | |
| color: white; | |
| padding: 10px 15px; | |
| border: none; | |
| border-radius: 4px; | |
| cursor: pointer; | |
| } | |
| button[type="submit"]:hover { | |
| background-color: #45a049; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>Hugging Face Space 作成</h1> | |
| <div class="tab"> | |
| <button class="tablinks active" onclick="openTab(event, 'tokenTab')">トークン入力</button> | |
| <button class="tablinks" onclick="openTab(event, 'oauthTab')">Hugging Faceでログイン</button> | |
| </div> | |
| <form method="POST"> | |
| <div id="tokenTab" class="tabcontent active"> | |
| <div class="form-group"> | |
| <label for="token">Hugging Face 書き込みトークン:</label> | |
| <input type="password" name="token" id="token" required> | |
| </div> | |
| </div> | |
| <div id="oauthTab" class="tabcontent"> | |
| <div class="form-group"> | |
| <p>Hugging FaceのOAuthを使用してログインします。</p> | |
| <button type="button" id="oauthLogin" onclick="startOAuth()">Hugging Faceでログイン</button> | |
| </div> | |
| </div> | |
| <div class="form-group"> | |
| <label for="space_name">スペース名:</label> | |
| <input type="text" name="space_name" id="space_name" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="github_url">GitHub リポジトリURL または ID:</label> | |
| <input type="text" name="github_url" id="github_url" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="github_token">GitHub トークン (必要な場合):</label> | |
| <input type="password" name="github_token" id="github_token"> | |
| </div> | |
| <div class="form-group"> | |
| <label> | |
| <input type="checkbox" name="private" id="private"> | |
| 非公開にする | |
| </label> | |
| </div> | |
| <div class="form-group"> | |
| <label for="sdk">SDK:</label> | |
| <select name="sdk" id="sdk"> | |
| <option value="gradio">Gradio</option> | |
| <option value="streamlit">Streamlit</option> | |
| <option value="static">Static</option> | |
| <option value="docker">Docker</option> | |
| </select> | |
| </div> | |
| <div class="form-group"> | |
| <label for="description">説明:</label> | |
| <input type="text" name="description" id="description"> | |
| </div> | |
| <div class="form-group"> | |
| <label for="license">ライセンス:</label> | |
| <input type="text" name="license" id="license" placeholder="e.g. apache-2.0"> | |
| </div> | |
| <div class="form-group"> | |
| <label for="port">アプリのポート:</label> | |
| <input type="text" name="port" id="port" placeholder="例: 7860"> | |
| </div> | |
| <div class="form-group"> | |
| <label for="title">スペースの名前 (表示用):</label> | |
| <input type="text" name="title" id="title"> | |
| </div> | |
| <div class="form-group"> | |
| <label for="emoji">絵文字 (表示アイコン):</label> | |
| <input type="text" name="emoji" id="emoji" placeholder="例: 🚀"> | |
| </div> | |
| <div class="form-group"> | |
| <label> | |
| <input type="checkbox" name="pinned" id="pinned"> | |
| 固定 (ピン止め) | |
| </label> | |
| </div> | |
| <button type="submit">スペースを作成</button> | |
| </form> | |
| <script> | |
| function openTab(evt, tabName) { | |
| const tabcontent = document.getElementsByClassName("tabcontent"); | |
| for (let i = 0; i < tabcontent.length; i++) { | |
| tabcontent[i].classList.remove("active"); | |
| } | |
| const tablinks = document.getElementsByClassName("tablinks"); | |
| for (let i = 0; i < tablinks.length; i++) { | |
| tablinks[i].classList.remove("active"); | |
| } | |
| document.getElementById(tabName).classList.add("active"); | |
| evt.currentTarget.classList.add("active"); | |
| } | |
| function startOAuth() { | |
| // 実際のアプリではHugging FaceのOAuthエンドポイントを使用 | |
| // デモ用に簡略化 | |
| const state = Math.random().toString(36).substring(2); | |
| 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}`; | |
| } | |
| document.addEventListener("DOMContentLoaded", () => { | |
| const tokenInput = document.getElementById("token"); | |
| // URLからOAuthトークンを取得 (デモ用) | |
| const urlParams = new URLSearchParams(window.location.search); | |
| const oauthToken = urlParams.get('oauth_token'); | |
| if (oauthToken) { | |
| tokenInput.value = oauthToken; | |
| } else { | |
| // 保存されたトークンを自動で読み込み | |
| tokenInput.value = localStorage.getItem("hf_token") || ""; | |
| } | |
| tokenInput.addEventListener("input", () => { | |
| localStorage.setItem("hf_token", tokenInput.value); | |
| }); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |
| ''', token=token) | |
| if __name__ == "__main__": | |
| app.run(host="0.0.0.0", port=7860) |