-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.py
More file actions
709 lines (611 loc) · 29.1 KB
/
app.py
File metadata and controls
709 lines (611 loc) · 29.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
import os
import json
import yaml
import markdown
import requests
import os
import sys
from flask import Flask, render_template, jsonify, send_from_directory, abort
from datetime import datetime
from flask import Flask, render_template_string
import jinja2
import shutil
app = Flask(__name__)
# 导入必要的库
import time
# 读取配置文件
@app.before_first_request
def load_config():
global config
try:
with open('config.json', 'r', encoding='utf-8') as f:
config = json.load(f)
except FileNotFoundError:
# 如果config.json不存在,尝试从default文件夹复制
default_config_path = os.path.join('default', 'default_config.json')
if os.path.exists(default_config_path):
print(f"config.json不存在,从{default_config_path}复制默认配置")
shutil.copy2(default_config_path, 'config.json')
# 读取复制过来的配置
with open('config.json', 'r', encoding='utf-8') as f:
config = json.load(f)
# 同时检查background.jpg是否存在,如果不存在也从default文件夹复制
if 'background' in config and 'image' in config['background']:
background_image = config['background']['image']
if not os.path.exists(background_image) and os.path.exists(os.path.join('default', background_image)):
print(f"{background_image}不存在,从default文件夹复制")
shutil.copy2(os.path.join('default', background_image), background_image)
else:
# 如果default_config.json也不存在,使用内置默认配置
print("default/default_config.json不存在,使用内置默认配置")
config = {
"github_url": "https://github.com/example",
"dark_mode": "auto",
"name": "Example User",
"bio": "Python Developer",
"introduction_file": "Introduction.md",
"github_token": "", # 添加GitHub令牌配置项
"theme": {
"primary_color": "#6a11cb",
"secondary_color": "#2575fc"
},
"background": {
"image": "background.png",
"blur": 8,
"overlay_opacity": 0.6,
"overlay_color": "#121212"
}
}
# 保存默认配置
with open('config.json', 'w', encoding='utf-8') as f:
json.dump(config, f, indent=2, ensure_ascii=False)
# 创建通用的GitHub API请求函数
def make_github_request(url, timeout=5):
try:
# 配置requests不验证SSL证书(解决本地环境中的证书验证问题)
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
# 准备请求头
headers = {'Accept': 'application/vnd.github.v3+json'}
github_token = ''
# 首先尝试从github_token.txt文件中读取令牌
token_file = os.path.join(app.root_path, 'github_token.txt')
try:
if os.path.exists(token_file):
with open(token_file, 'r', encoding='utf-8') as f:
github_token = f.read().strip()
# 移除可能的空白字符和引号
github_token = github_token.replace('"', '').replace("'", '')
print(f"从github_token.txt文件中读取令牌成功")
else:
# 如果文件不存在,尝试从配置中获取
github_token = config.get('github_token', '')
print("github_token.txt文件不存在,尝试从配置中获取令牌")
except Exception as e:
print(f"读取GitHub令牌时出错: {e}")
# 出错时,尝试从配置中获取
github_token = config.get('github_token', '')
# 如果配置了GitHub令牌,添加到请求头
if github_token:
headers['Authorization'] = f'token {github_token}'
print(f"使用GitHub令牌进行认证")
else:
print("未使用GitHub令牌,使用匿名访问")
# 发送请求
response = requests.get(url, headers=headers, timeout=timeout, verify=False)
print(f"GitHub API请求: {url}, 状态码: {response.status_code}")
# 检查是否达到速率限制
if response.status_code == 403 and 'rate limit' in response.text.lower():
print("GitHub API速率限制已达,建议配置GitHub令牌")
return response
except Exception as e:
print(f"GitHub API请求异常: {e}")
# 创建一个模拟的响应对象
class MockResponse:
def __init__(self):
self.status_code = 500
self.text = "模拟错误响应"
return MockResponse()
# 从 GitHub API 获取用户信息
def get_github_user_info():
print("开始获取GitHub用户信息")
github_url = config.get('github_url', 'https://github.com/example')
username = github_url.rstrip('/').split('/')[-1]
print(f"配置的GitHub URL: {github_url}")
print(f"提取的用户名: {username}")
try:
print(f"准备请求GitHub API: https://api.github.com/users/{username}")
# 获取用户信息
user_response = make_github_request(f'https://api.github.com/users/{username}')
print(f"GitHub API响应状态码: {user_response.status_code}")
if user_response.status_code == 200:
user_data = user_response.json()
print(f"成功获取用户数据: {user_data.get('name')}, {user_data.get('login')}")
# 获取用户的仓库信息
repos_response = make_github_request(f'https://api.github.com/users/{username}/repos?sort=pushed&per_page=100')
if repos_response.status_code == 200:
repos = repos_response.json()
# 获取总仓库数和总 stars 数
total_repos = len(repos)
total_stars = sum(repo.get('stargazers_count', 0) for repo in repos)
# 获取同名仓库的 README
readme_content = get_readme_content(username)
# 获取最近有推送的 5 个仓库
recent_repos = sorted(repos, key=lambda x: x.get('pushed_at', ''), reverse=True)[:5]
# 获取用户的活动数据(过去12个月的提交统计)
# 传递已获取的repos参数,避免重复获取
activity_data = get_github_activity_data(username, repos)
# 分析用户的技术栈
tech_stack = analyze_tech_stack(repos)
return {
"avatar_url": user_data.get('avatar_url'),
"name": user_data.get('name') or username,
"bio": config.get('bio', 'Python Developer'), # 使用配置文件中的bio
"total_repos": total_repos,
"total_stars": total_stars,
"readme_content": readme_content,
"recent_repos": recent_repos,
"activity_data": activity_data,
"tech_stack": tech_stack
}
except Exception as e:
print(f"GitHub API调用异常: {type(e).__name__}: {str(e)}")
import traceback
traceback.print_exc()
# 如果获取失败,返回默认值
return {
"avatar_url": "https://avatars.githubusercontent.com/u/1000000?v=4",
"name": config.get('name', 'Example User'),
"bio": config.get('bio', 'Python Developer'),
"total_repos": 0,
"total_stars": 0,
"readme_content": get_local_readme(),
"recent_repos": [],
"activity_data": [65, 59, 80, 81, 56, 55, 70, 65, 85, 75, 60, 75], # 默认数据
"tech_stack": [
{"name": "Python", "color": "#6a11cb"},
{"name": "JavaScript", "color": "#2575fc"},
{"name": "HTML/CSS", "color": "#560bad"},
{"name": "Flask", "color": "#1e40af"}
]
}
import requests
from datetime import datetime, timedelta
def get_github_activity_data(username, token=None):
"""
获取 GitHub 用户过去 12 个月的活跃度(已去重并修正月份逻辑)
"""
headers = {'Authorization': f'token {token}'} if token else {}
now = datetime.now()
# 索引 0 是 11 个月前,索引 11 是当前月
activity_counts = [0] * 12
# 辅助函数:计算日期属于 activity_counts 的哪个索引
def get_month_index(dt):
diff = (now.year - dt.year) * 12 + (now.month - dt.month)
if 0 <= diff < 12:
return 11 - diff # 转换为 0 (最旧) 到 11 (最新)
return None
try:
# 1. 优先获取 Events API (涵盖了大部分动态)
# 注意:GitHub Events 仅保留 90 天内的数据
events_url = f"https://api.github.com/users/{username}/events/public?per_page=100"
response = requests.get(events_url, headers=headers)
if response.status_code == 200:
events = response.json()
for event in events:
if event['type'] == 'PushEvent':
created_at = datetime.strptime(event['created_at'], '%Y-%m-%dT%H:%M:%SZ')
idx = get_month_index(created_at)
if idx is not None:
# PushEvent 中的 size 代表该次推送包含的 commit 数量
commit_count = event.get('payload', {}).get('size', 1)
activity_counts[idx] += commit_count
# 2. 只有当 Events 数据不足以覆盖 12 个月时,才按需查询 Repo Commits
# 这里为了防止重复,我们只统计 Events 覆盖不到的更早日期
# (篇幅原因简化处理:建议只针对特定重要仓库进行回溯)
# 检查是否全为 0,如果是则返回兜底数据
if sum(activity_counts) == 0:
return [45, 52, 38, 65, 48, 56, 70, 80, 85, 75, 60, 75]
return activity_counts
except Exception as e:
print(f"Error fetching data: {e}")
return [0] * 12
# 分析用户的技术栈
# 限制处理的仓库数量,优化性能
cached_tech_stack = None
cached_timestamp = 0
CACHE_DURATION = 3600 # 缓存1小时
def analyze_tech_stack(repos):
global cached_tech_stack, cached_timestamp
# 检查缓存是否有效(如果启用了缓存)
current_time = time.time()
if cached_tech_stack and (current_time - cached_timestamp < CACHE_DURATION):
print("使用缓存的技术栈数据")
return cached_tech_stack
try:
print("开始分析用户的技术栈")
# 创建语言统计字典
language_stats = {}
total_bytes = 0
# 限制处理的仓库数量,只处理最近活跃的前10个仓库
if len(repos) > 10:
repos = repos[:10]
# 遍历仓库,统计语言使用情况
for repo in repos:
# 获取仓库的语言信息(先尝试从仓库数据中获取,减少API调用)
if 'language' in repo and repo['language']:
lang = repo['language']
if lang not in language_stats:
language_stats[lang] = 1 # 给主要语言一个基础分
else:
language_stats[lang] += 1 # 增加主要语言的权重
# 仅对前几个重要的仓库进行详细的语言统计
if len(language_stats) < 5 and 'languages_url' in repo:
try:
languages_response = make_github_request(repo['languages_url'])
if languages_response.status_code == 200:
languages_data = languages_response.json()
# 更新语言统计
for lang, bytes_count in languages_data.items():
if lang not in language_stats:
language_stats[lang] = 0
language_stats[lang] += bytes_count
total_bytes += bytes_count
except Exception as e:
print(f"获取仓库 {repo['name']} 的语言信息时出错: {e}")
continue
# 如果没有获取到语言数据,返回默认技术栈
if not language_stats:
print("没有获取到语言数据,返回默认技术栈")
# 更新缓存
cached_tech_stack = [
{"name": "Python", "color": "#6a11cb"},
{"name": "JavaScript", "color": "#2575fc"},
{"name": "HTML/CSS", "color": "#560bad"},
{"name": "Flask", "color": "#1e40af"}
]
cached_timestamp = time.time()
return cached_tech_stack
# 计算每种语言的使用比例
language_ratios = {}
for lang, bytes_count in language_stats.items():
# 考虑仓库数量和代码量的权重
language_ratios[lang] = bytes_count
# 按使用比例排序,取前10种语言
sorted_languages = sorted(language_ratios.items(), key=lambda x: x[1], reverse=True)[:10]
# 获取配置中的主题色
from flask import current_app
theme = current_app.config.get('theme', {
'primary_color': '#6a11cb',
'secondary_color': '#2575fc',
'dark_primary_color': '#a855f7',
'dark_secondary_color': '#60a5fa'
})
# 根据主题色生成和谐的标签颜色
def generate_harmonious_color(base_color, index, is_dark=False):
# 简单的颜色变体生成算法
# 将HEX颜色转换为RGB
hex_color = base_color.lstrip('#')
r = int(hex_color[0:2], 16)
g = int(hex_color[2:4], 16)
b = int(hex_color[4:6], 16)
# 根据索引调整颜色亮度和饱和度
factor = 1.0 - (index * 0.15) # 递减因子
if factor < 0.4:
factor = 0.4 # 确保颜色不会太暗
# 考虑暗色模式调整
if is_dark:
# 在暗色模式下,使用较亮的颜色变体
factor = 0.6 + (index * 0.1) # 递增因子,确保至少有一定亮度
if factor > 0.9:
factor = 0.9 # 避免颜色过亮
# 应用调整
r = int(r * factor)
g = int(g * factor)
b = int(b * factor)
# 转换回HEX
return f'#{r:02x}{g:02x}{b:02x}'
# 检查是否为暗色模式
is_dark = current_app.config.get('dark_mode', 'auto') == 'dark'
if is_dark:
base_colors = [theme['dark_primary_color'], theme['dark_secondary_color']]
else:
base_colors = [theme['primary_color'], theme['secondary_color']]
color_map = {
'Python': generate_harmonious_color(base_colors[0], 0, is_dark),
'JavaScript': generate_harmonious_color(base_colors[1], 0, is_dark),
'Java': generate_harmonious_color(base_colors[0], 1, is_dark),
'TypeScript': generate_harmonious_color(base_colors[1], 1, is_dark),
'Go': generate_harmonious_color(base_colors[0], 2, is_dark),
'Rust': generate_harmonious_color(base_colors[1], 2, is_dark),
'PHP': generate_harmonious_color(base_colors[0], 3, is_dark),
'C++': generate_harmonious_color(base_colors[1], 3, is_dark),
'C': generate_harmonious_color(base_colors[0], 4, is_dark),
'C#': generate_harmonious_color(base_colors[1], 4, is_dark),
'Ruby': generate_harmonious_color(base_colors[0], 5, is_dark),
'Swift': generate_harmonious_color(base_colors[1], 5, is_dark),
'Kotlin': generate_harmonious_color(base_colors[0], 6, is_dark),
'HTML': generate_harmonious_color(base_colors[1], 6, is_dark),
'CSS': generate_harmonious_color(base_colors[0], 7, is_dark),
'SCSS': generate_harmonious_color(base_colors[1], 7, is_dark),
'Less': generate_harmonious_color(base_colors[0], 8, is_dark),
'Flask': generate_harmonious_color(base_colors[1], 8, is_dark),
'Django': generate_harmonious_color(base_colors[0], 9, is_dark),
'React': generate_harmonious_color(base_colors[1], 9, is_dark),
'Vue': generate_harmonious_color(base_colors[0], 10, is_dark),
'Angular': generate_harmonious_color(base_colors[1], 10, is_dark),
'Node.js': generate_harmonious_color(base_colors[0], 11, is_dark)
}
# 创建技术栈列表
tech_stack = []
for lang, _ in sorted_languages:
# 特殊处理HTML和CSS,合并为HTML/CSS
if lang == 'HTML' or lang == 'CSS':
# 检查是否已经添加了HTML/CSS
html_css_exists = False
for tech in tech_stack:
if tech['name'] == 'HTML/CSS':
html_css_exists = True
break
if not html_css_exists:
tech_stack.append({
"name": "HTML/CSS",
"color": color_map.get('HTML', 'orange')
})
else:
# 添加其他语言
tech_stack.append({
"name": lang,
"color": color_map.get(lang, 'gray') # 默认灰色
})
# 如果没有HTML/CSS但有SCSS或Less等,也添加HTML/CSS
html_css_exists = False
for tech in tech_stack:
if tech['name'] == 'HTML/CSS':
html_css_exists = True
break
if not html_css_exists:
for lang, _ in sorted_languages:
if lang in ['SCSS', 'Less', 'Sass']:
tech_stack.append({
"name": "HTML/CSS",
"color": color_map.get('HTML', 'orange')
})
break
# 确保返回的技术栈不超过10个
if len(tech_stack) > 10:
tech_stack = tech_stack[:10]
# 更新缓存
cached_tech_stack = tech_stack
cached_timestamp = time.time()
print(f"分析完成的技术栈: {[tech['name'] for tech in tech_stack]}")
return tech_stack
except Exception as e:
print(f"分析技术栈时发生异常: {e}")
# 发生错误时返回默认技术栈
return [
{"name": "Python", "color": "blue"},
{"name": "JavaScript", "color": "yellow"},
{"name": "HTML/CSS", "color": "orange"},
{"name": "Flask", "color": "green"}
]
# 获取同名仓库的 README","}]}}}
def get_readme_content(username):
try:
print(f"尝试获取GitHub同名仓库README: {username}/{username}")
# 配置requests不验证SSL证书(解决本地环境中的证书验证问题)
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
# 尝试获取同名仓库的 README(main分支)
readme_url_main = f'https://raw.githubusercontent.com/{username}/{username}/main/README.md'
print(f"尝试获取README URL (main): {readme_url_main}")
readme_response = requests.get(readme_url_main, timeout=5, verify=False)
print(f"README响应状态码 (main): {readme_response.status_code}")
if readme_response.status_code == 200:
print("成功获取main分支的README")
# 将 Markdown 转换为 HTML
return markdown.markdown(readme_response.text)
# 尝试其他分支(master)
readme_url_master = f'https://raw.githubusercontent.com/{username}/{username}/master/README.md'
print(f"尝试获取README URL (master): {readme_url_master}")
readme_response = requests.get(readme_url_master, timeout=5, verify=False)
print(f"README响应状态码 (master): {readme_response.status_code}")
if readme_response.status_code == 200:
print("成功获取master分支的README")
return markdown.markdown(readme_response.text)
print(f"GitHub README获取失败,状态码: {readme_response.status_code}")
except Exception as e:
print(f"GitHub README获取异常: {type(e).__name__}: {str(e)}")
# 如果获取失败,读取本地文件
print("使用本地README文件")
return get_local_readme()
# 读取本地 README 文件
def get_local_readme():
try:
introduction_file = config.get('introduction_file', 'Introduction.md')
if os.path.exists(introduction_file):
with open(introduction_file, 'r', encoding='utf-8') as f:
return markdown.markdown(f.read())
except Exception:
pass
# 如果都没有,返回默认信息
return "<p>这个人很懒,什么都没有留下~</p>"
@app.route('/')
def index():
github_info = get_github_user_info()
# 检查背景图片是否存在
background_image = config.get('background', {}).get('image', 'background.png')
# 构建完整的背景图片路径 - 检查多个可能的位置
possible_paths = [
os.path.join(os.getcwd(), background_image), # 当前目录
os.path.join(os.getcwd(), 'static', background_image) # static目录
]
background_exists = False
background_path = background_image # 默认使用配置中的路径
# 检查哪个路径是有效的
for path in possible_paths:
if os.path.exists(path):
background_exists = True
# 如果找到图片,使用相对路径供模板使用
if 'static' in path:
background_path = f'/static/{background_image}'
break
print(f"背景图片配置: {background_image}")
print(f"背景图片存在: {background_exists}")
print(f"使用的背景图片路径: {background_path}")
return render_template('index.html',
github_info=github_info,
config=config,
now=datetime.now(),
background_exists=background_exists,
background_path=background_path)
@app.route('/api/config')
def get_config():
return jsonify(config)
# 提供根目录下的静态文件访问
@app.route('/<path:filename>')
def serve_root_file(filename):
# 只允许访问特定的文件类型
allowed_extensions = {'.jpg', '.jpeg', '.png', '.gif', '.svg', '.css', '.js'}
file_ext = os.path.splitext(filename)[1].lower()
if file_ext in allowed_extensions:
try:
return send_from_directory(os.getcwd(), filename)
except FileNotFoundError:
abort(404)
# 对于不允许的文件类型,返回404
abort(404)
def generate_static_html():
"""
生成静态HTML文件,用于部署到GitHub Pages
"""
print("开始生成静态HTML文件...")
# 创建静态文件目录
static_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static_build')
if not os.path.exists(static_dir):
os.makedirs(static_dir)
else:
# 清空目录中的所有文件
for file in os.listdir(static_dir):
file_path = os.path.join(static_dir, file)
if os.path.isfile(file_path):
os.remove(file_path)
try:
# 确保配置已加载
if 'config' not in globals():
print("加载配置文件...")
try:
with open('config.json', 'r', encoding='utf-8') as f:
config = json.load(f)
except FileNotFoundError:
# 默认配置
config = {
"github_url": "https://github.com/example",
"dark_mode": "auto",
"name": "Example User",
"bio": "Python Developer",
"introduction_file": "Introduction.md",
"github_token": "",
"theme": {
"primary_color": "#6a11cb",
"secondary_color": "#2575fc"
},
"background": {
"image": "background.png",
"blur": 8,
"overlay_opacity": 0.6,
"overlay_color": "#121212"
}
}
# 读取介绍内容
introduction_content = ""
if 'introduction_file' in config and os.path.exists(config['introduction_file']):
try:
with open(config['introduction_file'], 'r', encoding='utf-8') as f:
introduction_content = f.read()
# 转换Markdown为HTML
introduction_content = markdown.markdown(introduction_content)
except Exception as e:
print(f"警告:无法读取或解析介绍文件: {e}")
# 创建一个临时的Jinja2环境来渲染模板
template_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates')
env = jinja2.Environment(
loader=jinja2.FileSystemLoader(template_dir),
autoescape=jinja2.select_autoescape(['html', 'xml'])
)
# 获取模板
template = env.get_template('index.html')
# 准备渲染参数
render_args = {
'config': config,
'introduction_content': introduction_content,
'tech_stack': [], # 由于没有GitHub数据,这里使用空列表
'activity_data': [], # 由于没有GitHub数据,这里使用空列表
'error_message': None,
'github_info': {
'name': config.get('name', 'Example User'),
'bio': config.get('bio', ''),
'avatar_url': 'https://avatars.githubusercontent.com/u/10000000?v=4', # 默认头像
'html_url': config.get('github_url', 'https://github.com/example'),
'public_repos': 0,
'followers': 0,
'activity_data': [] # 添加可序列化的activity_data键
},
'now': datetime.now() # 添加当前时间对象,用于显示年份
}
# 渲染模板
print("渲染HTML模板...")
html_content = template.render(**render_args)
# 保存HTML文件
html_path = os.path.join(static_dir, 'index.html')
with open(html_path, 'w', encoding='utf-8') as f:
f.write(html_content)
print(f"静态HTML文件已保存到: {html_path}")
# 复制必要的静态资源
resources_to_copy = []
# 检查是否有背景图片
if 'background' in config and 'image' in config['background']:
background_image = config['background']['image']
if os.path.exists(background_image):
resources_to_copy.append(background_image)
# 检查一些常见的资源文件
for file in ['background.jpg', '1background.jpg', 'favicon.ico']:
if os.path.exists(file):
resources_to_copy.append(file)
# 复制资源文件
for resource in resources_to_copy:
try:
dst = os.path.join(static_dir, os.path.basename(resource))
shutil.copy(resource, dst)
print(f"已复制资源文件: {resource}")
except Exception as e:
print(f"警告:无法复制资源文件 {resource}: {e}")
print("\n静态文件生成成功!")
print(f"\n如何部署到GitHub Pages:")
print("1. 进入static_build目录")
print("2. 初始化git仓库(如果尚未初始化)")
print("3. 添加并提交所有文件")
print("4. 将文件推送到GitHub仓库的gh-pages分支")
print("\n示例命令:")
print("cd static_build")
print("git init")
print("git add .")
print("git commit -m 'Deploy to GitHub Pages'")
print("git remote add origin https://github.com/YOUR_USERNAME/YOUR_REPO.git")
print("git push -f origin master:gh-pages")
print("\n注意:您需要替换YOUR_USERNAME和YOUR_REPO为您的GitHub用户名和仓库名。")
except Exception as e:
print(f"错误:生成静态文件失败: {e}")
import traceback
traceback.print_exc()
return False
return True
if __name__ == '__main__':
# 检查是否需要生成静态HTML
if len(sys.argv) > 1 and sys.argv[1] == 'generate_static':
generate_static_html()
else:
# 设置环境变量,使得 GitHub Pages 能够正确运行
os.environ['FLASK_APP'] = 'app.py'
# 在开发环境中运行
app.run(debug=True, host='0.0.0.0', port=5000)