import gradio as gr import requests import logging import datetime import threading import time from pytz import timezone from collections import defaultdict logging.basicConfig(level=logging.INFO) # 로그인 인증 정보 VALID_USERNAME = "ets2020" VALID_PASSWORD = "1089" # 로그인 상태 관리 is_logged_in = False # 로그아웃 처리 함수 def process_logout(): global is_logged_in is_logged_in = False return gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), None # 아이디별 이메일 및 비밀번호 정보 (실제 배포 시에는 보안을 위해 다른 방식으로 관리하는 것이 좋습니다) CREDENTIALS = { "whispersound": ["*****", "*****"], "happydoggg": ["*****", "*****"], "magictreee": ["*****", "*****"], "fastcarr": ["*****", "*****"], "corpcorp1": ["*****", "*****"], "rara527": ["*****", "*****"], "bluskyyy": ["*****", "*****"], "ssboost": ["*****", "*****"], "ekdhstldhs": ["*****", "*****"], } # 서비스 목록 TARGETS = [ {"name": "수동블로그-일반+생활건강", "url": "https://huggingface.co/spaces/whispersound/kmkm_1-1"}, {"name": "수동블로그-여행", "url": "https://huggingface.co/spaces/whispersound/kmkm_2-1"}, {"name": "수동블로그-상품리뷰", "url": "https://huggingface.co/spaces/whispersound/kmkm_3-1"}, {"name": "수동블로그-핵심기능", "url": "https://huggingface.co/spaces/whispersound/kmkm_4-1"}, {"name": "수동블로그-방문후기", "url": "https://huggingface.co/spaces/whispersound/kmkm_5-1"}, {"name": "자동블로그-일반", "url": "https://huggingface.co/spaces/whispersound/mmmha_1-1"}, {"name": "자동블로그-여행", "url": "https://huggingface.co/spaces/whispersound/mmmha_2-1"}, {"name": "자동블로그-상품리뷰", "url": "https://huggingface.co/spaces/whispersound/mmmha_3-1"}, {"name": "유튜브-요약", "url": "https://huggingface.co/spaces/whispersound/ybyb-r-1"}, {"name": "유튜브-블로그(일반)", "url": "https://huggingface.co/spaces/whispersound/ybyb_1-1"}, {"name": "유튜브-블로그(생활건강)", "url": "https://huggingface.co/spaces/whispersound/ybyb_1-2-1"}, {"name": "유튜브-블로그(여행)", "url": "https://huggingface.co/spaces/whispersound/ybyb_2-1"}, {"name": "유튜브-블로그(상품리뷰)", "url": "https://huggingface.co/spaces/whispersound/ybyb_3-1"}, {"name": "이커머스-카피라이팅", "url": "https://huggingface.co/spaces/whispersound/LuLu_cp_1_R-1"}, {"name": "이커머-상세페이지기획", "url": "https://huggingface.co/spaces/whispersound/plpl_1-1"}, {"name": "수동블로그-일반+생활건강(제휴용)", "url": "https://huggingface.co/spaces/whispersound/kmkm_1-4"}, {"name": "수동블로그-상품리뷰(제휴용)", "url": "https://huggingface.co/spaces/whispersound/kmkm_3-4"}, {"name": "수동블로그-핵심기능(제휴용)", "url": "https://huggingface.co/spaces/whispersound/kmkm_4-4"}, {"name": "컨트롤-수동블로그", "url": "https://huggingface.co/spaces/fastcarr/ldleeer1-1"}, {"name": "컨트롤-자동블로그", "url": "https://huggingface.co/spaces/fastcarr/lleryr1-1"}, {"name": "컨트롤-유튜브", "url": "https://huggingface.co/spaces/fastcarr/kketywe1-1"}, {"name": "컨트롤-카피상세", "url": "https://huggingface.co/spaces/fastcarr/euywe1-1"}, {"name": "기능-리뷰분석", "url": "https://huggingface.co/spaces/fastcarr/vvvpppp_1-1"}, {"name": "기능-블로그크롤러", "url": "https://huggingface.co/spaces/fastcarr/ttpp323-1"}, {"name": "기능-유튜브대본추출기", "url": "https://huggingface.co/spaces/fastcarr/YT_Ts_R-1"}, {"name": "기능-PDF생성기", "url": "https://huggingface.co/spaces/fastcarr/gaewate1-1"}, {"name": "기능-블로그크롤러(제휴용)", "url": "https://huggingface.co/spaces/fastcarr/ttpp323-4"}, {"name": "기능-유튜브대본추출기(제휴용)", "url": "https://huggingface.co/spaces/fastcarr/YT_Ts_R-4"}, {"name": "수동블로그-일반+생활건강", "url": "https://huggingface.co/spaces/happydoggg/kmkm_1-2"}, {"name": "수동블로그-여행", "url": "https://huggingface.co/spaces/happydoggg/kmkm_2-2"}, {"name": "수동블로그-상품리뷰", "url": "https://huggingface.co/spaces/happydoggg/kmkm_3-2"}, {"name": "수동블로그-핵심기능", "url": "https://huggingface.co/spaces/happydoggg/kmkm_4-2"}, {"name": "수동블로그-방문후기", "url": "https://huggingface.co/spaces/happydoggg/kmkm_5-2"}, {"name": "자동블로그-일반", "url": "https://huggingface.co/spaces/happydoggg/mmmha_1-2"}, {"name": "자동블로그-여행", "url": "https://huggingface.co/spaces/happydoggg/mmmha_2-2"}, {"name": "자동블로그-상품리뷰", "url": "https://huggingface.co/spaces/happydoggg/mmmha_3-2"}, {"name": "유튜브-요약", "url": "https://huggingface.co/spaces/happydoggg/ybyb-r-2"}, {"name": "유튜브-블로그(일반)", "url": "https://huggingface.co/spaces/happydoggg/ybyb_1-2"}, {"name": "유튜브-블로그(생활건강)", "url": "https://huggingface.co/spaces/happydoggg/ybyb_1-2-2"}, {"name": "유튜브-블로그(여행)", "url": "https://huggingface.co/spaces/happydoggg/ybyb_2-2"}, {"name": "유튜브-블로그(상품리뷰)", "url": "https://huggingface.co/spaces/happydoggg/ybyb_3-2"}, {"name": "이커머스-카피라이팅", "url": "https://huggingface.co/spaces/happydoggg/LuLu_cp_1_R-2"}, {"name": "이커머스-상세페이지기획", "url": "https://huggingface.co/spaces/happydoggg/plpl_1-2"}, {"name": "컨트롤-수동블로그", "url": "https://huggingface.co/spaces/corpcorp1/ldleeer1-2"}, {"name": "컨트롤-자동블로그", "url": "https://huggingface.co/spaces/corpcorp1/lleryr1-2"}, {"name": "컨트롤-유튜브", "url": "https://huggingface.co/spaces/corpcorp1/kketywe1-2"}, {"name": "컨트롤-카피상세", "url": "https://huggingface.co/spaces/corpcorp1/euywe1-2"}, {"name": "기능-리뷰분석", "url": "https://huggingface.co/spaces/corpcorp1/vvvpppp_1-2"}, {"name": "기능-블로그크롤러", "url": "https://huggingface.co/spaces/corpcorp1/ttpp323-2"}, {"name": "기능-유튜브대본추출기", "url": "https://huggingface.co/spaces/corpcorp1/YT_Ts_R-2"}, {"name": "기능-PDF생성기", "url": "https://huggingface.co/spaces/corpcorp1/gaewate1-2"}, {"name": "수동블로그-일반+생활건강", "url": "https://huggingface.co/spaces/magictreee/kmkm_1-3"}, {"name": "수동블로그-여행", "url": "https://huggingface.co/spaces/magictreee/kmkm_2-3"}, {"name": "수동블로그-상품리뷰", "url": "https://huggingface.co/spaces/magictreee/kmkm_3-3"}, {"name": "수동블로그-핵심기능", "url": "https://huggingface.co/spaces/magictreee/kmkm_4-3"}, {"name": "수동블로그-방문후기", "url": "https://huggingface.co/spaces/magictreee/kmkm_5-3"}, {"name": "자동블로그-일반", "url": "https://huggingface.co/spaces/magictreee/mmmha_1-3"}, {"name": "자동블로그-여행", "url": "https://huggingface.co/spaces/magictreee/mmmha_2-3"}, {"name": "자동블로그-상품리뷰", "url": "https://huggingface.co/spaces/magictreee/mmmha_3-3"}, {"name": "유튜브-요약", "url": "https://huggingface.co/spaces/magictreee/ybyb-r-3"}, {"name": "유튜브-블로그(일반)", "url": "https://huggingface.co/spaces/magictreee/ybyb_1-3"}, {"name": "유튜브-블로그(생활건강)", "url": "https://huggingface.co/spaces/magictreee/ybyb_1-2-3"}, {"name": "유튜브-블로그(여행)", "url": "https://huggingface.co/spaces/magictreee/ybyb_2-3"}, {"name": "유튜브-블로그(상품리뷰)", "url": "https://huggingface.co/spaces/magictreee/ybyb_3-3"}, {"name": "이커머스-카피라이팅", "url": "https://huggingface.co/spaces/magictreee/LuLu_cp_1_R-3"}, {"name": "이커머스-상세페이지기획", "url": "https://huggingface.co/spaces/magictreee/plpl_1-3"}, {"name": "컨트롤-수동블로그", "url": "https://huggingface.co/spaces/rara527/ldleeer1-3"}, {"name": "컨트롤-자동블로그", "url": "https://huggingface.co/spaces/rara527/lleryr1-3"}, {"name": "컨트롤-유튜브", "url": "https://huggingface.co/spaces/rara527/kketywe1-3"}, {"name": "컨트롤-카피상세", "url": "https://huggingface.co/spaces/rara527/euywe1-3"}, {"name": "기능-리뷰분석", "url": "https://huggingface.co/spaces/rara527/vvvpppp_1-3"}, {"name": "기능-블로그크롤러", "url": "https://huggingface.co/spaces/rara527/ttpp323-3"}, {"name": "기능-유튜브대본추출기", "url": "https://huggingface.co/spaces/rara527/YT_Ts_R-3"}, {"name": "기능-PDF생성기", "url": "https://huggingface.co/spaces/rara527/gaewate1-3"}, {"name": "메인기능-상품배경이미지", "url": "https://huggingface.co/spaces/bluskyyy/asvdqw3g234"}, {"name": "메인기능-리뷰분석", "url": "https://huggingface.co/spaces/bluskyyy/4234g5hhs"}, {"name": "메인기능-이미지생성기", "url": "https://huggingface.co/spaces/bluskyyy/asdfqwe"}, {"name": "메인기능-블로그포스팅", "url": "https://huggingface.co/spaces/bluskyyy/33f23f3f-3"}, {"name": "이미지-배경제거", "url": "https://huggingface.co/spaces/ssboost/B_G"}, {"name": "이미지-화질개선기", "url": "https://huggingface.co/spaces/ssboost/Up_G"}, {"name": "이미지-ModelSwap", "url": "https://huggingface.co/spaces/ssboost/ModelSwap-1"}, {"name": "이미지-필터", "url": "https://huggingface.co/spaces/ssboost/Image_Filter-1"}, {"name": "이미지-복원", "url": "https://huggingface.co/spaces/ssboost/old_restoration-1"}, {"name": "이미지-이커머스AI이미지생성", "url": "https://huggingface.co/spaces/ssboost/6hjw3hs3"}, {"name": "이미지-배경생성기", "url": "https://huggingface.co/spaces/ssboost/g7yhw43gssdf"}, {"name": "이미지-배경생성기(예시)", "url": "https://huggingface.co/spaces/ssboost/asdfewef"}, {"name": "이미지-배경생성기(유)", "url": "https://huggingface.co/spaces/ssboost/veegsd"}, {"name": "이미지-블로그이미지생", "url": "https://huggingface.co/spaces/ssboost/dfq23d"}, {"name": "이미지-이미지편집", "url": "https://huggingface.co/spaces/ssboost/swq2f"}, {"name": "이커머스-리뷰분석", "url": "https://huggingface.co/spaces/ssboost/fe2fsd"}, {"name": "이커머스-네이버쇼핑상품명분석", "url": "https://huggingface.co/spaces/ssboost/d34feer"}, {"name": "유용한툴-글다듬", "url": "https://huggingface.co/spaces/ssboost/E2T4TWEWEWE"}, {"name": "유용한툴-셀러 자동 번역기", "url": "https://huggingface.co/spaces/ssboost/G378DFE4AS"}, {"name": "유용한툴-gif", "url": "https://huggingface.co/spaces/ssboost/gif-1"}, {"name": "유용한툴-유사방지이미지변형기", "url": "https://huggingface.co/spaces/ssboost/N_I"}, {"name": "유용한툴-블로그형태소분석", "url": "https://huggingface.co/spaces/ssboost/ge3gsdgas"}, {"name": "글쓰기-수동블로그", "url": "https://huggingface.co/spaces/ssboost/EWV2DDWEE"}, {"name": "글쓰기-자동블로그", "url": "https://huggingface.co/spaces/ssboost/G3YGSDE"}, {"name": "글쓰기-유튜브", "url": "https://huggingface.co/spaces/ssboost/FVW3GSX3"}, {"name": "이커머스-상품명(메인키워드)", "url": "https://huggingface.co/spaces/ssboost/F23ASFGGD"}, {"name": "이커머스-상품명(상품명분석)", "url": "https://huggingface.co/spaces/ssboost/F23ASFGGD-2"}, {"name": "이커머스-소싱분석기(풀버전)", "url": "https://huggingface.co/spaces/ssboost/3gghdf5"}, {"name": "이커머스-소싱분석기(간략화)", "url": "https://huggingface.co/spaces/ssboost/geo5g4gfj"}, {"name": "이미지-글자지우기", "url": "https://huggingface.co/spaces/ssboost/hkpdejhed"}, {"name": "이미지-이미지바꾸기", "url": "https://huggingface.co/spaces/ssboost/g94h4jd"}, {"name": "메인페이지-리뷰분석", "url": "https://huggingface.co/spaces/ekdhstldhs/86f86f80yg"}, {"name": "메인페이지-블로그생성", "url": "https://huggingface.co/spaces/ekdhstldhs/f23fewde"}, {"name": "메인페이지-소싱분석기(풀버전)", "url": "https://huggingface.co/spaces/ekdhstldhs/f232fwe"}, {"name": "메인페이지-소싱분석기(간략화)", "url": "https://huggingface.co/spaces/ekdhstldhs/df2wwewe"}, {"name": "메인페이지-썸네일배경바꾸기", "url": "https://huggingface.co/spaces/ekdhstldhs/fsqwgvdsd"}, {"name": "메인페이지-이미지생성", "url": "https://huggingface.co/spaces/ekdhstldhs/g34ysdv234g"}, {"name": "기타기능-배경지우기", "url": "https://huggingface.co/spaces/ekdhstldhs/df2wedds"}, {"name": "기타기능-화질개선기", "url": "https://huggingface.co/spaces/ekdhstldhs/fef232fsdes"}, {"name": "보조기능-블로그(상품리뷰)", "url": "https://huggingface.co/spaces/ekdhstldhs/kkie2"}, {"name": "보조기능-블로그(핵심기능)", "url": "https://huggingface.co/spaces/ekdhstldhs/kkie3"} ] KST = timezone('Asia/Seoul') # 로그인 처리 함수 def process_login(username, password): global is_logged_in if username == VALID_USERNAME and password == VALID_PASSWORD: is_logged_in = True dashboard_html = update_dashboard() # 대시보드 데이터 로드 return gr.update(visible=False), gr.update(visible=True), "로그인 성공! 대시보드로 이동합니다.", dashboard_html else: return gr.update(visible=True), gr.update(visible=False), "아이디 또는 비밀번호가 일치하지 않습니다.", None # URL 상태 체크 함수 - 개선된 버전 def check_url_status(url): try: response = requests.get(url, timeout=5) response_text = response.text.lower() # 페이지가 일시중지된 경우의 명확한 표시 확인 if "paused by its owner" in response_text or "this space has been paused" in response_text: return "Paused" # 응답 코드 확인 if response.status_code == 200: # 200 OK이지만 내용에 오류 키워드가 있는지 확인 error_keywords = ["runtime error", "no application file", "build error", "configuration error", "error", "failed"] for keyword in error_keywords: if keyword in response_text: return "오류" # 다른 상태 키워드도 확인 if "paused" in response_text: return "Paused" elif "sleeping" in response_text or "asleep" in response_text: return "Sleeping" elif "building" in response_text: return "Building" elif "restarting" in response_text: return "Restarting" elif "starting" in response_text: return "Starting" # 정상 실행 중 return "Running" elif response.status_code == 503: return "Sleeping" # 503은 일반적으로 sleeping 상태 elif response.status_code >= 400: return "오류" # 400대, 500대 코드는 오류 # 기본값 return "상태 불명" except requests.exceptions.Timeout: return "타임아웃" except requests.exceptions.ConnectionError: return "연결 오류" except Exception as e: logging.error(f"URL 체크 중 오류 발생: {url}, {str(e)}") return "기타 오류" # HTML 블록 생성 함수 def generate_html(targets): grouped_by_id = defaultdict(lambda: defaultdict(list)) for target in targets: user_id = target['url'].split('/')[4] # 아이디 추출 function = target['name'].split('-')[0] # 기능별 카테고리 추출 grouped_by_id[user_id][function].append(target) status_html = "
이메일: {email}
비밀번호: {password}
업데이트 시간: {current_time}
" return full_html # 자동 업데이트를 위한 캐시 변수들 dashboard_html_cache = "" last_update_time = None # 매일 오전 7시에 실행되는 스케줄러 함수 def schedule_daily_check(): global dashboard_html_cache, last_update_time while True: now = datetime.datetime.now(KST) if now.hour == 7 and now.minute == 0: logging.info("일일 상태 체크 실행 중...") # 자동 업데이트 로직 dashboard_html_cache = generate_html(TARGETS) last_update_time = now logging.info(f"일일 상태 체크 완료: {now.strftime('%Y.%m.%d %H:%M:%S')}") time.sleep(60) # 중복 실행 방지 time.sleep(30) # 30초마다 시간 확인 # 대시보드 가져오기 함수 def get_dashboard(): global dashboard_html_cache, last_update_time # 캐시가 비어있거나 마지막 업데이트 후 1시간이 지났으면 새로 업데이트 if dashboard_html_cache == "" or last_update_time is None or \ (datetime.datetime.now(KST) - last_update_time).seconds > 3600: return update_dashboard() # 캐시된 HTML에 현재 시간 추가 current_time = datetime.datetime.now(KST).strftime('%Y.%m.%d %H:%M:%S') return f"{dashboard_html_cache}업데이트 시간: {current_time} (캐시됨)
" # 앱 생성 함수 def create_app(): # CSS 스타일 정의 - 푸터 숨김 및 추가 스타일 custom_css = """ footer {visibility: hidden} .gradio-container {max-width: 1200px} .login-container {max-width: 500px; margin: 0 auto; padding: 20px;} """ with gr.Blocks(css=custom_css) as app: # 로그인 페이지 with gr.Group(visible=True, elem_classes="login-container") as login_page: gr.Markdown("# 서비스 대시보드 로그인") gr.Markdown("허깅페이스 서비스 대시보드에 접속하려면 로그인하세요.") username_input = gr.Textbox(label="아이디", placeholder="아이디를 입력하세요") password_input = gr.Textbox(label="비밀번호", placeholder="비밀번호를 입력하세요", type="password") login_btn = gr.Button("로그인", variant="primary", scale=1) login_message = gr.Textbox(label="상태", interactive=False, visible=False) # 메인 대시보드 페이지 with gr.Group(visible=False) as dashboard_page: with gr.Row(): gr.Markdown("# 허깅페이스 서비스 대시보드") with gr.Row(): refresh_button = gr.Button("상태 새로고침", variant="primary") logout_button = gr.Button("로그아웃", variant="secondary") status_html = gr.HTML() # 로그인 이벤트 설정 login_btn.click( fn=process_login, inputs=[username_input, password_input], outputs=[login_page, dashboard_page, login_message, status_html] ) # 대시보드 새로고침 이벤트 설정 refresh_button.click( fn=update_dashboard, outputs=status_html ) # 로그아웃 이벤트 설정 logout_button.click( fn=process_logout, outputs=[login_page, dashboard_page, login_message, status_html] ) # 스케줄러 스레드 실행 threading.Thread(target=schedule_daily_check, daemon=True).start() return app # 메인 실행 코드 if __name__ == "__main__": app = create_app() app.launch(share=False, server_name="0.0.0.0", server_port=7860)