deploy: remove local build compose path

This commit is contained in:
fawney19
2026-05-14 14:40:25 +08:00
parent a7e71624e3
commit d2831ec6f0
10 changed files with 33 additions and 615 deletions

View File

@@ -1,131 +0,0 @@
# syntax=docker/dockerfile:1
# Aether 运行镜像Rust gateway 直接服务 API + 前端静态文件(国内镜像源版本)
# 构建命令: docker build -f Dockerfile.app.local -t aether-app:latest .
# ==================== 前端构建 ====================
FROM node:22-slim AS frontend-builder
WORKDIR /app/frontend
COPY frontend/package*.json ./
RUN npm config set registry https://registry.npmmirror.com && npm ci
COPY frontend/ ./
RUN npm run build
# ==================== Rust gateway 构建 ====================
FROM rust:1.94.1-slim AS gateway-base
WORKDIR /build
# 本地镜像优先缩短构建时间,保留 release 语义,但改用更快的 thin LTO。
ENV CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse \
CARGO_PROFILE_RELEASE_LTO=thin \
CARGO_PROFILE_RELEASE_CODEGEN_UNITS=16
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
sed -i 's/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list.d/debian.sources && \
apt-get update && apt-get install -y --no-install-recommends \
build-essential \
ca-certificates \
libssl-dev \
pkg-config \
perl
RUN --mount=type=cache,id=aether-cargo-registry,target=/usr/local/cargo/registry,sharing=locked \
--mount=type=cache,id=aether-cargo-git,target=/usr/local/cargo/git,sharing=locked \
cargo install cargo-chef --locked
FROM gateway-base AS gateway-planner
COPY Cargo.toml Cargo.lock ./
COPY apps/ ./apps/
COPY crates/ ./crates/
RUN cargo chef prepare --recipe-path recipe.json
FROM gateway-base AS gateway-builder
COPY --from=gateway-planner /build/recipe.json ./recipe.json
RUN --mount=type=cache,id=aether-cargo-registry,target=/usr/local/cargo/registry,sharing=locked \
--mount=type=cache,id=aether-cargo-git,target=/usr/local/cargo/git,sharing=locked \
--mount=type=cache,id=aether-cargo-target-local,target=/build/target,sharing=locked \
cargo chef cook --release --locked --package aether-gateway --bin aether-gateway --recipe-path recipe.json
COPY Cargo.toml Cargo.lock ./
COPY apps/ ./apps/
COPY crates/ ./crates/
RUN --mount=type=cache,id=aether-cargo-registry,target=/usr/local/cargo/registry,sharing=locked \
--mount=type=cache,id=aether-cargo-git,target=/usr/local/cargo/git,sharing=locked \
--mount=type=cache,id=aether-cargo-target-local,target=/build/target,sharing=locked \
cargo build --release --locked -p aether-gateway && \
cp target/release/aether-gateway /tmp/aether-gateway
# ==================== 最小运行时打包 ====================
FROM gateway-builder AS runtime-prep
RUN set -eux; \
mkdir -p \
/runtime-root/app/data \
/runtime-root/app/logs \
/runtime-root/etc \
/runtime-root/etc/ssl \
/runtime-root/lib \
/runtime-root/lib64 \
/runtime-root/usr/local/bin; \
cp /tmp/aether-gateway /runtime-root/usr/local/bin/aether-gateway; \
: > /tmp/runtime-libs.txt; \
: > /tmp/runtime-scan-queue.txt; \
printf '%s\n' /tmp/aether-gateway >> /tmp/runtime-scan-queue.txt; \
while [ -s /tmp/runtime-scan-queue.txt ]; do \
current="$(head -n1 /tmp/runtime-scan-queue.txt)"; \
sed -i '1d' /tmp/runtime-scan-queue.txt; \
ldd "$current" | awk '/=>/ { print $3 } $1 ~ /^\// { print $1 }' | while read -r lib; do \
[ -n "$lib" ]; \
if ! grep -Fxq "$lib" /tmp/runtime-libs.txt; then \
printf '%s\n' "$lib" >> /tmp/runtime-libs.txt; \
printf '%s\n' "$lib" >> /tmp/runtime-scan-queue.txt; \
fi; \
done; \
done; \
sort -u /tmp/runtime-libs.txt -o /tmp/runtime-libs.txt; \
while read -r lib; do \
[ -n "$lib" ]; \
dest="/runtime-root$(dirname "$lib")"; \
mkdir -p "$dest"; \
cp -L "$lib" "$dest/"; \
done < /tmp/runtime-libs.txt; \
for lib in \
/lib/x86_64-linux-gnu/libnss_dns.so.2 \
/lib/x86_64-linux-gnu/libnss_files.so.2 \
/lib/x86_64-linux-gnu/libresolv.so.2; do \
if [ -f "$lib" ]; then \
dest="/runtime-root$(dirname "$lib")"; \
mkdir -p "$dest"; \
cp -L "$lib" "$dest/"; \
fi; \
done; \
cp -a /usr/lib/ssl /runtime-root/usr/lib/; \
cp -a /etc/ssl/certs /runtime-root/etc/ssl/; \
if [ -f /etc/ssl/openssl.cnf ]; then \
cp /etc/ssl/openssl.cnf /runtime-root/etc/ssl/openssl.cnf; \
fi; \
if [ -f /etc/nsswitch.conf ]; then \
cp /etc/nsswitch.conf /runtime-root/etc/nsswitch.conf; \
fi
# ==================== 运行时镜像 ====================
FROM scratch
# 复制 gateway 二进制
COPY --from=runtime-prep /runtime-root/ /
# 复制前端构建产物
COPY --from=frontend-builder /app/frontend/dist /srv/frontend
WORKDIR /app
ENV LANG=C.UTF-8 \
LC_ALL=C.UTF-8 \
RUST_LOG=aether_gateway=info \
APP_PORT=8084 \
AETHER_GATEWAY_STATIC_DIR=/srv/frontend
EXPOSE 8084
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD ["/usr/local/bin/aether-gateway", "--healthcheck"]
ENTRYPOINT ["/usr/local/bin/aether-gateway"]

