fix(deploy): 服务器部署修复 — 全栈编译运行通过

在内网服务器 (192.168.1.222) 首次部署时发现并修复的所有问题,
涉及 backend (12微服务)、blockchain (Cosmos SDK节点)、前端 (admin-web)。

## Backend 修复

### docker-compose.yml
- DB_PASSWORD: 从硬编码 `genex_dev_password` 改为 `${DB_PASSWORD}` 环境变量插值,
  所有13个服务统一从 .env 读取,避免密码不一致导致连接失败
- Kong 镜像: `kong:3.5-alpine` → `kong:3.5`,alpine 版本在 amd64 上缺少依赖
- chain-indexer: 环境变量名 `CHAIN_RPC_URL` → `RPC_URL` (与 Go 代码 getEnv 一致)
- chain-indexer: RPC 地址改为 `http://172.17.0.1:8545` (Docker bridge gateway,
  跨 docker-compose network 访问 blockchain 节点的 EVM JSON-RPC)
- chain-indexer: 补全缺失的 DB_HOST/DB_PORT/DB_USERNAME/DB_PASSWORD/DB_NAME

### ai-service
- ai.module.ts: 移除重复的 TypeOrmModule.forRootAsync — 该模块错误地使用了
  DB_USER/DB_PASS (默认 'genex'/'genex') 和独立数据库 genex_ai,与 app.module.ts
  中已有的 TypeOrmModule.forRoot 冲突,导致密码认证失败
- app.module.ts: 添加 ConfigModule.forRoot({ isGlobal: true }),因为
  JwtModule.registerAsync 依赖 ConfigService 注入

### 依赖补全
- user-service/package.json: 添加 kafkajs ^2.2.4 (Kafka 事件消费)
- admin-service/package.json: 添加 kafkajs ^2.2.4 (Kafka 事件消费)

## Blockchain 修复

### genex-chain/app.go — 修复 chainConfig panic
- 问题: NewGenexApp 被调用两次 (tempApp 注册编码 + 实际启动),cosmos/evm 的
  SetChainConfig 在第二次调用时 panic: "chainConfig already set"
- 原因: 原代码 `if evmChainID == 0 { evmChainID = GenexEVMChainID }` 导致
  tempApp 也使用 8888,SetChainConfig 设置后,实际 app 再次设置时触发 panic
- 修复: 移除默认值回退,让 tempApp 使用 evmChainID=0 → DefaultEVMChainID(262144),
  实际 app 从 app.toml 读取 8888 后可正常覆盖

### genex-chain/cmd/genexd/cmd/root.go
- 移除未使用的 banktypes import (编译错误)

### docker-compose.yml
- 添加 `command: ["start", "--chain-id", "genex-testnet-1"]` 到 x-genex-node 默认配置,
  否则 InitChain 时 chain-id 为空导致校验失败

### init-genesis.sh (新增)
- 创世初始化脚本: init → 补丁 genesis.json → 创建验证者 → gentx → collect-gentxs
- 将所有默认 denom (aatom) 替换为 agnx (GNX, 18 decimals EVM 兼容)
- 配置: evm_denom, bond_denom, mint_denom, gov min_deposit, bank denom_metadata
- 启用 JSON-RPC (0.0.0.0:8545/8546)、REST API、CORS
- 设置 evm-chain-id=8888

## 部署结果
- 20 个 Docker 容器全部运行 (4 基础设施 + 12 后端 + 1 区块链节点 + 1 前端 + 2 辅助)
- 区块链稳定出块 (height 80+),EVM JSON-RPC 正常
- chain-indexer 实时索引区块 (lag=0)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-02-20 05:29:33 -08:00
parent 8e9ee1d38b
commit 0b1de382b0
9 changed files with 112 additions and 35 deletions

View File

