openfree commited on
Commit
74fdc7e
·
verified ·
1 Parent(s): ac5c0c0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +298 -142
app.py CHANGED
@@ -667,19 +667,188 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
667
  <h1>🎮 Vibe Game Craft</h1>
668
  <p>설명을 입력하면 웹 기반 HTML5, JavaScript, CSS 게임을 생성합니다. 직관적인 인터페이스로 쉽게 게임을 만들고, 실시간으로 미리보기를 확인하세요.</p>
669
  </div>
670
- """)
671
 
672
- # 배포 결과 배너 - 별도 컴포넌트로 추가
673
- deploy_banner = gr.HTML("""
674
- <div class="deploy-banner">
675
  <div class="deploy-banner-content">
676
  <div class="deploy-banner-icon">🚀</div>
677
  <div class="deploy-banner-info">
678
- <div class="deploy-banner-title">배포 준비됨</div>
679
- <div class="deploy-banner-message">배포 버튼을 클릭하여 게임을 배포하세요.</div>
 
 
 
 
680
  </div>
681
  </div>
682
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
683
  """)
684
 
685
  history = gr.State([])
@@ -772,7 +941,7 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
772
  """
773
  <div class="deploy-section">
774
  <div class="deploy-header">📤 배포 결과</div>
775
- <div class="deploy-result-box">
776
  <div class="no-deploy">아직 배포된 게임이 없습니다.</div>
777
  </div>
778
  </div>
@@ -850,191 +1019,178 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
850
  outputs=[sandbox, state_tab]
851
  )
852
 
853
- # 배포 함수
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
854
  def handle_deploy(code, deploy_status):
855
  if not code:
856
- banner_html = """
857
- <div class="deploy-banner error">
858
- <div class="deploy-banner-content">
859
- <div class="deploy-banner-icon">⚠️</div>
860
- <div class="deploy-banner-info">
861
- <div class="deploy-banner-title">배포 실패</div>
862
- <div class="deploy-banner-message">배포할 코드가 없��니다. 먼저 게임 코드를 생성해주세요.</div>
863
- </div>
864
- </div>
865
- </div>
866
- """
867
- result_html = """
868
- <div class="deploy-section">
869
- <div class="deploy-header">📤 배포 결과</div>
870
- <div class="deploy-result-box">
871
- <div class="deploy-error">
872
- <div class="error-icon">⚠️</div>
873
- <div class="error-message">배포할 코드가 없습니다.</div>
874
- </div>
875
  </div>
876
- </div>
 
877
  """
878
- return banner_html, result_html, deploy_status
 
 
 
 
 
879
 
880
  try:
881
- # 로딩 상태 표시
882
- banner_loading_html = """
883
- <div class="deploy-banner loading">
884
- <div class="deploy-banner-content">
885
- <div class="deploy-banner-icon">🔄</div>
886
- <div class="deploy-banner-info">
887
- <div class="deploy-banner-title">배포 진행 중</div>
888
- <div class="deploy-banner-message">Vercel에 게임을 배포하고 있습니다. 잠시만 기다려주세요.</div>
889
- </div>
890
- </div>
891
- </div>
892
- """
893
- result_loading_html = """
894
- <div class="deploy-section">
895
- <div class="deploy-header">📤 배포 결과</div>
896
- <div class="deploy-result-box">
897
- <div class="deploy-loading">
898
- <div class="loading-spinner"></div>
899
- <div class="loading-message">Vercel에 배포 중입니다...</div>
900
- </div>
901
  </div>
902
- </div>
 
903
  """
 
904
 
905
- # 로딩 상태 반환
906
- yield banner_loading_html, result_loading_html, deploy_status
907
-
908
- # 실제 배포 처리
909
  clean_code = remove_code_block(code)
910
  result = deploy_to_vercel(clean_code)
911
 
912
- # 배포 성공
913
  if result.get("status") == "success":
914
  url = result.get("url")
915
- banner_success_html = f"""
916
- <div class="deploy-banner success">
917
- <div class="deploy-banner-content">
918
- <div class="deploy-banner-icon">✅</div>
919
- <div class="deploy-banner-info">
920
- <div class="deploy-banner-title">배포 완료!</div>
921
- <div class="deploy-banner-message">게임이 성공적으로 Vercel에 배포되었습니다.</div>
922
- </div>
923
- <div class="deploy-banner-url-container" style="display:flex;">
924
- <a href="{url}" target="_blank" class="deploy-banner-url">{url}</a>
925
- <button onclick="navigator.clipboard.writeText('{url}')" class="deploy-banner-copy-btn">복사</button>
926
  </div>
927
  </div>
928
- </div>
 
929
  """
930
-
931
- result_success_html = f"""
932
- <div class="deploy-section">
933
- <div class="deploy-header">📤 배포 결과</div>
934
- <div class="deploy-result-box">
935
- <div class="deploy-success">
936
- <div class="success-icon">✅</div>
937
- <div class="success-message">배포 완료!</div>
938
- <div class="url-box">
939
- <a href="{url}" target="_blank">{url}</a>
940
- <button class="copy-btn" onclick="navigator.clipboard.writeText('{url}')">복사</button>
941
- </div>
942
- </div>
943
- </div>
944
- </div>
945
- """
946
-
947
- updated_status = {
948
  "is_deployed": True,
949
  "status": "success",
950
  "url": url,
951
  "message": "배포 완료!"
952
  }
953
-
954
- return banner_success_html, result_success_html, updated_status
955
-
956
- # 배포 실패
957
  else:
958
  error_msg = result.get("message", "알 수 없는 오류")
959
- banner_error_html = f"""
960
- <div class="deploy-banner error">
961
- <div class="deploy-banner-content">
962
- <div class="deploy-banner-icon">⚠️</div>
963
- <div class="deploy-banner-info">
964
- <div class="deploy-banner-title">배포 실패</div>
965
- <div class="deploy-banner-message">{error_msg}</div>
966
- </div>
967
- </div>
968
- </div>
969
- """
970
-
971
- result_error_html = f"""
972
- <div class="deploy-section">
973
- <div class="deploy-header">📤 배포 결과</div>
974
- <div class="deploy-result-box">
975
- <div class="deploy-error">
976
- <div class="error-icon">⚠️</div>
977
- <div class="error-message">배포 실패: {error_msg}</div>
978
- </div>
979
  </div>
980
- </div>
 
981
  """
982
-
983
- updated_status = {
984
  "is_deployed": False,
985
  "status": "error",
986
  "message": error_msg,
987
  "url": ""
988
  }
989
 
990
- return banner_error_html, result_error_html, updated_status
991
-
992
  except Exception as e:
993
  error_msg = str(e)
994
- banner_exception_html = f"""
995
- <div class="deploy-banner error">
996
- <div class="deploy-banner-content">
997
- <div class="deploy-banner-icon">⚠️</div>
998
- <div class="deploy-banner-info">
999
- <div class="deploy-banner-title">시스템 오류</div>
1000
- <div class="deploy-banner-message">{error_msg}</div>
1001
- </div>
1002
- </div>
1003
- </div>
1004
- """
1005
-
1006
- result_exception_html = f"""
1007
- <div class="deploy-section">
1008
- <div class="deploy-header">📤 배포 결과</div>
1009
- <div class="deploy-result-box">
1010
- <div class="deploy-error">
1011
- <div class="error-icon">⚠️</div>
1012
- <div class="error-message">시스템 오류: {error_msg}</div>
1013
- </div>
1014
  </div>
1015
- </div>
 
1016
  """
1017
-
1018
- updated_status = {
1019
  "is_deployed": False,
1020
  "status": "error",
1021
  "message": error_msg,
1022
  "url": ""
1023
  }
1024
-
1025
- return banner_exception_html, result_exception_html, updated_status
1026
 
1027
- # 배포 버튼 클릭 - 수정된 부분
1028
  deploy_btn.click(
1029
  fn=handle_deploy,
1030
  inputs=[code_output, deploy_status],
1031
- outputs=[deploy_banner, deploy_result_container, deploy_status]
1032
  )
1033
 
 
 
 
 
 
1034
  if __name__ == "__main__":
1035
  try:
1036
  demo_instance = Demo()
1037
  demo.queue(default_concurrency_limit=20).launch(ssr_mode=False)
1038
  except Exception as e:
1039
  print(f"Initialization error: {e}")
1040
- raise
 
667
  <h1>🎮 Vibe Game Craft</h1>
668
  <p>설명을 입력하면 웹 기반 HTML5, JavaScript, CSS 게임을 생성합니다. 직관적인 인터페이스로 쉽게 게임을 만들고, 실시간으로 미리보기를 확인하세요.</p>
669
  </div>
 
670
 
671
+ <!-- 배포 결과 박스 - 헤더 바로 아래 위치 -->
672
+ <div id="deploy-banner" style="display:none;" class="deploy-banner">
 
673
  <div class="deploy-banner-content">
674
  <div class="deploy-banner-icon">🚀</div>
675
  <div class="deploy-banner-info">
