initial wcag-ada
This commit is contained in:
parent
042b8cb83a
commit
d52cfe7de2
112 changed files with 9069 additions and 0 deletions
181
apps/wcag-ada/config/README.md
Normal file
181
apps/wcag-ada/config/README.md
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
# WCAG-ADA Configuration
|
||||
|
||||
Centralized configuration management for the WCAG-ADA compliance platform, built on top of the core configuration system.
|
||||
|
||||
## Overview
|
||||
|
||||
This configuration system provides:
|
||||
- Type-safe configuration with Zod schemas
|
||||
- Multi-source configuration (JSON files + environment variables)
|
||||
- Environment-specific overrides
|
||||
- Service-specific configurations
|
||||
- Feature flags management
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```typescript
|
||||
import { initializeWcagConfig, getWcagConfig } from '@wcag-ada/config';
|
||||
|
||||
// Initialize configuration (do this once at app startup)
|
||||
const config = initializeWcagConfig('api'); // 'api' | 'dashboard' | 'worker'
|
||||
|
||||
// Access configuration values
|
||||
console.log(config.services.api.port); // 3001
|
||||
console.log(config.scanner.concurrency); // 2
|
||||
```
|
||||
|
||||
### Helper Functions
|
||||
|
||||
```typescript
|
||||
import {
|
||||
getScannerConfig,
|
||||
getWorkerConfig,
|
||||
isFeatureEnabled,
|
||||
getServiceConfig
|
||||
} from '@wcag-ada/config';
|
||||
|
||||
// Get specific configurations
|
||||
const scannerConfig = getScannerConfig();
|
||||
const workerConfig = getWorkerConfig();
|
||||
|
||||
// Check feature flags
|
||||
if (isFeatureEnabled('screenshots')) {
|
||||
// Screenshot feature is enabled
|
||||
}
|
||||
|
||||
if (isFeatureEnabled('reports.pdf')) {
|
||||
// PDF reports are enabled
|
||||
}
|
||||
|
||||
// Get service configuration
|
||||
const apiConfig = getServiceConfig('api');
|
||||
```
|
||||
|
||||
## Configuration Structure
|
||||
|
||||
### Scanner Configuration
|
||||
- `concurrency`: Number of concurrent scans (1-10)
|
||||
- `timeout`: Maximum scan duration
|
||||
- `headless`: Run browser in headless mode
|
||||
- `blockResources`: Block images/fonts for faster scans
|
||||
- `viewport`: Default viewport dimensions
|
||||
|
||||
### Worker Configuration
|
||||
- `concurrency`: Number of concurrent jobs
|
||||
- `queueName`: BullMQ queue name
|
||||
- `redis`: Redis connection settings
|
||||
- `jobs`: Job-specific configurations
|
||||
- `scheduler`: Scheduled job settings
|
||||
|
||||
### Features Configuration
|
||||
- `screenshots`: Screenshot capture settings
|
||||
- `customRules`: Custom accessibility rules
|
||||
- `multiPage`: Multi-page scanning
|
||||
- `reports`: Report generation features
|
||||
- `integrations`: External service integrations
|
||||
- `enterprise`: Enterprise features (SSO, white-label)
|
||||
|
||||
### Providers Configuration
|
||||
- `storage`: File storage (local, S3, GCS, Azure)
|
||||
- `email`: Email providers (SMTP, SendGrid, SES)
|
||||
- `auth`: Authentication settings (JWT, OAuth)
|
||||
- `analytics`: Analytics providers
|
||||
- `cdn`: CDN configuration
|
||||
|
||||
## Environment Variables
|
||||
|
||||
The system supports environment variable overrides with the `WCAG_` prefix:
|
||||
|
||||
```bash
|
||||
# Scanner settings
|
||||
WCAG_SCANNER_CONCURRENCY=5
|
||||
WCAG_SCANNER_TIMEOUT=180000
|
||||
WCAG_SCANNER_HEADLESS=true
|
||||
|
||||
# Worker settings
|
||||
WCAG_WORKER_CONCURRENCY=3
|
||||
WCAG_WORKER_REDIS_HOST=redis.example.com
|
||||
WCAG_WORKER_REDIS_PORT=6379
|
||||
|
||||
# Service ports
|
||||
WCAG_SERVICES_API_PORT=3001
|
||||
WCAG_SERVICES_DASHBOARD_PORT=3000
|
||||
|
||||
# Storage
|
||||
WCAG_PROVIDERS_STORAGE_TYPE=s3
|
||||
WCAG_PROVIDERS_STORAGE_S3_BUCKET=wcag-ada-storage
|
||||
WCAG_PROVIDERS_STORAGE_S3_REGION=us-east-1
|
||||
|
||||
# Authentication
|
||||
WCAG_PROVIDERS_AUTH_JWT_SECRET=your-secret-key
|
||||
```
|
||||
|
||||
## Configuration Files
|
||||
|
||||
Configuration files are loaded in this order (later files override earlier ones):
|
||||
1. `config/default.json` - Base configuration
|
||||
2. `config/{environment}.json` - Environment-specific (development, production, test)
|
||||
3. Environment variables - Highest priority
|
||||
|
||||
## Service-Specific Configuration
|
||||
|
||||
Each service can have its own configuration:
|
||||
|
||||
```typescript
|
||||
// API Service
|
||||
initializeWcagConfig('api');
|
||||
// Uses services.api configuration
|
||||
|
||||
// Dashboard
|
||||
initializeWcagConfig('dashboard');
|
||||
// Uses services.dashboard configuration
|
||||
|
||||
// Worker
|
||||
initializeWcagConfig('worker');
|
||||
// Uses services.worker configuration
|
||||
```
|
||||
|
||||
## Adding New Configuration
|
||||
|
||||
1. Add schema definition in `src/schemas/`
|
||||
2. Update `wcag-app.schema.ts` to include new schema
|
||||
3. Add default values in `config/default.json`
|
||||
4. Add environment-specific overrides as needed
|
||||
5. Create helper functions in `src/index.ts` for easy access
|
||||
|
||||
## Examples
|
||||
|
||||
### Check Subscription Limits
|
||||
|
||||
```typescript
|
||||
import { getSubscriptionConfig } from '@wcag-ada/config';
|
||||
|
||||
const starterLimits = getSubscriptionConfig('starter');
|
||||
console.log(starterLimits.websites); // 5
|
||||
console.log(starterLimits.scansPerMonth); // 500
|
||||
```
|
||||
|
||||
### Get Compliance Settings
|
||||
|
||||
```typescript
|
||||
import { getComplianceConfig } from '@wcag-ada/config';
|
||||
|
||||
const compliance = getComplianceConfig();
|
||||
console.log(compliance.defaultLevel); // { standard: 'WCAG21', level: 'AA' }
|
||||
console.log(compliance.criticalCriteria); // ['1.1.1', '1.3.1', ...]
|
||||
```
|
||||
|
||||
### Storage Configuration
|
||||
|
||||
```typescript
|
||||
import { getStorageConfig } from '@wcag-ada/config';
|
||||
|
||||
const storage = getStorageConfig();
|
||||
if (storage.type === 's3') {
|
||||
// Use S3 storage
|
||||
} else {
|
||||
// Use local storage
|
||||
}
|
||||
```
|
||||
117
apps/wcag-ada/config/config/default.json
Normal file
117
apps/wcag-ada/config/config/default.json
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
{
|
||||
"appName": "wcag-ada",
|
||||
"environment": "default",
|
||||
|
||||
"log": {
|
||||
"level": "info",
|
||||
"format": "json",
|
||||
"pretty": false
|
||||
},
|
||||
|
||||
"database": {
|
||||
"postgres": {
|
||||
"host": "localhost",
|
||||
"port": 5432,
|
||||
"database": "wcag_ada",
|
||||
"connectionLimit": 10
|
||||
},
|
||||
"redis": {
|
||||
"host": "localhost",
|
||||
"port": 6379,
|
||||
"db": 0
|
||||
}
|
||||
},
|
||||
|
||||
"scanner": {
|
||||
"concurrency": 2,
|
||||
"timeout": 120000,
|
||||
"pageLoadTimeout": 30000,
|
||||
"headless": true,
|
||||
"blockResources": true,
|
||||
"viewport": {
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"deviceScaleFactor": 1
|
||||
}
|
||||
},
|
||||
|
||||
"worker": {
|
||||
"enabled": true,
|
||||
"concurrency": 2,
|
||||
"queueName": "accessibility-scans",
|
||||
"redis": {
|
||||
"host": "localhost",
|
||||
"port": 6379,
|
||||
"db": 2
|
||||
}
|
||||
},
|
||||
|
||||
"features": {
|
||||
"screenshots": {
|
||||
"enabled": true,
|
||||
"quality": 80
|
||||
},
|
||||
"customRules": {
|
||||
"enabled": true
|
||||
},
|
||||
"reports": {
|
||||
"pdf": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"providers": {
|
||||
"storage": {
|
||||
"type": "local",
|
||||
"local": {
|
||||
"basePath": "/tmp/wcag-ada"
|
||||
}
|
||||
},
|
||||
"email": {
|
||||
"enabled": true,
|
||||
"provider": "smtp"
|
||||
},
|
||||
"auth": {
|
||||
"jwt": {
|
||||
"expiresIn": "7d"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"services": {
|
||||
"api": {
|
||||
"name": "wcag-api",
|
||||
"port": 3001,
|
||||
"cors": {
|
||||
"enabled": true,
|
||||
"origin": "*"
|
||||
},
|
||||
"rateLimit": {
|
||||
"enabled": true,
|
||||
"windowMs": 900000,
|
||||
"max": 100
|
||||
}
|
||||
},
|
||||
"dashboard": {
|
||||
"name": "wcag-dashboard",
|
||||
"port": 3000,
|
||||
"apiUrl": "http://localhost:3001"
|
||||
},
|
||||
"worker": {
|
||||
"name": "wcag-worker",
|
||||
"port": 3002
|
||||
}
|
||||
},
|
||||
|
||||
"compliance": {
|
||||
"defaultLevel": {
|
||||
"standard": "WCAG21",
|
||||
"level": "AA"
|
||||
}
|
||||
},
|
||||
|
||||
"subscriptions": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
51
apps/wcag-ada/config/config/development.json
Normal file
51
apps/wcag-ada/config/config/development.json
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
{
|
||||
"environment": "development",
|
||||
|
||||
"log": {
|
||||
"level": "debug",
|
||||
"pretty": true
|
||||
},
|
||||
|
||||
"database": {
|
||||
"postgres": {
|
||||
"host": "localhost",
|
||||
"database": "wcag_ada_dev"
|
||||
}
|
||||
},
|
||||
|
||||
"scanner": {
|
||||
"headless": false,
|
||||
"timeout": 300000
|
||||
},
|
||||
|
||||
"worker": {
|
||||
"concurrency": 1
|
||||
},
|
||||
|
||||
"features": {
|
||||
"reports": {
|
||||
"pdf": {
|
||||
"watermark": false
|
||||
}
|
||||
},
|
||||
"enterprise": {
|
||||
"audit": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"providers": {
|
||||
"auth": {
|
||||
"jwt": {
|
||||
"secret": "dev-secret-change-me"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"services": {
|
||||
"dashboard": {
|
||||
"apiUrl": "http://localhost:3001"
|
||||
}
|
||||
}
|
||||
}
|
||||
64
apps/wcag-ada/config/config/production.json
Normal file
64
apps/wcag-ada/config/config/production.json
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
"environment": "production",
|
||||
|
||||
"log": {
|
||||
"level": "warn",
|
||||
"pretty": false
|
||||
},
|
||||
|
||||
"scanner": {
|
||||
"concurrency": 5,
|
||||
"headless": true
|
||||
},
|
||||
|
||||
"worker": {
|
||||
"concurrency": 4,
|
||||
"redis": {
|
||||
"maxRetriesPerRequest": 3
|
||||
}
|
||||
},
|
||||
|
||||
"features": {
|
||||
"screenshots": {
|
||||
"quality": 70
|
||||
},
|
||||
"reports": {
|
||||
"pdf": {
|
||||
"watermark": true
|
||||
}
|
||||
},
|
||||
"enterprise": {
|
||||
"audit": {
|
||||
"enabled": true,
|
||||
"retention": 365
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"providers": {
|
||||
"storage": {
|
||||
"type": "s3"
|
||||
},
|
||||
"email": {
|
||||
"provider": "sendgrid"
|
||||
},
|
||||
"cdn": {
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
|
||||
"services": {
|
||||
"api": {
|
||||
"cors": {
|
||||
"origin": "https://app.wcag-ada.com"
|
||||
},
|
||||
"rateLimit": {
|
||||
"max": 1000
|
||||
}
|
||||
},
|
||||
"dashboard": {
|
||||
"apiUrl": "https://api.wcag-ada.com",
|
||||
"publicUrl": "https://app.wcag-ada.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
53
apps/wcag-ada/config/config/test.json
Normal file
53
apps/wcag-ada/config/config/test.json
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
"environment": "test",
|
||||
|
||||
"log": {
|
||||
"level": "error",
|
||||
"pretty": false
|
||||
},
|
||||
|
||||
"database": {
|
||||
"postgres": {
|
||||
"database": "wcag_ada_test"
|
||||
},
|
||||
"redis": {
|
||||
"db": 15
|
||||
}
|
||||
},
|
||||
|
||||
"scanner": {
|
||||
"concurrency": 1,
|
||||
"timeout": 10000,
|
||||
"headless": true
|
||||
},
|
||||
|
||||
"worker": {
|
||||
"enabled": false
|
||||
},
|
||||
|
||||
"features": {
|
||||
"screenshots": {
|
||||
"enabled": false
|
||||
},
|
||||
"webhooks": {
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
|
||||
"providers": {
|
||||
"email": {
|
||||
"enabled": false
|
||||
},
|
||||
"analytics": {
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
|
||||
"services": {
|
||||
"api": {
|
||||
"rateLimit": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
19
apps/wcag-ada/config/package.json
Normal file
19
apps/wcag-ada/config/package.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "@wcag-ada/config",
|
||||
"version": "0.1.0",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc -b",
|
||||
"dev": "tsc -w",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@stock-bot/core-config": "workspace:*",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.11.5",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
67
apps/wcag-ada/config/src/config-instance.ts
Normal file
67
apps/wcag-ada/config/src/config-instance.ts
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import { ConfigManager, createAppConfig } from '@stock-bot/core-config';
|
||||
import { wcagAppConfigSchema, type WcagAppConfig } from './schemas/wcag-app.schema';
|
||||
import * as path from 'path';
|
||||
|
||||
let configInstance: ConfigManager<WcagAppConfig> | null = null;
|
||||
|
||||
/**
|
||||
* Initialize the WCAG-ADA configuration
|
||||
* @param serviceName - Optional service name for service-specific overrides
|
||||
*/
|
||||
export function initializeWcagConfig(
|
||||
serviceName?: 'api' | 'dashboard' | 'worker'
|
||||
): WcagAppConfig {
|
||||
if (!configInstance) {
|
||||
configInstance = createAppConfig(wcagAppConfigSchema, {
|
||||
configPath: path.join(__dirname, '../config'),
|
||||
envPrefix: 'WCAG_',
|
||||
});
|
||||
}
|
||||
|
||||
const config = configInstance.initialize(wcagAppConfigSchema);
|
||||
|
||||
// Apply service-specific overrides if provided
|
||||
if (serviceName && config.services[serviceName]) {
|
||||
const serviceConfig = config.services[serviceName];
|
||||
|
||||
// Override port if specified for the service
|
||||
if (serviceConfig.port && process.env.PORT) {
|
||||
serviceConfig.port = parseInt(process.env.PORT, 10);
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current WCAG configuration instance
|
||||
* @throws Error if configuration hasn't been initialized
|
||||
*/
|
||||
export function getWcagConfig(): WcagAppConfig {
|
||||
if (!configInstance) {
|
||||
throw new Error('WCAG configuration not initialized. Call initializeWcagConfig() first.');
|
||||
}
|
||||
return configInstance.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific configuration value by path
|
||||
* @param path - Dot-notation path to the configuration value
|
||||
*/
|
||||
export function getConfigValue<T = unknown>(path: string): T {
|
||||
if (!configInstance) {
|
||||
throw new Error('WCAG configuration not initialized. Call initializeWcagConfig() first.');
|
||||
}
|
||||
return configInstance.getValue<T>(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a configuration path exists
|
||||
* @param path - Dot-notation path to check
|
||||
*/
|
||||
export function hasConfigValue(path: string): boolean {
|
||||
if (!configInstance) {
|
||||
return false;
|
||||
}
|
||||
return configInstance.has(path);
|
||||
}
|
||||
106
apps/wcag-ada/config/src/index.ts
Normal file
106
apps/wcag-ada/config/src/index.ts
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
// Main configuration exports
|
||||
export {
|
||||
initializeWcagConfig,
|
||||
getWcagConfig,
|
||||
getConfigValue,
|
||||
hasConfigValue,
|
||||
} from './config-instance';
|
||||
|
||||
// Schema exports
|
||||
export { wcagAppConfigSchema, type WcagAppConfig } from './schemas/wcag-app.schema';
|
||||
export { scannerConfigSchema, type ScannerConfig } from './schemas/scanner.schema';
|
||||
export { workerConfigSchema, type WorkerConfig } from './schemas/worker.schema';
|
||||
export { featuresConfigSchema, type FeaturesConfig } from './schemas/features.schema';
|
||||
export { providersConfigSchema, type ProvidersConfig } from './schemas/providers.schema';
|
||||
|
||||
// Helper functions
|
||||
import { getWcagConfig } from './config-instance';
|
||||
import type { ScannerConfig, WorkerConfig, FeaturesConfig, ProvidersConfig } from './schemas';
|
||||
|
||||
/**
|
||||
* Get scanner configuration
|
||||
*/
|
||||
export function getScannerConfig(): ScannerConfig {
|
||||
return getWcagConfig().scanner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get worker configuration
|
||||
*/
|
||||
export function getWorkerConfig(): WorkerConfig {
|
||||
return getWcagConfig().worker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get features configuration
|
||||
*/
|
||||
export function getFeaturesConfig(): FeaturesConfig {
|
||||
return getWcagConfig().features;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get providers configuration
|
||||
*/
|
||||
export function getProvidersConfig(): ProvidersConfig {
|
||||
return getWcagConfig().providers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get service configuration
|
||||
*/
|
||||
export function getServiceConfig(service: 'api' | 'dashboard' | 'worker') {
|
||||
return getWcagConfig().services[service];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a feature is enabled
|
||||
*/
|
||||
export function isFeatureEnabled(feature: string): boolean {
|
||||
const features = getFeaturesConfig();
|
||||
const parts = feature.split('.');
|
||||
|
||||
let current: any = features;
|
||||
for (const part of parts) {
|
||||
if (typeof current !== 'object' || !(part in current)) {
|
||||
return false;
|
||||
}
|
||||
current = current[part];
|
||||
}
|
||||
|
||||
return current === true || (typeof current === 'object' && current.enabled === true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get database configuration
|
||||
*/
|
||||
export function getDatabaseConfig() {
|
||||
return getWcagConfig().database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Redis configuration for queues
|
||||
*/
|
||||
export function getRedisConfig() {
|
||||
return getWcagConfig().worker.redis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get storage configuration
|
||||
*/
|
||||
export function getStorageConfig() {
|
||||
return getWcagConfig().providers.storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get compliance configuration
|
||||
*/
|
||||
export function getComplianceConfig() {
|
||||
return getWcagConfig().compliance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subscription tier configuration
|
||||
*/
|
||||
export function getSubscriptionConfig(tier: 'starter' | 'professional' | 'enterprise') {
|
||||
return getWcagConfig().subscriptions.tiers[tier];
|
||||
}
|
||||
89
apps/wcag-ada/config/src/schemas/features.schema.ts
Normal file
89
apps/wcag-ada/config/src/schemas/features.schema.ts
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
export const featuresConfigSchema = z.object({
|
||||
// Scanner features
|
||||
screenshots: z.object({
|
||||
enabled: z.boolean().default(true),
|
||||
quality: z.number().min(0).max(100).default(80),
|
||||
fullPage: z.boolean().default(false),
|
||||
}).default({}),
|
||||
|
||||
customRules: z.object({
|
||||
enabled: z.boolean().default(true),
|
||||
maxRules: z.number().default(50),
|
||||
}).default({}),
|
||||
|
||||
multiPage: z.object({
|
||||
enabled: z.boolean().default(true),
|
||||
maxPages: z.number().default(10),
|
||||
maxDepth: z.number().default(3),
|
||||
}).default({}),
|
||||
|
||||
// API features
|
||||
apiKeys: z.object({
|
||||
enabled: z.boolean().default(true),
|
||||
maxPerUser: z.number().default(5),
|
||||
}).default({}),
|
||||
|
||||
webhooks: z.object({
|
||||
enabled: z.boolean().default(true),
|
||||
maxPerWebsite: z.number().default(3),
|
||||
retryAttempts: z.number().default(3),
|
||||
}).default({}),
|
||||
|
||||
// Report features
|
||||
reports: z.object({
|
||||
pdf: z.object({
|
||||
enabled: z.boolean().default(true),
|
||||
watermark: z.boolean().default(true),
|
||||
}).default({}),
|
||||
excel: z.object({
|
||||
enabled: z.boolean().default(false),
|
||||
}).default({}),
|
||||
scheduling: z.object({
|
||||
enabled: z.boolean().default(true),
|
||||
}).default({}),
|
||||
}).default({}),
|
||||
|
||||
// Compliance features
|
||||
compliance: z.object({
|
||||
wcag20: z.boolean().default(true),
|
||||
wcag21: z.boolean().default(true),
|
||||
wcag22: z.boolean().default(true),
|
||||
section508: z.boolean().default(false),
|
||||
ada: z.boolean().default(true),
|
||||
}).default({}),
|
||||
|
||||
// Integration features
|
||||
integrations: z.object({
|
||||
github: z.object({
|
||||
enabled: z.boolean().default(true),
|
||||
}).default({}),
|
||||
slack: z.object({
|
||||
enabled: z.boolean().default(true),
|
||||
}).default({}),
|
||||
teams: z.object({
|
||||
enabled: z.boolean().default(false),
|
||||
}).default({}),
|
||||
jira: z.object({
|
||||
enabled: z.boolean().default(false),
|
||||
}).default({}),
|
||||
}).default({}),
|
||||
|
||||
// Enterprise features
|
||||
enterprise: z.object({
|
||||
sso: z.object({
|
||||
enabled: z.boolean().default(false),
|
||||
providers: z.array(z.enum(['saml', 'oauth', 'ldap'])).default([]),
|
||||
}).default({}),
|
||||
whiteLabel: z.object({
|
||||
enabled: z.boolean().default(false),
|
||||
}).default({}),
|
||||
audit: z.object({
|
||||
enabled: z.boolean().default(true),
|
||||
retention: z.number().default(365), // days
|
||||
}).default({}),
|
||||
}).default({}),
|
||||
});
|
||||
|
||||
export type FeaturesConfig = z.infer<typeof featuresConfigSchema>;
|
||||
84
apps/wcag-ada/config/src/schemas/providers.schema.ts
Normal file
84
apps/wcag-ada/config/src/schemas/providers.schema.ts
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
export const providersConfigSchema = z.object({
|
||||
// Storage providers
|
||||
storage: z.object({
|
||||
type: z.enum(['local', 's3', 'gcs', 'azure']).default('local'),
|
||||
local: z.object({
|
||||
basePath: z.string().default('/tmp/wcag-ada'),
|
||||
reports: z.string().default('reports'),
|
||||
screenshots: z.string().default('screenshots'),
|
||||
exports: z.string().default('exports'),
|
||||
}).default({}),
|
||||
s3: z.object({
|
||||
enabled: z.boolean().default(false),
|
||||
bucket: z.string().optional(),
|
||||
region: z.string().default('us-east-1'),
|
||||
accessKeyId: z.string().optional(),
|
||||
secretAccessKey: z.string().optional(),
|
||||
endpoint: z.string().optional(),
|
||||
}).default({}),
|
||||
}).default({}),
|
||||
|
||||
// Email providers
|
||||
email: z.object({
|
||||
enabled: z.boolean().default(true),
|
||||
provider: z.enum(['smtp', 'sendgrid', 'ses', 'postmark']).default('smtp'),
|
||||
from: z.object({
|
||||
name: z.string().default('WCAG-ADA Compliance'),
|
||||
email: z.string().email().default('noreply@wcag-ada.com'),
|
||||
}).default({}),
|
||||
smtp: z.object({
|
||||
host: z.string().default('localhost'),
|
||||
port: z.number().default(587),
|
||||
secure: z.boolean().default(false),
|
||||
auth: z.object({
|
||||
user: z.string().optional(),
|
||||
pass: z.string().optional(),
|
||||
}).default({}),
|
||||
}).default({}),
|
||||
sendgrid: z.object({
|
||||
apiKey: z.string().optional(),
|
||||
}).default({}),
|
||||
}).default({}),
|
||||
|
||||
// Authentication providers
|
||||
auth: z.object({
|
||||
jwt: z.object({
|
||||
secret: z.string().default('change-me-in-production'),
|
||||
expiresIn: z.string().default('7d'),
|
||||
refreshExpiresIn: z.string().default('30d'),
|
||||
}).default({}),
|
||||
oauth: z.object({
|
||||
google: z.object({
|
||||
enabled: z.boolean().default(false),
|
||||
clientId: z.string().optional(),
|
||||
clientSecret: z.string().optional(),
|
||||
}).default({}),
|
||||
github: z.object({
|
||||
enabled: z.boolean().default(false),
|
||||
clientId: z.string().optional(),
|
||||
clientSecret: z.string().optional(),
|
||||
}).default({}),
|
||||
}).default({}),
|
||||
}).default({}),
|
||||
|
||||
// Analytics providers
|
||||
analytics: z.object({
|
||||
enabled: z.boolean().default(true),
|
||||
provider: z.enum(['posthog', 'mixpanel', 'amplitude', 'custom']).default('posthog'),
|
||||
posthog: z.object({
|
||||
apiKey: z.string().optional(),
|
||||
host: z.string().default('https://app.posthog.com'),
|
||||
}).default({}),
|
||||
}).default({}),
|
||||
|
||||
// CDN providers
|
||||
cdn: z.object({
|
||||
enabled: z.boolean().default(false),
|
||||
provider: z.enum(['cloudflare', 'cloudfront', 'fastly']).default('cloudflare'),
|
||||
baseUrl: z.string().optional(),
|
||||
}).default({}),
|
||||
});
|
||||
|
||||
export type ProvidersConfig = z.infer<typeof providersConfigSchema>;
|
||||
34
apps/wcag-ada/config/src/schemas/scanner.schema.ts
Normal file
34
apps/wcag-ada/config/src/schemas/scanner.schema.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
export const scannerConfigSchema = z.object({
|
||||
concurrency: z.number().min(1).max(10).default(2),
|
||||
timeout: z.number().min(30000).default(120000), // 2 minutes default
|
||||
pageLoadTimeout: z.number().min(10000).default(30000),
|
||||
headless: z.boolean().default(true),
|
||||
blockResources: z.boolean().default(true),
|
||||
viewport: z.object({
|
||||
width: z.number().default(1280),
|
||||
height: z.number().default(720),
|
||||
deviceScaleFactor: z.number().default(1),
|
||||
}).default({}),
|
||||
browsers: z.object({
|
||||
chromium: z.object({
|
||||
enabled: z.boolean().default(true),
|
||||
args: z.array(z.string()).default(['--no-sandbox', '--disable-setuid-sandbox']),
|
||||
}).default({}),
|
||||
}).default({}),
|
||||
axe: z.object({
|
||||
tags: z.array(z.string()).default(['wcag2aa', 'wcag21aa']),
|
||||
resultTypes: z.array(z.enum(['violations', 'passes', 'incomplete', 'inapplicable']))
|
||||
.default(['violations', 'passes', 'incomplete', 'inapplicable']),
|
||||
}).default({}),
|
||||
retries: z.object({
|
||||
maxAttempts: z.number().default(3),
|
||||
backoff: z.object({
|
||||
type: z.enum(['exponential', 'fixed']).default('exponential'),
|
||||
delay: z.number().default(2000),
|
||||
}).default({}),
|
||||
}).default({}),
|
||||
});
|
||||
|
||||
export type ScannerConfig = z.infer<typeof scannerConfigSchema>;
|
||||
111
apps/wcag-ada/config/src/schemas/wcag-app.schema.ts
Normal file
111
apps/wcag-ada/config/src/schemas/wcag-app.schema.ts
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
import { z } from 'zod';
|
||||
import {
|
||||
baseAppConfigSchema,
|
||||
logConfigSchema,
|
||||
databaseConfigSchema,
|
||||
serviceConfigSchema,
|
||||
} from '@stock-bot/core-config';
|
||||
import { scannerConfigSchema } from './scanner.schema';
|
||||
import { workerConfigSchema } from './worker.schema';
|
||||
import { featuresConfigSchema } from './features.schema';
|
||||
import { providersConfigSchema } from './providers.schema';
|
||||
|
||||
// Service-specific configurations
|
||||
const wcagServicesSchema = z.object({
|
||||
api: serviceConfigSchema.extend({
|
||||
port: z.number().default(3001),
|
||||
cors: z.object({
|
||||
enabled: z.boolean().default(true),
|
||||
origin: z.string().default('*'),
|
||||
credentials: z.boolean().default(true),
|
||||
}).default({}),
|
||||
rateLimit: z.object({
|
||||
enabled: z.boolean().default(true),
|
||||
windowMs: z.number().default(900000), // 15 minutes
|
||||
max: z.number().default(100),
|
||||
keyGenerator: z.enum(['ip', 'userId', 'apiKey']).default('userId'),
|
||||
}).default({}),
|
||||
pagination: z.object({
|
||||
defaultLimit: z.number().default(20),
|
||||
maxLimit: z.number().default(100),
|
||||
}).default({}),
|
||||
}),
|
||||
|
||||
dashboard: serviceConfigSchema.extend({
|
||||
port: z.number().default(3000),
|
||||
apiUrl: z.string().default('http://localhost:3001'),
|
||||
publicUrl: z.string().default('http://localhost:3000'),
|
||||
}),
|
||||
|
||||
worker: serviceConfigSchema.extend({
|
||||
port: z.number().default(3002), // For health checks
|
||||
}),
|
||||
});
|
||||
|
||||
// Main WCAG application configuration schema
|
||||
export const wcagAppConfigSchema = baseAppConfigSchema.extend({
|
||||
appName: z.literal('wcag-ada').default('wcag-ada'),
|
||||
|
||||
// Core configurations
|
||||
log: logConfigSchema,
|
||||
database: databaseConfigSchema,
|
||||
|
||||
// WCAG-specific configurations
|
||||
scanner: scannerConfigSchema,
|
||||
worker: workerConfigSchema,
|
||||
features: featuresConfigSchema,
|
||||
providers: providersConfigSchema,
|
||||
|
||||
// Service configurations
|
||||
services: wcagServicesSchema.default({}),
|
||||
|
||||
// Business logic configurations
|
||||
compliance: z.object({
|
||||
defaultLevel: z.object({
|
||||
standard: z.enum(['WCAG20', 'WCAG21', 'WCAG22']).default('WCAG21'),
|
||||
level: z.enum(['A', 'AA', 'AAA']).default('AA'),
|
||||
}).default({}),
|
||||
passingScore: z.object({
|
||||
A: z.number().min(0).max(100).default(95),
|
||||
AA: z.number().min(0).max(100).default(98),
|
||||
AAA: z.number().min(0).max(100).default(100),
|
||||
}).default({}),
|
||||
criticalCriteria: z.array(z.string()).default([
|
||||
'1.1.1', // Non-text Content
|
||||
'1.3.1', // Info and Relationships
|
||||
'1.4.3', // Contrast (Minimum)
|
||||
'2.1.1', // Keyboard
|
||||
'2.1.2', // No Keyboard Trap
|
||||
'2.4.1', // Bypass Blocks
|
||||
'2.4.2', // Page Titled
|
||||
'4.1.2', // Name, Role, Value
|
||||
]),
|
||||
}).default({}),
|
||||
|
||||
// Subscription/pricing tiers
|
||||
subscriptions: z.object({
|
||||
enabled: z.boolean().default(true),
|
||||
tiers: z.object({
|
||||
starter: z.object({
|
||||
websites: z.number().default(5),
|
||||
scansPerMonth: z.number().default(500),
|
||||
users: z.number().default(1),
|
||||
price: z.number().default(49),
|
||||
}).default({}),
|
||||
professional: z.object({
|
||||
websites: z.number().default(25),
|
||||
scansPerMonth: z.number().default(5000),
|
||||
users: z.number().default(5),
|
||||
price: z.number().default(149),
|
||||
}).default({}),
|
||||
enterprise: z.object({
|
||||
websites: z.number().default(-1), // Unlimited
|
||||
scansPerMonth: z.number().default(-1),
|
||||
users: z.number().default(-1),
|
||||
price: z.number().default(499),
|
||||
}).default({}),
|
||||
}).default({}),
|
||||
}).default({}),
|
||||
});
|
||||
|
||||
export type WcagAppConfig = z.infer<typeof wcagAppConfigSchema>;
|
||||
49
apps/wcag-ada/config/src/schemas/worker.schema.ts
Normal file
49
apps/wcag-ada/config/src/schemas/worker.schema.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
export const workerConfigSchema = z.object({
|
||||
enabled: z.boolean().default(true),
|
||||
concurrency: z.number().min(1).max(10).default(2),
|
||||
queueName: z.string().default('accessibility-scans'),
|
||||
redis: z.object({
|
||||
host: z.string().default('localhost'),
|
||||
port: z.number().default(6379),
|
||||
password: z.string().optional(),
|
||||
db: z.number().default(2), // Different DB for WCAG
|
||||
maxRetriesPerRequest: z.number().nullable().default(null),
|
||||
}).default({}),
|
||||
jobs: z.object({
|
||||
scan: z.object({
|
||||
priority: z.number().default(0),
|
||||
attempts: z.number().default(3),
|
||||
backoff: z.object({
|
||||
type: z.enum(['exponential', 'fixed']).default('exponential'),
|
||||
delay: z.number().default(2000),
|
||||
}).default({}),
|
||||
timeout: z.number().default(300000), // 5 minutes
|
||||
removeOnComplete: z.object({
|
||||
age: z.number().default(86400), // 24 hours
|
||||
count: z.number().default(100),
|
||||
}).default({}),
|
||||
removeOnFail: z.object({
|
||||
age: z.number().default(604800), // 7 days
|
||||
count: z.number().default(500),
|
||||
}).default({}),
|
||||
}).default({}),
|
||||
report: z.object({
|
||||
priority: z.number().default(1),
|
||||
attempts: z.number().default(2),
|
||||
backoff: z.object({
|
||||
type: z.enum(['exponential', 'fixed']).default('exponential'),
|
||||
delay: z.number().default(5000),
|
||||
}).default({}),
|
||||
timeout: z.number().default(600000), // 10 minutes
|
||||
}).default({}),
|
||||
}).default({}),
|
||||
scheduler: z.object({
|
||||
enabled: z.boolean().default(true),
|
||||
interval: z.number().default(60000), // Check every minute
|
||||
timezone: z.string().default('UTC'),
|
||||
}).default({}),
|
||||
});
|
||||
|
||||
export type WorkerConfig = z.infer<typeof workerConfigSchema>;
|
||||
13
apps/wcag-ada/config/tsconfig.json
Normal file
13
apps/wcag-ada/config/tsconfig.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"composite": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"],
|
||||
"references": [
|
||||
{ "path": "../../../libs/core/config" }
|
||||
]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue