Anyexyz commited on
Commit
8b502bb
1 Parent(s): e4c07ad

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +200 -200
main.py CHANGED
@@ -1,201 +1,201 @@
1
- import zipfile
2
- import yaml
3
- from pywebio.input import *
4
- from pywebio.output import *
5
- from pywebio.platform import config
6
- from pywebio.platform.tornado import start_server
7
- from pathlib import Path
8
- import shutil
9
- import logging
10
- import os
11
- import io
12
- import re
13
-
14
- # 环境变量
15
- APPS_DIR = Path("apps")
16
- DEFAULT_LOGO = Path("default_logo.png")
17
-
18
- # 初始化logging
19
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
20
-
21
- # 校验key是否为英文字符串
22
- def is_valid_key(key):
23
- return bool(re.match(r'^[a-zA-Z]+$', key))
24
-
25
- # 校验基本信息
26
- def check_base_info(data):
27
- required_fields = [
28
- "name", "key", "tags", "shortDescZh", "shortDescEn",
29
- "type", "crossVersionUpdate", "website", "github", "document"
30
- ]
31
- for field in required_fields:
32
- if not data[field]:
33
- return (field, f"{field} 不能为空")
34
- if len(data["shortDescZh"]) > 30:
35
- return ("shortDescZh", "中文描述不能超过30个字")
36
- if not is_valid_key(data["key"]):
37
- return ("key", "key 必须是纯英文字符串")
38
- return None
39
-
40
- # 保存文件
41
- def save_file(path, content, mode='w', encoding=None):
42
- try:
43
- if 'b' in mode: # 二进制模式
44
- with open(path, mode) as f:
45
- f.write(content)
46
- else: # 文本模式
47
- with open(path, mode, encoding=encoding or 'utf-8') as f:
48
- f.write(content)
49
- logging.info(f"File saved successfully: {path}")
50
- except IOError as e:
51
- logging.error(f"Error saving file {path}: {e}")
52
- raise
53
-
54
- # 复制文件
55
- def copy_file(src, dst):
56
- try:
57
- shutil.copy(src, dst)
58
- logging.info(f"File copied successfully from {src} to {dst}")
59
- except IOError as e:
60
- logging.error(f"Error copying file from {src} to {dst}: {e}")
61
- raise
62
-
63
- # 创建目录
64
- def create_directory(path):
65
- try:
66
- path.mkdir(parents=True, exist_ok=True)
67
- logging.info(f"Directory created: {path}")
68
- except OSError as e:
69
- logging.error(f"Error creating directory {path}: {e}")
70
- raise
71
-
72
- # 创建版本
73
- def create_version(app_dir, existing_versions):
74
- while True:
75
- version = input("请输入应用的版本 (不要以v开头)")
76
- if version in existing_versions:
77
- put_error(f"版本 {version} 已存在,请输入一个新的版本号")
78
- else:
79
- break
80
-
81
- version_dir = app_dir / version
82
- create_directory(version_dir)
83
-
84
- version_info = input_group("版本信息", [
85
- textarea("请编写docker-compose.yml", name="docker_compose", code={"mode": "yaml", "theme": ""}),
86
- textarea("请编写data.yml", name="data", code={"mode": "yaml", "theme": ""}),
87
- ])
88
-
89
-
90
-
91
- save_file(version_dir / "data.yml", version_info["data"])
92
- save_file(version_dir / "docker-compose.yml", version_info["docker_compose"])
93
- put_success(f"已成功创建版本 {version}")
94
-
95
- return version
96
-
97
- # 压缩文件夹
98
- def zip_folder(folder_path, output_path):
99
- with zipfile.ZipFile(output_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
100
- for root, _, files in os.walk(folder_path):
101
- for file in files:
102
- file_path = os.path.join(root, file)
103
- arcname = os.path.relpath(file_path, folder_path)
104
- zipf.write(file_path, arcname)
105
-
106
- # 主函数
107
- def main():
108
- base_info = input_group(
109
- "自助创建 1Panel 应用",
110
- [
111
- input("1. 请输入应用名称* ", name="name", type=TEXT),
112
- input("2. 请输入应用的key* (仅限英文,用于创建文件夹)", name="key", type=TEXT),
113
- checkbox("3. 选择应用标签*(可以有多个)", inline=True, options=[
114
- {"label": "建站", "value": "WebSite"},
115
- {"label": "Web 服务器", "value": "Server"},
116
- {"label": "运行环境", "value": "Runtime"},
117
- {"label": "数据库", "value": "Database"},
118
- {"label": "工具", "value": "Tool"},
119
- {"label": "CI/CD", "value": "CI/CD"},
120
- {"label": "本地", "value": "Local"},
121
- ], name="tags"),
122
- input("4. 请输入应用中文描述*(不要超过30个字)", name="shortDescZh", type=TEXT),
123
- input("5. 请输入应用英文描述*", name="shortDescEn", type=TEXT),
124
- select("6. 选择应用类型*", options=[
125
- {"label": "工具类应用,如 phpMyAdmin redis-commander jenkins", "value": "tool"},
126
- {"label": "支持一键部署的站点类应用类型,如 wordpress halo", "value": "website"},
127
- {"label": "服务类型的运行时应用,如 mysql openresty redis", "value": "runtime"},
128
- ], name="type"),
129
- select("7. 是否可跨大版本升级*", options=[
130
- {"label": "��", "value": "true"},
131
- {"label": "否", "value": "false"},
132
- ], name="crossVersionUpdate"),
133
- slider("8. 应用安装数量限制,(0 代表无限制)*", name="limit", min=0, max=100, step=1, value=0),
134
- input("9. 官网地址*", name="website", type=URL),
135
- input("10. Github 地址*", name="github", type=URL),
136
- input("11. 文档地址*", name="document", type=URL),
137
- file_upload("上传应用Logo图片(最好是 180 * 180 px)(可选): ", name="logo", accept=[".png", ".jpg", ".jpeg"], max_size="5M"),
138
- ],
139
- validate=check_base_info,
140
- )
141
-
142
- app_dir = APPS_DIR / base_info["key"]
143
- create_directory(app_dir)
144
-
145
- app_info = {
146
- "additionalProperties": {
147
- "key": base_info["key"],
148
- "name": base_info["name"],
149
- "tags": base_info["tags"],
150
- "shortDescZh": base_info["shortDescZh"],
151
- "shortDescEn": base_info["shortDescEn"],
152
- "type": base_info["type"],
153
- "crossVersionUpdate": base_info["crossVersionUpdate"],
154
- "limit": base_info["limit"],
155
- "website": base_info["website"],
156
- "github": base_info["github"],
157
- "document": base_info["document"],
158
- }
159
- }
160
-
161
- save_file(app_dir / "data.yml", yaml.dump(app_info, allow_unicode=True))
162
-
163
- if base_info["logo"]:
164
- _, file_extension = os.path.splitext(base_info["logo"]["filename"])
165
- logo_filename = f"logo{file_extension.lower()}"
166
- save_file(app_dir / logo_filename, base_info["logo"]["content"], mode='wb')
167
- else:
168
- copy_file(DEFAULT_LOGO, app_dir / "logo.png")
169
-
170
- put_success("已成功创建基本信息")
171
-
172
- readme = textarea("请编写README", code={"mode": "markdown", "theme": ""})
173
- save_file(app_dir / "README.md", readme)
174
- put_success("已成功创建README")
175
-
176
- versions = []
177
- while True:
178
- version = create_version(app_dir, versions)
179
- versions.append(version)
180
- if not actions("是否继续创建新版本?", [
181
- {"label": "是", "value": "yes"},
182
- {"label": "否", "value": "no"},
183
- ]) == "yes":
184
- break
185
-
186
- # 压缩应用文件夹
187
- zip_buffer = io.BytesIO()
188
- zip_folder(app_dir, zip_buffer)
189
- zip_buffer.seek(0)
190
-
191
- # 美化下载按钮
192
- put_button(
193
- f"下载 {base_info['name']} 应用文件",
194
- onclick=lambda: put_file(f"{base_info['key']}.zip", zip_buffer.getvalue()),
195
- color="success",
196
- outline=True
197
- )
198
-
199
- if __name__ == "__main__":
200
- config(title="自助创建 1Panel 应用")
201
  start_server(main, debug=False, port=8080, cdn=False)
 
1
+ import zipfile
2
+ import yaml
3
+ from pywebio.input import *
4
+ from pywebio.output import *
5
+ from pywebio.platform import config
6
+ from pywebio.platform.tornado import start_server
7
+ from pathlib import Path
8
+ import shutil
9
+ import logging
10
+ import os
11
+ import io
12
+ import re
13
+
14
+ # 环境变量
15
+ APPS_DIR = Path("apps")
16
+ DEFAULT_LOGO = Path("default_logo.png")
17
+
18
+ # 初始化logging
19
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
20
+
21
+ # 校验key是否为英文字符串
22
+ def is_valid_key(key):
23
+ return bool(re.match(r'^[a-zA-Z]+$', key))
24
+
25
+ # 校验基本信息
26
+ def check_base_info(data):
27
+ required_fields = [
28
+ "name", "key", "tags", "shortDescZh", "shortDescEn",
29
+ "type", "crossVersionUpdate", "website", "github", "document"
30
+ ]
31
+ for field in required_fields:
32
+ if not data[field]:
33
+ return (field, f"{field} 不能为空")
34
+ if len(data["shortDescZh"]) > 30:
35
+ return ("shortDescZh", "中文描述不能超过30个字")
36
+ if not is_valid_key(data["key"]):
37
+ return ("key", "key 必须是纯英文字符串")
38
+ return None
39
+
40
+ # 保存文件
41
+ def save_file(path, content, mode='w', encoding=None):
42
+ try:
43
+ if 'b' in mode: # 二进制模式
44
+ with open(path, mode) as f:
45
+ f.write(content)
46
+ else: # 文本模式
47
+ with open(path, mode, encoding=encoding or 'utf-8') as f:
48
+ f.write(content)
49
+ logging.info(f"File saved successfully: {path}")
50
+ except IOError as e:
51
+ logging.error(f"Error saving file {path}: {e}")
52
+ raise
53
+
54
+ # 复制文件
55
+ def copy_file(src, dst):
56
+ try:
57
+ shutil.copy(src, dst)
58
+ logging.info(f"File copied successfully from {src} to {dst}")
59
+ except IOError as e:
60
+ logging.error(f"Error copying file from {src} to {dst}: {e}")
61
+ raise
62
+
63
+ # 创建目录
64
+ def create_directory(path):
65
+ try:
66
+ path.mkdir(parents=True, exist_ok=True)
67
+ logging.info(f"Directory created: {path}")
68
+ except OSError as e:
69
+ logging.error(f"Error creating directory {path}: {e}")
70
+ raise
71
+
72
+ # 创建版本
73
+ def create_version(app_dir, existing_versions):
74
+ while True:
75
+ version = input("请输入应用的版本 (不要以v开头)")
76
+ if version in existing_versions:
77
+ put_error(f"版本 {version} 已存在,请输入一个新的版本号")
78
+ else:
79
+ break
80
+
81
+ version_dir = app_dir / version
82
+ create_directory(version_dir)
83
+
84
+ version_info = input_group("版本信息", [
85
+ textarea("请编写docker-compose.yml", name="docker_compose", code={"mode": "yaml", "theme": ""}),
86
+ textarea("请编写data.yml", name="data", code={"mode": "yaml", "theme": ""}),
87
+ ])
88
+
89
+
90
+
91
+ save_file(version_dir / "data.yml", version_info["data"])
92
+ save_file(version_dir / "docker-compose.yml", version_info["docker_compose"])
93
+ put_success(f"已成功创建版本 {version}")
94
+
95
+ return version
96
+
97
+ # 压缩文件夹
98
+ def zip_folder(folder_path, output_path):
99
+ with zipfile.ZipFile(output_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
100
+ for root, _, files in os.walk(folder_path):
101
+ for file in files:
102
+ file_path = os.path.join(root, file)
103
+ arcname = os.path.relpath(file_path, folder_path)
104
+ zipf.write(file_path, arcname)
105
+
106
+ # 主函数
107
+ def main():
108
+ base_info = input_group(
109
+ "自助创建 1Panel 应用",
110
+ [
111
+ input("1. 请输入应用名称* ", name="name", type=TEXT),
112
+ input("2. 请输入应用的key* (仅限英文,用于创建文件夹)", name="key", type=TEXT),
113
+ checkbox("3. 选择应用标签*(可以有多个)", inline=True, options=[
114
+ {"label": "建站", "value": "WebSite"},
115
+ {"label": "Web 服务器", "value": "Server"},
116
+ {"label": "运行环境", "value": "Runtime"},
117
+ {"label": "数据库", "value": "Database"},
118
+ {"label": "工具", "value": "Tool"},
119
+ {"label": "CI/CD", "value": "CI/CD"},
120
+ {"label": "本地", "value": "Local"},
121
+ ], name="tags"),
122
+ input("4. 请输入应用中文描述*(不要超过30个字)", name="shortDescZh", type=TEXT),
123
+ input("5. 请输入应用英文描述*", name="shortDescEn", type=TEXT),
124
+ select("6. 选择应用类型*", options=[
125
+ {"label": "工具类应用,如 phpMyAdmin redis-commander jenkins", "value": "tool"},
126
+ {"label": "支持一键部署的站点类应用类型,如 wordpress halo", "value": "website"},
127
+ {"label": "服务类型的运行时应用,如 mysql openresty redis", "value": "runtime"},
128
+ ], name="type"),
129
+ select("7. 是否可跨大版本升级*", options=[
130
+ {"label": "", "value": True},
131
+ {"label": "否", "value": False},
132
+ ], name="crossVersionUpdate"),
133
+ slider("8. 应用安装数量限制,(0 代表无限制)*", name="limit", min=0, max=100, step=1, value=0),
134
+ input("9. 官网地址*", name="website", type=URL),
135
+ input("10. Github 地址*", name="github", type=URL),
136
+ input("11. 文档地址*", name="document", type=URL),
137
+ file_upload("上传应用Logo图片(最好是 180 * 180 px)(可选): ", name="logo", accept=[".png", ".jpg", ".jpeg"], max_size="5M"),
138
+ ],
139
+ validate=check_base_info,
140
+ )
141
+
142
+ app_dir = APPS_DIR / base_info["key"]
143
+ create_directory(app_dir)
144
+
145
+ app_info = {
146
+ "additionalProperties": {
147
+ "key": base_info["key"],
148
+ "name": base_info["name"],
149
+ "tags": base_info["tags"],
150
+ "shortDescZh": base_info["shortDescZh"],
151
+ "shortDescEn": base_info["shortDescEn"],
152
+ "type": base_info["type"],
153
+ "crossVersionUpdate": base_info["crossVersionUpdate"],
154
+ "limit": base_info["limit"],
155
+ "website": base_info["website"],
156
+ "github": base_info["github"],
157
+ "document": base_info["document"],
158
+ }
159
+ }
160
+
161
+ save_file(app_dir / "data.yml", yaml.dump(app_info, allow_unicode=True))
162
+
163
+ if base_info["logo"]:
164
+ _, file_extension = os.path.splitext(base_info["logo"]["filename"])
165
+ logo_filename = f"logo{file_extension.lower()}"
166
+ save_file(app_dir / logo_filename, base_info["logo"]["content"], mode='wb')
167
+ else:
168
+ copy_file(DEFAULT_LOGO, app_dir / "logo.png")
169
+
170
+ put_success("已成功创建基本信息")
171
+
172
+ readme = textarea("请编写README", code={"mode": "markdown", "theme": ""})
173
+ save_file(app_dir / "README.md", readme)
174
+ put_success("已成功创建README")
175
+
176
+ versions = []
177
+ while True:
178
+ version = create_version(app_dir, versions)
179
+ versions.append(version)
180
+ if not actions("是否继续创建新版本?", [
181
+ {"label": "是", "value": "yes"},
182
+ {"label": "否", "value": "no"},
183
+ ]) == "yes":
184
+ break
185
+
186
+ # 压缩应用文件夹
187
+ zip_buffer = io.BytesIO()
188
+ zip_folder(app_dir, zip_buffer)
189
+ zip_buffer.seek(0)
190
+
191
+ # 美化下载按钮
192
+ put_button(
193
+ f"下载 {base_info['name']} 应用文件",
194
+ onclick=lambda: put_file(f"{base_info['key']}.zip", zip_buffer.getvalue()),
195
+ color="success",
196
+ outline=True
197
+ )
198
+
199
+ if __name__ == "__main__":
200
+ config(title="自助创建 1Panel 应用")
201
  start_server(main, debug=False, port=8080, cdn=False)