138
README.md
View File

@@ -44,70 +44,23 @@ cd Aether
# 2. 配置环境变量 # 2. 配置环境变量
cp .env.example .env cp .env.example .env
./generate_keys.sh # 生成 JWT_SECRET_KEY / ENCRYPTION_KEY, 并填入 .env # 生成 JWT_SECRET_KEY / ENCRYPTION_KEY, 并填入 .env
# 编辑 .env 设置 ADMIN_PASSWORD
# 3. 首次部署 / 更新
docker compose pull && docker compose up -d
# 4. 默认会在 app 启动前自动执行挂起的 migration / backfill
# 如需手工控制,可在 .env 中设 AETHER_GATEWAY_AUTO_PREPARE_DATABASE=false
# 然后按需执行:
docker compose run --rm app --migrate
docker compose run --rm app --apply-backfills
# 5. 升级前备份 (可选)
docker compose exec postgres pg_dump -U postgres aether | gzip > backup_$(date +%Y%m%d_%H%M%S).sql.gz
```
### Docker Compose本地构建镜像
```bash
# 1. 克隆代码
git clone https://github.com/fawney19/Aether.git
cd Aether
# 2. 配置环境变量
cp .env.example .env
./generate_keys.sh # 生成 JWT_SECRET_KEY / ENCRYPTION_KEY, 并填入 .env
# 编辑 .env 设置 ADMIN_PASSWORD
# 3. 部署 / 更新(自动构建并启动)
git pull
./deploy.sh
```
### Docker Compose单容器 SQLite / solo
```bash
# 1. 克隆代码
git clone https://github.com/fawney19/Aether.git
cd Aether
# 2. 配置环境变量
cp .env.example .env
./generate_keys.sh ./generate_keys.sh
# 编辑 .env 设置 ADMIN_PASSWORD # 编辑 .env 设置 ADMIN_PASSWORD
# 3. 启动单容器 SQLite 版本 # 3. 首次部署 / 更新 (从以下数据库、内存策略任选其一)
docker compose -f docker-compose.solo.yml up -d # Postgres + Redis (适用于企业或多人使用)
docker compose pull && docker compose up -d
# 4. 升级前备份(可选) # 仅SQLite (适用于个人用户或朋友分享)
cp -a data/aether.db backup_$(date +%Y%m%d_%H%M%S).db docker compose -f docker-compose.sqlite.yml pull && docker compose -f docker-compose.sqlite.yml up -d
``` ```
### 一键安装(可选部署方式) ### 一键安装(可选部署方式)
安装脚本先从 `aether-rust-pioneer` 分支下载,不依赖 GitHub Release 的 `latest` 脚本地址。运行后会先选择语言再选择版本和部署方式。Linux 单机 / 集群服务使用 systemdmacOS 单机 / 集群服务使用系统级 launchd脚本会按当前系统自动下载 `linux-*``macos-*` Release 压缩包。
```bash ```bash
curl -fsSL https://raw.githubusercontent.com/fawney19/Aether/aether-rust-pioneer/install.sh | sudo bash curl -fsSL https://raw.githubusercontent.com/fawney19/Aether/main/install.sh | sudo bash
``` ```
运行后按提示输入语言、版本和部署方式。默认会安装最新正式版。需要提前测试时可用 `AETHER_CHANNEL=rc``AETHER_CHANNEL=beta`Docker Compose 模式默认使用 `latest` 镜像通道。固定安装某个 tag 时,版本选择选 `4`,再输入类似 `v0.7.0-rc.1` 的 tag。
二进制安装在下载 Release 压缩包前会询问是否使用下载加速源;选择使用时会先打印原始 GitHub URL再要求输入新的压缩包下载 URL。非交互式安装可用 `AETHER_RELEASE_ARCHIVE_URL` 指定压缩包 URL。
如果安装目录里已经有配置脚本会优先复用Docker Compose 保留已有 `.env`,二进制服务模式保留已有 `/etc/aether/aether-gateway.env`。只有首次生成新配置时才会提示输入管理员密码。
```text ```text
请选择安装语言 / Choose installer language: 请选择安装语言 / Choose installer language:
1) 中文 1) 中文
@@ -173,47 +126,6 @@ DATABASE_URL=postgresql://...
REDIS_URL=redis://... REDIS_URL=redis://...
``` ```
### 本地开发
```bash
# 启动依赖
docker compose -f docker-compose.build.yml up -d postgres redis
# 数据库迁移(仅在已有数据库引入新 migration 时需要)
./dev.sh --migrate
# 数据回填
./dev.sh --apply-backfills
# 后端
./dev.sh
# 前端
cd frontend && npm install && npm run dev
```
`./dev.sh` 现在只保留一种本地模式:
| 角色 | 本地地址 | 说明 |
|------|----------|------|
| Rust frontdoor | 默认 `http://localhost:8084` | `aether-gateway`,本地唯一公开入口;实际端口由 `APP_PORT` 控制 |
本地默认链路是:
```text
client -> rust frontdoor (aether-gateway) -> execution_runtime/provider transport
```
其中:
- `aether-gateway` 负责公开入口、健康检查、格式转换、本地执行 runtime以及当前已迁到 Rust 的 frontdoor/control/background 路径。
- `./dev.sh` 不再启动 Python 宿主;未下沉到 Rust 的 legacy 路由会直接失败。
- `./dev.sh --migrate` 会复用 `.env` 里的数据库配置,显式执行一次数据库迁移后退出。
- `./dev.sh` 默认把 `AETHER_GATEWAY_VIDEO_TASK_TRUTH_SOURCE_MODE` 设为 `rust-authoritative`,避免本地还依赖 Python sync report 语义。
- 空库首次启动会自动初始化到当前 baseline。
- `aether-gateway` 默认启动不会自动应用后续 schema migration如果数据库版本落后服务会拒绝启动并提示先执行 `aether-gateway --migrate`
- 仓库自带的 `docker-compose.yml``docker-compose.build.yml` 都已把 `AETHER_GATEWAY_AUTO_PREPARE_DATABASE` 设为默认开启,因此无论是预构建镜像部署还是 `./deploy.sh` / 本地构建 compose常规启动都会在监听端口前自动执行挂起的 migration 和 backfill。
## Aether Proxy (可选) ## Aether Proxy (可选)
Aether Proxy 是配套的正向代理节点,部署在海外 VPS 上,为墙内的 Aether 实例中转 API 流量。或者部署在其他服务器为指定的提供商、账号、Key使用不同的节点访问。支持 TUI 向导一键配置、systemd 服务管理、TLS 加密、DNS 缓存及连接池调优。 Aether Proxy 是配套的正向代理节点,部署在海外 VPS 上,为墙内的 Aether 实例中转 API 流量。或者部署在其他服务器为指定的提供商、账号、Key使用不同的节点访问。支持 TUI 向导一键配置、systemd 服务管理、TLS 加密、DNS 缓存及连接池调优。
@@ -240,7 +152,7 @@ Aether Proxy 是配套的正向代理节点,部署在海外 VPS 上,为墙
- `AETHER_DATABASE_DRIVER` / `AETHER_DATABASE_URL`:二进制单机部署可用 `sqlite`,例如 `sqlite:///opt/aether/data/aether.db` - `AETHER_DATABASE_DRIVER` / `AETHER_DATABASE_URL`:二进制单机部署可用 `sqlite`,例如 `sqlite:///opt/aether/data/aether.db`
- `DATABASE_URL` / `REDIS_URL`:共享后端连接串;多节点必须配置共享数据库和 Redis - `DATABASE_URL` / `REDIS_URL`:共享后端连接串;多节点必须配置共享数据库和 Redis
- `AETHER_RUNTIME_BACKEND=memory|redis`:运行时缓存/协调后端。单机 SQLite 默认用 `memory`,不会连接 Redis显式设为 `redis` 或多节点部署才会把 `REDIS_URL` 注入运行时 Redis 后端 - `AETHER_RUNTIME_BACKEND=memory|redis`:运行时缓存/协调后端。单机 SQLite 默认用 `memory`,不会连接 Redis显式设为 `redis` 或多节点部署才会把 `REDIS_URL` 注入运行时 Redis 后端
- `AETHER_GATEWAY_AUTO_PREPARE_DATABASE`:常规启动前自动执行挂起的 schema migration 和 backfill仓库自带的 `docker-compose.yml``docker-compose.build.yml` 默认开启 - `AETHER_GATEWAY_AUTO_PREPARE_DATABASE`:常规启动前自动执行挂起的 schema migration 和 backfill仓库自带的 `docker-compose.yml` 默认开启
- `JWT_SECRET_KEY` / `ENCRYPTION_KEY`:认证和敏感数据加密所需密钥 - `JWT_SECRET_KEY` / `ENCRYPTION_KEY`:认证和敏感数据加密所需密钥
- `API_KEY_PREFIX`:用户和管理员新建 API Key 时使用的前缀,默认 `sk` - `API_KEY_PREFIX`:用户和管理员新建 API Key 时使用的前缀,默认 `sk`
- `ADMIN_USERNAME` / `ADMIN_PASSWORD` / `ADMIN_EMAIL`:首次启动时自举首个本地管理员;`install.sh` 会提示输入管理员密码 - `ADMIN_USERNAME` / `ADMIN_PASSWORD` / `ADMIN_EMAIL`:首次启动时自举首个本地管理员;`install.sh` 会提示输入管理员密码
@@ -252,40 +164,6 @@ Aether Proxy 是配套的正向代理节点,部署在海外 VPS 上,为墙
systemd 的 `.env` 必须保持简单 `KEY=VALUE` 形式,不要写 `export``${VAR}` 或命令替换。 systemd 的 `.env` 必须保持简单 `KEY=VALUE` 形式,不要写 `export``${VAR}` 或命令替换。
## Q&A
### Q: 如何开启/关闭请求体记录?
管理员在 **系统设置** 中配置日志记录的详细程度:
| 级别 | 记录内容 |
|------|----------|
| Base | 基本请求信息 |
| Headers | Base + 请求头 |
| Full | Headers + 请求体 |
### Q: 更新出问题如何回滚?
**有备份的情况(推荐):**
```bash
# Docker Compose:
# 1. 切回旧镜像 tag / digest
# 2. 恢复 Postgres 备份
# 3. 再启动 app
# systemd:
# 1. 把 /opt/aether/current 切回旧 release
# 2. systemctl restart aether-gateway
# 3. 如果升级包含数据库结构变更,再恢复 Postgres 备份
```
> 可以在升级前通过 `docker inspect ghcr.io/fawney19/aether:latest --format '{{index .RepoDigests 0}}'` 记录当前镜像 digest方便回滚时使用。
**没有备份的情况:**
当前不应该再依赖旧的 `alembic downgrade` 路线。空库首次启动会自动初始化;如果开启 `AETHER_GATEWAY_AUTO_PREPARE_DATABASE=true`(仓库自带的两份 compose 默认都如此),常规服务启动也会自动应用挂起的 migration/backfill。无论是否自动执行只要本次发布带来了不可逆的数据结构变化没有备份就不能保证安全回滚。因此升级前强烈建议先备份 `Postgres`
--- ---
## 许可证 ## 许可证

