449 lines
14 KiB
Bash
Executable File
449 lines
14 KiB
Bash
Executable File
#!/bin/bash
|
||
# www.tlyq.cc 内容管理脚本
|
||
# 兼容 macOS (Bash 3.2+) / Ubuntu / Rocky Linux
|
||
# 用法:bash edit-site-cc.sh
|
||
|
||
set -e
|
||
|
||
# ============================================================
|
||
# 配置
|
||
# ============================================================
|
||
SERVER="tgz"
|
||
SRC_DIR="/tmp/project-extract-2/turing-engine"
|
||
TRANS="src/lib/translations.ts"
|
||
FOOTER="src/components/Footer.tsx"
|
||
CONTACT="src/app/contact/page.tsx"
|
||
NEWS="src/app/news/page.tsx"
|
||
|
||
# ANSI 颜色
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
CYAN='\033[0;36m'
|
||
RED='\033[0;31m'
|
||
NC='\033[0m'
|
||
|
||
log() { printf "${GREEN}[✓]${NC} %s\n" "$1"; }
|
||
warn() { printf "${YELLOW}[!]${NC} %s\n" "$1"; }
|
||
err() { printf "${RED}[✗]${NC} %s\n" "$1"; }
|
||
info() { printf "${CYAN}[i]${NC} %s\n" "$1"; }
|
||
|
||
# 在服务器上执行命令
|
||
rsh() { ssh "$SERVER" "$@"; }
|
||
|
||
# 获取 translations.ts 中指定 marker 的行号
|
||
# 用法: get_line "marker" [nth] nth=1 返回第1个匹配,nth=2 返回第2个
|
||
get_line() {
|
||
local marker="$1"
|
||
local nth="${2:-1}"
|
||
rsh "grep -n \"$marker\" $SRC_DIR/$TRANS | sed -n '${nth}p' | cut -d: -f1"
|
||
}
|
||
|
||
# 在服务器文件中批量插入多行
|
||
# 用法: insert_lines <line1> <content1> <line2> <content2> ...
|
||
# 行号从大到小排序后依次插入,保证行号准确性
|
||
insert_lines() {
|
||
local args=("$@")
|
||
local pairs=""
|
||
local i=0
|
||
while [ $i -lt $# ]; do
|
||
local ln="${args[$i]}"
|
||
local ct="${args[$((i+1))]}"
|
||
# 构造 python 元组列表: (line_num, content)
|
||
if [ -n "$pairs" ]; then
|
||
pairs="$pairs,"
|
||
fi
|
||
# 转义内容中的反斜杠和引号
|
||
pairs="$pairs($ln, \"$(printf '%s' "$ct" | sed 's/\\/\\\\/g; s/"/\\"/g')\")"
|
||
i=$((i+2))
|
||
done
|
||
ssh "$SERVER" python3 - <<PYEOF
|
||
pairs = [$pairs]
|
||
with open('$SRC_DIR/$TRANS') as f:
|
||
lines = f.readlines()
|
||
# 按行号从大到小排序,避免插入后行号偏移
|
||
pairs.sort(reverse=True)
|
||
for line_num, content in pairs:
|
||
lines.insert(line_num, content + '\n')
|
||
with open('$SRC_DIR/$TRANS', 'w') as f:
|
||
f.writelines(lines)
|
||
PYEOF
|
||
}
|
||
|
||
# ============================================================
|
||
# 主菜单
|
||
# ============================================================
|
||
show_menu() {
|
||
echo ""
|
||
printf "${CYAN}=========================================${NC}\n"
|
||
printf "${CYAN} www.tlyq.cc 内容管理${NC}\n"
|
||
printf "${CYAN}=========================================${NC}\n"
|
||
echo ""
|
||
echo " 1) 修改联系方式(邮箱/电话/地址)"
|
||
echo " 2) 添加企业动态(新闻文章)"
|
||
echo " 3) 管理发展历程(时间线)"
|
||
echo " 4) 部署到服务器"
|
||
echo " 0) 退出"
|
||
echo ""
|
||
printf "请输入编号 (0-4): "
|
||
read choice
|
||
echo ""
|
||
case "$choice" in
|
||
1) edit_contact_info ;;
|
||
2) add_news ;;
|
||
3) manage_milestones ;;
|
||
4) deploy ;;
|
||
0) exit 0 ;;
|
||
*) warn "无效选择"; show_menu ;;
|
||
esac
|
||
}
|
||
|
||
# ============================================================
|
||
# 功能 1:修改联系方式
|
||
# ============================================================
|
||
edit_contact_info() {
|
||
printf "${CYAN}--- 修改联系方式 ---${NC}\n"
|
||
echo ""
|
||
|
||
# 获取当前值
|
||
CUR_EMAIL=$(rsh "grep -m1 'mailto:' $SRC_DIR/$FOOTER | sed 's/.*mailto:\\([^\\\"]*\\).*/\\1/'")
|
||
CUR_PHONE=$(rsh "grep -m1 'tel:' $SRC_DIR/$FOOTER | sed 's/.*tel:\\([^\\\"]*\\).*/\\1/'")
|
||
CUR_ADDR_ZH=$(rsh "grep 'addrText' $SRC_DIR/$TRANS | head -1 | sed \"s/.*addrText: '\\([^']*\\)'.*/\\1/\"")
|
||
CUR_ADDR_EN=$(rsh "grep 'addrText' $SRC_DIR/$TRANS | tail -1 | sed \"s/.*addrText: '\\([^']*\\)'.*/\\1/\"")
|
||
|
||
info "当前邮箱: $CUR_EMAIL"
|
||
info "当前电话: $CUR_PHONE"
|
||
info "当前地址(中文): $CUR_ADDR_ZH"
|
||
info "当前地址(英文): $CUR_ADDR_EN"
|
||
echo ""
|
||
|
||
printf "新邮箱 (回车跳过): "
|
||
read NEW_EMAIL
|
||
printf "新电话 (回车跳过): "
|
||
read NEW_PHONE
|
||
printf "新地址-中文 (回车跳过): "
|
||
read NEW_ADDR_ZH
|
||
printf "新地址-英文 (回车跳过): "
|
||
read NEW_ADDR_EN
|
||
|
||
if [ -n "$NEW_EMAIL" ]; then
|
||
info "替换邮箱: $CUR_EMAIL → $NEW_EMAIL"
|
||
rsh "sed -i 's/$CUR_EMAIL/$NEW_EMAIL/g' $SRC_DIR/$FOOTER $SRC_DIR/$CONTACT $SRC_DIR/$NEWS"
|
||
log "邮箱已更新(Footer、联系我们、企业动态 3个文件)"
|
||
fi
|
||
|
||
if [ -n "$NEW_PHONE" ]; then
|
||
info "替换电话: $CUR_PHONE → $NEW_PHONE"
|
||
rsh "sed -i 's/$CUR_PHONE/$NEW_PHONE/g' $SRC_DIR/$FOOTER $SRC_DIR/$CONTACT"
|
||
log "电话已更新(Footer、联系我们 2个文件)"
|
||
fi
|
||
|
||
if [ -n "$NEW_ADDR_ZH" ]; then
|
||
info "替换地址(中文): $CUR_ADDR_ZH → $NEW_ADDR_ZH"
|
||
rsh "sed -i \"s/addrText: '$CUR_ADDR_ZH'/addrText: '$NEW_ADDR_ZH'/\" $SRC_DIR/$TRANS"
|
||
log "中文地址已更新"
|
||
fi
|
||
|
||
if [ -n "$NEW_ADDR_EN" ]; then
|
||
info "替换地址(英文): $CUR_ADDR_EN → $NEW_ADDR_EN"
|
||
rsh "sed -i \"s/addrText: '$CUR_ADDR_EN'/addrText: '$NEW_ADDR_EN'/\" $SRC_DIR/$TRANS"
|
||
log "英文地址已更新"
|
||
fi
|
||
|
||
if [ -z "$NEW_EMAIL" ] && [ -z "$NEW_PHONE" ] && [ -z "$NEW_ADDR_ZH" ] && [ -z "$NEW_ADDR_EN" ]; then
|
||
warn "未修改任何内容"
|
||
fi
|
||
|
||
ask_deploy
|
||
}
|
||
|
||
# ============================================================
|
||
# 功能 2:添加企业动态
|
||
# ============================================================
|
||
add_news() {
|
||
printf "${CYAN}--- 添加企业动态 ---${NC}\n"
|
||
echo ""
|
||
|
||
# 选择分类
|
||
echo "选择分类:"
|
||
echo " 1) 公司新闻"
|
||
echo " 2) 行业资讯"
|
||
printf "请选择 (1/2): "
|
||
read cat_choice
|
||
case "$cat_choice" in
|
||
1) ZH_CAT="公司新闻"; EN_CAT="Company News" ;;
|
||
2) ZH_CAT="行业资讯"; EN_CAT="Industry News" ;;
|
||
*) err "无效选择"; return ;;
|
||
esac
|
||
|
||
echo ""
|
||
printf "${YELLOW}--- 中文信息 ---${NC}\n"
|
||
printf "日期 (如 2026年4月): "
|
||
read ZH_DATE
|
||
[ -z "$ZH_DATE" ] && { err "日期不能为空"; return; }
|
||
printf "标题: "
|
||
read ZH_TITLE
|
||
[ -z "$ZH_TITLE" ] && { err "标题不能为空"; return; }
|
||
printf "摘要: "
|
||
read ZH_DESC
|
||
[ -z "$ZH_DESC" ] && { err "摘要不能为空"; return; }
|
||
printf "标签 (逗号分隔, 如 AI,产品): "
|
||
read ZH_TAGS_INPUT
|
||
[ -z "$ZH_TAGS_INPUT" ] && { err "标签不能为空"; return; }
|
||
|
||
# 构造 tags 数组字符串
|
||
ZH_TAGS=$(printf '%s' "$ZH_TAGS_INPUT" | sed "s/ *, */', '/g" | sed "s/^/['/" | sed "s/$/']/")
|
||
|
||
echo ""
|
||
printf "${YELLOW}--- English Info ---${NC}\n"
|
||
printf "Date (e.g. April 2026): "
|
||
read EN_DATE
|
||
[ -z "$EN_DATE" ] && { err "Date is required"; return; }
|
||
printf "Title: "
|
||
read EN_TITLE
|
||
[ -z "$EN_TITLE" ] && { err "Title is required"; return; }
|
||
printf "Description: "
|
||
read EN_DESC
|
||
[ -z "$EN_DESC" ] && { err "Description is required"; return; }
|
||
printf "Tags (comma separated, e.g. AI,Product): "
|
||
read EN_TAGS_INPUT
|
||
[ -z "$EN_TAGS_INPUT" ] && { err "Tags are required"; return; }
|
||
|
||
EN_TAGS=$(printf '%s' "$EN_TAGS_INPUT" | sed "s/ *, */', '/g" | sed "s/^/['/" | sed "s/$/']/")
|
||
|
||
# 预览
|
||
echo ""
|
||
printf "${CYAN}--- 确认信息 ---${NC}\n"
|
||
echo "分类: $ZH_CAT / $EN_CAT"
|
||
echo "日期: $ZH_DATE / $EN_DATE"
|
||
echo "标题: $ZH_TITLE"
|
||
echo " $EN_TITLE"
|
||
echo "摘要: $ZH_DESC"
|
||
echo " $EN_DESC"
|
||
echo "标签: $ZH_TAGS"
|
||
echo " $EN_TAGS"
|
||
echo ""
|
||
printf "确认添加? (y/n): "
|
||
read confirm
|
||
[ "$confirm" != "y" ] && { warn "已取消"; return; }
|
||
|
||
# 获取 zh 和 en 的 articles: [ 行号
|
||
ZH_ART_LINE=$(get_line "articles: \[" 1)
|
||
EN_ART_LINE=$(get_line "articles: \[" 2)
|
||
|
||
# 一次性插入中英文(按行号从大到小排序,保证准确性)
|
||
insert_lines \
|
||
"$ZH_ART_LINE" " { category: '$ZH_CAT', date: '$ZH_DATE', title: '$ZH_TITLE', desc: '$ZH_DESC', tags: $ZH_TAGS }," \
|
||
"$EN_ART_LINE" " { category: '$EN_CAT', date: '$EN_DATE', title: '$EN_TITLE', desc: '$EN_DESC', tags: $EN_TAGS },"
|
||
|
||
log "企业动态已添加(中英文)"
|
||
ask_deploy
|
||
}
|
||
|
||
# ============================================================
|
||
# 功能 3:管理发展历程
|
||
# ============================================================
|
||
manage_milestones() {
|
||
printf "${CYAN}--- 管理发展历程 ---${NC}\n"
|
||
echo ""
|
||
echo " 1) 添加新条目"
|
||
echo " 2) 修改现有条目"
|
||
echo " 0) 返回"
|
||
echo ""
|
||
printf "请选择 (0-2): "
|
||
read ms_choice
|
||
case "$ms_choice" in
|
||
1) add_milestone ;;
|
||
2) edit_milestone ;;
|
||
0) show_menu ;;
|
||
*) warn "无效选择"; manage_milestones ;;
|
||
esac
|
||
}
|
||
|
||
add_milestone() {
|
||
echo ""
|
||
printf "${YELLOW}--- 中文信息 ---${NC}\n"
|
||
printf "日期 (如 2026年4月): "
|
||
read ZH_DATE
|
||
[ -z "$ZH_DATE" ] && { err "日期不能为空"; return; }
|
||
printf "标题: "
|
||
read ZH_TITLE
|
||
[ -z "$ZH_TITLE" ] && { err "标题不能为空"; return; }
|
||
printf "描述: "
|
||
read ZH_DESC
|
||
[ -z "$ZH_DESC" ] && { err "描述不能为空"; return; }
|
||
|
||
echo ""
|
||
printf "${YELLOW}--- English Info ---${NC}\n"
|
||
printf "Date (e.g. Apr 2026): "
|
||
read EN_DATE
|
||
[ -z "$EN_DATE" ] && { err "Date is required"; return; }
|
||
printf "Title: "
|
||
read EN_TITLE
|
||
[ -z "$EN_TITLE" ] && { err "Title is required"; return; }
|
||
printf "Description: "
|
||
read EN_DESC
|
||
[ -z "$EN_DESC" ] && { err "Description is required"; return; }
|
||
|
||
echo ""
|
||
printf "确认添加? (y/n): "
|
||
read confirm
|
||
[ "$confirm" != "y" ] && { warn "已取消"; return; }
|
||
|
||
# 获取 zh 和 en 的 milestones: [ 行号
|
||
ZH_MS_LINE=$(get_line "milestones: \[" 1)
|
||
EN_MS_LINE=$(get_line "milestones: \[" 2)
|
||
|
||
# 一次性插入中英文
|
||
insert_lines \
|
||
"$ZH_MS_LINE" " { date: '$ZH_DATE', title: '$ZH_TITLE', desc: '$ZH_DESC' }," \
|
||
"$EN_MS_LINE" " { date: '$EN_DATE', title: '$EN_TITLE', desc: '$EN_DESC' },"
|
||
|
||
log "发展历程已添加(中英文)"
|
||
ask_deploy
|
||
}
|
||
|
||
edit_milestone() {
|
||
echo ""
|
||
info "当前发展历程 (中文):"
|
||
echo ""
|
||
|
||
# 获取 zh milestones 的起始行
|
||
ZH_MS_LINE=$(get_line "milestones: \[" 1)
|
||
EN_MS_LINE=$(get_line "milestones: \[" 2)
|
||
|
||
# 列出所有中文里程碑条目
|
||
rsh "awk 'NR>${ZH_MS_LINE} && /date:.*title:/{printf \" %d: %s\\n\", NR, \$0}' $SRC_DIR/$TRANS"
|
||
|
||
echo ""
|
||
printf "请输入要修改的条目行号: "
|
||
read LINE_NUM
|
||
[ -z "$LINE_NUM" ] && { err "行号不能为空"; return; }
|
||
|
||
# 验证行号是否有效
|
||
VALID=$(rsh "sed -n '${LINE_NUM}p' $SRC_DIR/$TRANS | grep -c 'date:.*title:' || true")
|
||
if [ "$VALID" != "1" ]; then
|
||
err "该行不是有效的历程条目"
|
||
return
|
||
fi
|
||
|
||
# 计算对应的英文行号
|
||
OFFSET=$((LINE_NUM - ZH_MS_LINE))
|
||
EN_LINE_NUM=$((EN_MS_LINE + OFFSET))
|
||
|
||
# 显示当前值
|
||
info "当前中文内容:"
|
||
rsh "sed -n '${LINE_NUM}p' $SRC_DIR/$TRANS"
|
||
info "当前英文内容:"
|
||
rsh "sed -n '${EN_LINE_NUM}p' $SRC_DIR/$TRANS"
|
||
|
||
echo ""
|
||
printf "${YELLOW}--- 新的中文信息 ---${NC}\n"
|
||
printf "日期: "
|
||
read ZH_DATE
|
||
[ -z "$ZH_DATE" ] && { err "日期不能为空"; return; }
|
||
printf "标题: "
|
||
read ZH_TITLE
|
||
[ -z "$ZH_TITLE" ] && { err "标题不能为空"; return; }
|
||
printf "描述: "
|
||
read ZH_DESC
|
||
[ -z "$ZH_DESC" ] && { err "描述不能为空"; return; }
|
||
|
||
echo ""
|
||
printf "${YELLOW}--- New English Info ---${NC}\n"
|
||
printf "Date: "
|
||
read EN_DATE
|
||
[ -z "$EN_DATE" ] && { err "Date is required"; return; }
|
||
printf "Title: "
|
||
read EN_TITLE
|
||
[ -z "$EN_TITLE" ] && { err "Title is required"; return; }
|
||
printf "Description: "
|
||
read EN_DESC
|
||
[ -z "$EN_DESC" ] && { err "Description is required"; return; }
|
||
|
||
echo ""
|
||
printf "确认修改? (y/n): "
|
||
read confirm
|
||
[ "$confirm" != "y" ] && { warn "已取消"; return; }
|
||
|
||
# 替换中文行
|
||
ZH_NEW=" { date: '$ZH_DATE', title: '$ZH_TITLE', desc: '$ZH_DESC' },"
|
||
# 转义替换内容中的特殊字符
|
||
ZH_NEW_ESC=$(printf '%s' "$ZH_NEW" | sed 's/[&/\\]/\\&/g')
|
||
rsh "sed -i '${LINE_NUM}s/.*/${ZH_NEW_ESC}/' $SRC_DIR/$TRANS"
|
||
|
||
# 替换英文行
|
||
EN_NEW=" { date: '$EN_DATE', title: '$EN_TITLE', desc: '$EN_DESC' },"
|
||
EN_NEW_ESC=$(printf '%s' "$EN_NEW" | sed 's/[&/\\]/\\&/g')
|
||
rsh "sed -i '${EN_LINE_NUM}s/.*/${EN_NEW_ESC}/' $SRC_DIR/$TRANS"
|
||
|
||
log "发展历程已更新(中英文)"
|
||
ask_deploy
|
||
}
|
||
|
||
# ============================================================
|
||
# 功能 4:部署到服务器
|
||
# ============================================================
|
||
deploy() {
|
||
printf "${CYAN}--- 部署 www.tlyq.cc ---${NC}\n"
|
||
echo ""
|
||
|
||
LOCAL_DIR="/Users/niuniu/programs/docker/www-cc/src"
|
||
REMOTE_DIR="$SRC_DIR"
|
||
DEPLOY_DIR="/root/docker/www-cc/html"
|
||
|
||
# 从服务器同步最新源码到本地
|
||
info "从服务器同步最新源码..."
|
||
rsync -az --delete --exclude='node_modules' --exclude='.next' --exclude='out' \
|
||
"$SERVER:$REMOTE_DIR/" "$LOCAL_DIR/" 2>/dev/null || {
|
||
warn "rsync 不可用,使用 scp..."
|
||
rm -rf "$LOCAL_DIR"
|
||
mkdir -p "$LOCAL_DIR"
|
||
scp -r "$SERVER:$REMOTE_DIR/." "$LOCAL_DIR/" 2>/dev/null
|
||
rm -rf "$LOCAL_DIR/node_modules" "$LOCAL_DIR/.next" "$LOCAL_DIR/out"
|
||
}
|
||
|
||
log "打包源码..."
|
||
cd "$LOCAL_DIR"
|
||
COPYFILE_DISABLE=1 tar czf /tmp/www-src.tar.gz --exclude='node_modules' --exclude='.next' --exclude='out' .
|
||
|
||
log "上传到服务器..."
|
||
scp /tmp/www-src.tar.gz "$SERVER:/tmp/www-src.tar.gz"
|
||
|
||
log "服务器上构建并部署..."
|
||
ssh "$SERVER" "cd $REMOTE_DIR && rm -rf .next out src && tar xzf /tmp/www-src.tar.gz && npm install && rm -rf .next out && npm run build && rm -rf ${DEPLOY_DIR:?}/* && cp -r out/* $DEPLOY_DIR/"
|
||
|
||
echo ""
|
||
log "验证部署..."
|
||
URL="https://www.tlyq.cc"
|
||
STATUS=$(ssh "$SERVER" "curl -s -o /dev/null -w '%{http_code}' -k '$URL'")
|
||
if [ "$STATUS" = "200" ]; then
|
||
log "部署成功!访问 $URL"
|
||
else
|
||
warn "返回状态码: $STATUS,请检查"
|
||
fi
|
||
|
||
echo ""
|
||
show_menu
|
||
}
|
||
|
||
# ============================================================
|
||
# 通用:询问是否部署
|
||
# ============================================================
|
||
ask_deploy() {
|
||
echo ""
|
||
printf "是否立即部署到服务器? (y/n): "
|
||
read d
|
||
if [ "$d" = "y" ]; then
|
||
deploy
|
||
else
|
||
warn "未部署,修改仅在服务器源码中生效"
|
||
info "稍后可选择菜单 4) 部署到服务器"
|
||
show_menu
|
||
fi
|
||
}
|
||
|
||
# ============================================================
|
||
# 启动
|
||
# ============================================================
|
||
show_menu
|