Update app.py
Browse files
app.py
CHANGED
|
@@ -1,205 +1,93 @@
|
|
| 1 |
import os
|
| 2 |
import gradio as gr
|
| 3 |
-
from authlib.integrations.starlette_client import OAuth, OAuthError
|
| 4 |
-
from starlette.config import Config
|
| 5 |
-
from starlette.middleware.sessions import SessionMiddleware
|
| 6 |
-
import secrets
|
| 7 |
-
import json
|
| 8 |
|
| 9 |
-
# 환경 변수에서
|
| 10 |
GOOGLE_CLIENT_ID = os.environ.get("GOOGLE_CLIENT_ID", "your-client-id")
|
| 11 |
GOOGLE_CLIENT_SECRET = os.environ.get("GOOGLE_CLIENT_SECRET", "your-secret")
|
| 12 |
|
| 13 |
-
#
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
oauth = OAuth(config)
|
| 26 |
-
oauth.register(
|
| 27 |
-
name='google',
|
| 28 |
-
server_metadata_url='https://accounts.google.com/.well-known/openid-configuration',
|
| 29 |
-
client_kwargs={'scope': 'openid email profile'}
|
| 30 |
-
)
|
| 31 |
-
|
| 32 |
-
# 전역 변수로 사용자 정보 저장 (실제 프로덕션에서는 세션 사용 권장)
|
| 33 |
-
current_user = None
|
| 34 |
-
|
| 35 |
-
def get_login_url():
|
| 36 |
-
"""Google 로그인 URL 생성"""
|
| 37 |
-
redirect_uri = f"{SPACE_HOST}/auth/callback"
|
| 38 |
-
return oauth.google.authorize_redirect(redirect_uri).headers['Location']
|
| 39 |
-
|
| 40 |
-
def handle_user_info(info):
|
| 41 |
-
"""사용자 정보 처리"""
|
| 42 |
-
global current_user
|
| 43 |
-
current_user = info
|
| 44 |
-
return info
|
| 45 |
-
|
| 46 |
-
def logout():
|
| 47 |
-
"""로그아웃 처리"""
|
| 48 |
-
global current_user
|
| 49 |
-
current_user = None
|
| 50 |
-
return None, gr.update(visible=True), gr.update(visible=False)
|
| 51 |
-
|
| 52 |
-
def create_oauth_app():
|
| 53 |
-
with gr.Blocks(title="Google OAuth Gradio 앱") as demo:
|
| 54 |
-
gr.Markdown("# Google OAuth 통합 Gradio 앱")
|
| 55 |
-
|
| 56 |
-
# 상태 관리를 위한 State
|
| 57 |
-
user_info_state = gr.State(value=None)
|
| 58 |
-
|
| 59 |
-
# 로그인 전 화면
|
| 60 |
-
with gr.Column(visible=True) as login_section:
|
| 61 |
-
gr.Markdown("### 환경 설정 상태")
|
| 62 |
-
gr.Markdown(f"✅ Client ID 설정됨" if GOOGLE_CLIENT_ID != "your-client-id" else "❌ Client ID 미설정")
|
| 63 |
-
gr.Markdown(f"✅ Client Secret 설정됨" if GOOGLE_CLIENT_SECRET != "your-secret" else "❌ Client Secret 미설정")
|
| 64 |
-
|
| 65 |
-
gr.Markdown("---")
|
| 66 |
-
gr.Markdown("### Google 로그인이 필요합니다")
|
| 67 |
-
|
| 68 |
-
if GOOGLE_CLIENT_ID != "your-client-id" and GOOGLE_CLIENT_SECRET != "your-secret":
|
| 69 |
-
login_btn = gr.Button("🔐 Google로 로그인", variant="primary", size="lg")
|
| 70 |
-
gr.Markdown("""
|
| 71 |
-
**참고:** Hugging Face Spaces에서는 OAuth 리다이렉트가 제한적일 수 있습니다.
|
| 72 |
-
|
| 73 |
-
OAuth 설정 방법:
|
| 74 |
-
1. [Google Cloud Console](https://console.cloud.google.com/)에서 OAuth 2.0 클라이언트 생성
|
| 75 |
-
2. 승인된 리다이렉트 URI에 추가:
|
| 76 |
-
- `https://[space-name].hf.space/auth/callback`
|
| 77 |
-
- `http://localhost:7860/auth/callback` (로컬 테스트용)
|
| 78 |
-
3. Hugging Face Space Settings에서 환경변수 설정
|
| 79 |
-
""")
|
| 80 |
-
else:
|
| 81 |
-
gr.Markdown("""
|
| 82 |
-
⚠️ **OAuth 설정이 필요합니다**
|
| 83 |
-
|
| 84 |
-
1. 환경변수 `GOOGLE_CLIENT_ID`와 `GOOGLE_CLIENT_SECRET` 설정
|
| 85 |
-
2. Hugging Face Space Settings > Variables and secrets에서 추가
|
| 86 |
-
""")
|
| 87 |
-
|
| 88 |
-
# 로그인 후 화면
|
| 89 |
-
with gr.Column(visible=False) as user_section:
|
| 90 |
-
gr.Markdown("### 👤 로그인된 사용자 정보")
|
| 91 |
-
|
| 92 |
-
with gr.Row():
|
| 93 |
-
user_name = gr.Textbox(label="이름", interactive=False)
|
| 94 |
-
user_email = gr.Textbox(label="이메일", interactive=False)
|
| 95 |
-
|
| 96 |
-
user_picture = gr.Image(label="프로필 사진", type="filepath", interactive=False, height=100)
|
| 97 |
-
|
| 98 |
-
logout_btn = gr.Button("로그아웃", variant="secondary")
|
| 99 |
-
|
| 100 |
-
gr.Markdown("---")
|
| 101 |
-
gr.Markdown("### 🎉 환영합니다! 이제 앱을 사용할 수 있습니다.")
|
| 102 |
-
|
| 103 |
-
# 로그인 후 사용 가능한 기능들
|
| 104 |
-
with gr.Row():
|
| 105 |
-
name_input = gr.Textbox(label="인사할 이름", placeholder="친구 이름")
|
| 106 |
-
greeting_output = gr.Textbox(label="개인화된 인사말")
|
| 107 |
-
|
| 108 |
-
greet_btn = gr.Button("인사하기")
|
| 109 |
-
|
| 110 |
-
def personalized_greet(name, user_info):
|
| 111 |
-
if user_info and name:
|
| 112 |
-
user_name = user_info.get('name', '사용자')
|
| 113 |
-
return f"안녕하세요 {name}님! {user_name}님이 인사드립니다! 😊"
|
| 114 |
-
return "이름을 입력해주세요."
|
| 115 |
-
|
| 116 |
-
greet_btn.click(
|
| 117 |
-
fn=personalized_greet,
|
| 118 |
-
inputs=[name_input, user_info_state],
|
| 119 |
-
outputs=greeting_output
|
| 120 |
-
)
|
| 121 |
|
| 122 |
-
#
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
'name': '테스트 사용자',
|
| 127 |
-
'email': 'test@example.com',
|
| 128 |
-
'picture': 'https://via.placeholder.com/100'
|
| 129 |
-
}
|
| 130 |
-
return (
|
| 131 |
-
fake_user_info, # user_info_state
|
| 132 |
-
gr.update(visible=False), # login_section
|
| 133 |
-
gr.update(visible=True), # user_section
|
| 134 |
-
fake_user_info.get('name', ''), # user_name
|
| 135 |
-
fake_user_info.get('email', ''), # user_email
|
| 136 |
-
fake_user_info.get('picture', '') # user_picture
|
| 137 |
-
)
|
| 138 |
|
| 139 |
-
|
| 140 |
-
return (
|
| 141 |
-
None, # user_info_state
|
| 142 |
-
gr.update(visible=True), # login_section
|
| 143 |
-
gr.update(visible=False), # user_section
|
| 144 |
-
'', # user_name
|
| 145 |
-
'', # user_email
|
| 146 |
-
None # user_picture
|
| 147 |
-
)
|
| 148 |
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 154 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 161 |
|
| 162 |
-
# OAuth 콜백 처리를 위한 숨겨진 컴포넌트
|
| 163 |
gr.Markdown("""
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
"""
|
| 172 |
|
| 173 |
return demo
|
| 174 |
|
| 175 |
-
#
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
demo.
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
redirect_uri = f"{SPACE_HOST}/auth/callback"
|
| 185 |
-
return await oauth.google.authorize_redirect(request, redirect_uri)
|
| 186 |
-
|
| 187 |
-
@demo.app.get("/auth/callback")
|
| 188 |
-
async def auth_callback(request):
|
| 189 |
-
try:
|
| 190 |
-
token = await oauth.google.authorize_access_token(request)
|
| 191 |
-
user = token.get('userinfo')
|
| 192 |
-
if user:
|
| 193 |
-
request.session['user'] = user
|
| 194 |
-
return {"status": "success", "user": user}
|
| 195 |
-
except OAuthError as e:
|
| 196 |
-
return {"status": "error", "message": str(e)}
|
| 197 |
-
|
| 198 |
-
# requirements.txt에 추가 필요:
|
| 199 |
-
# gradio
|
| 200 |
-
# authlib
|
| 201 |
-
# httpx
|
| 202 |
-
# starlette
|
| 203 |
|
| 204 |
if __name__ == "__main__":
|
| 205 |
-
demo.launch(server_name="0.0.0.0", server_port=
|
|
|
|
| 1 |
import os
|
| 2 |
import gradio as gr
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
|
| 4 |
+
# 환경 변수에서 설정 가져오기
|
| 5 |
GOOGLE_CLIENT_ID = os.environ.get("GOOGLE_CLIENT_ID", "your-client-id")
|
| 6 |
GOOGLE_CLIENT_SECRET = os.environ.get("GOOGLE_CLIENT_SECRET", "your-secret")
|
| 7 |
|
| 8 |
+
# Gradio의 기본 인증 사용 (OAuth 대신)
|
| 9 |
+
USERS = os.environ.get("USERS", "user1:pass1,user2:pass2").split(",")
|
| 10 |
+
auth_dict = {}
|
| 11 |
+
for user in USERS:
|
| 12 |
+
if ":" in user:
|
| 13 |
+
username, password = user.split(":", 1)
|
| 14 |
+
auth_dict[username] = password
|
| 15 |
+
|
| 16 |
+
def create_app():
|
| 17 |
+
with gr.Blocks(title="Google 로그인 스타일 앱") as demo:
|
| 18 |
+
gr.Markdown("# 🔐 로그인이 필요한 Gradio 앱")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
+
# 환경 설정 상태
|
| 21 |
+
gr.Markdown("### 환경 설정 상태")
|
| 22 |
+
gr.Markdown(f"✅ Client ID 설정됨" if GOOGLE_CLIENT_ID != "your-client-id" else "❌ Client ID 미설정")
|
| 23 |
+
gr.Markdown(f"✅ Client Secret 설정됨" if GOOGLE_CLIENT_SECRET != "your-secret" else "❌ Client Secret 미설정")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
+
gr.Markdown("---")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
|
| 27 |
+
# 메인 기능
|
| 28 |
+
gr.Markdown("### 👋 환영합니다!")
|
| 29 |
+
gr.Markdown("이 앱은 로그인된 사용자만 사용할 수 있습니다.")
|
| 30 |
+
|
| 31 |
+
with gr.Row():
|
| 32 |
+
name_input = gr.Textbox(
|
| 33 |
+
label="이름을 입력하세요",
|
| 34 |
+
placeholder="홍길동",
|
| 35 |
+
info="인사받을 이름을 입력하세요"
|
| 36 |
)
|
| 37 |
+
output = gr.Textbox(label="인사말")
|
| 38 |
+
|
| 39 |
+
greet_btn = gr.Button("인사하기", variant="primary")
|
| 40 |
+
|
| 41 |
+
def greet(name):
|
| 42 |
+
if name:
|
| 43 |
+
return f"안녕하세요, {name}님! 로그인해주셔서 감사합니다! 😊"
|
| 44 |
+
return "이름을 입력해주세요."
|
| 45 |
|
| 46 |
+
greet_btn.click(fn=greet, inputs=name_input, outputs=output)
|
| 47 |
+
|
| 48 |
+
gr.Markdown("---")
|
| 49 |
+
|
| 50 |
+
# Google OAuth 안내
|
| 51 |
+
gr.Markdown("""
|
| 52 |
+
### 📝 Google OAuth 통합 안내
|
| 53 |
+
|
| 54 |
+
Hugging Face Spaces는 서버사이드 OAuth를 완전히 지원하지 않습니다.
|
| 55 |
+
|
| 56 |
+
**대안:**
|
| 57 |
+
1. **Gradio 기본 인증 사용** (현재 적용됨)
|
| 58 |
+
- Space Settings에서 `USERS` 환경변수 설정
|
| 59 |
+
- 예: `USERS=admin:password123,user:pass456`
|
| 60 |
+
|
| 61 |
+
2. **Google OAuth 버튼 (외부 링크)**
|
| 62 |
+
- 아래 버튼을 클릭하면 Google 로그인 페이지로 이동합니다
|
| 63 |
+
""")
|
| 64 |
+
|
| 65 |
+
# Google OAuth URL 생성 (클라이언트 사이드)
|
| 66 |
+
if GOOGLE_CLIENT_ID != "your-client-id":
|
| 67 |
+
oauth_url = f"https://accounts.google.com/o/oauth2/v2/auth?client_id={GOOGLE_CLIENT_ID}&redirect_uri=https://your-space.hf.space&response_type=token&scope=email%20profile"
|
| 68 |
+
gr.Markdown(f"[🔗 Google로 로그인]({oauth_url})")
|
| 69 |
|
|
|
|
| 70 |
gr.Markdown("""
|
| 71 |
+
### 🔧 설정 방법
|
| 72 |
+
|
| 73 |
+
1. **Hugging Face Space Settings > Variables**에서:
|
| 74 |
+
- `USERS`: 사용자명:비밀번호 형식으로 설정
|
| 75 |
+
- 예: `admin:mypassword,john:doe123`
|
| 76 |
+
|
| 77 |
+
2. **앱 재시작 후 로그인 창이 표시됩니다**
|
| 78 |
+
""")
|
| 79 |
|
| 80 |
return demo
|
| 81 |
|
| 82 |
+
# 인증이 있는 앱 생성
|
| 83 |
+
if auth_dict:
|
| 84 |
+
# 사용자 인증이 설정된 경우
|
| 85 |
+
demo = create_app()
|
| 86 |
+
demo.auth = list(auth_dict.items())
|
| 87 |
+
demo.auth_message = "Google 계정 대신 설정된 사용자명/비밀번호를 사용하세요"
|
| 88 |
+
else:
|
| 89 |
+
# 인증이 설정되지 않은 경우
|
| 90 |
+
demo = create_app()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
|
| 92 |
if __name__ == "__main__":
|
| 93 |
+
demo.launch(server_name="0.0.0.0", server_port=786
|