144
deploy.sh
View File

@@ -1,144 +0,0 @@
#!/bin/bash
# 智能部署脚本 - 自动检测代码变化并重建
#
# 用法:
# 部署/更新: ./deploy.sh
# 强制全部重建: ./deploy.sh --force
set -euo pipefail
cd "$(dirname "$0")"
# 兼容 docker-compose 和 docker compose
if command -v docker-compose &> /dev/null; then
DC="docker-compose -f docker-compose.build.yml"
USE_LEGACY_COMPOSE=true
else
DC="docker compose -f docker-compose.build.yml"
USE_LEGACY_COMPOSE=false
fi
compose_up() {
if [ "$USE_LEGACY_COMPOSE" = true ]; then
$DC up -d --no-build "$@"
else
$DC up -d --no-build --pull never "$@"
fi
}
# 缓存文件
CODE_HASH_FILE=".code-hash"
usage() {
cat <<'EOF'
Usage: ./deploy.sh [options]
Options:
--force, -f 强制重建并重启
-h, --help 显示帮助
EOF
}
FORCE_REBUILD_ALL=false
while [ $# -gt 0 ]; do
case "$1" in
--force|-f)
FORCE_REBUILD_ALL=true
shift
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown argument: $1"
usage
exit 1
;;
esac
done
# 计算代码文件的哈希值
calc_code_hash() {
{
cat Dockerfile.app.local 2>/dev/null
cat Cargo.toml Cargo.lock 2>/dev/null
find frontend/src -type f \( -name "*.vue" -o -name "*.ts" -o -name "*.tsx" -o -name "*.js" \) 2>/dev/null | sort | xargs cat 2>/dev/null
find apps -type f \( -name "*.rs" -o -name "Cargo.toml" \) 2>/dev/null | sort | xargs cat 2>/dev/null
find crates -type f \( -name "*.rs" -o -name "*.sql" -o -name "Cargo.toml" \) 2>/dev/null | sort | xargs cat 2>/dev/null
} | md5sum | cut -d' ' -f1
}
# 检查代码是否变化
check_code_changed() {
local current_hash=$(calc_code_hash)
if [ -f "$CODE_HASH_FILE" ]; then
local saved_hash=$(cat "$CODE_HASH_FILE")
if [ "$current_hash" = "$saved_hash" ]; then
return 1
fi
fi
return 0
}
save_code_hash() { calc_code_hash > "$CODE_HASH_FILE"; }
# 构建应用镜像
build_app() {
echo ">>> Building app image (rust gateway + frontend)..."
docker build --pull=false -f Dockerfile.app.local -t aether-app:latest .
save_code_hash
}
# 强制全部重建
if [ "$FORCE_REBUILD_ALL" = true ]; then
echo ">>> Force rebuilding everything..."
build_app
compose_up --force-recreate
docker image prune -f
echo ">>> Done!"
$DC ps
exit 0
fi
# 标记是否需要重启
NEED_RESTART=false
# 检查代码是否变化
if ! docker image inspect aether-app:latest >/dev/null 2>&1; then
echo ">>> App image not found, building..."
build_app
NEED_RESTART=true
elif check_code_changed; then
echo ">>> Code changed, rebuilding app image..."
build_app
NEED_RESTART=true
else
echo ">>> Code unchanged."
fi
# 检查容器是否在运行
CONTAINERS_RUNNING=true
if [ -z "$($DC ps -q 2>/dev/null)" ]; then
CONTAINERS_RUNNING=false
fi
# 有变化时重启,或容器未运行时启动
if [ "$NEED_RESTART" = true ]; then
echo ">>> Restarting services..."
compose_up
elif [ "$CONTAINERS_RUNNING" = false ]; then
echo ">>> Containers not running, starting services..."
compose_up
else
echo ">>> No changes detected, skipping restart."
fi
# 清理
docker image prune -f >/dev/null 2>&1 || true
echo ">>> Done!"
echo ">>> Note: empty databases auto-bootstrap on first start."
echo ">>> Note: docker compose now defaults to auto-running pending migrations/backfills on app startup."
echo ">>> Note: set AETHER_GATEWAY_AUTO_PREPARE_DATABASE=false if you want to keep manual rollout."
$DC ps