676
+ <div id="deploy-banner-title" class="deploy-banner-title">배포 상태</div>
677
+ <div id="deploy-banner-message" class="deploy-banner-message"></div>
678
+ </div>
679
+ <div id="deploy-banner-url-container" class="deploy-banner-url-container" style="display:none;">
680
+ <a id="deploy-banner-url" href="#" target="_blank" class="deploy-banner-url"></a>
681
+ <button onclick="copyBannerUrl()" class="deploy-banner-copy-btn">복사</button>
682
  </div>
683
  </div>
684
  </div>
685
+
686
+ <style>
687
+ /* 전체 앱 스타일 */
688
+ :root {
689
+ --primary-color: #9c89b8;
690
+ --secondary-color: #f0a6ca;
691
+ --accent-color: #b8bedd;
692
+ --background-color: #f9f7fd;
693
+ --panel-color: #ffffff;
694
+ --text-color: #3a3042;
695
+ --button-hover: #efc3e6;
696
+ --shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
697
+ --radius: 12px;
698
+ }
699
+
700
+ body {
701
+ background-color: var(--background-color);
702
+ color: var(--text-color);
703
+ font-family: 'Poppins', sans-serif;
704
+ }
705
+
706
+ /* 헤더 스타일 */
707
+ .app-header {
708
+ text-align: center;
709
+ padding: 1.5rem 1rem;
710
+ margin-bottom: 0.5rem;
711
+ background: linear-gradient(to right, var(--primary-color), var(--secondary-color));
712
+ border-radius: var(--radius);
713
+ box-shadow: var(--shadow);
714
+ color: white;
715
+ }
716
+
717
+ .app-header h1 {
718
+ font-size: 2.5rem;
719
+ font-weight: 700;
720
+ margin-bottom: 0.5rem;
721
+ text-shadow: 0 2px 4px rgba(0,0,0,0.1);
722
+ }
723
+
724
+ .app-header p {
725
+ font-size: 1.1rem;
726
+ opacity: 0.9;
727
+ max-width: 800px;
728
+ margin: 0 auto;
729
+ }
730
+
731
+ /* 배포 배너 스타일 - 헤더 바로 아래에 표시 */
732
+ .deploy-banner {
733
+ background: white;
734
+ border-radius: var(--radius);
735
+ margin: 0.5rem auto 1.5rem auto;
736
+ box-shadow: var(--shadow);
737
+ max-width: 1200px;
738
+ border: 1px solid #ddd;
739
+ overflow: hidden;
740
+ transition: all 0.3s ease;
741
+ }
742
+
743
+ .deploy-banner.success {
744
+ border-left: 5px solid #34c759;
745
+ }
746
+
747
+ .deploy-banner.error {
748
+ border-left: 5px solid #ff3b30;
749
+ }
750
+
751
+ .deploy-banner.loading {
752
+ border-left: 5px solid #007aff;
753
+ }
754
+
755
+ .deploy-banner-content {
756
+ display: flex;
757
+ align-items: center;
758
+ padding: 15px 20px;
759
+ }
760
+
761
+ .deploy-banner-icon {
762
+ font-size: 24px;
763
+ margin-right: 15px;
764
+ }
765
+
766
+ .deploy-banner-info {
767
+ flex: 1;
768
+ }
769
+
770
+ .deploy-banner-title {
771
+ font-weight: bold;
772
+ font-size: 16px;
773
+ margin-bottom: 5px;
774
+ }
775
+
776
+ .deploy-banner-message {
777
+ color: #666;
778
+ }
779
+
780
+ .deploy-banner-url-container {
781
+ background: #f5f8ff;
782
+ padding: 10px 15px;
783
+ border-radius: 8px;
784
+ margin-left: 20px;
785
+ max-width: 400px;
786
+ display: flex;
787
+ align-items: center;
788
+ }
789
+
790
+ .deploy-banner-url {
791
+ color: #0066cc;
792
+ text-decoration: none;
793
+ font-weight: 500;
794
+ word-break: break-all;
795
+ flex: 1;
796
+ }
797
+
798
+ .deploy-banner-copy-btn {
799
+ background: #0066cc;
800
+ color: white;
801
+ border: none;
802
+ border-radius: 4px;
803
+ padding: 5px 10px;
804
+ margin-left: 10px;
805
+ cursor: pointer;
806
+ font-size: 12px;
807
+ }
808
+
809
+ .deploy-banner-copy-btn:hover {
810
+ background: #0052a3;
811
+ }
812
+ </style>
813
+
814
+ <script>
815
+ // URL 복사 함수
816
+ function copyBannerUrl() {
817
+ const url = document.getElementById('deploy-banner-url').href;
818
+ navigator.clipboard.writeText(url)
819
+ .then(() => {
820
+ const copyBtn = document.querySelector('.deploy-banner-copy-btn');
821
+ const originalText = copyBtn.textContent;
822
+ copyBtn.textContent = '복사됨!';
823
+ setTimeout(() => {
824
+ copyBtn.textContent = originalText;
825
+ }, 1000);
826
+ });
827
+ }
828
+
829
+ // 배포 배너 표시 함수
830
+ function showDeployBanner(type, title, message, url) {
831
+ const banner = document.getElementById('deploy-banner');
832
+ const bannerTitle = document.getElementById('deploy-banner-title');
833
+ const bannerMessage = document.getElementById('deploy-banner-message');
834
+ const bannerUrlContainer = document.getElementById('deploy-banner-url-container');
835
+ const bannerUrl = document.getElementById('deploy-banner-url');
836
+
837
+ banner.className = 'deploy-banner ' + type;
838
+ bannerTitle.textContent = title;
839
+ bannerMessage.textContent = message;
840
+
841
+ if (url) {
842
+ bannerUrl.href = url;
843
+ bannerUrl.textContent = url;
844
+ bannerUrlContainer.style.display = 'flex';
845
+ } else {
846
+ bannerUrlContainer.style.display = 'none';
847
+ }
848
+
849
+ banner.style.display = 'block';
850
+ }
851
+ </script>
852
  """)
853
 
854
  history = gr.State([])
 
941
  """
