diff --git a/README.md b/README.md index c27c408..c48ad17 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,29 @@ If Node/npm is missing (Windows example): 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 cd netx @@ -46,7 +68,7 @@ python -m venv .venv .\.venv\Scripts\python -m pip install -r requirements.txt ``` -### 2) Frontend deps (npm) +### 3) Frontend deps (npm) ```powershell cd .\web @@ -54,7 +76,7 @@ npm install cd .. ``` -### 3) Configure environment variables +### 4) Configure environment variables 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 ``` -### 4) Start services +### 5) Start services 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/` API base: `http://127.0.0.1:8890/` -### 5) Optional: MCP server (for oclaw integration) +### 6) Optional: MCP server (for oclaw integration) ```powershell .\.venv\Scripts\python -m netx_api.mcp_server diff --git a/scripts/init_pg.ps1 b/scripts/init_pg.ps1 new file mode 100644 index 0000000..a7edb25 --- /dev/null +++ b/scripts/init_pg.ps1 @@ -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"