4
dev.sh
View File

@@ -95,13 +95,13 @@ dev_uses_redis_runtime() {
print_dev_infra_hint() { print_dev_infra_hint() {
echo "=> 本地开发依赖未就绪。" echo "=> 本地开发依赖未就绪。"
echo "=> 请先启动 Postgres / Redis:" echo "=> 请先启动 Postgres / Redis:"
echo "=> docker compose -f docker-compose.build.yml up -d postgres redis" echo "=> docker compose up -d postgres redis"
} }
print_postgres_hint() { print_postgres_hint() {
echo "=> PostgreSQL 未就绪。" echo "=> PostgreSQL 未就绪。"
echo "=> 请先启动 Postgres:" echo "=> 请先启动 Postgres:"
echo "=> docker compose -f docker-compose.build.yml up -d postgres" echo "=> docker compose up -d postgres"
} }
check_postgres_ready() { check_postgres_ready() {

View File

@@ -1,95 +0,0 @@
# Aether 部署配置 - 本地构建
# 使用方法:
# 启动服务: docker compose -f docker-compose.build.yml up -d --build
# 或使用: ./deploy.sh
services:
postgres:
image: postgres:15
container_name: aether-postgres
environment:
POSTGRES_DB: aether
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${DB_PASSWORD}
TZ: Asia/Shanghai
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "${DB_PORT:-5432}:5432"
healthcheck:
test: [ "CMD-SHELL", "pg_isready -U postgres" ]
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
redis:
image: redis:7-alpine
container_name: aether-redis
command: redis-server --appendonly yes --appendfsync everysec --save 60 1000 --requirepass ${REDIS_PASSWORD}
volumes:
- redis_data:/data
ports:
- "${REDIS_PORT:-6379}:6379"
healthcheck:
test: [ "CMD-SHELL", "redis-cli -a \"${REDIS_PASSWORD}\" ping | grep -q PONG" ]
interval: 5s
timeout: 3s
retries: 5
restart: unless-stopped
mysql:
image: mysql:8.0
container_name: aether-mysql
profiles:
- mysql
environment:
MYSQL_DATABASE: ${MYSQL_DATABASE:-aether}
MYSQL_USER: ${MYSQL_USER:-aether}
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-aether}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-aether_root}
TZ: Asia/Shanghai
volumes:
- mysql_data:/var/lib/mysql
ports:
- "${MYSQL_PORT:-3306}:3306"
healthcheck:
test:
[
"CMD-SHELL",
"mysqladmin ping -h 127.0.0.1 -u$${MYSQL_USER} -p$${MYSQL_PASSWORD} --silent"
]
interval: 5s
timeout: 5s
retries: 10
restart: unless-stopped
app:
build:
context: .
dockerfile: Dockerfile.app.local
image: aether-app:latest
container_name: aether-app
env_file:
- .env
environment:
DATABASE_URL: postgresql://postgres:${DB_PASSWORD}@postgres:5432/aether
REDIS_URL: redis://:${REDIS_PASSWORD}@redis:6379/0
TZ: Asia/Shanghai
APP_PORT: ${APP_PORT:-8084}
AETHER_GATEWAY_AUTO_PREPARE_DATABASE: ${AETHER_GATEWAY_AUTO_PREPARE_DATABASE:-true}
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
ports:
- "${APP_PORT:-8084}:${APP_PORT:-8084}"
volumes:
- ./logs:/app/logs
restart: unless-stopped
volumes:
postgres_data:
mysql_data:
redis_data:

View File

@@ -12,6 +12,8 @@ services:
TZ: Asia/Shanghai TZ: Asia/Shanghai
volumes: volumes:
- postgres_data:/var/lib/postgresql/data - postgres_data:/var/lib/postgresql/data
ports:
- "127.0.0.1:${DB_PORT:-5432}:5432"
command: postgres -c idle_in_transaction_session_timeout=30000 -c tcp_keepalives_idle=30 -c tcp_keepalives_interval=10 command: postgres -c idle_in_transaction_session_timeout=30000 -c tcp_keepalives_idle=30 -c tcp_keepalives_interval=10
healthcheck: healthcheck:
test: [ "CMD-SHELL", "pg_isready -U postgres" ] test: [ "CMD-SHELL", "pg_isready -U postgres" ]
@@ -26,6 +28,8 @@ services:
command: redis-server --appendonly yes --appendfsync everysec --save 60 1000 --requirepass ${REDIS_PASSWORD} command: redis-server --appendonly yes --appendfsync everysec --save 60 1000 --requirepass ${REDIS_PASSWORD}
volumes: volumes:
- redis_data:/data - redis_data:/data
ports:
- "127.0.0.1:${REDIS_PORT:-6379}:6379"
healthcheck: healthcheck:
test: [ "CMD-SHELL", "redis-cli -a \"${REDIS_PASSWORD}\" ping | grep -q PONG" ] test: [ "CMD-SHELL", "redis-cli -a \"${REDIS_PASSWORD}\" ping | grep -q PONG" ]
interval: 5s interval: 5s

View File

@@ -4,7 +4,6 @@ import {
Server, Server,
Key, Key,
Container, Container,
Code,
Shield, Shield,
Monitor, Monitor,
Check, Check,
@@ -44,38 +43,11 @@ const productionSteps = [
} }
] ]
const localBuildSteps = [
{
title: '克隆代码',
code: 'git clone https://github.com/fawney19/Aether.git\ncd Aether',
icon: Code
},
{
title: '配置环境变量',
note: '生成密钥并填入 .env',
code: 'cp .env.example .env\npython generate_keys.py',
icon: Key
},
{
title: '构建',
note: '自动构建、启动、迁移',
code: './deploy.sh',
icon: Container
},
{
title: '更新',
note: '需要拉取最新代码',
code: 'git pull origin main',
icon: Code,
optional: true
}
]
const developmentSteps = [ const developmentSteps = [
{ {
title: '启动依赖', title: '启动依赖',
note: 'PostgreSQL + Redis', note: 'PostgreSQL + Redis',
code: 'docker compose -f docker-compose.build.yml up -d postgres redis', code: 'docker compose up -d postgres redis',
icon: Container icon: Container
}, },
{ {
@@ -133,7 +105,6 @@ function copyStep(stepId: string, code: string) {
<button <button
v-for="(tab, idx) in [ v-for="(tab, idx) in [
{ icon: Container, label: 'Docker 预构建镜像' }, { icon: Container, label: 'Docker 预构建镜像' },
{ icon: Code, label: '本地代码构建' },
{ icon: Monitor, label: '本地开发' } { icon: Monitor, label: '本地开发' }
]" ]"
:key="idx" :key="idx"
@@ -200,58 +171,9 @@ function copyStep(stepId: string, code: string) {
</div> </div>
</div> </div>
<!-- 本地构建步骤 -->
<div
v-show="activeDeployTab === 1"
class="p-5 space-y-3"
>
<div
v-for="(step, idx) in localBuildSteps"
:key="idx"
class="group rounded-xl border border-[#e5e4df] dark:border-[rgba(227,224,211,0.12)] overflow-hidden transition-colors"
:class="step.optional ? 'border-dashed opacity-80' : ''"
>
<div class="flex items-center gap-3 px-4 py-3">
<span class="w-6 h-6 rounded-full flex items-center justify-center text-xs font-bold flex-shrink-0 bg-[#cc785c] text-white">
{{ idx + 1 }}
</span>
<div class="flex-1 min-w-0">
<div class="flex items-center gap-2">
<span class="text-sm font-medium text-[#262624] dark:text-[#f1ead8]">{{ step.title }}</span>
<span
v-if="step.optional"
class="text-[10px] px-1.5 py-0.5 rounded-full bg-[#e5e4df] dark:bg-[rgba(227,224,211,0.12)] text-[#666663] dark:text-[#a3a094]"
>
更新时
</span>
</div>
<span
v-if="step.note"
class="text-xs text-[#91918d] dark:text-[#a3a094]/80"
>{{ step.note }}</span>
</div>
<button
class="flex items-center gap-1.5 px-2.5 py-1 rounded-md text-xs text-[#666663] dark:text-[#a3a094] hover:bg-[#f0f0eb] dark:hover:bg-[#3a3731] transition-colors shrink-0"
@click="copyStep(`build-${idx}`, step.code)"
>
<Check
v-if="copiedStep === `build-${idx}`"
class="h-3.5 w-3.5 text-green-500"
/>
<Copy
v-else
class="h-3.5 w-3.5"
/>
{{ copiedStep === `build-${idx}` ? '已复制' : '复制' }}
</button>
</div>
<pre class="px-4 pb-3 text-[13px] font-mono text-[#262624] dark:text-[#f1ead8] overflow-x-auto leading-relaxed border-t border-[#e5e4df]/50 dark:border-[rgba(227,224,211,0.06)] pt-3 mx-4 mb-1"><code>{{ step.code }}</code></pre>
</div>
</div>
<!-- 开发环境步骤 --> <!-- 开发环境步骤 -->
<div <div
v-show="activeDeployTab === 2" v-show="activeDeployTab === 1"
class="p-5 space-y-3" class="p-5 space-y-3"
> >
<div <div

View File

@@ -20,28 +20,11 @@ docker compose pull && docker compose up -d
docker compose exec postgres pg_dump -U postgres aether | gzip > backup_$(date +%Y%m%d_%H%M%S).sql.gz docker compose exec postgres pg_dump -U postgres aether | gzip > backup_$(date +%Y%m%d_%H%M%S).sql.gz
``` ```
### 2. 本地代码构建镜像 (Docker Compose) ### 2. 本地开发
```markdown
# 1. 克隆代码
git clone https://github.com/fawney19/Aether.git
cd Aether
# 2. 配置环境变量
cp .env.example .env
./generate_keys.sh # 生成密钥, 并将生成的密钥填入 .env
# 3. 构建(自动构建、启动、迁移)
./deploy.sh
# 4. 更新需要拉取最新代码
git pull origin main
```
### 3. 本地开发
依赖 Docker、uv、nodejs 依赖 Docker、uv、nodejs
```markdown ```markdown
# 启动数据库 # 启动数据库
docker compose -f docker-compose.build.yml up -d postgres redis docker compose up -d postgres redis
# 后端 # 后端
uv sync uv sync

View File

@@ -49,9 +49,9 @@ Usage: install.sh [options]
Install Aether Gateway. Install Aether Gateway.
Options: Options:
--mode MODE Deployment mode: compose, compose-solo, single, or cluster --mode MODE Deployment mode: compose, compose-sqlite, single, or cluster
compose: Docker Compose app + Postgres + Redis compose: Docker Compose app + Postgres + Redis
compose-solo: Docker Compose app + SQLite compose-sqlite: Docker Compose app + SQLite
single: system service with SQLite + in-process runtime single: system service with SQLite + in-process runtime
cluster: system service connected to shared database + Redis cluster: system service connected to shared database + Redis
Linux services use systemd; macOS services use launchd Linux services use systemd; macOS services use launchd
@@ -436,8 +436,8 @@ select_mode() {
MODE="compose" MODE="compose"
return return
;; ;;
compose-solo|solo|docker-solo|docker-solo-compose) compose-sqlite|sqlite-compose|compose-solo|solo|docker-solo|docker-solo-compose)
MODE="compose-solo" MODE="compose-sqlite"
return return
;; ;;
single|service|systemd|launchd|sqlite) single|service|systemd|launchd|sqlite)
@@ -451,7 +451,7 @@ select_mode() {
auto|"") auto|"")
;; ;;
*) *)
die "unsupported install mode: ${MODE}; expected compose, compose-solo, single, or cluster" die "unsupported install mode: ${MODE}; expected compose, compose-sqlite, single, or cluster"
;; ;;
esac esac
@@ -494,7 +494,7 @@ EOF
MODE="cluster" MODE="cluster"
;; ;;
4) 4)
MODE="compose-solo" MODE="compose-sqlite"
;; ;;
*) *)
if ui_is_zh; then if ui_is_zh; then
@@ -1054,7 +1054,7 @@ generate_compose_env() {
replace_or_append_env "${output}" "AETHER_GATEWAY_AUTO_PREPARE_DATABASE" "true" replace_or_append_env "${output}" "AETHER_GATEWAY_AUTO_PREPARE_DATABASE" "true"
} }
generate_compose_solo_env() { generate_compose_sqlite_env() {
local output="$1" local output="$1"
local jwt_key encryption_key local jwt_key encryption_key
prompt_admin_password prompt_admin_password
@@ -1488,11 +1488,11 @@ Generate a fresh key set any time:
EOF EOF
} }
install_compose_solo_mode() { install_compose_sqlite_mode() {
info "preparing Docker Compose solo deployment in ${COMPOSE_DIR}" info "preparing Docker Compose SQLite deployment in ${COMPOSE_DIR}"
install -d -m 0755 "${COMPOSE_DIR}" "${COMPOSE_DIR}/logs" "${COMPOSE_DIR}/data" install -d -m 0755 "${COMPOSE_DIR}" "${COMPOSE_DIR}/logs" "${COMPOSE_DIR}/data"
install_project_file "docker-compose.solo.yml" "${COMPOSE_DIR}/docker-compose.yml" "0644" install_project_file "docker-compose.sqlite.yml" "${COMPOSE_DIR}/docker-compose.yml" "0644"
install_project_file ".env.example" "${COMPOSE_DIR}/.env.example" "0644" install_project_file ".env.example" "${COMPOSE_DIR}/.env.example" "0644"
write_generate_keys_script "${COMPOSE_DIR}/generate_keys.sh" write_generate_keys_script "${COMPOSE_DIR}/generate_keys.sh"
@@ -1500,13 +1500,13 @@ install_compose_solo_mode() {
warn "keeping existing ${COMPOSE_DIR}/.env" warn "keeping existing ${COMPOSE_DIR}/.env"
else else
info "generating ${COMPOSE_DIR}/.env" info "generating ${COMPOSE_DIR}/.env"
generate_compose_solo_env "${COMPOSE_DIR}/.env" generate_compose_sqlite_env "${COMPOSE_DIR}/.env"
chmod 0600 "${COMPOSE_DIR}/.env" chmod 0600 "${COMPOSE_DIR}/.env"
fi fi
cat <<EOF cat <<EOF
Docker Compose solo files are ready: Docker Compose SQLite files are ready:
${COMPOSE_DIR}/docker-compose.yml ${COMPOSE_DIR}/docker-compose.yml
${COMPOSE_DIR}/.env ${COMPOSE_DIR}/.env
${COMPOSE_DIR}/.env.example ${COMPOSE_DIR}/.env.example
@@ -1516,6 +1516,7 @@ Docker Compose solo files are ready:
Next steps: Next steps:
cd ${COMPOSE_DIR} cd ${COMPOSE_DIR}
docker compose pull
docker compose up -d docker compose up -d
docker compose logs -f app docker compose logs -f app
@@ -1941,8 +1942,8 @@ main() {
if [[ "${MODE}" == "compose" ]]; then if [[ "${MODE}" == "compose" ]]; then
install_compose_mode install_compose_mode
elif [[ "${MODE}" == "compose-solo" ]]; then elif [[ "${MODE}" == "compose-sqlite" ]]; then
install_compose_solo_mode install_compose_sqlite_mode
else else
require_root require_root
require_service_manager require_service_manager