Initial commit: add .gitignore and README

This commit is contained in:
defiQUG
2026-02-09 21:51:31 -08:00
commit ef7df1fb2f
58 changed files with 8414 additions and 0 deletions

82
scripts/add-omada-env.sh Executable file
View File

@@ -0,0 +1,82 @@
#!/bin/bash
# Script to add Omada environment variables to existing .env file
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
ENV_FILE="$PROJECT_ROOT/.env"
ENV_EXAMPLE="$PROJECT_ROOT/env.example"
if [ ! -f "$ENV_FILE" ]; then
echo "❌ .env file not found. Creating from template..."
cp "$ENV_EXAMPLE" "$ENV_FILE"
echo "✅ Created .env file. Please edit it with your credentials."
exit 0
fi
echo "Adding Omada environment variables to existing .env file..."
# Check if variables already exist
OMADA_VARS=(
"OMADA_USERNAME"
"OMADA_PASSWORD"
"OMADA_ID"
"OMADA_CONTROLLER_BASE"
"OMADA_NORTHBOUND_BASE"
"DATABASE_URL"
"JWT_SECRET"
"PORT"
"NODE_ENV"
"LOG_LEVEL"
"SYNC_JOB_SCHEDULE"
"LICENSE_JOB_SCHEDULE"
)
# Read existing .env and check what's missing
MISSING_VARS=()
for var in "${OMADA_VARS[@]}"; do
if ! grep -q "^${var}=" "$ENV_FILE" 2>/dev/null; then
MISSING_VARS+=("$var")
fi
done
if [ ${#MISSING_VARS[@]} -eq 0 ]; then
echo "✅ All Omada environment variables are already present in .env"
exit 0
fi
echo "Found ${#MISSING_VARS[@]} missing variable(s): ${MISSING_VARS[*]}"
echo ""
# Append missing variables from template
echo "" >> "$ENV_FILE"
echo "# ============================================" >> "$ENV_FILE"
echo "# Omada Cloud Integration Variables" >> "$ENV_FILE"
echo "# ============================================" >> "$ENV_FILE"
# Extract and add missing variables from env.example
while IFS= read -r line; do
# Skip comments and empty lines
[[ "$line" =~ ^[[:space:]]*# ]] && continue
[[ -z "${line// }" ]] && continue
# Extract variable name
var_name=$(echo "$line" | cut -d'=' -f1)
# Check if this variable is missing
if [[ " ${MISSING_VARS[@]} " =~ " ${var_name} " ]]; then
echo "$line" >> "$ENV_FILE"
fi
done < "$ENV_EXAMPLE"
echo "✅ Added missing Omada environment variables to .env"
echo ""
echo "📝 Please edit .env and fill in the actual values for:"
for var in "${MISSING_VARS[@]}"; do
echo " - $var"
done
echo ""

232
scripts/init-topology-vlans.ts Executable file
View File

@@ -0,0 +1,232 @@
#!/usr/bin/env ts-node
/**
* Script to initialize the four topology VLANs for the datacenter network
*
* VLANs to create:
* - VLAN 10: MGMT (10.10.10.0/24, gateway 10.10.10.1)
* - VLAN 20: VM-LAN (10.20.0.0/22, gateway 10.20.0.1)
* - VLAN 30: CEPH-PUBLIC (10.30.0.0/24, gateway 10.30.0.1)
* - VLAN 99: OOB-MGMT (10.99.99.0/24, gateway 10.99.99.1)
*
* Usage:
* ts-node scripts/init-topology-vlans.ts <siteId>
* or
* node dist/scripts/init-topology-vlans.js <siteId>
*/
import { createVlan } from '../src/services/vlanService';
import prisma from '../src/lib/db';
import logger from '../src/lib/logger';
import { OmadaVlanCreate } from '../src/types/omada';
interface TopologyVlan {
name: string;
vlanId: number;
subnet: string;
gateway: string;
description: string;
}
const TOPOLOGY_VLANS: TopologyVlan[] = [
{
name: 'MGMT',
vlanId: 10,
subnet: '10.10.10.0/24',
gateway: '10.10.10.1',
description: 'Proxmox host management, SSH, APIs',
},
{
name: 'VM-LAN',
vlanId: 20,
subnet: '10.20.0.0/22',
gateway: '10.20.0.1',
description: 'Default VM / container network',
},
{
name: 'CEPH-PUBLIC',
vlanId: 30,
subnet: '10.30.0.0/24',
gateway: '10.30.0.1',
description: 'Ceph "public" network (client/public side)',
},
{
name: 'OOB-MGMT',
vlanId: 99,
subnet: '10.99.99.0/24',
gateway: '10.99.99.1',
description: 'iDRAC/IPMI, PDUs, router mgmt, console',
},
];
async function initializeTopologyVlans(siteIdOrName: string): Promise<void> {
try {
logger.info('Starting topology VLAN initialization', { siteIdOrName });
// Find site in database
const site = await prisma.site.findFirst({
where: {
OR: [
{ id: siteIdOrName },
{ omadaSiteId: siteIdOrName },
{ name: siteIdOrName },
],
},
});
if (!site) {
throw new Error(`Site not found: ${siteIdOrName}`);
}
logger.info('Found site', { siteId: site.id, siteName: site.name, omadaSiteId: site.omadaSiteId });
const results = [];
for (const vlanConfig of TOPOLOGY_VLANS) {
try {
// Check if VLAN already exists in database
const existingVlan = await prisma.vlan.findFirst({
where: {
siteId: site.id,
vlanId: vlanConfig.vlanId,
},
});
if (existingVlan) {
logger.warn('VLAN already exists in database, skipping', {
vlanId: vlanConfig.vlanId,
vlanName: vlanConfig.name,
existingVlanId: existingVlan.id,
});
results.push({
vlan: vlanConfig.name,
status: 'skipped',
reason: 'Already exists in database',
});
continue;
}
// Create VLAN via Omada API
const omadaVlanConfig: OmadaVlanCreate = {
name: vlanConfig.name,
vlanId: vlanConfig.vlanId,
subnet: vlanConfig.subnet,
gateway: vlanConfig.gateway,
dhcpEnabled: false, // Disable DHCP by default, can be enabled later if needed
description: vlanConfig.description,
enabled: true,
};
logger.info('Creating VLAN', {
siteId: site.omadaSiteId,
vlanConfig: omadaVlanConfig,
});
const omadaVlan = await createVlan(site.omadaSiteId, omadaVlanConfig);
// Store in database
const dbVlan = await prisma.vlan.create({
data: {
omadaVlanId: omadaVlan.id,
siteId: site.id,
name: omadaVlan.name,
vlanId: vlanConfig.vlanId,
subnet: vlanConfig.subnet,
gateway: vlanConfig.gateway,
dhcpEnabled: false,
description: vlanConfig.description,
},
});
logger.info('VLAN created successfully', {
vlanId: vlanConfig.vlanId,
vlanName: vlanConfig.name,
dbVlanId: dbVlan.id,
});
results.push({
vlan: vlanConfig.name,
status: 'created',
vlanId: vlanConfig.vlanId,
dbVlanId: dbVlan.id,
});
// Log audit
await prisma.auditLog.create({
data: {
action: 'create_vlan',
targetType: 'site',
targetId: site.id,
details: {
siteName: site.name,
vlanName: vlanConfig.name,
vlanId: vlanConfig.vlanId,
script: 'init-topology-vlans',
},
},
});
} catch (error) {
logger.error('Error creating VLAN', {
vlanName: vlanConfig.name,
vlanId: vlanConfig.vlanId,
error: error instanceof Error ? error.message : String(error),
});
results.push({
vlan: vlanConfig.name,
status: 'error',
error: error instanceof Error ? error.message : String(error),
});
}
}
// Print summary
console.log('\n=== Topology VLAN Initialization Summary ===');
console.log(`Site: ${site.name} (${site.omadaSiteId})`);
console.log('\nResults:');
results.forEach((result) => {
if (result.status === 'created') {
console.log(`${result.vlan} (VLAN ${result.vlanId}) - Created`);
} else if (result.status === 'skipped') {
console.log(`${result.vlan} - Skipped (${result.reason})`);
} else {
console.log(`${result.vlan} - Error: ${result.error}`);
}
});
const successCount = results.filter((r) => r.status === 'created').length;
const skippedCount = results.filter((r) => r.status === 'skipped').length;
const errorCount = results.filter((r) => r.status === 'error').length;
console.log(`\nSummary: ${successCount} created, ${skippedCount} skipped, ${errorCount} errors`);
if (errorCount > 0) {
process.exit(1);
}
} catch (error) {
logger.error('Fatal error initializing topology VLANs', {
error: error instanceof Error ? error.message : String(error),
});
console.error('Error:', error instanceof Error ? error.message : String(error));
process.exit(1);
} finally {
await prisma.$disconnect();
}
}
// Main execution
const siteIdOrName = process.argv[2];
if (!siteIdOrName) {
console.error('Usage: ts-node scripts/init-topology-vlans.ts <siteId|siteName>');
console.error('\nExample:');
console.error(' ts-node scripts/init-topology-vlans.ts "Default"');
console.error(' ts-node scripts/init-topology-vlans.ts <omada-site-id>');
process.exit(1);
}
initializeTopologyVlans(siteIdOrName).catch((error) => {
console.error('Unhandled error:', error);
process.exit(1);
});

54
scripts/setup-database.sh Executable file
View File

@@ -0,0 +1,54 @@
#!/bin/bash
# Database setup script for Omada Cloud Integration
set -e
echo "=== Database Setup Script ==="
echo ""
# Check if PostgreSQL is running
if ! pg_isready -h localhost -p 5432 >/dev/null 2>&1; then
echo "⚠️ PostgreSQL is not running on localhost:5432"
echo ""
echo "Options:"
echo "1. Start PostgreSQL with Docker:"
echo " docker run -d --name omada-postgres \\"
echo " -e POSTGRES_USER=omada_user \\"
echo " -e POSTGRES_PASSWORD=omada_password \\"
echo " -e POSTGRES_DB=omada_db \\"
echo " -p 5433:5432 postgres:15-alpine"
echo ""
echo " Then update DATABASE_URL in .env to use port 5433"
echo ""
echo "2. Use existing PostgreSQL (you'll need admin credentials)"
echo ""
exit 1
fi
echo "✅ PostgreSQL is running"
echo ""
# Try to create database with different methods
echo "Attempting to create database..."
# Method 1: Try with default postgres user
if psql -h localhost -U postgres -lqt 2>/dev/null | cut -d \| -f 1 | grep -qw omada_db; then
echo "✅ Database 'omada_db' already exists"
else
echo "Creating database 'omada_db'..."
echo "You may need to enter PostgreSQL admin password"
createdb -h localhost -U postgres omada_db 2>/dev/null || \
psql -h localhost -U postgres -c "CREATE DATABASE omada_db;" 2>/dev/null || \
echo "⚠️ Could not create database automatically. Please create it manually:"
echo " CREATE DATABASE omada_db;"
echo " CREATE USER omada_user WITH PASSWORD 'omada_password';"
echo " GRANT ALL PRIVILEGES ON DATABASE omada_db TO omada_user;"
fi
echo ""
echo "✅ Database setup complete!"
echo ""
echo "Next steps:"
echo "1. Run: pnpm run prisma:migrate"
echo "2. Verify: pnpm run prisma:studio"

54
scripts/setup-env.sh Executable file
View File

@@ -0,0 +1,54 @@
#!/bin/bash
# Setup script to create .env file from template
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
ENV_EXAMPLE="$PROJECT_ROOT/env.example"
ENV_FILE="$PROJECT_ROOT/.env"
echo "Setting up environment variables..."
if [ -f "$ENV_FILE" ]; then
echo "⚠️ .env file already exists!"
read -p "Do you want to overwrite it? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Aborted. Keeping existing .env file."
exit 0
fi
fi
if [ ! -f "$ENV_EXAMPLE" ]; then
echo "❌ Error: env.example file not found at $ENV_EXAMPLE"
exit 1
fi
# Copy template to .env
cp "$ENV_EXAMPLE" "$ENV_FILE"
echo "✅ Created .env file from env.example"
echo ""
echo "📝 Next steps:"
echo " 1. Edit .env file and fill in your Omada credentials:"
echo " - OMADA_USERNAME"
echo " - OMADA_PASSWORD"
echo " - OMADA_ID"
echo " - OMADA_CONTROLLER_BASE"
echo " - OMADA_NORTHBOUND_BASE"
echo ""
echo " 2. Configure your database:"
echo " - DATABASE_URL"
echo ""
echo " 3. Set a strong JWT_SECRET (generate with: openssl rand -base64 32)"
echo ""
echo " 4. Adjust optional settings as needed:"
echo " - PORT"
echo " - LOG_LEVEL"
echo " - SYNC_JOB_SCHEDULE"
echo " - LICENSE_JOB_SCHEDULE"
echo ""

46
scripts/test-auth.ts Executable file
View File

@@ -0,0 +1,46 @@
#!/usr/bin/env ts-node
/**
* Test script to verify Omada authentication
* Usage: npx ts-node scripts/test-auth.ts
*/
import { login } from '../src/services/authService';
import { listSites } from '../src/services/siteService';
async function testAuthentication() {
try {
console.log('Testing Omada Cloud authentication...\n');
// Test login
console.log('1. Testing login...');
const token = await login();
console.log('✅ Login successful!');
console.log(` Token: ${token.substring(0, 20)}...\n`);
// Test API call
console.log('2. Testing API call (fetching sites)...');
const sites = await listSites();
console.log(`✅ API call successful!`);
console.log(` Found ${sites.length} site(s):`);
sites.forEach((site, index) => {
console.log(` ${index + 1}. ${site.name} (ID: ${site.id || site.siteId})`);
});
console.log('\n✅ All tests passed!');
process.exit(0);
} catch (error) {
console.error('\n❌ Test failed:');
if (error instanceof Error) {
console.error(` ${error.message}`);
if (error.stack) {
console.error(`\n Stack trace:\n ${error.stack.split('\n').slice(1).join('\n ')}`);
}
} else {
console.error(' Unknown error:', error);
}
process.exit(1);
}
}
testAuthentication();