mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-01-12 23:32:55 +01:00
24 KiB
24 KiB
DEPLOY-003: Deploy Settings & Environment Variables
Overview
Provide comprehensive deployment configuration including environment variables, build settings, custom domains, and deployment rules. Users can manage different environments (development, staging, production) with different configurations.
Context
Currently:
- Environment variables set per deploy manually
- No persistent environment configuration
- No distinction between environments
- Custom domain setup requires external configuration
This task adds:
- Persistent environment variable management
- Multiple environment profiles
- Custom domain configuration
- Build optimization settings
- Deploy rules and triggers
Requirements
Functional Requirements
-
Environment Variables
- Add/edit/delete variables
- Sensitive variable masking
- Import from .env file
- Export to .env file
- Variable validation
-
Environment Profiles
- Development, Staging, Production presets
- Custom profiles
- Variables per profile
- Easy switching
-
Custom Domains
- View current domains
- Add custom domain
- SSL certificate status
- DNS configuration help
-
Build Settings
- Output directory
- Base URL configuration
- Asset optimization
- Source maps (dev only)
-
Deploy Rules
- Auto-deploy on push
- Branch-based rules
- Deploy schedule
- Deploy hooks/webhooks
Non-Functional Requirements
- Variables encrypted at rest
- Sensitive values never logged
- Sync with platform settings
- Works offline (cached)
Technical Approach
1. Environment Configuration Service
// packages/noodl-editor/src/editor/src/services/EnvironmentConfigService.ts
interface EnvironmentVariable {
key: string;
value: string;
sensitive: boolean; // Masked in UI
scope: VariableScope;
}
enum VariableScope {
BUILD = 'build', // Available during build
RUNTIME = 'runtime', // Injected into app
BOTH = 'both'
}
interface EnvironmentProfile {
id: string;
name: string;
description?: string;
variables: EnvironmentVariable[];
isDefault: boolean;
platform?: DeployPlatform; // If linked to a platform
}
interface DeploySettings {
outputDirectory: string;
baseUrl: string;
assetOptimization: boolean;
sourceMaps: boolean;
cleanUrls: boolean;
trailingSlash: boolean;
}
class EnvironmentConfigService {
private static instance: EnvironmentConfigService;
// Profiles
async getProfiles(projectId: string): Promise<EnvironmentProfile[]>;
async createProfile(projectId: string, profile: Omit<EnvironmentProfile, 'id'>): Promise<EnvironmentProfile>;
async updateProfile(projectId: string, profileId: string, updates: Partial<EnvironmentProfile>): Promise<void>;
async deleteProfile(projectId: string, profileId: string): Promise<void>;
// Variables
async getVariables(projectId: string, profileId: string): Promise<EnvironmentVariable[]>;
async setVariable(projectId: string, profileId: string, variable: EnvironmentVariable): Promise<void>;
async deleteVariable(projectId: string, profileId: string, key: string): Promise<void>;
async importFromEnvFile(projectId: string, profileId: string, content: string): Promise<void>;
async exportToEnvFile(projectId: string, profileId: string): Promise<string>;
// Build settings
async getDeploySettings(projectId: string): Promise<DeploySettings>;
async updateDeploySettings(projectId: string, settings: Partial<DeploySettings>): Promise<void>;
// Platform sync
async syncWithPlatform(projectId: string, profileId: string, platform: DeployPlatform): Promise<void>;
async pullFromPlatform(projectId: string, platform: DeployPlatform): Promise<EnvironmentVariable[]>;
}
2. Environment Storage
// Store in project metadata, encrypted
interface ProjectDeployConfig {
profiles: EnvironmentProfile[];
activeProfileId: string;
deploySettings: DeploySettings;
domains: CustomDomain[];
deployRules: DeployRule[];
}
// Encryption for sensitive values
class SecureStorage {
async encrypt(value: string): Promise<string>;
async decrypt(value: string): Promise<string>;
}
// Store encrypted in project.json
{
"metadata": {
"deployConfig": {
"profiles": [
{
"id": "prod",
"name": "Production",
"variables": [
{
"key": "API_URL",
"value": "https://api.example.com", // Plain text
"sensitive": false
},
{
"key": "API_KEY",
"value": "encrypted:abc123...", // Encrypted
"sensitive": true
}
]
}
]
}
}
}
3. Domain Configuration
interface CustomDomain {
domain: string;
platform: DeployPlatform;
siteId: string;
status: DomainStatus;
sslStatus: SSLStatus;
dnsRecords?: DNSRecord[];
}
enum DomainStatus {
PENDING = 'pending',
ACTIVE = 'active',
FAILED = 'failed'
}
enum SSLStatus {
PENDING = 'pending',
ACTIVE = 'active',
EXPIRED = 'expired',
FAILED = 'failed'
}
interface DNSRecord {
type: 'A' | 'CNAME' | 'TXT';
name: string;
value: string;
required: boolean;
}
class DomainService {
async addDomain(siteId: string, domain: string): Promise<CustomDomain>;
async verifyDomain(domainId: string): Promise<DomainStatus>;
async getDNSInstructions(domain: string): Promise<DNSRecord[]>;
async checkSSL(domainId: string): Promise<SSLStatus>;
}
4. Deploy Rules
interface DeployRule {
id: string;
name: string;
enabled: boolean;
trigger: DeployTrigger;
conditions: DeployCondition[];
actions: DeployAction[];
}
interface DeployTrigger {
type: 'push' | 'schedule' | 'manual' | 'webhook';
config: PushConfig | ScheduleConfig | WebhookConfig;
}
interface PushConfig {
branches: string[]; // Glob patterns
paths?: string[]; // Only deploy if these paths changed
}
interface ScheduleConfig {
cron: string; // Cron expression
timezone: string;
}
interface DeployCondition {
type: 'branch' | 'tag' | 'path' | 'message';
operator: 'equals' | 'contains' | 'matches';
value: string;
}
interface DeployAction {
type: 'deploy' | 'notify' | 'webhook';
config: DeployActionConfig | NotifyConfig | WebhookConfig;
}
5. UI Components
Environment Variables Panel
┌─────────────────────────────────────────────────────────────────────┐
│ Environment Variables [×] │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Profile: [Production ▾] [+ New Profile] │
│ │
│ VARIABLES [Import .env] │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Key Value Scope [⋯] │ │
│ ├─────────────────────────────────────────────────────────────────┤ │
│ │ API_URL https://api.example.com Runtime [✎🗑] │ │
│ │ API_KEY •••••••••••••••••••••• Runtime [✎🗑] │ │
│ │ ANALYTICS_ID UA-12345678-1 Runtime [✎🗑] │ │
│ │ DEBUG false Build [✎🗑] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ [+ Add Variable] [Export .env] │
│ │
│ ☑ Sync with Netlify │
│ Last synced: 5 minutes ago [Sync Now] │
│ │
└─────────────────────────────────────────────────────────────────────┘
Build Settings Panel
┌─────────────────────────────────────────────────────────────────────┐
│ Build Settings [×] │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ OUTPUT │
│ Output Directory: [dist ] │
│ Base URL: [/ ] │
│ │
│ OPTIMIZATION │
│ ☑ Optimize assets (minify JS/CSS) │
│ ☐ Generate source maps (increases build size) │
│ ☑ Clean URLs (remove .html extension) │
│ ☐ Trailing slash on URLs │
│ │
│ ADVANCED │
│ Build Command: [npm run build ] │
│ Publish Directory: [build ] │
│ │
│ NODE VERSION │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 18 (LTS) [▾] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ [Save Settings] │
└─────────────────────────────────────────────────────────────────────┘
Custom Domains Panel
┌─────────────────────────────────────────────────────────────────────┐
│ Custom Domains [×] │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ CONNECTED DOMAINS │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 🌐 myapp.com │ │
│ │ ✓ DNS Configured ✓ SSL Active │ │
│ │ Primary domain [Remove] │ │
│ ├─────────────────────────────────────────────────────────────────┤ │
│ │ 🌐 www.myapp.com │ │
│ │ ✓ DNS Configured ✓ SSL Active │ │
│ │ Redirects to myapp.com [Remove] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ [+ Add Custom Domain] │
│ │
│ DEFAULT DOMAIN │
│ https://myapp.netlify.app [Copy] │
│ │
└─────────────────────────────────────────────────────────────────────┘
Add Domain Modal
┌─────────────────────────────────────────────────────────────────────┐
│ Add Custom Domain [×] │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Domain: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ app.example.com │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ DNS CONFIGURATION REQUIRED │
│ │
│ Add these records to your DNS provider: │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Type Name Value [Copy] │ │
│ ├─────────────────────────────────────────────────────────────────┤ │
│ │ CNAME app myapp.netlify.app [Copy] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ⚠️ DNS changes can take up to 48 hours to propagate │
│ │
│ Status: ⏳ Waiting for DNS verification... │
│ │
│ [Cancel] [Verify Domain] [Done] │
└─────────────────────────────────────────────────────────────────────┘
Deploy Rules Panel
┌─────────────────────────────────────────────────────────────────────┐
│ Deploy Rules [×] │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ RULES [+ Add Rule] │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ ☑ Auto-deploy production │ │
│ │ When: Push to main │ │
│ │ Deploy to: Production [Edit 🗑] │ │
│ ├─────────────────────────────────────────────────────────────────┤ │
│ │ ☑ Preview branches │ │
│ │ When: Push to feature/* │ │
│ │ Deploy to: Preview [Edit 🗑] │ │
│ ├─────────────────────────────────────────────────────────────────┤ │
│ │ ☐ Scheduled deploy │ │
│ │ When: Daily at 2:00 AM UTC │ │
│ │ Deploy to: Production [Edit 🗑] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ WEBHOOKS [+ Add Webhook] │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Build hook URL: │ │
│ │ https://api.netlify.com/build_hooks/abc123 [Copy] [🔄] │ │
│ │ Trigger: POST request to this URL │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Files to Create
packages/noodl-editor/src/editor/src/services/EnvironmentConfigService.tspackages/noodl-editor/src/editor/src/services/DomainService.tspackages/noodl-editor/src/editor/src/services/DeployRulesService.tspackages/noodl-core-ui/src/components/deploy/EnvironmentVariables/EnvironmentVariables.tsxpackages/noodl-core-ui/src/components/deploy/EnvironmentVariables/VariableRow.tsxpackages/noodl-core-ui/src/components/deploy/EnvironmentVariables/ProfileSelector.tsxpackages/noodl-core-ui/src/components/deploy/BuildSettings/BuildSettings.tsxpackages/noodl-core-ui/src/components/deploy/CustomDomains/CustomDomains.tsxpackages/noodl-core-ui/src/components/deploy/CustomDomains/AddDomainModal.tsxpackages/noodl-core-ui/src/components/deploy/DeployRules/DeployRules.tsxpackages/noodl-core-ui/src/components/deploy/DeployRules/RuleEditor.tsx
Files to Modify
-
packages/noodl-editor/src/editor/src/views/DeployPopup/DeployPopup.tsx- Add settings tabs
- Integrate new panels
-
packages/noodl-editor/src/editor/src/utils/compilation/compilation.ts- Use environment variables from config
- Apply build settings
-
packages/noodl-editor/src/editor/src/models/projectmodel.ts- Store deploy configuration
- Load/save config
-
packages/noodl-editor/src/editor/src/services/DeployService.ts- Apply environment variables to deploy
- Handle domain configuration
Implementation Steps
Phase 1: Environment Variables
- Create EnvironmentConfigService
- Implement variable storage
- Implement encryption for sensitive values
- Add import/export .env
Phase 2: Environment Profiles
- Add profile management
- Implement profile switching
- Add default profiles (dev/staging/prod)
- UI for profile management
Phase 3: UI - Variables Panel
- Create EnvironmentVariables component
- Create VariableRow component
- Add add/edit/delete functionality
- Add import/export buttons
Phase 4: Build Settings
- Create build settings storage
- Create BuildSettings component
- Integrate with compilation
- Test with deployments
Phase 5: Custom Domains
- Create DomainService
- Implement platform-specific domain APIs
- Create CustomDomains component
- Create AddDomainModal
Phase 6: Deploy Rules
- Create DeployRulesService
- Implement rule evaluation
- Create DeployRules component
- Create RuleEditor
Testing Checklist
- Variables saved correctly
- Sensitive values encrypted
- Variables applied to deploy
- Import .env works
- Export .env works
- Profile switching works
- Build settings applied
- Custom domain setup works
- DNS verification works
- Deploy rules trigger correctly
- Webhooks work
- Platform sync works
Dependencies
- DEPLOY-001 (One-Click Deploy) - for platform integration
Blocked By
- DEPLOY-001
Blocks
- None (final DEPLOY task)
Estimated Effort
- Environment config service: 4-5 hours
- Variable storage/encryption: 3-4 hours
- Environment profiles: 3-4 hours
- UI variables panel: 4-5 hours
- Build settings: 3-4 hours
- Custom domains: 4-5 hours
- Deploy rules: 4-5 hours
- Testing & polish: 3-4 hours
- Total: 28-36 hours
Success Criteria
- Environment variables persist across deploys
- Sensitive values properly secured
- Multiple profiles supported
- Import/export .env works
- Custom domains configurable
- Deploy rules automate deployments
- Settings sync with platforms
Security Considerations
- Sensitive values encrypted at rest
- Never log sensitive values
- Use platform-native secret storage where available
- Clear memory after use
- Validate input to prevent injection
Future Enhancements
- Environment variable inheritance
- Secret rotation reminders
- Integration with secret managers (Vault, AWS Secrets)
- A/B testing configuration
- Feature flags integration
- Monitoring/alerting integration