# Finish empty DBs on APP SERVER, expose via intranet 10.206.0.15 (not 127.0.0.1). # Cannot CREATE on 10.206.0.14 without DBA. Document as interim. # Never print secrets. $ErrorActionPreference = "Stop" $mysql = "C:\yutongda\tools\mysql-8.0.39-winx64\bin\mysql.exe" $psql = "C:\yutongda\tools\pgsql\bin\psql.exe" $pgData = "C:\yutongda\data\pgsql" # may vary; detect $ops = "E:\wwwroot\ops" $sec = Get-Content "$ops\inquiry_robot_db_secrets.json" -Raw | ConvertFrom-Json $mysqlPass = [string]$sec.mysql_password $pgPass = [string]$sec.pg_password $intranet = (Get-NetIPAddress -AddressFamily IPv4 | Where-Object { $_.IPAddress -like '10.*' } | Select-Object -First 1 -ExpandProperty IPAddress) if (-not $intranet) { throw "no 10.x intranet IP" } Write-Output ("intranet=" + $intranet) function Mysql-Ok([string]$h, [string]$u, [string]$p, [string]$db) { $a = @("-h$h","-P3306","-u$u","-p$p","--connect-timeout=8","--batch","-N") if ($db) { $a += $db } $a += @("-e","SELECT 1") $out = & $mysql @a 2>&1 $text = ($out | ForEach-Object {"$_"}) -join "`n" if ($text -match "ERROR") { return $false } $lines = $out | ForEach-Object {"$_".Trim()} | Where-Object { $_ -ne "" -and $_ -notmatch "Warning" } return ($lines -contains "1") } # Resolve root password $rootPass = $null foreach ($f in @("$ops\local_mysql_root_secret.json", "$ops\.mysql_root_stash.tmp")) { if (-not (Test-Path $f)) { continue } if ($f -match '\.json$') { $rootPass = [string](Get-Content $f -Raw | ConvertFrom-Json).root_password } else { $rootPass = (Get-Content $f -Raw).Trim() } if ($rootPass -and (Mysql-Ok "127.0.0.1" "root" $rootPass $null)) { Write-Output ("mysql_root_ok via=" + $f + " len=" + $rootPass.Length) break } $rootPass = $null } if (-not $rootPass) { # try skip-grant again briefly if service allows stop Write-Output "mysql_root_missing; will re-run maintenance create" } Write-Output "=== MySQL: ensure inquiry_robot ===" if ($rootPass) { $sql = @" CREATE DATABASE IF NOT EXISTS ``inquiry_robot`` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER IF NOT EXISTS 'inquiry_robot'@'localhost' IDENTIFIED BY '$mysqlPass'; CREATE USER IF NOT EXISTS 'inquiry_robot'@'127.0.0.1' IDENTIFIED BY '$mysqlPass'; CREATE USER IF NOT EXISTS 'inquiry_robot'@'%' IDENTIFIED BY '$mysqlPass'; CREATE USER IF NOT EXISTS 'inquiry_robot'@'$intranet' IDENTIFIED BY '$mysqlPass'; ALTER USER 'inquiry_robot'@'localhost' IDENTIFIED BY '$mysqlPass'; ALTER USER 'inquiry_robot'@'127.0.0.1' IDENTIFIED BY '$mysqlPass'; ALTER USER 'inquiry_robot'@'%' IDENTIFIED BY '$mysqlPass'; GRANT ALL PRIVILEGES ON ``inquiry_robot``.* TO 'inquiry_robot'@'localhost'; GRANT ALL PRIVILEGES ON ``inquiry_robot``.* TO 'inquiry_robot'@'127.0.0.1'; GRANT ALL PRIVILEGES ON ``inquiry_robot``.* TO 'inquiry_robot'@'%'; GRANT ALL PRIVILEGES ON ``inquiry_robot``.* TO 'inquiry_robot'@'$intranet'; FLUSH PRIVILEGES; SELECT 'mysql_ok' AS s; "@ $sqlPath = "$ops\sql\_run_ir_mysql.sql" Set-Content $sqlPath -Value $sql -Encoding ascii $o = & $mysql -h127.0.0.1 -P3306 -uroot "-p$rootPass" -e "source $($sqlPath -replace '\\','/')" 2>&1 | Out-String if ($o -notmatch "mysql_ok") { Write-Output $o; throw "mysql create failed" } Remove-Item $sqlPath -Force -EA SilentlyContinue } else { throw "no mysql root; cannot finish MySQL empty DB on app server" } # Smoke via intranet IP if (-not (Mysql-Ok $intranet "inquiry_robot" $mysqlPass "inquiry_robot")) { # bind-address may block; check my.ini Write-Output "intranet mysql smoke failed; checking bind-address" Select-String -Path "C:\yutongda\tools\mysql-8.0.39-winx64\my.ini" -Pattern "bind-address|skip-networking" | ForEach-Object { $_.Line } # retry 127 if (-not (Mysql-Ok "127.0.0.1" "inquiry_robot" $mysqlPass "inquiry_robot")) { throw "mysql app smoke failed even on 127.0.0.1" } Write-Output "mysql_ok_127_only" } else { Write-Output "mysql_ok_intranet" } Write-Output "=== PG: ensure listen on intranet + role/db ===" $env:PGPASSWORD = (Get-Content "C:\yutongda\secure\pgpass.txt" -Raw).Trim() # find data dir $pgConfCandidates = @( "C:\yutongda\data\pgsql\postgresql.conf", "C:\yutongda\tools\pgsql\data\postgresql.conf", "C:\yutongda\data\postgresql\postgresql.conf" ) $pgConf = $pgConfCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1 if (-not $pgConf) { # ask running postgres $dd = & $psql -h 127.0.0.1 -p 5432 -U postgres -d postgres -tAc "SHOW data_directory;" 2>&1 $dd = ($dd | ForEach-Object {"$_"}).Trim() Write-Output ("pg_data_directory=" + $dd) if ($dd -and (Test-Path (Join-Path $dd "postgresql.conf"))) { $pgConf = Join-Path $dd "postgresql.conf" } } if (-not $pgConf) { throw "postgresql.conf not found" } Write-Output ("pgConf=" + $pgConf) $pgHba = Join-Path (Split-Path $pgConf -Parent) "pg_hba.conf" # ensure listen_addresses includes intranet or * $confText = Get-Content $pgConf -Raw if ($confText -notmatch "(?m)^\s*listen_addresses\s*=\s*'[^']*${intranet}" -and $confText -notmatch "(?m)^\s*listen_addresses\s*=\s*'\*'") { if ($confText -match "(?m)^\s*#?\s*listen_addresses\s*=") { $confText = [regex]::Replace($confText, "(?m)^\s*#?\s*listen_addresses\s*=\s*.*$", "listen_addresses = '*' # inquiry_robot interim", 1) } else { $confText += "`r`nlisten_addresses = '*'`r`n" } Set-Content $pgConf -Value $confText -Encoding UTF8 Write-Output "pg_listen_patched" } else { Write-Output "pg_listen_ok" } # ensure hba allows intranet md5/scram $hba = Get-Content $pgHba $need = "host inquiry_robot_runtime inquiry_robot_runtime 10.206.0.0/16 scram-sha-256" if (-not ($hba -match "inquiry_robot_runtime")) { Add-Content $pgHba $need Add-Content $pgHba "host inquiry_robot_runtime inquiry_robot_runtime 127.0.0.1/32 scram-sha-256" Write-Output "pg_hba_patched" } else { Write-Output "pg_hba_ok" } # create role/db if missing & $psql -h 127.0.0.1 -p 5432 -U postgres -d postgres -v ON_ERROR_STOP=1 -c @" DO `$`$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'inquiry_robot_runtime') THEN CREATE ROLE inquiry_robot_runtime LOGIN PASSWORD '$pgPass'; ELSE ALTER ROLE inquiry_robot_runtime WITH LOGIN PASSWORD '$pgPass'; END IF; END `$`$; SELECT 'role_ok'; "@ 2>&1 | ForEach-Object { if ($_ -notmatch $pgPass) { "$_" } } $exists = & $psql -h 127.0.0.1 -p 5432 -U postgres -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname='inquiry_robot_runtime'" 2>&1 if (($exists | Out-String) -notmatch "1") { & $psql -h 127.0.0.1 -p 5432 -U postgres -d postgres -c "CREATE DATABASE inquiry_robot_runtime OWNER inquiry_robot_runtime;" 2>&1 | ForEach-Object { "$_" } } & $psql -h 127.0.0.1 -p 5432 -U postgres -d inquiry_robot_runtime -c "GRANT ALL ON SCHEMA public TO inquiry_robot_runtime;" 2>&1 | Out-Null # reload or restart PG $pgSvc = Get-Service *pgsql*,*postgres*,YTD-PG*,YTD-Postgres* -EA SilentlyContinue Write-Output ("pg_services=" + (($pgSvc | ForEach-Object { $_.Name + ":" + $_.Status }) -join ",")) try { & "$($psql -replace 'psql.exe','pg_ctl.exe')" reload -D (Split-Path $pgConf -Parent) 2>&1 | Out-Null } catch {} # prefer reload via SQL & $psql -h 127.0.0.1 -p 5432 -U postgres -d postgres -c "SELECT pg_reload_conf();" 2>&1 | Out-Null # If listen_addresses changed, need restart $listen = & $psql -h 127.0.0.1 -p 5432 -U postgres -d postgres -tAc "SHOW listen_addresses;" 2>&1 Write-Output ("listen_now=" + (($listen | ForEach-Object {"$_"}).Trim())) if ((($listen | Out-String) -notmatch "\*|${intranet}")) { Write-Output "restarting postgres for listen_addresses" if ($pgSvc) { Restart-Service $pgSvc[0].Name -Force Start-Sleep -Seconds 5 } } $env:PGPASSWORD = $pgPass $pgSmokeLocal = & $psql -h 127.0.0.1 -p 5432 -U inquiry_robot_runtime -d inquiry_robot_runtime -tAc "SELECT 'pg_ok'" 2>&1 Write-Output ("pg_local=" + (($pgSmokeLocal | ForEach-Object {"$_"}) -join " ")) $pgSmokeNet = & $psql -h $intranet -p 5432 -U inquiry_robot_runtime -d inquiry_robot_runtime -tAc "SELECT 'pg_ok'" 2>&1 Write-Output ("pg_intranet=" + (($pgSmokeNet | ForEach-Object {"$_"}) -join " ")) Remove-Item Env:PGPASSWORD -EA SilentlyContinue Write-Output "=== patch .env to intranet app-server IP ===" $envPath = "E:\wwwroot\.env" $jdbc = "jdbc:mysql://${intranet}:3306/inquiry_robot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&allowPublicKeyRetrieval=true&tinyInt1isBit=false&serverTimezone=Asia/Shanghai" $lines = Get-Content $envPath $out = foreach ($line in $lines) { if ($line -match '^MYSQL_HOST=') { "MYSQL_HOST=$intranet" } elseif ($line -match '^PG_HOST=') { "PG_HOST=$intranet" } elseif ($line -match '^YTD_MYSQL_URL=') { "YTD_MYSQL_URL=$jdbc" } elseif ($line -match '^MYSQL_PASSWORD=') { "MYSQL_PASSWORD=$mysqlPass" } elseif ($line -match '^PG_PASSWORD=') { "PG_PASSWORD=$pgPass" } else { $line } } Set-Content $envPath -Value $out -Encoding UTF8 Copy-Item $envPath "E:\wwwroot\agent\current\.env" -Force -EA SilentlyContinue # cleanup maint sql with secrets if present Remove-Item "$ops\sql\_maint_create_inquiry_robot.sql" -Force -EA SilentlyContinue Remove-Item "$ops\sql\_run_create_inquiry_robot_mysql.sql" -Force -EA SilentlyContinue $status = @{ mysqlHost = $intranet pgHost = $intranet mysqlDb = "inquiry_robot" pgDb = "inquiry_robot_runtime" mysqlCreated = $true pgCreated = $true placement = "app-server-local-via-intranet-ip" note = "Interim: empty DBs on app server 10.206.0.15 local mysqld/postgres, apps connect via intranet IP (not 127.0.0.1). Target later: migrate to data host 10.206.0.14 when DBA available. Do not use jeecg-boot/ytd_runtime." needDbaForDataHost14 = $true updatedAt = (Get-Date).ToString("o") } | ConvertTo-Json Set-Content "$ops\data-plane-status.json" -Value $status -Encoding UTF8 Write-Output $status # final smoke summary Write-Output ("FINAL mysql@" + $intranet + "=" + (Mysql-Ok $intranet "inquiry_robot" $mysqlPass "inquiry_robot")) Write-Output ("FINAL mysql@127=" + (Mysql-Ok "127.0.0.1" "inquiry_robot" $mysqlPass "inquiry_robot")) Write-Output "CREATE_INTERIM_DONE"