BEXO ERP Docs

BEXO ERP — Complete Setup & Deployment Guide

For Deployment Engineers and DevOps Teams


Pre-Deployment Checklist

☐ Dedicated server provisioned (not shared)
☐ Windows Server 2016 or newer installed
☐ .NET 8 ASP.NET Core Runtime available
☐ SQL Server 2016+ running and accessible
☐ SSL certificate obtained (production)
☐ Network firewall rules approved
☐ Database backup location prepared
☐ Documentation reviewed by team
☐ Test deployment completed successfully
☐ Rollback procedure documented

Phase 1: Server Infrastructure Setup

Hardware Requirements

MINIMUM:
  CPU:     Intel Core i5 / AMD Ryzen 5 (4 cores)
  RAM:     8 GB
  Disk:    50 GB free (SSD preferred)
  Network: Gigabit LAN connection

RECOMMENDED:
  CPU:     Intel Xeon / AMD EPYC (8+ cores)
  RAM:     16 GB +
  Disk:    100 GB+ SSD
  Network: 10 Gbps LAN
  Redundancy: RAID 1 disk mirroring

Windows Server Setup

1. Install Windows Server

Edition:  Windows Server 2022 Standard (64-bit)
Options:  Server with Desktop Experience
Disk:     Use C: for OS, D: for data (separate disk if possible)

2. Configure Network

# Set static IP
$ipConfig = Get-NetIPConfiguration
$interface = Get-NetAdapter -InterfaceIndex $ipConfig.InterfaceIndex
New-NetIPAddress -InterfaceIndex $interface.ifIndex `
    -IPAddress 192.168.0.XX -PrefixLength 24 `
    -DefaultGateway 192.168.0.1

# Set DNS
Set-DnsClientServerAddress -InterfaceIndex $interface.ifIndex `
    -ServerAddresses ("8.8.8.8", "8.8.4.4")

# Rename computer
Rename-Computer -NewName "BEXO-PROD-01" -Restart

3. Enable RDP & Firewall

# Enable RDP
Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server' `
    -Name 'fDenyTSConnections' -Value 0

# Configure Windows Firewall
Enable-NetFirewallRule -DisplayName "Remote Desktop - User Mode (TCP-In)"

Phase 2: Install .NET 8 Runtime

Download & Install

# Download .NET 8 ASP.NET Core Runtime
# From: https://dotnet.microsoft.com/download/dotnet/8.0

# Run installer
.\dotnet-hosting-8.0.5-win-x64.exe

# Verify installation
dotnet --version
dotnet --list-runtimes

# Output should show:
# .NET 8.0.5
# Microsoft.AspNetCore.App 8.0.5

Phase 3: SQL Server Setup

Installation

Edition:        Standard or Enterprise
Instance:       MSSQLSERVER (default)
Data Path:      D:\Data\MSSQL (separate disk)
Log Path:       E:\Logs\MSSQL (separate disk if available)
Backup Path:    C:\Backups\BEXO_SQL

Post-Installation Configuration

1. Enable SQL Server Agent

# Required for scheduled backups
$svc = Get-Service SQLSERVERAGENT
Set-Service $svc -StartupType Automatic
Start-Service $svc

2. Create BEXO Database

-- Connect as SA
-- Database will auto-create via EF migrations when API starts

-- Manual creation (optional):
CREATE DATABASE BEXO_ERP
ON PRIMARY (
    NAME = 'BEXO_ERP_Data',
    FILENAME = 'D:\Data\MSSQL\BEXO_ERP.mdf',
    SIZE = 100MB,
    FILEGROWTH = 50MB
)
LOG ON (
    NAME = 'BEXO_ERP_Log',
    FILENAME = 'E:\Logs\MSSQL\BEXO_ERP_log.ldf',
    SIZE = 100MB,
    FILEGROWTH = 50MB
)

3. Create Service Account

-- Create SQL login for API
CREATE LOGIN [BEXO_API] WITH PASSWORD = 'YourStrongPassword123!'

-- Create database user
USE BEXO_ERP
CREATE USER [BEXO_API] FOR LOGIN [BEXO_API]

-- Grant permissions
ALTER ROLE db_owner ADD MEMBER [BEXO_API]

