mirror of
https://github.com/dataease/SQLBot.git
synced 2026-01-24 10:33:10 +08:00
feat: add installer files & update docker-compose.yaml
This commit is contained in:
26
.env.example
26
.env.example
@@ -1,26 +0,0 @@
|
||||
PROJECT_NAME="SQLBot"
|
||||
# Backend
|
||||
BACKEND_CORS_ORIGINS="http://localhost,http://localhost:5173,https://localhost,https://localhost:5173"
|
||||
SECRET_KEY=y5txe1mRmS_JpOrUzFzHEu-kIQn3lf7ll0AOv9DQh0s
|
||||
|
||||
TOKEN_KEY="X-SQLBOT-TOKEN"
|
||||
DEFAULT_PWD="SQLBot@123456"
|
||||
ASSISTANT_TOKEN_KEY="X-SQLBOT-ASSISTANT-TOKEN"
|
||||
|
||||
LOG_LEVEL="INFO" # DEBUG, INFO, WARNING, ERROR
|
||||
LOG_DIR="/opt/sqlbot/logs"
|
||||
LOG_FORMAT="%(asctime)s - %(name)s - %(levelname)s:%(lineno)d - %(message)s"
|
||||
SQL_DEBUG=False
|
||||
|
||||
CACHE_TYPE="memory"
|
||||
|
||||
# Postgres
|
||||
POSTGRES_SERVER=localhost
|
||||
POSTGRES_PORT=5432
|
||||
POSTGRES_DB=sqlbot
|
||||
POSTGRES_USER=root
|
||||
POSTGRES_PASSWORD=132456 # Change this to your pwd
|
||||
|
||||
MCP_IMAGE_PATH=/opt/sqlbot/images
|
||||
MCP_IMAGE_HOST=http://localhost:3000
|
||||
SERVER_IMAGE_HOST=https://sqlbot.fit2cloud.cn/images/
|
||||
@@ -19,19 +19,13 @@ services:
|
||||
PROJECT_NAME: "SQLBot"
|
||||
DEFAULT_PWD: "SQLBot@123456"
|
||||
# MCP settings
|
||||
MCP_IMAGE_PATH: /opt/sqlbot/images
|
||||
MCP_IMAGE_HOST: http://localhost:3000
|
||||
SERVER_IMAGE_HOST: https://sqlbot.fit2cloud.cn/images/
|
||||
SERVER_IMAGE_HOST: https://YOUR_SERVE_IP:MCP_PORT/images/
|
||||
# Auth & Security
|
||||
TOKEN_KEY: "X-SQLBOT-TOKEN"
|
||||
ASSISTANT_TOKEN_KEY: "X-SQLBOT-ASSISTANT-TOKEN"
|
||||
SECRET_KEY: y5txe1mRmS_JpOrUzFzHEu-kIQn3lf7ll0AOv9DQh0s
|
||||
# CORS settings
|
||||
BACKEND_CORS_ORIGINS: "http://localhost,http://localhost:5173,https://localhost,https://localhost:5173"
|
||||
# Logging
|
||||
LOG_LEVEL: "INFO"
|
||||
LOG_DIR: "/opt/sqlbot/logs"
|
||||
LOG_FORMAT: "%(asctime)s - %(name)s - %(levelname)s:%(lineno)d - %(message)s"
|
||||
SQL_DEBUG: False
|
||||
volumes:
|
||||
- ./data/sqlbot/excel:/opt/sqlbot/data/excel
|
||||
|
||||
16
installer/docker-compose-pg.yml
Normal file
16
installer/docker-compose-pg.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
services:
|
||||
sqlbot-db:
|
||||
image: registry.cn-qingdao.aliyuncs.com/dataease/postgres:17.5
|
||||
container_name: sqlbot-db
|
||||
restart: always
|
||||
networks:
|
||||
- sqlbot-network
|
||||
volumes:
|
||||
- ./data/postgresql:/var/lib/postgresql/data
|
||||
env_file:
|
||||
- conf/sqlbot.conf
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready"]
|
||||
interval: 3s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
22
installer/docker-compose.yaml
Normal file
22
installer/docker-compose.yaml
Normal file
@@ -0,0 +1,22 @@
|
||||
services:
|
||||
sqlbot:
|
||||
image: registry.cn-qingdao.aliyuncs.com/dataease/sqlbot:v0.9.5
|
||||
container_name: sqlbot
|
||||
restart: always
|
||||
networks:
|
||||
- sqlbot-network
|
||||
ports:
|
||||
- ${SQLBOT_WEB_PORT}:8000
|
||||
- ${SQLBOT_MCP_PORT}:8001
|
||||
env_file:
|
||||
- conf/sqlbot.conf
|
||||
volumes:
|
||||
- ./data/sqlbot/excel:/opt/sqlbot/data/excel
|
||||
- ./data/sqlbot/images:/opt/sqlbot/images
|
||||
- ./data/sqlbot/logs:/opt/sqlbot/logs
|
||||
depends_on:
|
||||
sqlbot-db:
|
||||
condition: service_healthy
|
||||
|
||||
networks:
|
||||
sqlbot-network:
|
||||
34
installer/install.conf
Normal file
34
installer/install.conf
Normal file
@@ -0,0 +1,34 @@
|
||||
# 基础配置
|
||||
## 安装目录
|
||||
SQLBOT_BASE=/opt
|
||||
## SQLBot 端口
|
||||
SQLBOT_WEB_PORT=8000
|
||||
SQLBOT_MCP_PORT=8001
|
||||
|
||||
# 数据库配置
|
||||
## 是否使用外部数据库
|
||||
SQLBOT_EXTERNAL_DB=false
|
||||
## 数据库地址
|
||||
SQLBOT_DB_HOST=sqlbot-db
|
||||
## 数据库端口 (仅使用外部数据库时才生效)
|
||||
SQLBOT_DB_PORT=5432
|
||||
## SQLBot 数据库库名
|
||||
SQLBOT_DB_DB=sqlbot
|
||||
## 数据库用户名
|
||||
SQLBOT_DB_USER=root
|
||||
## 数据库密码,密码如包含特殊字符,请用双引号引起来,例如 SQLBOT_DB_PASSWORD="Test@4&^%*^"
|
||||
SQLBOT_DB_PASSWORD=Password123@pg
|
||||
|
||||
# 其他配置
|
||||
## 普通用户默认密码
|
||||
SQLBOT_DEFAULT_PWD=SQLBot@123456
|
||||
## SQLBot Secret Key
|
||||
SQLBOT_SECRET_KEY=y5txe1mRmS_JpOrUzFzHEu-kIQn3lf7ll0AOv9DQh0s
|
||||
## Cross-Origin Resource Sharing (CORS) 设置
|
||||
SQLBOT_CORS_ORIGINS=http://localhost,http://localhost:5173,https://localhost,https://localhost:5173
|
||||
## 日志级别 DEBUG, INFO, WARNING, ERROR
|
||||
SQLBOT_LOG_LEVEL="INFO"
|
||||
## 缓存类型
|
||||
SQLBOT_CACHE_TYPE="memory"
|
||||
## MCP 图片存储路径
|
||||
SQLBOT_SERVER_IMAGE_HOST=https://YOUR_SERVER_IP:MCP_PORT/images/
|
||||
284
installer/install.sh
Normal file
284
installer/install.sh
Normal file
@@ -0,0 +1,284 @@
|
||||
#!/bin/bash
|
||||
|
||||
INSTALL_TYPE='install'
|
||||
title_count=1
|
||||
|
||||
CURRENT_DIR=$(
|
||||
cd "$(dirname "$0")"
|
||||
pwd
|
||||
)
|
||||
|
||||
function log() {
|
||||
echo -e "${1}" 2>&1 | tee -a ${CURRENT_DIR}/install.log
|
||||
}
|
||||
|
||||
function log_title () {
|
||||
log "${title_count}. ${1}"
|
||||
let title_count++
|
||||
}
|
||||
|
||||
function log_content () {
|
||||
log "\t${1}"
|
||||
}
|
||||
|
||||
function check_and_prepare_env_params() {
|
||||
log "当前时间 : $(date)"
|
||||
log_title "检查安装环境并初始化环境变量"
|
||||
|
||||
cd ${CURRENT_DIR}
|
||||
if [ -f /usr/bin/sctl ]; then
|
||||
echo "当前版本: $(sctl version | head -n 1)"
|
||||
|
||||
# 获取已安装的 SQLBOT 的运行目录
|
||||
SQLBOT_BASE=$(grep "^SQLBOT_BASE=" /usr/bin/sctl | cut -d'=' -f2)
|
||||
SQLBOT_BASE_OLD=${SQLBOT_BASE}
|
||||
sed -i -e "s#SQLBOT_BASE=.*#SQLBOT_BASE_BASE=${SQLBOT_BASE}#g" sctl
|
||||
\cp sctl /usr/local/bin && chmod +x /usr/local/bin/sctl
|
||||
|
||||
log_content "停止 SQLBot 服务"
|
||||
sctl stop
|
||||
|
||||
INSTALL_TYPE='upgrade'
|
||||
fi
|
||||
|
||||
set -a
|
||||
source ${CURRENT_DIR}/install.conf
|
||||
if [[ ${SQLBOT_BASE_OLD} ]];then
|
||||
SQLBOT_BASE=${SQLBOT_BASE_OLD}
|
||||
export SQLBOT_BASE=${SQLBOT_BASE_OLD}
|
||||
fi
|
||||
if [[ -d ${SQLBOT_BASE} ]] && [[ -f ${SQLBOT_BASE}/sqlbot/.env ]]; then
|
||||
source $SQLBOT_BASE/sqlbot/.env
|
||||
INSTALL_TYPE='upgrade'
|
||||
log_content "升级安装"
|
||||
else
|
||||
INSTALL_TYPE='install'
|
||||
mkdir -p ${SQLBOT_BASE}
|
||||
log_content "全新安装"
|
||||
fi
|
||||
set +a
|
||||
}
|
||||
|
||||
function set_run_base_path() {
|
||||
log_title "设置运行目录"
|
||||
SQLBOT_RUN_BASE=$SQLBOT_BASE/sqlbot
|
||||
CONF_FOLDER=${SQLBOT_RUN_BASE}/conf
|
||||
TEMPLATES_FOLDER=${SQLBOT_RUN_BASE}/templates
|
||||
log_content "运行目录 $SQLBOT_RUN_BASE"
|
||||
log_content "配置文件目录 $CONF_FOLDER"
|
||||
}
|
||||
|
||||
function prepare_sqlbot_run_base() {
|
||||
log_title "初始化运行目录"
|
||||
cd ${CURRENT_DIR}
|
||||
mkdir -p ${SQLBOT_RUN_BASE}
|
||||
log_content "复制安装文件到运行目录"
|
||||
cp -r ./sqlbot/* ${SQLBOT_RUN_BASE}/
|
||||
|
||||
cd ${SQLBOT_RUN_BASE}
|
||||
env | grep SQLBOT_ >.env
|
||||
|
||||
mkdir -p ${SQLBOT_RUN_BASE}/conf
|
||||
mkdir -p ${SQLBOT_RUN_BASE}/data/sqlbot/{excel,images,logs}
|
||||
|
||||
if [ "${SQLBOT_EXTERNAL_DB}" = "false" ]; then
|
||||
mkdir -p ${SQLBOT_RUN_BASE}/data/postgresql
|
||||
export SQLBOT_DB_PORT=5432
|
||||
else
|
||||
sed -i -e "/^ depends_on/,+2d" docker-compose.yml
|
||||
fi
|
||||
|
||||
log_content "调整配置文件参数"
|
||||
cd ${SQLBOT_RUN_BASE}
|
||||
cp -r ${TEMPLATES_FOLDER}/* ${CONF_FOLDER}
|
||||
|
||||
cd ${TEMPLATES_FOLDER}
|
||||
templates_files=( sqlbot.conf )
|
||||
for i in ${templates_files[@]}; do
|
||||
if [ -f $i ]; then
|
||||
envsubst < $i > ${CONF_FOLDER}/$i
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
function update_sctl() {
|
||||
log_title "安装 sctl 命令行工具"
|
||||
log_content "安装至 /usr/local/bin/sctl & /usr/bin/sctl"
|
||||
cd ${CURRENT_DIR}
|
||||
sed -i -e "s#SQLBOT_BASE=.*#SQLBOT_BASE=${SQLBOT_BASE}#g" sctl
|
||||
\cp sctl /usr/local/bin && chmod +x /usr/local/bin/sctl
|
||||
if [ ! -f /usr/bin/sctl ]; then
|
||||
ln -s /usr/local/bin/sctl /usr/bin/sctl 2>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
function prepare_system_settings() {
|
||||
log_title "修改操作系统相关设置"
|
||||
if which getenforce >/dev/null 2>&1 && [ $(getenforce) == "Enforcing" ];then
|
||||
log_content "关闭 SELINUX"
|
||||
setenforce 0
|
||||
sed -i "s/SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config
|
||||
fi
|
||||
|
||||
if which firewall-cmd >/dev/null 2>&1; then
|
||||
if systemctl is-active firewalld &>/dev/null ;then
|
||||
log_content "开启防火墙端口 ${SQLBOT_WEB_PORT}"
|
||||
firewall-cmd --zone=public --add-port=${SQLBOT_WEB_PORT}/tcp --permanent
|
||||
log_content "开启防火墙端口 ${SQLBOT_MCP_PORT}"
|
||||
firewall-cmd --zone=public --add-port=${SQLBOT_MCP_PORT}/tcp --permanent
|
||||
firewall-cmd --reload
|
||||
else
|
||||
log_content "防火墙未开启,忽略端口开放"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function install_docker() {
|
||||
log_title "安装 docker"
|
||||
#Install docker
|
||||
##Install Latest Stable Docker Release
|
||||
cd ${CURRENT_DIR}
|
||||
|
||||
if which docker >/dev/null 2>&1; then
|
||||
log_content "检测到 Docker 已安装,跳过安装步骤"
|
||||
log_content "启动 Docker "
|
||||
service docker start >/dev/null 2>&1 | tee -a ${CURRENT_DIR}/install.log
|
||||
else
|
||||
if [[ -d docker ]]; then
|
||||
log_content "离线安装 docker"
|
||||
cp docker/bin/* /usr/bin/
|
||||
cp docker/service/docker.service /etc/systemd/system/
|
||||
chmod +x /usr/bin/docker*
|
||||
chmod 644 /etc/systemd/system/docker.service
|
||||
else
|
||||
log_content "在线安装 docker"
|
||||
curl -fsSL https://resource.fit2cloud.com/get-docker-linux.sh -o get-docker.sh 2>&1 | tee -a ${CURRENT_DIR}/install.log
|
||||
if [[ ! -f get-docker.sh ]];then
|
||||
log_content "docker 在线安装脚本下载失败,请稍候重试"
|
||||
exit 1
|
||||
fi
|
||||
sudo sh get-docker.sh 2>&1 | tee -a ${CURRENT_DIR}/install.log
|
||||
fi
|
||||
|
||||
docker_config_folder="/etc/docker"
|
||||
if [ ! -d "$docker_config_folder" ];then
|
||||
mkdir -p "$docker_config_folder"
|
||||
cat <<EOF> $docker_config_folder/daemon.json
|
||||
{
|
||||
"log-driver": "json-file",
|
||||
"log-opts": {
|
||||
"max-file": "3",
|
||||
"max-size": "10m"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
|
||||
log_content "启动 docker"
|
||||
systemctl enable docker >/dev/null 2>&1; systemctl daemon-reload; systemctl start docker 2>&1 | tee -a ${CURRENT_DIR}/install.log
|
||||
|
||||
docker version >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
log_content "docker 安装失败"
|
||||
exit 1
|
||||
else
|
||||
log_content "docker 安装成功"
|
||||
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function install_docker_compose() {
|
||||
log_title "安装 docker-compose"
|
||||
#Install docker-compose
|
||||
cd ${CURRENT_DIR}
|
||||
##Install Latest Stable Docker Compose Release
|
||||
docker-compose version >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
docker compose version >/dev/null 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
echo 'docker compose "$@"' > /usr/bin/docker-compose
|
||||
chmod +x /usr/bin/docker-compose
|
||||
else
|
||||
if [[ -d docker ]]; then
|
||||
log_content "离线安装 docker-compose"
|
||||
cp docker/bin/docker-compose /usr/bin/
|
||||
chmod +x /usr/bin/docker-compose
|
||||
else
|
||||
log_content "在线安装 docker-compose"
|
||||
curl -L https://resource.fit2cloud.com/docker/compose/releases/download/v2.16.0/docker-compose-$(uname -s | tr A-Z a-z)-$(uname -m) -o /usr/local/bin/docker-compose 2>&1 | tee -a ${CURRENT_DIR}/install.log
|
||||
if [[ ! -f /usr/local/bin/docker-compose ]];then
|
||||
log_content "docker-compose 下载失败,请稍候重试"
|
||||
exit 1
|
||||
fi
|
||||
chmod +x /usr/local/bin/docker-compose
|
||||
ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
|
||||
fi
|
||||
fi
|
||||
|
||||
docker-compose version >/dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
log_content "docker-compose 安装失败"
|
||||
exit 1
|
||||
else
|
||||
log_content "docker-compose 安装成功"
|
||||
fi
|
||||
else
|
||||
log_content "检测到 Docker Compose 已安装,跳过安装步骤"
|
||||
fi
|
||||
export COMPOSE_HTTP_TIMEOUT=180
|
||||
}
|
||||
|
||||
function load_images() {
|
||||
log_title "加载 SQLBOT 镜像"
|
||||
cd ${CURRENT_DIR}
|
||||
|
||||
for i in $(docker images --format '{{.Repository}}:{{.Tag}}' | grep dataease); do
|
||||
current_images[${#current_images[@]}]=${i##*/}
|
||||
done
|
||||
|
||||
# 加载镜像
|
||||
if [[ -d images ]]; then
|
||||
for i in $(ls images); do
|
||||
if [[ "${current_images[@]}" =~ "${i%.tar.gz}" ]]; then
|
||||
log_content "已存在镜像 ${i%.tar.gz}"
|
||||
else
|
||||
log_content "加载镜像 ${i%.tar.gz}"
|
||||
docker load -i images/$i >/dev/null 2>&1 | tee -a ${CURRENT_DIR}/install.log
|
||||
fi
|
||||
done
|
||||
else
|
||||
SQLBOTVERSION=$(cat ${CURRENT_DIR}/sqlbot/templates/version)
|
||||
curl -sfL https://resource.fit2cloud.com/installation-log.sh | sh -s sqlbot ${INSTALL_TYPE} ${SQLBOTVERSION}
|
||||
fi
|
||||
}
|
||||
|
||||
function start_sqlbot() {
|
||||
log_title "启动 SQLBOT 服务"
|
||||
sctl reload 2>&1 | tee -a ${CURRENT_DIR}/install.log
|
||||
if [[ $? -ne 0 ]]; then
|
||||
log_content "SQLBOT 服务启动失败,请检查日志"
|
||||
exit 1
|
||||
fi
|
||||
echo
|
||||
if [[ $INSTALL_TYPE != "upgrade" ]];then
|
||||
echo -e "======================= 安装完成 =======================\n" 2>&1 | tee -a ${CURRENT_DIR}/install.log
|
||||
echo -e "系统登录信息如下:\n\t访问地址: http://服务器IP:$SQLBOT_WEB_PORT\n\t用户名: admin\n\t初始密码: SQLBot@123456" 2>&1 | tee -a ${CURRENT_DIR}/install.log
|
||||
else
|
||||
echo -e "======================= 升级完成 =======================\n" 2>&1 | tee -a ${CURRENT_DIR}/install.log
|
||||
fi
|
||||
}
|
||||
|
||||
function main() {
|
||||
check_and_prepare_env_params
|
||||
set_run_base_path
|
||||
prepare_sqlbot_run_base
|
||||
update_sctl
|
||||
prepare_system_settings
|
||||
install_docker
|
||||
install_docker_compose
|
||||
load_images
|
||||
start_sqlbot
|
||||
}
|
||||
|
||||
main
|
||||
210
installer/sctl
Normal file
210
installer/sctl
Normal file
@@ -0,0 +1,210 @@
|
||||
#!/bin/bash
|
||||
action=$1
|
||||
target=$2
|
||||
|
||||
SQLBOT_BASE=/opt
|
||||
SQLBOT_RUNNING_BASE=${SQLBOT_BASE}/sqlbot
|
||||
SQLBOT_LOG_PATH=${SQLBOT_RUNNING_BASE}/data/sqlbot/logs
|
||||
compose_files="-f docker-compose.yml"
|
||||
compose_cmd="docker-compose"
|
||||
current_version=""
|
||||
|
||||
set -a
|
||||
source ${SQLBOT_RUNNING_BASE}/.env
|
||||
set +a
|
||||
export COMPOSE_HTTP_TIMEOUT=180
|
||||
|
||||
docker-compose version >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
docker compose version >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
compose_cmd="docker compose"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ! ${SQLBOT_EXTERNAL_DB} ]] || [ "${SQLBOT_EXTERNAL_DB}" = "false" ]; then
|
||||
compose_files="${compose_files} -f docker-compose-pg.yml"
|
||||
fi
|
||||
|
||||
function usage() {
|
||||
echo "SQLBot 控制脚本"
|
||||
echo
|
||||
echo "Usage: "
|
||||
echo " ./sctl [COMMAND] [ARGS...]"
|
||||
echo " ./sctl --help"
|
||||
echo
|
||||
echo "Commands: "
|
||||
echo " status 查看 SQLBot 服务运行状态"
|
||||
echo " start 启动 SQLBot 服务"
|
||||
echo " stop 停止 SQLBot 服务"
|
||||
echo " restart 重启 SQLBot 服务"
|
||||
echo " reload 重载 SQLBot 服务"
|
||||
echo " clear-images 清理 SQLBot 旧版本的相关镜像"
|
||||
echo " clear-logs 清理 SQLBot 历史日志"
|
||||
echo " version 查看 SQLBot 版本"
|
||||
}
|
||||
|
||||
function _healthcheck() {
|
||||
echo
|
||||
container_name=$(grep "container_name" $SQLBOT_RUNNING_BASE/docker-compose.yml | awk -F': ' '{print $2}')
|
||||
sleep 1
|
||||
if [ -z '$(docker ps --filter "name=^$container_name$" -q)' ];then
|
||||
echo "未找到容器 $container_name。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for b in {1..90}
|
||||
do
|
||||
sleep 1
|
||||
read status healthy<<<$(docker inspect $container_name --format '{{.State.Status}} {{.State.Health.Status}}')
|
||||
if [[ "$healthy" == "starting" ]];then
|
||||
printf "\rSQLBot 服务状态 : 正在启动 ... %3ds" $b
|
||||
elif [[ "$status" == "running" ]] && [[ "$healthy" == "healthy" ]];then
|
||||
printf "\rSQLBot 服务状态 : 正常运行 "
|
||||
echo
|
||||
return;
|
||||
else
|
||||
echo
|
||||
echo -e "SQLBot 服务状态 : \033[31m无法访问\033[0m"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
echo
|
||||
echo "【警告】服务在等待时间内未完全启动!请稍后使用 sctl status 检查服务运行状况。"
|
||||
echo
|
||||
}
|
||||
function _get_current_version() {
|
||||
sqlbot_current_version=$(grep "^ image:.*sqlbot:" ${SQLBOT_RUNNING_BASE}/docker-compose.yml | awk -F'sqlbot:' '{print $2}')
|
||||
if test -z $sqlbot_current_version; then
|
||||
echo "获取当前版本失败,请检查当前版本是否正确"
|
||||
exit 1
|
||||
fi
|
||||
current_version=$sqlbot_current_version
|
||||
}
|
||||
|
||||
function status() {
|
||||
echo
|
||||
echo "SQLBot 容器运行状态"
|
||||
cd ${SQLBOT_RUNNING_BASE}
|
||||
${compose_cmd} ${compose_files} ps
|
||||
|
||||
echo
|
||||
docker_root_dir=$(docker info | grep "Docker Root Dir" | awk -F': ' '{print $2}')
|
||||
echo "Docker 目录及文件大小 : "
|
||||
du -sh $docker_root_dir
|
||||
echo
|
||||
echo "Docker 目录所在磁盘使用情况 : "
|
||||
df -H $docker_root_dir
|
||||
|
||||
echo
|
||||
echo "日志文件大小 : "
|
||||
du -Sh ${SQLBOT_RUNNING_BASE}/logs/
|
||||
|
||||
echo
|
||||
echo "SQLBot 运行目录及文件大小 : "
|
||||
du -sh ${SQLBOT_BASE}
|
||||
echo
|
||||
echo "SQLBot 运行目录使用情况 : "
|
||||
df -H ${SQLBOT_BASE}
|
||||
|
||||
_healthcheck
|
||||
}
|
||||
function start() {
|
||||
echo
|
||||
cd ${SQLBOT_RUNNING_BASE}
|
||||
${compose_cmd} ${compose_files} up -d
|
||||
_healthcheck
|
||||
}
|
||||
function stop() {
|
||||
echo
|
||||
cd ${SQLBOT_RUNNING_BASE}
|
||||
${compose_cmd} ${compose_files} down -v ${target}
|
||||
}
|
||||
function restart() {
|
||||
stop
|
||||
start
|
||||
}
|
||||
function reload() {
|
||||
start
|
||||
}
|
||||
function version() {
|
||||
echo
|
||||
_get_current_version
|
||||
echo "current version is $current_version"
|
||||
}
|
||||
function clear_images() {
|
||||
echo
|
||||
for f in $SQLBOT_RUNNING_BASE/*.yml; do
|
||||
[[ -e "$f" ]] || break
|
||||
grep "^ image:.*:" "$f" | uniq | while read -r component_image_defined; do
|
||||
component_version=$(echo ${component_image_defined} | awk -F":" '{print $3}' | uniq)
|
||||
component_image=$(echo ${component_image_defined} | awk -F'image: ' '{print $2}' | awk -F':' '{print $1}')
|
||||
|
||||
if [[ $(docker images | grep "$component_image[[:space:]]" | grep -v " $component_version " | wc -l) == 0 ]]; then
|
||||
echo "$component_image 不存在旧版本镜像"
|
||||
else
|
||||
echo "存在非当前版本镜像 : "
|
||||
docker images | grep "$component_image[[:space:]]" | grep -v " $component_version "
|
||||
echo "清理${component}镜像"
|
||||
docker rmi $(docker images | grep "$component_image[[:space:]]" | grep -v " $component_version " | awk -F' ' '{print $1":"$2}')
|
||||
echo "清理完毕"
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
if [ $(docker images -f dangling=true -q | wc -l) -gt 0 ]; then
|
||||
echo "清理虚悬镜像"
|
||||
docker rmi $(docker images -f dangling=true -q)
|
||||
echo "清理完毕"
|
||||
fi
|
||||
}
|
||||
function clear_logs() {
|
||||
echo "开始清理 SQLBot 历史日志"
|
||||
rm -rf ${SQLBOT_LOG_PATH}/*.*.*
|
||||
echo "" > ${SQLBOT_LOG_PATH}/info.log
|
||||
echo "" > ${SQLBOT_LOG_PATH}/debug.log
|
||||
echo "" > ${SQLBOT_LOG_PATH}/error.log
|
||||
echo "清理完毕"
|
||||
}
|
||||
|
||||
function main() {
|
||||
case "${action}" in
|
||||
status)
|
||||
status
|
||||
;;
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
restart)
|
||||
restart
|
||||
;;
|
||||
reload)
|
||||
reload
|
||||
;;
|
||||
clear-images)
|
||||
clear_images
|
||||
;;
|
||||
clear-logs)
|
||||
clear_logs
|
||||
;;
|
||||
version)
|
||||
version
|
||||
;;
|
||||
help)
|
||||
usage
|
||||
;;
|
||||
--help)
|
||||
usage
|
||||
;;
|
||||
"")
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
echo "不支持的参数,请使用 help 或 --help 参数获取帮助"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
main
|
||||
20
installer/templates/sqlbot.conf
Normal file
20
installer/templates/sqlbot.conf
Normal file
@@ -0,0 +1,20 @@
|
||||
PROJECT_NAME="SQLBot"
|
||||
# Backend
|
||||
BACKEND_CORS_ORIGINS=${SQLBOT_CORS_ORIGINS}
|
||||
SECRET_KEY=${SQLBOT_SECRET_KEY}
|
||||
|
||||
DEFAULT_PWD=${SQLBOT_DEFAULT_PWD}
|
||||
|
||||
LOG_LEVEL=${SQLBOT_LOG_LEVEL} # DEBUG, INFO, WARNING, ERROR
|
||||
SQL_DEBUG=False
|
||||
|
||||
CACHE_TYPE=${SQLBOT_CACHE_TYPE}
|
||||
|
||||
# Postgres
|
||||
POSTGRES_SERVER=${SQLBOT_DB_HOST}
|
||||
POSTGRES_PORT=${SQLBOT_DB_PORT}
|
||||
POSTGRES_DB=${SQLBOT_DB_DB}
|
||||
POSTGRES_USER=${SQLBOT_DB_USER}
|
||||
POSTGRES_PASSWORD=${SQLBOT_DB_PASSWORD} # Change this to your pwd
|
||||
|
||||
SERVER_IMAGE_HOST=${SQLBOT_SERVER_IMAGE_HOST}
|
||||
43
installer/uninstall.sh
Normal file
43
installer/uninstall.sh
Normal file
@@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
|
||||
SQLBOT_BASE=/opt
|
||||
|
||||
read -r -p "即将卸载 SQLBot 服务,包括删除运行目录、数据及相关镜像,是否继续? [Y/n] " input
|
||||
|
||||
case $input in
|
||||
[yY][eE][sS]|[yY])
|
||||
echo "Yes"
|
||||
;;
|
||||
[nN][oO]|[nN])
|
||||
echo "No"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
echo "无效输入..."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "停止 SQLBot 服务"
|
||||
sctl stop >/dev/null 2>&1
|
||||
|
||||
if [ -f /usr/bin/sctl ]; then
|
||||
# 获取已安装的 SQLBot 的运行目录
|
||||
SQLBOT_BASE=$(grep "^SQLBOT_BASE=" /usr/bin/sctl | cut -d'=' -f2)
|
||||
fi
|
||||
|
||||
# 清理 SQLBot 相关镜像
|
||||
if test ! -z "$(docker images -f dangling=true -q)"; then
|
||||
echo "清理虚悬镜像"
|
||||
docker rmi $(docker images -f dangling=true -q)
|
||||
fi
|
||||
|
||||
if test -n "$(docker images | grep 'registry.cn-qingdao.aliyuncs.com/dataease/sqlbot')"; then
|
||||
echo "清理 SQLBot 镜像"
|
||||
docker rmi $(docker images | grep "registry.cn-qingdao.aliyuncs.com/dataease/sqlbot" | awk -F' ' '{print $1":"$2}')
|
||||
fi
|
||||
|
||||
# 清理 SQLBot 运行目录及命令行工具 sctl
|
||||
rm -rf ${SQLBOT_BASE}/sqlbot /usr/bin/sctl
|
||||
|
||||
echo "SQLBot 服务卸载完成"
|
||||
593
sqlbot-assistant-demo.html
Normal file
593
sqlbot-assistant-demo.html
Normal file
@@ -0,0 +1,593 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>优购电商 - 您的一站式购物平台</title>
|
||||
<style>
|
||||
/* 全局样式 */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: "Arial", "Microsoft YaHei", sans-serif;
|
||||
}
|
||||
|
||||
/* 滚动条样式 */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(255, 107, 107, 0.5);
|
||||
border-radius: 4px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
:hover::-webkit-scrollbar-thumb,
|
||||
:focus::-webkit-scrollbar-thumb,
|
||||
:active::-webkit-scrollbar-thumb {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #ff6b6b;
|
||||
}
|
||||
|
||||
body {
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(255, 107, 107, 0.5) transparent;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
/* 导航栏 */
|
||||
header {
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #ff6b6b;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.nav-links li {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
.nav-links a:hover {
|
||||
color: #ff6b6b;
|
||||
}
|
||||
|
||||
.mobile-menu {
|
||||
display: none;
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 英雄区域 */
|
||||
.hero {
|
||||
background: linear-gradient(135deg, #f6d365 0%, #fda085 100%);
|
||||
color: white;
|
||||
padding: 80px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: 48px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.hero p {
|
||||
font-size: 20px;
|
||||
max-width: 700px;
|
||||
margin: 0 auto 30px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
background-color: #ff6b6b;
|
||||
color: white;
|
||||
padding: 12px 30px;
|
||||
border-radius: 30px;
|
||||
font-weight: bold;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background-color: #ff5252;
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
/* 特色服务 */
|
||||
.features {
|
||||
padding: 80px 0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
text-align: center;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
.section-title h2 {
|
||||
font-size: 36px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.features-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
background-color: white;
|
||||
border-radius: 10px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
|
||||
text-align: center;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.feature-card:hover {
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
font-size: 50px;
|
||||
color: #ff6b6b;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.feature-card h3 {
|
||||
margin-bottom: 15px;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
/* 热门产品 */
|
||||
.products {
|
||||
padding: 80px 0;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.product-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.product-card {
|
||||
background-color: white;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.product-card:hover {
|
||||
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.product-img {
|
||||
height: 200px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.product-img img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.5s;
|
||||
}
|
||||
|
||||
.product-card:hover .product-img img {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.product-info {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.product-info h3 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.price {
|
||||
color: #ff6b6b;
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
/* 客户评价 */
|
||||
.testimonials {
|
||||
padding: 80px 0;
|
||||
}
|
||||
|
||||
.testimonial-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.testimonial-card {
|
||||
background-color: white;
|
||||
border-radius: 10px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.testimonial-text {
|
||||
font-style: italic;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.client-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.client-img {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.client-img img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
/* 页脚 */
|
||||
footer {
|
||||
background-color: #333;
|
||||
color: white;
|
||||
padding: 60px 0 20px;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 40px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.footer-column h3 {
|
||||
margin-bottom: 20px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.footer-links li {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.footer-links a:hover {
|
||||
color: #ff6b6b;
|
||||
}
|
||||
|
||||
.social-links {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.social-links a {
|
||||
display: inline-block;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
line-height: 40px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.social-links a:hover {
|
||||
background-color: #ff6b6b;
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.copyright {
|
||||
text-align: center;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.nav-links {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mobile-menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
.hero p {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script
|
||||
async
|
||||
defer
|
||||
id="sqlbot-assistant-float-script-7356228649239973888"
|
||||
src="https://sqlbot.fit2cloud.cn/assistant.js?id=7356228649239973888"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- 导航栏 -->
|
||||
<header>
|
||||
<div class="container">
|
||||
<nav class="navbar">
|
||||
<a href="#" class="logo">优购电商</a>
|
||||
<ul class="nav-links">
|
||||
<li><a href="#">首页</a></li>
|
||||
<li><a href="#">商品分类</a></li>
|
||||
<li><a href="#">热销商品</a></li>
|
||||
<li><a href="#">关于我们</a></li>
|
||||
<li><a href="#">联系我们</a></li>
|
||||
</ul>
|
||||
<div class="mobile-menu">☰</div>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- 英雄区域 -->
|
||||
<section class="hero">
|
||||
<div class="container">
|
||||
<h1>发现优质商品,享受便捷购物</h1>
|
||||
<p>
|
||||
优购电商为您提供海量正品商品,全场包邮,7天无理由退换货,让您购物无忧
|
||||
</p>
|
||||
<a href="#" class="btn">立即购物</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 特色服务 -->
|
||||
<section class="features">
|
||||
<div class="container">
|
||||
<div class="section-title">
|
||||
<h2>我们的优势</h2>
|
||||
</div>
|
||||
<div class="features-grid">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">🚚</div>
|
||||
<h3>快速配送</h3>
|
||||
<p>全国300+城市次日达,部分地区可实现当日送达</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">🔒</div>
|
||||
<h3>正品保障</h3>
|
||||
<p>所有商品100%正品,假一赔十,购物更放心</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">💯</div>
|
||||
<h3>无忧售后</h3>
|
||||
<p>7天无理由退换货,专业客服团队24小时在线服务</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 热门产品 -->
|
||||
<section class="products">
|
||||
<div class="container">
|
||||
<div class="section-title">
|
||||
<h2>热门商品</h2>
|
||||
</div>
|
||||
<div class="product-grid">
|
||||
<div class="product-card">
|
||||
<div class="product-img">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1523275335684-37898b6baf30?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=80"
|
||||
alt="智能手表"
|
||||
/>
|
||||
</div>
|
||||
<div class="product-info">
|
||||
<h3>智能手表 Pro</h3>
|
||||
<p>多功能健康监测,超长续航</p>
|
||||
<div class="price">¥899</div>
|
||||
<a href="#" class="btn">加入购物车</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="product-card">
|
||||
<div class="product-img">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1505740420928-5e560c06d30e?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=80"
|
||||
alt="无线耳机"
|
||||
/>
|
||||
</div>
|
||||
<div class="product-info">
|
||||
<h3>无线降噪耳机</h3>
|
||||
<p>主动降噪,Hi-Fi音质</p>
|
||||
<div class="price">¥599</div>
|
||||
<a href="#" class="btn">加入购物车</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="product-card">
|
||||
<div class="product-img">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1561154464-82e9adf32764?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=80"
|
||||
alt="投影仪"
|
||||
/>
|
||||
</div>
|
||||
<div class="product-info">
|
||||
<h3>便携式投影仪</h3>
|
||||
<p>1080P高清,内置电池</p>
|
||||
<div class="price">¥1299</div>
|
||||
<a href="#" class="btn">加入购物车</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="product-card">
|
||||
<div class="product-img">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1561154464-82e9adf32764?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=80"
|
||||
alt="空气炸锅"
|
||||
/>
|
||||
</div>
|
||||
<div class="product-info">
|
||||
<h3>智能空气炸锅</h3>
|
||||
<p>无油健康烹饪,大容量</p>
|
||||
<div class="price">¥399</div>
|
||||
<a href="#" class="btn">加入购物车</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 客户评价 -->
|
||||
<section class="testimonials">
|
||||
<div class="container">
|
||||
<div class="section-title">
|
||||
<h2>客户评价</h2>
|
||||
</div>
|
||||
<div class="testimonial-grid">
|
||||
<div class="testimonial-card">
|
||||
<div class="testimonial-text">
|
||||
"在优购电商购物体验非常好,商品质量有保障,物流速度也很快,客服态度也很专业,会继续支持!"
|
||||
</div>
|
||||
<div class="client-info">
|
||||
<div class="client-img">
|
||||
<img
|
||||
src="https://randomuser.me/api/portraits/women/44.jpg"
|
||||
alt="张女士"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h4>张女士</h4>
|
||||
<p>上海</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="testimonial-card">
|
||||
<div class="testimonial-text">
|
||||
"第一次在优购买东西就爱上了这个平台,价格实惠,经常有活动,退换货也很方便,强烈推荐!"
|
||||
</div>
|
||||
<div class="client-info">
|
||||
<div class="client-img">
|
||||
<img
|
||||
src="https://randomuser.me/api/portraits/men/32.jpg"
|
||||
alt="李先生"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h4>李先生</h4>
|
||||
<p>北京</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="testimonial-card">
|
||||
<div class="testimonial-text">
|
||||
"对比了好几个电商平台,最后还是选择优购,商品种类齐全,售后服务有保障,购物很放心。"
|
||||
</div>
|
||||
<div class="client-info">
|
||||
<div class="client-img">
|
||||
<img
|
||||
src="https://randomuser.me/api/portraits/women/65.jpg"
|
||||
alt="王小姐"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h4>王小姐</h4>
|
||||
<p>广州</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 页脚 -->
|
||||
<footer>
|
||||
<div class="container">
|
||||
<div class="footer-content">
|
||||
<div class="footer-column">
|
||||
<h3>关于优购</h3>
|
||||
<ul class="footer-links">
|
||||
<li><a href="#">公司简介</a></li>
|
||||
<li><a href="#">招贤纳士</a></li>
|
||||
<li><a href="#">联系我们</a></li>
|
||||
<li><a href="#">隐私政策</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer-column">
|
||||
<h3>购物指南</h3>
|
||||
<ul class="footer-links">
|
||||
<li><a href="#">购物流程</a></li>
|
||||
<li><a href="#">会员介绍</a></li>
|
||||
<li><a href="#">常见问题</a></li>
|
||||
<li><a href="#">联系客服</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer-column">
|
||||
<h3>配送方式</h3>
|
||||
<ul class="footer-links">
|
||||
<li><a href="#">上门自提</a></li>
|
||||
<li><a href="#">211限时达</a></li>
|
||||
<li><a href="#">配送服务查询</a></li>
|
||||
<li><a href="#">配送费收取标准</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer-column">
|
||||
<h3>关注我们</h3>
|
||||
<p>订阅我们的电子报,获取最新优惠信息</p>
|
||||
<div class="social-links">
|
||||
<a href="#">微</a>
|
||||
<a href="#">微</a>
|
||||
<a href="#">抖</a>
|
||||
<a href="#">快</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="copyright">
|
||||
<p>© 2023 优购电商 版权所有 | ICP备案号:京ICP备XXXXXXXX号</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user