ginipick commited on
Commit
0dc8807
ยท
verified ยท
1 Parent(s): e05ca78

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +52 -13
app.py CHANGED
@@ -3,9 +3,12 @@ import os, re, time, json, datetime, requests, gradio as gr
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}")
@@ -16,7 +19,7 @@ if not TOKEN:
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
@@ -47,14 +50,22 @@ def fetch_all(limit=200):
47
  print("์˜ค๋ฅ˜: API ํ† ํฐ์ด ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค")
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
 
@@ -64,7 +75,10 @@ def fetch_all(limit=200):
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 []
69
 
70
  data = resp.json()
@@ -80,12 +94,15 @@ def fetch_all(limit=200):
80
  first = data["deployments"][0]
81
  print(f"\n์ฒซ ๋ฒˆ์งธ ๋ฐฐํฌ URL: {first.get('url')}")
82
  print(f"์ฒซ ๋ฒˆ์งธ ๋ฐฐํฌ Alias: {first.get('alias', [])}\n")
 
83
 
84
  # ํŒจํ„ด ์™„ํ™”: vercel.app์œผ๋กœ ๋๋‚˜๋Š” ๋ชจ๋“  URL ํ—ˆ์šฉ
85
  domain_pat = re.compile(r"\.vercel\.app$", re.I)
86
 
87
  games = []
88
  for d in data.get("deployments", []):
 
 
89
  if d.get("state") != "READY":
90
  continue
91
 
@@ -103,10 +120,26 @@ def fetch_all(limit=200):
103
  break
104
 
105
  if matched_url:
 
 
 
 
 
 
 
 
 
 
 
106
  games.append({
107
  "title": d.get("name", "(์ œ๋ชฉ ์—†์Œ)"),
108
  "url": f"https://{matched_url}",
109
- "ts": int(d.get("created", time.time() * 1000) / 1000)
 
 
 
 
 
110
  })
111
 
112
  print(f"vercel.app ๋„๋ฉ”์ธ ๋ฐฐํฌ {len(games)}๊ฐœ๋ฅผ ํ•„ํ„ฐ๋งํ–ˆ์Šต๋‹ˆ๋‹ค")
@@ -168,7 +201,7 @@ def html(cards, pg, total):
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
  }
@@ -176,11 +209,13 @@ def html(cards, pg, total):
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
  }
@@ -192,17 +227,20 @@ def html(cards, pg, total):
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') {
 
206
  handleIframeError(frame);
207
  }
208
  }, 5000);
@@ -218,7 +256,8 @@ def html(cards, pg, total):
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>
 
3
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 1. Vercel API โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
4
  TOKEN = os.getenv("SVR_TOKEN")
5
  TEAM = os.getenv("VERCEL_TEAM_ID")
6
+ TEAM_SLUG = os.getenv("VERCEL_TEAM_SLUG") # ํŒ€ ์Šฌ๋Ÿฌ๊ทธ๋„ ์ถ”๊ฐ€
7
 
8
+ # ํŒ€ ID ๋˜๋Š” ์Šฌ๋Ÿฌ๊ทธ ํ™•์ธ
9
+ if not TEAM and TEAM_SLUG:
10
+ print(f"ํŒ€ ID ๋Œ€์‹  ํŒ€ ์Šฌ๋Ÿฌ๊ทธ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค: {TEAM_SLUG}")
11
+ elif not TEAM:
12
  # ์‹ค์ œ ํŒ€ ID๋กœ ๊ต์ฒดํ•˜์„ธ์š”
13
  TEAM = "team_YOUR_ACTUAL_TEAM_ID_HERE"
14
  print(f"ํ™˜๊ฒฝ ๋ณ€์ˆ˜์— ํŒ€ ID๊ฐ€ ์—†์–ด์„œ ํ•˜๋“œ์ฝ”๋”ฉ๋œ ๊ฐ’์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค: {TEAM}")
 
19
  API = "https://api.vercel.com"
20
  HEAD = {"Authorization": f"Bearer {TOKEN}"}
21
 
22
+ print(f"์‚ฌ์šฉ ์ค‘์ธ ํŒ€ ์ •๋ณด: ID={TEAM if TEAM else '์—†์Œ'}, Slug={TEAM_SLUG if TEAM_SLUG else '์—†์Œ'}")
23
 
24
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 2. BEST ๋ฐ์ดํ„ฐ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
25
  BEST_FILE, PER_PAGE = "best_games.json", 48
 
50
  print("์˜ค๋ฅ˜: API ํ† ํฐ์ด ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค")
51
  return []
52
 
53
+ # API ๋ฌธ์„œ ๊ธฐ๋ฐ˜์œผ๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ ์„ค์ •
54
+ params = {
55
+ "limit": limit,
56
+ # ์ƒํƒœ๊ฐ€ "READY"์ธ ๋ฐฐํฌ๋งŒ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•œ ํ•„ํ„ฐ๋ง
57
+ "state": "READY"
58
+ }
59
 
60
+ # ํŒ€ ID ๋˜๋Š” ์Šฌ๋Ÿฌ๊ทธ ์ฒ˜๋ฆฌ
61
  if TEAM:
62
  params["teamId"] = TEAM
63
  print(f"ํŒ€ ID๋กœ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค: {TEAM}")
64
+ elif TEAM_SLUG:
65
+ params["slug"] = TEAM_SLUG
66
+ print(f"ํŒ€ ์Šฌ๋Ÿฌ๊ทธ๋กœ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค: {TEAM_SLUG}")
67
  else:
68
+ print("๊ฒฝ๊ณ : ํŒ€ ์ •๋ณด๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๊ฐœ์ธ ํ”„๋กœ์ ํŠธ๋งŒ ์กฐํšŒ๋ฉ๋‹ˆ๋‹ค.")
69
 
70
  print(f"Vercel API ํ˜ธ์ถœ: {API}/v6/deployments (params={params})")
71
 
 
75
  # ์ƒ์„ธํ•œ ์‘๋‹ต ์ •๋ณด ์ถœ๋ ฅ
76
  print(f"API ์‘๋‹ต ์ƒํƒœ ์ฝ”๋“œ: {resp.status_code}")
77
  if resp.status_code != 200:
78
+ error_msg = f"API ์‘๋‹ต ์˜ค๋ฅ˜: {resp.status_code}"
79
+ if hasattr(resp, 'text'):
80
+ error_msg += f", {resp.text[:200]}"
81
+ print(error_msg)
82
  return []
83
 
84
  data = resp.json()
 
94
  first = data["deployments"][0]
95
  print(f"\n์ฒซ ๋ฒˆ์งธ ๋ฐฐํฌ URL: {first.get('url')}")
96
  print(f"์ฒซ ๋ฒˆ์งธ ๋ฐฐํฌ Alias: {first.get('alias', [])}\n")
97
+ print(f"์ฒซ ๋ฒˆ์งธ ๋ฐฐํฌ ์ƒํƒœ: {first.get('state')}\n")
98
 
99
  # ํŒจํ„ด ์™„ํ™”: vercel.app์œผ๋กœ ๋๋‚˜๋Š” ๋ชจ๋“  URL ํ—ˆ์šฉ
100
  domain_pat = re.compile(r"\.vercel\.app$", re.I)
101
 
102
  games = []
103
  for d in data.get("deployments", []):
104
+ # ๋ฌธ์„œ์—์„œ๋Š” state ํ•„ํ„ฐ๋ง์„ URL ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ํ–ˆ์ง€๋งŒ,
105
+ # ์ถ”๊ฐ€ ๊ฒ€์ฆ์„ ์œ„ํ•ด ์—ฌ๊ธฐ์„œ๋„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค
106
  if d.get("state") != "READY":
107
  continue
108
 
 
120
  break
121
 
122
  if matched_url:
123
+ # ์‹œ๊ฐ„ ํ˜•์‹ ๋ณ€ํ™˜ ๊ฐœ์„ 
124
+ created_time = d.get("created")
125
+ if created_time:
126
+ # milliseconds to seconds ๋ณ€ํ™˜
127
+ try:
128
+ ts = int(created_time / 1000)
129
+ except:
130
+ ts = int(time.time())
131
+ else:
132
+ ts = int(time.time())
133
+
134
  games.append({
135
  "title": d.get("name", "(์ œ๋ชฉ ์—†์Œ)"),
136
  "url": f"https://{matched_url}",
137
+ "ts": ts,
138
+ "meta": { # ์ถ”๊ฐ€ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ €์žฅ
139
+ "projectId": d.get("projectId"),
140
+ "target": d.get("target"),
141
+ "deploymentId": d.get("uid")
142
+ }
143
  })
144
 
145
  print(f"vercel.app ๋„๋ฉ”์ธ ๋ฐฐํฌ {len(games)}๊ฐœ๋ฅผ ํ•„ํ„ฐ๋งํ–ˆ์Šต๋‹ˆ๋‹ค")
 
201
  errorDiv.className = 'frame-error';
202
  errorDiv.innerHTML = '<div class="frame-error-icon">๐Ÿ”’</div>' +
203
  '<div><strong>์ ‘๊ทผ์ด ์ œํ•œ๋œ ๋ฐฐํฌ์ž…๋‹ˆ๋‹ค</strong></div>' +
204
+ '<div>๋ณด์•ˆ ์„ค์ •์œผ๋กœ ์ธํ•ด iframe์—์„œ ํ‘œ์‹œํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค</div>' +
205
  '<button class="frame-error-btn" onclick="window.open(\'' + url + '\', \'_blank\')">์ƒˆ ํƒญ์—์„œ ์—ด๊ธฐ</button>';
206
  container.appendChild(errorDiv);
207
  }
 
209
  // iframe ๋กœ๋“œ ์„ฑ๊ณต ์ฒ˜๋ฆฌ
210
  function handleIframeLoad(iframe) {
211
  try {
212
+ // CORS ์˜ค๋ฅ˜ ํƒ์ง€ ์‹œ๋„ - ๋‹ค๋ฅธ ๋„๋ฉ”์ธ์˜ iframe ์ ‘๊ทผ ์‹œ ์˜ˆ์™ธ ๋ฐœ์ƒ
213
+ if(iframe.contentWindow.location.href) {
214
+ iframe.style.opacity = 1;
215
+ }
216
  } catch (e) {
217
  // CORS ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด handleIframeError ํ˜ธ์ถœ
218
+ console.log("CORS ์˜ค๋ฅ˜ ๋˜๋Š” ์ ‘๊ทผ ๊ฑฐ๋ถ€๋จ:", e);
219
  handleIframeError(iframe);
220
  }
221
  }
 
227
  frames.forEach(frame => {
228
  // ์˜ค๋ฅ˜ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ
229
  frame.addEventListener('error', function() {
230
+ console.log("iframe ๋กœ๋“œ ์˜ค๋ฅ˜ ๋ฐœ์ƒ");
231
  handleIframeError(this);
232
  });
233
 
234
  // ๋กœ๋“œ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ
235
  frame.addEventListener('load', function() {
236
+ console.log("iframe ๋กœ๋“œ๋จ:", this.src);
237
  handleIframeLoad(this);
238
  });
239
 
240
  // 5์ดˆ ํ›„์—๋„ ๋กœ๋“œ๋˜์ง€ ์•Š์œผ๋ฉด ์˜ค๋ฅ˜๋กœ ๊ฐ„์ฃผ
241
  setTimeout(function() {
242
  if(frame.style.opacity !== '1') {
243
+ console.log("iframe ๋กœ๋“œ ํƒ€์ž„์•„์›ƒ:", frame.src);
244
  handleIframeError(frame);
245
  }
246
  }, 5000);
 
256
  <div class='hdr'><p class='ttl'>{c['title']}</p><p class='date'>{date}</p></div>
257
  <div class='frame'>
258
  <iframe src="{c['url']}" loading="lazy"
259
+ referrerpolicy="no-referrer"
260
+ sandbox="allow-scripts allow-same-origin allow-forms"
261
  allow="accelerometer; camera; encrypted-media; gyroscope;"></iframe>
262
  </div>
263
  <div class='foot'><a class='link' href="{c['url']}" target="_blank">์›๋ณธโ†—</a></div>