From 96a1190f744f1fd39fa349d01a639d9ac7e0608f Mon Sep 17 00:00:00 2001 From: richarjiang Date: Thu, 14 Aug 2025 16:11:22 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=96=87=E7=AB=A0=E9=98=85?= =?UTF-8?q?=E8=AF=BB=E6=95=B0=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=8C=85=E6=8B=AC?= =?UTF-8?q?=E5=9C=A8=E6=8E=A7=E5=88=B6=E5=99=A8=E5=92=8C=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E4=B8=AD=E6=B7=BB=E5=8A=A0=E5=A2=9E=E5=8A=A0=E9=98=85=E8=AF=BB?= =?UTF-8?q?=E6=95=B0=E7=9A=84=E6=96=B9=E6=B3=95=EF=BC=8C=E5=B9=B6=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E7=9B=B8=E5=85=B3=E8=B7=AF=E7=94=B1=E4=BB=A5=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E8=AF=A5=E5=8A=9F=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy.sh | 329 +++++++++++++--------------- src/articles/articles.controller.ts | 8 + src/articles/articles.service.ts | 8 + 3 files changed, 173 insertions(+), 172 deletions(-) diff --git a/deploy.sh b/deploy.sh index 69c58c3..116c68e 100755 --- a/deploy.sh +++ b/deploy.sh @@ -1,8 +1,8 @@ #!/bin/bash -# 发布脚本配置 -SERVER_HOST="129.204.155.94" -SERVER_USER="root" # 根据实际情况修改用户名 +# 服务器配置 +SERVER_IP="129.204.155.94" +SERVER_USER="root" # 请根据实际情况修改用户名 SERVER_PATH="/usr/local/web/pilates-server" PROJECT_NAME="pilates-server" @@ -19,7 +19,7 @@ print_message() { } print_warning() { - echo -e "${YELLOW}[WARN] $1${NC}" + echo -e "${YELLOW}[WARNING] $1${NC}" } print_error() { @@ -30,23 +30,45 @@ print_step() { echo -e "${BLUE}[STEP] $1${NC}" } +# 错误处理 +handle_error() { + print_error "部署失败: $1" + exit 1 +} + # 检查必要的工具 -check_requirements() { - print_step "检查部署环境..." +check_dependencies() { + print_step "检查必要的工具..." - # 检查rsync - if ! command -v rsync &> /dev/null; then - print_error "rsync未安装,请先安装rsync" - exit 1 - fi - - # 检查ssh if ! command -v ssh &> /dev/null; then - print_error "ssh未安装,请先安装ssh" - exit 1 + handle_error "SSH未安装" fi - print_message "环境检查完成" + if ! command -v scp &> /dev/null; then + handle_error "SCP未安装" + fi + + if ! command -v yarn &> /dev/null; then + handle_error "Yarn未安装" + fi + + print_message "所有必要工具已安装" +} + +# 测试服务器连接 +test_server_connection() { + print_step "测试服务器连接..." + + if ssh -o ConnectTimeout=10 -o BatchMode=yes $SERVER_USER@$SERVER_IP exit 2>/dev/null; then + print_message "服务器连接成功" + else + print_error "无法连接到服务器 $SERVER_IP" + print_warning "请确保:" + print_warning "1. 服务器IP地址正确" + print_warning "2. SSH密钥已配置或可以密码登录" + print_warning "3. 服务器防火墙允许SSH连接" + exit 1 + fi } # 本地构建 @@ -55,17 +77,11 @@ build_project() { # 安装依赖 print_message "安装依赖..." - if ! yarn install; then - print_error "依赖安装失败" - exit 1 - fi + yarn install || handle_error "依赖安装失败" # 构建项目 print_message "构建项目..." - if ! yarn build; then - print_error "项目构建失败" - exit 1 - fi + yarn build || handle_error "项目构建失败" print_message "本地构建完成" } @@ -75,179 +91,148 @@ create_deployment_package() { print_step "创建部署包..." # 创建临时目录 - TEMP_DIR=$(mktemp -d) - DEPLOY_DIR="$TEMP_DIR/$PROJECT_NAME" - - print_message "创建临时目录: $DEPLOY_DIR" - mkdir -p "$DEPLOY_DIR" + TEMP_DIR="./deploy_temp" + rm -rf $TEMP_DIR + mkdir -p $TEMP_DIR # 复制必要文件 - print_message "复制项目文件..." + print_message "复制必要文件..." + cp -r dist $TEMP_DIR/ + cp package.json $TEMP_DIR/ + cp yarn.lock $TEMP_DIR/ + cp ecosystem.config.js $TEMP_DIR/ + cp start.sh $TEMP_DIR/ - # 复制构建后的文件 - cp -r dist "$DEPLOY_DIR/" - - # 复制配置文件 - cp package.json "$DEPLOY_DIR/" - cp yarn.lock "$DEPLOY_DIR/" - cp ecosystem.config.js "$DEPLOY_DIR/" - cp start.sh "$DEPLOY_DIR/" - - # 复制其他必要文件 - if [ -f "SubscriptionKey_K3L2F8HFTS.p8" ]; then - cp SubscriptionKey_K3L2F8HFTS.p8 "$DEPLOY_DIR/" + # 如果有环境配置文件,也复制过去 + if [ -f ".env.production" ]; then + cp .env.production $TEMP_DIR/.env fi - # 创建日志目录 - mkdir -p "$DEPLOY_DIR/logs" - - print_message "部署包创建完成: $DEPLOY_DIR" - echo "$DEPLOY_DIR" + print_message "部署包创建完成" } -# 部署到服务器 -deploy_to_server() { - local deploy_dir=$1 - print_step "部署到服务器..." +# 上传到服务器 +upload_to_server() { + print_step "上传文件到服务器..." - # 测试SSH连接 - print_message "测试SSH连接..." - if ! ssh -o ConnectTimeout=10 "$SERVER_USER@$SERVER_HOST" "echo 'SSH连接成功'"; then - print_error "无法连接到服务器 $SERVER_HOST" - print_warning "请检查:" - print_warning "1. 服务器地址是否正确" - print_warning "2. SSH密钥是否配置正确" - print_warning "3. 服务器是否可访问" - exit 1 - fi + # 在服务器上创建项目目录 + ssh $SERVER_USER@$SERVER_IP "mkdir -p $SERVER_PATH" - # 在服务器上创建目录 - print_message "在服务器上创建目录..." - ssh "$SERVER_USER@$SERVER_HOST" "mkdir -p $SERVER_PATH" + # 上传部署包 + print_message "上传部署包..." + scp -r ./deploy_temp/* $SERVER_USER@$SERVER_IP:$SERVER_PATH/ || handle_error "文件上传失败" - # 备份现有部署(如果存在) - print_message "备份现有部署..." - ssh "$SERVER_USER@$SERVER_HOST" " - if [ -d '$SERVER_PATH' ] && [ -f '$SERVER_PATH/package.json' ]; then - backup_dir='$SERVER_PATH.backup.$(date +%Y%m%d_%H%M%S)' - echo '创建备份目录: '\$backup_dir - cp -r '$SERVER_PATH' \$backup_dir - echo '备份完成' - fi - " - - # 停止现有服务 - print_message "停止现有服务..." - ssh "$SERVER_USER@$SERVER_HOST" " - cd $SERVER_PATH - if [ -f 'ecosystem.config.js' ]; then - pm2 stop ecosystem.config.js 2>/dev/null || true - pm2 delete ecosystem.config.js 2>/dev/null || true - fi - " - - # 同步文件到服务器 - print_message "同步文件到服务器..." - if ! rsync -avz --delete "$deploy_dir/" "$SERVER_USER@$SERVER_HOST:$SERVER_PATH/"; then - print_error "文件同步失败" - exit 1 - fi - - print_message "文件同步完成" + print_message "文件上传完成" } -# 在服务器上启动服务 -start_service() { - print_step "启动服务..." +# 在服务器上部署 +deploy_on_server() { + print_step "在服务器上部署应用..." - # 在服务器上执行start.sh - print_message "在服务器上执行start.sh..." - ssh "$SERVER_USER@$SERVER_HOST" " - cd $SERVER_PATH - chmod +x start.sh - ./start.sh - " - - # 检查服务状态 - print_message "检查服务状态..." - ssh "$SERVER_USER@$SERVER_HOST" " + ssh $SERVER_USER@$SERVER_IP << EOF cd $SERVER_PATH + + # 检查Node.js和yarn + if ! command -v node &> /dev/null; then + echo "正在安装Node.js..." + curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - + sudo apt-get install -y nodejs + fi + + if ! command -v yarn &> /dev/null; then + echo "正在安装Yarn..." + npm install -g yarn + fi + + if ! command -v pm2 &> /dev/null; then + echo "正在安装PM2..." + npm install -g pm2 + fi + + # 检查并安装Redis + if ! command -v redis-server &> /dev/null; then + echo "正在安装Redis..." + sudo apt-get update + sudo apt-get install -y redis-server + sudo systemctl start redis-server + sudo systemctl enable redis-server + echo "Redis安装完成" + else + echo "Redis已安装,检查服务状态..." + sudo systemctl start redis-server || true + fi + + # 安装生产依赖 + echo "安装生产依赖..." + yarn + + # 创建日志目录 + mkdir -p logs + + # 停止旧的应用实例 + echo "停止旧的应用实例..." + pm2 delete $PROJECT_NAME 2>/dev/null || true + + # 启动新的应用实例 + echo "启动新的应用实例..." + pm2 start ecosystem.config.js --env production + + # 保存PM2配置 + pm2 save + pm2 startup + + echo "部署完成!" pm2 status - " +EOF + + if [ $? -eq 0 ]; then + print_message "服务器部署完成" + else + handle_error "服务器部署失败" + fi } # 清理临时文件 cleanup() { - if [ -n "$TEMP_DIR" ] && [ -d "$TEMP_DIR" ]; then - print_message "清理临时文件: $TEMP_DIR" - rm -rf "$TEMP_DIR" - fi + print_step "清理临时文件..." + rm -rf ./deploy_temp + print_message "清理完成" +} + +# 显示部署信息 +show_deployment_info() { + print_step "部署信息" + echo "==================================" + print_message "应用名称: $PROJECT_NAME" + print_message "服务器IP: $SERVER_IP" + print_message "部署路径: $SERVER_PATH" + print_message "应用端口: 3000" + echo "==================================" + print_message "可以通过以下命令查看应用状态:" + echo "ssh $SERVER_USER@$SERVER_IP 'pm2 status'" + print_message "可以通过以下命令查看日志:" + echo "ssh $SERVER_USER@$SERVER_IP 'pm2 logs $PROJECT_NAME'" } # 主函数 main() { - print_message "开始部署 $PROJECT_NAME 到 $SERVER_HOST" - print_message "目标目录: $SERVER_PATH" - echo "" + echo "==================================" + print_message "开始一键部署到服务器" + echo "==================================" - # 设置错误处理 - trap cleanup EXIT - - # 执行部署步骤 - check_requirements + check_dependencies + test_server_connection build_project + create_deployment_package + upload_to_server + deploy_on_server + cleanup + show_deployment_info - DEPLOY_DIR=$(create_deployment_package) - TEMP_DIR=$(dirname "$DEPLOY_DIR") - - deploy_to_server "$DEPLOY_DIR" - start_service - - print_message "" - print_message "🎉 部署成功完成!" - print_message "服务器: $SERVER_HOST" - print_message "目录: $SERVER_PATH" - print_message "" - print_message "常用命令:" - print_message "查看日志: ssh $SERVER_USER@$SERVER_HOST 'cd $SERVER_PATH && pm2 logs'" - print_message "查看状态: ssh $SERVER_USER@$SERVER_HOST 'cd $SERVER_PATH && pm2 status'" - print_message "重启服务: ssh $SERVER_USER@$SERVER_HOST 'cd $SERVER_PATH && pm2 restart ecosystem.config.js'" + print_message "🎉 部署成功完成!" } -# 参数处理 -case "$1" in - --help|-h) - echo "用法: $0 [选项]" - echo "" - echo "选项:" - echo " --help, -h 显示帮助信息" - echo " --dry-run 仅显示将要执行的操作,不实际执行" - echo "" - echo "配置:" - echo " 服务器: $SERVER_HOST" - echo " 用户: $SERVER_USER" - echo " 路径: $SERVER_PATH" - echo "" - echo "注意事项:" - echo "1. 确保已配置SSH密钥认证" - echo "2. 确保服务器上已安装Node.js、yarn、pm2" - echo "3. 确保有足够的权限访问目标目录" - exit 0 - ;; - --dry-run) - print_message "这是一个模拟运行,不会实际执行部署操作" - print_message "将要执行的操作:" - print_message "1. 检查本地环境" - print_message "2. 构建项目" - print_message "3. 创建部署包" - print_message "4. 连接到服务器: $SERVER_HOST" - print_message "5. 备份现有部署" - print_message "6. 停止现有服务" - print_message "7. 同步文件到: $SERVER_PATH" - print_message "8. 执行start.sh启动服务" - exit 0 - ;; - *) - main - ;; -esac \ No newline at end of file +# 如果脚本被直接执行,则运行主函数 +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + main "$@" +fi \ No newline at end of file diff --git a/src/articles/articles.controller.ts b/src/articles/articles.controller.ts index 1fcf4a3..f2664a8 100644 --- a/src/articles/articles.controller.ts +++ b/src/articles/articles.controller.ts @@ -32,6 +32,14 @@ export class ArticlesController { async getOne(@Param('id') id: string): Promise { return this.articlesService.getAndIncreaseReadCount(id); } + + // 增加阅读数 + @Post(':id/read-count') + @HttpCode(HttpStatus.OK) + @ApiOperation({ summary: '增加文章阅读数' }) + async increaseReadCount(@Param('id') id: string): Promise { + return this.articlesService.increaseReadCount(id); + } } diff --git a/src/articles/articles.service.ts b/src/articles/articles.service.ts index 0991247..b75ed9f 100644 --- a/src/articles/articles.service.ts +++ b/src/articles/articles.service.ts @@ -57,6 +57,14 @@ export class ArticlesService { await article.save(); return { code: ResponseCode.SUCCESS, message: 'success', data: article.toJSON() as ArticleVo }; } + + async increaseReadCount(id: string) { + const article = await this.articleModel.findByPk(id); + if (!article) throw new NotFoundException('文章不存在'); + article.readCount += 1; + await article.save(); + return { code: ResponseCode.SUCCESS, message: 'success', data: article.toJSON() as ArticleVo }; + } }