mirror of
https://gitee.com/mirrors/AllinSSL.git
synced 2026-03-13 10:00:53 +08:00
【同步】前端项目源码
【修复】工作流兼容问题
This commit is contained in:
7
frontend/scripts/clear.sh
Normal file
7
frontend/scripts/clear.sh
Normal file
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
# 查找并删除当前目录及子目录下所有 node_modules 文件夹和pnpm-lock.yaml文件 和 .turbo 文件夹
|
||||
find . -name "node_modules" -type d -prune -exec rm -rf {} +
|
||||
find . -name "pnpm-lock.yaml" -type f -delete
|
||||
find . -name "dist" -type d -prune -exec rm -rf {} +
|
||||
find . -name ".turbo" -type d -prune -exec rm -rf {} +
|
||||
echo "删除成功"
|
||||
620
frontend/scripts/cursor_backup.sh
Normal file
620
frontend/scripts/cursor_backup.sh
Normal file
@@ -0,0 +1,620 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 检测操作系统并设置相关变量
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
OS="macOS"
|
||||
SETTINGS_DIR="$HOME/Library/Application Support/Cursor"
|
||||
EXTENSIONS_DIR="$HOME/.cursor"
|
||||
USER_DIR="$SETTINGS_DIR/User"
|
||||
TEMP_ROOT="/tmp"
|
||||
PATH_SEP="/"
|
||||
STAT_CMD="stat -f"
|
||||
STAT_TIME_FORMAT="%Sm"
|
||||
STAT_SIZE_FORMAT="%z"
|
||||
elif [[ "$OSTYPE" == "msys" || "$OSTYPE" == "win32" || "$OSTYPE" == "cygwin" ]]; then
|
||||
OS="Windows"
|
||||
# Windows 下使用 APPDATA 环境变量
|
||||
if [ -n "$APPDATA" ]; then
|
||||
SETTINGS_DIR="$APPDATA/Cursor"
|
||||
EXTENSIONS_DIR="$APPDATA/Cursor"
|
||||
else
|
||||
SETTINGS_DIR="$HOME/AppData/Roaming/Cursor"
|
||||
EXTENSIONS_DIR="$HOME/AppData/Roaming/Cursor"
|
||||
fi
|
||||
USER_DIR="$SETTINGS_DIR/User"
|
||||
TEMP_ROOT="$TEMP"
|
||||
[ -z "$TEMP_ROOT" ] && TEMP_ROOT="$TMP"
|
||||
[ -z "$TEMP_ROOT" ] && TEMP_ROOT="$HOME/AppData/Local/Temp"
|
||||
PATH_SEP="/"
|
||||
STAT_CMD="stat -c"
|
||||
STAT_TIME_FORMAT="%y"
|
||||
STAT_SIZE_FORMAT="%s"
|
||||
else
|
||||
echo -e "${RED}错误: 不支持的操作系统${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 规范化路径
|
||||
normalize_path() {
|
||||
local path="$1"
|
||||
echo "$path" | sed 's/\\/\//g'
|
||||
}
|
||||
|
||||
# 备份目录
|
||||
BACKUP_DIR="$(normalize_path "$HOME/cursor_backups")"
|
||||
|
||||
# 检查目录是否存在
|
||||
if [ ! -d "$SETTINGS_DIR" ] && [ ! -d "$EXTENSIONS_DIR" ]; then
|
||||
echo -e "${RED}错误: 未找到 Cursor 目录${NC}"
|
||||
echo -e "${YELLOW}请确保 Cursor 编辑器已经安装并运行过至少一次${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 创建备份目录
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
# 获取文件修改时间
|
||||
get_file_time() {
|
||||
local file="$1"
|
||||
if [[ "$OS" == "Windows" ]]; then
|
||||
# 对于 Windows,使用兼容的时间格式
|
||||
$STAT_CMD "$STAT_TIME_FORMAT" "$file" 2>/dev/null || echo "Unknown"
|
||||
else
|
||||
$STAT_CMD "$STAT_TIME_FORMAT" "$file"
|
||||
fi
|
||||
}
|
||||
|
||||
# 获取文件大小
|
||||
get_file_size() {
|
||||
local file="$1"
|
||||
if [[ "$OS" == "Windows" ]]; then
|
||||
# 对于 Windows,使用兼容的大小获取方式
|
||||
$STAT_CMD "$STAT_SIZE_FORMAT" "$file" 2>/dev/null || echo "0"
|
||||
else
|
||||
$STAT_CMD "$STAT_SIZE_FORMAT" "$file"
|
||||
fi
|
||||
}
|
||||
|
||||
# 创建临时目录
|
||||
create_temp_dir() {
|
||||
local prefix="$1"
|
||||
local temp_dir
|
||||
|
||||
if [[ "$OS" == "Windows" ]]; then
|
||||
temp_dir="$(normalize_path "$TEMP_ROOT/$prefix")"
|
||||
else
|
||||
temp_dir="$TEMP_ROOT/$prefix"
|
||||
fi
|
||||
|
||||
mkdir -p "$temp_dir"
|
||||
echo "$temp_dir"
|
||||
}
|
||||
|
||||
# 获取下一个可用的序号
|
||||
get_next_sequence() {
|
||||
local date=$1
|
||||
local max_seq=0
|
||||
|
||||
# 查找同一天的备份,获取最大序号
|
||||
find "$BACKUP_DIR" -maxdepth 1 -type d -name "cursor_${date}_*" | while read -r backup; do
|
||||
if [ -d "$backup" ]; then
|
||||
backup_name=$(basename "$backup")
|
||||
# 提取日期和序号
|
||||
if [[ $backup_name =~ ^cursor_${date}_([0-9]+)$ ]]; then
|
||||
seq_num=${BASH_REMATCH[1]}
|
||||
if (( seq_num > max_seq )); then
|
||||
max_seq=$seq_num
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# 返回下一个序号
|
||||
echo $((max_seq + 1))
|
||||
}
|
||||
|
||||
# 获取插件列表
|
||||
get_extensions_list() {
|
||||
local extensions_dir="$(normalize_path "$1")"
|
||||
local output_file="$(normalize_path "$2")"
|
||||
|
||||
if [ ! -d "$extensions_dir/extensions" ]; then
|
||||
echo -e "${YELLOW}! 未找到插件目录${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo -e "${YELLOW}正在检查插件列表...${NC}"
|
||||
|
||||
# 创建一个临时文件来存储插件信息
|
||||
local temp_list="$(create_temp_dir "cursor_ext_list")/extensions.tmp"
|
||||
|
||||
# 遍历插件目录
|
||||
if [[ "$OS" == "Windows" ]]; then
|
||||
# Windows 环境使用 dir /b 命令
|
||||
(cd "$extensions_dir/extensions" && cmd //c "dir /b /ad" 2>/dev/null) | while read -r ext_name; do
|
||||
local package_json="$extensions_dir/extensions/$ext_name/package.json"
|
||||
package_json="$(normalize_path "$package_json")"
|
||||
if [ -f "$package_json" ]; then
|
||||
# 尝试从 package.json 中提取版本信息
|
||||
local version=$(grep -o '"version": *"[^"]*"' "$package_json" 2>/dev/null | cut -d'"' -f4)
|
||||
if [ -n "$version" ]; then
|
||||
echo "$ext_name@$version" >> "$temp_list"
|
||||
else
|
||||
echo "$ext_name" >> "$temp_list"
|
||||
fi
|
||||
else
|
||||
echo "$ext_name" >> "$temp_list"
|
||||
fi
|
||||
done
|
||||
else
|
||||
# Unix 环境使用 find 命令
|
||||
find "$extensions_dir/extensions" -maxdepth 1 -type d | while read -r ext_dir; do
|
||||
if [ "$ext_dir" != "$extensions_dir/extensions" ]; then
|
||||
local ext_name=$(basename "$ext_dir")
|
||||
local package_json="$ext_dir/package.json"
|
||||
if [ -f "$package_json" ]; then
|
||||
local version=$(grep -o '"version": *"[^"]*"' "$package_json" 2>/dev/null | cut -d'"' -f4)
|
||||
if [ -n "$version" ]; then
|
||||
echo "$ext_name@$version" >> "$temp_list"
|
||||
else
|
||||
echo "$ext_name" >> "$temp_list"
|
||||
fi
|
||||
else
|
||||
echo "$ext_name" >> "$temp_list"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# 排序插件列表
|
||||
if [ -f "$temp_list" ]; then
|
||||
sort "$temp_list" > "$output_file"
|
||||
rm -f "$temp_list"
|
||||
return 0
|
||||
fi
|
||||
|
||||
rm -f "$temp_list"
|
||||
return 1
|
||||
}
|
||||
|
||||
# 显示插件列表差异
|
||||
show_extensions_diff() {
|
||||
local backup_list="$1"
|
||||
local current_list="$2"
|
||||
|
||||
if [ ! -f "$backup_list" ] || [ ! -f "$current_list" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo -e "\n${YELLOW}插件对比:${NC}"
|
||||
|
||||
# 找出新增的插件
|
||||
echo -e "\n${GREEN}新增的插件:${NC}"
|
||||
comm -13 "$backup_list" "$current_list" | while read -r ext; do
|
||||
echo -e "${GREEN}+ $ext${NC}"
|
||||
done
|
||||
|
||||
# 找出删除的插件
|
||||
echo -e "\n${RED}删除的插件:${NC}"
|
||||
comm -23 "$backup_list" "$current_list" | while read -r ext; do
|
||||
echo -e "${RED}- $ext${NC}"
|
||||
done
|
||||
|
||||
# 找出相同的插件
|
||||
echo -e "\n${YELLOW}保持不变的插件:${NC}"
|
||||
comm -12 "$backup_list" "$current_list" | while read -r ext; do
|
||||
echo -e " $ext"
|
||||
done
|
||||
}
|
||||
|
||||
# 创建备份
|
||||
create_backup() {
|
||||
# 生成日期和序号
|
||||
DATE=$(date +"%Y%m%d")
|
||||
SEQ=$(get_next_sequence "$DATE")
|
||||
# 格式化序号为两位数
|
||||
printf -v SEQ_PADDED "%02d" "$SEQ"
|
||||
BACKUP_NAME="cursor_${DATE}_${SEQ_PADDED}"
|
||||
BACKUP_PATH="$BACKUP_DIR/$BACKUP_NAME"
|
||||
TEMP_PATH="/tmp/$BACKUP_NAME"
|
||||
|
||||
echo -e "${YELLOW}正在创建备份...${NC}"
|
||||
echo -e "${YELLOW}操作系统: $OS${NC}"
|
||||
echo -e "${YELLOW}设置目录: $SETTINGS_DIR${NC}"
|
||||
echo -e "${YELLOW}插件目录: $EXTENSIONS_DIR${NC}"
|
||||
|
||||
# 创建临时目录
|
||||
mkdir -p "$TEMP_PATH"
|
||||
|
||||
# 获取当前插件列表
|
||||
local extensions_list="$TEMP_PATH/extensions.list"
|
||||
if get_extensions_list "$EXTENSIONS_DIR" "$extensions_list"; then
|
||||
echo -e "${GREEN}✓ 已保存插件列表${NC}"
|
||||
echo -e "\n${YELLOW}当前安装的插件:${NC}"
|
||||
cat "$extensions_list" | while read -r ext; do
|
||||
echo " $ext"
|
||||
done
|
||||
echo
|
||||
fi
|
||||
|
||||
# 备份设置文件
|
||||
if [ -f "$USER_DIR/settings.json" ]; then
|
||||
mkdir -p "$TEMP_PATH/User"
|
||||
cp "$USER_DIR/settings.json" "$TEMP_PATH/User/"
|
||||
echo -e "${GREEN}✓ 已备份设置文件${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}! 未找到设置文件${NC}"
|
||||
fi
|
||||
|
||||
# 备份扩展目录
|
||||
if [ -d "$EXTENSIONS_DIR/extensions" ]; then
|
||||
cp -r "$EXTENSIONS_DIR/extensions" "$TEMP_PATH/"
|
||||
echo -e "${GREEN}✓ 已备份扩展目录${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}! 未找到扩展目录${NC}"
|
||||
fi
|
||||
|
||||
# 压缩备份
|
||||
echo -e "${YELLOW}正在压缩备份...${NC}"
|
||||
tar -czf "${BACKUP_PATH}.tar.gz" -C "/tmp" "$BACKUP_NAME"
|
||||
|
||||
# 清理临时目录
|
||||
rm -rf "$TEMP_PATH"
|
||||
|
||||
echo -e "${GREEN}备份创建成功: ${BACKUP_PATH}.tar.gz${NC}"
|
||||
}
|
||||
|
||||
# 还原备份
|
||||
restore_backup() {
|
||||
if [ -z "$1" ]; then
|
||||
echo -e "${RED}错误: 请指定要还原的备份名称${NC}"
|
||||
echo "用法: $0 restore <backup_name>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BACKUP_NAME="$1"
|
||||
BACKUP_PATH="$BACKUP_DIR/$BACKUP_NAME"
|
||||
TEMP_PATH="/tmp/$BACKUP_NAME"
|
||||
|
||||
if [ ! -f "${BACKUP_PATH}.tar.gz" ]; then
|
||||
echo -e "${RED}错误: 未找到备份文件: ${BACKUP_PATH}.tar.gz${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${YELLOW}正在还原备份...${NC}"
|
||||
echo -e "${YELLOW}操作系统: $OS${NC}"
|
||||
echo -e "${YELLOW}设置目录: $SETTINGS_DIR${NC}"
|
||||
echo -e "${YELLOW}插件目录: $EXTENSIONS_DIR${NC}"
|
||||
|
||||
# 解压备份到临时目录
|
||||
echo -e "${YELLOW}正在解压备份...${NC}"
|
||||
rm -rf "$TEMP_PATH"
|
||||
tar -xzf "${BACKUP_PATH}.tar.gz" -C "/tmp"
|
||||
|
||||
# 获取当前插件列表
|
||||
local current_list=$(mktemp)
|
||||
local backup_list="$TEMP_PATH/extensions.list"
|
||||
|
||||
if get_extensions_list "$EXTENSIONS_DIR" "$current_list"; then
|
||||
if [ -f "$backup_list" ]; then
|
||||
show_extensions_diff "$backup_list" "$current_list"
|
||||
echo
|
||||
echo -n "是否继续还原? [y/N] "
|
||||
read -r confirm
|
||||
if [[ ! $confirm =~ ^[Yy]$ ]]; then
|
||||
echo -e "${YELLOW}取消还原操作${NC}"
|
||||
rm -f "$current_list"
|
||||
rm -rf "$TEMP_PATH"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
rm -f "$current_list"
|
||||
|
||||
# 还原设置文件
|
||||
if [ -f "$TEMP_PATH/User/settings.json" ]; then
|
||||
mkdir -p "$USER_DIR"
|
||||
cp "$TEMP_PATH/User/settings.json" "$USER_DIR/"
|
||||
echo -e "${GREEN}✓ 已还原设置文件${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}! 备份中未找到设置文件${NC}"
|
||||
fi
|
||||
|
||||
# 还原扩展目录
|
||||
if [ -d "$TEMP_PATH/extensions" ]; then
|
||||
rm -rf "$EXTENSIONS_DIR/extensions"
|
||||
cp -r "$TEMP_PATH/extensions" "$EXTENSIONS_DIR/"
|
||||
echo -e "${GREEN}✓ 已还原扩展目录${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}! 备份中未找到扩展目录${NC}"
|
||||
fi
|
||||
|
||||
# 清理临时目录
|
||||
rm -rf "$TEMP_PATH"
|
||||
|
||||
echo -e "${GREEN}备份还原成功${NC}"
|
||||
echo -e "${YELLOW}请重启 Cursor 编辑器以使更改生效${NC}"
|
||||
}
|
||||
|
||||
# 删除备份
|
||||
delete_backup() {
|
||||
if [ -z "$1" ]; then
|
||||
echo -e "${RED}错误: 请指定要删除的备份名称${NC}"
|
||||
echo "用法: $0 delete <backup_name>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BACKUP_NAME="$1"
|
||||
BACKUP_PATH="$BACKUP_DIR/$BACKUP_NAME"
|
||||
|
||||
if [ ! -f "${BACKUP_PATH}.tar.gz" ]; then
|
||||
echo -e "${RED}错误: 未找到备份文件: ${BACKUP_PATH}.tar.gz${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${YELLOW}即将删除备份: $BACKUP_NAME${NC}"
|
||||
echo -n "确认删除? [y/N] "
|
||||
read -r confirm
|
||||
|
||||
if [[ $confirm =~ ^[Yy]$ ]]; then
|
||||
rm -f "${BACKUP_PATH}.tar.gz"
|
||||
echo -e "${GREEN}备份已删除: $BACKUP_NAME${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}取消删除操作${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# 删除所有备份
|
||||
delete_all_backups() {
|
||||
# 检查是否有备份
|
||||
if ! list_backups; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo -e "${YELLOW}警告: 即将删除所有备份!${NC}"
|
||||
echo -n "确认删除所有备份? [y/N] "
|
||||
read -r confirm
|
||||
|
||||
if [[ $confirm =~ ^[Yy]$ ]]; then
|
||||
rm -f "$BACKUP_DIR"/cursor_*.tar.gz
|
||||
echo -e "${GREEN}已删除所有备份${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}取消删除操作${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# 格式化文件大小
|
||||
format_size() {
|
||||
local size=$1
|
||||
local units=("B" "KiB" "MiB" "GiB" "TiB")
|
||||
local unit=0
|
||||
|
||||
while (( size > 1024 && unit < 4 )); do
|
||||
size=$(( (size + 512) / 1024 ))
|
||||
((unit++))
|
||||
done
|
||||
|
||||
echo "${size}${units[$unit]}"
|
||||
}
|
||||
|
||||
# 列出备份并返回备份名称数组
|
||||
list_backups() {
|
||||
if [ ! -d "$BACKUP_DIR" ]; then
|
||||
echo -e "${YELLOW}未找到备份${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 创建一个数组来存储备份名称
|
||||
backup_names=()
|
||||
|
||||
echo -e "${YELLOW}可用的备份:${NC}"
|
||||
id=1
|
||||
|
||||
if [[ "$OS" == "Windows" ]]; then
|
||||
# Windows 环境使用 dir 命令
|
||||
(cd "$BACKUP_DIR" && cmd //c "dir /b *.tar.gz" 2>/dev/null) | sort -r | while read -r backup_file; do
|
||||
local backup="$BACKUP_DIR/$backup_file"
|
||||
backup="$(normalize_path "$backup")"
|
||||
if [ -f "$backup" ]; then
|
||||
local backup_name=$(basename "$backup" .tar.gz)
|
||||
backup_names+=("$backup_name")
|
||||
local backup_time=$(get_file_time "$backup")
|
||||
local backup_size=$(get_file_size "$backup")
|
||||
local formatted_size=$(format_size "$backup_size")
|
||||
printf "%2d) %s (%s) [%8s]\n" $id "$backup_name" "$backup_time" "$formatted_size"
|
||||
((id++))
|
||||
fi
|
||||
done
|
||||
else
|
||||
# Unix 环境使用 find 命令
|
||||
find "$BACKUP_DIR" -maxdepth 1 -type f -name "cursor_*.tar.gz" | sort -r | while read -r backup; do
|
||||
if [ -f "$backup" ]; then
|
||||
local backup_name=$(basename "$backup" .tar.gz)
|
||||
backup_names+=("$backup_name")
|
||||
local backup_time=$(get_file_time "$backup")
|
||||
local backup_size=$(get_file_size "$backup")
|
||||
local formatted_size=$(format_size "$backup_size")
|
||||
printf "%2d) %s (%s) [%8s]\n" $id "$backup_name" "$backup_time" "$formatted_size"
|
||||
((id++))
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# 如果没有找到备份
|
||||
if [ ${#backup_names[@]} -eq 0 ]; then
|
||||
echo -e "${YELLOW}没有可用的备份${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# 根据ID获取备份名称
|
||||
get_backup_by_id() {
|
||||
local id=$1
|
||||
local -a backup_names=()
|
||||
|
||||
# 获取所有备份名称并排序
|
||||
find "$BACKUP_DIR" -maxdepth 1 -type f -name "cursor_*.tar.gz" | sort -r | while read -r backup; do
|
||||
if [ -f "$backup" ]; then
|
||||
backup_name=$(basename "$backup" .tar.gz)
|
||||
backup_names+=("$backup_name")
|
||||
fi
|
||||
done
|
||||
|
||||
# 检查ID是否有效
|
||||
if [ "$id" -le 0 ] || [ "$id" -gt "${#backup_names[@]}" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 返回对应的备份名称
|
||||
echo "${backup_names[$((id-1))]}"
|
||||
return 0
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo -e "${GREEN}Cursor 编辑器备份工具${NC}"
|
||||
echo -e "${YELLOW}当前操作系统: $OS${NC}"
|
||||
if [[ "$OS" == "macOS" ]]; then
|
||||
echo -e "${YELLOW}设置目录: $SETTINGS_DIR${NC}"
|
||||
echo -e "${YELLOW}插件目录: $EXTENSIONS_DIR${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}Cursor 目录: $SETTINGS_DIR${NC}"
|
||||
fi
|
||||
echo -e "${YELLOW}备份目录: $BACKUP_DIR${NC}"
|
||||
echo
|
||||
echo "用法:"
|
||||
echo " 创建备份: $0 backup"
|
||||
echo " 还原备份: $0 restore <backup_name>"
|
||||
echo " 删除备份: $0 delete <backup_name>"
|
||||
echo " 删除所有: $0 delete-all"
|
||||
echo " 列出备份: $0 list"
|
||||
echo " 显示帮助: $0 help"
|
||||
}
|
||||
|
||||
# 显示菜单并获取用户选择
|
||||
show_menu() {
|
||||
clear
|
||||
echo -e "${GREEN}Cursor 编辑器备份工具${NC}"
|
||||
echo -e "${YELLOW}当前操作系统: $OS${NC}"
|
||||
if [[ "$OS" == "macOS" ]]; then
|
||||
echo -e "${YELLOW}设置目录: $SETTINGS_DIR${NC}"
|
||||
echo -e "${YELLOW}插件目录: $EXTENSIONS_DIR${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}Cursor 目录: $SETTINGS_DIR${NC}"
|
||||
fi
|
||||
echo -e "${YELLOW}备份目录: $BACKUP_DIR${NC}"
|
||||
echo
|
||||
echo "请选择操作:"
|
||||
echo "1) 创建备份"
|
||||
echo "2) 还原备份"
|
||||
echo "3) 删除备份"
|
||||
echo "4) 删除所有备份"
|
||||
echo "5) 列出备份"
|
||||
echo "0) 退出"
|
||||
echo
|
||||
echo -n "请输入选项 [0-5]: "
|
||||
read -r choice
|
||||
|
||||
case $choice in
|
||||
1)
|
||||
create_backup
|
||||
;;
|
||||
2)
|
||||
# 显示可用备份并获取用户选择
|
||||
echo
|
||||
if list_backups; then
|
||||
echo
|
||||
echo -n "请输入要还原的备份ID: "
|
||||
read -r backup_id
|
||||
if [[ "$backup_id" =~ ^[0-9]+$ ]]; then
|
||||
backup_name=$(get_backup_by_id "$backup_id")
|
||||
if [ -n "$backup_name" ]; then
|
||||
restore_backup "$backup_name"
|
||||
else
|
||||
echo -e "${RED}错误: 无效的备份ID${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}错误: 请输入有效的数字ID${NC}"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
3)
|
||||
# 显示可用备份并获取用户选择
|
||||
echo
|
||||
if list_backups; then
|
||||
echo
|
||||
echo -n "请输入要删除的备份ID: "
|
||||
read -r backup_id
|
||||
if [[ "$backup_id" =~ ^[0-9]+$ ]]; then
|
||||
backup_name=$(get_backup_by_id "$backup_id")
|
||||
if [ -n "$backup_name" ]; then
|
||||
delete_backup "$backup_name"
|
||||
else
|
||||
echo -e "${RED}错误: 无效的备份ID${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}错误: 请输入有效的数字ID${NC}"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
4)
|
||||
delete_all_backups
|
||||
;;
|
||||
5)
|
||||
list_backups
|
||||
;;
|
||||
0)
|
||||
echo "退出程序"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}无效的选项${NC}"
|
||||
;;
|
||||
esac
|
||||
|
||||
echo
|
||||
echo -n "按回车键继续..."
|
||||
read -r
|
||||
show_menu
|
||||
}
|
||||
|
||||
# 主程序
|
||||
if [ $# -eq 0 ]; then
|
||||
# 如果没有命令行参数,显示交互式菜单
|
||||
show_menu
|
||||
else
|
||||
# 保持原有的命令行参数支持
|
||||
case "$1" in
|
||||
"backup")
|
||||
create_backup
|
||||
;;
|
||||
"restore")
|
||||
restore_backup "$2"
|
||||
;;
|
||||
"delete")
|
||||
delete_backup "$2"
|
||||
;;
|
||||
"delete-all")
|
||||
delete_all_backups
|
||||
;;
|
||||
"list")
|
||||
list_backups
|
||||
;;
|
||||
"help"|"--help"|"-h")
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}未知命令: $1${NC}"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
16
frontend/scripts/extensions.json
Normal file
16
frontend/scripts/extensions.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"extensions": [
|
||||
"akamud.vscode-theme-onedark",
|
||||
"atommaterial.a-file-icon-vscode",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"donjayamanne.githistory",
|
||||
"eamodio.gitlens",
|
||||
"esbenp.prettier-vscode",
|
||||
"lokalise.i18n-ally",
|
||||
"maggie.eslint-rules-zh-plugin",
|
||||
"ms-ceintl.vscode-language-pack-zh-hans",
|
||||
"ms-vscode-remote.remote-containers",
|
||||
"rvest.vs-code-prettier-eslint",
|
||||
"vue.volar"
|
||||
]
|
||||
}
|
||||
194
frontend/scripts/index.md
Normal file
194
frontend/scripts/index.md
Normal file
@@ -0,0 +1,194 @@
|
||||
|
||||
# 多功能任务同步工作流管理平台项目文档
|
||||
|
||||
|
||||
## 一、项目概述
|
||||
|
||||
### 1. 项目目标
|
||||
打造一个可视化、可扩展的任务同步工作流管理平台,支持多种主流任务执行类型的配置、管理与自动化执行,帮助用户高效管理跨平台、跨工具的任务流程,提升开发、运维及文件处理效率。
|
||||
|
||||
|
||||
### 2. 核心价值
|
||||
- **统一管理**:通过标准化界面管理文件同步、项目编译、代码同步等多类型任务,避免工具碎片化。
|
||||
- **灵活配置**:支持动态添加任务类型,每种任务类型提供专属配置表单,适配不同场景需求。
|
||||
- **高效执行**:集成主流协议(SFTP/FTP)、开发工具(TurboRepo、Git)及本地操作,一键触发任务执行并实时反馈状态。
|
||||
|
||||
|
||||
### 3. 技术架构
|
||||
#### 后端
|
||||
- **框架**:Egg.js(Node.js 企业级框架,支持高性能 API 开发与插件扩展)
|
||||
- **数据库**:SQLite(轻量级文件型数据库,适合快速原型与小型部署)
|
||||
- **任务执行**:通过 Node.js 原生模块(如 `child_process`)调用系统命令,集成 SFTP/FTP 客户端库(如 `ssh2-sftp-client`)、Git 工具库(如 `simple-git`)实现任务逻辑。
|
||||
|
||||
#### 前端
|
||||
- **构建工具**:Vite 6(极速开发服务器与优化构建)
|
||||
- **框架**:Svelte 5(轻量响应式 UI 框架,组件化开发)
|
||||
- **UI 组件库**:Shadcn-Svelte(现代风格、可自定义的组件集合,含表单、表格、模态框等)
|
||||
- **状态管理**:Svelte 原生响应式系统 + 轻量表单库(如 `svelte-forms-library`)
|
||||
|
||||
|
||||
### 4. 适用场景
|
||||
- **开发团队**:管理项目编译、代码同步(Git)、微服务架构下的项目分离(TurboRepo)。
|
||||
- **运维/文件处理**:批量配置文件本地同步、跨服务器 SFTP/FTP 上传任务。
|
||||
- **自动化场景**:通过定时触发或手动执行,减少重复性操作。
|
||||
|
||||
|
||||
## 二、核心功能模块
|
||||
|
||||
### 1. 工作流基础管理
|
||||
#### (1)工作流列表
|
||||
- **展示字段**:任务名称、类型(文件同步/SFTP上传等)、状态(待执行/进行中/成功/失败)、创建时间、操作(编辑/删除/立即执行)。
|
||||
- **交互功能**:
|
||||
- 搜索过滤:支持按任务名称、类型关键词搜索。
|
||||
- 排序功能:按创建时间、执行状态升/降序排列。
|
||||
- 批量操作:可选删除多个任务(需二次确认)。
|
||||
|
||||
#### (2)增删改查操作
|
||||
- **添加任务**:
|
||||
1. 选择任务类型(下拉菜单,含 5 种预设类型,预留扩展接口)。
|
||||
2. 动态加载该类型专属配置表单(见下文各类型详情)。
|
||||
3. 提交后生成任务记录,状态默认「待执行」。
|
||||
- **编辑任务**:支持修改配置参数,保存后不影响历史执行记录。
|
||||
- **删除任务**:物理删除数据库记录,关联执行日志可选择保留或清除。
|
||||
|
||||
|
||||
### 2. 任务类型与配置详情
|
||||
|
||||
#### (1)文件本地同步
|
||||
- **核心功能**:将本地文件/目录从源路径复制到目标路径,支持增量同步(可选)。
|
||||
- **配置参数**:
|
||||
- 任务名称(必填)
|
||||
- 入口文件地址:文件/目录选择组件(支持系统文件弹窗选择,显示绝对路径)
|
||||
- 出口文件地址:同上
|
||||
- 同步模式:立即同步/定时同步(可选,需后续扩展定时模块)
|
||||
|
||||
#### (2)文件 SFTP/FTP 上传
|
||||
- **核心功能**:通过 SFTP(安全传输)或 FTP 协议将本地文件上传至远程服务器。
|
||||
- **配置参数**:
|
||||
- 任务名称(必填)
|
||||
- 入口文件地址:本地文件/目录选择
|
||||
- 协议类型:单选按钮(SFTP/FTP,默认 SFTP)
|
||||
- 远程服务器信息:
|
||||
- IP 地址(必填)
|
||||
- 端口(选填,SFTP 默认为 22,FTP 默认为 21)
|
||||
- 用户名(必填)
|
||||
- 密码(必填,输入框掩码处理)
|
||||
|
||||
#### (3)项目编译
|
||||
- **核心功能**:在指定项目路径执行自定义编译命令(如 `npm run build`、`make` 等)。
|
||||
- **配置参数**:
|
||||
- 任务名称(必填)
|
||||
- 项目路径:选择项目根目录(文件选择组件,校验是否存在 `package.json` 等标识文件)
|
||||
- 编译命令:文本输入框(支持多行,如分步骤执行命令,用 `&&` 分隔)
|
||||
- 环境变量:选填(可配置编译所需的环境参数,如 `NODE_ENV=production`)
|
||||
|
||||
#### (4)Git 项目同步
|
||||
- **核心功能**:克隆、拉取或推送 Git 仓库,支持代码同步与版本控制。
|
||||
- **配置参数**:
|
||||
- 任务名称(必填)
|
||||
- Git 项目地址:输入框(支持 `http`/`https`/`ssh` 协议,如 `git@github.com:user/repo.git`)
|
||||
- 本地存储路径:选填(默认克隆至系统临时目录,可自定义本地存放路径)
|
||||
- 操作类型:单选(克隆/拉取/推送,默认拉取)
|
||||
|
||||
#### (5)项目分离(TurboRepo)
|
||||
- **核心功能**:基于 TurboRepo 架构分离 monorepo 中的子项目,支持独立构建或发布。
|
||||
- **配置参数**:
|
||||
- 任务名称(必填)
|
||||
- 分离的项目名称:输入框(需与 TurboRepo 配置中的 `name` 字段匹配)
|
||||
- 根项目路径:选择 TurboRepo 根目录(校验是否存在 `turbo.json` 配置文件)
|
||||
- 操作类型:单选(分离并构建/仅分离,默认分离并构建)
|
||||
|
||||
|
||||
### 3. 任务执行与反馈
|
||||
- **触发方式**:
|
||||
- 手动触发:列表页「立即执行」按钮。
|
||||
- 定时触发:后续扩展(需添加 cron 表达式配置字段)。
|
||||
- **执行状态**:
|
||||
- 进行中:显示加载动画,禁用编辑/删除操作。
|
||||
- 成功:记录执行时间,可查看日志详情(如同步文件数量、编译输出)。
|
||||
- 失败:显示错误原因(如网络异常、权限不足),支持重试。
|
||||
- **日志系统**:
|
||||
- 记录每次执行的开始/结束时间、状态、关键参数及错误信息。
|
||||
- 日志详情页支持关键词搜索、导出为文本文件。
|
||||
|
||||
|
||||
## 三、技术实现细节
|
||||
|
||||
### 1. 后端(Egg.js + SQLite)
|
||||
#### (1)数据模型设计
|
||||
`Workflow` 表字段:
|
||||
| 字段名 | 类型 | 说明 |
|
||||
|----------------|--------------|----------------------------------------------------------------------|
|
||||
| id | INTEGER | 自增主键 |
|
||||
| name | VARCHAR(50) | 任务名称(唯一) |
|
||||
| type | VARCHAR(20) | 任务类型(file_local, sftp_ftp, compile, git, turborepo) |
|
||||
| config | TEXT | 配置参数(JSON 字符串,存储各类型专属字段,如 SFTP 的 IP、端口等) |
|
||||
| status | VARCHAR(20) | 状态(pending/running/success/failed,默认 pending) |
|
||||
| create_time | DATETIME | 创建时间(默认当前时间) |
|
||||
|
||||
#### (2)API 接口设计
|
||||
- **列表接口**:`GET /workflows`,支持分页、搜索、排序(返回包含状态、类型、操作按钮的列表数据)。
|
||||
- **创建/编辑接口**:`POST /workflows`、`PUT /workflows/:id`,接收包含 `type` 和 `config` 的 JSON 数据。
|
||||
- **执行接口**:`POST /workflows/:id/execute`,触发任务执行逻辑,返回执行任务 ID 用于状态轮询。
|
||||
- **日志接口**:`GET /workflows/:id/logs`,获取该任务历史执行日志。
|
||||
|
||||
#### (3)任务执行逻辑
|
||||
- 基于 `child_process.spawn` 执行本地命令(如编译、Git 操作),通过流处理实时捕获输出日志。
|
||||
- SFTP/FTP 上传使用 `ssh2-sftp-client` 库,支持连接池管理与错误重试(3 次失败后终止)。
|
||||
- 任务执行时通过 Egg.js 插件(如 `egg-redis`)实现分布式锁(后续扩展多实例部署)。
|
||||
|
||||
|
||||
### 2. 前端(Svelte 5 + Shadcn-Svelte)
|
||||
#### (1)组件结构
|
||||
- **核心组件**:
|
||||
- `WorkflowList.svelte`:任务列表,包含搜索栏、操作按钮,集成 `shadcn-svelte` 的 `Table`、`Button` 组件。
|
||||
- `WorkflowForm.svelte`:动态表单,根据 `type` 渲染不同字段(如 SFTP 表单包含 IP/端口输入,Git 表单包含仓库地址输入)。
|
||||
- `FileSelect.svelte`:封装文件/目录选择组件,基于原生 `<input type="file">` 扩展目录选择(需浏览器支持 `webkitdirectory` 属性)。
|
||||
- **状态管理**:
|
||||
- 使用 Svelte 响应式变量(`let config = {}`)存储表单数据,通过 `bind:value` 绑定输入框。
|
||||
- 表单提交前校验必填字段(如 SFTP 的 IP、用户名),错误信息通过 `shadcn-svelte` 的 `Alert` 组件提示。
|
||||
|
||||
#### (2)动态表单实现
|
||||
```svelte
|
||||
{#if type === 'sftp_ftp'}
|
||||
<SFTPConfigForm bind:config />
|
||||
{:else if type === 'compile'}
|
||||
<CompileConfigForm bind:config />
|
||||
{:else if type === 'git'}
|
||||
<GitConfigForm bind:config />
|
||||
{/if}
|
||||
```
|
||||
每个类型对应独立子组件,封装专属字段逻辑,提升可维护性。
|
||||
|
||||
#### (3)执行状态交互
|
||||
- 点击「立即执行」按钮后,禁用按钮并显示加载状态,通过 WebSocket(后续扩展)或轮询接口(`GET /workflows/:id/status`)实时更新任务状态。
|
||||
- 失败状态显示红色警告,附带「重试」按钮,一键重新提交执行请求。
|
||||
|
||||
|
||||
## 四、技术栈文档索引
|
||||
1. **Egg.js 框架**
|
||||
- 官网:[https://www.eggjs.org/zh-CN](https://www.eggjs.org/zh-CN)
|
||||
- 核心功能:插件机制、中间件、ORM 集成(本项目使用 SQLite 原生驱动)。
|
||||
|
||||
2. **Shadcn-Svelte**
|
||||
- 官网:[https://next.shadcn-svelte.com/](https://next.shadcn-svelte.com/)
|
||||
- 组件列表:按钮、表单、模态框、表格等,支持完全自定义样式(基于 Tailwind CSS)。
|
||||
|
||||
3. **Svelte 5**
|
||||
- 官网:[https://svelte.dev/](https://svelte.dev/)
|
||||
- 特性:组件化开发、响应式系统、编译时优化,适合构建高性能前端界面。
|
||||
|
||||
4. **Vite 6**
|
||||
- 官网:[https://cn.vitejs.dev/](https://cn.vitejs.dev/)
|
||||
- 优势:极速冷启动、按需编译、支持 Svelte 单文件组件(.svelte)热更新。
|
||||
|
||||
|
||||
## 五、后续扩展方向
|
||||
1. **定时任务**:添加 cron 表达式配置,支持按计划执行任务(如每天凌晨同步文件)。
|
||||
2. **可视化工作流编辑器**:允许用户通过拖拽节点配置多步骤任务流程(如先编译项目,再同步至服务器)。
|
||||
3. **权限管理**:区分管理员与普通用户,控制任务创建、编辑权限。
|
||||
4. **多语言支持**:集成 i18n 插件,适配中英文界面。
|
||||
|
||||
|
||||
|
||||
通过以上设计,项目实现了多类型任务的统一管理与高效执行,结合现代技术栈确保了开发效率与用户体验,适用于中小型团队及个人用户的自动化任务场景。
|
||||
65
frontend/scripts/settings.json
Normal file
65
frontend/scripts/settings.json
Normal file
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"workbench.colorTheme": "Atom One Dark", // 工作台 设置主题
|
||||
"workbench.iconTheme": "a-file-icon-vscode", // 工作台 设置图标主题
|
||||
"workbench.editor.enablePreview": true, // 工作台 设置预览
|
||||
"workbench.settings.applyToAllProfiles": [], // 工作台 设置应用到所有配置文件
|
||||
|
||||
"editor.fontSize": 14, // 编辑器 设置字体大小
|
||||
"editor.tabSize": 2, // 编辑器 设置制表符大小
|
||||
"editor.formatOnSave": false, // 编辑器 设置保存时自动格式化
|
||||
"editor.formatOnType": true, // 编辑器 设置输入时自动格式化
|
||||
"editor.formatOnPaste": true, // 编辑器 设置粘贴时自动格式化
|
||||
"editor.detectIndentation": false, // 编辑器 设置自动检测缩进
|
||||
"editor.insertSpaces": false, // 编辑器 设置插入空格
|
||||
"editor.bracketPairColorization.enabled": true, // 编辑器 设置括号颜色
|
||||
"editor.guides.bracketPairs": "active", // 编辑器 设置括号对齐
|
||||
"editor.quickSuggestions": {
|
||||
"strings": true // 编辑器 设置快速建议
|
||||
},
|
||||
"editor.unicodeHighlight.allowedLocales": {
|
||||
// 编辑器 设置允许的语言
|
||||
"zh-hant": true,
|
||||
"de": true,
|
||||
"zh-hans": true
|
||||
},
|
||||
"editor.unicodeHighlight.invisibleCharacters": false, // 编辑器 设置隐藏字符
|
||||
"editor.inlineSuggest.enabled": true, // 设置内联建议
|
||||
"editor.codeActionsOnSave": {
|
||||
// 编辑器 设置保存时自动执行
|
||||
"quickfix.biome": "explicit"
|
||||
},
|
||||
"editor.suggestSelection": "first", // 编辑器 设置建议选择
|
||||
|
||||
"explorer.compactFolders": false, // 资源管理器 设置紧凑文件夹
|
||||
"explorer.confirmDelete": true, // 资源管理器 设置确认删除
|
||||
|
||||
"git.autofetch": true, // git 设置自动获取
|
||||
"git.enableSmartCommit": true, // git 设置智能提交
|
||||
"git.confirmSync": false, // git 设置确认同步
|
||||
"git.useEditorAsCommitInput": false, // git 设置使用编辑器作为提交输入
|
||||
"git.autoRepositoryDetection": "subFolders", // git 设置自动检测仓库
|
||||
|
||||
"window.menuBarVisibility": "classic", // 视图 设置菜单栏可见性
|
||||
"files.eol": "\n", // 设置行
|
||||
"terminal.integrated.defaultProfile.windows": "Command Prompt", // 设置默认终端
|
||||
|
||||
"typescript.updateImportsOnFileMove.enabled": "always", // 设置更新导入
|
||||
"i18n-ally.displayLanguage": "zh-cn", // i18n-ally 设置显示语言
|
||||
|
||||
"security.workspace.trust.untrustedFiles": "open", // 设置信任未受信任的文件
|
||||
"security.promptForLocalFileProtocolHandling": false, // 安全 设置提示本地文件协议处理
|
||||
"gitlens.graph.minimap.enabled": false, // gitlens 设置最小地图
|
||||
"chat.editing.alwaysSaveWithGeneratedChanges": true, // chat 设置总是保存生成的更改
|
||||
"cursor.cpp.disabledLanguages": [
|
||||
// cursor cpp 设置禁用语言
|
||||
"plaintext",
|
||||
"scminput"
|
||||
],
|
||||
"files.autoSave": "afterDelay", // 自动保存
|
||||
"files.autoSaveDelay": 1000,
|
||||
"remote.autoForwardPortsSource": "hybrid",
|
||||
"workbench.activityBar.orientation": "vertical",
|
||||
"[jsonc]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user