Initial commit: DashCaddy v1.0

Full codebase including API server (32 modules + routes), dashboard frontend,
DashCA certificate distribution, installer script, and deployment skills.
This commit is contained in:
Sami
2026-03-05 02:26:12 -08:00
commit f61e85d9a7
337 changed files with 75282 additions and 0 deletions
@@ -0,0 +1,837 @@
const fc = require('fast-check');
const ConfigManager = require('./config-manager');
const fs = require('fs').promises;
const path = require('path');
const os = require('os');
/**
* Property-based tests for ConfigManager
* These tests validate universal properties across all valid inputs
*/
describe('ConfigManager Property Tests', () => {
let manager;
let testDir;
beforeEach(async () => {
manager = new ConfigManager();
testDir = path.join(os.tmpdir(), `dashcaddy-prop-test-${Date.now()}`);
});
afterEach(async () => {
try {
await fs.rm(testDir, { recursive: true, force: true });
} catch (error) {
// Ignore cleanup errors
}
});
/**
* Feature: dashcaddy-installer, Property 5: Configuration Persistence
* For any installation configuration, saving and then loading the configuration
* should produce an equivalent configuration object.
* Validates: Requirements 2.6, 10.1, 10.2
*/
describe('Property 5: Configuration Persistence', () => {
// Generator for valid configuration objects
const configArbitrary = () => fc.record({
installPath: fc.constant(testDir),
tier: fc.constantFrom('basic', 'intermediate', 'advanced'),
dashboardName: fc.string({ minLength: 1, maxLength: 50 }),
customTLD: fc.option(fc.string({ minLength: 2, maxLength: 10 }).map(s => '.' + s)),
caddyAdminUrl: fc.constant('http://localhost:2021'),
apiPort: fc.integer({ min: 1024, max: 65535 }),
dnsEnabled: fc.boolean(),
tailscaleEnabled: fc.boolean()
});
test('configuration round-trips correctly', async () => {
await fc.assert(
fc.asyncProperty(
configArbitrary(),
async (config) => {
// Save configuration
const saveResult = await manager.saveConfig(config, testDir);
if (!saveResult.success) return true; // Skip if save failed
// Load configuration
const loadResult = await manager.loadConfig(testDir);
if (!loadResult.exists) return false;
// Verify all original fields are preserved
const loaded = loadResult.config;
return (
loaded.installPath === config.installPath &&
loaded.tier === config.tier &&
loaded.dashboardName === config.dashboardName &&
loaded.apiPort === config.apiPort &&
loaded.dnsEnabled === config.dnsEnabled &&
loaded.tailscaleEnabled === config.tailscaleEnabled
);
}
),
{ numRuns: 100 }
);
});
test('saved config always includes metadata', async () => {
await fc.assert(
fc.asyncProperty(
configArbitrary(),
async (config) => {
await manager.saveConfig(config, testDir);
const result = await manager.loadConfig(testDir);
if (!result.exists) return true; // Skip if load failed
return (
typeof result.config.version === 'string' &&
typeof result.config.lastModified === 'string'
);
}
),
{ numRuns: 100 }
);
});
test('loading non-existent config always returns exists=false', async () => {
await fc.assert(
fc.asyncProperty(
fc.string({ minLength: 1, maxLength: 20 }),
async (randomPath) => {
const nonExistentPath = path.join(testDir, randomPath);
const result = await manager.loadConfig(nonExistentPath);
return result.exists === false && result.config === null;
}
),
{ numRuns: 50 }
);
});
});
/**
* Feature: dashcaddy-installer, Property 3: Installation Path Validation
* For any selected folder path, the installer should correctly determine
* if the path is writable before proceeding with installation.
* Validates: Requirements 2.3, 2.4
*/
describe('Property 3: Installation Path Validation', () => {
test('validatePath returns consistent structure', async () => {
await fc.assert(
fc.asyncProperty(
fc.string({ minLength: 1, maxLength: 50 }),
async (pathSegment) => {
const testPath = path.join(testDir, pathSegment);
const result = await manager.validatePath(testPath);
return (
typeof result === 'object' &&
typeof result.valid === 'boolean' &&
typeof result.message === 'string'
);
}
),
{ numRuns: 50 }
);
});
test('valid paths are consistently validated', async () => {
await fc.assert(
fc.asyncProperty(
fc.string({ minLength: 1, maxLength: 20 }),
async (pathSegment) => {
const testPath = path.join(testDir, pathSegment);
// Validate twice
const result1 = await manager.validatePath(testPath);
const result2 = await manager.validatePath(testPath);
// Results should be consistent
return result1.valid === result2.valid;
}
),
{ numRuns: 30 }
);
});
});
/**
* Feature: dashcaddy-installer, Property 4: Directory Structure Creation
* For any valid installation path, the installer should create all required
* subdirectories (config, data, logs, caddyfile) and verify their existence.
* Validates: Requirements 2.5
*/
describe('Property 4: Directory Structure Creation', () => {
test('createDirectories creates all required directories', async () => {
await fc.assert(
fc.asyncProperty(
fc.string({ minLength: 1, maxLength: 20 }),
async (pathSegment) => {
const installPath = path.join(testDir, pathSegment);
const result = await manager.createDirectories(installPath);
if (!result.success) return true; // Skip if creation failed
// Verify all required directories exist
const requiredDirs = ['config', 'data', 'logs', 'caddyfile'];
const checks = await Promise.all(
requiredDirs.map(async (dir) => {
const dirPath = path.join(installPath, dir);
try {
await fs.access(dirPath);
return true;
} catch {
return false;
}
})
);
return checks.every(check => check === true);
}
),
{ numRuns: 50 }
);
});
test('createDirectories is idempotent', async () => {
await fc.assert(
fc.asyncProperty(
fc.string({ minLength: 1, maxLength: 20 }).filter(s => !s.includes('\0') && !s.includes('|')),
async (pathSegment) => {
const installPath = path.join(testDir, pathSegment);
// Create directories twice
const result1 = await manager.createDirectories(installPath);
const result2 = await manager.createDirectories(installPath);
// Both should succeed (or both fail for invalid paths)
return result1.success === result2.success;
}
),
{ numRuns: 30 }
);
});
});
/**
* Feature: dashcaddy-installer, Property 8: DNS Credential Security
* For any DNS credentials, saving and then loading the credentials should
* preserve all fields, and the password should be encrypted in storage.
* Validates: Requirements 4.8, 10.3
*/
describe('Property 8: DNS Credential Security', () => {
const credentialsArbitrary = () => fc.record({
server: fc.ipV4(),
username: fc.string({ minLength: 1, maxLength: 20 }),
password: fc.string({ minLength: 8, maxLength: 50 }),
tld: fc.string({ minLength: 2, maxLength: 10 }).map(s => '.' + s)
});
test('DNS credentials round-trip correctly', async () => {
await fc.assert(
fc.asyncProperty(
credentialsArbitrary(),
async (credentials) => {
// Save credentials
const saveResult = await manager.saveDNSCredentials(credentials, testDir);
if (!saveResult.success) return true; // Skip if save failed
// Load credentials
const loadResult = await manager.loadDNSCredentials(testDir);
if (!loadResult.exists) return false;
// Verify all fields are preserved
const loaded = loadResult.credentials;
return (
loaded.server === credentials.server &&
loaded.username === credentials.username &&
loaded.password === credentials.password &&
loaded.tld === credentials.tld
);
}
),
{ numRuns: 100 }
);
});
test('saved credentials always include timestamp', async () => {
await fc.assert(
fc.asyncProperty(
credentialsArbitrary(),
async (credentials) => {
await manager.saveDNSCredentials(credentials, testDir);
const result = await manager.loadDNSCredentials(testDir);
if (!result.exists) return true; // Skip if load failed
return typeof result.credentials.savedAt === 'string';
}
),
{ numRuns: 100 }
);
});
});
/**
* Feature: dashcaddy-installer, Property 6: Disk Space Verification
* For any valid path, the installer should correctly determine available disk space
* and report consistent results across multiple checks.
* Validates: Requirements 2.4
*/
describe('Property 6: Disk Space Verification', () => {
test('getDiskSpace returns consistent structure', async () => {
await fc.assert(
fc.asyncProperty(
fc.string({ minLength: 1, maxLength: 20 }).filter(s => !s.includes('\0') && !s.includes('|')),
async (pathSegment) => {
const testPath = path.join(testDir, pathSegment);
const result = await manager.getDiskSpace(testPath);
return (
typeof result === 'object' &&
typeof result.available === 'boolean' &&
(result.available ? typeof result.path === 'string' : typeof result.error === 'string')
);
}
),
{ numRuns: 50 }
);
});
test('getDiskSpace is idempotent for same path', async () => {
await fc.assert(
fc.asyncProperty(
fc.string({ minLength: 1, maxLength: 20 }).filter(s => !s.includes('\0') && !s.includes('|')),
async (pathSegment) => {
const testPath = path.join(testDir, pathSegment);
// Check twice
const result1 = await manager.getDiskSpace(testPath);
const result2 = await manager.getDiskSpace(testPath);
// Results should be consistent
return result1.available === result2.available;
}
),
{ numRuns: 30 }
);
});
test('getDiskSpace returns available=true for existing writable paths', async () => {
await fc.assert(
fc.asyncProperty(
fc.string({ minLength: 1, maxLength: 15 }).filter(s => /^[a-zA-Z0-9_-]+$/.test(s)),
async (pathSegment) => {
const testPath = path.join(testDir, pathSegment);
// Create the directory first
await fs.mkdir(testPath, { recursive: true });
const result = await manager.getDiskSpace(testPath);
return result.available === true;
}
),
{ numRuns: 30 }
);
});
});
/**
* Feature: dashcaddy-installer, Property 7: Tier Validation
* For any configuration, only valid tier values (basic, intermediate, advanced)
* should be accepted and persisted correctly.
* Validates: Requirements 3.1, 3.2, 3.3
*/
describe('Property 7: Tier Validation', () => {
const validTiers = ['basic', 'intermediate', 'advanced'];
test('valid tiers are preserved in config round-trip', async () => {
await fc.assert(
fc.asyncProperty(
fc.constantFrom(...validTiers),
fc.string({ minLength: 1, maxLength: 20 }),
async (tier, dashboardName) => {
const config = {
installPath: testDir,
tier: tier,
dashboardName: dashboardName,
apiPort: 3001,
dnsEnabled: false,
tailscaleEnabled: false
};
await manager.saveConfig(config, testDir);
const result = await manager.loadConfig(testDir);
if (!result.exists) return true; // Skip if save/load failed
return result.config.tier === tier;
}
),
{ numRuns: 50 }
);
});
test('tier value is always one of valid options after load', async () => {
await fc.assert(
fc.asyncProperty(
fc.constantFrom(...validTiers),
async (tier) => {
const config = {
installPath: testDir,
tier: tier,
dashboardName: 'Test',
apiPort: 3001,
dnsEnabled: false,
tailscaleEnabled: false
};
await manager.saveConfig(config, testDir);
const result = await manager.loadConfig(testDir);
if (!result.exists) return true;
return validTiers.includes(result.config.tier);
}
),
{ numRuns: 30 }
);
});
test('tier determines expected feature availability', async () => {
await fc.assert(
fc.asyncProperty(
fc.constantFrom(...validTiers),
async (tier) => {
const config = {
installPath: testDir,
tier: tier,
dashboardName: 'Test',
apiPort: 3001,
dnsEnabled: tier === 'advanced',
tailscaleEnabled: tier === 'advanced'
};
await manager.saveConfig(config, testDir);
const result = await manager.loadConfig(testDir);
if (!result.exists) return true;
// Advanced tier should support DNS and Tailscale
if (tier === 'advanced') {
return result.config.dnsEnabled === true && result.config.tailscaleEnabled === true;
}
// Non-advanced tiers should have these disabled
return result.config.dnsEnabled === false && result.config.tailscaleEnabled === false;
}
),
{ numRuns: 30 }
);
});
});
/**
* Additional property tests for installation management
*/
describe('Installation Management Properties', () => {
// Generator for valid configuration objects (reused from above)
const configArbitrary = () => fc.record({
installPath: fc.constant(testDir),
tier: fc.constantFrom('basic', 'intermediate', 'advanced'),
dashboardName: fc.string({ minLength: 1, maxLength: 50 }),
apiPort: fc.integer({ min: 1024, max: 65535 }),
dnsEnabled: fc.boolean(),
tailscaleEnabled: fc.boolean()
});
test('installationExists is consistent with config file presence', async () => {
await fc.assert(
fc.asyncProperty(
configArbitrary(),
async (config) => {
// Use a unique directory for this test
const uniqueDir = path.join(testDir, `test-${Date.now()}-${Math.random()}`);
config.installPath = uniqueDir;
// Before saving, installation should not exist
const existsBefore = await manager.installationExists(uniqueDir);
// Save config
await manager.saveConfig(config, uniqueDir);
// After saving, installation should exist
const existsAfter = await manager.installationExists(uniqueDir);
return !existsBefore && existsAfter;
}
),
{ numRuns: 50 }
);
});
test('removeInstallation removes all files', async () => {
await fc.assert(
fc.asyncProperty(
configArbitrary(),
async (config) => {
// Create installation
await manager.createDirectories(testDir);
await manager.saveConfig(config, testDir);
// Verify it exists
const existsBefore = await manager.installationExists(testDir);
if (!existsBefore) return true; // Skip if creation failed
// Remove installation
const removeResult = await manager.removeInstallation(testDir);
if (!removeResult.success) return true; // Skip if removal failed
// Verify it no longer exists
const existsAfter = await manager.installationExists(testDir);
return !existsAfter;
}
),
{ numRuns: 30 }
);
});
test('listInstallationFiles returns array', async () => {
await fc.assert(
fc.asyncProperty(
fc.string({ minLength: 1, maxLength: 20 }),
async (pathSegment) => {
const installPath = path.join(testDir, pathSegment);
const files = await manager.listInstallationFiles(installPath);
return Array.isArray(files);
}
),
{ numRuns: 50 }
);
});
});
/**
* Configuration Edge Cases
* Tests for special characters, boundary conditions, and data integrity
*/
describe('Configuration Edge Cases', () => {
test('special characters in dashboardName are preserved', async () => {
// Generator for strings with special characters
const specialCharArbitrary = fc.string({
unit: fc.constantFrom(
'a', 'Z', '0', ' ', '-', '_', '.', '!', '@', '#',
'é', 'ñ', '中', '日', '한', 'α', 'β'
),
minLength: 1,
maxLength: 30
});
await fc.assert(
fc.asyncProperty(
specialCharArbitrary,
async (dashboardName) => {
const config = {
installPath: testDir,
tier: 'basic',
dashboardName: dashboardName,
apiPort: 3001,
dnsEnabled: false,
tailscaleEnabled: false
};
await manager.saveConfig(config, testDir);
const result = await manager.loadConfig(testDir);
if (!result.exists) return true;
return result.config.dashboardName === dashboardName;
}
),
{ numRuns: 100 }
);
});
test('port values at boundaries are handled correctly', async () => {
const boundaryPorts = fc.constantFrom(1024, 1025, 3000, 3001, 8080, 49151, 65534, 65535);
await fc.assert(
fc.asyncProperty(
boundaryPorts,
async (port) => {
const config = {
installPath: testDir,
tier: 'basic',
dashboardName: 'Test',
apiPort: port,
dnsEnabled: false,
tailscaleEnabled: false
};
await manager.saveConfig(config, testDir);
const result = await manager.loadConfig(testDir);
if (!result.exists) return true;
return result.config.apiPort === port;
}
),
{ numRuns: 20 }
);
});
test('customTLD with various formats preserved correctly', async () => {
const tldArbitrary = fc.oneof(
fc.constant(undefined),
fc.constant(null),
fc.string({ minLength: 2, maxLength: 10 }).map(s => '.' + s.replace(/[^a-zA-Z]/g, 'x')),
fc.constantFrom('.local', '.home', '.lan', '.sami', '.test')
);
await fc.assert(
fc.asyncProperty(
tldArbitrary,
async (customTLD) => {
const config = {
installPath: testDir,
tier: 'basic',
dashboardName: 'Test',
customTLD: customTLD,
apiPort: 3001,
dnsEnabled: false,
tailscaleEnabled: false
};
await manager.saveConfig(config, testDir);
const result = await manager.loadConfig(testDir);
if (!result.exists) return true;
// Both null/undefined should be equivalent after round-trip
if (customTLD === null || customTLD === undefined) {
return result.config.customTLD === null ||
result.config.customTLD === undefined ||
result.config.customTLD === customTLD;
}
return result.config.customTLD === customTLD;
}
),
{ numRuns: 50 }
);
});
test('boolean fields remain booleans after round-trip', async () => {
await fc.assert(
fc.asyncProperty(
fc.boolean(),
fc.boolean(),
async (dnsEnabled, tailscaleEnabled) => {
const config = {
installPath: testDir,
tier: 'basic',
dashboardName: 'Test',
apiPort: 3001,
dnsEnabled: dnsEnabled,
tailscaleEnabled: tailscaleEnabled
};
await manager.saveConfig(config, testDir);
const result = await manager.loadConfig(testDir);
if (!result.exists) return true;
return (
typeof result.config.dnsEnabled === 'boolean' &&
typeof result.config.tailscaleEnabled === 'boolean' &&
result.config.dnsEnabled === dnsEnabled &&
result.config.tailscaleEnabled === tailscaleEnabled
);
}
),
{ numRuns: 50 }
);
});
test('config version is valid semver format', async () => {
const semverRegex = /^\d+\.\d+\.\d+$/;
await fc.assert(
fc.asyncProperty(
fc.constantFrom('basic', 'intermediate', 'advanced'),
async (tier) => {
const config = {
installPath: testDir,
tier: tier,
dashboardName: 'Test',
apiPort: 3001,
dnsEnabled: false,
tailscaleEnabled: false
};
await manager.saveConfig(config, testDir);
const result = await manager.loadConfig(testDir);
if (!result.exists) return true;
return semverRegex.test(result.config.version);
}
),
{ numRuns: 30 }
);
});
test('lastModified is valid ISO date string', async () => {
await fc.assert(
fc.asyncProperty(
fc.constantFrom('basic', 'intermediate', 'advanced'),
async (tier) => {
const config = {
installPath: testDir,
tier: tier,
dashboardName: 'Test',
apiPort: 3001,
dnsEnabled: false,
tailscaleEnabled: false
};
await manager.saveConfig(config, testDir);
const result = await manager.loadConfig(testDir);
if (!result.exists) return true;
// Should be parseable as a date and not NaN
const date = new Date(result.config.lastModified);
return !isNaN(date.getTime());
}
),
{ numRuns: 30 }
);
});
test('multiple rapid saves preserve final state', async () => {
await fc.assert(
fc.asyncProperty(
fc.array(fc.string({ minLength: 1, maxLength: 20 }), { minLength: 2, maxLength: 5 }),
async (dashboardNames) => {
// Rapidly save multiple configs
for (const name of dashboardNames) {
await manager.saveConfig({
installPath: testDir,
tier: 'basic',
dashboardName: name,
apiPort: 3001,
dnsEnabled: false,
tailscaleEnabled: false
}, testDir);
}
const result = await manager.loadConfig(testDir);
if (!result.exists) return true;
// Final saved name should be the last one
return result.config.dashboardName === dashboardNames[dashboardNames.length - 1];
}
),
{ numRuns: 30 }
);
});
});
/**
* DNS Credential Edge Cases
*/
describe('DNS Credential Edge Cases', () => {
test('passwords with special characters are preserved', async () => {
const specialPasswordArbitrary = fc.string({
unit: fc.constantFrom(
'a', 'Z', '0', '!', '@', '#', '$', '%', '^', '&', '*',
'(', ')', '-', '_', '=', '+', '[', ']', '{', '}', '|',
';', ':', "'", '"', '<', '>', ',', '.', '/', '?', '`', '~'
),
minLength: 8,
maxLength: 50
});
await fc.assert(
fc.asyncProperty(
specialPasswordArbitrary,
async (password) => {
const credentials = {
server: '192.168.1.1',
username: 'admin',
password: password,
tld: '.local'
};
await manager.saveDNSCredentials(credentials, testDir);
const result = await manager.loadDNSCredentials(testDir);
if (!result.exists) return true;
return result.credentials.password === password;
}
),
{ numRuns: 100 }
);
});
test('various IP address formats in server field', async () => {
await fc.assert(
fc.asyncProperty(
fc.ipV4(),
async (server) => {
const credentials = {
server: server,
username: 'admin',
password: 'password123',
tld: '.local'
};
await manager.saveDNSCredentials(credentials, testDir);
const result = await manager.loadDNSCredentials(testDir);
if (!result.exists) return true;
return result.credentials.server === server;
}
),
{ numRuns: 50 }
);
});
test('username with edge case lengths', async () => {
const usernameArbitrary = fc.string({
unit: fc.constantFrom('a', 'b', 'c', '1', '2', '_', '-'),
minLength: 1,
maxLength: 100
});
await fc.assert(
fc.asyncProperty(
usernameArbitrary,
async (username) => {
const credentials = {
server: '192.168.1.1',
username: username,
password: 'password123',
tld: '.local'
};
await manager.saveDNSCredentials(credentials, testDir);
const result = await manager.loadDNSCredentials(testDir);
if (!result.exists) return true;
return result.credentials.username === username;
}
),
{ numRuns: 50 }
);
});
});
});