furniture-3d-system-Wu/scripts/create-wyj-test-deploy.ps1

260 lines
8.6 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

param(
[string]$ProjectRoot = "D:\AI Code\3danzhuang\furniture-3d-system",
[string]$OutputRoot = "D:\AI Code\3danzhuang"
)
$ErrorActionPreference = 'Stop'
if (-not (Test-Path -LiteralPath $ProjectRoot)) {
throw "项目目录不存在: $ProjectRoot"
}
function Get-EnvValue {
param(
[string]$Path,
[string]$Key
)
if (-not (Test-Path -LiteralPath $Path)) {
return $null
}
$line = Get-Content -LiteralPath $Path | Where-Object { $_ -match "^$([regex]::Escape($Key))=" } | Select-Object -First 1
if (-not $line) {
return $null
}
$value = $line.Substring($line.IndexOf('=') + 1).Trim()
if ($value.Length -ge 2) {
if (($value.StartsWith('"') -and $value.EndsWith('"')) -or ($value.StartsWith("'") -and $value.EndsWith("'"))) {
$value = $value.Substring(1, $value.Length - 2)
}
}
return $value
}
$localEnvPath = Join-Path $ProjectRoot '.env'
$jwtSecret = Get-EnvValue -Path $localEnvPath -Key 'JWT_SECRET'
if ([string]::IsNullOrWhiteSpace($jwtSecret) -or $jwtSecret.Length -lt 32) {
$jwtSecret = 'wyj-test-jwt-secret-20260410-solidjoin-3d'
}
$nextAuthSecret = Get-EnvValue -Path $localEnvPath -Key 'NEXTAUTH_SECRET'
if ([string]::IsNullOrWhiteSpace($nextAuthSecret)) {
$nextAuthSecret = $jwtSecret
}
$timestamp = Get-Date -Format 'yyyyMMdd-HHmmss'
$archiveName = "deploy-furniture-wyj-test-$timestamp.tar.gz"
$scriptName = "deploy-furniture-wyj-test-$timestamp.sh"
$archivePath = Join-Path $OutputRoot $archiveName
$scriptPath = Join-Path $OutputRoot $scriptName
$includePaths = @(
'furniture-3d-system/package.json'
'furniture-3d-system/package-lock.json'
'furniture-3d-system/.npmrc'
'furniture-3d-system/next.config.js'
'furniture-3d-system/postcss.config.mjs'
'furniture-3d-system/tsconfig.json'
'furniture-3d-system/next-env.d.ts'
'furniture-3d-system/prisma.config.ts'
'furniture-3d-system/src'
'furniture-3d-system/public'
'furniture-3d-system/prisma'
'furniture-3d-system/scripts'
'furniture-3d-system/lib'
)
Push-Location (Split-Path $ProjectRoot -Parent)
try {
if (Test-Path -LiteralPath $archivePath) {
Remove-Item -LiteralPath $archivePath -Force
}
tar -czf $archivePath @includePaths
} finally {
Pop-Location
}
$shellScript = @'
set -e
BASE=/root/codex-workspaces/furniture-3d-system-wyj-test
APP=$BASE/furniture-3d-system
ARCHIVE=/root/codex-workspaces/__ARCHIVE_NAME__
DB_NAME=furniture3d_wyj_test
DB_USER=furniture3d_wyj_test
DB_PASS='F3dWyjTest_3200_20260407'
APP_PORT=3200
BUCKET=furniture-3d-models-wyj-test
MINIO_USER=furniture3dtest
MINIO_PASS='F3dMinio_39100_20260331'
S3_ENDPOINT='http://127.0.0.1:39100'
S3_PUBLIC_ENDPOINT='http://192.168.42.134:39100'
mkdir -p $BASE
mkdir -p $APP
OLD_LOCK_HASH=""
if [ -f "$APP/package-lock.json" ]; then
OLD_LOCK_HASH=$(sha256sum "$APP/package-lock.json" | awk '{print $1}')
fi
rm -rf \
$APP/src \
$APP/public \
$APP/prisma \
$APP/scripts \
$APP/lib \
$APP/.next
rm -f \
$APP/package.json \
$APP/package-lock.json \
$APP/.npmrc \
$APP/next.config.js \
$APP/postcss.config.mjs \
$APP/tsconfig.json \
$APP/next-env.d.ts \
$APP/prisma.config.ts
cd $BASE
tar -xzf $ARCHIVE
NEW_LOCK_HASH=""
if [ -f "$APP/package-lock.json" ]; then
NEW_LOCK_HASH=$(sha256sum "$APP/package-lock.json" | awk '{print $1}')
fi
sudo -u postgres psql -d postgres <<SQL
DO \$\$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '$DB_USER') THEN
CREATE ROLE $DB_USER LOGIN PASSWORD '$DB_PASS';
END IF;
END
\$\$;
SQL
if ! sudo -u postgres psql -d postgres -Atqc "SELECT 1 FROM pg_database WHERE datname='$DB_NAME'" | grep -q 1; then
sudo -u postgres createdb -O $DB_USER $DB_NAME
sudo -u postgres pg_dump -d furniture3d_test --no-owner --no-privileges | sudo -u postgres psql -d $DB_NAME
fi
sudo -u postgres psql -d postgres <<SQL
GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;
ALTER DATABASE $DB_NAME OWNER TO $DB_USER;
SQL
sudo -u postgres psql -d $DB_NAME <<SQL
GRANT ALL PRIVILEGES ON SCHEMA public TO $DB_USER;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO $DB_USER;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO $DB_USER;
GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public TO $DB_USER;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO $DB_USER;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO $DB_USER;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON FUNCTIONS TO $DB_USER;
SQL
cp /root/codex-workspaces/furniture-3d-system-test/.env $BASE/.env
python3 - <<PY
from pathlib import Path
p = Path('$BASE/.env')
lines = p.read_text().splitlines()
repl = {
'DATABASE_URL': '"postgresql://$DB_USER:$DB_PASS@127.0.0.1:5432/$DB_NAME"',
'DIRECT_URL': '"postgresql://$DB_USER:$DB_PASS@127.0.0.1:5432/$DB_NAME"',
'NEXTAUTH_URL': 'http://192.168.42.134:$APP_PORT',
'NEXTAUTH_SECRET': '"__NEXTAUTH_SECRET__"',
'NEXT_PUBLIC_APP_URL': 'http://192.168.42.134:$APP_PORT',
'S3_ENDPOINT': '"$S3_ENDPOINT"',
'S3_PUBLIC_ENDPOINT': '"$S3_PUBLIC_ENDPOINT"',
'S3_PUBLIC_URL': '"$S3_PUBLIC_ENDPOINT"',
'S3_ACCESS_KEY': '"$MINIO_USER"',
'S3_SECRET_KEY': '"$MINIO_PASS"',
'S3_REGION': '"us-east-1"',
'S3_BUCKET': '"$BUCKET"',
'JWT_SECRET': '"__JWT_SECRET__"',
}
out=[]
seen=set()
for line in lines:
if '=' in line and not line.lstrip().startswith('#'):
k,_ = line.split('=',1)
if k in repl:
out.append(f'{k}={repl[k]}')
seen.add(k)
else:
out.append(line)
else:
out.append(line)
for k,v in repl.items():
if k not in seen:
out.append(f'{k}={v}')
p.write_text('\n'.join(out)+'\n')
PY
ln -sfn $BASE/.env $APP/.env
docker exec furniture-3d-system-test-minio sh -lc "mc alias set local http://127.0.0.1:9000 $MINIO_USER $MINIO_PASS >/dev/null && mc mb -p local/$BUCKET >/dev/null 2>&1 || true && mc anonymous set download local/$BUCKET >/dev/null"
cd $APP
export PUPPETEER_SKIP_DOWNLOAD=true
if [ ! -d "$APP/node_modules" ] || [ "$OLD_LOCK_HASH" != "$NEW_LOCK_HASH" ]; then
echo '=== 安装或更新依赖 ==='
npm install --no-audit --no-fund
else
echo '=== 复用现有依赖(package-lock.json 未变化) ==='
fi
if find /root/.cache/puppeteer -type f -name chrome-headless-shell 2>/dev/null | grep -q .; then
echo '=== 复用现有 chrome-headless-shell ==='
else
echo '=== 安装 chrome-headless-shell ==='
PUPPETEER_CACHE_DIR=/root/.cache/puppeteer npx puppeteer browsers install chrome-headless-shell@stable
fi
sudo -u postgres psql -d $DB_NAME <<SQL
ALTER TABLE "User"
ADD COLUMN IF NOT EXISTS "loginSource" TEXT NOT NULL DEFAULT 'LOCAL',
ADD COLUMN IF NOT EXISTS "dingtalkUserId" TEXT,
ADD COLUMN IF NOT EXISTS "dingtalkUnionId" TEXT,
ADD COLUMN IF NOT EXISTS "dingtalkBoundAt" TIMESTAMP(3);
CREATE UNIQUE INDEX IF NOT EXISTS "User_dingtalkUserId_key" ON "User"("dingtalkUserId");
CREATE UNIQUE INDEX IF NOT EXISTS "User_dingtalkUnionId_key" ON "User"("dingtalkUnionId");
CREATE INDEX IF NOT EXISTS "User_loginSource_idx" ON "User"("loginSource");
SQL
npx prisma generate
PRISMA_SCHEMA_SYNC_SQL=/tmp/furniture-3d-system-wyj-test-prisma-schema-sync.sql
npx prisma migrate diff --from-config-datasource --to-schema prisma/schema.prisma --script -o "$PRISMA_SCHEMA_SYNC_SQL"
if [ -s "$PRISMA_SCHEMA_SYNC_SQL" ]; then
echo '=== 同步 Prisma 数据结构差异 ==='
sudo -u postgres psql -v ON_ERROR_STOP=1 -d $DB_NAME -f "$PRISMA_SCHEMA_SYNC_SQL"
else
echo '=== Prisma 数据结构已是最新 ==='
fi
npm run build
pm2 delete furniture-3d-system-wyj-test >/dev/null 2>&1 || true
pm2 start /bin/bash --name furniture-3d-system-wyj-test --cwd $APP -- -lc "set -a; source ./.env; set +a; mkdir -p .next/standalone/.next; rm -rf .next/standalone/.next/static .next/standalone/public; cp -R .next/static .next/standalone/.next/static; cp -R public .next/standalone/public; exec env PORT=$APP_PORT HOSTNAME=0.0.0.0 node .next/standalone/server.js"
pm2 save >/dev/null
sleep 5
curl -I -s http://127.0.0.1:$APP_PORT | sed -n '1,12p'
ls -1dt /root/codex-workspaces/deploy-furniture-wyj-test-*.tar.gz 2>/dev/null | tail -n +4 | xargs -r rm -f
echo '=== DONE ==='
pm2 describe furniture-3d-system-wyj-test | sed -n '1,35p'
'@
$shellScript = $shellScript.Replace('__ARCHIVE_NAME__', $archiveName)
$shellScript = $shellScript.Replace('__JWT_SECRET__', $jwtSecret)
$shellScript = $shellScript.Replace('__NEXTAUTH_SECRET__', $nextAuthSecret)
[System.IO.File]::WriteAllText($scriptPath, $shellScript, [System.Text.UTF8Encoding]::new($false))
$archive = Get-Item -LiteralPath $archivePath
[PSCustomObject]@{
Archive = $archive.FullName
ArchiveMB = [math]::Round($archive.Length / 1MB, 2)
Script = $scriptPath
IncludedPaths = $includePaths -join ', '
}