4. Enable Backups

-- Configure backup directory
EXEC xp_cmdshell 'mkdir C:\Backups\BEXO_SQL'

-- Create maintenance plan (or use PowerShell scheduled task)
-- Daily full backup at 02:00 AM

Phase 4: Deploy BEXO API

Prepare Binaries

1. Get Release Package

# From NAS or CI/CD pipeline
$releasePackage = "\\nas\releases\BEXO\v1.0.0\BEXO_API.zip"

# Extract to deployment location
Expand-Archive -Path $releasePackage -DestinationPath "C:\Release\BEXO"

# Verify files
Get-ChildItem C:\Release\BEXO\

# Should contain:
# - BEXO.API.dll
# - appsettings.json (REDACTED - needs secrets)
# - web.config (if IIS hosting)
# - Other assemblies & dependencies

Configure appsettings.json

Template

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning"
    }
  },
  "ConnectionStrings": {
    "DefaultConnection": "Server=.\\BEXO_ERP;Database=BEXO_ERP;Integrated Security=true;TrustServerCertificate=true"
  },
  "DatabaseProvider": "SqlServer",
  "Jwt": {
    "SigningKey": "your-256-bit-base64-key-here",
    "Issuer": "bexo-api",
    "Audience": "bexo-client",
    "ExpirationMinutes": 1440
  },
  "Kestrel": {
    "Endpoints": {
      "Https": {
        "Url": "https://0.0.0.0:5443",
        "Certificate": {
          "Path": "C:\\certs\\bexo.pfx",
          "Password": "cert-password-here"
        }
      }
    }
  }
}

Setup JWT Key (256-bit)

# Generate secure JWT key
[Convert]::ToBase64String((1..32 | ForEach-Object { [byte](Get-Random -Minimum 0 -Maximum 256) }))

# Output example:
# a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6r7s8t9u0v1w2x3y4z5a6b7c8d9e0f1

For Production - Use Environment Variables

# Set environment variables (survives service restart)
[Environment]::SetEnvironmentVariable("BEXO_JWT_SIGNING_KEY", "your-key", "Machine")
[Environment]::SetEnvironmentVariable("BEXO_API_CERT_PWD", "cert-password", "Machine")

# Restart required
Restart-Computer

Install SSL Certificate

Option 1: Self-Signed (Dev/Test)

# Create self-signed cert valid for 365 days
$cert = New-SelfSignedCertificate `
    -DnsName "localhost", "192.168.0.XX", "bexo.company.local" `
    -CertStoreLocation "Cert:\LocalMachine\My" `
    -NotAfter (Get-Date).AddDays(365) `
    -KeyAlgorithm RSA `
    -KeyLength 2048 `
    -Subject "CN=BEXO ERP Server"

# Export with private key
$pwd = ConvertTo-SecureString -String "cert-password" -Force -AsPlainText
Export-PfxCertificate -Cert $cert -FilePath "C:\certs\bexo.pfx" -Password $pwd

# Note thumbprint for config
$cert.Thumbprint

Option 2: Production Certificate

# Import issued certificate
Import-PfxCertificate -FilePath "C:\certs\bexo_prod.pfx" `
    -CertStoreLocation "Cert:\LocalMachine\My" `
    -Password (ConvertTo-SecureString "password" -AsPlainText -Force)

Phase 5: Install API as Windows Service

Option A: Windows Service (NSSM)

# Download NSSM (Non-Sucking Service Manager)
# https://nssm.cc/download

# Install service
nssm install BEXO_API_Prod `
    "C:\Program Files\dotnet\dotnet.exe" `
    "C:\Release\BEXO\BEXO.API.dll"

# Configure service
nssm set BEXO_API_Prod AppDirectory C:\Release\BEXO
nssm set BEXO_API_Prod AppStdout C:\Tools\logs\stdout.log
nssm set BEXO_API_Prod AppStderr C:\Tools\logs\stderr.log
nssm set BEXO_API_Prod AppRotateFiles 1
nssm set BEXO_API_Prod AppRotateOnline 1

# Start service
Start-Service BEXO_API_Prod

# Verify
Get-Service BEXO_API_Prod

Option B: Scheduled Task (Windows Task Scheduler)

# Create scheduled task to run at startup
$trigger = New-ScheduledTaskTrigger -AtStartup
$action = New-ScheduledTaskAction `
    -Execute "C:\Program Files\dotnet\dotnet.exe" `
    -Argument "C:\Release\BEXO\BEXO.API.dll" `
    -WorkingDirectory "C:\Release\BEXO"
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest

Register-ScheduledTask `
    -TaskName "BEXO_API_Prod" `
    -Trigger $trigger `
    -Action $action `
    -Principal $principal `
    -Force

# Start task
Start-ScheduledTask -TaskName "BEXO_API_Prod"

Phase 6: Deploy WPF Client

Installation on Client Machines

# Copy release folder to Program Files
Copy-Item -Path "\\nas\releases\BEXO\v1.0.0\BEXO.UI" `
    -Destination "C:\Program Files\BEXO\" `
    -Recurse -Force

# Create shortcut on desktop
$WshShell = New-Object -ComObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut("C:\Users\Public\Desktop\BEXO.lnk")
$Shortcut.TargetPath = "C:\Program Files\BEXO\BEXO.UI.exe"
$Shortcut.WorkingDirectory = "C:\Program Files\BEXO\"
$Shortcut.Save()

# Set API endpoint in config (or via login dialog)
# Edit: C:\Program Files\BEXO\appsettings.json

Phase 7: Backup & Recovery Setup

Create Backup Folder

# Local backup
New-Item -Path "C:\Backups\BEXO_SQL" -ItemType Directory -Force

# NAS backup (off-box copy)
New-NetAdapter -Name "BEXO_NAS" # (if needed)
New-SmbMapping -LocalPath "Z:" `
    -RemotePath "\\192.168.0.33\common-data\BEXO_Deploy\db_backups" `
    -Persist

Schedule Daily Backup

Option 1: Scheduled Task

# Create backup script
$script = @'
[string[]]$sqltoolspath = "C:\Program Files\Microsoft SQL Server\*\Tools\Binn"
[string]$backupdir = "C:\Backups\BEXO_SQL"
[string]$timestamp = (Get-Date).ToString("yyyy-MM-dd_HH-mm")

$env:Path += ";" + (Resolve-Path $sqltoolspath).Path

# Backup
sqlcmd -S . -Q "BACKUP DATABASE BEXO_ERP TO DISK = '$backupdir\BEXO_ERP_$timestamp.bak' WITH INIT, COPY_ONLY, STATS=10, COMPRESSION"

# Verify
sqlcmd -S . -Q "RESTORE VERIFYONLY FROM DISK = '$backupdir\BEXO_ERP_$timestamp.bak'"

# Copy to NAS
Copy-Item "$backupdir\BEXO_ERP_$timestamp.bak" -Destination "Z:\"

# Cleanup old backups (7-day retention)
Get-ChildItem $backupdir -Filter "*.bak" | Where {$_.LastWriteTime -lt (Get-Date).AddDays(-7)} | Remove-Item
'@

$script | Out-File "C:\Tools\scripts\backup_bexo.ps1" -Force

# Create scheduled task
$trigger = New-ScheduledTaskTrigger -Daily -At "02:00 AM"
$action = New-ScheduledTaskAction `
    -Execute "powershell.exe" `
    -Argument "-NoProfile -ExecutionPolicy Bypass -File C:\Tools\scripts\backup_bexo.ps1"
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest

Register-ScheduledTask `
    -TaskName "BEXO_DB_Backup" `
    -Trigger $trigger `
    -Action $action `
    -Principal $principal `
    -Force

# Verify
Get-ScheduledTask -TaskName "BEXO_DB_Backup"

Phase 8: Health Monitoring

Create Health Check Script

# C:\Tools\scripts\health_watchdog.ps1
[int]$maxRetries = 3
[int]$retryDelay = 80
[string]$logFile = "C:\Tools\logs\bexo_alerts.log"

function LogAlert {
    param([string]$message)
    Add-Content -Path $logFile -Value "$(Get-Date): $message"
}

# Test health endpoint
$healthy = $false
for ($i = 0; $i -lt $maxRetries; $i++) {
    try {
        $response = Invoke-RestMethod `
            -Uri "https://localhost:5443/api/health" `
            -SkipCertificateCheck `
            -TimeoutSec 10

        if ($response.status -eq "Healthy") {
            $healthy = $true
            LogAlert "OK: API is healthy"
            break
        }
    }
    catch {
        LogAlert "WARN: Health check failed (attempt $($i+1)/$maxRetries): $_"

        if ($i -lt ($maxRetries - 1)) {
            LogAlert "INFO: Restarting API service..."
            Restart-Service BEXO_API_Prod -Force
            Start-Sleep -Seconds $retryDelay
        }
    }
}