@ -10,7 +10,7 @@ services:
container_name: genex-postgres
environment:
POSTGRES_USER: genex
POSTGRES_PASSWORD: genex_dev_password
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: genex
ports:
- "127.0.0.1:45432:5432"
@ -165,7 +165,7 @@ services:
# Kong API Gateway (DB-less / Declarative mode)
kong:
image: kong:3.5-alpine
image: kong:3.5
container_name: genex-kong
environment:
KONG_DATABASE: "off"
@ -207,7 +207,7 @@ services:
- DB_HOST=postgres
- DB_PORT=5432
- DB_USERNAME=genex
- DB_PASSWORD=genex_dev_password
- DB_PASSWORD=${DB_PASSWORD}
- DB_NAME=genex
- REDIS_HOST=redis
- REDIS_PORT=6379
@ -239,7 +239,7 @@ services:
- DB_HOST=postgres
- DB_PORT=5432
- DB_USERNAME=genex
- DB_PASSWORD=genex_dev_password
- DB_PASSWORD=${DB_PASSWORD}
- DB_NAME=genex
- REDIS_HOST=redis
- REDIS_PORT=6379
@ -268,7 +268,7 @@ services:
- DB_HOST=postgres
- DB_PORT=5432
- DB_USERNAME=genex
- DB_PASSWORD=genex_dev_password
- DB_PASSWORD=${DB_PASSWORD}
- DB_NAME=genex
- KAFKA_BROKERS=kafka:9092
- JWT_ACCESS_SECRET=dev-access-secret-change-in-production
@ -293,7 +293,7 @@ services:
- DB_HOST=postgres
- DB_PORT=5432
- DB_USERNAME=genex
- DB_PASSWORD=genex_dev_password
- DB_PASSWORD=${DB_PASSWORD}
- DB_NAME=genex
- KAFKA_BROKERS=kafka:9092
- JWT_ACCESS_SECRET=dev-access-secret-change-in-production
@ -318,7 +318,7 @@ services:
- DB_HOST=postgres
- DB_PORT=5432
- DB_USERNAME=genex
- DB_PASSWORD=genex_dev_password
- DB_PASSWORD=${DB_PASSWORD}
- DB_NAME=genex
- KAFKA_BROKERS=kafka:9092
- REDIS_HOST=redis
@ -347,7 +347,7 @@ services:
- DB_HOST=postgres
- DB_PORT=5432
- DB_USERNAME=genex
- DB_PASSWORD=genex_dev_password
- DB_PASSWORD=${DB_PASSWORD}
- DB_NAME=genex
- REDIS_HOST=redis
- REDIS_PORT=6379
@ -381,7 +381,7 @@ services:
- DB_HOST=postgres
- DB_PORT=5432
- DB_USERNAME=genex
- DB_PASSWORD=genex_dev_password
- DB_PASSWORD=${DB_PASSWORD}
- DB_NAME=genex
- MINIO_ENDPOINT=minio
- MINIO_PORT=9000
@ -413,7 +413,7 @@ services:
- DB_HOST=postgres
- DB_PORT=5432
- DB_USERNAME=genex
- DB_PASSWORD=genex_dev_password
- DB_PASSWORD=${DB_PASSWORD}
- DB_NAME=genex
- REDIS_HOST=redis
- REDIS_PORT=6379
@ -441,7 +441,7 @@ services:
- DB_HOST=postgres
- DB_PORT=5432
- DB_USERNAME=genex
- DB_PASSWORD=genex_dev_password
- DB_PASSWORD=${DB_PASSWORD}
- DB_NAME=genex
- REDIS_HOST=redis
- REDIS_PORT=6379
@ -464,7 +464,12 @@ services:
environment:
- PORT=3009
- KAFKA_BROKERS=kafka:9092
- CHAIN_RPC_URL=http://localhost:26657
- RPC_URL=http://172.17.0.1:8545
- DB_HOST=postgres
- DB_PORT=5432
- DB_USERNAME=genex
- DB_PASSWORD=${DB_PASSWORD}
- DB_NAME=genex
depends_on:
kafka:
condition: service_healthy
@ -489,7 +494,7 @@ services:
- DB_HOST=postgres
- DB_PORT=5432
- DB_USERNAME=genex
- DB_PASSWORD=genex_dev_password
- DB_PASSWORD=${DB_PASSWORD}
- DB_NAME=genex
- REDIS_HOST=redis
- REDIS_PORT=6379
@ -526,7 +531,7 @@ services:
- DB_HOST=postgres
- DB_PORT=5432
- DB_USERNAME=genex
- DB_PASSWORD=genex_dev_password
- DB_PASSWORD=${DB_PASSWORD}
- DB_NAME=genex
- KAFKA_BROKERS=kafka:9092
- REDIS_HOST=redis