942
  <div class="deploy-section">
943
  <div class="deploy-header">📤 배포 결과</div>
944
+ <div id="deploy-result-box" class="deploy-result-box">
945
  <div class="no-deploy">아직 배포된 게임이 없습니다.</div>
946
  </div>
947
  </div>
 
1019
  outputs=[sandbox, state_tab]
1020
  )
1021
 
1022
+ # 실제 배포 로직
1023
+ def deploy_to_vercel(code: str):
1024
+ try:
1025
+ token = "A8IFZmgW2cqA4yUNlLPnci0N" # 실제 토큰 필요
1026
+ if not token:
1027
+ return {"status": "error", "message": "Vercel 토큰이 설정되지 않았습니다."}
1028
+
1029
+ project_name = ''.join(random.choice(string.ascii_lowercase) for i in range(6))
1030
+ deploy_url = "https://api.vercel.com/v13/deployments"
1031
+ headers = {
1032
+ "Authorization": f"Bearer {token}",
1033
+ "Content-Type": "application/json"
1034
+ }
1035
+ package_json = {
1036
+ "name": project_name,
1037
+ "version": "1.0.0",
1038
+ "private": True,
1039
+ "dependencies": {"vite": "^5.0.0"},
1040
+ "scripts": {
1041
+ "dev": "vite",
1042
+ "build": "echo 'No build needed' && mkdir -p dist && cp index.html dist/",
1043
+ "preview": "vite preview"
1044
+ }
1045
+ }
1046
+ files = [
1047
+ {"file": "index.html", "data": code},
1048
+ {"file": "package.json", "data": json.dumps(package_json, indent=2)}
1049
+ ]
1050
+ project_settings = {
1051
+ "buildCommand": "npm run build",
1052
+ "outputDirectory": "dist",
1053
+ "installCommand": "npm install",
1054
+ "framework": None
1055
+ }
1056
+ deploy_data = {
1057
+ "name": project_name,
1058
+ "files": files,
1059
+ "target": "production",
1060
+ "projectSettings": project_settings
1061
+ }
1062
+ deploy_response = requests.post(deploy_url, headers=headers, json=deploy_data)
1063
+ if deploy_response.status_code != 200:
1064
+ return {"status": "error", "message": f"배포 실패: {deploy_response.text}"}
1065
+
1066
+ deployment_url = f"https://{project_name}.vercel.app"
1067
+ time.sleep(5)
1068
+
1069
+ return {
1070
+ "status": "success",
1071
+ "url": deployment_url,
1072
+ "project_name": project_name
1073
+ }
1074
+ except Exception as e:
1075
+ return {"status": "error", "message": f"배포 중 오류 발생: {str(e)}"}
1076
+
1077
+ # handle_deploy
1078
  def handle_deploy(code, deploy_status):
1079
  if not code:
1080
+ js_code = """
1081
+ <script>
1082
+ showDeployBanner('error', '⚠️ 배포 실패', '배포할 코드가 없습니다. 먼저 게임 코드를 생성해주세요.');
1083
+ document.getElementById('deploy-result-box').innerHTML = `
1084
+ <div class="deploy-error">
1085
+ <div class="error-icon">⚠️</div>
1086
+ <div class="error-message">배포할 코드가 없습니다.</div>
 
 
 
 
 
 
 
 
 
 
 
 
1087
  </div>
1088
+ `;
1089
+ </script>
1090
  """
1091
+ return js_code, {
1092
+ "is_deployed": False,
1093
+ "status": "error",
1094
+ "message": "배포할 코드가 없습니다.",
1095
+ "url": ""
1096
+ }
1097
 
1098
  try:
1099
+ loading_js = """
1100
+ <script>
1101
+ showDeployBanner('loading', '🔄 배포 진행 중', 'Vercel에 게임을 배포하고 있습니다. 잠시만 기다려주세요.');
1102
+ document.getElementById('deploy-result-box').innerHTML = `
1103
+ <div class="deploy-loading">
1104
+ <div class="loading-spinner"></div>
1105
+ <div class="loading-message">Vercel에 배포 중입니다...</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
1106
  </div>
1107
+ `;
1108
+ </script>
1109
  """
1110
+ yield loading_js, deploy_status
1111
 
 
 
 
 
1112
  clean_code = remove_code_block(code)
1113
  result = deploy_to_vercel(clean_code)
1114
 
 
1115
  if result.get("status") == "success":
1116
  url = result.get("url")
1117
+ success_js = f"""
1118
+ <script>
1119
+ showDeployBanner('success', '✅ 배포 완료!', '게임이 성공적으로 Vercel에 배포되었습니다.', '{url}');
1120
+ document.getElementById('deploy-result-box').innerHTML = `
1121
+ <div class="deploy-success">
1122
+ <div class="success-icon">✅</div>
1123
+ <div class="success-message">배포 완료!</div>
1124
+ <div class="url-box">
1125
+ <a href="{url}" target="_blank">{url}</a>
1126
+ <button class="copy-btn" onclick="navigator.clipboard.writeText('{url}')">복사</button>
 
1127
  </div>
1128
  </div>
1129
+ `;
1130
+ </script>
1131
  """
1132
+ return success_js, {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1133
  "is_deployed": True,
1134
  "status": "success",
1135
  "url": url,
1136
  "message": "배포 완료!"
1137
  }
 
 
 
 
1138
  else:
1139
  error_msg = result.get("message", "알 수 없는 오류")
1140
+ error_js = f"""
1141
+ <script>
1142
+ showDeployBanner('error', '⚠️ 배포 실패', '{error_msg}');
1143
+ document.getElementById('deploy-result-box').innerHTML = `
1144
+ <div class="deploy-error">
1145
+ <div class="error-icon">⚠️</div>
1146
+ <div class="error-message">배포 실패: {error_msg}</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
1147
  </div>
1148
+ `;
1149
+ </script>
1150
  """
1151
+ return error_js, {
 
1152
  "is_deployed": False,
1153
  "status": "error",
1154
  "message": error_msg,
1155
  "url": ""
1156
  }
1157
 
 
 
1158
  except Exception as e:
1159
  error_msg = str(e)
1160
+ exception_js = f"""
1161
+ <script>
1162
+ showDeployBanner('error', '⚠️ 시스템 오류', '{error_msg}');
1163
+ document.getElementById('deploy-result-box').innerHTML = `
1164
+ <div class="deploy-error">
1165
+ <div class="error-icon">⚠️</div>
1166
+ <div class="error-message">시스템 오류: {error_msg}</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
1167
  </div>
1168
+ `;
1169
+ </script>
1170
  """
1171
+ return exception_js, {
 
1172
  "is_deployed": False,
1173
  "status": "error",
1174
  "message": error_msg,
1175
  "url": ""
1176
  }
 
 
1177
 
1178
+ # 배포 버튼 클릭
1179
  deploy_btn.click(
1180
  fn=handle_deploy,
1181
  inputs=[code_output, deploy_status],
1182
+ outputs=[deploy_result_container, deploy_status]
1183
  )
1184
 
1185
+
1186
+ # ------------------------
1187
+ # 8) 실제 실행
1188
+ # ------------------------
1189
+
1190
  if __name__ == "__main__":
1191
  try:
1192
  demo_instance = Demo()
1193
  demo.queue(default_concurrency_limit=20).launch(ssr_mode=False)
1194
  except Exception as e:
1195
  print(f"Initialization error: {e}")
1196
+ raise