furniture-3d-system-Wu/scripts/create-wyj-test-deploy.ps1
2026-04-11 15:13:26 +08:00

211 lines
7.2 KiB
PowerShell

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/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
rm -rf $APP
cd $BASE
tar -xzf $ARCHIVE
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
npm install --no-audit --no-fund
PUPPETEER_CACHE_DIR=/root/.cache/puppeteer npx puppeteer browsers install chrome-headless-shell@stable
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
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 ', '
}