ginipick commited on
Commit
e05ca78
·
verified ·
1 Parent(s): c3cffa9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +47 -15
app.py CHANGED
@@ -1,14 +1,23 @@
1
-
2
  import os, re, time, json, datetime, requests, gradio as gr
3
 
4
  # ───────────────────── 1. Vercel API ─────────────────────
5
  TOKEN = os.getenv("SVR_TOKEN")
6
  TEAM = os.getenv("VERCEL_TEAM_ID")
 
 
 
 
 
 
 
7
  if not TOKEN:
8
  raise EnvironmentError("SVR_TOKEN 환경변수를 설정하세요.")
 
9
  API = "https://api.vercel.com"
10
  HEAD = {"Authorization": f"Bearer {TOKEN}"}
11
 
 
 
12
  # ───────────────────── 2. BEST 데이터 ────────────────────
13
  BEST_FILE, PER_PAGE = "best_games.json", 48
14
  def _init_best():
@@ -24,11 +33,9 @@ def _load_best():
24
  except Exception:
25
  return []
26
 
27
- # ───────────────────── 3. NEW: 6글자 vercel.app 배포 ─────
28
- # 3. NEW: 6글자 vercel.app 배포
29
-
30
- # 수정
31
- PAT = re.compile(r"^[a-z0-9-]{1,}\.vercel\.app$", re.I) # 6자 제한 완화
32
 
33
  def fetch_all(limit=200):
34
  """
@@ -41,14 +48,21 @@ def fetch_all(limit=200):
41
  return []
42
 
43
  params = {"limit": limit}
 
 
44
  if TEAM:
45
  params["teamId"] = TEAM
 
 
 
46
 
47
- print(f"Vercel API 호출: {API}/v6/deployments (limit={limit})")
48
 
49
  resp = requests.get(f"{API}/v6/deployments",
50
  headers=HEAD, params=params, timeout=30)
51
 
 
 
52
  if resp.status_code != 200:
53
  print(f"API 응답 오류: {resp.status_code}, {resp.text[:200]}")
54
  return []
@@ -138,36 +152,54 @@ def html(cards, pg, total):
138
  background:#f8f8f8;color:#666;font-size:14px;text-align:center;
139
  padding:20px;box-sizing:border-box;flex-direction:column;}
140
  .frame-error-icon {font-size:36px;margin-bottom:12px;color:#999;}
 
 
 
 
141
  </style>
142
 
143
  <script>
144
  // iframe 로드 오류 처리 함수
145
  function handleIframeError(iframe) {
146
  const container = iframe.parentNode;
 
147
  iframe.style.display = 'none';
148
  const errorDiv = document.createElement('div');
149
  errorDiv.className = 'frame-error';
150
  errorDiv.innerHTML = '<div class="frame-error-icon">🔒</div>' +
151
- '<div>이 콘텐츠는 iframe으로 표시할 수 없습니다.</div>' +
152
- '<div>보안 설정으로 인해 차단되었습니다.</div>' +
153
- '<div style="margin-top:10px;font-size:12px;color:#999;">아래 원본 링크를 클릭하여 탭에서 열어보세요.</div>';
154
  container.appendChild(errorDiv);
155
  }
156
 
157
  // iframe 로드 성공 처리
158
  function handleIframeLoad(iframe) {
159
- iframe.style.opacity = 1;
 
 
 
 
 
 
 
160
  }
161
 
162
  // iframe 로드 상태 확인
163
  document.addEventListener('DOMContentLoaded', function() {
164
- // 모든 iframe에 error 핸들러 추가
165
  const frames = document.querySelectorAll('iframe');
166
  frames.forEach(frame => {
 
167
  frame.addEventListener('error', function() {
168
  handleIframeError(this);
169
  });
170
 
 
 
 
 
 
171
  // 5초 후에도 로드되지 않으면 오류로 간주
172
  setTimeout(function() {
173
  if(frame.style.opacity !== '1') {
@@ -186,8 +218,8 @@ def html(cards, pg, total):
186
  <div class='hdr'><p class='ttl'>{c['title']}</p><p class='date'>{date}</p></div>
187
  <div class='frame'>
188
  <iframe src="{c['url']}" loading="lazy"
189
- allow="accelerometer; camera; encrypted-media; gyroscope;"
190
- onload="handleIframeLoad(this)"></iframe>
191
  </div>
192
  <div class='foot'><a class='link' href="{c['url']}" target="_blank">원본↗</a></div>
193
  </div>"""
@@ -234,4 +266,4 @@ def build():
234
  app = build()
235
 
236
  if __name__ == "__main__":
237
- app.launch()
 
 
1
  import os, re, time, json, datetime, requests, gradio as gr
2
 
3
  # ───────────────────── 1. Vercel API ─────────────────────
4
  TOKEN = os.getenv("SVR_TOKEN")
5
  TEAM = os.getenv("VERCEL_TEAM_ID")
6
+
7
+ # 팀 ID가 없는 경우 여기에 직접 입력할 수 있음
8
+ if not TEAM:
9
+ # 실제 팀 ID로 교체하세요
10
+ TEAM = "team_YOUR_ACTUAL_TEAM_ID_HERE"
11
+ print(f"환경 변수에 팀 ID가 없어서 하드코딩된 값을 사용합니다: {TEAM}")
12
+
13
  if not TOKEN:
14
  raise EnvironmentError("SVR_TOKEN 환경변수를 설정하세요.")
15
+
16
  API = "https://api.vercel.com"
17
  HEAD = {"Authorization": f"Bearer {TOKEN}"}
18
 
19
+ print(f"사용 중인 팀 ID: {TEAM if TEAM else '없음'}")
20
+
21
  # ───────────────────── 2. BEST 데이터 ────────────────────
22
  BEST_FILE, PER_PAGE = "best_games.json", 48
23
  def _init_best():
 
33
  except Exception:
34
  return []
35
 
36
+ # ───────────────────── 3. NEW: Vercel.app 배포 ─────
37
+ # 패턴 완화: 모든 vercel.app 도메인 허용
38
+ PAT = re.compile(r"^[a-z0-9-]{1,}\.vercel\.app$", re.I)
 
 
39
 
40
  def fetch_all(limit=200):
41
  """
 
48
  return []
49
 
50
  params = {"limit": limit}
51
+
52
+ # 팀 ID 처리 개선
53
  if TEAM:
54
  params["teamId"] = TEAM
55
+ print(f"팀 ID로 요청합니다: {TEAM}")
56
+ else:
57
+ print("경고: 팀 ID가 설정되지 않았습니다. 개인 프로젝트만 조회됩니다.")
58
 
59
+ print(f"Vercel API 호출: {API}/v6/deployments (params={params})")
60
 
61
  resp = requests.get(f"{API}/v6/deployments",
62
  headers=HEAD, params=params, timeout=30)
63
 
64
+ # 상세한 응답 정보 출력
65
+ print(f"API 응답 상태 코드: {resp.status_code}")
66
  if resp.status_code != 200:
67
  print(f"API 응답 오류: {resp.status_code}, {resp.text[:200]}")
68
  return []
 
152
  background:#f8f8f8;color:#666;font-size:14px;text-align:center;
153
  padding:20px;box-sizing:border-box;flex-direction:column;}
154
  .frame-error-icon {font-size:36px;margin-bottom:12px;color:#999;}
155
+ .frame-error-btn {margin-top:12px;padding:6px 16px;background:#4a6dd8;color:white;
156
+ border-radius:20px;font-size:12px;cursor:pointer;border:none;
157
+ transition:all 0.2s ease;}
158
+ .frame-error-btn:hover {background:#3a5dc8;transform:scale(1.05);}
159
  </style>
160
 
161
  <script>
162
  // iframe 로드 오류 처리 함수
163
  function handleIframeError(iframe) {
164
  const container = iframe.parentNode;
165
+ const url = iframe.src;
166
  iframe.style.display = 'none';
167
  const errorDiv = document.createElement('div');
168
  errorDiv.className = 'frame-error';
169
  errorDiv.innerHTML = '<div class="frame-error-icon">🔒</div>' +
170
+ '<div><strong>접근이 제한된 배포입니다</strong></div>' +
171
+ '<div>팀 설정으로 인해 iframe에서 표시할 수 없습니다</div>' +
172
+ '<button class="frame-error-btn" onclick="window.open(\'' + url + '\', \'_blank\')">새 탭에서 열기</button>';
173
  container.appendChild(errorDiv);
174
  }
175
 
176
  // iframe 로드 성공 처리
177
  function handleIframeLoad(iframe) {
178
+ try {
179
+ // CORS 오류 탐지 시도
180
+ iframe.contentWindow.location.href;
181
+ iframe.style.opacity = 1;
182
+ } catch (e) {
183
+ // CORS 오류가 발생하면 handleIframeError 호출
184
+ handleIframeError(iframe);
185
+ }
186
  }
187
 
188
  // iframe 로드 상태 확인
189
  document.addEventListener('DOMContentLoaded', function() {
190
+ // 모든 iframe에 핸들러 추가
191
  const frames = document.querySelectorAll('iframe');
192
  frames.forEach(frame => {
193
+ // 오류 이벤트 리스너
194
  frame.addEventListener('error', function() {
195
  handleIframeError(this);
196
  });
197
 
198
+ // 로드 이벤트 리스너
199
+ frame.addEventListener('load', function() {
200
+ handleIframeLoad(this);
201
+ });
202
+
203
  // 5초 후에도 로드되지 않으면 오류로 간주
204
  setTimeout(function() {
205
  if(frame.style.opacity !== '1') {
 
218
  <div class='hdr'><p class='ttl'>{c['title']}</p><p class='date'>{date}</p></div>
219
  <div class='frame'>
220
  <iframe src="{c['url']}" loading="lazy"
221
+ sandbox="allow-scripts allow-same-origin"
222
+ allow="accelerometer; camera; encrypted-media; gyroscope;"></iframe>
223
  </div>
224
  <div class='foot'><a class='link' href="{c['url']}" target="_blank">원본↗</a></div>
225
  </div>"""
 
266
  app = build()
267
 
268
  if __name__ == "__main__":
269
+ app.launch()