feat(setup): add idempotent PostgreSQL bootstrap script for first install

Provide a Windows init script to create/repair the netx role and database with a connection check, and document the step in README so new machines avoid initial auth failures.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-05-06 16:47:12 +08:00
parent d48cd000d3
commit 32bfa78383
2 changed files with 120 additions and 5 deletions

View file

@ -37,7 +37,29 @@ If Node/npm is missing (Windows example):
winget install OpenJS.NodeJS.LTS winget install OpenJS.NodeJS.LTS
``` ```
### 1) Python virtual environment and backend deps ### 1) Initialize PostgreSQL role/database (Windows, recommended)
For first-time setup on a new machine, run:
```powershell
cd netx
powershell -ExecutionPolicy Bypass -File .\scripts\init_pg.ps1
```
The script is idempotent: it creates/repairs role `netx`, database `netx`, grants privileges, and verifies connection.
Common options:
```powershell
powershell -ExecutionPolicy Bypass -File .\scripts\init_pg.ps1 `
-SuperUser postgres `
-SuperPassword "your-postgres-password" `
-NetxUser netx `
-NetxPassword "your-netx-password" `
-NetxDatabase netx
```
### 2) Python virtual environment and backend deps
```powershell ```powershell
cd netx cd netx
@ -46,7 +68,7 @@ python -m venv .venv
.\.venv\Scripts\python -m pip install -r requirements.txt .\.venv\Scripts\python -m pip install -r requirements.txt
``` ```
### 2) Frontend deps (npm) ### 3) Frontend deps (npm)
```powershell ```powershell
cd .\web cd .\web
@ -54,7 +76,7 @@ npm install
cd .. cd ..
``` ```
### 3) Configure environment variables ### 4) Configure environment variables
Option A: temporary env vars in current shell Option A: temporary env vars in current shell
@ -72,7 +94,7 @@ NETX_OCLAW_ANALYZE_TOKEN=admin123
NETX_OCLAW_HEALTH_URL=http://127.0.0.1:8787/admin/api/ops-ai/health NETX_OCLAW_HEALTH_URL=http://127.0.0.1:8787/admin/api/ops-ai/health
``` ```
### 4) Start services ### 5) Start services
Direct backend start: Direct backend start:
@ -103,7 +125,7 @@ powershell -ExecutionPolicy Bypass -File .\scripts\stop_netx.ps1 -Force
Primary web UI (Vite): `http://127.0.0.1:5173/` Primary web UI (Vite): `http://127.0.0.1:5173/`
API base: `http://127.0.0.1:8890/` API base: `http://127.0.0.1:8890/`
### 5) Optional: MCP server (for oclaw integration) ### 6) Optional: MCP server (for oclaw integration)
```powershell ```powershell
.\.venv\Scripts\python -m netx_api.mcp_server .\.venv\Scripts\python -m netx_api.mcp_server

93
scripts/init_pg.ps1 Normal file
View file

@ -0,0 +1,93 @@
param(
[string]$PgHost = "127.0.0.1",
[int]$PgPort = 5432,
[string]$SuperUser = "postgres",
[string]$SuperPassword = "",
[string]$NetxUser = "netx",
[string]$NetxPassword = "netx",
[string]$NetxDatabase = "netx",
[switch]$SkipConnectionTest = $false
)
$ErrorActionPreference = "Stop"
function Invoke-Psql {
param(
[string]$Database,
[string]$Sql
)
& psql -h $PgHost -p $PgPort -U $SuperUser -d $Database -v ON_ERROR_STOP=1 -c $Sql
if ($LASTEXITCODE -ne 0) {
throw "psql_failed (db=$Database)"
}
}
if (-not (Get-Command psql -ErrorAction SilentlyContinue)) {
throw "psql_not_found: please install PostgreSQL client tools and ensure psql is in PATH"
}
if (-not $SuperPassword) {
$sec = Read-Host -Prompt "PostgreSQL superuser password for '$SuperUser'" -AsSecureString
$bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($sec)
try {
$SuperPassword = [Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)
} finally {
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
}
}
if (-not $SuperPassword) {
throw "superuser_password_required"
}
$env:PGPASSWORD = $SuperPassword
try {
Write-Host "==> Ensuring role '$NetxUser'"
$roleSql = @"
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '$NetxUser') THEN
CREATE ROLE $NetxUser LOGIN PASSWORD '$NetxPassword';
ELSE
ALTER ROLE $NetxUser WITH LOGIN PASSWORD '$NetxPassword';
END IF;
END
$$;
"@
Invoke-Psql -Database "postgres" -Sql $roleSql
Write-Host "==> Ensuring database '$NetxDatabase'"
$dbExists = & psql -h $PgHost -p $PgPort -U $SuperUser -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname='$NetxDatabase'"
if ($LASTEXITCODE -ne 0) {
throw "psql_failed (query database exists)"
}
if (-not ($dbExists -match "1")) {
Invoke-Psql -Database "postgres" -Sql "CREATE DATABASE $NetxDatabase OWNER $NetxUser;"
} else {
Write-Host "Database already exists: $NetxDatabase"
}
Write-Host "==> Granting database privileges"
Invoke-Psql -Database "postgres" -Sql "GRANT ALL PRIVILEGES ON DATABASE $NetxDatabase TO $NetxUser;"
} finally {
Remove-Item Env:PGPASSWORD -ErrorAction SilentlyContinue
}
if (-not $SkipConnectionTest) {
Write-Host "==> Verifying netx connection"
$env:PGPASSWORD = $NetxPassword
try {
& psql -h $PgHost -p $PgPort -U $NetxUser -d $NetxDatabase -c "select current_user, current_database();"
if ($LASTEXITCODE -ne 0) {
throw "netx_connection_test_failed"
}
} finally {
Remove-Item Env:PGPASSWORD -ErrorAction SilentlyContinue
}
}
Write-Host ""
Write-Host "Done."
Write-Host "Use this DATABASE URL in netx:"
Write-Host "postgresql+psycopg://$NetxUser:$NetxPassword@$PgHost`:$PgPort/$NetxDatabase"