View File

@ -29,7 +29,8 @@
"bplist-parser": "^0.3.2",
"minio": "^8.0.0",
"reflect-metadata": "^0.2.1",
"rxjs": "^7.8.1"
"rxjs": "^7.8.1",
"kafkajs": "^2.2.4"
},
"devDependencies": {
"@nestjs/cli": "^10.3.0",

View File

@ -30,21 +30,6 @@ import { AdminAgentController } from './interface/http/controllers/admin-agent.c
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
type: 'postgres' as const,
host: config.get<string>('DB_HOST', 'localhost'),
port: config.get<number>('DB_PORT', 5432),
username: config.get<string>('DB_USER', 'genex'),
password: config.get<string>('DB_PASS', 'genex'),
database: config.get<string>('DB_NAME', 'genex_ai'),
entities: [AiConversation],
synchronize: false,
}),
}),
TypeOrmModule.forFeature([AiConversation]),
PassportModule.register({ defaultStrategy: 'jwt' }),
JwtModule.registerAsync({

View File

@ -1,10 +1,12 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ThrottlerModule } from '@nestjs/throttler';
import { AiModule } from './ai.module';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
TypeOrmModule.forRoot({
type: 'postgres',
host: process.env.DB_HOST || 'localhost',

View File

@ -28,7 +28,8 @@
"pg": "^8.11.3",
"reflect-metadata": "^0.2.1",
"rxjs": "^7.8.1",
"typeorm": "^0.3.19"
"typeorm": "^0.3.19",
"kafkajs": "^2.2.4"
},
"devDependencies": {
"@nestjs/cli": "^10.3.0",

View File

@ -36,6 +36,7 @@ x-genex-node: &genex-node-defaults
build:
context: ./genex-chain
dockerfile: Dockerfile
command: ["start", "--chain-id", "genex-testnet-1"]
restart: unless-stopped
networks:
- genex-net

View File

@ -248,9 +248,10 @@ func NewGenexApp(
baseAppOptions ...func(*baseapp.BaseApp),
) *GenexApp {
evmChainID := cast.ToUint64(appOpts.Get(srvflags.EVMChainID))
if evmChainID == 0 {
evmChainID = GenexEVMChainID
}
// Note: don't default to GenexEVMChainID here.
// The real app gets 8888 from app.toml (set by evmconfig.InitAppConfig in root.go).
// tempApp (EmptyAppOptions) uses 0 → DefaultEVMChainID (262144), allowing
// the real app to later override the chain config via SetChainConfig.
encodingConfig := evmencoding.MakeConfig(evmChainID)
appCodec := encodingConfig.Codec

View File

@ -8,7 +8,6 @@ import (
"github.com/cosmos/evm/x/vm/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/spf13/cast"
"github.com/spf13/cobra"
"github.com/spf13/viper"

View File

@ -0,0 +1,82 @@
#!/bin/bash
set -e
CHAIN_ID="genex-testnet-1"
DENOM="agnx"
HOME_DIR="/home/genex/.genexd"
KEYRING="test"
GENESIS="$HOME_DIR/config/genesis.json"
echo "[1/6] Initializing chain..."
genexd init "genex-us-east-1" --chain-id "$CHAIN_ID" --home "$HOME_DIR"
echo "[2/6] Patching genesis for Genex denom (agnx)..."
# Replace default denoms with agnx
cat "$GENESIS" | jq '
# EVM module: set denom to agnx
.app_state.evm.params.evm_denom = "agnx" |
.app_state.evm.params.extended_denom_options.extended_denom = "agnx" |
# Staking: bond denom
.app_state.staking.params.bond_denom = "agnx" |
# Mint: mint denom
.app_state.mint.params.mint_denom = "agnx" |
# Gov: min deposit denom
.app_state.gov.params.min_deposit[0].denom = "agnx" |
.app_state.gov.params.expedited_min_deposit[0].denom = "agnx" |
# Bank: add denom metadata for agnx (18 decimals for EVM compat)
.app_state.bank.denom_metadata = [{
"description": "The native staking and gas token of Genex Chain",
"denom_units": [
{"denom": "agnx", "exponent": 0, "aliases": ["attoGNX"]},
{"denom": "GNX", "exponent": 18, "aliases": []}
],
"base": "agnx",
"display": "GNX",
"name": "Genex Token",
"symbol": "GNX"
}] |
# FeeMarket: use zero gas for dev
.app_state.feemarket.params.min_gas_price = "0.000000000000000000" |
.app_state.feemarket.params.base_fee = "1000000000.000000000000000000"
' > "${GENESIS}.tmp" && mv "${GENESIS}.tmp" "$GENESIS"
echo "[3/6] Creating validator key..."
genexd keys add validator --keyring-backend "$KEYRING" --home "$HOME_DIR" 2>&1
echo "[4/6] Adding genesis account..."
ADDR=$(genexd keys show validator -a --keyring-backend "$KEYRING" --home "$HOME_DIR")
genexd genesis add-genesis-account "$ADDR" "500000000000000000000000000${DENOM}" --home "$HOME_DIR" --keyring-backend "$KEYRING"
echo "[5/6] Creating genesis transaction..."
genexd genesis gentx validator "250000000000000000000000000${DENOM}" \
--chain-id "$CHAIN_ID" \
--moniker "genex-us-east-1" \
--commission-rate "0.10" \
--commission-max-rate "0.20" \
--commission-max-change-rate "0.01" \
--min-self-delegation "1" \
--keyring-backend "$KEYRING" \
--home "$HOME_DIR"
echo "[6/6] Collecting genesis transactions and validating..."
genexd genesis collect-gentxs --home "$HOME_DIR"
# Configure node
sed -i "s/minimum-gas-prices = \"\"/minimum-gas-prices = \"0${DENOM}\"/" "$HOME_DIR/config/app.toml"
# Enable API
sed -i "/\[api\]/,/\[/ s/enable = false/enable = true/" "$HOME_DIR/config/app.toml"
# Enable JSON-RPC
sed -i "/\[json-rpc\]/,/\[/ s/enable = false/enable = true/" "$HOME_DIR/config/app.toml"
sed -i "s/address = \"127.0.0.1:8545\"/address = \"0.0.0.0:8545\"/" "$HOME_DIR/config/app.toml"
sed -i "s/ws-address = \"127.0.0.1:8546\"/ws-address = \"0.0.0.0:8546\"/" "$HOME_DIR/config/app.toml"
# CORS
sed -i "s/cors_allowed_origins = \[\]/cors_allowed_origins = [\"*\"]/" "$HOME_DIR/config/config.toml"
sed -i "s/laddr = \"tcp:\/\/127.0.0.1:26657\"/laddr = \"tcp:\/\/0.0.0.0:26657\"/" "$HOME_DIR/config/config.toml"
# Set EVM chain ID
sed -i "s/evm-chain-id = 262144/evm-chain-id = 8888/" "$HOME_DIR/config/app.toml" 2>/dev/null || true
echo ""
echo "Genesis initialization complete!"
echo "Chain ID: $CHAIN_ID"
echo "Validator: $ADDR"
echo "EVM Chain ID: 8888"
echo "Denom: $DENOM (GNX, 18 decimals)"