refactor(project): 全面清理项目代码并重命名项目
- 移除无用文件和空文件夹,清理 effects 和 scripts 目录 - 将项目从 ruixin-website-react 重命名为 novalon-website-react - 修复所有测试用例,确保 731 个测试全部通过 - 优化组件导入路径和测试 mock 设置 - 更新项目配置文件和依赖管理 关联任务:项目清理与重构
This commit is contained in:
@@ -1,37 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""对比两个字体文件"""
|
||||
|
||||
from fontTools.ttLib import TTFont
|
||||
from fontTools.ttLib.tables import _h_m_t_x, _g_a_s_p
|
||||
|
||||
original_hmtx = _h_m_t_x.table__h_m_t_x.decompile
|
||||
def patched_hmtx(self, data, ttFont):
|
||||
try: return original_hmtx(self, data, ttFont)
|
||||
except: self.metrics = {}
|
||||
_h_m_t_x.table__h_m_t_x.decompile = patched_hmtx
|
||||
|
||||
original_gasp = _g_a_s_p.table__g_a_s_p.decompile
|
||||
def patched_gasp(self, data, ttFont):
|
||||
try: return original_gasp(self, data, ttFont)
|
||||
except: self.gaspRanges = {}
|
||||
_g_a_s_p.table__g_a_s_p.decompile = patched_gasp
|
||||
|
||||
print('=== public/fonts/AoyagiReisho.ttf ===')
|
||||
f1 = TTFont('public/fonts/AoyagiReisho.ttf')
|
||||
cmap1 = f1.getBestCmap()
|
||||
print('U+9060 遠:', 0x9060 in cmap1)
|
||||
print('U+8fdc 远:', 0x8fdc in cmap1)
|
||||
print('字形数:', len(f1.getGlyphOrder()))
|
||||
print('GSUB:', 'GSUB' in f1)
|
||||
f1.close()
|
||||
|
||||
print()
|
||||
print('=== src/app/fonts/AoyagiReisho.ttf ===')
|
||||
f2 = TTFont('src/app/fonts/AoyagiReisho.ttf')
|
||||
cmap2 = f2.getBestCmap()
|
||||
print('U+9060 遠:', 0x9060 in cmap2)
|
||||
print('U+8fdc 远:', 0x8fdc in cmap2)
|
||||
print('字形数:', len(f2.getGlyphOrder()))
|
||||
print('GSUB:', 'GSUB' in f2)
|
||||
f2.close()
|
||||
@@ -1,134 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""将青柳隷書字体中的文字转换为 SVG 路径"""
|
||||
|
||||
from fontTools.ttLib import TTFont
|
||||
from fontTools.ttLib.tables import _h_m_t_x, _g_a_s_p
|
||||
import os
|
||||
|
||||
# 修补表解析
|
||||
original_hmtx = _h_m_t_x.table__h_m_t_x.decompile
|
||||
def patched_hmtx(self, data, ttFont):
|
||||
try: return original_hmtx(self, data, ttFont)
|
||||
except: self.metrics = {}
|
||||
_h_m_t_x.table__h_m_t_x.decompile = patched_hmtx
|
||||
|
||||
original_gasp = _g_a_s_p.table__g_a_s_p.decompile
|
||||
def patched_gasp(self, data, ttFont):
|
||||
try: return original_gasp(self, data, ttFont)
|
||||
except: self.gaspRanges = {}
|
||||
_g_a_s_p.table__g_a_s_p.decompile = patched_gasp
|
||||
|
||||
def get_glyph_path(font, char):
|
||||
"""获取字符的 SVG 路径"""
|
||||
cmap = font.getBestCmap()
|
||||
codepoint = ord(char)
|
||||
|
||||
if codepoint not in cmap:
|
||||
print(f"警告: 字符 '{char}' (U+{codepoint:04X}) 不在字体中")
|
||||
return None, None
|
||||
|
||||
glyph_name = cmap[codepoint]
|
||||
|
||||
# 获取 glyf 表
|
||||
glyf_table = font['glyf']
|
||||
glyph = glyf_table[glyph_name]
|
||||
|
||||
# 获取度量
|
||||
hmtx = font['hmtx']
|
||||
advance_width, lsb = hmtx[glyph_name]
|
||||
|
||||
# 获取边界框
|
||||
if hasattr(glyph, 'xMin') and glyph.xMin is not None:
|
||||
bbox = (glyph.xMin, glyph.yMin, glyph.xMax, glyph.yMax)
|
||||
else:
|
||||
bbox = (0, 0, advance_width, 1000)
|
||||
|
||||
# 获取字形轮廓
|
||||
try:
|
||||
coords, endPts, flags = glyph.getCoordinates(glyf_table)
|
||||
except:
|
||||
print(f" 无法获取轮廓: {glyph_name}")
|
||||
return None, None
|
||||
|
||||
# 构建 SVG 路径
|
||||
path_parts = []
|
||||
start_idx = 0
|
||||
|
||||
for end_pt in endPts:
|
||||
contour_coords = coords[start_idx:end_pt + 1]
|
||||
contour_flags = flags[start_idx:end_pt + 1]
|
||||
|
||||
if len(contour_coords) > 0:
|
||||
path_parts.append(f"M {contour_coords[0][0]:.2f} {-contour_coords[0][1]:.2f}")
|
||||
|
||||
for i in range(1, len(contour_coords)):
|
||||
x, y = contour_coords[i]
|
||||
path_parts.append(f"L {x:.2f} {-y:.2f}")
|
||||
|
||||
path_parts.append("Z")
|
||||
|
||||
start_idx = end_pt + 1
|
||||
|
||||
return " ".join(path_parts), {'advance': advance_width, 'lsb': lsb, 'bbox': bbox}
|
||||
|
||||
# 加载字体
|
||||
font_path = 'public/fonts/AoyagiReisho.ttf'
|
||||
font = TTFont(font_path)
|
||||
|
||||
print("=" * 60)
|
||||
print("青柳隷書 字形路径提取")
|
||||
print("=" * 60)
|
||||
|
||||
chars = ['睿', '新', '致', '遠']
|
||||
glyphs_data = []
|
||||
|
||||
for char in chars:
|
||||
print(f"\n字符: {char} (U+{ord(char):04X})")
|
||||
path, metrics = get_glyph_path(font, char)
|
||||
if path and metrics:
|
||||
print(f" Advance: {metrics['advance']}, LSB: {metrics['lsb']}")
|
||||
print(f" BBox: {metrics['bbox']}")
|
||||
print(f" Path length: {len(path)} chars")
|
||||
glyphs_data.append({
|
||||
'char': char,
|
||||
'path': path,
|
||||
'metrics': metrics
|
||||
})
|
||||
|
||||
font.close()
|
||||
|
||||
# 生成 SVG
|
||||
print("\n" + "=" * 60)
|
||||
print("生成 SVG 文件...")
|
||||
print("=" * 60)
|
||||
|
||||
# 计算总宽度
|
||||
total_width = sum(g['metrics']['advance'] for g in glyphs_data)
|
||||
scale = 48 / 1000 # 缩放因子
|
||||
|
||||
svg_paths = []
|
||||
x_offset = 0
|
||||
|
||||
for g in glyphs_data:
|
||||
m = g['metrics']
|
||||
# 计算字符居中偏移
|
||||
char_width = m['advance'] * scale
|
||||
path = g['path']
|
||||
|
||||
# 缩放路径
|
||||
scaled_path = path
|
||||
for coord in [('M', 'L')]:
|
||||
pass # 路径已经是正确的格式
|
||||
|
||||
svg_paths.append(f''' <!-- {g['char']} -->
|
||||
<g transform="translate({x_offset:.2f}, 0) scale({scale})">
|
||||
<path d="{path}" fill="currentColor"/>
|
||||
</g>''')
|
||||
|
||||
x_offset += char_width
|
||||
|
||||
print(f"\n总宽度: {total_width * scale:.2f}px")
|
||||
print("\nSVG 路径组:")
|
||||
print("\n".join(svg_paths[:2]))
|
||||
print("...")
|
||||
@@ -1,67 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""生成包含繁体'遠'的字体子集"""
|
||||
|
||||
from fontTools.ttLib import TTFont
|
||||
from fontTools.ttLib.tables import _h_m_t_x, _g_a_s_p
|
||||
from fontTools.subset import Subsetter, Options
|
||||
|
||||
# 修补表解析以跳过损坏的数据
|
||||
original_hmtx = _h_m_t_x.table__h_m_t_x.decompile
|
||||
def patched_hmtx(self, data, ttFont):
|
||||
try:
|
||||
return original_hmtx(self, data, ttFont)
|
||||
except:
|
||||
self.metrics = {}
|
||||
_h_m_t_x.table__h_m_t_x.decompile = patched_hmtx
|
||||
|
||||
original_gasp = _g_a_s_p.table__g_a_s_p.decompile
|
||||
def patched_gasp(self, data, ttFont):
|
||||
try:
|
||||
return original_gasp(self, data, ttFont)
|
||||
except:
|
||||
self.gaspRanges = {}
|
||||
_g_a_s_p.table__g_a_s_p.decompile = patched_gasp
|
||||
|
||||
# 加载字体
|
||||
font = TTFont('src/app/fonts/AoyagiReisho.ttf')
|
||||
|
||||
# 删除损坏的表
|
||||
for t in ['vmtx', 'gasp', 'VORG', 'mort', 'morx']:
|
||||
if t in font:
|
||||
del font[t]
|
||||
print(f'Deleted table: {t}')
|
||||
|
||||
# 创建子集器
|
||||
subsetter = Subsetter()
|
||||
options = Options()
|
||||
options.drop_tables = ['gasp', 'vmtx', 'VORG', 'mort', 'morx', 'GSUB', 'GPOS', 'GDEF']
|
||||
subsetter.options = options
|
||||
|
||||
# 目标字符: 睿(0x777f), 新(0x65b0), 致(0x81f4), 遠(0x9060), 空格(0x20)
|
||||
unicodes = [0x20, 0x777f, 0x65b0, 0x81f4, 0x9060]
|
||||
print(f'Target Unicode: {[hex(u) for u in unicodes]}')
|
||||
|
||||
subsetter.populate(unicodes=unicodes)
|
||||
|
||||
# 执行子集化
|
||||
try:
|
||||
subsetter.subset(font)
|
||||
except Exception as e:
|
||||
print(f'Warning during subsetting: {e}')
|
||||
|
||||
# 保存
|
||||
output_path = 'src/app/fonts/AoyagiReisho-subset.ttf'
|
||||
font.save(output_path)
|
||||
font.close()
|
||||
print(f'Saved to: {output_path}')
|
||||
|
||||
# 验证
|
||||
verify_font = TTFont(output_path)
|
||||
cmap = verify_font.getBestCmap()
|
||||
chars = [chr(k) for k in sorted(cmap.keys())]
|
||||
codes = [hex(k) for k in sorted(cmap.keys())]
|
||||
print(f'Subset characters: {chars}')
|
||||
print(f'Unicode codes: {codes}')
|
||||
print(f'Contains U+9060 (遠): {0x9060 in cmap}')
|
||||
verify_font.close()
|
||||
@@ -1,180 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""将青柳隷書字体中的文字转换为 SVG 路径并生成 logo"""
|
||||
|
||||
from fontTools.ttLib import TTFont
|
||||
from fontTools.ttLib.tables import _h_m_t_x, _g_a_s_p
|
||||
import os
|
||||
|
||||
# 修补表解析
|
||||
original_hmtx = _h_m_t_x.table__h_m_t_x.decompile
|
||||
def patched_hmtx(self, data, ttFont):
|
||||
try: return original_hmtx(self, data, ttFont)
|
||||
except: self.metrics = {}
|
||||
_h_m_t_x.table__h_m_t_x.decompile = patched_hmtx
|
||||
|
||||
original_gasp = _g_a_s_p.table__g_a_s_p.decompile
|
||||
def patched_gasp(self, data, ttFont):
|
||||
try: return original_gasp(self, data, ttFont)
|
||||
except: self.gaspRanges = {}
|
||||
_g_a_s_p.table__g_a_s_p.decompile = patched_gasp
|
||||
|
||||
def get_glyph_path(font, char):
|
||||
"""获取字符的 SVG 路径"""
|
||||
cmap = font.getBestCmap()
|
||||
codepoint = ord(char)
|
||||
|
||||
if codepoint not in cmap:
|
||||
return None, None
|
||||
|
||||
glyph_name = cmap[codepoint]
|
||||
glyf_table = font['glyf']
|
||||
glyph = glyf_table[glyph_name]
|
||||
hmtx = font['hmtx']
|
||||
advance_width, lsb = hmtx[glyph_name]
|
||||
|
||||
try:
|
||||
coords, endPts, flags = glyph.getCoordinates(glyf_table)
|
||||
except:
|
||||
return None, None
|
||||
|
||||
path_parts = []
|
||||
start_idx = 0
|
||||
|
||||
for end_pt in endPts:
|
||||
contour_coords = coords[start_idx:end_pt + 1]
|
||||
if len(contour_coords) > 0:
|
||||
path_parts.append(f"M {contour_coords[0][0]:.2f} {-contour_coords[0][1]:.2f}")
|
||||
for i in range(1, len(contour_coords)):
|
||||
x, y = contour_coords[i]
|
||||
path_parts.append(f"L {x:.2f} {-y:.2f}")
|
||||
path_parts.append("Z")
|
||||
start_idx = end_pt + 1
|
||||
|
||||
return " ".join(path_parts), {'advance': advance_width, 'lsb': lsb}
|
||||
|
||||
# 加载字体
|
||||
font = TTFont('public/fonts/AoyagiReisho.ttf')
|
||||
|
||||
chars = ['睿', '新', '致', '遠']
|
||||
glyphs_data = []
|
||||
|
||||
for char in chars:
|
||||
path, metrics = get_glyph_path(font, char)
|
||||
if path and metrics:
|
||||
glyphs_data.append({'char': char, 'path': path, 'metrics': metrics})
|
||||
|
||||
font.close()
|
||||
|
||||
# 生成主标题 SVG 路径
|
||||
scale = 48 / 1000
|
||||
total_width = sum(g['metrics']['advance'] for g in glyphs_data) * scale
|
||||
|
||||
svg_title_paths = []
|
||||
x_offset = 0
|
||||
for g in glyphs_data:
|
||||
svg_title_paths.append(f''' <g transform="translate({x_offset:.2f}, 0) scale({scale})">
|
||||
<path d="{g['path']}" fill="currentColor"/>
|
||||
</g>''')
|
||||
x_offset += g['metrics']['advance'] * scale
|
||||
|
||||
# 生成印章内文字 (较小尺寸)
|
||||
scale_seal = 26 / 1000
|
||||
|
||||
# 睿新
|
||||
svg_seal_line1 = []
|
||||
x_offset = 0
|
||||
for char in ['睿', '新']:
|
||||
g = next((x for x in glyphs_data if x['char'] == char), None)
|
||||
if g:
|
||||
svg_seal_line1.append(f''' <g transform="translate({x_offset:.2f}, 0) scale({scale_seal})">
|
||||
<path d="{g['path']}" fill="white"/>
|
||||
</g>''')
|
||||
x_offset += g['metrics']['advance'] * scale_seal
|
||||
|
||||
# 致遠
|
||||
svg_seal_line2 = []
|
||||
x_offset = 0
|
||||
for char in ['致', '遠']:
|
||||
g = next((x for x in glyphs_data if x['char'] == char), None)
|
||||
if g:
|
||||
svg_seal_line2.append(f''' <g transform="translate({x_offset:.2f}, 0) scale({scale_seal})">
|
||||
<path d="{g['path']}" fill="white"/>
|
||||
</g>''')
|
||||
x_offset += g['metrics']['advance'] * scale_seal
|
||||
|
||||
# 计算印章文字居中偏移
|
||||
line1_width = sum(g['metrics']['advance'] for g in glyphs_data if g['char'] in ['睿', '新']) * scale_seal
|
||||
line2_width = sum(g['metrics']['advance'] for g in glyphs_data if g['char'] in ['致', '遠']) * scale_seal
|
||||
seal_center = 43 # 印章中心 x 坐标
|
||||
line1_x = seal_center - line1_width / 2
|
||||
line2_x = seal_center - line2_width / 2
|
||||
|
||||
# 生成完整 SVG
|
||||
svg_content = f'''<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 480 120" width="480" height="120">
|
||||
<defs>
|
||||
<!-- 印章纹理滤镜 -->
|
||||
<filter id="sealTexture" x="0%" y="0%" width="100%" height="100%">
|
||||
<feTurbulence type="fractalNoise" baseFrequency="0.1" numOctaves="3" result="noise"/>
|
||||
<feDisplacementMap in="SourceGraphic" in2="noise" scale="2" xChannelSelector="R" yChannelSelector="G"/>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<!-- 红色印章 - 手绘不规则风格 -->
|
||||
<g transform="translate(12, 12)">
|
||||
<!-- 印章外框 - 不规则手绘路径 -->
|
||||
<path d="M8,2
|
||||
C25,-2 45,-2 72,3
|
||||
C82,5 85,12 84,25
|
||||
C83,40 85,55 84,70
|
||||
C83,82 78,88 65,89
|
||||
C45,91 25,90 10,88
|
||||
C2,86 -2,78 1,65
|
||||
C3,50 2,35 1,20
|
||||
C0,10 3,4 8,2 Z"
|
||||
fill="#C41E3A"/>
|
||||
<!-- 印章内框 - 手绘风格 -->
|
||||
<path d="M14,10
|
||||
C28,8 55,8 72,12
|
||||
C78,14 79,20 78,30
|
||||
C77,45 78,60 77,72
|
||||
C76,80 72,84 62,85
|
||||
C45,86 28,85 16,83
|
||||
C10,82 8,76 9,65
|
||||
C10,50 9,35 8,22
|
||||
C7,15 10,11 14,10 Z"
|
||||
fill="none" stroke="#fff" stroke-width="1.5" opacity="0.5"/>
|
||||
<!-- 睿新 - 书法字体路径 -->
|
||||
<g transform="translate({line1_x:.2f}, 38)">
|
||||
{chr(10).join(svg_seal_line1)}
|
||||
</g>
|
||||
<!-- 致遠 - 书法字体路径 -->
|
||||
<g transform="translate({line2_x:.2f}, 70)">
|
||||
{chr(10).join(svg_seal_line2)}
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- 公司名称 -->
|
||||
<g transform="translate(110, 60)">
|
||||
<!-- 睿新致遠 - 书法字体路径 -->
|
||||
<g transform="translate(0, 0)">
|
||||
{chr(10).join(svg_title_paths)}
|
||||
</g>
|
||||
<!-- NOVALON - 英文字体 -->
|
||||
<text x="24" y="42" font-family="Arial, sans-serif" font-size="14.5" font-weight="500" fill="currentColor" letter-spacing="10.5">NOVALON</text>
|
||||
</g>
|
||||
</svg>'''
|
||||
|
||||
# 写入文件
|
||||
with open('public/logo.svg', 'w', encoding='utf-8') as f:
|
||||
f.write(svg_content)
|
||||
|
||||
print("✅ 已生成 public/logo.svg")
|
||||
|
||||
# 生成白色版本 (logo-white.svg)
|
||||
svg_white = svg_content.replace('fill="#C41E3A"', 'fill="currentColor"')
|
||||
with open('public/logo-white.svg', 'w', encoding='utf-8') as f:
|
||||
f.write(svg_white)
|
||||
|
||||
print("✅ 已生成 public/logo-white.svg")
|
||||
print(f"\n标题总宽度: {total_width:.2f}px")
|
||||
@@ -1,83 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "========================================="
|
||||
echo "Gitea OAuth2应用自动配置"
|
||||
echo "========================================="
|
||||
|
||||
echo ""
|
||||
echo "步骤1: 生成管理员Access Token..."
|
||||
# 使用正确的scope (all包含所有权限)
|
||||
OUTPUT=$(docker exec -u git forgejo gitea admin user generate-access-token \
|
||||
--username novalon-admin \
|
||||
--token-name oauth2-setup-$(date +%s) \
|
||||
--scopes all 2>&1)
|
||||
|
||||
echo "$OUTPUT"
|
||||
|
||||
# 从输出中提取token
|
||||
TOKEN=$(echo "$OUTPUT" | grep -oP 'Access token: \K.*' || echo "")
|
||||
|
||||
echo ""
|
||||
echo "步骤2: 使用Token创建OAuth2应用..."
|
||||
|
||||
if [ -n "$TOKEN" ]; then
|
||||
echo "Token已生成: ${TOKEN:0:20}..."
|
||||
|
||||
# 使用API创建OAuth2应用
|
||||
RESPONSE=$(docker exec forgejo curl -s -X POST "http://localhost:3000/api/v1/applications/oauth2" \
|
||||
-H "Authorization: token $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "Woodpecker CI",
|
||||
"redirect_uri": "https://ci.f.novalon.cn/authorize",
|
||||
"confidential_client": true
|
||||
}')
|
||||
|
||||
echo "API响应: $RESPONSE"
|
||||
|
||||
# 提取Client ID和Secret
|
||||
CLIENT_ID=$(echo "$RESPONSE" | grep -oP '"client_id":"\K[^"]+' || echo "")
|
||||
CLIENT_SECRET=$(echo "$RESPONSE" | grep -oP '"client_secret":"\K[^"]+' || echo "")
|
||||
|
||||
if [ -n "$CLIENT_ID" ] && [ -n "$CLIENT_SECRET" ]; then
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo "✅ OAuth2应用创建成功!"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
echo "Client ID: $CLIENT_ID"
|
||||
echo "Client Secret: $CLIENT_SECRET"
|
||||
echo ""
|
||||
echo "请将以下内容添加到.env文件:"
|
||||
echo "WOODPECKER_FORGEJO_CLIENT=$CLIENT_ID"
|
||||
echo "WOODPECKER_FORGEJO_SECRET=$CLIENT_SECRET"
|
||||
echo ""
|
||||
echo "然后重启Woodpecker服务:"
|
||||
echo "cd /home/novalon/docker-app/novalon-cicd"
|
||||
echo "docker-compose restart woodpecker-server"
|
||||
echo "========================================="
|
||||
exit 0
|
||||
else
|
||||
echo "警告: 无法从API响应中提取凭证"
|
||||
fi
|
||||
else
|
||||
echo "警告: 无法生成Token"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo "⚠️ 自动配置失败,请手动完成"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
echo "1. 访问 https://git.f.novalon.cn"
|
||||
echo "2. 登录凭证:"
|
||||
echo " 用户名: novalon-admin"
|
||||
echo " 密码: Novalon@Admin2026"
|
||||
echo ""
|
||||
echo "3. 创建OAuth2应用:"
|
||||
echo " 头像 -> 设置 -> 应用 -> OAuth2应用 -> 创建应用"
|
||||
echo " 名称: Woodpecker CI"
|
||||
echo " 重定向URI: https://ci.f.novalon.cn/authorize"
|
||||
echo ""
|
||||
echo "4. 记录Client ID和Secret并更新.env文件"
|
||||
echo "========================================="
|
||||
@@ -1,51 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "========================================="
|
||||
echo "Gitea OAuth2应用配置"
|
||||
echo "========================================="
|
||||
|
||||
echo ""
|
||||
echo "步骤1: 生成管理员Access Token..."
|
||||
# 生成access token
|
||||
docker exec -u git forgejo gitea admin user generate-access-token \
|
||||
--username novalon-admin \
|
||||
--token-name oauth2-setup \
|
||||
--scopes write:application,read:application,write:user,read:user
|
||||
|
||||
echo ""
|
||||
echo "步骤2: 从数据库获取Token..."
|
||||
# 从数据库获取token (Gitea存储的是hash,我们需要原始token)
|
||||
# 查看access_token表
|
||||
docker exec postgresql psql -U forgejo -d forgejo -c \
|
||||
"SELECT id, uid, name, created_unix FROM access_token WHERE name='oauth2-setup' ORDER BY created_unix DESC LIMIT 1;"
|
||||
|
||||
echo ""
|
||||
echo "步骤3: 尝试使用API创建OAuth2应用..."
|
||||
# 由于我们无法直接获取原始token,让我们使用Web UI方式
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo "请手动完成以下步骤:"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
echo "1. 访问 https://git.f.novalon.cn"
|
||||
echo "2. 使用以下凭证登录:"
|
||||
echo " 用户名: novalon-admin"
|
||||
echo " 密码: Novalon@Admin2026"
|
||||
echo ""
|
||||
echo "3. 点击右上角头像 -> 设置 -> 应用 -> OAuth2应用"
|
||||
echo "4. 点击'创建新的OAuth2应用'"
|
||||
echo "5. 填写以下信息:"
|
||||
echo " 应用名称: Woodpecker CI"
|
||||
echo " 重定向URI: https://ci.f.novalon.cn/authorize"
|
||||
echo "6. 点击'创建应用'"
|
||||
echo "7. 记录生成的Client ID和Client Secret"
|
||||
echo ""
|
||||
echo "8. 将凭证更新到.env文件:"
|
||||
echo " WOODPECKER_FORGEJO_CLIENT=<Client ID>"
|
||||
echo " WOODPECKER_FORGEJO_SECRET=<Client Secret>"
|
||||
echo ""
|
||||
echo "9. 重启Woodpecker服务:"
|
||||
echo " cd /home/novalon/docker-app/novalon-cicd"
|
||||
echo " docker-compose restart woodpecker-server"
|
||||
echo ""
|
||||
echo "========================================="
|
||||
@@ -1,60 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "========================================="
|
||||
echo "Gitea SSO集成配置脚本"
|
||||
echo "========================================="
|
||||
|
||||
echo ""
|
||||
echo "步骤1: 创建Gitea管理员账户..."
|
||||
# 创建管理员账户(使用novalon-admin而不是admin)
|
||||
docker exec -u git forgejo gitea admin user create \
|
||||
--username novalon-admin \
|
||||
--password Novalon@Admin2026 \
|
||||
--email admin@novalon.cn \
|
||||
--admin \
|
||||
--must-change-password=false
|
||||
|
||||
echo ""
|
||||
echo "步骤2: 创建Woodpecker CI OAuth2应用..."
|
||||
# 使用Gitea API创建OAuth2应用
|
||||
# 首先获取管理员token
|
||||
TOKEN=$(docker exec -u git forgejo gitea admin user generate-access-token \
|
||||
--username novalon-admin \
|
||||
--token-name woodpecker-setup \
|
||||
--scopes write:application,read:application 2>&1 | grep -oP 'Access token: \K.*')
|
||||
|
||||
echo "管理员Token: $TOKEN"
|
||||
|
||||
# 使用API创建OAuth2应用
|
||||
RESPONSE=$(curl -s -X POST "http://localhost:3001/api/v1/applications/oauth2" \
|
||||
-H "Authorization: token $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "Woodpecker CI",
|
||||
"redirect_uri": "https://ci.f.novalon.cn/authorize"
|
||||
}')
|
||||
|
||||
echo "OAuth2应用创建响应: $RESPONSE"
|
||||
|
||||
# 提取Client ID和Secret
|
||||
CLIENT_ID=$(echo "$RESPONSE" | grep -oP '"client_id":"\K[^"]+')
|
||||
CLIENT_SECRET=$(echo "$RESPONSE" | grep -oP '"client_secret":"\K[^"]+')
|
||||
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo "配置完成!"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
echo "管理员账户:"
|
||||
echo " 用户名: novalon-admin"
|
||||
echo " 密码: Novalon@Admin2026"
|
||||
echo " 邮箱: admin@novalon.cn"
|
||||
echo ""
|
||||
echo "OAuth2凭证:"
|
||||
echo " Client ID: $CLIENT_ID"
|
||||
echo " Client Secret: $CLIENT_SECRET"
|
||||
echo ""
|
||||
echo "请将以下内容添加到.env文件:"
|
||||
echo " WOODPECKER_FORGEJO_CLIENT=$CLIENT_ID"
|
||||
echo " WOODPECKER_FORGEJO_SECRET=$CLIENT_SECRET"
|
||||
echo "========================================="
|
||||
@@ -1,31 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "========================================="
|
||||
echo "Docker Registry认证配置"
|
||||
echo "========================================="
|
||||
|
||||
echo ""
|
||||
echo "方案1: 使用htpasswd基础认证(推荐用于快速部署)"
|
||||
echo "----------------------------------------"
|
||||
|
||||
# 创建htpasswd文件
|
||||
echo "创建Registry用户..."
|
||||
docker run --rm -v /home/novalon/docker-app/novalon-cicd/registry_auth:/auth httpd:alpine htpasswd -Bbn novalon-admin Novalon@Registry2026 > /home/novalon/docker-app/novalon-cicd/registry_auth/htpasswd
|
||||
|
||||
echo "✅ htpasswd文件已创建"
|
||||
|
||||
echo ""
|
||||
echo "方案2: 使用Gitea Token认证(高级方案)"
|
||||
echo "----------------------------------------"
|
||||
echo "Docker Registry支持Token认证,可以与Gitea OAuth2集成。"
|
||||
echo "但这需要额外的Token服务(如docker_auth)。"
|
||||
echo ""
|
||||
echo "当前配置:"
|
||||
echo " Registry OAuth2 Client ID: 58c26bfc-f3f7-46f4-9096-3b532d6ab154"
|
||||
echo " Registry OAuth2 Secret: gto_cc5cntwcds5lna66yjnlzlt5y5vkm2i272p2bqt6zxwwxi57cmfa"
|
||||
echo ""
|
||||
echo "建议:"
|
||||
echo "1. 当前使用htpasswd认证(用户名/密码)"
|
||||
echo "2. 后续可部署docker_auth实现OAuth2集成"
|
||||
echo ""
|
||||
echo "========================================="
|
||||
@@ -1,25 +0,0 @@
|
||||
const { calculateContrastRatio, meetsWCAGStandard } = require('../src/lib/color-contrast.ts');
|
||||
|
||||
console.log('Testing CSS color contrast...');
|
||||
|
||||
const primaryResult = meetsWCAGStandard('#1C1C1C', '#FFFFFF', 'AA', 'normal');
|
||||
console.log('Primary text (#1C1C1C) on background (#FFFFFF):', primaryResult);
|
||||
|
||||
const tertiaryResult = meetsWCAGStandard('#4A4A4A', '#FFFFFF', 'AA', 'normal');
|
||||
console.log('Tertiary text (#4A4A4A) on background (#FFFFFF):', tertiaryResult);
|
||||
|
||||
const mutedResult = meetsWCAGStandard('#6B6B6B', '#FFFFFF', 'AA', 'normal');
|
||||
console.log('Muted text (#6B6B6B) on background (#FFFFFF):', mutedResult);
|
||||
|
||||
console.log('\nExpected: All should pass (passes: true)');
|
||||
console.log('Actual results:');
|
||||
console.log('- Primary:', primaryResult.passes ? '✓ PASS' : '✗ FAIL', `(ratio: ${primaryResult.ratio.toFixed(2)}:1)`);
|
||||
console.log('- Tertiary:', tertiaryResult.passes ? '✓ PASS' : '✗ FAIL', `(ratio: ${tertiaryResult.ratio.toFixed(2)}:1)`);
|
||||
console.log('- Muted:', mutedResult.passes ? '✓ PASS' : '✗ FAIL', `(ratio: ${mutedResult.ratio.toFixed(2)}:1)`);
|
||||
|
||||
if (!primaryResult.passes || !tertiaryResult.passes || !mutedResult.passes) {
|
||||
console.log('\n⚠️ Some tests failed - need to optimize CSS variables');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log('\n✅ All tests passed!');
|
||||
@@ -1,15 +0,0 @@
|
||||
const { calculateContrastRatio, meetsWCAGStandard } = require('../src/lib/color-contrast.ts');
|
||||
|
||||
console.log('Testing color contrast functions...');
|
||||
|
||||
const ratio = calculateContrastRatio('#000000', '#FFFFFF');
|
||||
console.log('Black on white ratio:', ratio);
|
||||
console.log('Expected: ~21, Actual:', ratio);
|
||||
|
||||
const result = meetsWCAGStandard('#000000', '#FFFFFF', 'AA', 'normal');
|
||||
console.log('WCAG AA compliance:', result);
|
||||
|
||||
const lowContrastResult = meetsWCAGStandard('#808080', '#FFFFFF', 'AA', 'normal');
|
||||
console.log('Low contrast test:', lowContrastResult);
|
||||
|
||||
console.log('All tests completed!');
|
||||
@@ -1,68 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""验证字体子集与原始字体的字形一致性"""
|
||||
|
||||
from fontTools.ttLib import TTFont
|
||||
from fontTools.ttLib.tables import _h_m_t_x, _g_a_s_p
|
||||
import os
|
||||
|
||||
# 修补表解析
|
||||
original_hmtx = _h_m_t_x.table__h_m_t_x.decompile
|
||||
def patched_hmtx(self, data, ttFont):
|
||||
try: return original_hmtx(self, data, ttFont)
|
||||
except: self.metrics = {}
|
||||
_h_m_t_x.table__h_m_t_x.decompile = patched_hmtx
|
||||
|
||||
original_gasp = _g_a_s_p.table__g_a_s_p.decompile
|
||||
def patched_gasp(self, data, ttFont):
|
||||
try: return original_gasp(self, data, ttFont)
|
||||
except: self.gaspRanges = {}
|
||||
_g_a_s_p.table__g_a_s_p.decompile = patched_gasp
|
||||
|
||||
base = 'src/app/fonts'
|
||||
|
||||
# 加载字体
|
||||
original = TTFont(f'{base}/AoyagiReisho.ttf')
|
||||
subset = TTFont(f'{base}/AoyagiReisho-subset.ttf')
|
||||
|
||||
print("=" * 50)
|
||||
print("字体对比验证")
|
||||
print("=" * 50)
|
||||
|
||||
# 文件大小
|
||||
orig_size = os.path.getsize(f'{base}/AoyagiReisho.ttf')
|
||||
sub_size = os.path.getsize(f'{base}/AoyagiReisho-subset.ttf')
|
||||
print(f"\n原始字体大小: {orig_size / 1024:.1f} KB ({orig_size} bytes)")
|
||||
print(f"子集字体大小: {sub_size / 1024:.1f} KB ({sub_size} bytes)")
|
||||
|
||||
# CMAP 对比
|
||||
orig_cmap = original.getBestCmap()
|
||||
sub_cmap = subset.getBestCmap()
|
||||
|
||||
target_chars = [0x20, 0x777f, 0x65b0, 0x81f4, 0x9060]
|
||||
char_names = {0x20: '空格', 0x777f: '睿', 0x65b0: '新', 0x81f4: '致', 0x9060: '遠'}
|
||||
|
||||
print("\n字符映射对比:")
|
||||
for code in target_chars:
|
||||
name = char_names[code]
|
||||
orig_glyph = orig_cmap.get(code, 'MISSING')
|
||||
sub_glyph = sub_cmap.get(code, 'MISSING')
|
||||
match = "✓" if orig_glyph == sub_glyph else "✗"
|
||||
print(f" U+{code:04X} ({name}): 原始={orig_glyph}, 子集={sub_glyph} {match}")
|
||||
|
||||
# 字形数量
|
||||
print(f"\n字形数量:")
|
||||
print(f" 原始: {len(original.getGlyphOrder())}")
|
||||
print(f" 子集: {len(subset.getGlyphOrder())}")
|
||||
|
||||
# 表对比
|
||||
print("\n字体表:")
|
||||
orig_tables = set(original.keys())
|
||||
sub_tables = set(subset.keys())
|
||||
print(f" 原始表: {sorted(orig_tables)}")
|
||||
print(f" 子集表: {sorted(sub_tables)}")
|
||||
|
||||
original.close()
|
||||
subset.close()
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
Reference in New Issue
Block a user