if (-not $healthy) {
    LogAlert "ERROR: API failed all health checks. Manual intervention required."
}

Schedule Watchdog (Every 5 Minutes)

$trigger = New-ScheduledTaskTrigger -RepetitionInterval (New-TimeSpan -Minutes 5) -Once -At (Get-Date)
$action = New-ScheduledTaskAction `
    -Execute "powershell.exe" `
    -Argument "-NoProfile -ExecutionPolicy Bypass -File C:\Tools\scripts\health_watchdog.ps1"
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest

Register-ScheduledTask `
    -TaskName "BEXO_Health_Watchdog" `
    -Trigger $trigger `
    -Action $action `
    -Principal $principal `
    -Force

Phase 9: Firewall Configuration

# Allow API traffic (LAN only)
New-NetFirewallRule `
    -DisplayName "BEXO API - LAN (5443)" `
    -Direction Inbound `
    -LocalPort 5443 `
    -Protocol TCP `
    -RemoteAddress 192.168.0.0/16 `
    -Action Allow

# Allow SQL Server (if not local)
New-NetFirewallRule `
    -DisplayName "SQL Server - BEXO (1433)" `
    -Direction Outbound `
    -RemotePort 1433 `
    -Protocol TCP `
    -RemoteAddress 192.168.0.XX `
    -Action Allow

Phase 10: Post-Deployment Verification

Test API Connectivity

# Health check
Invoke-RestMethod -Uri "https://localhost:5443/api/health" -SkipCertificateCheck

# Sample data endpoint
Invoke-RestMethod -Uri "https://localhost:5443/api/companies" `
    -SkipCertificateCheck `
    -Headers @{Authorization = "Bearer <jwt-token>"}

# Database test
sqlcmd -S . -Q "SELECT COUNT(*) FROM BEXO_ERP.dbo.Companies"

Performance Baseline

# Measure endpoint response times
$uri = "https://localhost:5443/api/invoices"
$times = @()

for ($i = 0; $i -lt 10; $i++) {
    $stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
    Invoke-RestMethod -Uri $uri -SkipCertificateCheck | Out-Null
    $stopwatch.Stop()
    $times += $stopwatch.ElapsedMilliseconds
}

"Average Response: $([int]($times | Measure-Object -Average).Average) ms"
"P95: $($times | Sort-Object)[[int]($times.Count * 0.95)] ms"

Rollback Procedure

If deployment fails:

# 1. Stop API
Stop-Service BEXO_API_Prod

# 2. Restore previous version
Remove-Item "C:\Release\BEXO" -Recurse -Force
Rename-Item "C:\Release\BEXO_backup" -NewName "BEXO"

# 3. Restore database (if needed)
sqlcmd -S . -Q "RESTORE DATABASE BEXO_ERP FROM DISK = 'C:\Backups\BEXO_SQL\BEXO_ERP_previous.bak' WITH REPLACE, RECOVERY"

# 4. Restart
Start-Service BEXO_API_Prod

# 5. Verify
Invoke-RestMethod -Uri https://localhost:5443/api/health -SkipCertificateCheck

Checklist - Ready for UAT

☐ API service running and healthy
☐ Database contains test data
☐ All 68 UI views accessible & fast
☐ Health checks passing (5-min intervals)
☐ Backups completed successfully
☐ Logs being written (no errors)
☐ SSL certificate valid (not expired)
☐ Firewall rules allowing LAN access
☐ WPF clients can login successfully
☐ Sample transaction workflows tested
☐ Performance baseline measured
☐ Rollback procedure documented
☐ Admin trained on operations
☐ Documentation deployed to NAS

Last Updated: 2026-05-30
Version: 1.0