diff --git a/README.md b/README.md index 2531b0a1..0b12638d 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ - 演示账号: demo 密码:demo123 - 管理端:https://admin.pandarobot.chat - 演示账号: admin 密码:admin123 -- 商业版:体验商业版请联系下方小助手获取演示地址(预计6月份上线)。 + ### 源码地址 [1]github @@ -297,13 +297,6 @@ 5. 打开拉取请求 6. pr请提交到GitHub上,会定时同步到gitee -#### 项目文档 -1. 项目文档基于vitepress构建 -2. 按照[如何参与开源项目](#如何参与开源项目)拉取https://github.com/ageerle/ruoyi-doc -3. 安装依赖:npm install -4. 启动项目:npm run docs:dev -5. 主页路径:docs/guide/introduction/index.md - ### 鸣谢 - [chatgpt-java](https://github.com/Grt1228/chatgpt-java) - [RuoYi-Vue-Plus](https://gitee.com/dromara/RuoYi-Vue-Plus) diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/PromptTemplate.java b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/PromptTemplate.java new file mode 100644 index 00000000..fbf81d77 --- /dev/null +++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/PromptTemplate.java @@ -0,0 +1,47 @@ +package org.ruoyi.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.ruoyi.core.domain.BaseEntity; + +/** + * 提示词模板对象 prompt_template + * + * @author evo + * @date 2025-06-12 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("prompt_template") +public class PromptTemplate extends BaseEntity { + + /** + * 主键 + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 提示词模板名称 + */ + private String templateName; + + /** + * 提示词模板内容 + */ + private String templateContent; + + /** + * 提示词分类,knowledge 知识库类型,chat 对话类型,draw绘画类型 ... + */ + private String category; + + /** + * 备注 + */ + private String remark; + +} \ No newline at end of file diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/bo/PromptTemplateBo.java b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/bo/PromptTemplateBo.java new file mode 100644 index 00000000..2392af09 --- /dev/null +++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/bo/PromptTemplateBo.java @@ -0,0 +1,53 @@ +package org.ruoyi.domain.bo; + +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.ruoyi.common.core.validate.AddGroup; +import org.ruoyi.common.core.validate.EditGroup; +import org.ruoyi.core.domain.BaseEntity; +import org.ruoyi.domain.PromptTemplate; + +/** + * 提示词模板业务对象 prompt_template + * + * @author evo + * @date 2025-06-12 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = PromptTemplate.class, reverseConvertGenerate = false) +public class PromptTemplateBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 提示词模板名称 + */ + @NotBlank(message = "提示词模板名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String templateName; + + /** + * 提示词模板内容 + */ + @NotBlank(message = "提示词模板内容不能为空", groups = {AddGroup.class, EditGroup.class}) + private String templateContent; + + /** + * 提示词分类,knowledge 知识库类型,chat 对话类型,draw绘画类型 ... + */ + @NotBlank(message = "提示词分类", groups = {AddGroup.class, EditGroup.class}) + private String category; + + /** + * 备注 + */ + @NotBlank(message = "备注不能为空", groups = {AddGroup.class, EditGroup.class}) + private String remark; +} \ No newline at end of file diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/vo/PromptTemplateVo.java b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/vo/PromptTemplateVo.java new file mode 100644 index 00000000..5ba9bb71 --- /dev/null +++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/vo/PromptTemplateVo.java @@ -0,0 +1,52 @@ +package org.ruoyi.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.ruoyi.domain.PromptTemplate; + +import java.io.Serializable; + + +/** + * 提示词模板视图对象 prompt_template + * + * @author evo + * @date 2025-06-12 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = PromptTemplate.class) +public class PromptTemplateVo implements Serializable { + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 提示词模板名称 + */ + @ExcelProperty(value = "提示词模板名称") + private String templateName; + + /** + * 提示词模板内容 + */ + @ExcelProperty(value = "提示词模板内容") + private String templateContent; + + /** + * 提示词分类,knowledge 知识库类型,chat 对话类型,draw绘画类型 ... + */ + @ExcelProperty(value = "提示词分类") + private String category; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; +} \ No newline at end of file diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/mapper/PromptTemplateMapper.java b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/mapper/PromptTemplateMapper.java new file mode 100644 index 00000000..93195bd0 --- /dev/null +++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/mapper/PromptTemplateMapper.java @@ -0,0 +1,16 @@ +package org.ruoyi.mapper; + +import org.mapstruct.Mapper; +import org.ruoyi.core.mapper.BaseMapperPlus; +import org.ruoyi.domain.PromptTemplate; +import org.ruoyi.domain.vo.PromptTemplateVo; + +/** + * 提示词模板Mapper接口 + * + * @author evo + * @date 2025-06-12 + */ +public interface PromptTemplateMapper extends BaseMapperPlus { + +} \ No newline at end of file diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/IPromptTemplateService.java b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/IPromptTemplateService.java new file mode 100644 index 00000000..89cff106 --- /dev/null +++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/IPromptTemplateService.java @@ -0,0 +1,49 @@ +package org.ruoyi.service; + + +import org.ruoyi.core.page.PageQuery; +import org.ruoyi.core.page.TableDataInfo; +import org.ruoyi.domain.bo.PromptTemplateBo; +import org.ruoyi.domain.vo.PromptTemplateVo; + +import java.util.Collection; +import java.util.List; + +/** + * 提示词模板Service接口 + * + * @author evo + * @date 2025-06-12 + */ +public interface IPromptTemplateService { + + /** + * 查询提示词模板 + */ + PromptTemplateVo queryById(Long id); + + /** + * 查询提示词模板列表 + */ + TableDataInfo queryPageList(PromptTemplateBo bo, PageQuery pageQuery); + + /** + * 查询提示词模板列表 + */ + List queryList(PromptTemplateBo bo); + + /** + * 新增提示词模板 + */ + Boolean insertByBo(PromptTemplateBo bo); + + /** + * 修改提示词模板 + */ + Boolean updateByBo(PromptTemplateBo bo); + + /** + * 校验并批量删除提示词模板信息 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/impl/PromptTemplateServiceImpl.java b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/impl/PromptTemplateServiceImpl.java new file mode 100644 index 00000000..85fdcd40 --- /dev/null +++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/impl/PromptTemplateServiceImpl.java @@ -0,0 +1,112 @@ +package org.ruoyi.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.ruoyi.common.core.utils.MapstructUtils; +import org.ruoyi.common.core.utils.StringUtils; +import org.ruoyi.core.page.PageQuery; +import org.ruoyi.core.page.TableDataInfo; +import org.ruoyi.domain.PromptTemplate; +import org.ruoyi.domain.bo.PromptTemplateBo; +import org.ruoyi.domain.vo.PromptTemplateVo; +import org.ruoyi.mapper.PromptTemplateMapper; +import org.ruoyi.service.IPromptTemplateService; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; + +/** + * 提示词模板Service业务层处理 + * + * @author evo + * @date 2025-06-12 + */ +@Service +@RequiredArgsConstructor +public class PromptTemplateServiceImpl implements IPromptTemplateService { + + private final PromptTemplateMapper baseMapper; + + /** + * 查询提示词模板 + */ + @Override + public PromptTemplateVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 查询提示词模板列表 + */ + @Override + public TableDataInfo queryPageList(PromptTemplateBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询提示词模板列表 + */ + @Override + public List queryList(PromptTemplateBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(PromptTemplateBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(StringUtils.isNotBlank(bo.getTemplateName()), + PromptTemplate::getTemplateName, bo.getTemplateName()); + lqw.like(StringUtils.isNotBlank(bo.getTemplateContent()), + PromptTemplate::getTemplateContent, bo.getTemplateContent()); + lqw.eq(StringUtils.isNotBlank(bo.getCategory()), + PromptTemplate::getCategory, bo.getCategory()); + return lqw; + } + + /** + * 新增提示词模板 + */ + @Override + public Boolean insertByBo(PromptTemplateBo bo) { + PromptTemplate add = MapstructUtils.convert(bo, PromptTemplate.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改提示词模板 + */ + @Override + public Boolean updateByBo(PromptTemplateBo bo) { + PromptTemplate update = MapstructUtils.convert(bo, PromptTemplate.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(PromptTemplate entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 批量删除提示词模板 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteBatchIds(ids) > 0; + } +} \ No newline at end of file diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/resources/mapper/PromptTemplateMapper.xml b/ruoyi-modules-api/ruoyi-chat-api/src/main/resources/mapper/PromptTemplateMapper.xml new file mode 100644 index 00000000..386b2348 --- /dev/null +++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/resources/mapper/PromptTemplateMapper.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/controller/chat/PromptTemplateController.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/controller/chat/PromptTemplateController.java new file mode 100644 index 00000000..ec7b8057 --- /dev/null +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/controller/chat/PromptTemplateController.java @@ -0,0 +1,111 @@ +package org.ruoyi.chat.controller.chat; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.ruoyi.common.core.domain.R; +import org.ruoyi.common.core.validate.AddGroup; +import org.ruoyi.common.core.validate.EditGroup; +import org.ruoyi.common.excel.utils.ExcelUtil; +import org.ruoyi.common.idempotent.annotation.RepeatSubmit; +import org.ruoyi.common.log.annotation.Log; +import org.ruoyi.common.log.enums.BusinessType; +import org.ruoyi.common.web.core.BaseController; +import org.ruoyi.core.page.PageQuery; +import org.ruoyi.core.page.TableDataInfo; +import org.ruoyi.domain.bo.PromptTemplateBo; +import org.ruoyi.domain.vo.PromptTemplateVo; +import org.ruoyi.service.IPromptTemplateService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 提示词模板 + * + * @author evo + * @date 2025-06-12 + */ +@Validated +@RestController +@RequiredArgsConstructor +@RequestMapping("/system/promptTemplate") +public class PromptTemplateController extends BaseController { + + private final IPromptTemplateService promptTemplateService; + + /** + * 查询提示词模板列表 + */ + @SaCheckPermission("system:promptTemplate:list") + @GetMapping("/list") + public TableDataInfo list(PromptTemplateBo bo, PageQuery pageQuery) { + return promptTemplateService.queryPageList(bo, pageQuery); + } + + /** + * 导出提示词模板列表 + */ + @SaCheckPermission("system:promptTemplate:export") + @Log(title = "提示词模板", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(PromptTemplateBo bo, HttpServletResponse response) { + List list = promptTemplateService.queryList(bo); + ExcelUtil.exportExcel(list, "提示词模板", PromptTemplateVo.class, response); + } + + /** + * 获取提示词模板详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("system:promptTemplate:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) { + return R.ok(promptTemplateService.queryById(id)); + } + + /** + * 新增提示词模板 + */ + @SaCheckPermission("system:promptTemplate:add") + @Log(title = "提示词模板", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody PromptTemplateBo bo) { + return toAjax(promptTemplateService.insertByBo(bo)); + } + + /** + * 修改提示词模板 + */ + @SaCheckPermission("system:promptTemplate:edit") + @Log(title = "提示词模板", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody PromptTemplateBo bo) { + return toAjax(promptTemplateService.updateByBo(bo)); + } + + /** + * 删除提示词模板 + * + * @param ids 主键串 + */ + @SaCheckPermission("system:promptTemplate:remove") + @Log(title = "提示词模板", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) { + return toAjax(promptTemplateService.deleteWithValidByIds(List.of(ids), true)); + } +} \ No newline at end of file diff --git a/script/deploy/one-step-script/deploy-cn-macos.sh b/script/deploy/one-step-script/deploy-cn-macos.sh new file mode 100644 index 00000000..2237fb97 --- /dev/null +++ b/script/deploy/one-step-script/deploy-cn-macos.sh @@ -0,0 +1,519 @@ +#!/bin/bash + +# RuoYi-AI Interactive Deployment Script +# This script helps configure and deploy the RuoYi-AI project with custom settings + +set -e + +echo "==================================================" +echo " RuoYi-AI 交互式部署脚本" +echo "==================================================" +echo "" +echo "此脚本将引导您完成 RuoYi-AI 的配置和部署。" +echo "系统将提示您输入各种配置参数。" +echo "" + +SCRIPT_DIR=${PWD} + +# 提示输入部署目录,带有默认值 +read -p "请输入部署目录 [${PWD}/ruoyi-ai-deploy]: " user_input +DEPLOY_DIR="${user_input:-${PWD}/ruoyi-ai-deploy}" + +# 检查目录是否存在 +if [ -d "$DEPLOY_DIR" ]; then + echo "警告:目录 $DEPLOY_DIR 已存在!" + read -p "您想删除它吗?[y/N]: " delete_choice + + case "${delete_choice:-N}" in + [Yy]* ) + echo "正在删除现有目录..." + rm -rf "$DEPLOY_DIR" + mkdir -p "$DEPLOY_DIR" + echo "目录已重新创建。" + ;; + * ) + echo "保留现有目录。" + ;; + esac +else + mkdir -p "$DEPLOY_DIR" + echo "目录已创建于 $DEPLOY_DIR" +fi + +echo "选定的部署目录: $DEPLOY_DIR" + +mkdir -p ${DEPLOY_DIR}/{data/mysql,data/redis,data/logs,data/weaviate} +cd ${DEPLOY_DIR} + +# Function to prompt for a value with a default +prompt_with_default() { + local prompt=$1 + local default=$2 + local var_name=$3 + + read -p "${prompt} [${default}]: " input + if [ -z "$input" ]; then + eval "${var_name}=\"${default}\"" + else + eval "${var_name}=\"${input}\"" + fi +} + +# Function to prompt for a password with masking +prompt_for_password() { + local prompt=$1 + local default=$2 + local var_name=$3 + + read -sp "${prompt} [default: ${default}]: " input + echo "" + if [ -z "$input" ]; then + eval "${var_name}=\"${default}\"" + else + eval "${var_name}=\"${input}\"" + fi +} + +# Function to escape special characters for sed replacement string +escape_sed_replacement_string() { + # Escape &, \, and the delimiter | for the sed replacement string + echo "$1" | sed -e 's/[&\\|]/\\&/g' +} + +echo "=== 常规配置 ===" +prompt_with_default "时区" "Asia/Shanghai" "TZ" + +echo "" +echo "=== MySQL 配置 ===" +prompt_with_default "MySQL 端口" "3306" "MYSQL_PORT" +prompt_with_default "MySQL 数据库名称" "ruoyi-ai" "MYSQL_DATABASE" +prompt_for_password "MySQL root 密码" "root" "MYSQL_ROOT_PASSWORD" + +echo "" +echo "=== Redis 配置 ===" +prompt_with_default "Redis 端口" "6379" "REDIS_PORT" +prompt_for_password "Redis 密码 (留空则无密码)" "" "REDIS_PASSWORD" +prompt_with_default "Redis 数据库索引" "0" "REDIS_DATABASE" +prompt_with_default "Redis 连接超时时间" "10s" "REDIS_TIMEOUT" + +echo "" +echo "=== 后端服务配置 ===" +prompt_with_default "后端服务端口" "6039" "SERVER_PORT" +prompt_with_default "后端服务主机名" "ruoyi-backend" "BACKEND_HOST" +prompt_with_default "数据库用户名" "root" "DB_USERNAME" +prompt_for_password "数据库密码" "root" "DB_PASSWORD" + +echo "" +echo "=== 前端服务配置 ===" +prompt_with_default "Admin UI 端口" "8082" "ADMIN_PORT" +prompt_with_default "Web UI 端口" "8081" "WEB_PORT" + +echo "" +echo "=== Weaviate 向量数据库配置 ===" +prompt_with_default "Weaviate HTTP 端口" "50050" "WEAVIATE_HTTP_PORT" +prompt_with_default "Weaviate gRPC 端口" "50051" "WEAVIATE_GRPC_PORT" +prompt_with_default "Weaviate 查询限制" "25" "WEAVIATE_QUERY_LIMIT" +prompt_with_default "Weaviate 匿名访问" "true" "WEAVIATE_ANONYMOUS_ACCESS" +prompt_with_default "Weaviate 数据路径" "/var/lib/weaviate" "WEAVIATE_DATA_PATH" +prompt_with_default "Weaviate 向量化模块" "none" "WEAVIATE_VECTORIZER_MODULE" +prompt_with_default "Weaviate 模块" "text2vec-cohere,text2vec-huggingface,text2vec-palm,text2vec-openai,generative-openai,generative-cohere,generative-palm,ref2vec-centroid,reranker-cohere,qna-openai" "WEAVIATE_MODULES" +prompt_with_default "Weaviate 集群主机名" "node1" "WEAVIATE_CLUSTER_HOSTNAME" +prompt_with_default "Weaviate 协议" "http" "WEAVIATE_PROTOCOL" +prompt_with_default "Weaviate 类名" "LocalKnowledge" "WEAVIATE_CLASSNAME" + +echo "" +echo "=== 生产环境配置 ===" +prompt_with_default "生产环境数据库 URL" "jdbc:mysql://mysql:3306/ruoyi-ai?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true" "PROD_DB_URL" +prompt_with_default "生产环境数据库用户名" "root" "PROD_DB_USERNAME" +prompt_for_password "生产环境数据库密码" "root" "PROD_DB_PASSWORD" +prompt_with_default "生产环境 Redis 主机" "redis" "PROD_REDIS_HOST" +prompt_with_default "生产环境 Redis 端口" "6379" "PROD_REDIS_PORT" +prompt_with_default "生产环境 Redis 数据库" "0" "PROD_REDIS_DATABASE" +prompt_for_password "生产环境 Redis 密码 (留空则无密码)" "" "PROD_REDIS_PASSWORD" +prompt_with_default "生产环境 Redis 超时时间" "10s" "PROD_REDIS_TIMEOUT" + +echo "" +echo "=== 前端配置 ===" +prompt_with_default "前端后端 API 基础 URL" "http://${BACKEND_HOST}:${SERVER_PORT}" "FRONTEND_API_BASE_URL" +prompt_with_default "前端开发服务器端口" "3000" "FRONTEND_DEV_PORT" + +# Copy template files +cp ${SCRIPT_DIR}/template/.env.template ${DEPLOY_DIR}/.env +cp ${SCRIPT_DIR}/template/docker-compose.yaml.template ${DEPLOY_DIR}/docker-compose.yaml + +echo "已将模板文件复制到部署目录。" + +# 替换 .env 文件中的占位符 +echo "正在使用您的配置更新 .env 文件..." +sed -i '' "s|{{TZ}}|$(escape_sed_replacement_string "${TZ}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{MYSQL_ROOT_PASSWORD}}|$(escape_sed_replacement_string "${MYSQL_ROOT_PASSWORD}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{MYSQL_DATABASE}}|$(escape_sed_replacement_string "${MYSQL_DATABASE}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{MYSQL_PORT}}|$(escape_sed_replacement_string "${MYSQL_PORT}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{REDIS_PORT}}|$(escape_sed_replacement_string "${REDIS_PORT}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{REDIS_PASSWORD}}|$(escape_sed_replacement_string "${REDIS_PASSWORD}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{REDIS_DATABASE}}|$(escape_sed_replacement_string "${REDIS_DATABASE}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{REDIS_TIMEOUT}}|$(escape_sed_replacement_string "${REDIS_TIMEOUT}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{SERVER_PORT}}|$(escape_sed_replacement_string "${SERVER_PORT}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{DB_URL}}|$(escape_sed_replacement_string "jdbc:mysql://mysql:3306/${MYSQL_DATABASE}?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{DB_USERNAME}}|$(escape_sed_replacement_string "${DB_USERNAME}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{DB_PASSWORD}}|$(escape_sed_replacement_string "${DB_PASSWORD}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{BACKEND_HOST}}|$(escape_sed_replacement_string "${BACKEND_HOST}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{ADMIN_PORT}}|$(escape_sed_replacement_string "${ADMIN_PORT}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{WEB_PORT}}|$(escape_sed_replacement_string "${WEB_PORT}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{FRONTEND_API_BASE_URL}}|$(escape_sed_replacement_string "${FRONTEND_API_BASE_URL}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{FRONTEND_DEV_PORT}}|$(escape_sed_replacement_string "${FRONTEND_DEV_PORT}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{WEAVIATE_HTTP_PORT}}|$(escape_sed_replacement_string "${WEAVIATE_HTTP_PORT}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{WEAVIATE_GRPC_PORT}}|$(escape_sed_replacement_string "${WEAVIATE_GRPC_PORT}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{WEAVIATE_QUERY_LIMIT}}|$(escape_sed_replacement_string "${WEAVIATE_QUERY_LIMIT}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{WEAVIATE_ANONYMOUS_ACCESS}}|$(escape_sed_replacement_string "${WEAVIATE_ANONYMOUS_ACCESS}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{WEAVIATE_DATA_PATH}}|$(escape_sed_replacement_string "${WEAVIATE_DATA_PATH}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{WEAVIATE_VECTORIZER_MODULE}}|$(escape_sed_replacement_string "${WEAVIATE_VECTORIZER_MODULE}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{WEAVIATE_MODULES}}|$(escape_sed_replacement_string "${WEAVIATE_MODULES}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{WEAVIATE_CLUSTER_HOSTNAME}}|$(escape_sed_replacement_string "${WEAVIATE_CLUSTER_HOSTNAME}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{WEAVIATE_PROTOCOL}}|$(escape_sed_replacement_string "${WEAVIATE_PROTOCOL}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{WEAVIATE_CLASSNAME}}|$(escape_sed_replacement_string "${WEAVIATE_CLASSNAME}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{PROD_DB_URL}}|$(escape_sed_replacement_string "${PROD_DB_URL}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{PROD_DB_USERNAME}}|$(escape_sed_replacement_string "${PROD_DB_USERNAME}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{PROD_DB_PASSWORD}}|$(escape_sed_replacement_string "${PROD_DB_PASSWORD}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{PROD_REDIS_HOST}}|$(escape_sed_replacement_string "${PROD_REDIS_HOST}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{PROD_REDIS_PORT}}|$(escape_sed_replacement_string "${PROD_REDIS_PORT}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{PROD_REDIS_DATABASE}}|$(escape_sed_replacement_string "${PROD_REDIS_DATABASE}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{PROD_REDIS_PASSWORD}}|$(escape_sed_replacement_string "${PROD_REDIS_PASSWORD}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{PROD_REDIS_TIMEOUT}}|$(escape_sed_replacement_string "${PROD_REDIS_TIMEOUT}")|g" ${DEPLOY_DIR}/.env + +echo "已使用您的配置更新 .env 文件。" + +# 替换 docker-compose.yaml 文件中的占位符 +echo "正在使用您的配置更新 docker-compose.yaml 文件..." + +# Determine Redis command arguments based on password +#if [ -n "${REDIS_PASSWORD}" ]; then +# REDIS_COMMAND_ARGS="--requirepass $(escape_sed_replacement_string "${REDIS_PASSWORD}")" +#else +# REDIS_COMMAND_ARGS="" +#fi + +sed -i '' "s|{{MYSQL_ROOT_PASSWORD}}|$(escape_sed_replacement_string "${MYSQL_ROOT_PASSWORD}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{MYSQL_DATABASE}}|$(escape_sed_replacement_string "${MYSQL_DATABASE}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{MYSQL_PORT}}|$(escape_sed_replacement_string "${MYSQL_PORT}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{REDIS_PORT}}|$(escape_sed_replacement_string "${REDIS_PORT}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{REDIS_COMMAND_ARGS}}|$(escape_sed_replacement_string "${REDIS_COMMAND_ARGS}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{WEAVIATE_HTTP_PORT}}|$(escape_sed_replacement_string "${WEAVIATE_HTTP_PORT}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{WEAVIATE_GRPC_PORT}}|$(escape_sed_replacement_string "${WEAVIATE_GRPC_PORT}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{WEAVIATE_QUERY_LIMIT}}|$(escape_sed_replacement_string "${WEAVIATE_QUERY_LIMIT}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{WEAVIATE_ANONYMOUS_ACCESS}}|$(escape_sed_replacement_string "${WEAVIATE_ANONYMOUS_ACCESS}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{WEAVIATE_DATA_PATH}}|$(escape_sed_replacement_string "${WEAVIATE_DATA_PATH}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{WEAVIATE_VECTORIZER_MODULE}}|$(escape_sed_replacement_string "${WEAVIATE_VECTORIZER_MODULE}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{WEAVIATE_MODULES}}|$(escape_sed_replacement_string "${WEAVIATE_MODULES}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{WEAVIATE_CLUSTER_HOSTNAME}}|$(escape_sed_replacement_string "${WEAVIATE_CLUSTER_HOSTNAME}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{SERVER_PORT}}|$(escape_sed_replacement_string "${SERVER_PORT}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{DB_URL}}|$(escape_sed_replacement_string "jdbc:mysql://mysql:3306/${MYSQL_DATABASE}?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{DB_USERNAME}}|$(escape_sed_replacement_string "${DB_USERNAME}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{DB_PASSWORD}}|$(escape_sed_replacement_string "${DB_PASSWORD}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{REDIS_HOST}}|redis|g" ${DEPLOY_DIR}/docker-compose.yaml # REDIS_HOST is hardcoded to 'redis' in docker-compose +sed -i '' "s|{{REDIS_DATABASE}}|$(escape_sed_replacement_string "${REDIS_DATABASE}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{REDIS_PASSWORD}}|$(escape_sed_replacement_string "${REDIS_PASSWORD}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{REDIS_TIMEOUT}}|$(escape_sed_replacement_string "${REDIS_TIMEOUT}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{TZ}}|$(escape_sed_replacement_string "${TZ}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{ADMIN_PORT}}|$(escape_sed_replacement_string "${ADMIN_PORT}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{WEB_PORT}}|$(escape_sed_replacement_string "${WEB_PORT}")|g" ${DEPLOY_DIR}/docker-compose.yaml + +echo "" +echo "=== 构建或部署选项 ===" +read -p "您想构建新镜像 (B) 还是直接使用现有镜像部署 (D)?[B/d]: " build_or_deploy_choice +BUILD_CHOICE="${build_or_deploy_choice:-B}" # Default to Build + +if [[ "${BUILD_CHOICE}" == [Bb]* ]]; then + echo "正在进行镜像构建过程..." + + # Clone ruoyi-ai-backend repositories + if [ -d "${DEPLOY_DIR}/ruoyi-ai" ]; then + echo "目录 ${DEPLOY_DIR}/ruoyi-ai 已存在。" + read -p "您想删除它并克隆一个新的副本吗?[Y/n]: " answer + case ${answer:-Y} in + [Yy]* ) + echo "正在删除现有目录..." + rm -rf ${DEPLOY_DIR}/ruoyi-ai + echo "正在克隆 ruoyi-ai-backend 仓库..." + cd ${DEPLOY_DIR} && git clone https://gitee.com/ageerle/ruoyi-ai + + # 提示选择分支 + read -p "请输入 ruoyi-ai 仓库的分支名称 [main]: " RUOYI_AI_BRANCH + RUOYI_AI_BRANCH="${RUOYI_AI_BRANCH:-main}" + echo "正在切换到分支: ${RUOYI_AI_BRANCH}" + cd ${DEPLOY_DIR}/ruoyi-ai && git checkout ${RUOYI_AI_BRANCH} + cd .. + ;; + [Nn]* ) + echo "跳过克隆操作。" + ;; + * ) + echo "无效输入。跳过克隆操作。" + ;; + esac + else + echo "正在克隆 ruoyi-ai-backend 仓库..." + cd ${DEPLOY_DIR} && git clone https://gitee.com/ageerle/ruoyi-ai + + # 提示选择分支 + read -p "请输入 ruoyi-ai 仓库的分支名称 [main]: " RUOYI_AI_BRANCH + RUOYI_AI_BRANCH="${RUOYI_AI_BRANCH:-main}" + echo "正在切换到分支: ${RUOYI_AI_BRANCH}" + cd ${DEPLOY_DIR}/ruoyi-ai && git checkout ${RUOYI_AI_BRANCH} + cd .. + fi + + # Clone ruoyi-ai-admin repositories + if [ -d "${DEPLOY_DIR}/ruoyi-admin" ]; then + echo "目录 ${DEPLOY_DIR}/ruoyi-admin 已存在。" + read -p "您想删除它并克隆一个新的副本吗?[Y/n]: " answer + case ${answer:-Y} in + [Yy]* ) + echo "正在删除现有目录..." + rm -rf ${DEPLOY_DIR}/ruoyi-admin + echo "正在克隆 ruoyi-admin 仓库..." + cd ${DEPLOY_DIR} && git clone https://gitee.com/ageerle/ruoyi-admin + + # 提示选择分支 + read -p "请输入 ruoyi-admin 仓库的分支名称 [main]: " RUOYI_ADMIN_BRANCH + RUOYI_ADMIN_BRANCH="${RUOYI_ADMIN_BRANCH:-main}" + echo "正在切换到分支: ${RUOYI_ADMIN_BRANCH}" + cd ${DEPLOY_DIR}/ruoyi-admin && git checkout ${RUOYI_ADMIN_BRANCH} + cd .. + ;; + [Nn]* ) + echo "跳过克隆操作。" + ;; + * ) + echo "无效输入。跳过克隆操作。" + ;; + esac + else + echo "正在克隆 ruoyi-ai-admin 仓库..." + cd ${DEPLOY_DIR} && git clone https://gitee.com/ageerle/ruoyi-admin + + # 提示选择分支 + read -p "请输入 ruoyi-admin 仓库的分支名称 [main]: " RUOYI_ADMIN_BRANCH + RUOYI_ADMIN_BRANCH="${RUOYI_ADMIN_BRANCH:-main}" + echo "正在切换到分支: ${RUOYI_ADMIN_BRANCH}" + cd ${DEPLOY_DIR}/ruoyi-admin && git checkout ${RUOYI_ADMIN_BRANCH} + cd .. + fi + + # Clone ruoyi-ai-web repositories + if [ -d "${DEPLOY_DIR}/ruoyi-web" ]; then + echo "目录 ${DEPLOY_DIR}/ruoyi-web 已存在。" + read -p "您想删除它并克隆一个新的副本吗?[Y/n]: " answer + case ${answer:-Y} in + [Yy]* ) + echo "正在删除现有目录..." + rm -rf ${DEPLOY_DIR}/ruoyi-web + echo "正在克隆 ruoyi-ai-web 仓库..." + cd ${DEPLOY_DIR} && git clone https://gitee.com/ageerle/ruoyi-web + + # 提示选择分支 + read -p "请输入 ruoyi-web 仓库的分支名称 [main]: " RUOYI_WEB_BRANCH + RUOYI_WEB_BRANCH="${RUOYI_WEB_BRANCH:-main}" + echo "正在切换到分支: ${RUOYI_WEB_BRANCH}" + cd ${DEPLOY_DIR}/ruoyi-web && git checkout ${RUOYI_WEB_BRANCH} + cd .. + ;; + [Nn]* ) + echo "跳过克隆操作。" + ;; + * ) + echo "无效输入。跳过克隆操作。" + ;; + esac + else + echo "正在克隆 ruoyi-ai-web 仓库..." + cd ${DEPLOY_DIR} && git clone https://gitee.com/ageerle/ruoyi-web + + # 提示选择分支 + read -p "请输入 ruoyi-web 仓库的分支名称 [main]: " RUOYI_WEB_BRANCH + RUOYI_WEB_BRANCH="${RUOYI_WEB_BRANCH:-main}" + echo "正在切换到分支: ${RUOYI_WEB_BRANCH}" + cd ${DEPLOY_DIR}/ruoyi-web && git checkout ${RUOYI_WEB_BRANCH} + cd .. + fi + + # 更新 application-prod.yml 文件 + echo "正在使用您的配置更新 application-prod.yml 文件..." + # Copy application-prod.yml template + cp ${SCRIPT_DIR}/template/application-prod.yml.template ${DEPLOY_DIR}/ruoyi-ai/ruoyi-admin/src/main/resources/application-prod.yml + + # Replace placeholders in application-prod.yml + sed -i '' "s|{{PROD_DB_URL}}|$(escape_sed_replacement_string "${PROD_DB_URL}")|g" ${DEPLOY_DIR}/ruoyi-ai/ruoyi-admin/src/main/resources/application-prod.yml + sed -i '' "s|{{PROD_DB_USERNAME}}|$(escape_sed_replacement_string "${PROD_DB_USERNAME}")|g" ${DEPLOY_DIR}/ruoyi-ai/ruoyi-admin/src/main/resources/application-prod.yml + sed -i '' "s|{{PROD_DB_PASSWORD}}|$(escape_sed_replacement_string "${PROD_DB_PASSWORD}")|g" ${DEPLOY_DIR}/ruoyi-ai/ruoyi-admin/src/main/resources/application-prod.yml + sed -i '' "s|{{PROD_REDIS_HOST}}|$(escape_sed_replacement_string "${PROD_REDIS_HOST}")|g" ${DEPLOY_DIR}/ruoyi-ai/ruoyi-admin/src/main/resources/application-prod.yml + sed -i '' "s|{{PROD_REDIS_PORT}}|$(escape_sed_replacement_string "${PROD_REDIS_PORT}")|g" ${DEPLOY_DIR}/ruoyi-ai/ruoyi-admin/src/main/resources/application-prod.yml + sed -i '' "s|{{PROD_REDIS_DATABASE}}|$(escape_sed_replacement_string "${PROD_REDIS_DATABASE}")|g" ${DEPLOY_DIR}/ruoyi-ai/ruoyi-admin/src/main/resources/application-prod.yml + if [ -z "${PROD_REDIS_PASSWORD}" ]; then + sed -i '' "s/^ password: {{PROD_REDIS_PASSWORD}}/# password: {{PROD_REDIS_PASSWORD}}/g" ${DEPLOY_DIR}/ruoyi-ai/ruoyi-admin/src/main/resources/application-prod.yml + else + sed -i '' "s|{{PROD_REDIS_PASSWORD}}|$(escape_sed_replacement_string "${PROD_REDIS_PASSWORD}")|g" ${DEPLOY_DIR}/ruoyi-ai/ruoyi-admin/src/main/resources/application-prod.yml + fi + sed -i '' "s|{{PROD_REDIS_TIMEOUT}}|$(escape_sed_replacement_string "${PROD_REDIS_TIMEOUT}")|g" ${DEPLOY_DIR}/ruoyi-ai/ruoyi-admin/src/main/resources/application-prod.yml + + # 更新 vite.config.mts 文件 + echo "正在使用您的配置更新 vite.config.mts 文件..." + sed -i '' "s|http://127.0.0.1:6039|${FRONTEND_API_BASE_URL}|g" ${DEPLOY_DIR}/ruoyi-admin/apps/web-antd/vite.config.mts + + # 更新 docker-compose.yaml 文件中的镜像标签 + echo "正在更新 docker-compose.yaml 文件中的镜像标签..." + sed -i '' "s|ruoyi-ai-backend:latest|ruoyi-ai-backend:${RUOYI_AI_BRANCH}|g" ${DEPLOY_DIR}/docker-compose.yaml + sed -i '' "s|ruoyi-ai-admin:latest|ruoyi-ai-admin:${RUOYI_ADMIN_BRANCH}|g" ${DEPLOY_DIR}/docker-compose.yaml + sed -i '' "s|ruoyi-ai-web:latest|ruoyi-ai-web:${RUOYI_WEB_BRANCH}|g" ${DEPLOY_DIR}/docker-compose.yaml + + # Create Nginx configuration files for frontend services + echo "正在将 Admin UI 的 Nginx 配置模板复制到临时位置..." + cp ${SCRIPT_DIR}/template/nginx.admin.conf.template ${DEPLOY_DIR}/nginx.admin.conf.tmp + + echo "正在临时文件中更新 Admin UI 的 Nginx 配置..." + sed -i '' "s|{{BACKEND_HOST}}|$(escape_sed_replacement_string "${BACKEND_HOST}")|g" ${DEPLOY_DIR}/nginx.admin.conf.tmp + sed -i '' "s|{{SERVER_PORT}}|$(escape_sed_replacement_string "${SERVER_PORT}")|g" ${DEPLOY_DIR}/nginx.admin.conf.tmp + + echo "正在将更新后的 Admin UI Nginx 配置移动到最终位置..." + mv ${DEPLOY_DIR}/nginx.admin.conf.tmp ${DEPLOY_DIR}/ruoyi-admin/nginx.conf + + echo "正在将 Web UI 的 Nginx 配置模板复制到临时位置..." + cp ${SCRIPT_DIR}/template/nginx.web.conf.template ${DEPLOY_DIR}/nginx.web.conf.tmp + + echo "正在临时文件中更新 Web UI 的 Nginx 配置..." + sed -i '' "s|{{BACKEND_HOST}}|$(escape_sed_replacement_string "${BACKEND_HOST}")|g" ${DEPLOY_DIR}/nginx.web.conf.tmp + sed -i '' "s|{{SERVER_PORT}}|$(escape_sed_replacement_string "${SERVER_PORT}")|g" ${DEPLOY_DIR}/nginx.web.conf.tmp + + echo "正在将更新后的 Web UI Nginx 配置移动到最终位置..." + mv ${DEPLOY_DIR}/nginx.web.conf.tmp ${DEPLOY_DIR}/ruoyi-web/nginx.conf + + # 为前端服务创建 Dockerfile + echo "正在为 Admin UI 创建 Dockerfile..." + cat > ${DEPLOY_DIR}/ruoyi-admin/Dockerfile << EOF +FROM nginx:1.25-alpine + +COPY dist/ /usr/share/nginx/html/ +COPY nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] +EOF + + echo "正在为 Web UI 创建 Dockerfile..." + cat > ${DEPLOY_DIR}/ruoyi-web/Dockerfile << EOF +FROM nginx:1.25-alpine + +COPY dist/ /usr/share/nginx/html/ +COPY nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] +EOF + + # 构建后端服务 + echo "正在构建 Ruoyi-AI 后端服务..." + cd ${DEPLOY_DIR}/ruoyi-ai + docker run -it --rm --name build-ruoyi-ai-backend -v ${DEPLOY_DIR}/ruoyi-ai:/code --entrypoint=/bin/bash maven:3.9.9-eclipse-temurin-17-alpine -c "cd /code && mvn clean package -P prod" + + # 构建前端 Admin 服务 + echo "正在构建 Ruoyi-AI 前端 Admin 服务..." + cd ${DEPLOY_DIR}/ruoyi-admin + docker run -it --rm --name build-ruoyi-ai-admin -v ${DEPLOY_DIR}/ruoyi-admin:/app -w /app node:20 sh -c "npm install -g pnpm && pnpm install && pnpm build" + + # 构建前端 Web 服务 + echo "正在构建 Ruoyi-AI 前端 Web 服务..." + cd ${DEPLOY_DIR}/ruoyi-web + docker run -it --rm --name build-ruoyi-ai-web -v ${DEPLOY_DIR}/ruoyi-web:/app -w /app node:20 sh -c "npm install -g pnpm && pnpm install && pnpm build" + + # Build Docker images + echo "Building Ruoyi-AI Backend Docker images..." + cd ${DEPLOY_DIR}/ruoyi-ai + rm -rf temp + mkdir temp + cp ./ruoyi-admin/target/ruoyi-admin.jar temp/ + cd temp/ + cat > Dockerfile << EOF +FROM openjdk:17-jdk-slim +WORKDIR /app +COPY ruoyi-admin.jar /app/ruoyi-admin.jar +EXPOSE ${SERVER_PORT} +ENTRYPOINT ["java","-jar","ruoyi-admin.jar","--spring.profiles.active=prod"] +EOF + docker build -t ruoyi-ai-backend:${RUOYI_AI_BRANCH} . + cd .. + + echo "Building Ruoyi-AI Admin Docker images..." + cd ${DEPLOY_DIR}/ruoyi-admin + rm -rf temp + mkdir temp + cp ./apps/web-antd/dist.zip temp/ + cp Dockerfile temp/ + cp nginx.conf temp/ + cd temp/ + unzip dist.zip -d dist + rm -f dist.zip + docker build -t ruoyi-ai-admin:${RUOYI_ADMIN_BRANCH} . + cd .. + + echo "Building Ruoyi-AI Web Docker images..." + cd ${DEPLOY_DIR}/ruoyi-web + rm -rf temp + mkdir temp + cp -pr ${DEPLOY_DIR}/ruoyi-web/dist temp/ + cp Dockerfile temp/ + cp nginx.conf temp/ + cd temp/ + docker build -t ruoyi-ai-web:${RUOYI_WEB_BRANCH} . + cd .. +else + echo "跳过镜像构建过程。正在使用现有镜像直接部署..." + + # 提示输入分支名称用于镜像标签 + read -p "请输入 ruoyi-ai-backend 镜像的标签 [main]: " RUOYI_AI_BRANCH + RUOYI_AI_BRANCH="${RUOYI_AI_BRANCH:-main}" + + read -p "请输入 ruoyi-ai-admin 镜像的标签 [main]: " RUOYI_ADMIN_BRANCH + RUOYI_ADMIN_BRANCH="${RUOYI_ADMIN_BRANCH:-main}" + + read -p "请输入 ruoyi-ai-web 镜像的标签 [main]: " RUOYI_WEB_BRANCH + RUOYI_WEB_BRANCH="${RUOYI_WEB_BRANCH:-main}" + + # 更新 docker-compose.yaml 文件中的镜像标签 + echo "正在更新 docker-compose.yaml 文件中的镜像标签..." + sed -i '' "s|ruoyi-ai-backend:latest|ruoyi-ai-backend:${RUOYI_AI_BRANCH}|g" ${DEPLOY_DIR}/docker-compose.yaml + sed -i '' "s|ruoyi-ai-admin:latest|ruoyi-ai-admin:${RUOYI_ADMIN_BRANCH}|g" ${DEPLOY_DIR}/docker-compose.yaml + sed -i '' "s|ruoyi-ai-web:latest|ruoyi-ai-web:${RUOYI_WEB_BRANCH}|g" ${DEPLOY_DIR}/docker-compose.yaml +fi + +# Copy SQL file +rm -rf ${DEPLOY_DIR}/mysql-init +cp -pr ${SCRIPT_DIR}/mysql-init ${DEPLOY_DIR}/ + +# 使用配置值更新 SQL 文件 +echo "正在更新 SQL 配置值..." +sed -i '' "s|'weaviate', 'host', '127.0.0.1:6038'|'weaviate', 'host', 'weaviate:8080'|g" ${DEPLOY_DIR}/mysql-init/ruoyi-ai.sql +sed -i '' "s|'weaviate', 'protocol', 'http'|'weaviate', 'protocol', '${WEAVIATE_PROTOCOL}'|g" ${DEPLOY_DIR}/mysql-init/ruoyi-ai.sql +sed -i '' "s|'weaviate', 'classname', 'LocalKnowledge'|'weaviate', 'classname', '${WEAVIATE_CLASSNAME}'|g" ${DEPLOY_DIR}/mysql-init/ruoyi-ai.sql + +# 使用 Docker Compose 部署 +echo "正在使用 Docker Compose 进行部署..." +cd ${DEPLOY_DIR} +docker-compose down +docker-compose up -d + +echo "==================================================" +echo " RuoYi-AI 部署完成" +echo "==================================================" +echo "" +echo "您的 RuoYi-AI 系统已部署以下服务:" +echo "- 后端 API: http://localhost:${SERVER_PORT}" +echo "- Admin UI: http://localhost:${ADMIN_PORT}" +echo "- Web UI: http://localhost:${WEB_PORT}" +echo "- Weaviate: http://localhost:${WEAVIATE_HTTP_PORT}" +echo "" +echo "所有配置均已根据您的输入进行自定义。" +echo "配置文件已更新为使用环境变量。" +echo "" +echo "感谢您使用 RuoYi-AI 交互式部署脚本!" diff --git a/script/deploy/one-step-script/deploy-en-macos.sh b/script/deploy/one-step-script/deploy-en-macos.sh new file mode 100644 index 00000000..1ac2b4fd --- /dev/null +++ b/script/deploy/one-step-script/deploy-en-macos.sh @@ -0,0 +1,519 @@ +#!/bin/bash + +# RuoYi-AI Interactive Deployment Script +# This script helps configure and deploy the RuoYi-AI project with custom settings + +set -e + +echo "==================================================" +echo " RuoYi-AI Interactive Deployment Script" +echo "==================================================" +echo "" +echo "This script will guide you through the configuration and deployment of RuoYi-AI." +echo "You will be prompted to enter various configuration parameters." +echo "" + +SCRIPT_DIR=${PWD} + +# Prompt for deployment directory with default value +read -p "Enter deployment directory [${PWD}/ruoyi-ai-deploy]: " user_input +DEPLOY_DIR="${user_input:-${PWD}/ruoyi-ai-deploy}" + +# Check if directory exists +if [ -d "$DEPLOY_DIR" ]; then + echo "Warning: Directory $DEPLOY_DIR already exists!" + read -p "Do you want to delete it? [y/N]: " delete_choice + + case "${delete_choice:-N}" in + [Yy]* ) + echo "Deleting existing directory..." + rm -rf "$DEPLOY_DIR" + mkdir -p "$DEPLOY_DIR" + echo "Directory has been recreated." + ;; + * ) + echo "Keeping existing directory." + ;; + esac +else + mkdir -p "$DEPLOY_DIR" + echo "Directory created at $DEPLOY_DIR" +fi + +echo "Selected deployment directory: $DEPLOY_DIR" + +mkdir -p ${DEPLOY_DIR}/{data/mysql,data/redis,data/logs,data/weaviate} +cd ${DEPLOY_DIR} + +# Function to prompt for a value with a default +prompt_with_default() { + local prompt=$1 + local default=$2 + local var_name=$3 + + read -p "${prompt} [${default}]: " input + if [ -z "$input" ]; then + eval "${var_name}=\"${default}\"" + else + eval "${var_name}=\"${input}\"" + fi +} + +# Function to prompt for a password with masking +prompt_for_password() { + local prompt=$1 + local default=$2 + local var_name=$3 + + read -sp "${prompt} [default: ${default}]: " input + echo "" + if [ -z "$input" ]; then + eval "${var_name}=\"${default}\"" + else + eval "${var_name}=\"${input}\"" + fi +} + +# Function to escape special characters for sed replacement string +escape_sed_replacement_string() { + # Escape &, \, and the delimiter | for the sed replacement string + echo "$1" | sed -e 's/[&\\|]/\\&/g' +} + +echo "=== General Configuration ===" +prompt_with_default "Time Zone" "Asia/Shanghai" "TZ" + +echo "" +echo "=== MySQL Configuration ===" +prompt_with_default "MySQL Port" "3306" "MYSQL_PORT" +prompt_with_default "MySQL Database Name" "ruoyi-ai" "MYSQL_DATABASE" +prompt_for_password "MySQL root Password" "root" "MYSQL_ROOT_PASSWORD" + +echo "" +echo "=== Redis Configuration ===" +prompt_with_default "Redis Port" "6379" "REDIS_PORT" +prompt_for_password "Redis Password (leave empty for no password)" "" "REDIS_PASSWORD" +prompt_with_default "Redis Database Index" "0" "REDIS_DATABASE" +prompt_with_default "Redis Connection Timeout" "10s" "REDIS_TIMEOUT" + +echo "" +echo "=== Backend Service Configuration ===" +prompt_with_default "Backend Service Port" "6039" "SERVER_PORT" +prompt_with_default "Backend Service Hostname" "ruoyi-backend" "BACKEND_HOST" +prompt_with_default "Database Username" "root" "DB_USERNAME" +prompt_for_password "Database Password" "root" "DB_PASSWORD" + +echo "" +echo "=== Frontend Service Configuration ===" +prompt_with_default "Admin UI Port" "8082" "ADMIN_PORT" +prompt_with_default "Web UI Port" "8081" "WEB_PORT" + +echo "" +echo "=== Weaviate Vector Database Configuration ===" +prompt_with_default "Weaviate HTTP Port" "50050" "WEAVIATE_HTTP_PORT" +prompt_with_default "Weaviate gRPC Port" "50051" "WEAVIATE_GRPC_PORT" +prompt_with_default "Weaviate Query Limit" "25" "WEAVIATE_QUERY_LIMIT" +prompt_with_default "Weaviate Anonymous Access" "true" "WEAVIATE_ANONYMOUS_ACCESS" +prompt_with_default "Weaviate Data Path" "/var/lib/weaviate" "WEAVIATE_DATA_PATH" +prompt_with_default "Weaviate Vectorizer Module" "none" "WEAVIATE_VECTORIZER_MODULE" +prompt_with_default "Weaviate Modules" "text2vec-cohere,text2vec-huggingface,text2vec-palm,text2vec-openai,generative-openai,generative-cohere,generative-palm,ref2vec-centroid,reranker-cohere,qna-openai" "WEAVIATE_MODULES" +prompt_with_default "Weaviate Cluster Hostname" "node1" "WEAVIATE_CLUSTER_HOSTNAME" +prompt_with_default "Weaviate Protocol" "http" "WEAVIATE_PROTOCOL" +prompt_with_default "Weaviate Class Name" "LocalKnowledge" "WEAVIATE_CLASSNAME" + +echo "" +echo "=== Production Environment Configuration ===" +prompt_with_default "Production Database URL" "jdbc:mysql://mysql:3306/ruoyi-ai?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true" "PROD_DB_URL" +prompt_with_default "Production Database Username" "root" "PROD_DB_USERNAME" +prompt_for_password "Production Database Password" "root" "PROD_DB_PASSWORD" +prompt_with_default "Production Redis Host" "redis" "PROD_REDIS_HOST" +prompt_with_default "Production Redis Port" "6379" "PROD_REDIS_PORT" +prompt_with_default "Production Redis Database" "0" "PROD_REDIS_DATABASE" +prompt_for_password "Production Redis Password (leave empty for no password)" "" "PROD_REDIS_PASSWORD" +prompt_with_default "Production Redis Timeout" "10s" "PROD_REDIS_TIMEOUT" + +echo "" +echo "=== Frontend Configuration ===" +prompt_with_default "Frontend API Base URL" "http://${BACKEND_HOST}:${SERVER_PORT}" "FRONTEND_API_BASE_URL" +prompt_with_default "Frontend Development Server Port" "3000" "FRONTEND_DEV_PORT" + +# Copy template files +cp ${SCRIPT_DIR}/template/.env.template ${DEPLOY_DIR}/.env +cp ${SCRIPT_DIR}/template/docker-compose.yaml.template ${DEPLOY_DIR}/docker-compose.yaml + +echo "Template files copied to deployment directory." + +# Replace placeholders in .env file +echo "Updating .env file with your configuration..." +sed -i '' "s|{{TZ}}|$(escape_sed_replacement_string "${TZ}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{MYSQL_ROOT_PASSWORD}}|$(escape_sed_replacement_string "${MYSQL_ROOT_PASSWORD}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{MYSQL_DATABASE}}|$(escape_sed_replacement_string "${MYSQL_DATABASE}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{MYSQL_PORT}}|$(escape_sed_replacement_string "${MYSQL_PORT}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{REDIS_PORT}}|$(escape_sed_replacement_string "${REDIS_PORT}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{REDIS_PASSWORD}}|$(escape_sed_replacement_string "${REDIS_PASSWORD}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{REDIS_DATABASE}}|$(escape_sed_replacement_string "${REDIS_DATABASE}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{REDIS_TIMEOUT}}|$(escape_sed_replacement_string "${REDIS_TIMEOUT}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{SERVER_PORT}}|$(escape_sed_replacement_string "${SERVER_PORT}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{DB_URL}}|$(escape_sed_replacement_string "jdbc:mysql://mysql:3306/${MYSQL_DATABASE}?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{DB_USERNAME}}|$(escape_sed_replacement_string "${DB_USERNAME}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{DB_PASSWORD}}|$(escape_sed_replacement_string "${DB_PASSWORD}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{BACKEND_HOST}}|$(escape_sed_replacement_string "${BACKEND_HOST}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{ADMIN_PORT}}|$(escape_sed_replacement_string "${ADMIN_PORT}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{WEB_PORT}}|$(escape_sed_replacement_string "${WEB_PORT}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{FRONTEND_API_BASE_URL}}|$(escape_sed_replacement_string "${FRONTEND_API_BASE_URL}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{FRONTEND_DEV_PORT}}|$(escape_sed_replacement_string "${FRONTEND_DEV_PORT}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{WEAVIATE_HTTP_PORT}}|$(escape_sed_replacement_string "${WEAVIATE_HTTP_PORT}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{WEAVIATE_GRPC_PORT}}|$(escape_sed_replacement_string "${WEAVIATE_GRPC_PORT}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{WEAVIATE_QUERY_LIMIT}}|$(escape_sed_replacement_string "${WEAVIATE_QUERY_LIMIT}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{WEAVIATE_ANONYMOUS_ACCESS}}|$(escape_sed_replacement_string "${WEAVIATE_ANONYMOUS_ACCESS}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{WEAVIATE_DATA_PATH}}|$(escape_sed_replacement_string "${WEAVIATE_DATA_PATH}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{WEAVIATE_VECTORIZER_MODULE}}|$(escape_sed_replacement_string "${WEAVIATE_VECTORIZER_MODULE}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{WEAVIATE_MODULES}}|$(escape_sed_replacement_string "${WEAVIATE_MODULES}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{WEAVIATE_CLUSTER_HOSTNAME}}|$(escape_sed_replacement_string "${WEAVIATE_CLUSTER_HOSTNAME}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{WEAVIATE_PROTOCOL}}|$(escape_sed_replacement_string "${WEAVIATE_PROTOCOL}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{WEAVIATE_CLASSNAME}}|$(escape_sed_replacement_string "${WEAVIATE_CLASSNAME}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{PROD_DB_URL}}|$(escape_sed_replacement_string "${PROD_DB_URL}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{PROD_DB_USERNAME}}|$(escape_sed_replacement_string "${PROD_DB_USERNAME}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{PROD_DB_PASSWORD}}|$(escape_sed_replacement_string "${PROD_DB_PASSWORD}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{PROD_REDIS_HOST}}|$(escape_sed_replacement_string "${PROD_REDIS_HOST}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{PROD_REDIS_PORT}}|$(escape_sed_replacement_string "${PROD_REDIS_PORT}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{PROD_REDIS_DATABASE}}|$(escape_sed_replacement_string "${PROD_REDIS_DATABASE}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{PROD_REDIS_PASSWORD}}|$(escape_sed_replacement_string "${PROD_REDIS_PASSWORD}")|g" ${DEPLOY_DIR}/.env +sed -i '' "s|{{PROD_REDIS_TIMEOUT}}|$(escape_sed_replacement_string "${PROD_REDIS_TIMEOUT}")|g" ${DEPLOY_DIR}/.env + +echo ".env file has been updated with your configuration." + +# Replace placeholders in docker-compose.yaml file +echo "Updating docker-compose.yaml file with your configuration..." + +# Determine Redis command arguments based on password +#if [ -n "${REDIS_PASSWORD}" ]; then +# REDIS_COMMAND_ARGS="--requirepass $(escape_sed_replacement_string "${REDIS_PASSWORD}")" +#else +# REDIS_COMMAND_ARGS="" +#fi + +sed -i '' "s|{{MYSQL_ROOT_PASSWORD}}|$(escape_sed_replacement_string "${MYSQL_ROOT_PASSWORD}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{MYSQL_DATABASE}}|$(escape_sed_replacement_string "${MYSQL_DATABASE}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{MYSQL_PORT}}|$(escape_sed_replacement_string "${MYSQL_PORT}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{REDIS_PORT}}|$(escape_sed_replacement_string "${REDIS_PORT}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{REDIS_COMMAND_ARGS}}|$(escape_sed_replacement_string "${REDIS_COMMAND_ARGS}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{WEAVIATE_HTTP_PORT}}|$(escape_sed_replacement_string "${WEAVIATE_HTTP_PORT}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{WEAVIATE_GRPC_PORT}}|$(escape_sed_replacement_string "${WEAVIATE_GRPC_PORT}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{WEAVIATE_QUERY_LIMIT}}|$(escape_sed_replacement_string "${WEAVIATE_QUERY_LIMIT}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{WEAVIATE_ANONYMOUS_ACCESS}}|$(escape_sed_replacement_string "${WEAVIATE_ANONYMOUS_ACCESS}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{WEAVIATE_DATA_PATH}}|$(escape_sed_replacement_string "${WEAVIATE_DATA_PATH}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{WEAVIATE_VECTORIZER_MODULE}}|$(escape_sed_replacement_string "${WEAVIATE_VECTORIZER_MODULE}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{WEAVIATE_MODULES}}|$(escape_sed_replacement_string "${WEAVIATE_MODULES}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{WEAVIATE_CLUSTER_HOSTNAME}}|$(escape_sed_replacement_string "${WEAVIATE_CLUSTER_HOSTNAME}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{SERVER_PORT}}|$(escape_sed_replacement_string "${SERVER_PORT}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{DB_URL}}|$(escape_sed_replacement_string "jdbc:mysql://mysql:3306/${MYSQL_DATABASE}?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{DB_USERNAME}}|$(escape_sed_replacement_string "${DB_USERNAME}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{DB_PASSWORD}}|$(escape_sed_replacement_string "${DB_PASSWORD}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{REDIS_HOST}}|redis|g" ${DEPLOY_DIR}/docker-compose.yaml # REDIS_HOST is hardcoded to 'redis' in docker-compose +sed -i '' "s|{{REDIS_DATABASE}}|$(escape_sed_replacement_string "${REDIS_DATABASE}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{REDIS_PASSWORD}}|$(escape_sed_replacement_string "${REDIS_PASSWORD}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{REDIS_TIMEOUT}}|$(escape_sed_replacement_string "${REDIS_TIMEOUT}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{TZ}}|$(escape_sed_replacement_string "${TZ}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{ADMIN_PORT}}|$(escape_sed_replacement_string "${ADMIN_PORT}")|g" ${DEPLOY_DIR}/docker-compose.yaml +sed -i '' "s|{{WEB_PORT}}|$(escape_sed_replacement_string "${WEB_PORT}")|g" ${DEPLOY_DIR}/docker-compose.yaml + +echo "" +echo "=== Build or Deploy Options ===" +read -p "Do you want to build new images (B) or deploy directly using existing images (D)?[B/d]: " build_or_deploy_choice +BUILD_CHOICE="${build_or_deploy_choice:-B}" # Default to Build + +if [[ "${BUILD_CHOICE}" == [Bb]* ]]; then + echo "Image build process in progress..." + + # Clone ruoyi-ai-backend repositories + if [ -d "${DEPLOY_DIR}/ruoyi-ai" ]; then + echo "Directory ${DEPLOY_DIR}/ruoyi-ai already exists." + read -p "Do you want to delete it and clone a new copy?[Y/n]: " answer + case ${answer:-Y} in + [Yy]* ) + echo "Deleting existing directory..." + rm -rf ${DEPLOY_DIR}/ruoyi-ai + echo "Cloning ruoyi-ai-backend repository..." + cd ${DEPLOY_DIR} && git clone https://github.com/ageerle/ruoyi-ai + + # Prompt for branch selection + read -p "Please enter the branch name for ruoyi-ai repository [main]: " RUOYI_AI_BRANCH + RUOYI_AI_BRANCH="${RUOYI_AI_BRANCH:-main}" + echo "Switching to branch: ${RUOYI_AI_BRANCH}" + cd ${DEPLOY_DIR}/ruoyi-ai && git checkout ${RUOYI_AI_BRANCH} + cd .. + ;; + [Nn]* ) + echo "Skipping clone operation." + ;; + * ) + echo "Invalid input. Skipping clone operation." + ;; + esac + else + echo "Cloning ruoyi-ai-backend repository..." + cd ${DEPLOY_DIR} && git clone https://github.com/ageerle/ruoyi-ai + + # Prompt for branch selection + read -p "Please enter the branch name for ruoyi-ai repository [main]: " RUOYI_AI_BRANCH + RUOYI_AI_BRANCH="${RUOYI_AI_BRANCH:-main}" + echo "Switching to branch: ${RUOYI_AI_BRANCH}" + cd ${DEPLOY_DIR}/ruoyi-ai && git checkout ${RUOYI_AI_BRANCH} + cd .. + fi + + # Clone ruoyi-ai-admin repositories + if [ -d "${DEPLOY_DIR}/ruoyi-admin" ]; then + echo "Directory ${DEPLOY_DIR}/ruoyi-admin already exists." + read -p "Do you want to delete it and clone a new copy?[Y/n]: " answer + case ${answer:-Y} in + [Yy]* ) + echo "Deleting existing directory..." + rm -rf ${DEPLOY_DIR}/ruoyi-admin + echo "Cloning ruoyi-admin repository..." + cd ${DEPLOY_DIR} && git clone https://github.com/ageerle/ruoyi-admin + + # Prompt for branch selection + read -p "Please enter the branch name for ruoyi-admin repository [main]: " RUOYI_ADMIN_BRANCH + RUOYI_ADMIN_BRANCH="${RUOYI_ADMIN_BRANCH:-main}" + echo "Switching to branch: ${RUOYI_ADMIN_BRANCH}" + cd ${DEPLOY_DIR}/ruoyi-admin && git checkout ${RUOYI_ADMIN_BRANCH} + cd .. + ;; + [Nn]* ) + echo "Skipping clone operation." + ;; + * ) + echo "Invalid input. Skipping clone operation." + ;; + esac + else + echo "Cloning ruoyi-ai-admin repository..." + cd ${DEPLOY_DIR} && git clone https://github.com/ageerle/ruoyi-admin + + # Prompt for branch selection + read -p "Please enter the branch name for ruoyi-admin repository [main]: " RUOYI_ADMIN_BRANCH + RUOYI_ADMIN_BRANCH="${RUOYI_ADMIN_BRANCH:-main}" + echo "Switching to branch: ${RUOYI_ADMIN_BRANCH}" + cd ${DEPLOY_DIR}/ruoyi-admin && git checkout ${RUOYI_ADMIN_BRANCH} + cd .. + fi + + # Clone ruoyi-ai-web repositories + if [ -d "${DEPLOY_DIR}/ruoyi-web" ]; then + echo "Directory ${DEPLOY_DIR}/ruoyi-web already exists." + read -p "Do you want to delete it and clone a new copy?[Y/n]: " answer + case ${answer:-Y} in + [Yy]* ) + echo "Deleting existing directory..." + rm -rf ${DEPLOY_DIR}/ruoyi-web + echo "Cloning ruoyi-ai-web repository..." + cd ${DEPLOY_DIR} && git clone https://github.com/ageerle/ruoyi-web + + # Prompt for branch selection + read -p "Please enter the branch name for ruoyi-web repository [main]: " RUOYI_WEB_BRANCH + RUOYI_WEB_BRANCH="${RUOYI_WEB_BRANCH:-main}" + echo "Switching to branch: ${RUOYI_WEB_BRANCH}" + cd ${DEPLOY_DIR}/ruoyi-web && git checkout ${RUOYI_WEB_BRANCH} + cd .. + ;; + [Nn]* ) + echo "Skipping clone operation." + ;; + * ) + echo "Invalid input. Skipping clone operation." + ;; + esac + else + echo "Cloning ruoyi-ai-web repository..." + cd ${DEPLOY_DIR} && git clone https://github.com/ageerle/ruoyi-web + + # Prompt for branch selection + read -p "Please enter the branch name for ruoyi-web repository [main]: " RUOYI_WEB_BRANCH + RUOYI_WEB_BRANCH="${RUOYI_WEB_BRANCH:-main}" + echo "Switching to branch: ${RUOYI_WEB_BRANCH}" + cd ${DEPLOY_DIR}/ruoyi-web && git checkout ${RUOYI_WEB_BRANCH} + cd .. + fi + + # Update application-prod.yml file + echo "Updating application-prod.yml file with your configuration..." + # Copy application-prod.yml template + cp ${SCRIPT_DIR}/template/application-prod.yml.template ${DEPLOY_DIR}/ruoyi-ai/ruoyi-admin/src/main/resources/application-prod.yml + + # Replace placeholders in application-prod.yml + sed -i '' "s|{{PROD_DB_URL}}|$(escape_sed_replacement_string "${PROD_DB_URL}")|g" ${DEPLOY_DIR}/ruoyi-ai/ruoyi-admin/src/main/resources/application-prod.yml + sed -i '' "s|{{PROD_DB_USERNAME}}|$(escape_sed_replacement_string "${PROD_DB_USERNAME}")|g" ${DEPLOY_DIR}/ruoyi-ai/ruoyi-admin/src/main/resources/application-prod.yml + sed -i '' "s|{{PROD_DB_PASSWORD}}|$(escape_sed_replacement_string "${PROD_DB_PASSWORD}")|g" ${DEPLOY_DIR}/ruoyi-ai/ruoyi-admin/src/main/resources/application-prod.yml + sed -i '' "s|{{PROD_REDIS_HOST}}|$(escape_sed_replacement_string "${PROD_REDIS_HOST}")|g" ${DEPLOY_DIR}/ruoyi-ai/ruoyi-admin/src/main/resources/application-prod.yml + sed -i '' "s|{{PROD_REDIS_PORT}}|$(escape_sed_replacement_string "${PROD_REDIS_PORT}")|g" ${DEPLOY_DIR}/ruoyi-ai/ruoyi-admin/src/main/resources/application-prod.yml + sed -i '' "s|{{PROD_REDIS_DATABASE}}|$(escape_sed_replacement_string "${PROD_REDIS_DATABASE}")|g" ${DEPLOY_DIR}/ruoyi-ai/ruoyi-admin/src/main/resources/application-prod.yml + if [ -z "${PROD_REDIS_PASSWORD}" ]; then + sed -i '' "s/^ password: {{PROD_REDIS_PASSWORD}}/# password: {{PROD_REDIS_PASSWORD}}/g" ${DEPLOY_DIR}/ruoyi-ai/ruoyi-admin/src/main/resources/application-prod.yml + else + sed -i '' "s|{{PROD_REDIS_PASSWORD}}|$(escape_sed_replacement_string "${PROD_REDIS_PASSWORD}")|g" ${DEPLOY_DIR}/ruoyi-ai/ruoyi-admin/src/main/resources/application-prod.yml + fi + sed -i '' "s|{{PROD_REDIS_TIMEOUT}}|$(escape_sed_replacement_string "${PROD_REDIS_TIMEOUT}")|g" ${DEPLOY_DIR}/ruoyi-ai/ruoyi-admin/src/main/resources/application-prod.yml + + # Update vite.config.mts file + echo "Updating vite.config.mts file with your configuration..." + sed -i '' "s|http://127.0.0.1:6039|${FRONTEND_API_BASE_URL}|g" ${DEPLOY_DIR}/ruoyi-admin/apps/web-antd/vite.config.mts + + # Update image tags in docker-compose.yaml file + echo "Updating image tags in docker-compose.yaml file..." + sed -i '' "s|ruoyi-ai-backend:latest|ruoyi-ai-backend:${RUOYI_AI_BRANCH}|g" ${DEPLOY_DIR}/docker-compose.yaml + sed -i '' "s|ruoyi-ai-admin:latest|ruoyi-ai-admin:${RUOYI_ADMIN_BRANCH}|g" ${DEPLOY_DIR}/docker-compose.yaml + sed -i '' "s|ruoyi-ai-web:latest|ruoyi-ai-web:${RUOYI_WEB_BRANCH}|g" ${DEPLOY_DIR}/docker-compose.yaml + + # Create Nginx configuration files for frontend services + echo "Copying Admin UI Nginx configuration template to temporary location..." + cp ${SCRIPT_DIR}/template/nginx.admin.conf.template ${DEPLOY_DIR}/nginx.admin.conf.tmp + + echo "Updating Admin UI Nginx configuration in temporary file..." + sed -i '' "s|{{BACKEND_HOST}}|$(escape_sed_replacement_string "${BACKEND_HOST}")|g" ${DEPLOY_DIR}/nginx.admin.conf.tmp + sed -i '' "s|{{SERVER_PORT}}|$(escape_sed_replacement_string "${SERVER_PORT}")|g" ${DEPLOY_DIR}/nginx.admin.conf.tmp + + echo "Moving updated Admin UI Nginx configuration to final location..." + mv ${DEPLOY_DIR}/nginx.admin.conf.tmp ${DEPLOY_DIR}/ruoyi-admin/nginx.conf + + echo "Copying Web UI Nginx configuration template to temporary location..." + cp ${SCRIPT_DIR}/template/nginx.web.conf.template ${DEPLOY_DIR}/nginx.web.conf.tmp + + echo "Updating Web UI Nginx configuration in temporary file..." + sed -i '' "s|{{BACKEND_HOST}}|$(escape_sed_replacement_string "${BACKEND_HOST}")|g" ${DEPLOY_DIR}/nginx.web.conf.tmp + sed -i '' "s|{{SERVER_PORT}}|$(escape_sed_replacement_string "${SERVER_PORT}")|g" ${DEPLOY_DIR}/nginx.web.conf.tmp + + echo "Moving updated Web UI Nginx configuration to final location..." + mv ${DEPLOY_DIR}/nginx.web.conf.tmp ${DEPLOY_DIR}/ruoyi-web/nginx.conf + + # Create Dockerfiles for frontend services + echo "Creating Dockerfile for Admin UI..." + cat > ${DEPLOY_DIR}/ruoyi-admin/Dockerfile << EOF +FROM nginx:1.25-alpine + +COPY dist/ /usr/share/nginx/html/ +COPY nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] +EOF + + echo "Creating Dockerfile for Web UI..." + cat > ${DEPLOY_DIR}/ruoyi-web/Dockerfile << EOF +FROM nginx:1.25-alpine + +COPY dist/ /usr/share/nginx/html/ +COPY nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] +EOF + + # Build backend service + echo "Building Ruoyi-AI backend service..." + cd ${DEPLOY_DIR}/ruoyi-ai + docker run -it --rm --name build-ruoyi-ai-backend -v ${DEPLOY_DIR}/ruoyi-ai:/code --entrypoint=/bin/bash maven:3.9.9-eclipse-temurin-17-alpine -c "cd /code && mvn clean package -P prod" + + # Build frontend Admin service + echo "Building Ruoyi-AI frontend Admin service..." + cd ${DEPLOY_DIR}/ruoyi-admin + docker run -it --rm --name build-ruoyi-ai-admin -v ${DEPLOY_DIR}/ruoyi-admin:/app -w /app node:20 sh -c "npm install -g pnpm && pnpm install && pnpm build" + + # Build frontend Web service + echo "Building Ruoyi-AI frontend Web service..." + cd ${DEPLOY_DIR}/ruoyi-web + docker run -it --rm --name build-ruoyi-ai-web -v ${DEPLOY_DIR}/ruoyi-web:/app -w /app node:20 sh -c "npm install -g pnpm && pnpm install && pnpm build" + + # Build Docker images + echo "Building Ruoyi-AI Backend Docker images..." + cd ${DEPLOY_DIR}/ruoyi-ai + rm -rf temp + mkdir temp + cp ./ruoyi-admin/target/ruoyi-admin.jar temp/ + cd temp/ + cat > Dockerfile << EOF +FROM openjdk:17-jdk-slim +WORKDIR /app +COPY ruoyi-admin.jar /app/ruoyi-admin.jar +EXPOSE ${SERVER_PORT} +ENTRYPOINT ["java","-jar","ruoyi-admin.jar","--spring.profiles.active=prod"] +EOF + docker build -t ruoyi-ai-backend:${RUOYI_AI_BRANCH} . + cd .. + + echo "Building Ruoyi-AI Admin Docker images..." + cd ${DEPLOY_DIR}/ruoyi-admin + rm -rf temp + mkdir temp + cp ./apps/web-antd/dist.zip temp/ + cp Dockerfile temp/ + cp nginx.conf temp/ + cd temp/ + unzip dist.zip -d dist + rm -f dist.zip + docker build -t ruoyi-ai-admin:${RUOYI_ADMIN_BRANCH} . + cd .. + + echo "Building Ruoyi-AI Web Docker images..." + cd ${DEPLOY_DIR}/ruoyi-web + rm -rf temp + mkdir temp + cp -pr ${DEPLOY_DIR}/ruoyi-web/dist temp/ + cp Dockerfile temp/ + cp nginx.conf temp/ + cd temp/ + docker build -t ruoyi-ai-web:${RUOYI_WEB_BRANCH} . + cd .. +else + echo "Skipping image build process. Deploying directly using existing images..." + + # Prompt for branch names to use as image tags + read -p "Please enter the tag for ruoyi-ai-backend image [main]: " RUOYI_AI_BRANCH + RUOYI_AI_BRANCH="${RUOYI_AI_BRANCH:-main}" + + read -p "Please enter the tag for ruoyi-ai-admin image [main]: " RUOYI_ADMIN_BRANCH + RUOYI_ADMIN_BRANCH="${RUOYI_ADMIN_BRANCH:-main}" + + read -p "Please enter the tag for ruoyi-ai-web image [main]: " RUOYI_WEB_BRANCH + RUOYI_WEB_BRANCH="${RUOYI_WEB_BRANCH:-main}" + + # Update image tags in docker-compose.yaml file + echo "Updating image tags in docker-compose.yaml file..." + sed -i '' "s|ruoyi-ai-backend:latest|ruoyi-ai-backend:${RUOYI_AI_BRANCH}|g" ${DEPLOY_DIR}/docker-compose.yaml + sed -i '' "s|ruoyi-ai-admin:latest|ruoyi-ai-admin:${RUOYI_ADMIN_BRANCH}|g" ${DEPLOY_DIR}/docker-compose.yaml + sed -i '' "s|ruoyi-ai-web:latest|ruoyi-ai-web:${RUOYI_WEB_BRANCH}|g" ${DEPLOY_DIR}/docker-compose.yaml +fi + +# Copy SQL file +rm -rf ${DEPLOY_DIR}/mysql-init +cp -pr ${SCRIPT_DIR}/mysql-init ${DEPLOY_DIR}/ + +# Update SQL file with configuration values +echo "Updating SQL configuration values..." +sed -i '' "s|'weaviate', 'host', '127.0.0.1:6038'|'weaviate', 'host', 'weaviate:8080'|g" ${DEPLOY_DIR}/mysql-init/ruoyi-ai.sql +sed -i '' "s|'weaviate', 'protocol', 'http'|'weaviate', 'protocol', '${WEAVIATE_PROTOCOL}'|g" ${DEPLOY_DIR}/mysql-init/ruoyi-ai.sql +sed -i '' "s|'weaviate', 'classname', 'LocalKnowledge'|'weaviate', 'classname', '${WEAVIATE_CLASSNAME}'|g" ${DEPLOY_DIR}/mysql-init/ruoyi-ai.sql + +# Deploy using Docker Compose +echo "Deploying with Docker Compose..." +cd ${DEPLOY_DIR} +docker-compose down +docker-compose up -d + +echo "==================================================" +echo " RuoYi-AI Deployment Complete" +echo "==================================================" +echo "" +echo "Your RuoYi-AI system has deployed the following services:" +echo "- Backend API: http://localhost:${SERVER_PORT}" +echo "- Admin UI: http://localhost:${ADMIN_PORT}" +echo "- Web UI: http://localhost:${WEB_PORT}" +echo "- Weaviate: http://localhost:${WEAVIATE_HTTP_PORT}" +echo "" +echo "All configurations have been customized according to your inputs." +echo "Configuration files have been updated to use environment variables." +echo "" +echo "Thank you for using the RuoYi-AI interactive deployment script!" diff --git a/script/sql/ruoyi-ai.sql b/script/sql/ruoyi-ai.sql index f4356fd2..a66031d2 100644 --- a/script/sql/ruoyi-ai.sql +++ b/script/sql/ruoyi-ai.sql @@ -492,6 +492,7 @@ INSERT INTO `sys_dict_data` VALUES (1776109770934677506, '000000', 0, 'token计 INSERT INTO `sys_dict_data` VALUES (1776109853377916929, '000000', 0, '次数计费', '2', 'sys_model_billing', '', 'success', 'N', '0', 103, 1, '2024-04-05 12:49:22', 1, '2024-04-05 12:49:22', ''); INSERT INTO `sys_dict_data` VALUES (1780264338471858177, '000000', 0, '未支付', '1', 'pay_state', '', 'info', 'N', '0', 103, 1, '2024-04-16 23:57:49', 1, '2024-04-16 23:58:29', ''); INSERT INTO `sys_dict_data` VALUES (1780264431589601282, '000000', 2, '已支付', '2', 'pay_state', '', 'success', 'N', '0', 103, 1, '2024-04-16 23:58:11', 1, '2024-04-16 23:58:21', ''); +INSERT INTO `sys_dict_data` VALUES (1933094189606670338, '000000', 0, '知识库', 'vector', 'prompt_template_type', null, '', 'N', '0', 103, 1, '2025-06-12 17:29:05', 1, '2025-06-12 17:29:05', null); -- ---------------------------- -- Table structure for sys_dict_type @@ -536,6 +537,8 @@ INSERT INTO `sys_dict_type` VALUES (1775756736895438849, '000000', '用户等级 INSERT INTO `sys_dict_type` VALUES (1776109665045278721, '000000', '模型计费方式', 'sys_model_billing', '0', 103, 1, '2024-04-05 12:48:37', 1, '2024-04-08 11:22:18', '模型计费方式'); INSERT INTO `sys_dict_type` VALUES (1780263881368219649, '000000', '支付状态', 'pay_state', '0', 103, 1, '2024-04-16 23:56:00', 1, '2025-03-29 15:21:57', '支付状态'); INSERT INTO `sys_dict_type` VALUES (1904565568803217409, '000000', '状态类型', 'status_type', '0', 103, 1, '2025-03-26 00:06:31', 1, '2025-03-26 00:06:31', NULL); +INSERT INTO `sys_dict_type` VALUES (1933093946274123777, '000000', '提示词模板分类', 'prompt_template_type', '0', 103, 1, '2025-06-12 17:28:07', 1, '2025-06-12 17:28:07', null); + -- ---------------------------- -- Table structure for sys_file_info @@ -713,6 +716,12 @@ INSERT INTO `sys_menu` VALUES (1906674838461321219, '配置信息新增', 190667 INSERT INTO `sys_menu` VALUES (1906674838461321220, '配置信息修改', 1906674838461321217, 3, '#', '', NULL, 1, 0, 'F', '0', '0', 'system:config:edit', '#', 103, 1, '2025-03-31 19:48:48', NULL, NULL, ''); INSERT INTO `sys_menu` VALUES (1906674838461321221, '配置信息删除', 1906674838461321217, 4, '#', '', NULL, 1, 0, 'F', '0', '0', 'system:config:remove', '#', 103, 1, '2025-03-31 19:48:48', NULL, NULL, ''); INSERT INTO `sys_menu` VALUES (1906674838461321222, '配置信息导出', 1906674838461321217, 5, '#', '', NULL, 1, 0, 'F', '0', '0', 'system:config:export', '#', 103, 1, '2025-03-31 19:48:48', NULL, NULL, ''); +INSERT INTO `sys_menu` VALUES (1929170702299045890, '提示词模板', 1775500307898949634, '1', 'promptTemplate', 'system/promptTemplate/index', '', 1, 0, 'C', '0', '0', 'system:promptTemplate:list', 'fluent:prompt-16-filled', 103, 1, sysdate(), null, null, '提示词模板菜单'); +INSERT INTO `sys_menu` VALUES (1929170702299045891, '提示词模板查询', 1929170702299045890, '1', '#', '', NULL, 1, 0, 'F', '0', '0', 'system:promptTemplate:query', '#', 103, 1, sysdate(), null, null, ''); +INSERT INTO `sys_menu` VALUES (1929170702299045892, '提示词模板新增', 1929170702299045890, '2', '#', '', NULL, 1, 0, 'F', '0', '0', 'system:promptTemplate:add', '#', 103, 1, sysdate(), null, null, ''); +INSERT INTO `sys_menu` VALUES (1929170702299045893, '提示词模板修改', 1929170702299045890, '3', '#', '', NULL, 1, 0, 'F', '0', '0', 'system:promptTemplate:edit', '#', 103, 1, sysdate(), null, null, ''); +INSERT INTO `sys_menu` VALUES (1929170702299045894, '提示词模板删除', 1929170702299045890, '4', '#', '', NULL, 1, 0, 'F', '0', '0', 'system:promptTemplate:remove', '#', 103, 1, sysdate(), null, null, ''); +INSERT INTO `sys_menu` VALUES (1929170702299045895, '提示词模板导出', 1929170702299045890, '5', '#', '', NULL, 1, 0, 'F', '0', '0', 'system:promptTemplate:export', '#', 103, 1, sysdate(), null, null, ''); -- ---------------------------- -- Table structure for sys_notice @@ -2510,4 +2519,24 @@ INSERT INTO `sys_user_role` VALUES (1871910972567822337, 1); INSERT INTO `sys_user_role` VALUES (1897620177094057985, 1); INSERT INTO `sys_user_role` VALUES (1925795787894333441, 1729685491108446210); +# 提示词模板表 +DROP TABLE IF EXISTS `prompt_template`; +CREATE TABLE prompt_template +( + id bigint auto_increment comment '主键' + primary key, + template_name varchar(128) null comment '提示词模板名称', + template_content text null comment '提示词模板内容', + category varchar(16) NULL COMMENT '提示词分类,knowledge 知识库类型,chat 对话类型,draw绘画类型 ...', + create_dept bigint null comment '创建部门', + create_by bigint null comment '创建者', + create_time datetime null comment '创建时间', + update_by bigint null comment '更新者', + update_time datetime null comment '更新时间', + remark varchar(256) null comment '备注' +) ENGINE = InnoDB + CHARACTER SET = utf8mb4 + COLLATE = utf8mb4_general_ci COMMENT = '提示词模板表' + ROW_FORMAT = Dynamic; + SET FOREIGN_KEY_CHECKS = 1;