From 44b72116533ee7cfb6d5ec76387ce57f9442acc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=83=81=ED=98=B8=20Sangho=20Park?= Date: Wed, 12 Aug 2026 19:42:14 +0900 Subject: [PATCH] refine: colab tunnel cell - skip poll when cloudflared missing, softer verify message Reviewer feedback: guard the 120s URL poll with shutil.which(cloudflared) so the failure case returns immediately; merge import re into the cell top import; note Cloudflare browser-check interstitials can fail urllib verification even when the link works in a real browser. --- notebooks/luke-scribe-colab.ipynb | 2 +- scripts/build_colab_notebook.py | 24 +++++++++++++----------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/notebooks/luke-scribe-colab.ipynb b/notebooks/luke-scribe-colab.ipynb index c3c3c61..75038ab 100644 --- a/notebooks/luke-scribe-colab.ipynb +++ b/notebooks/luke-scribe-colab.ipynb @@ -140,7 +140,7 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": "# 10) 서버 기동 (in-proc 큐, 백그라운드 — nohup)\n# 이전 실행에서 남은 서버가 포트 8000을 점유하면 새 키를 모른 채 401이 난다.\n# → 먼저 기존 서버를 모두 종료하고, 키 파일 경로를 명시해 재시작한다.\nimport subprocess, os, time, urllib.request, urllib.error\n\nassert 'RAW_KEY' in globals(), '셀 9(키 생성)를 먼저 실행하세요'\n\nsubprocess.run(['pkill', '-f', 'luke-scribe serve'], capture_output=True)\ntime.sleep(2)\n\n# 서버가 읽을 키 파일/큐를 명시 (cwd 의존 제거)\nos.environ['LUKESCRIBE_API_KEY_FILE'] = '/content/api_keys.json'\nos.environ['LUKESCRIBE_QUEUE_BACKEND'] = 'inproc'\n# in-proc 서버가 자체 워커 스레드로 큐를 소비 (업로드 → 완료까지 API 단독 처리)\nos.environ['LUKESCRIBE_AUTO_WORKER'] = 'true'\n# Cloudflare Quick Tunnel — 외부 접속 링크 발급 (cloudflared는 셀 3에서 설치)\nos.environ['LUKESCRIBE_TUNNEL'] = 'cloudflare'\n\n!mkdir -p /content/logs\n!nohup luke-scribe serve --port 8000 > /content/logs/server.log 2>&1 &\n\nok = False\nfor _ in range(40):\n try:\n urllib.request.urlopen('http://localhost:8000/health', timeout=1)\n ok = True\n break\n except Exception:\n time.sleep(1)\nif not ok:\n print('서버 기동 실패 — 로그:')\n print(open('/content/logs/server.log').read()[-1500:])\n raise SystemExit('서버 기동 실패')\n\n# 키 인증 검증 — RAW_KEY로 /v1/jobs 호출 → 200이어야 함 (401이면 종료)\nreq = urllib.request.Request('http://localhost:8000/v1/jobs', headers={'X-API-Key': RAW_KEY})\ntry:\n resp = urllib.request.urlopen(req, timeout=5)\n print('서버 기동 OK + 키 인증 OK (HTTP', resp.status, ')')\nexcept urllib.error.HTTPError as e:\n print('키 인증 실패 (HTTP', e.code, ') — 서버 로그:')\n print(open('/content/logs/server.log').read()[-1500:])\n raise SystemExit('키 인증 실패 — RAW_KEY 캡처/키 파일 확인')\n\n# ── Cloudflare 터널 URL 캡처 + 외부 접속 검증 (서버 로그의 trycloudflare URL) ──\nimport re\nTUNNEL_URL = None\nfor _ in range(60):\n log = open('/content/logs/server.log', encoding='utf-8', errors='replace').read()\n m = re.search(r'https://[a-z0-9-]+\\.trycloudflare\\.com', log)\n if m:\n TUNNEL_URL = m.group(0)\n break\n time.sleep(2)\n\nif TUNNEL_URL:\n print('🌐 Cloudflare 터널 (외부 접속):', TUNNEL_URL)\n print(' 대시보드 (API Docs):', TUNNEL_URL + '/docs')\n print(' 상태 (health):', TUNNEL_URL + '/health')\n try:\n ext = urllib.request.urlopen(TUNNEL_URL + '/health', timeout=15)\n print(' 외부 접속 검증 OK (HTTP', ext.status, ')')\n except Exception as exc:\n print(' 외부 접속 검증 실패:', exc)\n print(' ※ 임시 링크 — Colab 세션 종료 시 닫힘. API 호출에는 RAW_KEY(X-API-Key) 필요.')\nelse:\n print('터널 URL 미감지 (cloudflared 미설치/연결 실패) — 서버 로그:')\n print(open('/content/logs/server.log').read()[-1200:])\n print('로컬(8000)에서 계속 사용할 수 있습니다.')\n" + "source": "# 10) 서버 기동 (in-proc 큐, 백그라운드 — nohup)\n# 이전 실행에서 남은 서버가 포트 8000을 점유하면 새 키를 모른 채 401이 난다.\n# → 먼저 기존 서버를 모두 종료하고, 키 파일 경로를 명시해 재시작한다.\nimport subprocess, os, re, shutil, time, urllib.request, urllib.error\n\nassert 'RAW_KEY' in globals(), '셀 9(키 생성)를 먼저 실행하세요'\n\nsubprocess.run(['pkill', '-f', 'luke-scribe serve'], capture_output=True)\ntime.sleep(2)\n\n# 서버가 읽을 키 파일/큐를 명시 (cwd 의존 제거)\nos.environ['LUKESCRIBE_API_KEY_FILE'] = '/content/api_keys.json'\nos.environ['LUKESCRIBE_QUEUE_BACKEND'] = 'inproc'\n# in-proc 서버가 자체 워커 스레드로 큐를 소비 (업로드 → 완료까지 API 단독 처리)\nos.environ['LUKESCRIBE_AUTO_WORKER'] = 'true'\n# Cloudflare Quick Tunnel — 외부 접속 링크 발급 (cloudflared는 셀 3에서 설치)\nos.environ['LUKESCRIBE_TUNNEL'] = 'cloudflare'\n\n!mkdir -p /content/logs\n!nohup luke-scribe serve --port 8000 > /content/logs/server.log 2>&1 &\n\nok = False\nfor _ in range(40):\n try:\n urllib.request.urlopen('http://localhost:8000/health', timeout=1)\n ok = True\n break\n except Exception:\n time.sleep(1)\nif not ok:\n print('서버 기동 실패 — 로그:')\n print(open('/content/logs/server.log').read()[-1500:])\n raise SystemExit('서버 기동 실패')\n\n# 키 인증 검증 — RAW_KEY로 /v1/jobs 호출 → 200이어야 함 (401이면 종료)\nreq = urllib.request.Request('http://localhost:8000/v1/jobs', headers={'X-API-Key': RAW_KEY})\ntry:\n resp = urllib.request.urlopen(req, timeout=5)\n print('서버 기동 OK + 키 인증 OK (HTTP', resp.status, ')')\nexcept urllib.error.HTTPError as e:\n print('키 인증 실패 (HTTP', e.code, ') — 서버 로그:')\n print(open('/content/logs/server.log').read()[-1500:])\n raise SystemExit('키 인증 실패 — RAW_KEY 캡처/키 파일 확인')\n\n# ── Cloudflare 터널 URL 캡처 + 외부 접속 검증 (서버 로그의 trycloudflare URL) ──\nTUNNEL_URL = None\nif shutil.which('cloudflared'):\n for _ in range(60):\n log = open('/content/logs/server.log', encoding='utf-8', errors='replace').read()\n m = re.search(r'https://[a-z0-9-]+\\.trycloudflare\\.com', log)\n if m:\n TUNNEL_URL = m.group(0)\n break\n time.sleep(2)\nelse:\n print('cloudflared 미설치 (셀 3 다운로드 실패) — 터널 생략, 로컬(8000) 계속 사용')\n\nif TUNNEL_URL:\n print('🌐 Cloudflare 터널 (외부 접속):', TUNNEL_URL)\n print(' 대시보드 (API Docs):', TUNNEL_URL + '/docs')\n print(' 상태 (health):', TUNNEL_URL + '/health')\n try:\n ext = urllib.request.urlopen(TUNNEL_URL + '/health', timeout=15)\n print(' 외부 접속 검증 OK (HTTP', ext.status, ')')\n except Exception as exc:\n print(' 외부 접속 검증 실패 (브라우저에선 열릴 수 있음):', exc)\n print(' ※ 임시 링크 — Colab 세션 종료 시 닫힘. API 호출에는 RAW_KEY(X-API-Key) 필요.')\nelse:\n print('터널 URL 미감지 — 서버 로그:')\n print(open('/content/logs/server.log').read()[-1200:])\n print('로컬(8000)에서 계속 사용할 수 있습니다.')\n" }, { "cell_type": "code", diff --git a/scripts/build_colab_notebook.py b/scripts/build_colab_notebook.py index 999c5c8..abec94a 100644 --- a/scripts/build_colab_notebook.py +++ b/scripts/build_colab_notebook.py @@ -214,7 +214,7 @@ def cells() -> list[dict]: "# 10) 서버 기동 (in-proc 큐, 백그라운드 — nohup)\n" "# 이전 실행에서 남은 서버가 포트 8000을 점유하면 새 키를 모른 채 401이 난다.\n" "# → 먼저 기존 서버를 모두 종료하고, 키 파일 경로를 명시해 재시작한다.\n" - "import subprocess, os, time, urllib.request, urllib.error\n" + "import subprocess, os, re, shutil, time, urllib.request, urllib.error\n" "\n" "assert 'RAW_KEY' in globals(), '셀 9(키 생성)를 먼저 실행하세요'\n" "\n" @@ -256,15 +256,17 @@ def cells() -> list[dict]: " raise SystemExit('키 인증 실패 — RAW_KEY 캡처/키 파일 확인')\n" "\n" "# ── Cloudflare 터널 URL 캡처 + 외부 접속 검증 (서버 로그의 trycloudflare URL) ──\n" - "import re\n" "TUNNEL_URL = None\n" - "for _ in range(60):\n" - " log = open('/content/logs/server.log', encoding='utf-8', errors='replace').read()\n" - " m = re.search(r'https://[a-z0-9-]+\\.trycloudflare\\.com', log)\n" - " if m:\n" - " TUNNEL_URL = m.group(0)\n" - " break\n" - " time.sleep(2)\n" + "if shutil.which('cloudflared'):\n" + " for _ in range(60):\n" + " log = open('/content/logs/server.log', encoding='utf-8', errors='replace').read()\n" + " m = re.search(r'https://[a-z0-9-]+\\.trycloudflare\\.com', log)\n" + " if m:\n" + " TUNNEL_URL = m.group(0)\n" + " break\n" + " time.sleep(2)\n" + "else:\n" + " print('cloudflared 미설치 (셀 3 다운로드 실패) — 터널 생략, 로컬(8000) 계속 사용')\n" "\n" "if TUNNEL_URL:\n" " print('🌐 Cloudflare 터널 (외부 접속):', TUNNEL_URL)\n" @@ -274,10 +276,10 @@ def cells() -> list[dict]: " ext = urllib.request.urlopen(TUNNEL_URL + '/health', timeout=15)\n" " print(' 외부 접속 검증 OK (HTTP', ext.status, ')')\n" " except Exception as exc:\n" - " print(' 외부 접속 검증 실패:', exc)\n" + " print(' 외부 접속 검증 실패 (브라우저에선 열릴 수 있음):', exc)\n" " print(' ※ 임시 링크 — Colab 세션 종료 시 닫힘. API 호출에는 RAW_KEY(X-API-Key) 필요.')\n" "else:\n" - " print('터널 URL 미감지 (cloudflared 미설치/연결 실패) — 서버 로그:')\n" + " print('터널 URL 미감지 — 서버 로그:')\n" " print(open('/content/logs/server.log').read()[-1200:])\n" " print('로컬(8000)에서 계속 사용할 수 있습니다.')\n" ),